推しアイデア
プログラムをiOSから書き込む
プログラムをiOSから書き込む
組み込み開発を含めた全てをiPadだけで全てを済ましたい!
BLE経由のOTA!
iPadを“閲覧端末”じゃなくて“開発端末”として使いたい。 でも最後の最後、マイコン書き込みだけ有線必須で詰む。 じゃあそこを壊そう、という話です。
今回作ったのは、iPad (Bluefy) から ESP32-S3 Super Mini に対して、BLE経由でOTA書き込みできる Webアプリ + マイコン側ファームウェアの統合システムです。
単にOTAするだけではなく、以下をまとめて扱えるようにしています。
ポイントはここです。
つまり、
「iPadをメイン端末にして現地開発したい」
に対して、最後の障壁だった「書き込み」をかなり現実的にした構成です。

.bin ファイルを選択して OTA 実行.bin選択 → OTAアップロード.bin サイズは 2MB以下(この実装の制限)ここ、個人的にはかなり大事です。
「iPadで開発したい」は一見すると変なこだわりに見えるんですが、実際にはかなり合理的です。
ハッカソンや現地開発、出先での検証って、ただでさえ荷物が増えます。
…と持っていくより、できればメイン端末をiPadに寄せたい。
特に最近は、リモートデスクトップやブラウザベースの開発環境で、iPadでもかなり戦えます。 Chrome Remote Desktopのような構成を使えば、低遅延でPC作業にアクセスできる場面もあります(参考: Google公式ヘルプ)。
つまり、
みたいなことは、工夫すれば iPad 側にかなり寄せられる。
これは開発している人ほど刺さるやつですが、
みたいな作業、iPad + Pencil が強すぎるんですよね。
なので発想としては自然で、
「じゃあ iPad をメイン開発端末にしよう」
になります。
ここで詰まる。
マイコン開発では、最終的にプログラムを動かすには、
が必要です。
この「書き込み」は多くの場合、USB/UARTなどの有線接続が前提です。
つまり、iPadだけで頑張っても最後に
が必要になり、急に世界観が壊れる。
ハッカソンや展示会場、検証現場だと、こんな問題が起きがちです。
要するに、
「コードは書けるのに、書き込めない」
がめちゃくちゃストレスなんです。
だから今回のテーマはシンプルです。
iPadだけで開発したい。なら、iPadから書き込めるようにする。
今回の中核は OTA (Over-The-Air) です。
組み込み系でいう「書き込み」は、マイコンにプログラムを入れて動かすための処理です。
.bin(機械語のファームウェア)を作るESP32のプログラムはPCみたいにHDDに置くわけではなく、**Flash(不揮発メモリ)**に格納されます。
OTAは、ざっくり言うと
「USBケーブルの代わりに通信でFlashへ書き込む方法」
です。
ESP32はマイコンの一種で、特に以下が強いです。
有名どころだと M5Stackシリーズ など。今回は筆者がよく使う ESP32-S3 Super Mini を前提にしています。
一般的には OTA は Wi-Fi経由が多いです。
ただ、今回の狙いは iPad(特にiOS環境)からの実用運用 なので、そこで制約に当たります。
ということで今回は BLE OTA を採用しています。
ここがこの実装の肝です。
BLE OTAでは、やり取りするのはこの2者です。
重要ポイントは1つ。
ESP32側に最初から「受信してFlashへ書くためのOTAロジック」が入っている必要がある こと。
これが無いと、受け取れても書き込めません。
[iPad / Webアプリ] ① ファーム(.bin)を選ぶ ② BLEでESP32へ小分け送信 ↓ [ESP32] ③ 受け取った順にFlash(OTA領域)へ書く ④ 受信完了後に整合性チェック ⑤ 次回起動先を新ファームへ切替 ⑥ 再起動 → 新ファームで起動
BLEはWi-Fiより帯域が小さく、1回に送れるサイズ(MTUの制約)も小さいです。
だから、数百KB〜数MBあるファームウェアをそのまま送るのではなく、
という形にします。
今回の実装では、iOS / Bluefy 寄りの安定性を見て 180バイト程度のチャンクを推奨にしています。
BLEでは「サービス」「キャラクタリスティック」という箱を使って通信を整理します。
OTAでは典型的に以下の役割分担になります。
今回の実装もほぼこの構成です。
ESP32のFlashはパーティションに分かれています。 OTA対応では典型的に次のような構成を使います。
factoryota_0ota_1nvs更新時は、今動いている領域とは別のOTA領域に書きます。 これにより、動作中のファームを直接壊さないので安全性が上がります。
BLEで届いた断片を順に受けて、Update.write() のような処理でFlashへ書き込みます。
ここで設計上の論点になるのは、
です。
このプロジェクトでは、仕様をシンプルに保つため 途中再開は未対応 にしています。
最後まで受け取ったら、少なくとも以下を確認します。
不一致なら新ファームは採用せず、旧ファームのまま動作します。
検証OKなら、次回起動時に新ファーム領域を使うように切り替えます。 再起動後、ブートローダがその情報を見て新ファームを起動します。
ここまでできて、初めて「OTAで更新できた」と言えます。
この章では、実装の全詳細ではなく、GitHub/READMEに載せる内容と重複しすぎないように設計上の要点を絞って説明します。
設計の主眼は、通信方式を絞って iOS 環境での実用性を上げることです。
結果として、現地でのセットアップ差分を減らせます。
3つの独立サービスに分割しています。
ポイントは、OTAをDebugサービスに混ぜず、OTA専用サービスとして独立させたことです。 運用時の責務分離とトラブル切り分けがしやすくなります。
OTAサービスは、以下の3キャラクタリスティック構成です。
START:<size>, END, ABORTREADY, PROGRESS:x/y, SUCCESS, ERROR:*要点:
再送制御や再開機構を入れていない分、仕様理解しやすさと実装容易性を優先しています。
partitions_ota_2m.csv) を使用この構成により、更新失敗時に旧ファームへ戻れる余地を確保しています。
WebAppSide/ は機能ごとに分割しています。
ble-client.js: BLE接続・GATT操作の基盤ota-client.js: OTA手順の制御firmware-client.js: ファーム送信処理ui.js: UI更新constants.js: UUID / 定数定義要点は、BLE I/O と OTA手順ロジックとUI更新を分離して、変更影響範囲を狭めている点です。
MiconSide/ は PlatformIO 前提で、以下が重要ファイルです。
platformio.ini: ボード / フレームワーク / パーティション設定partitions_ota_2m.csv: OTA前提のFlash分割src/main.cpp: BLEサービス / OTA / Debug / Provisioning の統合実装プロジェクト初期段階では main.cpp 集約でも十分で、仕様が固まってから分割する方が追跡しやすい、という判断です。
このあたりは今後の拡張ポイントですが、現時点では「iPadから実際に書ける」を優先しています。
今回の構成は、派手なことをしているというより、iPad/iOSの制約下で現実的に動く導線を丁寧に組んだ、というタイプの実装です。
これで、少なくとも「iPadからマイコンへ書き込みたいんだ!!!」に対して、ちゃんと動く答えを作れました。
詳細はGitHub側で以下を参照する想定です。
CreatePlan.md(実装詳細設計)SpecifcationDoc.md(システム仕様)MiconSide/README.md(マイコン側手順)WebAppSide/README.md(WebApp側手順)この記事では背景と設計意図を厚めに書き、実装の完全仕様はリポジトリへ寄せる、という役割分担にしています。
iPadをメイン端末にしたい人って、たぶん「少数派」ではあるんですが、一定数いると思っています。
そしてその人たちが毎回ぶつかるのが、最後の「書き込み」です。
今回の構成はESP32系前提ではあるものの、
という考え方は、他の構成にも展開しやすいです。
同じことで詰まっている人の、突破口になればうれしいです。