0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

知識ゼロ人間がGeminiと壁打ちしてM5StackでIoTデバイスを作ったドキュメンタリー(3)実装とバグ修正編

0
Posted at

Part2では、開発環境の構築と、バージョン違いによるminiz.hエラーを乗り越えるまでをお届けしました。Part3では、いよいよ3ボタン通知機能の実装と、実機で発覚した2つのバグについてまとめます。

実装編:3ボタン通知機能

🧐私:右のボタンを押したら"今から帰るよー"って通知してほしい。左のボタンを押したら"もう少し遅くなる"まんなかのボタンを押したら"まだわからない"って通知をおくるようにして

コードをもらって実機で試したところ、無事に動作しました。

🧐私:うまくいったよ

つまずき④:ボタンを押していないのに通知が連射される

ところが、しばらく置いておくと想定外の現象が起きました。

🧐私:ボタンを押さずに置いたら、一定間隔で"もう少し遅くなる"が送信されるんだけどなぜ?

🤖Gemini先生:それは、コードの書き方としてよくやってしまう「条件の選び方のミス」だね。M5Stackのボタンライブラリには、主に次の2つの判定方法があるんだ。

1. 継続的に状態を拾ってしまう書き方(NGだった原因)

if (M5.BtnA.isPressed()) {
  // ボタンが「今、押されている」ならずっと実行
  sendDiscordMessage("もう少し遅くなる");
}

isPressed()は「今、ボタンが押し込まれていますか?」という現在の状態をYESかNOで答えるだけの関数です。マイコンの処理速度は非常に速く、loop()の中を1秒間に何千回、何万回とぐるぐる回っています。人間が「ポンッ」と一瞬だけボタンを押したつもりでも、マイコン側では何百回もisPressed() = YESの状態を検知してしまい、その結果メッセージが連続送信されることになります。

2.「押された瞬間」だけを狙い撃ちする書き方(修正後の正解)

if (M5.BtnA.wasPressed()) {
  // ボタンが「新しく押された瞬間」だけ1回実行
  sendDiscordMessage("もう少し遅くなる");
}

wasPressed()は「前回のチェックから今回のチェックの間に、新しく押された瞬間(変化)があったか」を答える関数です。指がボタンの上に乗ったままでも、新しく押された瞬間は最初の1回だけなので、通知は1回だけしか飛ばなくなります。

イメージでいうと、isPressed()(状態)は先生が「今、手を挙げている人〜?」と聞き続けている状態。手を挙げている間、毎回「はい!」と返事をしなければなりません。一方wasPressed()(変化)は「新しく手を挙げた人いる〜?」と聞く状態で、すでに挙げている人は無視され、新しく手を挙げた人だけが指名されます。

isPressed()wasPressed()の仕様は、M5Stack公式のボタンライブラリ(M5Unified/M5Stackライブラリ)のドキュメントに基づく説明です。

つまずき⑤:Wi-Fiが最初の1回しかつながらない

最後の壁がこちらでした。

🧐私:Wi-Fiにずっとつなげることができないみたい。一回目の送信はうまくいくんだけど

🤖Gemini先生:学校やフリーWi-Fi特有の「無通信状態が続くと自動で切断される仕様」が原因ですね。実は今のコードは、最初に電源を入れた時(setupの中)にしかWi-Fiに繋ぐ処理をしていません。

🧐私:じゃあ、切れたらもう一度繋ぎ直すようにしないといけないのか。どうやればいいの?

🤖Gemini先生:通知を送る直前に毎回「今、Wi-Fiに繋がってる?」と確認し、切れていたら「繋ぎ直す機能」を呼び出すように設計を変更しましょう。具体的には、以下の3つのステップでコードを改修しました。

1. Wi-Fi接続の専用機能(関数)を作る

最初はsetup()の中に直接書いていたWi-Fi接続のコードを、いつでもどこからでも呼び出せるようにconnectWiFi()という独立した機能として切り出しました。

// Wi-Fiに接続する専用の機能(切れた時もこれを呼び出す)
void connectWiFi() {
  WiFi.begin(ssid, password);

  // 繋がるまで(WL_CONNECTEDになるまで)待つ
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    M5.Lcd.print(".");
  }
}

2. WiFi.status()で今の状態をチェックする

ESP32には、現在のWi-Fiの状況を教えてくれるWiFi.status()という命令があります。これがWL_CONNECTED(繋がっている状態)かどうかを調べます。

3. 送信する「直前」にチェックする(ここがポイント)

メッセージを送信する機能(sendDiscordMessage)の一番最初に、以下のようなチェック処理を挟み込みました。

void sendDiscordMessage(String msg) {
  // 送信前に、Wi-Fiが切れていないかチェック
  if (WiFi.status() != WL_CONNECTED) {
    // もし切れていたら、専用機能を呼び出して再接続する
    connectWiFi();
  }

  // ここから下に、Discordへ送信する本番の処理を書く
  if (WiFi.status() == WL_CONNECTED) {
      // (Discordへの送信処理...)
  }
}

🧐私:なんでloop()の中でずっとチェックし続けるんじゃなくて、送信する直前にチェックする設計にしたの?

🤖Gemini先生:loop()の中で常に「繋がってる?」とチェックし続けることも可能ですが、それだとマイコンに負担がかかり、バッテリーも余分に消費してしまいます。今回は「ボタンを押して送信する時」にしかインターネットを使わないので、送信ボタンが押された瞬間にだけ確認し、切れていたら繋ぎ直してから送るという設計(Just-in-Timeの処理)が、一番無駄がないんです。

loop()内で常時監視する設計と、送信直前のみチェックする設計はどちらも実装可能で、どちらを選ぶかは消費電力や処理負荷とのトレードオフになる、という点は補足しておきます。

これにより、何回ボタンを押しても安定して通知が届くようになりました。

次回Part4(最終回)では、Geminiと組んで見えた「AI協働開発」の面白さと、今後の展望についてお届けします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?