KMK Firmware Tips
Input/Output error が発生して書き込めなくなる
コピーしたファイルが大きい場合、コピーが完了する前に auto-reload が走ってしまい、このエラーが表示されることがあります。
soft reboot
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
code.py output:
Traceback (most recent call last):
File "code.py", line 21, in <module>
OSError: [Errno 5] Input/output error
Code done running.
Press any key to enter the REPL. Use CTRL-D to reload.
auto-reload を止めて、syncコマンドを実行してから行うと、問題なくなったため、そのような手順を踏んで書き込むようになりました。
そこで、以下を取り組みました。
- auto-reload を停止する
- copyした後に sync する
- soft-reset するキーを作る
auto-reload の停止する
boot.py に以下を書き込みます。
import supervisor
supervisor.set_next_stack_limit(4096 + 4096)
supervisor.disable_autoreload()
boot.py の再読込には、hard-resetする必要があるため、Console上で以下のコマンドを実行するか、一度USBを抜き差しします。
import microcontroller
microcontroller.reset()
参考: https://learn.adafruit.com/circuitpython-essentials/circuitpython-resetting#hard-reset-3087083-8
soft-reset するキーを作る
適当なキーを作って、実行後に任意のコードを実行できる、after_press_handler に soft-reaset を折り込みます。
simple_key_sequence を利用しているのは、新しいキーコードを生成したいためです。
def do_soft_reset(*args):
import supervisor
supervisor.reload()
kc_reset = KC.NO.clone()
kc_reset.after_press_handler(do_soft_reset)
このキーをどこかのキーに割り当てます。
USBをマウントしても書き込めなくなったときの回復方法
Input/Output error が発生した後にさらに書き込んだ場合など、そもそもUSBに書き込めなくなってしまって、リセットできないことがあります。
Circuit Python のファームウェアを書き込み直しても、ユーザコード領域消去されません。
ユーザコード領域をまっさらにする
REPLで以下を実行します。
import storage
storage.erase_filesystem()
Raspberry PI ごとまっさらにする
flash_nuke.uf2 を以下から入手します。
BOOTSELボタンを押しながらRaspberry Pi PICOのUSBを抜き差しし、マウントされたRaspberry Pi PICOのドライブに flash_nuke.uf2 をコピーします。
すると、まっさらな Raspberry Pi PICO になるので、再度 Circuit Python のファームウェアを書き込むなどします。
1キー押下で複数のキーを連続して送る
私の場合、VIM のためにESC押下時に英数キーを押させるようにしています。
kmk.handlers.sequences.simple_key_sequence, kmk.handlers.sequences.send_string を使うと簡単にキーコードを作れます。
from kmk.handlers.sequences import simple_key_sequence
kc_esc_eisu = simple_key_sequence([KC.LANG2, KC.ESC])
公式ドキュメント: https://github.com/KMKfw/kmk_firmware/blob/master/docs/sequences.md
他のキーと同時押しするとレイヤー追加、単独で押すとキーコードというようなキー
ModTapを使います。まず、拡張機能扱いなので、キーマップの作成の前に追加しておきます。このあとは、KC.MT、KC.LT などが使えるようになります。
from kmk.modules.modtap import ModTap
keyboard = KMKKeyboard()
modtap = ModTap()
以前のバージョンでは、同時押しの判定に時間が使われていましたが、今のバージョンではprefer_hold というパラメータが利用できます。
他のキーと押すとCTRL、単独で押すとタブキー
kc_ctl_tab = KC.MT(KC.TAB, KC.LCTRL, prefer_hold=True)
他のキーと押すとレイヤー適用、単独で押すとENTERキー
kc_raise_ent = KC.LT(raise_layer, KC.ENT, prefer_hold=True)
公式ドキュメント: https://github.com/KMKfw/kmk_firmware/blob/master/docs/modtap.md
レイヤー移動でLEDの色を変える
今どのOS用のレイヤーとして動いているか判別のために、LEDを利用しています。
led の制御は、kmk.extensions.rgb.RGB を使います。
このモジュールはNeoPixelライブラリ を利用しているため、まずNeoPixelライブラリをインストールします。以下のリンクから、adafruit-circuitpython-bundle-x.x-mpy-xxxxxxx.zip をダウンロードし、lib/neopixel.mpy を、マウントしたCIRCUITPY内のlib/に配置します。
import board
from kmk.kmk_keyboard import KMKKeyboard
from kmk.extensions.rgb import RGB
keyboard = KMKKeyboard()
led_ext = RGB(board.GP14, 1, val_default=6)
keyboard.extensions.append(led_ext)
ドキュメントによるとこれで、keyboard.pixels でRGBモジュールにアクセスできるはずなのですが、動作しなかったため、以下のコードを実行しています。
keyboard.pixels = led_ext
私の場合はキーを実行した後に任意のコードを動かすことができるafter_event_hadler で、このkeyboard.pixels を直接制御するようにしています。
レイヤー移動のキーに対して、これを設定します。
ubuntu_orange = (151, 62, 0)
mac_blue = (5, 0, 150)
kc_to_mac = KC.DF(mac_base_layer)
kc_to_mac.after_press_handler(lambda *args: keyboard.pixels.set_rgb_fill(mac_blue))
kc_to_linux = KC.DF(linux_base_layer)
kc_to_linux.after_press_handler(lambda *args: keyboard.pixels.set_rgb_fill(ubuntu_orange))
なお、KC.DF は規定レイヤーを変更するキーです。
MacOS で CIRCUITPY が自動マウントされるのを防ぐ
まず、diskutil でマウントされたドライブのUUIDを調べます。
$diskutil info /Volumes/CIRCUITPY/
Volume UUID: 94459B9C-0F83-37A4-8891-237DDEA4ABE2
そして、/etc/fstab にマウントしないように書き込みます。
# sudo vi /etc/fstab
UUID=D53BCE64-0271-378E-9020-DBE94B5D55F4 none msdos r,noauto
KMK Firmware をコンパイルして容量を減らす
MicroPython には mpy-cross というコードをバイナリ化するコンパイラがあり、おおよそ容量が半分くらいになる。Circuit Python でもこれは使うことができる。
Circuit Python 用のmpy-cross は以下で入手できる。
利用するCircuit Pythonのバージョンのmpy-cross をダウンロードして、PATHの通ったところに置く。
KMK Firmware の Makefile には mpy-cross の命令が書かれており、これを実行する。
cd lib/kmk_firmware
make compile
すると、.compile/
というディレクトリに、コンパイル後のファイルが出力される。ソースコード1つにつき1つのmpyバイナリができる。
これを、CIRCUITPY ドライブの lib/ に代わりに置く。
PC上で容量を確認すると、ほとんど小さくなっていないが、CIRCUITPY 内に置くと小さくなっているのを確認できる。
Pythonコードの容量
mpyの容量
keymapのコードがミスっていて動かなくなるのを防ぐ
keymap をちょっといじっているだけのつもりが、syntax error やKC.xxx を間違えたりとかして、スクリプトをアップロードして、動かなくなり、わざわざ別のキーボードを取り出してデバッグする、ということを何度もやりましたが、なかなか生産性が落ちる。
そこで以下を組み込むことにした。
- キーマップとキーボード定義のコードを分けて、エントリーポイントになる code.py に、キーボードの定義を記述し、キーマップをkeymap.py に別にして、読み出す形にする。
- 今編集する keymap.py と、バックアップ用の backup_keymap.py の2つのコードを用意する。
- code.py に keymap.py を読み出しを、try ~ except でくくって、例外をキャッチして、その時には backup_keymap.py を代わりに呼び出すようにする。
- backup_keymap.py を読み出している時にはわかるように、LEDの色を赤色にする。
実際に組み込んだコードはこちら
CircuitPython のシリアルコンソールの開き方
Linux/MacOS の場合
tty のパスがLinuxとMacOSで異なる
- Linux /dev/ttyACM0
- MacOS /dev/tty.usbmodem1234
minicomを使う場合
minicom -D /dev/ttyACM0
離れるには、Ctrl+A -> q -> enter
screen を使う場合
screen /dev/ttyACM0 115200
離れるには、Ctrl+A -> k -> y
Windows の場合
まず、デバイスマネージャを開き、USBシリアルデバイスの番号を調べる(スクリーンショットはCOM8)
PuTTYをインストールする
PuTTYでConnection type に Serial を選択し、Serial Line に先の COM8 、Speedに 115200 を入力し、Openで開く。
覚えておくとよいコマンド
- 実行中のプログラムを中断して、REPLに移行する → Ctrl+C
- REPL実行中に、Soft Reset をかけて、また code.py を実行する → Ctrl+D
DigitalOベースのKeyMatrixを使う
今日(2022-05-21)時点のmasterブランチでは、キーのマトリックススキャンをするためのモジュールとして、CircuitPython付属の keypad モジュールを使うようになっている。このモジュールでは、rowとcolの引数に、物理GPIOを示す Pin を要求するようになっている。
Sparrow62v2では、rowとcolの引数がDigitalIOクラスであったことを利用して、DigitalIOクラスをインターフェースにもつ独自クラスを作ってMCP23017での右手キーボードの制御を行っている。今までのDigitalIOのほうがSparrow62v2 ではありがたかった。
この部分、キーマトリックスの部分はモジュールとして設定可能になっている。
from kmk.kmk_keyboard import KMKKeyboard
from kmk.scanners.digitalio import MatrixScanner
keyboard = KMKKeyboard()
# 中略
keyboard.matrix = MatrixScanner(
cols=keyboard.col_pins,
rows=keyboard.row_pins,
diode_orientation=keyboard.diode_orientation,
)
keyboard.go()
こちらを使えば今までどおり DigitalIO ベースで動いてくれる。