Node-RED の Pi-Keyboard フローで文字を取りこぼす問題


追記 (2020/11/17)

本修正は本家に取り込まれました。
Raspberry Piでご利用の方は下記コマンドを入力することで問題解決されるはずです。

$ cd $HOME/.node-red
$ npm update node-red-node-pi-gpio
$ node-red-restart

Node-RED の Pi-Keyboard フローで文字を取りこぼす問題

某SNSで

Pi-Keyboardを利用した際に文字を取りこぼすことがある。

という質問がありました。

問題は解決しないと気が済まないタチなので調査してみました。

Pi-Keyboardの仕組み

Pi-Keyboardというのは、こちら で管理されているノードでして、
USB接続されたキーボード入力を検出するためのものです。

ざっくりな仕組みとしては下記のようなイメージです

1. nrgpio.py

/dev/input/by-path/配下に存在するキーボードの入力を監視して入力があれば内容を標準出力する。

2. 36-rpi-gpio.js

1.の出力を拾ってPi-Keyboardの次のノードにメッセージを送る。

原因

問題はバースト的にキー入力イベントが発生した場合に起こります。

例えばabが同時に入力された場合

1.からは "a,0\n" "b,0\n" の2行分の文字列が出力されます。
2.は入力は1行である前提の実装で、キー入力間隔が長い場合は問題ありません。
しかしバースト的なキー入力で発生した場合、2行目以降のデータ "b,0\n" は捨てられてしまう。
.....という問題でした。

修正

RaspberryPi4では、下記にファイルがありますので公式修正されるまでは、直接書き換えてください。
$HOME/.node-red/node_modules/node-red-node-pi-gpio/36-rpi-gpio.js

下記のように入力は複数行あるという前提のソースに書き換えます。

36-rpi-gpio.js
    function PiKeyboardNode(n) {
        RED.nodes.createNode(this,n);
        var node = this;

        if (allOK === true) {
            node.child = spawn(gpioCommand+".py", ["kbd","0"]);
            node.status({fill:"green",shape:"dot",text:"rpi-gpio.status.ok"});

            node.child.stdout.on('data', function (data) {
-                var b = data.toString().trim().split(",");
-                var act = "up";
-                if (b[1] === "1") { act = "down"; }
-                if (b[1] === "2") { act = "repeat"; }
-                node.send({ topic:"pi/key", payload:Number(b[0]), action:act });
+                var d = data.toString().trim().split("\n");
+                for (var i = 0; i < d.length; i++) {
+                    if (d[i] !== '') {
+                        var b = d[i].trim().split(",");
+                        var act = "up";
+                        if (b[1] === "1") { act = "down"; }
+                        if (b[1] === "2") { act = "repeat"; }
+                        node.send({ topic:"pi/key", payload:Number(b[0]), action:act });
+                    }
                }
              }
            });

おまけ

改善確認のため raw というパラメータを追加すると状況がよく分かります。

          node.send({ topic:"pi/key", payload:Number(b[0]), action:act, raw:`${data}` });

ということで...

問題解決って、気持ちがいいですね。

※よく見るとGPIOInNodeでも同じ問題の対策実装が入っていますね

追記) プルリク作ってみた

あえてGPIOInNodeの実装に合わせて修正しプルリクを作ってみた。
https://github.com/node-red/node-red-nodes/pull/717

追記2) マージされました。

特にコメントも頂けず、、、1時間後にマージされました 😂
と思っていたら、しばらくたってコメント頂けた 😃
めでたしめでたし。