数日ぶりに CloudWatch ダッシュボードを見たら、グラフが3日前で止まっていました。ESP32 を確認しに行くと、LED が消えて完全に無反応。リセットボタンを押しても復帰しません。
目次
まず疑ったのは AWS 側
グラフが止まっているのを見て最初に思ったのは「Lambda かAPI Gateway側で何か起きたのでは」でした。ですが調べてみると、前回までに組んだ気象庁データの取得は普通に動き続けています。ESP32 からのリクエストを受けている Lambda のログを確認すると、最後の正常なリクエストを最後にぴたりと止まっていて、それ以降はエラーログすら1件もありません。
最後の成功データ: 2026-09-15 22:12:19 JST
以降3日間、API Gateway へのリクエストが一切なし(成功・失敗問わず)
停止直前のログも見てみましたが、送信間隔(約61.5秒)も気圧・温度の値も、それまでと変わらず安定していました。じわじわ悪化した形跡はなく、ある瞬間に突然止まっている。AWS 側は完全にクリーンだったので、問題はESP32側にあると判断できました。
LEDは消灯、リセットボタンも効かない
現地(といっても机の上ですが)で確認すると、LED は消灯。電源は来ているはずなのに反応がありません。リセットボタンを押しても復帰せず、USB を一度抜いて挿し直す(電源断→再投入)ことで、ようやく起動しました。
起動後のログは完全に正常で、Wi-Fi 再接続も BMP180 の初期化も問題なし。ハードウェア自体は壊れておらず、ソフトウェアが完全にハングしていたと考えるのが自然です。原因の特定は今回の1回の情報だけでは難しいですが(ESP32 の Wi-Fi ライブラリは長時間稼働でのハングが度々報告されています)、屋外設置後にこれが起きたら手も足も出ません。運用しているのは自分ひとりなので、自動復帰の仕組みが必須だと痛感しました。
ハードウェアウォッチドッグを仕込む
ESP32 には Task Watchdog Timer(TWDT)というハードウェア機構があります。特定のタスクが一定時間内に「生きている」と報告(feed)しなければ、強制的にリセットしてくれます。
実装はシンプルです。
#include <esp_task_wdt.h>
constexpr uint32_t WDT_TIMEOUT_S = 120;
void setup() {
// ...
esp_task_wdt_init(WDT_TIMEOUT_S, true);
esp_task_wdt_add(NULL); // 現在のタスク(loop)を登録
// ...
}
void loop() {
esp_task_wdt_reset(); // 周期の先頭でfeed
// センサー読み取り・AWS送信・60秒スリープ
}
loop() の先頭で1回 feed するだけです。センサー読み取り・HTTPS送信・60秒のスリープを含めた一周が、実測でおよそ61〜62秒。タイムアウトを120秒に設定したので、正常時には2倍近い余裕があります。逆に言えば、どこか(センサー読み取り中でも、HTTPS送信中でも)で120秒以上戻ってこられなければ、自動的にリセットがかかります。
ハマったところ
症状: 参考にしたネット上のサンプルコードでは esp_task_wdt_init() に構造体を渡す形(esp_task_wdt_config_t)で書かれているものが多く、そのままコピーするとコンパイルが通らない。
原因: esp_task_wdt の API は Arduino-ESP32 コアのバージョンによってシグネチャが異なる。新しいコア(ESP-IDF 5.x系)では設定用の構造体を渡す形に変わっているが、このプロジェクトで実際にインストールされているコア(framework-arduinoespressif32 @ 3.20017.x)は旧来のシンプルな2引数版(esp_task_wdt_init(uint32_t timeout, bool panic))のままだった。
解決策: ネットの情報を鵜呑みにせず、実際にインストールされているヘッダーファイル(~/.platformio/packages/framework-arduinoespressif32/.../esp_task_wdt.h)を直接確認してから実装した。
気づきにくい理由: 「ESP32のWDTの使い方」で検索して出てくる記事は書かれた時期によってAPIが違い、どちらも「正しい」情報として出てくる。自分の環境のバージョンを確認せずに実装すると、コンパイルエラーの原因が分かりにくい。
本当に動くか、わざとハングさせて確認した
実装しただけで満足せず、実際にハングした状態から復帰するかを検証しました。loop() の中に一時的に無限ループを仕込んで書き込み、シリアル出力を監視します。
esp_task_wdt_reset();
// 検証用:意図的にハングさせる
while (true) {
delay(1000);
}
約135秒後、狙い通りウォッチドッグが発動しました。
E (135620) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (135620) task_wdt: - loopTask (CPU 1)
E (135620) task_wdt: Aborting.
abort() was called at PC 0x400e0dfd on core 0
Rebooting...
再起動後は Wi-Fi 再接続・センサー初期化まで問題なく完走しました。検証用の無限ループを削除し、実機で複数周期分の正常動作も確認済みです。
まとめ
| 項目 | 内容 |
|---|---|
| 発生事象 | USB給電でのテスト稼働中、3日間の完全フリーズ。リセットボタンでは復帰せず |
| 原因切り分け | AWS側(API Gateway/Lambda)にエラーなし → ESP32側のソフトウェアハングと判断 |
| 対策 | esp_task_wdt によるハードウェアウォッチドッグ(タイムアウト120秒) |
| 検証 | 意図的なハングから約135秒後に自動リセット・正常復帰を確認 |
原因そのものの特定はできませんでしたが、「なぜ止まったか」より「止まっても自動で復帰できるか」を優先しました。屋外設置後は人の目が届かない前提で運用することになるので、これは早めに入れておいてよかったと思います。
次回
発注済みの電源部材が届き次第、ブレッドボードでの電源回路の検証に進みます。