今回使う部品たちの価格表。1000円を少し超えてしまった。集合抵抗を表面実装タイプにしたり、つまみやキーキャップをもっと安く手に入れられるとよかったのかもしれない。
| 部品名 | 個数 | 備考 | 価格 |
|---|---|---|---|
| RP2040 Zero | 1 | マイコンボード | 1873円/5個 |
| 74HC153 | 1 | 2回路4入力 | 100円/20個 |
| ロータリーエンコーダ | 1 | スイッチ付き | 353円/5個 |
| つまみ | 1 | 直径26mm | 452円/5個 |
| 集合抵抗 10k | 2 | 4素子 | 30円/1個 |
| キースイッチ | 8 | 青軸 | 510円/50個 |
| キーキャップ | 8 | 半透明 | 400円/20個 |
| セラミックコンデンサ | 4 | 1608 | 200円/100個 |
| プリント基板 | 1 | JLCPCB | 3.2$/5枚 |
| 合計 | 1073円/個 |
基板が届くまで暇なので筐体の3Dモデルを作っておく。少し角度が欲しいので手前にかけて傾斜をかけておいた。基板はM3のねじで固定し、筐体側面にはモード切り替えスイッチ(Win/Mac)などを付けようと思う。このサイズなら全体を一気にプリントできそうで良い。
無事に発注した基板が届いた。割愛するが、一回目の青基板は5日で、二回目の黒基板は9日で届いた。
黒のほうが何倍もカッコよく見える。光沢はあるがマットな質感で良い。
マイコンボードの裏側にRP2040が実装されている。ボードを表面実装すると裏側が干渉するので基板を一部カットしている。
はんだ付けができた。フラックス(無洗浄タイプ)を初めて使ったが、表面実装部品はこの方がよかった。両面のプリント基板だから少しはんだのノリが悪い気がするのでフラックスのおかげで作業がスムーズに進んだ。
今回の基板設計で、ロータリーエンコーダに割り当てたGPIOが6と8だが、rotaryioは連続するピン番号でなければならないらしく、ライブラリの使用は諦めた。
import board
import digitalio
import time
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from adafruit_hid.mouse import Mouse
kbd = Keyboard(usb_hid.devices)
m = Mouse(usb_hid.devices)
# --- 共通関数 ---
def setup_pin(pin, direction, pull=None, value=None):
p = digitalio.DigitalInOut(pin)
p.direction = direction
if pull:
p.pull = pull
if value is not None:
p.value = value
return p
# --- ロータリーエンコーダ ---
clk = setup_pin(board.GP8, digitalio.Direction.INPUT, digitalio.Pull.UP)
dt = setup_pin(board.GP6, digitalio.Direction.INPUT, digitalio.Pull.UP)
last_clk = clk.value
# --- MUX制御 ---
pin_a = setup_pin(board.GP14, digitalio.Direction.OUTPUT)
pin_b = setup_pin(board.GP10, digitalio.Direction.OUTPUT)
pin_ENU = setup_pin(board.GP11, digitalio.Direction.OUTPUT, value=False)
pin_END = setup_pin(board.GP12, digitalio.Direction.OUTPUT, value=False)
pin_COLMU = setup_pin(board.GP26, digitalio.Direction.OUTPUT, value=True)
pin_COLMD = setup_pin(board.GP15, digitalio.Direction.OUTPUT, value=True)
# --- MUX出力 ---
mux_outU = setup_pin(board.GP9, digitalio.Direction.INPUT, digitalio.Pull.UP)
mux_outL = setup_pin(board.GP13, digitalio.Direction.INPUT)
# --- チャンネル選択 ---
def select_channel(a, b):
pin_a.value = a
pin_b.value = b
time.sleep(0.001)
キースイッチの割り当てと読み取り。MUXの入力A,Bと上段(U)、下段(D)のスイッチの読み取りを行う。
channels = [
(False, False, "sw2", "sw3"),
(True, False, "sw4", "sw5"),
(False, True, "sw6", "sw7"),
(True, True, "sw8", "sw9"),
]
def read_keys():
keys = {}
for a, b, k1, k2 in channels:
select_channel(a, b)
keys[k1] = mux_outU.value
keys[k2] = mux_outL.value
return keys
# --- キー割り当て ---
keymap = {
"sw2": (Keycode.CONTROL, Keycode.Z),
"sw3": (Keycode.CONTROL, Keycode.Y),
"sw4": (Keycode.CONTROL, Keycode.V),
"sw5": (Keycode.ENTER,),
"sw6": (Keycode.CONTROL, Keycode.C),
"sw7": (Keycode.CONTROL, Keycode.S),
"sw8": (Keycode.CONTROL, Keycode.A),
"sw9": (Keycode.ESCAPE,),
}
prev_keys = {k: False for k in keymap}
ループ部分。キースイッチの検知をしながらロータリーエンコーダの読み取りをする。キースイッチもロータリーエンコーダも入力の変化を検知している。
key(sw2, sw3, ...)に対するkey_statesを読んでスイッチを押したかを検知、押していた場合そのkeyのkey(Keycode.A, ...)をkbd.press(*codes)で入力。
while True:
key_states = read_keys()
for key, codes in keymap.items():
if prev_keys[key] and not key_states[key]:
kbd.press(*codes)
kbd.release_all()
prev_keys = key_states.copy()
# --- エンコーダ ---
current_clk = clk.value
if current_clk != last_clk:
m.move(wheel=1 if dt.value != current_clk else -1)
last_clk = current_clk
time.sleep(0.0001)
次回は筐体を3Dプリントしたい。