micro:bitで気象情報・天気予報のMQTT通信
0. アイディア
気象情報・天気予報の作例
ウェブサイトや雑誌などでは、IoTの作例として、マイコンなどで実際の気象情報を計測し、その結果を、インターネットを通じてクラウドサービスなどにアップロードして、利用するもの(上り通信)を見かけます。
- Ambient(コネクトモード編)(ブロードキャスト編)
- アイデアをカタチにする! M5Stack入門&実践ガイド 6.3 M5StickCでIoT温室ハウス環境モニタを作る
- obniz-リアルタイムな温度グラフ
また、情報の流れが逆向きですが、インターネットを通じて取得した天気予報を、マイコンなどで表示する作例(下り通信)もあります。
- 「micro:bit v2」ではじめる電子工作 4-5 「スマートホーム」を始めよう、4−6IoTをはじめよう 室温管理システムを作る
- Interface 2022年4月号プログラミング学園 第5章 インターネット接続の雨降り警報器
- Interface 2022年6月号プログラミング学園 第4章 インターネット接続の雨降り警報器〜その2〜
これらをみて、なんとか上りと下り両方でIoT(らしきもの)をやってみたい、と思い立ちました。意図した通りに動くものはできましたが、電力消費が激しく、モバイルバッテリーで常時使用は難しい状況であり、こういうこともできる、ということに過ぎません。
micro:bitの限界について
そしてやはり、慣れ親しんだmicro:bitを使いたいと考えましたがそこでネックになるのは、ネットワークへの接続です。micro:bitは単体でWifi接続はできないという限界があり、外部モジュールもWifiに接続できるものは(国内での正規利用を考えると)限られています1。単体でもBluetoothやシリアル通信は可能であるため、micro:bitを使う場合、上記作例も含め、Bluetoothやシリアル通信でラズパイ(Raspberry Pi)、M5シリーズ又はPCに接続、ラズパイ等をゲートウェイにして、インターネットに繋ぐ作例が多いようです。
(今回もそうですが)ゲートウェイ側のラズパイ等のプログラムも必要となってしまうので、そうすると最初からラズパイ等だけでやる(micro:bit不要で、ラズパイ等に直接センサを繋いだり表示したりする)、というのが本来は素直です。。
やりとりするデータについて
やりとりするデータのうち、上り通信の方の気象情報については、micro:bitは、 以前ご紹介したとおり、そのままでもセンサーが充実しており、温度や明るさであれば単体でも計測できます。実際、これまでも、温度と明るさを計測するプログラムを、MakeCode EditorのブロックプログラミングやMicroPythonで作成してみたことがあります。他方、これ以外の気象情報、例えば、湿度や気圧の計測となると、外部センサーに頼る必要があります。
なお、下り通信の方の天気予報について、これは、micro:bitだからどうこうという話ではありませんが、今は気象庁の天気予報JSONが手軽でしょうか(気象庁の天気予報JSONファイルをWebAPI的に利用したサンプルアプリ | サンプルアプリ一覧 | あんこエデュケーション)。これを使って、改造Scratchでネコに天気予報をしゃべってもらうプログラムを作ったことはありました。もっとも、今回は楽をして別の方法をとっています。
今回の仕組み
今回の具体的な仕組みは、次の図のようになります。青色矢印が上り通信、オレンジ色矢印が下り通信です。
ネックとなるmicro:bitからのネットワークへの接続ですが、
- 今回は、以前にタイプラプスカメラとして利用した、M5Cameraをゲートウェイにしました。カメラは使わないため、これである必然性はありません。。
- その上で、ローカルネットワーク内のMQTTブローカーを通じて、スマートフォンと情報をやりとりしています。MQTTの仕組みは、MQTTで始めるIoTデバイスの作り方 - MONOistやMQTT - The Standard for IoT Messagingをご参照ください。私自身は他のプロトコルと比較できるような知識がないのですが、これらのウェブサイトでは、軽量かつ効率的である点等が特徴とされており、自動車、物流、製造、スマートホーム等の分野における利用例が紹介されています。
データについては、
micro:bitでの表示については、
- 気象情報は、Aボタンを押すと、外部のOLEDに表示されるようにし、
- 天気予報は、Bボタンを押すと、micro:bit本体のLEDで降水確率に応じたアイコン表示する形です。
1. 使用したもの
ハード
M5Cameara
過去にタイムラプスカメラとして使ってみたものの再利用です。他のM5シリーズでも代用可能です(ゲートウェイとして使うだけなのでカメラ機能は必要ないです)。M5:Bit
micro:bitとM5シリーズを繋ぐという目的に特化した、稀有なモジュールです。このモジュールはコンパクトですが、これを使うことで、micro:bitの端子がGroveコネクタに変換され、GroveケーブルでM5シリーズに接続できるようになります。また、micro:bitの電源はM5シリーズから給電されますし、エッジコネクタの各ピンにジャンパワイヤで接続できるようになっているため便利です2。VKLSVAN BME280
気温、湿度、気圧の計測のための外部センサです(I2C接続)。ヒト・ログ・ドライブさんのブログで紹介されていたのを拝見して、こちらにしてみました。HiLetgo 0.96" I2C シリアル 128×64 OLED LCDディスプレイSSD1306液晶 気温、湿度、気圧を一度に表示するためLCDを使っています(I2C接続)。同種のLCDはいくつかありますが、「子供のプログラミング」のウェブサイトで紹介されていたのを拝見して、こちらにしてみました。
ソフト
→「ショートカット」で天気予報アプリの取得情報(降水確率)をMQTT Analyzerでpublishする設定は次のとおり
2. 上り通信の雰囲気(気象情報のPublish)
micro:bit + ssd 1306 + bme280 + M5camera で 温度・湿度・気圧を計測・表示・MQTT #microbit pic.twitter.com/hi5N74opwQ
— vivitelaeti (@vivitelaeti) 2022年4月28日
配線は特殊な点はありませんが、外部センサ・外部ディスプレイと、micro:bitを、(M5:Bit経由で)I2C接続する際、繋ぎ間違えないようにするくらいでしょうか。M5:Bitは、エッジコネクタとの接続部を上にしたとき右側に2セットのI2C接続端子があり、DA/CL/V/Gの表示もされているので分かりやすいです。
androidスマートフォンのMQTTクライアント(MQTT dashboard)からの見え方。
— vivitelaeti (@vivitelaeti) 2022年4月28日
(weatherは、別途iPhoneから明日の降水確率をpublishしているものでmicro:bitからの数値ではありません。) pic.twitter.com/RSTtlSlviN
3. 下り通信の雰囲気(天気予報のSubscribe)
MQTTで天気予報(降水確率)のtopicをsubscribe、数値に応じて、LED表示(今回は晴れマーク)。これまでと同様、M5cameraをゲートウェイとして利用している。 pic.twitter.com/JmxOnLbOUd
— vivitelaeti (@vivitelaeti) 2022年5月1日
4. コード
micro:bit側
初期設定(「最初だけ」)
ループ(「ずっと」)
シリアル通信で受信した時(下り通信)
- スマホ側でpublishした天気予報の降水確率を、MQTTでsubscribeしているM5Camera経由で受信した時に、変数に格納します。
ボタン操作
Aボタンが押されると、センサで読み取った気象情報をOLEDディスプレイで表示します。
Bボタンが押されると、下り通信で取得した天気予報の降水確率に応じて、晴れ・雨・曇りマークをmicro:bit本体のLEDに表示しています。
MakeCodeの画面でJavaScriptに切り替えたコードは以下のとおりです。
input.onButtonPressed(Button.A, function () { OLED12864_I2C.showString( 0, 0, "Temp: " + convertToText(temperature), 1 ) OLED12864_I2C.showString( 0, 1, "Humi: " + convertToText(humidity), 1 ) OLED12864_I2C.showString( 0, 2, "Pres: " + convertToText(pressure), 1 ) basic.pause(5000) OLED12864_I2C.clear() }) serial.onDataReceived(serial.delimiters(Delimiters.NewLine), function () { weather = parseFloat(serial.readUntil(serial.delimiters(Delimiters.NewLine))) }) input.onButtonPressed(Button.B, function () { if (weather < 0) { basic.showIcon(IconNames.Asleep) } else if (weather < 40) { basic.showLeds(` # . # . # . # # # . # # # # # . # # # . # . # . # `) } else if (weather > 60) { basic.showIcon(IconNames.Umbrella) } else { basic.showLeds(` . . . . . . # # # . # # # # # . # # # . . . . . . `) } basic.pause(5000) basic.clearScreen() }) let pressure = 0 let humidity = 0 let temperature = 0 let weather = 0 basic.showIcon(IconNames.Giraffe) basic.pause(500) basic.clearScreen() weather = -1 temperature = 0 humidity = 0 pressure = 0 serial.redirect( SerialPin.P8, SerialPin.P12, BaudRate.BaudRate115200 ) OLED12864_I2C.init(60) BME280.Address(BME280_I2C_ADDRESS.ADDR_0x76) basic.pause(10000) basic.forever(function () { temperature = BME280.temperature(BME280_T.T_C) humidity = BME280.humidity() pressure = BME280.pressure(BME280_P.hPa) serial.writeString("{") serial.writeString("\"temperature\":" + temperature + ",") serial.writeString("\"humidity\":" + humidity + ",") serial.writeString("\"pressure\":" + pressure) serial.writeLine("}") basic.pause(3600000) })
M5Camera側
M5Camera側は、Arduino IDE上で、以下のコードを作成して書き込んでいます。
- MQTTクライアントは、Arduino Client for MQTTを使用しています。
WifiのSSIDとパスワードやMQTTのIPアドレスやポート番号の設定はそれぞれの環境にあったものを入力する必要があります。MQTTのユーザやトピックは任意ですが、トピックは同じものをスマホのMQTTクライアントで設定することになります。
micro:bitとのシリアル通信(前述の通り、GroveコネクタとM5:Bit経由)は、Serial2.begin()の箇所で初期設定しており、ボーレートはmicro:bitの設定と合わせ、TXは4、RXは13としています(M5Cameraの場合。他のM5シリーズを利用する場合などは異なります)。Serial2.begin()のパラメーターについてはこちらを参考にしました。
その他のコードの説明は、簡単なものですがコメントを入れています。定期処理はこのままですとかなり電力を消費するため、間隔を開けて実行するなど省電力化の工夫が必要となりそうです。
#include <WiFi.h> #include <PubSubClient.h> // 各種設定値 const char* ssid = "[SSIDを入力]"; const char* password = "[パスワードを入力]"; const char* mqtthost = "[MQTTブローカーのIPアドレス]"; const int mqttport = "[MQTTブローカーのポート番号]"; WiFiClient wfClient; PubSubClient client(wfClient); const char* userid = "[ユーザIDを入力]"; const char* topic_pub = "[Subscribeするトピック(天気予報)を入力]"; const char* topic_sub = "[Publishするトピック(気象情報)を入力]"; String message; const int len = 50; char payload_pub[len]; // Subscribe用の処理(天気予報のトピックをSubscribeしておきスマホのMQTTクライアントからMQTTブローカー経由で受診した場合、micro:bitにシリアル通知で送信する。) void callback(char* topic, byte* payload, unsigned int length) { for (int i=0; i<length; i++) { Serial.print((char)payload[i]); } Serial.println(); for (int i=0; i<length; i++) { Serial2.print((char)payload[i]); } Serial2.println(); } void setup() { Serial.begin(9600); Serial.println(); //M5CameraのLED pinMode(14, OUTPUT); setup_wifi(); setup_mqtt(); //micro:bitとのシリアル通信用の初期設定(M5Cameraの場合、TXは4、RXは13とする。) Serial2.begin(115200, SERIAL_8N1, 13, 4); //M5CameraのLEDを点滅 digitalWrite(14, HIGH); delay(100); digitalWrite(14, LOW); delay(100); } // Wifi接続 void setup_wifi() { Serial.println(); Serial.print("connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.print("WiFi connected at "); Serial.print(WiFi.localIP()); } // MQTT接続 void setup_mqtt(){ Serial.println(); Serial.print("connecting to mqtthost"); client.setServer(mqtthost, mqttport); //subscribe client.setCallback(callback); while (!client.connected()) { delay(1000); Serial.print("."); client.connect(userid); } Serial.println(); Serial.print("mqtt connected as "); Serial.println(userid); // Subscribe用の処理 client.subscribe(topic_sub); Serial.print("mqtt subscribed for "); Serial.println(topic_sub); } // 定期処理 void loop() { // Publish用の処理(micro:bitからのシリアル通信を読み取る。読み取ったデータはJSON形式であるが、特にここでは要素の処理はせず、const char[ ]に変換して、Publishするのみ。) if (Serial2.available() > 0) { message = Serial2.readStringUntil(0x0a); message.toCharArray(payload_pub, len); client.publish(topic_pub, payload_pub, true); // client.publish(topic_pub, (uint8_t*)payload_pub, (uint8_t)len, true); Serial.print("published "); Serial.print(topic_pub); Serial.print(" as "); Serial.println(payload_pub); delay(500); } // Subscribe用の処理 client.loop(); }
-
例えば、grove shield for microbit v2を経由したgrove uart wifi v2というモジュールの利用が考えられます。これは既存の拡張機能によりIFTTT 経由でLINEに通知でき、また、ATコマンドを利用してウェブサイトへのアクセスも可能です(サヌキテックネットさんのGroveデバイスに関する一連の記事の15-3〜15-6)。そのため、実は、このモジュールだけで、MQTTブローカーに接続できるかもしれません。MQTTの構造と、ATコマンドをしっかり理解する必要がありそうですが、MQTTで始めるIoTデバイスの作り方 - MONOist参照。↩
-
なお、上記で参照した作例中、「IoTをはじめよう 室温管理システムを作る」は、M5シリーズのうちM5Stackとmicro:bitを接続しています。ここでは、M5:Bitは使わずに、通信はGroveケーブル経由でジャンパワイヤ・エッジコネクタ経由で繋ぎ(なお、この方法が実際可能かは、現行機種の使用も含めてご確認ください)、電源もM5Stackの3.3V端子を使っています。M5Cameraでは同じ方法は試していませんが、少なくとも電源については、M5Cameraは3.3V端子がないため、同じ方法ではできないことになりそうです。↩