Skip to content
まことの手帳
Go back

ESP32 でセンサーデータを AWS に送る #1 環境構築編

ESP32 で取得したセンサーデータを AWS に送って可視化する、という個人プロジェクトを始めました。

この記事はその第1回です。開発環境を構築して、ビルドが通るところまでを扱います。まだセンサーも AWS も出てきません。地味な回ですが、ここでハマると先に進めないので丁寧に書きます。

目次

使用するボード

Freenove の ESP32-WROOM Board(型番 FNK0090) を使います。2枚入りのパックです。

Freenove ESP32-WROOM Board (FNK0090) のパッケージ

搭載チップは無印の ESP32(ESP32-WROOM-32)で、Wi-Fi と Bluetooth を内蔵しています。AWS にデータを送るのに追加のモジュールが要らないのが選定理由です。

公式チュートリアルは docs.freenove.com/projects/fnk0090 にあり、C 言語版と Python 言語版の2系統が用意されています。

開発環境に PlatformIO を選んだ理由

ESP32 の開発環境にはいくつか選択肢があります。今回は PlatformIO + Arduino framework を選びました。

選択肢特徴
PlatformIO + ArduinoVS Code 統合。設定ファイルで依存を固定でき、CLI で完結する
MicroPython + ThonnyREPL で即座に試せる。センサーの動作確認が速い
Arduino IDEGUI 中心で初心者向け。設定がリポジトリに残らない
ESP-IDFEspressif 公式 SDK。低レベルだが学習コストが高い

決め手は再現性でした。

Arduino IDE はボード選択もライブラリ導入も GUI 操作なので、その設定がリポジトリに残りません。数ヶ月後に別のマシンで開いたとき、何のライブラリをどのバージョンで入れたか分からなくなります。PlatformIO は platformio.ini という設定ファイルにボード定義もライブラリのバージョンも書けるので、git で管理できます。

もうひとつは、後で AWS IoT Core に MQTT over TLS で接続する予定があることです。この用途は Arduino のライブラリ(PubSubClient + WiFiClientSecure)の事例が圧倒的に多く、詰まったときに情報を探しやすいと判断しました。

PlatformIO Core の導入

macOS なので Homebrew で入れます。

brew install platformio

これで pio コマンドが使えるようになります。

$ pio --version
PlatformIO Core, version 6.2.0

プロジェクトの作成

このプロジェクトは最終的に AWS 側の Terraform コードも持つことになります。そのため、リポジトリ直下をいきなり PlatformIO プロジェクトにせず、firmware/ という階層を切りました。

esp32/
├── firmware/    ESP32 ファームウェア(PlatformIO)
├── infra/       Terraform(AWS 側。今後作成)
└── docs/        作業記録

--board esp32dev が ESP32-WROOM-32 の標準的なボード定義です。Freenove 専用の定義は PlatformIO に無いので、この汎用定義を使います。

mkdir -p firmware
pio project init --project-dir firmware --board esp32dev

初回はツールチェーンとフレームワークを丸ごとダウンロードするので時間がかかります。最終的に ~/.platformio が 1.5GB ほどになりました。 ディスクの空きに注意してください。

導入されたのは以下の4つです。

platformio.ini の設定

生成された platformio.ini は最低限の内容なので、いくつか追記しました。

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino

; シリアル速度。src/main.cpp の Serial.begin() と一致させること
monitor_speed = 115200

; 書き込み速度。不安定な場合は 460800 や 115200 に下げる
upload_speed = 921600

; 例外発生時のスタックトレースをデコードして表示する
monitor_filters = esp32_exception_decoder

特に monitor_filters = esp32_exception_decoder は入れておくべきです。これが無いと、プログラムがクラッシュしたときにシリアルモニタには16進数のアドレスが並ぶだけで、何が起きたのか分かりません。この設定を入れると、アドレスをソースコードの行番号に変換して表示してくれます。

動作確認用のスケッチ

まずはボードが生きているかを確認するだけのコードを書きます。LED を点滅させつつ、チップの情報をシリアルに出力します。

#include <Arduino.h>

// オンボード LED。点灯しない場合は基板のシルク印刷を確認して調整する
constexpr uint8_t LED_PIN = 2;

void setup() {
  Serial.begin(115200);
  delay(1000);  // シリアルモニタの接続待ち

  pinMode(LED_PIN, OUTPUT);

  Serial.println();
  Serial.println("=== ESP32 起動 ===");
  Serial.printf("チップモデル : %s (リビジョン %d)\n", ESP.getChipModel(), ESP.getChipRevision());
  Serial.printf("CPU コア数   : %d\n", ESP.getChipCores());
  Serial.printf("Flash サイズ : %d MB\n", ESP.getFlashChipSize() / (1024 * 1024));
  Serial.println("==================");
}

void loop() {
  digitalWrite(LED_PIN, HIGH);
  Serial.println("LED: ON");
  delay(1000);

  digitalWrite(LED_PIN, LOW);
  Serial.println("LED: OFF");
  delay(1000);
}

setup() の冒頭に delay(1000) を入れているのがポイントです。ESP32 は起動が速いので、これが無いとシリアルモニタが接続する前に起動メッセージが流れてしまい、何も表示されないように見えます。

ビルド

pio run -d firmware

通りました。

RAM:   [=         ]   6.6% (used 21464 bytes from 327680 bytes)
Flash: [==        ]  20.7% (used 271045 bytes from 1310720 bytes)
Successfully created esp32 image.
========================= [SUCCESS] Took 4.06 seconds =========================

LED を光らせるだけのコードで Flash を 20.7% 使っています。Arduino フレームワークと Wi-Fi スタックが丸ごと含まれるためで、これは想定内です。

ハマったところ

コンテナの中から書き込みはできない

私は普段、開発用のスクリプトはすべてコンテナ(podman)の中で実行するルールにしています。環境依存を排除するためです。今回もそうしようとして、これは無理だと分かりました。

macOS の podman は仮想マシン(applehv)の上で動いているため、USB シリアルデバイスをコンテナに渡せません。 ESP32 への書き込みもシリアルモニタも、USB デバイスへのアクセスが前提なので、原理的にホストで実行するしかありません。

ビルドだけコンテナ化して、生成された .bin をホストから書き込む、という折衷案も考えました。ただ、開発中は「コードを直す→書き込む→シリアルを見る」を何十回も繰り返します。ここに手順が1段増えるのは地味に効いてきます。

結局、ファームウェアの領域についてはホスト実行を許容することにしました。代わりに再現性は platformio.ini でのバージョン固定によって担保します。AWS 側の Terraform や Lambda は従来どおりコンテナで実行します。

この判断とその理由はプロジェクトの CLAUDE.md に書き残しました。あとで自分がなぜこうしたか忘れるので。

.gitignore でファイルが黙って消えていた

VS Code の推奨拡張を設定する .vscode/extensions.json はコミットしたいけれど、他の VS Code 設定は除外したい。そこでこう書きました。

.vscode/
!.vscode/extensions.json

これは動きません。 git はディレクトリ自体を除外した場合、その配下のファイルを否定パターンで再包含できない仕様です。ディレクトリを走査しないので、中のファイルに到達しないわけです。

正しくはこう書きます。

.vscode/*
!.vscode/extensions.json

.vscode/* なら「ディレクトリの中身」を除外するので、否定パターンが効きます。

厄介なのは、これがエラーにならないことです。git add しても何も言われず、ただ静かに無視されます。気づいたのは git check-ignore -v で確認したときでした。

$ git check-ignore -v .vscode/extensions.json
.gitignore:7:.vscode/	.vscode/extensions.json

意図せず除外されているファイルが無いか不安なときは、このコマンドが役に立ちます。

認証情報の置き場所を最初に決めておく

Wi-Fi のパスワードは、いずれ必ずコードに書くことになります。あとから対処すると履歴に残ってしまうので、最初に決めておきました。

実体は firmware/include/secrets.h に置き、.gitignore で除外します。代わりにテンプレートの secrets.h.example をコミットしておきます。

// secrets.h.example
#pragma once

#define WIFI_SSID     "your-ssid"
#define WIFI_PASSWORD "your-password"

使うときはコピーして書き換えるだけです。

cp firmware/include/secrets.h.example firmware/include/secrets.h

現時点の構成

esp32/
├── CLAUDE.md                        プロジェクトの設計判断メモ
├── .gitignore
├── .vscode/extensions.json
└── firmware/
    ├── platformio.ini               ボード定義とバージョン固定
    ├── src/main.cpp                 動作確認用スケッチ
    └── include/secrets.h.example    認証情報テンプレート

次回

ビルドは通りましたが、まだ実機に書き込んでいません。 ボードを開封していないためです。

次回は実際に USB で接続して、書き込みとシリアル出力の確認をします。ここでも pio device list にボードが出てこない、といった定番のトラブルが待っていそうです。

その先はセンサーを繋いでデータを取り、AWS へ送る部分に進みます。AWS 側の構成(IoT Core を使うか、API Gateway + Lambda にするか)はまだ決めていないので、そこも含めて記録していく予定です。

参考リンク


Share this post on:

Next Post
ESP32 でセンサーデータを AWS に送る #2 接続テスト編