RaspberryPiのUSB接続を認識するの続き。

RaspberryPiはキーボード/モニタ無しで LAN のみ接続で使っている人が多いと思うんだけど コマンドを実行したい時に ssh で接続するのが以外に面倒。

とくに shutdown と reboot ぐらいは端末が無くても出来るようにしたい。

で、思い付いたのが joypad で入力する方法。 GPIO を使ってボタンを付ける方法も考えたのだが物理的な工作とか考えると USB Joypad の方が圧倒的にコストパフォーマンスが良い。

今回、購入したのはこれ。安っ。

joypad の入力シーケンスから任意のコマンドを実行するスクリプトは自前で作成。

入力シーケンスと言うのは ↑↓←→ABAB みたいなゲームの隠しコマンドとかで使うやつ。 単独のボタンにコマンド割り当だとうっかり実行しちゃうので。

必要なアプリが2つあるのでインストールする。

$ sudo apt-get install joystick
$ sudo apt-get install gawk
メインの AWK スクリプト。gawk専用(/home/pi/jscmd/jscmd.awk)
BEGIN {
    loadConfig(CONFIG); # -v CONFIG=file_name
    namesSize = split(config["BUTTONS"], names, /,/);
    sequence = "";
    lastModify = systime();
}

/^Event: type 1,/ {
    n = $7; sub(/,$/,"",n);
    v = $9;
    key = names[n+1];
    flag[key] = v;
    if (v) addKey();
}

/^Event: type 2,/ {
    n = $7; sub(/,$/,"",n);
    v = $9;

    if (n == 0) {
        flag["RIGHT"] = (v>0)?1:0;
        flag["LEFT"]  = (v<0)?1:0;
    } else {
        flag["DOWN"] = (v>0)?1:0;
        flag["UP"]   = (v<0)?1:0;
    }
    if (v != 0) addKey();
}

func addKey() {
    t = systime();
    if (t > lastModify+2) sequence = "";
    lastModify = t;

    keys = "";
    for (i=1; i<=namesSize; i++) {
        if (flag[names[i]]) {
            keys = sprintf("%s+%s",keys,names[i]);
        }
    }
    sub(/^[+]/,"",keys);
    sequence = sprintf("%s %s",sequence,keys);
    gsub(/[ ]+/," ",sequence);
    gsub(/^[ ]/,"",sequence);

    print ">",sequence;
    if (config[sequence]) {
        print "exec ",config[sequence];
        system(config[sequence]);
        sequence = "";
    }
}

func loadConfig(CONFIG_FILE) {
    print "--- Config --------------------------";
    while ((getline < CONFIG_FILE) > 0) {
        idx = index($0,"=");
        if (idx <= 0) continue;
        key = substr($0,0,idx-1); sub(/^[ ]+/,"",key);sub(/[ ]+$/,"",key);
        val = substr($0,idx+1); sub(/^[ ]+/,"",val);sub(/[ ]+$/,"",val);
        print key,"=",val;
        config[key] = val;
    }
    print "-------------------------------------";
}
起動スクリプト(/home/pi/jscmd/jscmd-start.sh)
#!/bin/sh -fx

BASE=`dirname $0`
JSDEV=$DEVNAME
if [ "$JSDEV" = "" ] ; then
        JSDEV=/dev/input/js0
fi

cd $BASE
jstest --event $JSDEV | gawk -v CONFIG=jscmd.cnf -f jscmd.awk &
udev設定(/etc/udev/rules.d/10-joypad.rules)
ACTION=="add",\
        SUBSYSTEMS=="usb", \
        KERNEL=="js*", \
        RUN:="/home/pi/jscmd/jscmd-start.sh", \
        OPTIONS+="last_rule"
  • 設定の反映
$ sudo service udev reload
コマンド設定ファイル(/home/pi/jscmd/jscmd.cnf)
BUTTONS=A,B,X,Y,L,R,select,start,RIGHT,LEFT,DOWN,UP

UP UP DOWN DOWN A B A B = shutdown -h now
LEFT LEFT RIGHT RIGHT A B A B = reboot
  • 最初の BUTTONS はJoypadのボタンの定義。
    • jstest コマンドで確認して「,」区切りでボタンの名前を順番に並べる。
    • 十字キーが Axes: の場合は RIGHT,LEFT,DOWN,UP を最後に追加する。
  • 3行目以降が入力シーケンスとコマンドのペア。「=」区切り。
    • 入力シーケンスはBUTTONSで定義した名前を空白区切りで記述。
    • 同時押しの場合は「A+B」のように書ける。
      • 但し、「B+A」は不可。BUTTONSでの定義順の必要がある。
    • コマンドは1行以内。
jstestの使い方

joypad は製品毎にボタンの配置が異るのでテストツールで確認する。

$ jstest /dev/input/js0
Driver version is 2.1.0.
Joystick (USB,2-axis 8-button gamepad  ) has 2 axes (X, Y)
and 8 buttons (Trigger, ThumbBtn, ThumbBtn2, TopBtn, TopBtn2, PinkieBtn, BaseBtn, BaseBtn2).
Testing ... (interrupt to exit)
Axes:  0:     0  1:     0 Buttons:  0:off  1:off  2:off  3:off  4:off  5:off  6:off  7:off



これで Joypad をUSBに差して ↑↑↓↓ABAB と入力して shutdown すれば成功です。