Node-RED の Grove Base HAT ノードが動かなくなっていたので、ひとまず手元で動くように修正したメモ

Node-RED の Grove Base HAT ノードが動かなくなっていたので、ひとまず手元で動くように修正したメモです。

この記事は Seeed UG Advent Calendar 2022 の 16 日目の記事です。

背景

私が作った node-red-contrib-grove-base-hat ノードが、

  • 2022/8 ごろのバージョンの Raspberry Pi OS
  • 2022/11 ごろのバージョンの grove.py

の組み合わせで動かなくなっていたことに、最近イチからセットアップしたら気づきました。

以前の私のリリースから長い時間が経過しているので、どれがクリティカルな原因か分からないのですが、以前のバージョンはひとまず固定して、

Release v0.0.10 · 1ft-seabass/node-red-contrib-grove-base-hat

手元で動くようにして方針が見えたら次のバージョンで修正してみます。

以前のコード

まず、 Node-RED ノードの仕組み。grove-led を例に。

var path = require('path');

module.exports = function(RED) {
    function GroveLedNode(config) {

        // console.log(path.join(__dirname , 'grove-led.py'));
        // console.log("__dirname " , __dirname );

        RED.nodes.createNode(this,config);

        this.port_number = config.port_number;

        this.port_name = "D" + this.port_number;
        this.status({fill:"blue",shape:"dot",text:this.port_name});
        
        var node = this;
        node.on('input', function(msg) {
            // msg.payload = msg.payload.toLowerCase();
            const gpio_pin = this.port_number;
            const exec = require('child_process').exec;
            this.status({fill:"yellow",shape:"ring",text:this.port_name + " connecting"});
            exec('python -u ' + path.join( __dirname , 'grove-led.py') + ' ' + gpio_pin + ' ' + msg.payload, (err, stdout, stderr) => {
                if (err) { console.log(err); }
                // console.log(stdout);
                this.status({fill:"green",shape:"dot",text:this.port_name + " connected"});
            });
            
            // node.send(msg);
        });
    }
    RED.nodes.registerType("grove-led",GroveLedNode);
}

いたってシンプル。ここは問題なく動いてました。grove.py を参考にして作った後述する grove-led.py という python コードを実行するものです。

以下は grove-led.py の v0.0.10 での中身です。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Grove Base Hat for the Raspberry Pi, used to connect grove sensors.
# Copyright (C) 2018  Seeed Technology Co.,Ltd.
'''
This is the code for
    - `Grove - Red LED    <https://www.seeedstudio.com/Grove-Red-LED-p-1142.html>`_
    - `Grove - Green LED  <https://www.seeedstudio.com/Grove-Green-LED-p-1144.html>`_
    - `Grove - Purple LED <https://www.seeedstudio.com/Grove-Purple-LED-3m-p-1143.html>`_
    - `Grove - White LED  <https://www.seeedstudio.com/Grove-White-LED-p-1140.html>`_

Examples:

    .. code-block:: python

        import time
        from grove.grove_led import GroveLed

        # connect to pin 5(slot D5)
        PIN   = 5
        led = GroveLed(PIN)

        while True:
            led.on()
            time.sleep(1)
            led.off()
            time.sleep(1)
'''
import time
import os # add for node-red
import json # add for node-red
import sys #add for node-red
from grove.gpio import GPIO

__all__ = ['GroveLed', 'GPIO']

class GroveLed(GPIO):
    '''
    Class for Grove - XXXX Led

    Args:
        pin(int): number of digital pin the led connected.
    '''
    def __init__(self, pin):
        super(GroveLed, self).__init__(pin, GPIO.OUT)

    def on(self):
        '''
        light on the led
        '''
        self.write(1)

    def off(self):
        '''
        light off the led
        '''
        self.write(0)


Grove = GroveLed


def main():

    # print disable
    sys.stdout = open(os.devnull, 'w')

    from grove.helper import SlotHelper
    sh = SlotHelper(SlotHelper.GPIO)
    pin = sh.argv2pin()
    argvs = sys.argv #add for node-red

    led = GroveLed(pin)

    # print enable
    sys.stdout = sys.__stdout__

    control =  argvs[2]
    # print (control == "1")
    if (control == "1"):
        led.on()
    if (control == "0"):
        led.off()

if __name__ == '__main__':
    main()


Node-RED から出された指示を実行してるんですが、2 年くらい前の grove.py の LED を動かすソースコードを、そのままコピーで持って来て使ってたんです。

これが、どうも最新の grove.py がある状態だと、うまく動かない様子。

アプローチ変更したら成功

grove.py を良く読んでいると、どうも、grove.py 自体はライブラリとして使って、自分の書いたコードで呼び出す使い方が良いようです。

ということで、grove-led.py の内容を以下のように変えてみました。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time
import os # add for node-red
import json # add for node-red
import sys #add for node-red

from grove.grove_led import GroveLed

def main():
  
    # print disable
    # sys.stdout = open(os.devnull, 'w')

    argvs = sys.argv #add for node-red

    pin = int(argvs[1])

    led = GroveLed(pin)

    # print enable
    # sys.stdout = sys.__stdout__

    control = argvs[2]
    # print (control == "1")
    if (control == "1"):
        led.on()
    if (control == "0"):
        led.off()

if __name__ == '__main__':
    main()

こうしてみたところ、Node-RED からこの python への指示を出す部分は一切変更せずに、LED が光るのが復活しました!

他でも同じように修正してみたらうまくいった

超音波距離センサーとボタンについても、同じように grove.py 自体はライブラリとして使って、自分の書いたコードで呼び出す方式で試してみたところ、うまく動きました。

ライブラリとしての使い方を、ちゃんとドキュメント読み込まないといけないのはありましたが、今後も grove.py が更新されても、ライブラリとして呼ぶ形で動作を保つと思うので、今回のやりかたのほうが、ちゃんと動く強度がありそうです。

たしかに、前回のやり方は、今思うと、そのときのライブラリの仕組みをコピーして持ってくるので、ライブラリ自体が更新されると不都合が起きそうな建付けですね。勉強になりました。

近々、もう少し念入りに動作確認をしつつ Grove Base HAT ノードを修正してアップデートしてみます!