PC1台から同一バイナリを複数のESP32へアップロード

# 作ったもの
PC1台とシリアル変換モジュール1台から複数のESP32へ同一のバイナリをアップロードするシステムを作成しました。また、同一のファイルをSPIFFSにアップロードすることも可能です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/167138/d6b919b4-4756-b2f2-b8cd-4a9c85a58adc.png)

# どれくらい便利なの？
理由は後述しますが、複数のESP32に**同時にプログラムのアップロードはできません。**（まだブラウザバックは待って！）ですので、**「プログラムのアップロードにかかる総時間」**自体は今回紹介するシステムでも変わりません。では何が変わるかというと、以下のような比較になります。

| 手順 | 従来のアップロード | 本システムのアップロード |
|:-----------:|:------------:|:------------:|
| **1** | シリアル変換モジュールとESP32を接続 | シリアル変換モジュールと複数のESP32を接続 |
| **2** | アップロード開始 | アップロード開始 |
| **3** | アップロード待機 | アップロード待機 |
| **4** | アップロードが終わったESP32を外す | アップロードが終わったESP32を外す |
| **5** | 「1」に戻る * n回 | 終了 |
| **6** | 終了 |  |

比較してみるとわかりますが、以下の作業を1回するだけでよくなる点が効率化されたところです。

- **プログラムのアップロードが終わるまで待つ * n回**
- **プログラムアップロードボタンを押す * n回**

**「プログラムのアップロードが終わるまで待つ」**、**「アップロードが終わったESP32と次にアップロードするESP32を入れ替える」**、**「プログラムのアップロードボタンを押す」**という動作をロスなく実行できるアップロード職人にこのシステムは不要なので、ブラウザバックしていただいて大丈夫です。ここまでお付き合い頂きありがとうございましたm(_ _)m。

**先が気になる？ではもうしばらくお付き合いください！**

# システムまでの道のり

## まずは調べる
目的を実現するシステムやツールが既に提供されているのではないかと思い調べてみましたが、私が調べた限りではそういうものは存在しないようです。PCBAサービス等でプログラムのアップロードまでしてくれるサービスがあったりしますが、あれはどうやってるんでしょうかね。誰か知っている方がいたら教えてほしいです。

## UARTを複数配線してみる
UARTは双方向の1対1の通信です。しかし、「シリアル変換モジュールからRX、TX端子を複数枝分かれさせて配線したらいけるんじゃないの？」という浅はかな考えで試してみました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/167138/3b0bd36f-c302-36a5-8a5d-969f72f8020c.png)
結果は当然アップロードエラーとなりました。UARTはアップロード等の一方向に見える通信でも、正しく通信できているかを受信しつつアップロードしているようで、その情報が混線するためエラーになるようです。

## OTAでブロードキャストっぽくアップロード
ESP32はOTA(Over the Air)による無線プログラムアップロードが可能です。ということは、なんかブロードキャストっぽい感じで複数のESP32にOTAでアップロードできるんじゃないかと思いましたが、思いも虚しく調べた限りではそういう機能はなさそうでした。（誰か知っていたら教えてください）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/167138/1504ff85-4524-0b52-3e00-7d5c70ea02a8.png)

仮にこの機能が存在したとしても、一度はOTAをアクティブにするためのプログラムをUART（有線）でアップロードしなければならないという、「鶏が先か、卵が先か」もとい、**「鶏が先」**という現実に直面します（「卵が先」でもいいです）。

# 本システムの概要
## 構成
以下の三種の神器によりシステムを構成します。

- **起動制御及びアップロード実行プログラム（下図のPC）**
- **起動制御モジュール（ESP32）**
- **シリアル変換モジュール（FT232RL等）**

![image.png](https://qiita-image-store.s3.amazonaws.com/0/167138/470ddbdd-ff10-5561-4d05-2e2f8bb5f3a1.png)

## 基本動作
アップロードは**UART**で実行します。上図の通り、シリアル変換モジュールとアップロード対象ESP32をUARTで枝分かれして配線します。**UARTは1対1の通信**しかできないため、このままでは先述の通りアップロードは失敗します。そこで、起動管理モジュール（ESP32）とMOSFETを用いて、アップロード対象のESP32の内、**1台にのみ電源が供給される状態**にします。その状態でアップロードを実行すると、UARTの信号が混線しないためアップロードが成功します。
よって、以下の動作で複数のモジュールにアップロードを実行します。

**0. 起動管理モジュールと接続しているESP32を探す**
**1. PCから起動管理モジュールへ電源を供給するESP32を通知**
**2. 起動管理モジュールが通知されたESP32のみに電源を供給し、PCに通知**
**3. 通知を受けたPCからプログラムをアップロードするシェルを実行**
**4. アップロードが終了したら「1.」に戻る**
**5. 「1.～4.をアップロード台数分繰り返す」**

2台分のアップロードを実行するシステムの一連の流れをGIFにしてみました。文字まで読んでいると追いつけないかもしれないので、とりあえず回路の切り替わりだけ見てもらえたらなと思います。

![module2.gif](https://qiita-image-store.s3.amazonaws.com/0/167138/864bbd1f-e0b1-0396-4de6-b62756d44477.gif)

# 細かい実装の話
## 電源供給回路
上手の通り、ただMOSFETをスイッチとして利用しているだけで特に難しいことはしていません。電源が供給されていないESP32のTXピンにも電圧がかかっているので、ESP32内部の保護ダイオード経由で電源を供給していないはずのESP32にも電源が中途半端に供給され、信号が混線するかもしれません。なので、改善するとしたらUARTの接続部分にマルチプレクサを挿入するとよりよいかもしれません（未検証）。現状マルチプレクサを入れなくても安定してアップロードできています。

## 起動管理モジュール
ESP32をwebサーバとして起動させます。POSTされたパラメータのGPIOをHIGH or LOWにすることで、任意のモジュールのみに電源を供給します。使用しているGPIOは以下の通りです。IO4、IO13のGPIOに関しては、デバッグ用途で使用しているので、**最大で14台のESP32の電源を管理**できます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/167138/7e67f619-5f9b-ad69-4d10-47d8fb19bcc2.png)


## 起動制御及びアップロード実行プログラム
Pythonで書いています。大きく以下の3つの機能を持っています。

### 電源を供給する任意のGPIOを起動管理モジュールにPOSTする
そのまんまです。

### ESP32が接続している起動管理モジュールのGPIOを全て調べる
UART Download Modeで起動（リセット）したESP32は以下の固定メッセージを出力します。（ファームウェアのバージョンによっては違うかもしれませんが未調査です）

```
rst:0x1 (POWERON_RESET),boot:0x3 (DOWNLOAD_BOOT(UART0/UART1/SDIO_REI_REO_V2))
waiting for download
```

電源が供給されているESP32とはシリアル変換モジュールを通してUARTで接続されているので、上記の文字列を受信可能です。起動管理モジュールの電源管理GPIOを順に電源供給状態にし、上記の固定文字列を受信できればそのGPIOにはESP32が接続されていると判定できます。

### プログラムをアップロードするシェルを起動する

プログラムのアップロードはPythonからアップロード用のシェルを実行します。純正のesptoolをシェルで起動して使用しても良いですが、今回は色々と便利な[PlatformIO Core](https://docs.platformio.org/en/latest/core.html)のコマンドを呼び出しています。以下のように非常に簡単にアップロードをが実行できます。

```
# プログラムアップロード
pio run -t upload -d [ソースのplatform.iniの配置パス]
# SPIFFSへのファイルアップロード
pio run -t uploadfs -d [ソースのplatform.iniの配置パス]
```

[PlatformIO](https://platformio.org/)って何？という方は、現状のマイコン、IoT開発で最も進んでいる開発環境なので、是非使用してみてください。どれくらい魅力的かというと、ArduinoだろうとESP-IDFだろうとSTM32だろうとPlatformIO + VScodeという統一された環境で開発が可能になります。

# 実物
システムと呼ぶにはあまりにごちゃっとしてます。ESP32開発ボードが起動管理モジュールで、その他の2つがアップロード対象のESP32です。
![s-s-P_20181216_124253.jpg](https://qiita-image-store.s3.amazonaws.com/0/167138/d5bf6300-63e2-7651-98e1-5ebeb2a1ae21.jpeg)

# ソースの公開
Githubで公開予定ですが、あまりにソースが汚いので最低限人間が書いたと思われる程度になるように修正中です。

# Seeedと関係なくない？
これだけごちゃっとしているので、基盤に落とし込んでSeeedさんに発注します！SeeedさんではどうやってPCBAサービスで大量のプログラムのアップロードを実施しているのか知りたいです！(これで許して)

# 最後に

5台くらいまでのアップロードならプログラムアップロード職人になったほうが早いです。

# 追記
現状の構成ですと、電源が供給されていないマイコンの入力端子に電圧をかけているため、**ラッチアップによるマイコンの破壊の危険性があります。**後日対策したものを公開予定ですので、試して見る方は自己責任の上お試しください。