課題: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 filesystem
Adafruitのバンドル(ライブラリ一式)を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 filesystem
ESP32S3で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を作る方法【自作マウス・自作キーボード】