課題:E-WorkでTV会議している時、急に振られてMuteボタン押すつもりが退室ボタンを押してしまったり、2画面でマウスがどっかに行ってたりして困る
解決策:Mute On/Offボタンを作る (通信販売で、そういった商品は売られているけど高いし、PythonのUSBライブラリ使うと作れるはず)
アプローチ:CircuitPythonにはUSBライブラリが提供されていて、HID Keyboardとして実装できるはず
結論: ESP32PICOを搭載したM5 ATOM S3では、Windows PCと接続した際、正しくUSBキーボードと認識されなかった。
詳細:
普段はESP32DevKitを使ってるが、デカいので、コンパクトなM5 Atomを使ってみる。
M5 ATOM Lite用CurcuitPython FirmをDLしてインストールする
ATOM Lite ESP32 IoT Download
マイコンはESP32Picoらしい
FirmDLモードはGPIO0をGNDに落とすと記憶しているが、USB Serial チップにつながっている。パッケージに入ってると自力でどうにかするというのが段々難しくなる(し、ESP32/M5 Stack/Stick/ATOM etc...)バリエーションが多くてどうやってファームを焼くのか混乱)
binなので、esptool等で焼くのだろうと推測
と思っていたら、AdafruitのDLサイトで、DLボタンの下がInstollerボタンで、ブラウザからローカルPCのUSBシリアルを叩いてファームが焼けるのであった。すごい技術。。
待ってる間にキャプチャしてみる

CircuitPython 8.1.10が入った。そんなに他人(=Adafruit社様)任せでいいのか・・自分。。
Adafruit CircuitPython 8.1.0 on 2023-05-22; M5Stack Atom Lite with ESP32
>>> help('modules')
__future__ canio mdns struct
__main__ collections memorymap supervisor
_asyncio countio microcontroller synthio
_pixelmap digitalio micropython sys
adafruit_bus_device displayio msgpack terminalio
adafruit_bus_device.i2c_device dualbank neopixel_write time
adafruit_bus_device.spi_device errno nvm touchio
adafruit_pixelbuf espidf onewireio traceback
aesio espnow os ulab
alarm espulp ps2io ulab.numpy
analogbufio fontio pulseio ulab.numpy.fft
analogio framebufferio pwmio ulab.numpy.linalg
array frequencyio rainbowio ulab.scipy
atexit gc random ulab.scipy.linalg
audiobusio getpass re ulab.scipy.optimize
audiocore hashlib rotaryio ulab.scipy.signal
audiomixer i2cperipheral rtc ulab.scipy.special
binascii i2ctarget sdcardio ulab.utils
bitbangio io select uselect
bitmaptools ipaddress sharpdisplay vectorio
board json socketpool watchdog
builtins keypad ssl wifi
busio math storage zlib
Plus any modules on the filesystemAdafruitのバンドル(ライブラリ一式)をDLサイトから落としてきて、必要な分だけATOMに転送する。
adafruit_hid/
$ ampy --port /dev/ttyS19 mkdir lib/adafruit_hid
ampyではbinaryは転送できないようなので、ソースを置くことにする。
Gitのレポジトリは以下
Adafruit_CircuitPython_HID/adafruit_hid at main · adafruit/Adafruit_CircuitPython_HID · GitHub
作業していてきづいたのだが、M5 Atom用CircuitPythonにはusbドライバが入っていない。これはESP32 Picoのアーキテクチャによるものだろうか。ハードブロックとかポーティングソースを見ていないので原因は分からないが、無いものは無いので、USBデバイスとしては動かせない。ということで、他のM5 Atomを確認する。確かに、USB/Serialは外付けで、ESP32の周辺I/OとしてUSBを内蔵していないからUSBはシリアルとしてしか使えないということか?
AtomS3 Lite ESP32S3用のFirmであればusbドライバ内蔵なので、これを使ってみる。先ほど書いたようにブラウザからインストールできてしまうのでめちゃ楽(だけどどういう仕組みでDLできるのか全く分からず)
Webからインストールしようとしたが、インジケータが全く進まず、ESP32S3(Atom S3)にはインストーラからインストールできない。 teratermで繋ぐと以下と表示される。ファームが不整合か?
invalid header: 0xffffffff invalid header: 0xffffffff invalid header: 0xfESP-ROM:esp32s3-20210327 Build:Mar 27 2021 rst:0x7 (TG0WDT_SYS_RST),boot:0x28 (SPI_FAST_FLASH_BOOT) Saved PC:0x40048839 invalid header: 0xffffffff invalid header: 0xffffffff
WebSerialがだめならesptoolを使えという説明ページがあり、開始番地は0x0の様である
esptool.py --chip esp32 --port /dev/tty.usbserial-1144440 erase_flash esptool.py --port /dev/tty.usbserial-1144440 write_flash -z 0x0 firmware.bin
上記を信じて0番地からで焼いてみる。
Command line ESPTool | CircuitPython on ESP32 Quick Start | Adafruit Learning System
チップがesp32s3なのでそこは変更する
実際のFlash消去、firmインストールの手順は以下(MacBookにインストールしたUbuntuで実行)
$ esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash esptool.py v4.6.1 Serial port /dev/ttyACM0 Connecting... Chip is ESP32-S3 (revision v0.1) Features: WiFi, BLE Crystal is 40MHz MAC: dc:54:75:cb:bb:8c Uploading stub... Running stub... Stub running... Erasing flash (this may take a while)... Chip erase completed successfully in 1.0s Hard resetting via RTS pin... $ esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash 0 adafruit-circuitpython-m5stack_atoms3_lite-en_US-8.1.0.bin esptool.py v4.6.1 Serial port /dev/ttyACM0 Connecting... Chip is ESP32-S3 (revision v0.1) Features: WiFi, BLE Crystal is 40MHz MAC: dc:54:75:cb:bb:8c Uploading stub... Running stub... Stub running... Configuring flash size... Flash will be erased from 0x00000000 to 0x00164fff... Compressed 1460240 bytes to 987170... Wrote 1460240 bytes (987170 compressed) at 0x00000000 in 9.1 seconds (effective 1286.1 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin...
ブートしてTeratermから接続して確認、以下の通り、usb_hidが組み込まれており、これでHIDが作れると期待
Adafruit CircuitPython 8.1.0 on 2023-05-22; M5Stack AtomS3 Lite with ESP32S3
>>> help('modules')
__future__ collections micropython terminalio
__main__ countio msgpack time
_asyncio digitalio neopixel_write touchio
_bleio displayio nvm traceback
_pixelmap dualbank onewireio ulab
adafruit_bus_device errno os ulab.numpy
adafruit_bus_device.i2c_device espidf ps2io ulab.numpy.fft
adafruit_bus_device.spi_device espnow pulseio ulab.numpy.linalg
adafruit_pixelbuf espulp pwmio ulab.scipy
aesio fontio rainbowio ulab.scipy.linalg
alarm framebufferio random ulab.scipy.optimize
analogbufio frequencyio re ulab.scipy.signal
analogio gc rgbmatrix ulab.scipy.special
array getpass rotaryio ulab.utils
atexit hashlib rtc usb_cdc
audiobusio i2cperipheral sdcardio usb_hid
audiocore i2ctarget select usb_midi
audiomixer io sharpdisplay uselect
binascii ipaddress socketpool vectorio
bitbangio json ssl watchdog
bitmaptools keypad storage wifi
board math struct zlib
builtins mdns supervisor
busio memorymap synthio
canio microcontroller sys
Plus any modules on the filesystemESP32S3でUSBブロックが内蔵されていてCircuitPythonでUSBがネイティサポートされているお陰か、ESP32S3のFlashがUSBストレージとしてPCから参照可能になっている。だから、HID用ドライバ類をDrag&Dropでコピーできてしまう。
Bundle for Version 8.xをPCにDLして展開して、lib/adafruit_hid配下のファイル一式をM5にコピーする。
コピーした後のCircuitPythonから見た /lib/adafruit_hid配下のファイル類
>>> os.listdir('lib/adafruit_hid')
['consumer_control.mpy', 'consumer_control_code.mpy', 'keyboard.mpy', 'keyboard_layout_base.mpy', 'keyboard_layout_us.mpy', 'keycode.mpy', 'mouse.mpy', '__init__.mpy']AtomS3の液晶パネルはプッシュボタンにもなっていて、接続されているIOがマイコンの足のGPIOのどれに相当するのか分からず。Arduino的にはG41となっていて、基本的にGPIOと同じ番号のようなので、GPIO41か?
GitHubのM5Stack ATOMS3 ポーティングでは、以下のように、GPIO41は、MP_QSTR_BTNとして定義されている。
{ MP_ROM_QSTR(MP_QSTR_BTN), MP_ROM_PTR(&pin_GPIO41) },circuitpython/ports/espressif/boards/m5stack_atoms3_lite/pins.c at main · adafruit/circuitpython · GitHub
boardモジュール内でもBTNが定義されており、これを使うとLEDのボタンの状態が取れるはず
>>> dir(board) ['__class__', '__name__', 'A1', 'A2', 'A4', 'A5', 'A6', 'A7', 'BTN', 'D1', 'D2', 'D38', 'D39', 'D5', 'D6', 'D7', 'D8', 'I2C', 'IR_LED', 'NEOPIXEL', 'PORTA_I2C', 'PORTA_SCL', 'PORTA_SDA', 'board_id']
以下でLCDのボタンの押下は取得できた
import digitalio button = digitalio.DigitalInOut(board.BTN) button.switch_to_input(pull=digitalio.Pull.UP) >>> dir(button) ['__class__', '__enter__', '__exit__', 'value', 'deinit', 'direction', 'pull', 'switch_to_input', 'switch_to_output'] >>> button.value True >>> button.value False
TV会議の音声Muteは、[Ctrl]+[Shift]+[M]を一度に押すのだけど、デバッグ目的で、まずは[M]を送るコードを書いてテストする。
import time
import digitalio
import board
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard import Keycode
button = digitalio.DigitalInOut(board.BTN)
button.switch_to_input(pull=digitalio.Pull.UP)
keyboard = Keyboard(usb_hid.devices)
while True:
if not button.value:
print("p")
# press Ctrl(CONTROL) + Shift(SHIFT) + M(M)
#keyboard.press(Keycode.CONTROL, Keycode.SHIFT, Keycode.M)
keyboard.send(Keycode.M)
time.sleep(0.1) # wait 100ms
#keyboard.release_all()
time.sleep(0.1) # wait 100ms
print("..")コードは上記なのだが、、Windowsでは動かない(Mが入力されない)。Ubuntu だとMが入力されるのだが。。
ループが回っているのかどうかもあやしい。
■追記
自宅にWindowsマシンが2台あって、普段開発に使ってるPCはNGで、E-Work用のPCはMが入力された。だから、E-Work用(=TV会議で使うPC)だとUSBキーボードとして使えそうだ。
■追記
最初は問題なかったが、何がきっかけか分からないが、「デバイスが正しく設定されていません」といったメッセージが出るようになった。結果、Windowsマシンは2台とも正しく接続できない状況 (MacのUbuntuはOKっぽい)
■今後の取り組み
他のマイコンを使ってどう動くのか見てみる。特に、RPi PicoでUSBキーボードを作る事例があり、これだとうまくいってそうだ。ESP32PICO以外だと動くのかもしれない。
■参考URL
RaspberryPi Pico CircuitPythonでUSB HIDを作る方法【自作マウス・自作キーボード】