キーボードのスイッチとダイオードを自動配置させる
meishi
Lastmod: 2023-06-23

先日書いた記事から、実際に配置してみようという段になったとき、配置するのめんどいなと思ったので、自動化したくなった。ので、kicadのpython APIを使った。その記録

罠たち

  • Python 2.7.18が使われてる
  • Kicad 6.0のRelease noteで、現在あるAPIが置き換わることが明言されているため、今から学ぶのは微妙
  • 公式APIノートが息してない。
    • 説明がないのはいい方で、そもそもAPIのドキュメントに名前が載ってないメソッドの方がおおい。
    • dir()を使って生えてるメソッド確認したり、先駆者のブログ、フォーラムのお世話にならないとお話にならない。

動作確認

一通り罠にはまってから、実際に動かしてちゃんと動いたコードをコピペする。以下のコードは次のような機能を持つ。

  • pcbnewに配置されているすべてのスイッチの位置座標を(x, y) -> (x + 1, y + 3)する。

途中でダイオードの情報も取得しているけれど、最後のコードブロックでスイッチだけアップデートしている。特に理由はない。

import pcbnew
import re
import operator

def main(prefix_switch="SW", prefix_diode="D"):
    """Align switches and diodes

    Args:
        prefix_switch (str, optional): Prefix of reference of switch. Defaults to "SW".
        prefix_diode (str, optional): Prefix of reference of switch. Defaults to "D".
    """
    board = pcbnew.GetBoard()
    switches = []
    diodes = []
    
    name_pattern = re.compile("^[^0-9]*")
    number_pattern = re.compile("[0-9]*$")
    for module in board.GetModules():
        ref = module.Reference().GetText()
        dict_ = { 
            "number": int(number_pattern.search(ref).group()),
            "module": module
        }
        name = name_pattern.search(ref).group()
        if name == prefix_switch:
            switches.append(dict_)
        elif name == prefix_diode:
            diodes.append(dict_)
    
    switches.sort(key=operator.itemgetter("number"))
    diodes.sort(key=operator.itemgetter("number"))
    
    for s, d in zip(switches, diodes):
        old_position_x, old_position_y = pcbnew.ToMM(s["module"].GetPosition())
        new_position_x, new_position_y = (pcbnew.FromMM(old_position_x + 1), pcbnew.FromMM(old_position_y + 3))
        s["module"].SetPosition(pcbnew.wxPoint(new_position_x, new_position_y))
    
    pcbnew.Refresh()

これを書く上でハマったこと。

  • 書く前に、GetPosition()で取れる値がmm単位でないことは把握していたが、SetPosition()には、GetPosition()で得た値と同じように入れては設定できないこと。pcbnew.FromMM()で処理した上で、pcbnew.wxPoint()で処理しないといけない。
  • Pythonのインタープリター上での変更は、pcbnew.Refresh()を叩かないと反映されない。

これを書いてて気づいたことなど。

  • フットプリントの名前を取得したいとき、フットプリントで分類したいときは、Module.GetFPID().GetLibItemName()で取得できるとフォーラムに書いてあった。確かにできた。
  • GetPosition()はModuleの中心座標を返す。

個人的に、sortしたときにオブジェクトがコピーされたりして、参照がうまくいかず、編集が反映されないのではないかと危惧していたが、これは杞憂だった。途中でList.sort()を叩いているけど問題なくpcbnewで反映された。copy()なんかを使うと、もしかしたら変なことになるかもしれない。

実際のコード

ここまでかなりめんどかったけれど、取得と更新の仕方のお作法を押さえれば後はなんてことないはず。

スイッチの大きさ、ダイオードの大きさは、本当はプログラムが認識するべきなんだろうけど、ここは人間のために人間が教えてあげることにした。

  1. SwitchとDiodeの縦、横の長さ
  2. クリアランス
    1. Between switches
    2. Between switches and diodes
    3. Between groups of switches and diodes
  3. Diodeの上下方向のズレ距離
  4. Diode間の中心座標間の距離

スイッチの回転角は入力パラメータから計算できそうなので計算させる。

スイッチの高さを$h$, $\theta$回転後のスイッチとスイッチのクリアランスを$d_c$としたとき、各スイッチ間の距離$d$は

$$ d = \frac{h}{\cos{\theta}} - d_c $$

となる。

できたスクリプトがここに入る予定だった…