Open10

KMK Firmware Tips

Atsushi MorimotoAtsushi Morimoto

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)

このキーをどこかのキーに割り当てます。

Atsushi MorimotoAtsushi Morimoto

USBをマウントしても書き込めなくなったときの回復方法

Input/Output error が発生した後にさらに書き込んだ場合など、そもそもUSBに書き込めなくなってしまって、リセットできないことがあります。

Circuit Python のファームウェアを書き込み直しても、ユーザコード領域消去されません。

ユーザコード領域をまっさらにする

REPLで以下を実行します。

import storage
storage.erase_filesystem()

Raspberry PI ごとまっさらにする

flash_nuke.uf2 を以下から入手します。

https://learn.adafruit.com/getting-started-with-raspberry-pi-pico-circuitpython/circuitpython#flash-resetting-uf2-3083182-8

BOOTSELボタンを押しながらRaspberry Pi PICOのUSBを抜き差しし、マウントされたRaspberry Pi PICOのドライブに flash_nuke.uf2 をコピーします。

すると、まっさらな Raspberry Pi PICO になるので、再度 Circuit Python のファームウェアを書き込むなどします。

Atsushi MorimotoAtsushi Morimoto

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

Atsushi MorimotoAtsushi Morimoto

他のキーと同時押しするとレイヤー追加、単独で押すとキーコードというようなキー

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

Atsushi MorimotoAtsushi Morimoto

レイヤー移動でLEDの色を変える

今どのOS用のレイヤーとして動いているか判別のために、LEDを利用しています。

led の制御は、kmk.extensions.rgb.RGB を使います。

このモジュールはNeoPixelライブラリ を利用しているため、まずNeoPixelライブラリをインストールします。以下のリンクから、adafruit-circuitpython-bundle-x.x-mpy-xxxxxxx.zip をダウンロードし、lib/neopixel.mpy を、マウントしたCIRCUITPY内のlib/に配置します。

https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases

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 は規定レイヤーを変更するキーです。

Atsushi MorimotoAtsushi Morimoto

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
Atsushi MorimotoAtsushi Morimoto

KMK Firmware をコンパイルして容量を減らす

MicroPython には mpy-cross というコードをバイナリ化するコンパイラがあり、おおよそ容量が半分くらいになる。Circuit Python でもこれは使うことができる。

Circuit Python 用のmpy-cross は以下で入手できる。

https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library?view=all#mpy-3106477-16

利用する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の容量

Atsushi MorimotoAtsushi Morimoto

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の色を赤色にする。

実際に組み込んだコードはこちら

https://github.com/74th/sparrow62-kmk/blob/14ff44695e434c0b3fae5af70b2d4c433e6767cf/code.py#L190-L231

Atsushi MorimotoAtsushi Morimoto

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をインストールする

https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html

PuTTYでConnection type に Serial を選択し、Serial Line に先の COM8 、Speedに 115200 を入力し、Openで開く。

覚えておくとよいコマンド

  • 実行中のプログラムを中断して、REPLに移行する → Ctrl+C
  • REPL実行中に、Soft Reset をかけて、また code.py を実行する → Ctrl+D
Atsushi MorimotoAtsushi Morimoto

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 ベースで動いてくれる。