第 09 章「組み込みを作る ── Pythonで考え、Claudeに翻訳させる」の主張を裏付ける。
章のどの主張に対応するか
ESP32 で温度センサ判定の開発:
- C++ で書き始める従来法: 2 週間
- Python で検証してから MicroPython に転送する新法: 2 日
- 7 倍速い
実機焼き直しサイクル:
- C++ + 書き込み: 約 2 分
- MicroPython 転送: 約 5 秒
- Python シミュレーション: 約 0.1 秒
(章本文「実例: 数字で見る」より)
実測 ── 4 時間ぶん(240 分)のシミュレーションが 0.3 ms(0.0003 秒)。 実機焼き直し 2 分と比べて 約 400,000 倍速い。
やること
温室のヒーター制御 ── ESP32 + 温度センサ + リレー出力 ── を、 実機を触る前に Python で完全に検証する。
- ロジックを Python で書く:
sensor_logic.py(HeaterController) - シミュレーションする:
simulate.pyで 4 時間ぶんの温度推移を生成- 仕様: 目標 22℃ ±1.5℃ ヒステリシス、連続 30 分稼働で強制 5 分休憩、 センサ 5 分無応答でフェイルセーフ
- ユニットテストする: ヒステリシス境界 / アラーム発火 / 状態維持
- C に翻訳する:
sensor_logic.c(Claude が Python を読んで翻訳) - ビルドして確認する: gcc で C コードがコンパイル通ることを確認
実機 (ESP32 + ESP-IDF) に焼くのは、ロジックが完全に動いてから。 実機での試行錯誤がほぼゼロになる。
構成
example-1/
├── README.md
├── sensor_logic.py ── 温度制御ロジック(HeaterController クラス)
├── sensor_logic.c ── 同じロジックの C 実装(ESP32 移植イメージ)
├── simulate.py ── 熱モデル + 4 時間シミュレーション
├── Makefile
├── results.md
└── out/
├── summary.json ── シミュレーション集計
├── trace.csv ── 1 分ごとの温度・ヒーター状態
└── ascii-chart.txt ── 温度推移の ASCII プロット
実行
make clean && make all # シミュレーション + ユニットテスト
make compile-c # C コードがコンパイル通ることを確認
なぜこれが「実例」になるのか
組み込み開発が遅い理由は、実機ループの遅さにある:
コード書く → コンパイル → ESP32 に書き込み → 起動待ち → 動作確認 → デバッグ
↑__________________________________________ 1 サイクル 2 分 _______________|
これを 1 日 50 回繰り返したら、コンパイルと書き込みだけで 100 分。
Python に置き換えると:
コード書く → python3 simulate.py → 結果を見る
↑__________ 1 サイクル 0.3 ms _______|
400,000 倍速い。バグの大半は実機を触る前に潰せる。
ロジックが完全に動いたら、Claude に「これを ESP-IDF で動く C に翻訳して」 と頼む。返ってきた C をコンパイルして、実機に書き込む。 移植時のバグ混入はほぼゼロ(同じ判定式、同じ状態遷移を辿るため)。
これが章で言う「Python で考えて、Claude に C に翻訳させる」の 最小実演。
計測結果 — 第 09 章 example-1
実行環境: Linux 6.18 / Python 3.x / gcc 13
シミュレーション(主目的)
=== シミュレーション結果 ===
シミュレーション時間: 240 分(4 時間)
実行時間: 0.3 ms
ヒーター ON 分数: 94 / 240
温度範囲: 18.0〜23.84℃ (平均 21.87℃)
発生イベント: 20 件
| 比較 | サイクル時間 |
|---|---|
| 実機 (C + ESP32 書き込み) | 約 2 分 |
| 実機 (MicroPython 転送) | 約 5 秒 |
| シミュレーション (このスクリプト) | 0.3 ms |
| 実機 / シミュレーション | 約 400,000 倍 |
イベントログ(out/summary.json 抜粋)
t= 0 ON (18.0℃ ≤ 20.5)
t= 16 OFF (23.6℃ ≥ 23.5)
t= 32 ON (20.4℃ ≤ 20.5)
t= 42 OFF (23.7℃ ≥ 23.5)
t= 59 ON (20.5℃ ≤ 20.5)
t= 64 ALARM ヒーター強制 OFF (センサ 5 分無応答)
t= 67 復旧 (センサ復活)
t= 74 ON (20.4℃ ≤ 20.5)
...
仕様通りに動いている:
- ヒステリシス: 20.5℃で ON、23.5℃で OFF(目標 22 ±1.5)
- フェイルセーフ: センサ 5 分無応答で強制 OFF + ALARM
- 復旧: センサが復活すると通常制御に戻る
- 過熱防止: 連続 30 分稼働で強制 5 分休憩(本シミュではトリガしない範囲)
ユニットテスト
=== ユニットテスト(ヒステリシス境界) ===
✓ 18℃ で ON
✓ 23.6℃ で OFF
✓ 22℃ では状態維持
✓ センサ 5 分無応答でアラーム
4 件すべて pass
実機でテストすると、4 件のテストにそれぞれ 30 分(温度を変える時間)。 合計 2 時間以上。Python では 数 ms。
C への翻訳(sensor_logic.c)
#define TARGET 22.0f
#define HYSTERESIS 1.5f
#define MAX_ON_MINUTES 30
void controller_tick(uint32_t minute) {
float temp = read_temperature_sensor();
bool sensor_ok = !isinf(temp);
if (!sensor_ok) {
ctrl.silent_minutes++;
if (ctrl.silent_minutes >= SENSOR_TIMEOUT_MINUTES) {
ctrl.heater_on = false;
set_heater(false);
trigger_alarm("...");
}
return;
}
...
}
閾値定数も状態遷移も Python 版と完全一致。
make compile-c で gcc でコンパイル通ることを確認:
=== C コードのコンパイル(ロジック層のみ、HAL なしで構文チェック) ===
✓ コンパイル成功 (3640 bytes)
実機 (ESP32) では read_temperature_sensor() set_heater() trigger_alarm()
の 3 関数を ESP-IDF で実装するだけ。ロジック本体は触らない。
実機への移行手順(参考)
- Python でロジック完成 ✓
- ユニットテスト 4 件 pass ✓
- シミュレーションで 4 時間ぶんの動作確認 ✓
- Claude に「ESP-IDF で動く C に翻訳して」 →
sensor_logic.c - ハードウェア依存の HAL 関数 (3 個) を ESP-IDF で書く
idf.py build flashで ESP32 に焼く- 実機で 1 時間動作確認 → デプロイ
実機を触るのは 最後の 30 分だけ。残りはすべて開発機の上で完結する。
再現手順
make clean && make all # シミュレーション + テスト
make compile-c # C コードがコンパイル通る確認