0. はじめに
先日、『ESP32マルチ書き込みボード』を紹介させていただきました。その後、ESP32-S3のネイティブUSBによる書き込みにも対応したので、今回はそれを紹介いたします。
1. 改造した書き込みボード
新しい書き込みボードは↓こんな感じです。左側のUSBアダプタも作成しました。3.3V降圧モジュールを仕込んであります。
下の写真は改造前のボードで、比較しながら改造点を説明します。
(1) ESP32-S3のネイティブUSBを使用するため、USBシリアル変換モジュールは使用しません。代わりUSBのD-/D+信号線をESP32-S3のIO19/IO20に接続するため、2Pピンソケットを追加しました。
(2) 手動書き込みに対応するため、IO0をGNDに落とすタクトスイッチを追加しました。ネイティブUSBで書き込む場合『自動書き込み』ができず、後から急遽追加しました。そのためタクトスイッチも不揃いで取って付けた感ありありです。
書き込みボード自体の改造は以上の2点だけです。
次回、書き込みボード自体を新規作成する時は、ネイティブUSB用の4Pヘッダ(3.3v/GND/D-/D+)を独立して準備しようと思います。そうすればシリアル接続と共存ができますので。
2. ネイティブUSBを使用する場合の気づき
自分に経験がないため、実際の書き込みは、一筋縄ではいきませんでした。
色々試行錯誤して、今は普通に書き込めるようになりましたので、そこで得た内容を気づき事項としてまとめておきます。
(1) 自動書き込みができない
通常のシリアルインタフェースによる自動書き込みは、RTS/DTRピンによりEN/IO0を制御することでブートモードを制御していますが、ネイティブUSBの場合はそれができません。よって、手動書き込みのため、再起動時にIO0をGNDに落としてDownload Bootにする必要がありました。
同様に、書き込み後の自動リセットも効かないため、手動でリセット(再起動)する必要があります。
『自動書き込みができない』という認識は誤り(?)であったことが、後で判明しました。詳しくは後述します。
(2) シリアルポートが変化する
CircuitPythonが書き込んであったESP32-S3をネイティブUSB接続したときのシリアルポートは/dev/cu.usbmodemC7FD1A7E6ACC1
でした。
USBデバイスツリーには、ESP32-S3-DevKitC-1-N8R8
と認識され、
CIRCUITPY
というフラッシュドライブも現れます。また、
screen /dev/cu.usbmodemC7FD1A7E6ACC1 115200
でREPLも機能します。
これを、Download Bootで起動すると、シリアルポートは/dev/cu.usbmodem144201
に変化し、USBデバイスツリーには、USB JTAG/serial debug unit
と認識されます。
ここで、Arduino IDEからESP32のスケッチ例KeyboardMessage
を書き込んで再起動すると、今度は、シリアルポートは/dev/cu.usbmodem144202
に変化し、USBデバイスツリーには、ESP32S3_DEV
と認識されます。
以上のように、シリアルインタフェースでは固定であったシリアルポートが、ネイティブUSBだと状況により変化するため、esptoolやArduino IDEから書き込みをする場合に、シリアルポートを確認する必要があります。
(3) Arduino IDEでのボードの指定方法
よく分からず戸惑ったのが、Arduino IDEでのボードの指定方法です。ボードそのものは、ESP32S3 Dev Module
でよいのですが、USB Mode
とUpload Mode
で何を指定すべきか分からず、何パターンも試して分かった事があります。
・Upload Mode
これは、現在のESP32-S3のネイティブUSBの認識状態により、指定値の振る舞いが違ってきます。下表は『USB認識状態』を現在のシリアルポートで代用します。
USB認識状態 (シリアルポート) |
UART0 / Hardware CDC | USB-OTG CDC (TinyUSB) |
---|---|---|
/dev/cu.usbmodemC7FD1A7E6ACC1 | 書き込み不可(*1) | 書き込み不可(*2) |
/dev/cu.usbmodem144201 | 書き込み可 | 書き込み可 |
/dev/cu.usbmodem144202 | 書き込み不可(*1) | 書き込み可 |
(*1)書き込み不可理由:Connecting...........スケッチの書き込み中にエラーが発生しました.
(*2)書き込み不可理由:シリアルポート「・・・」が選択されていますが、そのポートは存在しないか、ボードが接続されていません。
はじめて今回の書き込みボードでネイティブUSB接続によりアップロードを試みた時は、CircuitPython
が書き込んであるESP32-S3を使用し、シリアルポートは/dev/cu.usbmodemC7FD1A7E6ACC1
でした。上の表に示す通り、この状態では、Upload Mode
に何を指定してもアップロードすることができなかったことと、その後、(IO0をGNDに落として)Download Bootしたところ、シリアルポートが/dev/cu.usbmodem144201
に変化し、アップロードできたことで、前項の『ネイティブUSBの場合は自動書き込みはできない』という誤認識につながりました。
興味ある動作として、USB-OTG CDC (TinyUSB)
を指定した場合は、書き込みの最初に現在のシリアルポートを一度閉じて、複数のシリアルポートを試してアップロードするシリアルポートを決定する動作が確認できました。
シリアルポート「/dev/cu.usbmodem144202」を1200bpsで開いて閉じる事によって、リセットを行っています。
PORTS {/dev/cu.BLTH, /dev/cu.Bluetooth-Incoming-Port, /dev/cu.usbmodem144202, /dev/cu.usbmodemHIDLD1, /dev/tty.BLTH, /dev/tty.Bluetooth-Incoming-Port, /dev/tty.usbmodem144202, /dev/tty.usbmodemHIDLD1, } / {/dev/cu.BLTH, /dev/cu.Bluetooth-Incoming-Port, /dev/cu.usbmodem144201, /dev/cu.usbmodemHIDLD1, /dev/tty.BLTH, /dev/tty.Bluetooth-Incoming-Port, /dev/tty.usbmodem144201, /dev/tty.usbmodemHIDLD1, } => {/dev/cu.usbmodem144201, /dev/tty.usbmodem144201, }
Found upload port: /dev/cu.usbmodem144201
この動作により、シリアルポートが/dev/cu.usbmodem144202
のときも/dev/cu.usbmodem144201
に変化し、書き込み可となり、この動作は『自動書き込み(Download Bootへの移行)』の動作だと理解しました。(←この理解も正しく無い気がしています。)
・USB Mode
アップロード後のESP32-S3のUSBのモードを指定するものと思われます。
USB Mode | 目的 | 備考 |
---|---|---|
Hardware CDC and JTAG | Serial利用 | |
USB-OTG (TinyUSB) | USB-HID実装 |
単なるLチカのスケッチならどちらの指定でもちゃんと動作しますが、シリアルモニタに表示したい場合はHardware CDC and JTAG
を指定しないと表示しませんでした。逆に、スケッチ例KeyboardMessage
では、USB-OTG (TinyUSB)
を指定しないと機能しませんでした。
KeyboardMessage
のソースコードを見ると、コードの先頭にUSBモード
の判定が入っています。
#if ARDUINO_USB_MODE
#warning This sketch should be used when USB is in OTG mode
void setup(){}
void loop(){}
#else
: : :
Hardware CDC and JTAG
を指定してコンパイルすると、warningメッセージ「This sketch should be used when USB is in OTG mode」が出ていたのですが、処理が進んでアップロードができてしまうので、気づきませんでした。#warning
を#error
に変えてコンパイルエラーにすべきだと思います。
3. 書き込み性能
気持ちネイティブUSB接続の方が早く書き込みができるように感じたので、ESP32ダウンローダによるシリアル接続と、今回のネイティブUSB接続での、書き込み性能を比較してみました。
esptoolを使い、ファイル
adafruit-circuitpython-espressif_esp32s3_devkitc_1_n8r8-ja-7.3.3.bin
(約1.5MB)を書き込む速度をtimeコマンドで比較。通信以外の時間も含む。
書き込み | ボーレート | 速度 [秒] |
---|---|---|
ネイティブUSB接続 | 指定なし(115,200bps) | 23.672 |
ネイティブUSB接続 | 12,000,000bps | 23.675 |
シリアル接続 | 指定なし(115,200bps) | 94.538 |
シリアル接続 | 921,600bps | 28.440 |
シリアル接続 | 1,500,000bps | 25.127 |
数秒ですがシリアル接続より早いです。
『ESP32S3 Technical Reference Manualの「33.2 Features」』に「USBシリアル/JTAGデバイスは USB2.0デバイスですが、USB2.0規格で導入された高速スピード (480Mbps) モードではなく、フルスピード (12Mbps) のみをサポートする」と注記がありました。
ネイティブUSB接続の場合はボーレートの指定は意味が無い(?)ようです。
4. あらためて
この記事をまとめるにあたり、あらためて『ESP32-S3 Series Datasheet』を読むと、「3.5.12 USB Serial/JTAG Controller」に、「CDC-ACM supports host controllable chip reset and entry into download mode.」という記載がありました。また、『ESP32S3 Technical Reference Manual』の「Table 33-1. Standard CDC-ACM Control Requests」と「Table 33-2. CDC-ACM Settings with RTS and DTR」から、『SET_CONTROL_LINE_STATE』apiによって、RTS/DTRを制御していることが読み取れます。そして、RTS=1/DTR=0でESP32-S3をResetできるようです。
USB-CDCでシリアル通信をエミュレーションしていることが理解できて少しスッキリしました。
しかし、CircuitPython
が書き込んである状態のシリアルポート/dev/cu.usbmodemC7FD1A7E6ACC1
では書き込み出来ないことが腑に落ちません。また、どのモードでも書き込み後にリセット
が効かず手動リセットが必要で、これも納得できません。これについては、似たような指摘を『esp32-s3 does not reset after upload #6762』で発見しましたが、結論がよく分かりませんでした。
MicroPython
を書き込んで試してみましたが、状況は同じでした。ただし、シリアルポートは/dev/cu.usbmodem1234561
、USBデバイスツリーにはEspressif Device
と認識され、CircuitPython
の時のようなフラッシュドライブは現れません。
esptoolコマンドの--before
引数をdefault_reset
からusb_reset
に変えてみても、状況は変わらず。
この状態で、ESPアップローダ
でシリアル接続からアップロードすると、これは普通に問題なく可能で、シリアルポートは/dev/cu.wchusbserial53250003491
。
シリアル接続であれば、どの状態でも自動書き込みが可能です。
ここで、eFuseの設定値を確認しました。
ダウンロードに関係していそうなeFuseの値は以下の通りです。
DIS_FORCE_DOWNLOAD (BLOCK0) Disables forcing chip into Download mode = False R/W (0b0)
DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE (BLOCK0) Disables USB-Serial-JTAG download feature in UART = False R/W (0b0)
download boot mode
DIS_USB_OTG_DOWNLOAD_MODE (BLOCK0) Disables USB-OTG download feature in UART download = True R/W (0b1)
boot mode
DIS_DOWNLOAD_MODE (BLOCK0) Disables all Download boot modes = False R/W (0b0)
ENABLE_SECURITY_DOWNLOAD (BLOCK0) Enables secure UART download mode (read/write flas = False R/W (0b0)
h only)
DIS_USB_OTG_DOWNLOAD_MODE
がTrueになっているのが気になります。
4.1 eFuseを設定する
espefuseコマンドでDIS_USB_OTG_DOWNLOAD_MODE
をFalseに設定します。
$ espefuse.py --chip esp32s3 --port /dev/cu.wchusbserial53250003491 burn_efuse DIS_USB_OTG_DOWNLOAD_MODE 0
A fatal error occurred: New value is not accepted for efuse 'DIS_USB_OTG_DOWNLOAD_MODE' (will always burn 0->1), given value=0
$
しかし、エラーになりました。
0を1にする変更しか出来ない? 常にDisableでEnable出来ない???
シリアル接続でしたので、ネイティブUSB接続にて再度実行してみる。
$ espefuse.py --chip esp32s3 --port /dev/cu.usbmodem144201 burn_efuse DIS_USB_OTG_DOWNLOAD_MODE 0
A fatal error occurred: New value is not accepted for efuse 'DIS_USB_OTG_DOWNLOAD_MODE' (will always burn 0->1), given value=0
$
ネイティブUSB接続でも結果は同じ。
ESP32-S3の今のUSBモードがHardware CDC
のはずなので、これをUSB-OTG
に設定して、再度実行するも、結果は同じ。
$ espefuse.py --chip esp32s3 --port /dev/cu.usbmodem144201 burn_efuse DIS_USB_OTG_DOWNLOAD_MODE 0
A fatal error occurred: New value is not accepted for efuse 'DIS_USB_OTG_DOWNLOAD_MODE' (will always burn 0->1), given value=0
$
他、USBモードと接続形態(シリアル/ネイティブUSB)の組み合わせを試しましたが、すべて同じエラーでした。手詰まり、お手上げです。
ネイティブUSB接続の場合は自動書き込みはできず、DOWNLOAD BOOTで起動してから書き込むのが、唯一の手順なのでしょうか?
ESP32ネイティブUSB接続用のVCPドライバが必要な気がします・・・・・。
ネイティブUSB接続時の自動書き込みをご存知の方、ぜひ教えてください。
5. おわり
「自動書き込みはできない、書き込み後のリセットも効かない。」まったくスッキリしていませんが、ネイティブUSBによる書き込みができることは確認できたので、今回の記事は以上とします。
おまけ
ネイティブUSB接続ができると「Digispark ATTiny85」や「Beetle ATMega32u4」を、超高機能なESP32-S3に置き換えて、WiFi付き/Bluetooth付きにすることができます。実現したいことの内容とマイコンの単価を鑑みて、使い分けていこうと思います。
マイコン | サイズ(W x H) | 税込価格 | |
---|---|---|---|
ATTiny85-20SUR | 約5mm x 7mm | ¥150 | |
ATMega32u4 | 約12mm x 12mm | ¥640 | |
ESP32-S3-WROOM-1-N16R8 | 約14mm x 26mm (アンテナ部を含む) |
¥530 |
ESP32-S3のコスパの良さが際立ちます。チップサイズに制約が無いならATMega32u4の出番はもう無いかも・・・。