[Node.js x MQTT] ラスパイをMQTTクライアントにしてリモートからGPIOを制御する


2020/09/20

前の記事・壊れた扇風機を改造してラズパイで制御してみるの中で利用したラズパイのGPIO制御プログラムの具体的な実装を特集した内容です。

前回まででローカルのMQTTネットワーク環境の構築手順を何回かに分けて説明してきたので、 MQTTブローカーの立ち上げについては
こちらの記事も参考にしてください。

以降では、MQTTクライアントの実装に絞った説明になります。


サブスクライバー側の実装

前回までのにやった内容の復習になりますが、元の扇風機の仕様に合わせてGPIO側でスイッチングする簡単な動作を行う際には、以下のような状態を保持できるようにしていました。

            + メインスイッチを押す度にON/OFF
+ 3段階の風量調節(弱/中/強)
+ リズムモードの切り替え
+ タイマー4段階(オフ/1時間/2時間/4時間)
        
まずは先にサブスクライバー側の実装を行います。

実験のパートでも配線の概要は述べましたが、以下のようにラズパイを用いて4つのGPIOを使います。

            GPIO5:
    電源のオンオフ
GPIO6:
    回転速度の切り替え
GPIO7:
    運転モード(連続/リズム)の切り替え
GPIO8:
    タイマー時間の切り替え
        
特にGPIOの割り当ては任意ですので、空きのポートであれば今回はどこでもOKが、GPIOの出力電圧の扱いには注意が必要です。

なお、ラズパイ上のピンアサインメント(配置)は以下のコマンドで確認できます。

            $ pinout
,--------------------------------.
| oooooooooooooooooooo J8     +====
| 1ooooooooooooooooooo  PoE   | USB
|  Wi                    oo   +====
|  Fi  Pi Model 3B+ V1.3 oo      |
|        ,----.               +====
| |D|    |SoC |               | USB
| |S|    |    |               +====
| |I|    `----'                  |
|                   |C|     +======
|                   |S|     |   Net
| pwr        |HDMI| |I||A|  +======
`-| |--------|    |----|V|-------'

Revision           : a020d3
SoC                : BCM2837
RAM                : 1024Mb
Storage            : MicroSD
USB ports          : 4 (excluding power)
Ethernet ports     : 1
Wi-fi              : True
Bluetooth          : True
Camera ports (CSI) : 1
Display ports (DSI): 1

J8:
   3V3  (1) (2)  5V    
 GPIO2  (3) (4)  5V    
 GPIO3  (5) (6)  GND   
 GPIO4  (7) (8)  GPIO14
   GND  (9) (10) GPIO15
GPIO17 (11) (12) GPIO18
GPIO27 (13) (14) GND   
GPIO22 (15) (16) GPIO23
   3V3 (17) (18) GPIO24
GPIO10 (19) (20) GND   
 GPIO9 (21) (22) GPIO25
GPIO11 (23) (24) GPIO8 
   GND (25) (26) GPIO7 
 GPIO0 (27) (28) GPIO1 
 GPIO5 (29) (30) GND   
 GPIO6 (31) (32) GPIO12
GPIO13 (33) (34) GND   
GPIO19 (35) (36) GPIO16
GPIO26 (37) (38) GPIO20
   GND (39) (40) GPIO21

For further information, please refer to https://pinout.xyz/
        
実際の(今回は修復してみた扇風機)制御機器とラズパイの適切な配線に関しては前回の内容で説明していますので、ここでは省略します。

package.json

今回はnode.jsでGPIOを操作できるnpmライブラリのpigpioを使います。

            {
    "name": "mqtt-sub",
    "version": "0.0.1",
    "dependencies": {
        "mqtt": "~4.2.0",
        "pigpio": "^3.2.1"
    }
}
        
注意点として、このライブラリは本家・pigpio C libraryのラッパーですので、cのソースコードが適切にビルドできる環境でnpmインストールする必要があります。

プロジェクトフォルダ内にこのpackage.jsonを置き、

            $ npm install
#OR
$ yarn install
        
で正常にパッケージがインストールされていれば準備完了です。

subscrive.js

サブスクライバーのjs実行コードの一例としてsubscrive.jsというソースコードを作って、以下の内容で編集・保存します。

            const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://192.168.0.200');
const Gpio = require('pigpio').Gpio;

const topic = 'gpio';
const gpio5 = new Gpio(5, {mode: Gpio.OUTPUT});
const gpio3 = new Gpio(6, {mode: Gpio.OUTPUT});
const gpio4 = new Gpio(7, {mode: Gpio.OUTPUT});
const gpio5 = new Gpio(8, {mode: Gpio.OUTPUT});

const switch_timeout = 500;

client.on('connect', () => {
    console.log('Subscriber connected.');
});

client.subscribe(topic, (err, granted) => {
    console.log('Subscriber subscribed.');
});

client.on('message', (topic_, message) => {
    console.log(
        'Subscriber received topic:',
        topic_, 'message:', message.toString()
    );
    const gpioState = message.toString();
    if (gpioState == 'power') {
        setTimeout(() => gpio5.digitalWrite(0), switch_timeout);
        gpio5.digitalWrite(1);
    } else if (gpioState == 'speed') {
        setTimeout(() => gpio6.digitalWrite(0), switch_timeout);
        gpio6.digitalWrite(1);
    } else if (gpioState == 'mode') {
        setTimeout(() => gpio7.digitalWrite(0), switch_timeout);
        gpio7.digitalWrite(1);
    } else if (gpioState == 'timer') {
        setTimeout(() => gpio8.digitalWrite(0), switch_timeout);
        gpio8.digitalWrite(1);
    }
});
        
なお手元で利用しているローカルにおいたブローカー(MQTTサーバー)のIPアドレス192.168.0.200なのでこれを指定していますが、環境によって異なりますのでご留意ください。

サブスクライバーとパブリッシャーがMQTTネットワークに1つずつしかなくとも最低1つのトピックは必要になります。今回は1つのトピック
gpioを設定しています。

また、サブスクライバーの受け取ったメッセージ本文には、各イベントのキーワード(
'power' / 'speed' / 'mode' / 'timer')で処理の切り分けを行っています。

pigpioでのスイッチング動作の肝の部分は、

            //...
setTimeout(() => gpio.digitalWrite(0), 500);
gpio.digitalWrite(1);
//...
        
の部分で、GPIOピンをdigitalWrite(1)でHIGHの状態にした後で、setTimeoutを使って500ms後にdigitalWrite(0)によってLOWに戻ります。これでパルスは的な信号を送り、切り替えのスイッチとして利用しています。

出来上がったら、サブスクライバーを起動しておきます。

            $ node subscrive.js
        
これでサブスクライバーはMQTTからの電文待ちになっています。


パブリッシャー側の実装

続いて、パブリッシャー側のプログラムを作成していきます。先ほどのサブスクライバー側のコードに合わせて操作のコードを組み当てていきます。

package.json

mqtt.jsは必須なのですが、コマンドラインの引数をjsonオブジェクトとしてnode.jsコード内で利用できるargparseも利用します。

            {
    "name": "mqtt-pub",
    "version": "0.0.1",
    "dependencies": {
        "mqtt": "~4.2.0",
        "argparse": "^2.0.1"
    }
}
        
これで引数に'power' / 'speed' / 'mode' / 'timer'のどれかを指定することで、プログラム的にどのスイッチを押しているかを選択できるようになります。

プロジェクトフォルダ内にこのpackage.jsonを置き、

            $ npm install
#OR
$ yarn install
        
で正常にパッケージがインストールされれば準備完了です。

publish.js

パブリッシャー側のJSコードの実装例を挙げます。publish.jsという名前でファイルを新規作成し、以下の内容で実装します。

            const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://192.168.0.200');
const { ArgumentParser } = require('argparse');

const parser = new ArgumentParser({
    description: 'command options for mqtt publisher'
});
parser.add_argument('-c', '--cmd', { defaultValue: 'power', help: 'Command sets to control a fan.' });

const topic = 'gpio';

client.on('connect', () => {
    console.log('Publisher sends a command.');
    client.publish(topic, parser.parse_args().cmd);
    setTimeout(() => process.exit(0), 500);
});
        
これでパブリッシャー側のコードの準備も完了です。

上記で設定していたサブスクライバー側がMQTTネットワークに接続した状態で、以下のようなコマンドでパブリッシャー側から扇風機を操作することができるようになりました。

            #扇風機のオン・オフ
$ node publish.js
#回転速度(弱・中・強)の切り替え
$ node publish.js -c speed
#運転モード(連続・リズム)の切り替え
$ node publish.js -c mode
#タイマーモード(オフ・1時間・2時間・4時間)の切り替え
$ node publish.js -c timer
        
ということで結果は前回で検証していた扇風機の実験の反映された通りです。

まだこれだと端末を開いてMQTTネットワークに繋いで、コマンドラインから操作入力の度にタイプしないといけないので、なんだかとっても面倒なやり方かもしれません。

市販のスマート扇風機をみてみると、全てスマホアプリから操作する方式になっており、リモコン感覚で操作出来るようになっています。操作のUIについては、ブラウザ越しから操作するなど、もう少し改善策の検討が必要のようです。


まとめ

今回はMQTTを用いたラズパイのGPIOのリモート操作を行い、簡単なスイッチングを行うプログラムの構築手順を説明しました。オフィスなんかで扇風機のスイッチに手が届かない(という状態があるかは謎ですが)場合には、Wi-Fi越しに誰でもどこからでも操作可能...です。

扇風機が勝手に動き出すような事情を知らない同僚にサプライズを仕掛ける使い方は、相手を怒らせるかも知れませんので止めておいた方が良いかも知れません。
記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

電子工作を身近に知っていただけるように、材料調達からDIYのハウツーまで気になったところをできるだけ細かく記事にしてブログ配信してます。