IoT何をいまさら(77) M5Stack, M5ez GUIからNode-REDへMQTT

Joseph Halfmoon

M5Stack用のGUI、M5ezというもの、なかなか良いものです。スマホのように登録済のWiFiへの自動接続までしてくれるのでとても楽。であれば、運用中のNode-REDのダッシュボードにMQTTで接続するのは必然であります。実際、簡単に接続できたのですが、何時もの通りのお間抜けもあり。

M5ezのお陰で、M5StackのLCD画面、そして「3つしかない」ボタンが有効活用できるようになりました。画面の見栄えも嬉しいですが、ボタンの操作性が各段に上がったのが大きいです。以下の投稿にて、表側で動作しているGUI(メニュー)と、その裏側で周期的に実行される処理関数(とりあえずTaskと呼ばせていただきますが、RTOSというほどではありませぬ)は実装済であります。

鳥なき里のマイコン屋(113) M5Stack、M5ez、本当にイージー

上では、単に毎秒カウンタを+1するだけのTaskが走っているのであります。Startメニューを選択すると数え始め、Stopメニューを選択すると止まります。周期的に実行すべき計測などのお仕事をここに記述するつもりなのですが、まずはクラウドというか、ラズパイ上で動作しているサーバーに

  • Startしたら「実行中だよ」
  • Stopしたら「停止したよ」

と報告する機能を取り付けたいと思います。

ラズパイ上の準備

以下の投稿にて、M5StackからNode-REDダッシュボードへHello Worldはやっております。ただ、M5Stack専用のタブやTopicを用意しておりませんでした。今回、Node-REDダッシュボードに M5Stackというタブを追加し、その中にM5Stack/StatusというTopicを追加いたしました。

IoT何をいまさら(74) M5StickC/M5StackからNode-REDにHello

既に同じことを micro:bitやM5StickCのために繰り返してきたので、問題もなく追加(忘れずデプロイ)

M5Stack上の準備

上に掲げましたる2回の投稿で、骨組みは既にできております。コードをマージすれば今回はおしまい、かと高をくくっておりました。今回使用のソースコードは末尾にまとめて掲載しておりますが、M5ezのメニューのフローの中にRaspberry Pi上で走っているMQTTブローカへのPublishを挿入するだけのものであります。Startメニューからは”Running”というメッセージをブローカに送り、Stopメニューからは”Stop”というメッセージを送ります。

しかし、一撃では動きませなんだ。あれこれ時間を使ったあげく気付いたのは

MQTTブローカのIPアドレス間違っていた(タイポ)

存在しない虚空にむかってMQTT接続しようとしてERRORになっておりました。ちゃんとラズパイのIPアドレスに修正したら即OK。

しかし、M5ezのお陰でERRORメッセージすら、視認性、操作性が良くなっております。ERRORのときは、下のメニューからStartをselectすると

M5ez_menuMQTTERRORの発生場所が表示される、という具合です。こんな感じ。GUIの威力。

M5ez_MQTT_ERRORIPアドレスをFIXした後はERRORが消え、Startすれば、Node-REDのダッシュボードのM5Stackタブはこんな感じ。

M5ez_pub_runningそしてStopすればこのように。

M5ez_pub_stopGUIあるのでいろいろ機能を追加するのが楽しみだな!

IoT何をいまさら(76) また、ありがちなNode-REDからSQLite へ戻る

IoT何をいまさら(78) PIRセンサで picamera のシャッタを押す へ進む

#include <M5Stack.h>
#include <M5ez.h>
#include <PubSubClient.h>

#define MQTT_NOT_START      (0)
#define MQTT_CONNECTED      (1)
#define WIFI_NOT_CONNECTED  (100)
#define MQTT_NOT_CONNECTED  (101)
#define RETRY_MAX           (10)

const char *brokerAdr = "Your IP Address";
const int brokerPort = 1883;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

int counter;
uint16_t interval;
int mqttStatus;

void reconnect() {
  int retryCount = 0;
  ez.canvas.println("Check WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    mqttStatus = WIFI_NOT_CONNECTED;
    if (++retryCount > RETRY_MAX) {
      return; 
    }
    delay(500);  
  }
  retryCount = 0;
  ez.canvas.println("Reconnect MQT:");
  while(!mqttClient.connected()) {
    mqttStatus = MQTT_NOT_CONNECTED;
    if (++retryCount > RETRY_MAX) {
      return; 
    }
    if (!mqttClient.connect("M5Stack")) {
      delay(500);
    }
  }
  mqttStatus = MQTT_CONNECTED;
  ez.canvas.println("CONNECTED.");
}
uint16_t mytask() {
  counter++;
  return interval;
}

void mqttPub(char* msg) {
  if (mqttStatus == MQTT_CONNECTED) {
    mqttClient.publish("M5Stack/Status", msg);
  } else {
    ez.msgBox("ERROR:", "MQTT connection via WiFi", "Ok");    
  }  
}

void setup() {
  M5.begin();
    ez.begin();
  counter = 0;
  interval = 1000;
  mqttClient.setServer(brokerAdr, brokerPort);
  mqttStatus = MQTT_NOT_START;
}

void loop() {
  ezMenu myMenu("Task(w/MQTT):");
  myMenu.addItem("Start");
  myMenu.addItem("Stop");
  myMenu.addItem("Show");
  myMenu.addItem("Settings");
  myMenu.runOnce();
  if (myMenu.pickName() == "Start") {
    interval = 1000;
    ez.addEvent(mytask);
    reconnect();
    mqttPub("Running");
  }
  if (myMenu.pickName() == "Stop") {
    interval = 0;
    reconnect();
    mqttPub("stop");
  }
  if (myMenu.pickName() == "Show") {
    ez.msgBox("Counter:", String(counter), "Ok");
  }
  if (myMenu.pickName() == "Settings") {
    ez.settings.menu();
  }
}