BLE Compiler 〜iPadからマイコンに書き込みたい!〜

https://github.com/naka6ryo/RemoteCompilerToMicon

C++

CSS

JavaScript

HTML

iPadからマイコンに書き込む!

Nakamura Ryo

推しアイデア

プログラムをiOSから書き込む

作った背景

組み込み開発を含めた全てをiPadだけで全てを済ましたい!

推し技術

BLE経由のOTA!

プロジェクト詳細

iPadからマイコンに書き込みたいんだ!!!

iPadを“閲覧端末”じゃなくて“開発端末”として使いたい。 でも最後の最後、マイコン書き込みだけ有線必須で詰む。 じゃあそこを壊そう、という話です。


概要

今回作ったのは、iPad (Bluefy) から ESP32-S3 Super Mini に対して、BLE経由でOTA書き込みできる Webアプリ + マイコン側ファームウェアの統合システムです。

単にOTAするだけではなく、以下をまとめて扱えるようにしています。

  • BLE Wi-Fiプロビジョニング(SSID/パスワード送信)
  • BLE OTA(.bin ファイルの転送と更新)
  • BLEデバッグモニタ(ログ受信 / コマンド送信)

ポイントはここです。

  • OTAは完全にBLE経由
  • Wi-Fiはプロビジョニング確認用途のみ
  • iOS/iPadでも Bluefy + Web Bluetooth で実用的に操作可能

つまり、

「iPadをメイン端末にして現地開発したい」

に対して、最後の障壁だった「書き込み」をかなり現実的にした構成です。

デモ動画

image

使い方

ざっくり利用フロー

  1. ESP32-S3 Super Mini を起動(最初のみUSBで初期書き込み済みファームが必要)
  2. iPadで Bluefy を開いて WebApp にアクセス
  3. BLEでESP32に接続
  4. (任意)Wi-Fiプロビジョニング
  5. .bin ファイルを選択して OTA 実行
  6. 進捗を確認 → 更新完了後に自動再起動

WebApp側でできること

  • BLE接続パネル: デバイス接続/切断
  • Wi-Fi設定パネル: SSID/パスワード送信
  • ファームウェア更新パネル: .bin選択 → OTAアップロード
  • BLEデバッグモニタ: ログ購読 / コマンド送信

使うときの注意点(実運用で大事)

  • OTA前に BLE接続が安定していることを確認する
  • OTA中は iPadとデバイスを近距離に置く
  • .bin サイズは 2MB以下(この実装の制限)
  • 中断時の再開は未対応なので、失敗したら最初から再送

序盤の背景的説明(なぜ iPad だけで書き込みたいのか)

ここ、個人的にはかなり大事です。

「iPadで開発したい」は一見すると変なこだわりに見えるんですが、実際にはかなり合理的です。

1. まず単純に、iPad + PCの2台持ちが重い

ハッカソンや現地開発、出先での検証って、ただでさえ荷物が増えます。

  • PC本体
  • 充電器
  • ケーブル類
  • iPad(メモ、図、資料閲覧、設計用)

…と持っていくより、できればメイン端末をiPadに寄せたい

2. iPadは「PCっぽいこと」がかなりできる

特に最近は、リモートデスクトップやブラウザベースの開発環境で、iPadでもかなり戦えます。 Chrome Remote Desktopのような構成を使えば、低遅延でPC作業にアクセスできる場面もあります(参考: Google公式ヘルプ)。

つまり、

  • コーディング
  • ログ確認
  • 軽い修正
  • デバッグ補助

みたいなことは、工夫すれば iPad 側にかなり寄せられる。

3. 逆にPCは、手描き・メモ・図解が弱い

これは開発している人ほど刺さるやつですが、

  • 回路図のラフ
  • 状態遷移のメモ
  • UIラフ
  • アイデアの殴り書き

みたいな作業、iPad + Pencil が強すぎるんですよね。

なので発想としては自然で、

「じゃあ iPad をメイン開発端末にしよう」

になります。

4. でも最後に残るのが「書き込み」

ここで詰まる。

マイコン開発では、最終的にプログラムを動かすには、

  • ビルド(機械語への変換)
  • マイコンへの転送(書き込み)
  • 実行

が必要です。

この「書き込み」は多くの場合、USB/UARTなどの有線接続が前提です。

つまり、iPadだけで頑張っても最後に

  • PCを出す
  • 変換器を出す
  • ケーブルを挿す

が必要になり、急に世界観が壊れる。

5. 現地開発だと、さらに現実的につらい

ハッカソンや展示会場、検証現場だと、こんな問題が起きがちです。

  • 机が狭い
  • 配線スペースがない
  • すでに筐体に組み込まれていて書き込み端子に触りにくい
  • 変換器やピン変換を持ち歩くのが面倒

要するに、

「コードは書けるのに、書き込めない」

がめちゃくちゃストレスなんです。

だから今回のテーマはシンプルです。

iPadだけで開発したい。なら、iPadから書き込めるようにする。


メイン技術: OTA(ESP32系)を使う

今回の中核は OTA (Over-The-Air) です。

そもそも書き込みとは?

組み込み系でいう「書き込み」は、マイコンにプログラムを入れて動かすための処理です。

  • ソースコードをビルドして .bin(機械語のファームウェア)を作る
  • それをマイコンに転送する
  • マイコンがFlashから実行する

ESP32のプログラムはPCみたいにHDDに置くわけではなく、**Flash(不揮発メモリ)**に格納されます。

OTAって何?

OTAは、ざっくり言うと

「USBケーブルの代わりに通信でFlashへ書き込む方法」

です。

  • USB書き込み: PC → USB/UART → Flashへ書き込み
  • OTA書き込み: スマホ/PC → (Wi-Fi / BLE など) → Flashへ書き込み

前提: ESP32とは?

ESP32はマイコンの一種で、特に以下が強いです。

  • BLE / Bluetooth / Wi-Fi を扱える
  • モデルによっては Lipo充電系も載っている
  • 派生ボードが多く、汎用性が高い

有名どころだと M5Stackシリーズ など。今回は筆者がよく使う ESP32-S3 Super Mini を前提にしています。

なぜWi-Fi OTAじゃなくてBLE OTA?

一般的には OTA は Wi-Fi経由が多いです。

ただ、今回の狙いは iPad(特にiOS環境)からの実用運用 なので、そこで制約に当たります。

  • ESP32がローカルホストして配布する構成は扱いやすい
  • でも iOS / ブラウザ側の制約で、ローカルホスト経由アクセスが扱いにくいケースがある
  • 一方で Bluefy を使うと、iOSでも Webアプリから BLE を扱える

ということで今回は BLE OTA を採用しています。

ここがこの実装の肝です。


BLE OTAを初心者向けに噛み砕く(ここは丁寧に)

1) 登場人物

BLE OTAでは、やり取りするのはこの2者です。

  • 送る側(クライアント): iPad / スマホ / PC のアプリ(今回は Bluefy 上のWebアプリ)
  • 受け取る側(デバイス): ESP32(受信してFlashへ書き込む)

重要ポイントは1つ。

ESP32側に最初から「受信してFlashへ書くためのOTAロジック」が入っている必要がある こと。

これが無いと、受け取れても書き込めません。

2) データの流れ(超ざっくり)

[iPad / Webアプリ] ① ファーム(.bin)を選ぶ ② BLEでESP32へ小分け送信 [ESP32] ③ 受け取った順にFlash(OTA領域)へ書く ④ 受信完了後に整合性チェック ⑤ 次回起動先を新ファームへ切替 ⑥ 再起動 → 新ファームで起動

3) BLEなので「小分け送信」が必須

BLEはWi-Fiより帯域が小さく、1回に送れるサイズ(MTUの制約)も小さいです。

だから、数百KB〜数MBあるファームウェアをそのまま送るのではなく、

  • 小さなチャンクに分割
  • 順番に送信
  • ESP32側で順次Flashに追記

という形にします。

今回の実装では、iOS / Bluefy 寄りの安定性を見て 180バイト程度のチャンクを推奨にしています。

4) BLEのGATTって何をしてるの?(初心者向け)

BLEでは「サービス」「キャラクタリスティック」という箱を使って通信を整理します。

OTAでは典型的に以下の役割分担になります。

  • Control: 開始 / 終了 / 中止などの命令
  • Data: ファーム本体のバイナリ断片
  • Status: 進捗 / 成功 / エラー通知

今回の実装もほぼこの構成です。

5) ESP32側で何が起きてるの?(内部手順)

(A) OTA書き込み先を用意する

ESP32のFlashはパーティションに分かれています。 OTA対応では典型的に次のような構成を使います。

  • factory
  • ota_0
  • ota_1
  • nvs

更新時は、今動いている領域とは別のOTA領域に書きます。 これにより、動作中のファームを直接壊さないので安全性が上がります。

(B) 受信しながらFlashへ書く

BLEで届いた断片を順に受けて、Update.write() のような処理でFlashへ書き込みます。

ここで設計上の論点になるのは、

  • 中断時に再開可能にするか
  • 再開なしで最初からやり直しにするか

です。

このプロジェクトでは、仕様をシンプルに保つため 途中再開は未対応 にしています。

(C) 完了後に整合性チェック

最後まで受け取ったら、少なくとも以下を確認します。

  • サイズ一致
  • ハッシュ / CRC一致(将来拡張含む)
  • 書き込み完了判定

不一致なら新ファームは採用せず、旧ファームのまま動作します。

(D) ブート先切り替え → 再起動

検証OKなら、次回起動時に新ファーム領域を使うように切り替えます。 再起動後、ブートローダがその情報を見て新ファームを起動します。

ここまでできて、初めて「OTAで更新できた」と言えます。


技術構成(ここからはポイント中心に淡々と)

この章では、実装の全詳細ではなく、GitHub/READMEに載せる内容と重複しすぎないように設計上の要点を絞って説明します。

全体構成

  • クライアント: iPad + Bluefy 上の WebApp(HTML/CSS/JavaScript)
  • デバイス: ESP32-S3 Super Mini(Arduino / PlatformIO)
  • 主通信路: BLE(Provisioning / OTA / Debug すべて)
  • Wi-Fi: プロビジョニング確認用途のみ(OTAには不使用)

設計の主眼は、通信方式を絞って iOS 環境での実用性を上げることです。

なぜ「BLE一本化」寄りにしたか

  • iPad/Bluefy からの操作導線を単純化できる
  • OTA / ログ / 制御を同一接続上で扱える
  • Wi-Fi前提のローカルHTTP OTAに依存しない

結果として、現地でのセットアップ差分を減らせます。

BLE GATTの役割分割(要点)

3つの独立サービスに分割しています。

  • Provisioning Service: Wi-Fi資格情報の受け渡し
  • Debug Service: ログ通知 / コマンド受信
  • OTA Service: OTA制御 / バイナリ送信 / ステータス通知

ポイントは、OTAをDebugサービスに混ぜず、OTA専用サービスとして独立させたことです。 運用時の責務分離とトラブル切り分けがしやすくなります。

OTAプロトコル設計(簡易)

OTAサービスは、以下の3キャラクタリスティック構成です。

  • OtaControl: START:<size>, END, ABORT
  • OtaData: バイナリチャンク送信
  • OtaStatus: READY, PROGRESS:x/y, SUCCESS, ERROR:*

要点:

  • シーケンス番号なしの順次書き込み(シンプル実装)
  • 進捗通知を定期送信(100KBごと / 完了時)
  • 失敗時は明示的なエラーコードを返す

再送制御や再開機構を入れていない分、仕様理解しやすさと実装容易性を優先しています。

OTAパーティション方針

  • OTA対応パーティションテーブル (partitions_ota_2m.csv) を使用
  • 最大ファームウェアサイズは 2MB を前提
  • 更新先は非実行中スロットを選択

この構成により、更新失敗時に旧ファームへ戻れる余地を確保しています。

WebApp構成(責務分離のポイント)

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 集約でも十分で、仕様が固まってから分割する方が追跡しやすい、という判断です。

制限事項(設計上の割り切り)

  • HTTP OTA未実装(OTAはBLEのみ)
  • 途中再開未対応(中断時は再送)
  • 同時OTA不可(1デバイスずつ)
  • チャンクサイズ最適化はiOS/Bluefy寄り(推奨180B)

このあたりは今後の拡張ポイントですが、現時点では「iPadから実際に書ける」を優先しています。


技術構成まとめ(短く)

今回の構成は、派手なことをしているというより、iPad/iOSの制約下で現実的に動く導線を丁寧に組んだ、というタイプの実装です。

  • 通信方式をBLEに寄せる
  • GATTの責務を分ける
  • OTA手順を最小構成で安定化する
  • WebApp / マイコン側の責務を分離する

これで、少なくとも「iPadからマイコンへ書き込みたいんだ!!!」に対して、ちゃんと動く答えを作れました。


参考・補足(リポジトリ側に載せる内容)

詳細はGitHub側で以下を参照する想定です。

  • CreatePlan.md(実装詳細設計)
  • SpecifcationDoc.md(システム仕様)
  • MiconSide/README.md(マイコン側手順)
  • WebAppSide/README.md(WebApp側手順)

この記事では背景と設計意図を厚めに書き、実装の完全仕様はリポジトリへ寄せる、という役割分担にしています。


おわりに

iPadをメイン端末にしたい人って、たぶん「少数派」ではあるんですが、一定数いると思っています。

そしてその人たちが毎回ぶつかるのが、最後の「書き込み」です。

今回の構成はESP32系前提ではあるものの、

  • BLEで制御する
  • OTAロジックを先に載せる
  • Webアプリ化して端末依存を減らす

という考え方は、他の構成にも展開しやすいです。

同じことで詰まっている人の、突破口になればうれしいです。

Nakamura Ryo

@a70b49819a641008