[ラズパイ] MQTTをローカルネットワーク間で通信してみる 〜 対話型コンソールの導入


※ 当ページには【広告/PR】を含む場合があります。
2020/11/02
【ラズパイ x IoTネットワーク】MQTTをローカルネットワーク間で通信してみる
以前の記事の内容 でnode.jsのmqttクライアントモジュールであるmqtt.jsを使って簡単なMQTT接続を試しました。
mqtt.jsを使う際に単純にコマンドを叩くだけではブローカーに接続を切断する時にプロセスが走ったまま終了しない状態になります。
今回はコマンドプロンプトから対話型の操作を導入して、通信状態を管理するやり方を整えてみます。

合同会社タコスキングダム|蛸壺の技術ブログ
【ラズパイ x IoTネットワーク】MQTTをローカルネットワーク間で通信してみる

Node.jsをベースにラズパイをMQTTクライアントにして、パソコン側に構築したブローカー(MQTTサーバー)を介して通信してみます。


prompts.js



標準入出力用のnpmパッケージである
prompts.js から対話型のスクリプトを作成します。

前回のmqttサブスクライバを設定した項目 から以降の内容に置き換えて話をしていきます。

            $ npm i prompts -s
#OR
$ yarn add prompts -S

        

公式にあるようにサンプルのコードを走らせてみます。
prompt.js というファイル名で以下のコードの内容で保存します。

            const prompts = require('prompts');

(async () => {
    const response = await prompts({
        type: 'number',
        name: 'value',
        message: 'How old are you?',
        validate: value => value < 18 ? `Nightclub is 18+ only` : true
    });
    console.log(response); // => { value: 24 }
})();

        

このコードを動かしてみると、自前で実装せずとも
validate 関数で判定条件を定義すると簡単に対話型のアプリにすることができます。

            $ node prompt.js
? How old are you? › 13
› Nightclub is 18+ only
✔ How old are you? … 45
{ value: 45 }

        

mqttクライアントで利用する



では
select タイプの質問形式でプログラムを選択するようなプログラムの雛形を作成してみます。 先程の prompt.js の中身を以下のコードの内容に変更します。

            const prompts = require('prompts');

function sleep(_ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, _ms)
    });
}

async function planA() {
    console.log('Plan-A has begun.');
    await sleep(1000);
    console.log('Plan-A finished.');
}

async function planB() {
    console.log('Plan-B has begun.');
    await sleep(2000);
    console.log('Plan-B finished.');
}

async function main() {
    console.log('Start');

    while (true) {
        const question = [
            {
                type: "select",
                name: "plan",
                message: "Order?",
                choices: [
                    { title: "Plan A", value: "a" },
                    { title: "Plan B", value: "b" },
                    { title: "Quit", value: "q" }
                ]
            }
        ];

        const response = await prompts(question);
        console.log(response);
        if (!Object.keys(response).length || response.plan == "q") {
            console.log('Done!');
            break;
        } else if (response.plan == "a") {
            await planA();
        } else if (response.plan == "b") {
            await planB();
        }
    }
}

main();

        
Plan A を選択すると、responseで返るオブジェクトのplanプロパティに a が返り、同期的に planA 関数が処理されます。
Plan B も同様です。
また
Quit が選択されると、 response.planq となり処理が終了します。
Cntl + c キーで終了したい場合には、空のオブジェクトを判定して終了処理になります。
ということでこれを実行すると、

            $ node prompt.js
start
✔ Order? › Plan A
{ plan: 'a' }
Plan-A has begun.
Plan-A finished.
✔ Order? › Plan B
{ plan: 'b' }
Plan-B has begun.
Plan-B finished.
✔ Order? › Quit
{ plan: 'q' }
Done!

        

のような対話的に処理が流れるようなプログラムに仕上がりました。


mqttクライアントの動作を組み込む



本題であったmqttクライアントの処理を先程の雛形
prompt.js を拡張して作成します。
今回はMQTTブローカーに接続する、サブスクリプションを登録する、サブスクリプションを止める、程度の処理を行います。
コードを
index_sync.js として以下の内容で保存します。 なお、ブローカーのアドレスは 192.168.0.200 で起動したとします。

            const prompts = require('prompts');
const mqtt = require('mqtt');
let mqttClient;

async function mqttConnect() {
    if (!mqttClient) {
        mqttClient = mqtt.connect('mqtt://192.168.0.200:1883');
        mqttClient.on('connect', () => {
            console.log('Connected.');
        });
        mqttClient.on('message', (topic_, message) => {
            console.log('subscriber received topic:', topic_, 'message:', message.toString());
        });
    } else {
        console.log('Already Has Connected.');
    }
}

async function mqttSubscrive() {
    if (mqttClient) {
        const topic = 'hoge/piyo/fuga';
        mqttClient.subscribe(topic, (err, granted) => {
            console.log('Subscribed.');
        });
    } else {
        console.log('MQTT Client is an empty.');
    }
}

async function mqttUnsubscrive() {
    if (mqttClient) {
        const topic = 'hoge/piyo/fuga';
        mqttClient.unsubscribe(topic, (err, granted) => {
            console.log('Unsubscribed.');
        });
    } else {
        console.log('MQTT Client is an empty.');
    }
}

async function main() {
    console.log('Start');

    while (true) {
        const question = [
            {
                type: "select",
                name: "plan",
                message: "Order?",
                choices: [
                    { title: "Connect", value: "c" },
                    { title: "Subscrive", value: "s" },
                    { title: "Unsubscrive", value: "u" },
                    { title: "Quit", value: "q" }
                ]
            }
        ];
        const response = await prompts(question);
        console.log(response);
        if (!Object.keys(response).length || response.plan == "q") {
            console.log('Done!');
            if (mqttClient) {
                mqttClient.end();
            }
            break;
        } else if (response.plan == "c") {
            await mqttConnect();
        } else if (response.plan == "s") {
            await mqttSubscrive();
        } else if (response.plan == "u") {
            await mqttUnsubscrive();
        }
    }
}

main();

        

実行したら以下のように動きます。

合同会社タコスキングダム|蛸壺の技術ブログ


この画像で左の窓がサブスクライバ(mqttクライアント)側で、右の窓がブローカー(mqttサーバー)側です。 狙い通り、対話型でmqttを接続から切断まで可能なコンソールアプリに仕上がっています。


まとめ



前回はmqtt.jsをただ動かすだけのプログラムになっていましたが、対話型にすることでよりリッチな操作が可能になりました。
今回の内容としては、簡単のためサブスクライバとしてテストしましたが、パブリッシャ用のプログラムとしても同様の実装で作成できると思います。

参考サイト

prompts : node.js用のコマンドラインからの値を受け取る時に便利なライブラリ