超小型デバイスM5 ATOM Liteに、MicroPythonのESP32向けgenericポートを書き込んで動かしております。前回は nptサーバに接続してRTCに時刻を設定しました。今回はMQTTブローカにPublishして、Node-REDのダッシュボードにATOM LiteのRTCの時刻を表示させてみます。
※「MicroPython的午睡」投稿順 Indexはこちら
(末尾に実験に使用したスクリプト全文を掲げました。)
実験用のNode-REDとMQTTブローカ(Mosquitto)は、Raspberry Pi 3機で動かしています。昨日、別件でNode-REDのバージョンUP、ちょっとトラブリました。 本日は更新したばかりのNode-REDに向けて M5 ATOMLite から文字列を送り込んで表示させたいと思います。なお、M5 ATOMLiteとNode-REDの間の通信は、以下の投稿でC/C++処理系を使って一度行っており、既にM5 ATOMLite用のフローもダッシュボードも準備済。ありものに乗っかってMicroPythonでのMQTT Publishを確認するのみ。
IoT何をいまさら(83) M5Stack ATOMLite、MQTTでnode-RED接続
今回使用する MicroPython のモジュール
MicroPythonのESP32向けgenericポートにデフォルトで入っているモジュールを確認してみると、以下の2つがMQTTのモジュールであるようです。
-
- umqtt/robust
- umqtt/simple
2つも入っているので特に自力で何かインストールする必要は無さそうです。どうせ端から動かしてみるつもりなのですが、まず今回は 「simple」で行きたいと思います。実用上、通信はrobustである方が良いに決まっているけれど、なるべくお手軽なものから行きたい。。。
umqtt/simpleに関しては以下の立派なドキュメントがあります。
umqtt.simple — MQTT client function
モジュール自体はPyPIからダウンロード可能みたいです(今回使用のMicroPython実装にはデフォで入っているのでやらないケド。)
PyPI micropython-umqtt.simple 1.3.4
ありがちなこととは言え、個別の実装ではドキュメントで記述されている機能のサブセットという事態はままあるので、例によってMicroPythonのREPLから実装状況を確認してみます。こんな感じ。
>>> import umqtt.simple >>> help(umqtt.simple) object <module 'umqtt.simple' from 'umqtt/simple.py'> is of type module socket -- <module 'usocket'> MQTTClient -- <class 'MQTTClient'> struct -- <module 'ustruct'> __name__ -- umqtt.simple __file__ -- umqtt/simple.py hexlify -- <function> MQTTException -- <class 'MQTTException'> >>> help(umqtt.simple.MQTTClient) object <class 'MQTTClient'> is of type type set_last_will -- <function set_last_will at 0x3ffe7470> _recv_len -- <function _recv_len at 0x3ffe7430> ping -- <function ping at 0x3ffe7490> __qualname__ -- MQTTClient subscribe -- <function subscribe at 0x3ffe74f0> __init__ -- <function __init__ at 0x3ffe73e0> set_callback -- <function set_callback at 0x3ffe7410> disconnect -- <function disconnect at 0x3ffe74d0> _send_str -- <function _send_str at 0x3ffe73a0> connect -- <function connect at 0x3ffe7450> publish -- <function publish at 0x3ffe74b0> wait_msg -- <function wait_msg at 0x3ffe7510> __module__ -- umqtt.simple check_msg -- <function check_msg at 0x3ffe7520>
見たところ(ざっとですが)、当方で使いそうな機能は実装されているみたい。
Node-REDのノードの設定を確認
さてM5 ATOMLiteからのMQTTパケットを受け止めて表示する側のNode-REDのフローをアイキャッチ画像に掲げました。3つあるフローの先頭のノード2つだけのフローがそれです。ぶっちゃけ
-
- mqtt-inノードでブローカさんからパケット受領、ペイロードを流す
- Node-REDダッシュボードのtextノードで流れてきたペイロードをダッシュボード上に表示する
これだけ。いちおう2つのノードの設定をNode-RED上で確認しておきます。
mqtt-inノード
Dashboardのtextノード
MicroPythonスクリプトの概要
末尾のスクリプトは、前回作成した 「ntp 使って M5ATOM LiteのRTCに時刻を設定」するスクリプトの「チョイ変」です。前回、ntp使用するためにWiFi経由でネットワークに接続できるようになっているので、追加すべきはMQTTに関する操作のみです。
-
- MQTTクライアントを作る
- MQTTクライアントをブローカに接続する
- 接続できたら、MQTTブローカにPublishする
- MQTT接続を切る
Publishする際には、上記のmqtt-inノードで設定している「トピック」を指定します。また、Publishする内容は、折角RTCに時刻を設定しているので、RTCから読み出した現在時刻を文字列にしたものとします。
今回は、1発Publishした後「逃げて」しまうので、最後に接続は切っています。Subscribeはまた次回ということで(進捗遅いな。C/C++では1回で済ませたのに。)
MicroPythonスクリプトの実行結果
M5 ATOMLite上でスクリプトを実行させた後、PC上のブラウザからRaspberry Pi 3 上のNode-REDダッシュボードに接続して見えたのが以下です。
Publishは成功。次回は、下にあるColorを使って、ダッシュボードからの命令をM5 ATOMLite側に「伝達」してみる予定です。
MicroPython的午睡(40) まずはネット接続してntp、M5ATOM Lite に戻る
MicroPython的午睡(42) array.array、意外に小さい使える領域 へ進む
実験に使用したMicroPythonスクリプト全文
import network, ntptime, machine, time import umqtt.simple global wlan, mqttBroker, client ssid = "YOUR SSID" password = "YOUR PASSWORD" ntpAdr = "YOUR NTP SERVER" brokerAdr = "YOUR MQTT BROKER ADDRESS" brokerPort = 1883 tz = 9 #JST def connectMqttBroker(bAdr, bPort, clientId): global client try: client = umqtt.simple.MQTTClient(clientId, bAdr, port=bPort) client.connect() except: return False return True def do_connect(ssid, pw, timeout, opt=True): global wlan wlan = network.WLAN(network.STA_IF) wlan.active(True) if not wlan.isconnected(): print('connecting to network...') wlan.connect(ssid,pw) while (not wlan.isconnected()) and (timeout > 0): start = time.ticks_ms() while time.ticks_diff(time.ticks_ms(), start) < 1000: pass timeout -= 1 if opt: print('timeout: ', timeout) print('network config: ', wlan.ifconfig()) return wlan.isconnected() def currentTIME(timZone): t0 = machine.RTC().datetime() return "{0}/{1:02d}/{2:02d} {3:02d}:{4:02d}:{5:02d}".format(t0[0],t0[1],t0[2],t0[4]+timZone,t0[5],t0[6]) def initRTC(timZone, opt=True): ntptime.host = ntpAdr ntptime.settime() if opt: print(currentTIME(timZone)) def main(): if do_connect(ssid, password, 10): initRTC(tz) if connectMqttBroker(brokerAdr, brokerPort, "ATOMLite"): client.publish("ATOMLite/Status", "FROM ATOMLite: " + currentTIME(tz)) client.disconnect() else: print("ERROR: MQTT Broker connection.") else: print("ERROR: WiFi connection.") if __name__ == "__main__": main()