前回で PlatformIO の環境を整え、ビルドが通るところまで確認しました。ただしボードは開封すらしていませんでした。
今回は実際に Mac へ接続して、書き込みとシリアル出力を確認します。
目次
ボードを接続する
USB ケーブルで Mac に繋いで、認識されているか確認します。
pio device list
/dev/cu.usbserial-10
--------------------
Hardware ID: USB VID:PID=1A86:7523 LOCATION=0-1
Description: USB Serial
/dev/cu.usbserial-10 として認識されました。
VID:PID=1A86:7523 は WCH 社の CH340 という USB-シリアル変換チップです。ESP32 のチップそのものは USB を持たないため、こうした変換チップを介して PC と通信します。
前回の記事で「CH340 搭載機はドライバの追加導入が必要な場合がある」と書きましたが、今回は macOS の標準ドライバで認識されました。追加の作業は不要です。
もしここで何も出てこない場合は、USB ケーブルが充電専用でないかを最初に疑ってください。データ線が入っていないケーブルは意外と多く、私も過去に何度か引っかかっています。
書き込んでみる
前回書いた LED 点滅のスケッチを書き込みます。
pio run -d firmware -t upload --upload-port /dev/cu.usbserial-10
失敗しました。
Connecting...........
Chip is ESP32-D0WD-V3 (revision v3.1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
A fatal error occurred: Unable to verify flash chip connection
(Serial data stream stopped: Possible serial noise or corruption.)
*** [upload] Error 2
原因は書き込み速度だった
このエラーは原因が分かりにくい部類だと思います。
というのも、チップの型番もリビジョンも正しく読めているからです。ESP32-D0WD-V3 (revision v3.1)、Crystal is 40MHz まで表示されており、通信自体は成立しています。ここまで見えていると、配線やドライバを疑う気にはなりません。
手がかりは失敗の直前の1行です。
Changing baud rate to 921600
書き込みを高速化するため、通信速度を上げた直後に落ちています。つまり速度だけの問題でした。
platformio.ini の upload_speed を下げます。
; 書き込み速度。不安定な場合は 460800 や 115200 に下げる
upload_speed = 460800
実は前回この行を書いたとき、コメントに「不安定な場合は 460800 や 115200 に下げる」と自分で書いていました。将来の自分に向けたメモが、その日のうちに役に立ちました。
再実行すると一発で通ります。
Wrote 271408 bytes (150456 compressed) at 0x00010000 in 3.9 seconds (effective 560.8 kbit/s)
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
========================= [SUCCESS] Took 12.29 seconds =========================
460800 に下げても実効 560.8 kbit/s 出ており、271KB の書き込みが 3.9 秒で終わります。速度を落としたことによる不満はまったくありません。 CH340 を使っているなら、最初から 460800 にしておくのが無難だと思います。
シリアル出力を見る
書き込めたので、ボードが何を喋っているか確認します。PlatformIO にはシリアルモニタが付いています。
pio device monitor -b 115200
これで見られます。ただし、ここで別の問題に当たりました。
pio device monitor はスクリプトから使えない
出力をファイルに保存しようとしたところ、こう言われました。
UserSideException: `pio device monitor` requires an interactive terminal on stdin
and cannot run with piped or redirected input.
対話端末が必須で、パイプやリダイレクトの下では動きません。手で見る分には問題ありませんが、出力を記録したり、自動化したりはできません。
そこで Python の pyserial でシリアルポートを直接開くことにしました。Python は仮想環境(venv)で用意します。
python3 -m venv .venv
.venv/bin/pip install pyserial
システムの Python を汚さずに済みますし、Homebrew の Python は PEP 668 の制約で pip install が弾かれることもあるので、venv にしておくのが確実です。
リセットが効かない
ここでもう一段ハマりました。
シリアルポートを開いて読むと、LED: ON と LED: OFF は取れるのに、起動時に出力しているはずのチップ情報が一度も出てきません。
原因は、リセットのかけ方を間違えていたことでした。
ESP32 の開発ボードには「自動リセット回路」が載っていて、シリアルの制御線からリセットをかけられます。私は最初こう実装していました。
# 間違い
ser.setRTS(False) # IO0 のつもり
ser.setDTR(False) # EN のつもり
time.sleep(0.15)
ser.setDTR(True)
DTR と RTS の役割が逆でした。 正しくはこうです。
| 信号 | 制御対象 |
|---|---|
| RTS | EN(リセット。Low でリセット状態) |
| DTR | IO0(ブートモード選択。Low でダウンロードモード) |
通常起動させたいので、IO0 は High のまま EN だけをパルスさせます。
def hard_reset(ser):
ser.setDTR(False) # IO0 = High(通常起動)
ser.setRTS(True) # EN = Low(リセット状態へ)
time.sleep(0.1)
ser.setRTS(False) # EN = High(起動開始)
time.sleep(0.05)
この間違いが厄介なのは、エラーが何も出ないことです。 リセットが効かなくてもボードは動き続けているので、LED: ON / LED: OFF は普通に流れてきます。「基板が既に動作中だから途中から読んでいるだけ」なのか「リセットに失敗している」のかが、症状からは区別できません。
macOS では stty での設定が効かない
pyserial に行き着く前に、シェルだけで済ませようとして失敗した話も書いておきます。
stty -f /dev/cu.usbserial-10 115200
cat /dev/cu.usbserial-10
これは文字化けします。macOS では /dev/cu.* を開き直すたびに termios の設定が既定値に戻るためです。stty で設定したプロセスと cat するプロセスが別なので、cat が開いた時点で 9600 bps に戻っています。
同一のファイルディスクリプタに対して設定と読み出しを行えば回避できますが、素直に pyserial を使うほうが早いです。
動きました
リセットをかけてから読むと、起動直後から全部取れました。
ets Jul 29 2019 12:21:46
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13232
load:0x40080400,len:3028
entry 0x400805e4
=== ESP32 起動 ===
チップモデル : ESP32-D0WD-V3 (リビジョン 3)
CPU コア数 : 2
CPU 周波数 : 240 MHz
Flash サイズ : 4 MB
空きヒープ : 351484 bytes
==================
LED: ON
LED: OFF
上半分はブートローダのメッセージ、=== ESP32 起動 === から下が自分で書いたコードの出力です。日本語も問題なく表示されています。
分かったことをまとめます。
| 項目 | 値 |
|---|---|
| チップ | ESP32-D0WD-V3 rev 3.1 |
| コア数 | 2(デュアルコア) |
| 動作周波数 | 240 MHz |
| Flash | 4 MB |
| 空きヒープ | 約 351 KB |
LED も1秒間隔で点滅しています。前回「点灯しなければ基板のシルク印刷を見て調整する」と書いた LED_PIN = 2 は、このボードで正解でした。
ツールとして残す
同じことを毎回書くのは無駄なので、スクリプトにしてリポジトリに置きました。
.venv/bin/python firmware/tools/serial_monitor.py # Ctrl-C まで読む
.venv/bin/python firmware/tools/serial_monitor.py -d 10 # 10秒で終了
.venv/bin/python firmware/tools/serial_monitor.py --no-reset # リセットしない
USB-シリアル変換チップの VID:PID を見てポートを自動検出するようにしたので、ポート名を指定しなくても動きます。CH340 のほか CH9102、CP210x、FT232R に対応させました。
# ポート: /dev/cu.usbserial-10 (CH340)
pio device monitor は対話用として引き続き使い、記録や自動化が必要なときはこちらを使う、という住み分けにしています。
次回
ボードが生きていること、書き込めること、シリアルで喋ることが確認できました。
次回は Wi-Fi に接続します。ESP32 は 2.4GHz 帯にしか対応していないという、初見では気づきにくい制約があるので、そのあたりを中心に書く予定です。