平成最後はgRPCなサーバーを書いてハッカソンしていた話

こういうのってあとから書こうとすると時間が経ちすぎてなかなかポストできなくなりますよね。

そういうわけで書きます。

何してきたのよ

サイバーエージェントさんの 平成最後のハッカソンに出場してきました。

www.cyberagent.co.jp

4月の頭くらいからtwitter上で人事の方々がこのハッカソンについて話をしていたので、「ちょっと出てみるかぁ」みたいなノリで考えてました。

出場するまでの流れ

そんなことを考えてると、最近イキりがすごい id:NoahOrberg

id:NoahOrberg < これ、マヤミトくんと出ない?

みたいなことを言ってきました。

「プログラミングができる5歳児」ことのあくんが、インターネットで赤ちゃんをしている id:yt8492 を推していましたし、なによりもマヤミトくんと一緒に出たいなと僕も少し考えていたのでこの3人で出ることにしました。

チームの人員としては

  • プログラミングのできる5歳児 (サーバー&技術担当) のあ id:NoahOrberg
  • SNS広報担当とチームの平均体重を引っ張り上げる(サーバー) 僕 id:flying_hato_bus
  • 精神年齢生後3ヶ月(Android) マヤミト id:yt8492

この3人で蠱毒みたいなチーム作って参加登録しました。

数日後にはみんな参加できることが決定し、やっていきを高めてました。

お題が発表される

今回のハッカソンのお題は 「平成生まれの私たちが、平成を楽しく振り返ることができるサービス/モノ」というものでした。

さっそくのあくんとマヤミトくんと何を作るかを話し合うことにしました。

f:id:flying_hato_bus:20190508170237p:plain
チーム名とスクリーンネームが被ってちょっとテンションの上がるのあ

話した時に出たものはこちら

  • 小学校時代に書いた(こともある)プロフ帳を作る
  • 黒歴史を共有できるものを作る
  • spotifyでおすすめの曲をサジェスト

主に出たのはこの3つでした。

選ばれたのは黒歴史を共有するものを作るというプラン。

他の2つは

  • プログラミングのできる5歳児 (サーバー&技術担当) のあ id:NoahOrberg
  • SNS広報担当とチームの平均体重を引っ張り上げる(サーバー) 僕 id:flying_hato_bus
  • 精神年齢生後3ヶ月(Android) マヤミト id:yt8492

このチームの人員から考えても、そんなおしゃれなものを作るよりはネタに振り切ったほうがいいだろう、という考えでボツにしました。 僕達がそういうプロダクト作るのは似合わないってことですね

f:id:flying_hato_bus:20190508170904p:plain
みんなで話し合ったあとのホワイトボード

黒歴史を共有できるものということで深堀りしていき、出てきたのがこのホワイトボードのようなことです。

黒歴史 x ブロックチェーン

f:id:flying_hato_bus:20190508172051p:plain 黒歴史というものは、オタクならば結構な人たちが作ったことであろう歴史です。

  • イキリ満載のブログエントリ
  • 某動画サイトに実況動画を投稿
  • 肌にペンで傷を描いて学校に登校
  • 霊感があるキャラを装って心霊写真を捏造

こんな感じで「あの頃はこれがかっこいいと思った」「他人とは違う自分を演じたかった」という考えで突っ走ってしまう行為です(全部僕の体験談です)

黒歴史というものはあくまで「歴史」、勝手に改変されたり、なかったことにされるということはもってのほかです。

勝手にネット上で作った黒歴史を消されてはいけません。

つまり、これを恒久的に保存してあげる必要があります。

ここでブロックチェーンです!!

ブロックチェーンは情報の改ざんに強いデータベースです。

つまり、この黒歴史の情報をブロックにしてしまえば、半永久的に黒歴史は保存され、歴史の改ざんも防ぐことができるようになります。

作るにあたって

作るにあたっての作業分担はこんな感じ

サーバーは Go x gPRC、ネイティブアプリは Kotlin x gRPC という感じの構成。

github.com

github.com

それぞれのgithub repositoryです。

のあくんとマヤミトくんがいろいろと頑張ってくれたので実装は早く終わりました。

gRPCを使ってみた感想

今までやってきたようなRESTfulなJSONサーバーより断然書きやすかったです。

gRPCは自動生成されたgoのファイルを見れば何を実装すればいいか一目瞭然ですし、入出力がProtocol Buffersなので、jsonを扱う時のように Marshal / Unmarshal しなくて済むというのがめちゃくちゃ楽でした。

goではstructで扱えるので変数に型があり、どういうデータなのかがすぐ分かりますし、何よりもシリアライズされたデータが行き来するので速さが出ます。

そんなわけで大体のサーバーはハッカソンの3日前には大方の実装が終わり、マヤミトくんに頑張ってもらうだけになってました。

ブロックチェーンについて

ブロックチェーンは †最強†エンジニアののあくん id:NoahOrberg が担当しました。

合意形成アルゴリズム

ブロックチェーンの中心とも言える合意形成のアルゴリズムはのあくんが独自に実装してくれました。

独自合意形成アルゴリズム HEISEI

合意形成アルゴリズムは平成を使いました。いい感じにブロックが生成されるのが "平" "成" の二文字を使った時でした。

これも前日にはのあくんがすべて実装していたのでさすがですね。

ネイティブアプリ

一人で全部実装したマヤミトくんの記事でしっかり書かれているはずです。

yt8492.hatenablog.com

学部2年生ながらここまでやってくれたマヤミトくんは煽り抜きで最強だと思います。

これで彼女がいれば敵なしですね。

ハッカソン当日

ハッカソン当日には黒歴史ブロックチェーンがある程度出来た状態で参加できました。

あとは繋ぎこみをするだけという状態です。

まあ、こういう時は大体苦労するんですよね、苦労しました。

サーバーにバグが結構残っていた

本当に申し訳がなかったです。許して、マヤミトくん、のあくん。

直前まで小さなバグがちょっとずつ出ていてそれを潰す作業をしていました。

前日まで散々イキっていたのにこのザマです。精進します。

発表のフォーマットが特殊すぎた

チームが多すぎて発表の時間が押していた結果、

「スライドは2枚、1枚は実際に動いている動画を貼り、2枚目には説明を書く」

というものになっていました。

見た目にインパクトがあるものが有利になったのかなと思います。

僕らのチームは 見た目のインパクト < 技術 & 発想力 みたいな部分があったので技術力としてはそれなりにあったと思いますが、インパクトが足りませんでした。

優勝者のチームが以前会ったことのあるはととさんだったことにも衝撃でした。

作ったものが。「マリオのゲームで平成を振り返る」というコンセプトで、web上でマリオのステージが進むごとにゲームハードが進化していくという技術力がやばいやつでした。

「Vueのちからってすげーーーーー!!!!」

まさかの発想がかぶる

黒歴史という発想は被りそうだな〜〜〜〜」

とマヤミトくんとのあくんと笑いながら話していましたが、見事に被りました。

それも同じ大学のmtのチームでした。

会津大学生は黒歴史が好きです

思わず笑い合いました。

ちなみに、他のチームとは黒歴史のネタがかぶらなかったです。奇跡かな。

さいごに

懇親会で他の参加者の方とお話をしていたら

黒歴史ブロックチェーン、僕は投票しました!」

という声を結構聞いて嬉しかったです。やっぱり他の人からフィードバックを受けるというのは気持ちがいいです。

ブロックチェーンをフルタイムで働きながらも作ってくれたのあくん

のあくんのひどい彼女イキリに耐えながら完璧なネイティブアプリをつくってくれたマヤミトくん

最後の最後でマヤミトくんがキレながらのあくんに 「は!?!?」 と言っていたのは見ててめちゃくちゃ面白かったです。

二人のおかげでとっても面白いものが作れました。ありがとうございました。

おしらせ

お仕事をください

2019春インターン振り返り ~アカツキさん編~

基本的な情報

  • 参加させていただいたところ
  • インターンの内容
    • ゲーム内仮想通貨管理サービスのサーバー周り
  • 期間
    • 3/11 ~ 3/29 (三週間)
    • 15営業日分

やったこと(細かく)

  • 課金基盤のサーバー、インフラ周りの開発

    • 使った技術
      • Go (サーバー)
      • Terraform (インフラ)
      • GCP (TaskQueue, GAE, Stackdriver)
  • shellscript, yamlファイルのlinterをCircleCIに入れ込む

    • 使った技術
      • CircleCI (CI)
      • shellcheck (linter)
      • yamllint (linter)

はじめに

自己紹介

こんにちは、飛ばすはとバスと申します。この度はアカツキさんで課金基盤のサーバサイドエンジニアとしてインターンをさせていただきました。いつも僕はGoとPythonで色々な物を作っていますが、インターンとして大きなプロダクトの中でGoを書くのはほぼ初めてのインターンになりました。

インターンに参加したきっかけ

僕は会津大学という、東京駅から行くと新幹線とローカル線で3時間ほどかかる山の中の大学で勉強をしています。会津大学は色々と珍しい大学で、公立では珍しいコンピュータ専門の単科大学です。

会津大学では、たまに企業の方々を招いてLT会を開くことがあります。

www.facebook.com

アカツキさんを知ったのは学部2年のときのLT会でした。今回のインターンでメンターをしていただいたsachaosさんがLTをしていて、「面白そうな会社だなぁ」と感じたのが当時の印象です。

Goを書き始めたのは大学三年の夏で、春休みになったしインターンでGo/サーバーの知識を深めたいと思っていたところでした。そのときにちょうどアカツキさんから「サーバーサイド(Go)のインターンに参加しませんか?」という声をかけていただけたので、参加させていただきました。

LT会などで、どのような物を作っているかどのような人たちがいるかというのがある程度わかった状態で参加できるので、安心して業務に携わることができました。

インターンの内容

バックグラウンド

アカツキさんでは、様々なタイトルのゲームを配信しています。その中でも特に大きな役割を持っているのは「課金」の部分です。 お金という大きなものを動かすために、安定性や信頼性が必要になります。 各タイトルで課金の部分を実装してもいいのですが、タイトルごとに課金部分の実装に違いが出てしまうと、いざ障害が出た時などに解決に時間がかかる可能性があります。

課金の機能をマイクロサービスとして切り出すことによってこれを解決しています。今まではアプリごとに作っていた課金周りの機能を基盤としてまとめることで障害の対応や、プラットフォームのストアの仕様のアップデートへの追従などの開発・運用をやりやすくしています。

参加したプロジェクト

マイクロサービス化した課金基盤の開発にジョインさせていただきました。今回の記事では課金基盤の開発についてお話をします。

サービスの裏側

このサービスでは、課金の情報をBigQueryに保存しています。

f:id:flying_hato_bus:20190328135541j:plain

タイトルが大きくなれば、必然的に課金も増え、BigQueryに入ってくる課金のデータも多くなります。

f:id:flying_hato_bus:20190328135630j:plain

BigQueryにデータをinsertする処理はレイテンシが高く、これにリソースを割いてはレスポンスが悪くなります。ここで登場するのがTaskQueueです。

f:id:flying_hato_bus:20190328135745p:plain

タスクキューを使用すると、アプリケーションは、タスクと呼ばれる作業をユーザー リクエストの外で非同期に実行できます。アプリがバックグラウンドで作業を実行する必要がある場合、タスクがタスクキューに追加されます。

cloud.google.com

つまり、処理にコストのかかるinsertをTaskQueueで非同期で処理をすることで、リクエスト内でinsertにかかる時間をなくすことができ、レスポンスの向上ができます。 また、TaskQueueは処理が失敗すると自動でQueueの末尾にTaskを追加し、何回でも再実行されます。これによってデータが闇に葬り去られることもなく、安定してデータを保存することができます。

僕がやったタスク

初日のメンターさんとの1on1で、インターンの中で僕が何をするかを決めました。内容としては、「TaskQueue内のレイテンシとリトライ回数を計測する」というものでした。

f:id:flying_hato_bus:20190328135957j:plain

実現したい理由

理由としては、時間やリトライ回数を計測することでどれくらい処理が重いかということを見たかったため。 TaskQueueに入った時間と、TaskQueueできちんと処理された時間の差分で時間を計測することにしました。

実装

TaskQueueに入っている時間を取得するためには (TaskQueueで処理が成功した時間) - (TaskQueueにそのタスクが入ってきた時間) で取得ができます。

また、リトライ数の取得についてはGAEがタスクをpushする際にヘッダに情報を込めて送ってくるので、これを取得することで可能でした。

ヘッダに込められている情報

  • X-AppEngine-QueueName
    • キューの名前
  • X-AppEngine-TaskName
    • タスクの名前
  • X-AppEngine-TaskRetryCount
    • このタスクが再試行された回数。この試行回数には、インスタンス数不足が原因でタスクが異常終了した試行も含まれる。
  • X-AppEngine-TaskExecutionCount
    • このタスクがこれまでに異常終了した回数。この回数には、インスタンス数不足が原因の失敗は含まれない
  • X-AppEngine-TaskETA
    • タスクの目標実行時間。1970 年 1 月 1 日からの秒数

cloud.google.com

この二つがStackdriver Loggingで見られるようになったので、Terraformを書きました。

TerraformはIaCの一つで、インスタンスなどをコードで管理しようというものです。 これを使って、上で作ったログから数値を拾ってきてログの指標にしたり、値が閾値を超えたらアラートを作成できるようになります。

個人の開発ではなかなか使うものではないですが、最近の流行りとして色々な企業で導入されていたり、色々なサービスがTerraformに対応をし始めたりと注目されていて、僕自身、Terraformに触れてみたいと感じていました。

どちらとも、先日のデプロイで本番環境で走ることになり、とてもいい体験になりました。

インターン中の生活

会津大学生は、夏休みや春休みなど、長期休みになると出稼ぎに東京へ来るのがよくあります。 今回のインターン中に、出稼ぎに来ている同じ大学の人とご飯に行ったり、古くからの友達と飲んだりと楽しい生活を送ることができました。

社員の方にお昼に誘っていただいたり、同じインターン生同士でご飯に行ったりと社内での交流も盛んで、働いていても毎日が楽しいインターンでした。

最後に

普通の大学生では作るor触れられないような大きなサービスの開発を経験して、とても勉強になりました。

3週間という期間の中で多くのことを学ぶことができ、それでいて楽しい毎日を過ごせたような気がします。 インターン期間中にお世話になったメンターのsachaosさん、インターンを調整してくださった人事の方々、同じチームの皆さんなど、多くの方に感謝の気持ちを込め、ここで今回の記事を閉めようと思います。

本当にありがとうございました。

年末の振り返り

どうもみなさまこんばんは、はとバスです。

今私は川崎にある友人宅でRAIZINと無人島生活をローテーションしながら、紅白で椎名林檎ユーミンを見ていたら堀口がベラトールの首を取っていたのを見逃してしまい身悶えしています。

なんかみんな年末の振り返りをしているので、かるーく振り返ります。

インターン関連

春と夏の二回行きました。

春はネオス株式会社さんにお邪魔させて頂き、機械学習系のインターンに参加。

夏は株式会社リクルートさんにお邪魔させていただきました、こちらはGolangによるサーバーサイドのインターンに参加しました。

hatobus.hatenablog.jp

最近年収が一つの単位になった方に「安定の飯インターン」と言われましたが、それ以外にもGolangの大量の知見を得られ、今に至っています。

技術力の低い私をインターンに受け入れていただいた関係者の方々には厚くお礼を申し上げます。

今後の学生生活の中で幅広く使えるような様々な知識を学ぶことができ、最高のインターンでした!

賞を受賞

夏に作った農業とITを繋げるシステム、「SmartAgri」というシステムがあります。

ビニールハウスや農場にESP32で作成したデバイスを設置、温度や湿度をサーバーに送り、AmazonのAlexaを使い情報を取得できるようなシステムです。

  • 農場に置くセンシングデバイスHANIYAMA
  • Golang+MySQLのサーバーサイドアプリ OHJIN
  • アレクサスキルの UKEMOCHI

の3つのOSSによってこのシステムは動いています。

f:id:flying_hato_bus:20181231225928j:plain
SmartAgriのシステム構成図

サーバーとスキルは全てGolangで作成、これも夏インターンで学んだことの復習も兼ねて作成したものでした。

この度、私と農家の方、そして会津にあるIT企業の方の三人で作成したシステムが

というとても栄誉のある2つの賞をいただける運びとなりました。

www.minpo.jp

f:id:flying_hato_bus:20181231230642p:plain
受賞者一覧(福島民報のサイトより引用)

ICTスタートアップ支援センター | 会津産IT技術認定会議 〜 募集案内 | NICT-情報通信研究機構

SmartAgri

一年生の時からずっとtwitterなどに投稿してきた謎の電子工作の花が開いた瞬間かなと思います。

自分の作ったシステムを実際に農家の方に使って頂き「今までのスマホアプリやデバイスにはない利便性」「無限の可能性を感じる」などのお言葉を頂き、とても嬉しかったですし、自分の持てる力をもってして、農家の方々の問題を解決することができるということを知れ、とても有意義なものとなりました。

もう少し細かく知りたい場合は公式サイトがありますのでこちらをどうぞ。

SmartAgri

色々なものを考えるのが増えた

いよいよ大学三年生にもなり、必然的に考え事や思うことが多くなりました。

後輩や自分の進退について考える1年になったかなと思います。

自分の進退

自分の進退としては、学部で就職することを決めました。

自分は進学したいという気持ちがやまやまですが、親の意向 「それ以上勉強して何になるんだというジジババのコメント」 シンプルにお金がない! も相まって就職という形を取らせていただきました。

どうしてももっと勉強したいという気持ちがあるならば、そういうサポートをしていただけるような企業に入り、大学院のほうで勉強しながら働くという道もありますし、自分で勉強ということもできます。

幸いにも、就活の方でいくつかの内定を頂くことができたので最悪の結果は避けられるかなと言う感じです。

個人的には大学で計算機科学を学ぶことができたので、この時代のIT企業のトップクラスのGoogleとかをダメ元で受けてみようかなと思います。

あとGSoCに応募もしたいです。

後輩

後輩のことについては、一年生と一緒に行ったハッカソンが大きいです。

今年の夏の石巻ハッカソンで、今年はマヤミトくんと一ノ瀬くんとで参加しました。

twitter.com

twitter.com

一ノ瀬くんは去年も一緒に参戦した現役高校生です。一方で大学に入ったばかりのマヤミトくんは今回でハッカソン初参戦という感じでした。

blog.github.com

ここでなんとマヤミトくんが所属していたチームが「ヤバイで賞」を受賞、彼自身初めてのハッカソンで、それも技術的に素晴らしいと認められての入賞です。お知り合いのフィッシュさんとの一緒のチームでの受賞でした。

この後のマヤミトくんは外部のLTへの登壇や勉強会への参加というように、怒涛の勢いで成長をしています。

彼のこのような成長や行動を見ていると

「やっぱりハッカソンに連れてきてよかったな」

と心の底から思うことができました。

このような経験や体験により「後輩のためになるような行動をしよう」「後輩に憧れられるような先輩になろう」という気持ちが生まれました。

来年入ってくる学生や、今いる学生の模範となるようなことや、後輩などのサポートをしていけたらなと思います。

改めて 来年の目標

  • 後輩のためになるようなことをする
  • GSoCに応募
  • 日々のアウトプットも忘れない

この3つを念頭に置いて行きていきたいと思います。

最後に

最近Thinkpadを買いました。お金が無いけど頑張って働きます。

それでは、良いお年を。

AzureのFace APIを使って提供目を自動生成

本日の記事はAizu Advent Calendarの15日目の記事になっています。

adventar.org




記事を書いてほしい問題

このアドベントカレンダーを作ったのは僕なんですが

f:id:flying_hato_bus:20181212194915p:plain













みなさん













f:id:flying_hato_bus:20181212200140j:plain

記事書いてなさすぎじゃないですか!?!?!?!???













この問題割と深刻で、半数近くの人がきちんと書いてないです。













f:id:flying_hato_bus:20181212194915p:plain

ちゃんと













f:id:flying_hato_bus:20181212200140j:plain

書いて

本題

最初に飛ばしまくりまくりましたがどうもこんにちは、はとバスです。

f:id:flying_hato_bus:20181212194915p:plain

この画像見飽きましたよね、これ以上は使わないので勘弁してください。

こんにちは、はとバス(twitter: @flying_hato_bus)と申します。

普段僕は意味のわからないツイートをする傍ら、朝と昼と夜にご飯を食べたり、赤べこの首を振らせたりしています。 普段はGolangPythonを使ってサーバーの作成や、研究として機械学習をやったりしています。

赤べこの首を振らせる以外にも、ビニールハウス内の情報をセンサーで取得してAlexaスキルで取ってこれるようなもので新聞社から賞を頂いたりしています。



提供目について

みなさん、"提供目"ってご存知でしょうか?

dic.pixiv.net

提供目とはアニメやドラマなどがCMに入る際、アイキャッチと共に表示される『提 供』の文字が人物の目と被っている現象を指す。

提供目というのは、上にもあるように、色々なシチュエーションと出来事が重なり、目の部分に「提供」の文字が重なるということです。

例です

f:id:flying_hato_bus:20181210143413j:plain
アイカツ!での例

今回はこのように、目の部分にちょうど「提供」の文字が覆いかぶさるような画像を生成してくれるくんを作りました。

github.com

使い方

使い方ですが、必要なのはGolangの実行環境とAzureのFaceAPIキー。Golangのインストールは各自の課題だとして、Azure FaceAPIの有効化について説明します。

Azure FaceAPIの有効化

まず必要なのはAzureのアカウント登録、みなさんが持っているメールアドレスで登録できるので登録しちゃいましょう。ちなみに、今メールアドレスを登録すると20000円分のクレジットが付いてくるので、これを使って有料のプランも自分のおサイフを痛めないで使用することができます。

次にFaceAPIの有効化、アカウント登録した後にAzureのポータルで有効化できます。

AI + machinelearning をクリックして、Faceを選びます。

f:id:flying_hato_bus:20181212211633p:plain

必要な情報を色々入れればデプロイが開始されます。デプロイが終わると、FaceAPIが使えるようになります。

ここでサブスクリプションキーが生成されているので、FaceAPIのコンソールからKeyをクリックし、サブスクリプションキーを確認します。このキーが後々必要になります。

f:id:flying_hato_bus:20181212213212p:plain

githubからレポジトリを持ってくる

go getを使おう

コードの方はgithubで管理しているんですが、git cloneで持ってくるよりはgo getで取得してくる方を推奨しています。

理由なんですが、git cloneは、任意の場所にレポジトリを持ってくることができる一方でgo getではある程度決まった位置にレポジトリを持ってくるという性質からです。

go getをしてレポジトリを持ってきたときには、よっぽどで無い限り

GOPATH/src/github.com/hatobus/Teikyo

に僕のレポジトリがクローンされてきます。git cloneでは、任意の場所にクローンされるので、画像までのパスなどを解決する必要になります。go getを使えば一定の場所にインストールされるのでその心配はいらず、パスの設定などをせずにそのままで動かすことができます。

.envファイルの設定

クローンしてきたファイルの中には.env.sampleというファイルがあると思います。これはサブスクリプションキーなどを管理するためのファイルで、このファイルを.envというファイル名でコピーして、その中に先ほど取得したサブスクリプションキーを記載していきます。

URL=
KEY1=
KEY2=

ファイルの中身はこのようになっていますが、KEY1,2には、さきほど取得したサブスクリプションキーを入れます、URLはリソースを作成した場所で微妙に違いますが以下のようにすればいいでしょう。

URL=https://[location].api.cognitive.microsoft.com/face/v1.0/
KEY1=XXXXXXXXXXXXXXXXXXXX
KEY2=YYYYYYYYYYYYYYYYYYYY

実際に動かしてみる

.envファイルを書き終えればとりあえずできるはずです。

go run server.go

で動かしてみましょう。何もなければサーバーがlocalhostの8080番ポートで動いてくれるはずです。

もし8080が別プロセスなどで使用されているときには、server.goの r.Run(":8080")の部分を任意のポートに置き換えてください。

ポートが使われているかどうかを調べるには

lsof -i:[ポート番号]

で調べられます。

きちんと動いたことを確認してから、リクエストを投げます。リクエストの例です。

curl http://localhost:8080/detect -F "upload[]=@/path/to/picture1.jpg" -F "upload[]=@/path/to/picture2.jpg" -H "multipart/form-data"

このAPIでは複数の画像に対応するためにヘッダをmultipart/form-dataで処理をしています。(このためにちょっとしためんどくさいことになったりしたけど)また、jpeg画像でなければ弾かれてしまうので注意。

処理がきちんとされればpicture/output/output[n].pngに画像が生成されています。

f:id:flying_hato_bus:20181212203646p:plain f:id:flying_hato_bus:20181214192130j:plain

プログラムの解説

ここからは今回のプログラムを解説していきます。とは言っても本当に必要な部分のみになりますが。

画像フォーマットを取得する

画像のフォーマットはjpegのみを受け付けている、ファイルの終わりが.jpgになっているかなどで考えてもいいが、これだと.pngファイルの拡張子だけを.jpgに変更しただけのファイルなどの場合に死んだりする。ちゃんとやるときには、ファイルのバイナリを解析したりするのが良いみたいですが、それをよしなにしてくれるのが image.DecodeConfig

image.DecodeConfigは写真のカラータイプ、フォーマットを返すメソッド。これを使えば画像がjpegなのか、またはそれ以外のフォーマットなのかがわかります。

f, err := file.Open()
defer f.Close()

// 一回DecodeConfigでファイルをいじるとファイルが壊れるために
// 別のbufにコピーをして回避しておく
io.Copy(b, f)

_, format, err := image.DecodeConfig(b)
if err != nil {
    errch[file.Filename] = err.Error()
    b.Reset()
    break
} else if format != "jpeg" {
    errch[file.Filename] = "Filetype must be jpeg"
    b.Reset()
    break
}

画像のフォーマットを取得する部分はここ。ちなみになぜ別のbufにコピーをしているかと言うと、image.DecodeConfigの内部実装にファイルを読み込む部分があるため。一回ファイルが読み込まれているので、あとでファイルの中身を扱おうとした時にEOF errorで落ちてしまう。そのために一度バッファにコピーしておくことでそれを回避しています。

画像をデコードする

FaceAPIに画像を投げるときには、画像をデコードしないといけない、そのためにデコードする処理を挟む。

buf := new(bytes.Buffer)
// どうやらファイルの先頭までシークをしなければいけなかったっぽい
// https://stackoverflow.com/questions/32193395/golang-io-reader-issue-with-jpeg-decode-returning-eof
fstream.Seek(0, 0)

img, err := jpeg.Decode(fstream)
if err != nil {
    // シークしないとunexpected EOFで落ちる
    return buf.Bytes(), err
}

if err = jpeg.Encode(buf, img, nil); err != nil {
    return buf.Bytes(), err
}

ファイルのシーク操作ですが、画像のデータをバッファにコピーした時に、先頭の位置からずれるようです。 そのために、Seek関数を用いてファイルの先頭まで持ってきます。

提供をかぶせる部分の座標を作成

f:id:flying_hato_bus:20181212201214p:plain

Face APIのレスポンスには、顔のパーツの座標を返してきます。目、口、鼻のみならず、眉などの情報を持っています。 Golangは構造体を定義し、それを元にjsonをパースします。返ってくる情報は膨大な量があるのですが、本当に必要な情報だけに変換する処理を噛ませています。

func (fp FaceParts) ToLandmark() *Landmark {
    LM := &Landmark{}

    LM.EyeRight.TopX = fp.FaceLandmarks.EyebrowRightInner.X
    LM.EyeRight.TopY = fp.FaceLandmarks.EyebrowRightInner.Y

    LM.EyeRight.BottomX = fp.FaceLandmarks.EyebrowRightOuter.X
    LM.EyeRight.BottomY = fp.FaceLandmarks.EyeRightBottom.Y

    LM.EyeLeft.TopX = fp.FaceLandmarks.EyebrowLeftOuter.X
    LM.EyeLeft.TopY = fp.FaceLandmarks.EyebrowLeftOuter.Y

    LM.EyeLeft.BottomX = fp.FaceLandmarks.EyebrowLeftInner.X
    LM.EyeLeft.BottomY = fp.FaceLandmarks.EyeLeftBottom.Y

    return LM
}

見て分かると思うんですが、実は提供の字をかぶせているのは眉の情報を元にしています。 これから説明していくんですが、文字だけではわかりづらいと思うので

f:id:flying_hato_bus:20181214193017j:plain

千鳥のノブで説明します。

まず、やりたいこととしてはこういうことにしたい。 目の上にいい感じに乗せてあげたいです。

f:id:flying_hato_bus:20181214193628j:plain

しかし、Face APIは賢いんで、目に関してはこんな感じで情報を返してきます。

f:id:flying_hato_bus:20181214194058j:plain





賢すぎるんじゃあ!!!!!





このまま提供を重ねてしまうと、

f:id:flying_hato_bus:20181214195501j:plain

まあ、うん... 間違いとは言えないけどちょっと違うよね... もっとこう... 目の全体を覆うように...

どうしようかと思った時に、返ってくるのは目の情報だけではないことに気づきました。

そう、眉の情報も使えば良い

f:id:flying_hato_bus:20181214194955j:plain

こう考えれば良いんです。

そういうわけで

提の字の始まり(左上の点) = (左眉の外側の点, 左眉の一番低い点)
提の時の終わり(右下の点) = (左目の内側の点, 左目の一番下の点)

供の字の始まり(左上の点) = (右眉の内側の点, 右眉の一番低い点)
供の時の終わり(右下の点) = (右目の外側の点, 右目の一番下の点)

という4つの点で「提供」の字をかぶせています。

図で示すとこんな感じ

f:id:flying_hato_bus:20181214201623p:plain

さらにこれで画像を書き出すとこんな感じになります。

f:id:flying_hato_bus:20181214201146p:plain

アルゴリズム的にはこんな感じで目のところにかぶせているというものでした。

最後に

これを作ったのが今週の頭くらいで、「作ったよ〜」みたいなノリでtwitterにあげてみました。

ちょっとバズった。

中には僕のガバガバ英文がお気に召してくれた方がおり

みんなの心温めるコンテンツレーベルになれたかなと思います。

ちなみにこれのライセンスは SUSHI-WARE なので、何か使いたいときがあれば僕に寿司をおごってください。

github.com

参考にさせていただいたサイト

画像をリサイズする

[http://dempatow.hatenablog.com/entry/2016/11/17/画像をリサイズしてDBへ保存する/golang:embed:cite]

Seekしないと落ちる問題

stackoverflow.com

デコードした後にファイルがぶっ壊れる問題

suguru03.hatenablog.com

以上です、悪ふざけにお付き合い頂きありがとうございました。

go-xormを使ってスキーマからstructの自動生成

Golangは静的型付け言語なのでjsonをパースするときとかにはけっこう骨が折れる。

何行もstructを書かなくてはいけないのは結構めんどくさくて、JSON-to-Goとかでjson用のstructを自動生成して書きがち。

DBとかのスキーマも同じで、structの後ろの属性等で色々と設定しなくてはいけないことが多々ある。

別に 俺は全部手書きでやってやらあ!!! という人はいいんですが、僕はめんどくさがり屋なので色々と自動生成してくれる君を使うことが多い。

今回は、そのようなスキーマからstructを自動生成してくれるものの使い方を自分のメモ代わりに書く。

使うもの

go-xormのコマンドラインツールを使う。

xormはGolangのORMモデルの一つで、エウレカさんが作った、マッチングアプリのペアーズなどでも実際に使われているある程度メジャーなもの。

今回はコマンドラインツールの方を使う。

インストール

$ go get github.com/go-xorm/cmd/xorm

これで基本はOK。

もし以前にgo-xormを入れていて、

 cannot use "github.com/go-xorm/cmd/xorm/vendor/github.com/go-xorm/core".LOG_UNKNOWN (type "github.com/go-xorm/cmd/xorm/vendor/github.com/go-xorm/core".LogLevel) as type "github.com/go-xorm/core".LogLevel in argument to engine.Logger().SetLevel

みたいなエラーが出た際には、

cmd/xorm/vendor/github.com/go-xorm/

を消して、またインストールしなおせば行けるはず。

go-xormでの同様のissue

入ったら後はやるだけ。

なお、対応しているDBは

の5種類のみ、個人的に使っているのはmysqlのみなのでいいかなという感じ。

ちなみに、Golang以外にもC++に対応している模様。

実際に使う。

使う際にはテンプレートファイルをコピーしてくる。

プロジェクトルートとかで

$ cp -r $GOPATH/src/github.com/go-xorm/cmd/xorm/templates/goxorm/ ./xorm-template

上記のコマンドを打ち、テンプレートファイルを手元に持ってくる。

テンプレートファイルから自動生成してくれるっぽい。

mysqlでの例

mysqlをローカルホストの3306で動かしていて、ユーザー名 root パスワードpasswordにしている時に、hogedbのスキーマのモデルを得るは以下のようにする。

xorm reverse mysql "root:password@tcp(localhost:3306)/hogedb" xorm-template

これでディレクトリ直下に models/hogedb.goというファイルが生成されているはず。

リクルートさんのインターンシップに参加してきて圧倒的成長をしたのか?という話

こんにちは、はとバスです。

学部三年生でみんながインターンインターン!と言っていて「今年こそは企業のインターンに行って名実ともに最強のプログラマーになるぞ!」と言う意気込みでインターンのESを書いていました。

今回はその中で実際に行くことになった株式会社リクルートさんのインターンについてお話をしようと思います。

行くきっかけ

上の方で 今年こそは企業のインターンに行って名実ともに最強のプログラマーになるぞ!とかほざいていましたが、 大体のインターンでお祈りを食らってました。

仲のいい友人たちがインターンに決まっているところを見て、日に日に貯まるお祈りメールの山を見てとても焦っていました。

しかし、その中で「ぜひともこの夏のインターンに参加して、私達と一緒にプロダクトを作って欲しい」と言われたときにはとても嬉しかったですし、とてもモチベーションのアップになりました。

その企業さんこそが、今回のインターンシップを受け入れてくれた、株式会社リクルートさんでした。

www.recruit-jinji.jp

それはそうとこれは銀座の中華料理店で食べた小籠包です。

f:id:flying_hato_bus:20180930115313j:plain

実際に参加するまで

コーディングテスト

応募する時にしたのはコーディングテストと面談が2,3回。

コーディングテストはSQL、プログラミングのいろいろな知識、数学的知識など色々聞かれましたがAPIしか叩けませんでした。

面談

面談の方ですが、最初は大学での生活や普段しているプログラミングの内容を聞かれたり、今回どこらへんのタームで参加できるかの旨を聞かれました。

面談らしい面談です。一般人を装いながら面談をすればだいたい通るはずです。(ほんとか?)

最初の面談はある程度人間性を兼ね備えた人間ならば通ると思います。

鬼門は最終面談。

サービスを新たに作るとして、そのサービスに必要なものを全て列挙、またそれを使ってどういう流れでサービスが提供されるか

という実務経験やそれなりにしっかりと勉強をしていないと速攻で詰む問題だと思いました。

応募する時にマイケルさん@mic_psmが「エントリーシートに僕の名前書いていいよ」と言ってくださり、しっかりと記名。

面談のときなどにマイケルさんの名前に何度か助けられた部分もありました。

終結果が出るのは7月下旬から8月上旬にかけて、僕は大学の授業の真っ只中である7月下旬の論理回路の授業中に決定の連絡が来ました。

いろいろな会社と面談をして、来るメールがお祈りメールばかりで自己肯定感のNASAというところでこの決定の連絡が来たので本当に嬉しかった。

それはそうと、これは銀座で食べた海鮮丼です。

f:id:flying_hato_bus:20180930115426j:plain

実際にインターンに参加するまで

実際にインターンに行くのは9/3から。

その前に、メンターさんたちと実際に会って面談をしました。内容的にはインターン内容について。

インターン内容はある程度エントリーシートには書いていましたが、この面談で実際の内容を決めました。

実際に会って話をしないと何がしたいのか分からないしね。しょうがないよね。

エントリーシートには「サーバーサイドに関することをしたい」という旨を書いていたので、インターンはサーバーサイドの内容をすることになりました。

コレ以外にもネイティブアプリなどの内容があったので、人それぞれやりたいことをやらせてくれる感じでした。

メンターさんたちと面談をしたのはヒューリック銀座7丁目ビル。

goo.gl

道行く車が黒塗りのAMGやレクサス(4桁万円)ばっかりで「コレが勝ち組か...」という感じです。業務する場所は人それぞれ、東京駅のサウスタワー、目黒、ここヒューリック銀座など人や業務によって様々。

それはそうと、これは銀座の蕎麦屋で食べたもりそばのカレーセットです。

f:id:flying_hato_bus:20180930120236j:plain

インターン内容

インターン内容は NDANDANDA するためのツールの NDA に関する部分のサーバーサイドをGolangで実装しました。

詳しい内容はNDAなので言えないです。仕方ないね。

NDAに触れない部分を言うと、メンターさんたちがとてもすごかったことです。Golangの知識はHello Worldとのあくんのコードを「ワカラナイ...ワカラナイ...」と言いながら書いてただけの僕に対し、 何がわからないか、何を学ぶべきかということをしっかりと見つけ、それを達成するために最短でタスクを割り振ってくれたり、わからないところを教えてくれたりと、圧倒的な情報量とともに教えてくれていただきました。

それはそうと、これは亀戸で食べた亀戸ホルモンです。

f:id:flying_hato_bus:20180930121735j:plain

社内の雰囲気について

社内にいるエンジニアの方は優秀な方が多い印象です。優秀な方が多くとも怖い人がいっぱいいるわけではなく、分からないことを気軽に聞けたり、自分がやりたいことをやらせてくれたりと和やかな雰囲気がありました。

リクルートさん独特の文化として よもやま というものがあります。

よもやま

よもやまとは「よもやま話」のことです。どうでもよいことを話す、他愛もない話という内容ですが、これをメンターさんと毎日しました。今日は何をしたか、今後何をするかなどを話ししていました。もちろん場所は自由で、オフィスでやる以外にも、近くのコーヒーショップでやったりもできます。また、週に一回人事の方ともよもやまをし、インターンの進捗やいろいろな話などをしていました。

自由な場所で自由な話をするというのは発想が生まれたり、何をしたかという確認ができてとても良かったです。

リクルートさんのエンジニアはけっこうな頻度でやっているらしく、直接関わりがない人などでもどんどんよもまやすることができます。その道で有名な人にコードレビューを頼むためによもやまを入れたり、どのように開発をするか聞くためによもやまを入れているそうです。

実際に、エンジニアの方々が集うよもやまにいくつか参加させていただきました。

Node.jsのコミッターである古川さん(Twitter)とのよもやまのときには、サロゲートペアの話をしていただいたりしたなど、一端の大学生ができないような体験もできました。

イベント

技術共有会

所属部署を問わず、エンジニアが自分の取り組みや技術的に困っていることについて気軽に、オープンに共有・相談出来る場として隔週で実施されている会です。 これまで各部のそれぞれで勉強会などを行ってきましたが、日常の仕事の文脈に関係ある・なしを問わず、エンジニアが自分の興味関心あるテーマについて自由闊達に語る/相談する/共有することで、エンジニア間の技術を介した関係性の構築や、最新の技術動向に対する組織としての感度や視座を高めることを目的としています。

recruit-tech.co.jp

技術共有会にも参加しました。また、一部のインターン生はこの中で実際にLTをし、僕も実際にこの場でLTしました。

LT以外にも、リクルートさんで働くトップのエンジニア数名の方に、インターン生が質問をぶつけることができ、インターン生からの質問にtakesakoさんやt_wadaさんがいろいろと答えていました。今後のエンジニアの形や、セキュリティの話などが

LTの内容ですが、技術共有会の前におりさのさんが話をしていた「文字」についてLTをしました。

https://speakerdeck.com/hatobus/unicodefalsean-huo-yong-fa-karasheng-cheng-made

リンクを踏むとLTのスライドが見られます。

インターン生LT会

技術共有会でのLT以外にも、インターン生とそのメンターさんのみでのLT会もありました。自動微分とかXRの話とか、自分が興味を持っている分野以外にも自分があまり触れたことのない技術の話も聞けてとても勉強になった気がします。人事の方が作った「パソコンチョットデキル」Tシャツももらえたりします。

ちなみに、インターン生LT会にはお寿司が出ます。

銀のさら

f:id:flying_hato_bus:20180930145433j:plain

インターン生について

インターンに参加した学生は自分を含めて10名、他の大学の人は某東京大学とか、某京都大学などの旧帝や筑波大学関関同立の方などが多く、勉強では間違っても勝てないような人たちばかりでした。また、B3も僕を含め2,3人程度、他の方は総じて院生の方でした。最初に自己紹介をしたとき、自分がとんでもなく場違いなところに来たなあと感じました。実際のところ、インターン生はいろいろなところに散らばるので一緒に作業をしたりはしません、技術で殴ればいいんです。(殴るような技術もないけれど)

インターン生の方とは業務中に一緒に食事に行ったり、ホテルに泊まっている方とともに亀戸で餃子を食べたりといろいろなことができました。個性的な人たちが多く、やっていることが様々なので、いろいろな話を聞くことも勉強になりました。

ランチ

最高でした。毎日お昼が楽しみでした。

ここからはうじまるくんのツイートと、ご飯の写真をお楽しみください。

twitter.com

f:id:flying_hato_bus:20180930152220j:plain f:id:flying_hato_bus:20180930152238j:plain

f:id:flying_hato_bus:20180930152346j:plain

f:id:flying_hato_bus:20180930152458j:plain

f:id:flying_hato_bus:20180930152837j:plain

f:id:flying_hato_bus:20180930120236j:plain

f:id:flying_hato_bus:20180930153736j:plain

f:id:flying_hato_bus:20180930153837j:plain f:id:flying_hato_bus:20180930153853j:plain

ここまできたらなぜかうじまるくんが #今日の中指 の自動化をしていました。

f:id:flying_hato_bus:20180930154558j:plain

f:id:flying_hato_bus:20180930154629j:plain f:id:flying_hato_bus:20180930154700j:plain f:id:flying_hato_bus:20180930154747j:plain

最終日はお寿司の名店 久兵衛 に行きました。メンターさんと一緒に大興奮のお寿司です。

大学生の舌には高級すぎました😇😇😇。

f:id:flying_hato_bus:20180930154345j:plain f:id:flying_hato_bus:20180930155047j:plain f:id:flying_hato_bus:20180930155109j:plain f:id:flying_hato_bus:20180930155217j:plain f:id:flying_hato_bus:20180930155129j:plain f:id:flying_hato_bus:20180930155151j:plain f:id:flying_hato_bus:20180930155237j:plain f:id:flying_hato_bus:20180930155300j:plain

やっぱりお寿司は最高で、リクルートさんの凄まじき金銭的パワーで毎日素敵なランチにありつけました。

圧倒的感謝です🙏🙏🙏🙏🙏🙏🙏🙏🙏。

インターンを終えて

今回のインターンは大学に入ってから二回目のインターンでしたが、実際に現場で働いている人たちのそばに参加して一緒に業務をするのはやはり勉強になります。今回、サーバーサイドというあまり触れたことのない技術を学ぶという部分が多かったですが、メンターさんのタスクの振り分けや教え方がバッチリハマり、力をつけることができました。最初のほうで 自分が何を学ぶべきかということを明確にして、それを補う形でインターンできたことが要因かなと思います。

前までは、サーバーサイドというのをあまり分からず、雰囲気で話を合わせていたりしました。今現在は、まだわからない部分も結構ありますが、ある程度なら話を理解することができるようになった気がします。(だと言っても本業としている人には全然かなわないけど)

個人的にこのリクルートさんのインターンは、自分が一ヶ月で劇的に変わることができるインターンだと思います。

圧倒的当事者意識を持つというのは大きいと思います。このインターンに参加しているのは自分だ、このインターンでどう変わるかという意識を持つのは大きいと思います。

結局自分を育てるためには自分で知識を入れなければいけず、最後は自分がやるかどうかです。

意識高い感じでまとめましたが、成長するためのインターンとしてはとてもいいものだと思いました。

タイトルにもあるように、今回のインターンシップで圧倒的成長をしたのかと聞かれると、圧倒的成長をしました

今後の糧にできるような知識を多数学べ、実に有意義なインターンでした。

暮らし

朝10時出勤だからまだマシなんですが、通勤通学に中央総武線は使うものじゃないと思いました。

人間が乗るものじゃないです。

朝は9時頃に乗るのでまだ大丈夫ですが、問題は帰り。

夜7時近くに有楽町駅から乗ると、サラリーマンの人に囲まれて死ぬかと思いました。

ちなみに、私服の大学生が満員電車に乗ると、この世の全てに嫌気が差しているようなサラリーマンの方に

「遊んでいる大学生風情が電車に乗るもんじゃねえんだよ」

とか言われますが、そういう悲しい人は結構いるので心を強く持ちましょう。

だいたいそういう人は家庭とか、会社がうまく行ってません。温かい目で見守ってあげましょう。

最後に

今回、私に技術的な指導をしていただいたメンターのお二方、生活の支援やイベントの主催をしていただいた人事のお三方。その他私がお世話になった大勢の方に深くお礼を申し上げます。

一ヶ月という短い間でしたが多くの知識を蓄えることができ、感謝したくも感謝しきれません。

本当にありがとうございました。

ESP32の書き込みサイズを変更する

いろんなことに使われているESP32ですが、先日のハッカソンで渡されたサンプルプログラムがデフォルトで92%もメモリを使用するということで、何に使えるんだコレと海原雄山みたいなこと言いました。

ということで今回はそんな時に使えるESP32の書き込みができるファイルサイズを変更する方法です。

やること

ESP32の書き込みメモリのサイズを変更する

工程

  1. 設定ファイルの場所を特定する
  2. 電卓とかで増やすサイズを割り出す
  3. 設定ファイルを書き換え
  4. テストとして実際に書き込みしてみる

実際に変更してみる

設定ファイルを見つける

まずは設定ファイルの場所を特定する必要がある。

ちなみに、今回使用したOSはLinux(xubuntu)です。他のOSを使用している方とはファイルの場所などに違いがある場合があるので注意してください。

僕の場合、設定ファイルは

~/Arduino/hardware/espressif/esp32/tools/partitions/default.csv に存在しました。

このcsvファイルに色々と情報が書き込まれていて、実際にプログラムが書き込まれるときなどにコレが参照されるそうです。

# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x5000,
otadata,  data, ota,     0xe000,  0x2000,
app0,     app,  ota_0,   0x10000, 0x140000,
app1,     app,  ota_1,   0x150000,0x140000,
eeprom,   data, 0x99,    0x290000,0x1000,
spiffs,   data, spiffs,  0x291000,0x16F000,

初期の値はこうなっていて、0x140000 = 1310720 bytesだけ書き込めるようになっている。

nvsとotadataをいじってしまうと、動作が不安定になるので、よっぽどのことがなければotadata下の4行分データを変更します。

ちなみに、サイズを変更する時は 0x10000 ごとに変更をしないと怒られます。コンパイルエラーが出たときにも言われるので注意しましょう。

変更するサイズを決める

1310720 bytes ~ 1.3MBになるので、今回は1.5MBくらいまで増やすとこにしようと思います。

1500000 = 0x16E360になるので、1.5MBにするにはサイズを0x140000から0x1700000にすれば良さそうというのがわかります。

ちなみに 0x170000 = 1507328 > 1500000なので多分あってます。

というわけで変更していきます。

実際に設定ファイルを変更する

windowsのメモ帳でやる人はまさかいないと思うんですが、文字コードの影響とかでバグが起きそうなのでできるだけやめてください。

変更するときにはoffsetやsizeを逐次計算していったほうがいいです。

offsetはたぶんメモリ番地、sizeはその場所が専有しているサイズになります。

サイズを0x170000にする時はこうなります。

# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x5000,
otadata,  data, ota,     0xe000,  0x2000,
app0,     app,  ota_0,   0x10000, 0x170000,
app1,     app,  ota_1,   0x180000,0x170000,
eeprom,   data, 0x99,    0x2F0000,0x1000,
spiffs,   data, spiffs,  0x2F1000,0x0DF900,

ポイントとしてはoffsetの値にsizeを加えた数が次の場所のoffsetの番地になることです。

このブログを見ている皆さんは16進数同士の掛け算も秒でできる hexadecimal native な方も多いでしょう、しかし僕は普通の人なのでPCに搭載されている電卓を叩きました。

ちなみに、ESP32自体のメモリサイズは4MBなので、最後のspiffsの行にあるOffsetとSizeの値の和が 0x3D900 = 4000000になれば成功です。

もしそうでなかったらどこか間違っている場所があるのでよく確認してください。

ちなみに、appの部分を変更したためにspiffsの値が減っていますが動作に問題はありません。戦争のための致し方ない犠牲だ。

boards.txtの変更

実際に書き込めたら、もう一つファイルを変更します。

変更するのはesp32のルートにある boards.txt

場所としては~/Arduino/hardware/espressif/esp32にありました。

このファイルの esp32.upload.maximum_sizeの部分を変更します。今回は0x30000増やしたので

esp32.upload.maximum_size = 1507328に変更します。

実際に確認してみる

適当に何かファイルを書き込みます。

簡単にLチカなどでも大丈夫です。

#define LEDPIN 13

void setup{
    pinMode(LEDPIN, OUT);
}

void loop() {
  digitalWrite(led, HIGH);
  delay(1000);

  digitalWrite(led, LOW);
  delay(1000);
}

あとは書き込むと、書き込んだ後に出るメッセージが変わると思います。

変わればきちんと変更がされたので終わり、お疲れ様でした。