Firebase Cloud FunctionsでMQTT Publisherを作ってみた
VR空間とIoTデバイスをMQTTで接続する仕組みを構築するにあたり、Firebase Cloud FunctionsでMQTT Publisherを作ることができないか試してみたところ、うまくいったので忘れない内に記事にします。
ちなみに、以下が作ったものです!普段はVR関係の活動をしていますので、ご興味があればフォローよろしくお願いします!
VR空間内でお絵描きすると、自宅の8x8LEDに反映される仕組みを作ってみました!
— segur(せぎゅ) (@segur_vita) September 3, 2020
個人サーバーを介して、VR(STYLY)内の仮想LEDの状態を、自宅内の物理LEDへ反映させてます。
PlaymakerのWWW Objectと、MQTTを組み合わせて実現してます。
右下はMQTTのメッセージを可視化させたものです。#STYLY #IoT pic.twitter.com/YEL02dHsJN
やりたいこと
以下が、やりたいことです。
-
FirebaseにWebAPIを構築する
-
WebAPIにGETメソッドでアクセスすると、クエリパラメーター
message
の内容が、MQTT Brokerへ送信される。
事前準備
Firebase開発環境の構築
事前にFirebaseプロジェクトの作成やツールの導入をお願いします。
こちらがわかりやすいのでオススメです。
Cloud Functions + ExpressでサーバレスAPIを実装
従量課金制のプランにする
完全無料のSparkプランだと外部ドメインのサーバーにアクセスすることができないので、MQTT Brokerへ接続することができません。
あらかじめ、従量課金制のBlazeプランに変更しておく必要があります。(こればっかりはどうしようもないです。。。)
mqttのインストール
以下のコマンドでexpressとmqttをインストールしておいてください。
npm install express
npm install mqtt
サンプルコード
index.jsに以下のように書きます。定数の箇所は適宜、ご自身の環境に合わせて変更してください。
const functions = require('firebase-functions');
const express = require("express");
const app = express();
const mqtt = require('mqtt');
// 定数
const broker = 'mqtt://example.com';
const topic = 'example';
// MQTTでメッセージを送信する
const publishMessage = (message) => {
// Brokerに接続する
const client = mqtt.connect(mqttBroker);
client.on('connect', () => {
// メッセージをBrokerへ送信する
client.publish(topic, message, null, (err) => {
if (err) {
console.error(err);
}
// Brokerとの接続を切断する
client.end();
});
});
}
// GETコントローラー
app.get('/publish', (req, res) => {
const message = req.query.message;
if(message){
// メッセージをPublishする
publishMessage(message);
return res.send('Success!');
}else{
return res.send('Error!');
}
});
const api = functions.https.onRequest(app);
module.exports = { api };
これをデプロイすれば動くようになります!
解説
こちらの関数を解説します。
// MQTTでメッセージを送信する
const publishMessage = (message) => {
// Brokerに接続する
const client = mqtt.connect(mqttBroker);
client.on('connect', () => {
// メッセージをBrokerへ送信する
client.publish(topic, message, null, (err) => {
if (err) {
console.error(err);
}
// Brokerとの接続を切断する
client.end();
});
});
}
こちらの関数では、以下の3つの処理を一気に実施しています。
- Brokerと接続
- Brokerへメッセージの送信
- Brokerとの接続を切断
つまり、関数が呼ばれる度に、Brokerとの接続・切断を繰り返すことになり、非常に効率が悪いです。
しかし、Firebase Cloud Functionsでは、Brokerとの接続を維持することが難しかったため、このようなスタイルになりました。
たとえば、以下のコードのように、関数外でBrokerとの接続処理を書いてみても、うまく動きませんでした。
(厳密には app.get
の外です)
// Brokerに接続する
const client = mqtt.connect(mqttBroker);
client.on('connect', () => {});
// MQTTでメッセージを送信する
const publishMessage = (message) => {
// メッセージをBrokerへ送信する
client.publish(topic, message, null, (err) => {
if (err) {
console.error(err);
}
});
}
さいごに
本記事作成にあたり、以下の記事を参考にさせていただきました。ありがとうございました。