知的好奇心 for IoT

IoT関連の知的好奇心を探求するブログです

一周回ってMQTTに回帰。パブリックMQTTブローカーやスマホアプリもいろいろあって、なんとか使えそうな感触。

Blynkの代わりをいろいろと探していたのですが全然見つからず、一周回ってMQTTの現状を調べてみたら、パブリックMQTTブローカやスマホアプリもいろいろあることがわかって、MQTTに回帰することにしました。

 

IoT OnOff」というアプリで温度・湿度・気圧を表示した画面

f:id:IntellectualCuriosity:20211026230116p:plain

MQTTは軽いので、1秒更新でも大丈夫みたいです。

 

パブリックMQTTブローカー

パブリックMQTTブローカーは、ユーザー登録などの事前手続きをしなくても利用できる手軽なブローカーです。

ここでやりとりされるデータは誰もが見ることができるため、機微な情報をこの場所で扱うことは適切ではありません。

探せばもっとたくさん見つかると思いますが、先程の「IoT OnOff」アプリのブローカーリストには次の3つが登録されていました。

f:id:IntellectualCuriosity:20211026234802p:plain

ポート番号がMQTT標準の「1883」や「8883」でなく「8080」や「443」になっているのは、このアプリがデフォルトでWebSocketsを使用してブローカーにアクセスするためです。

 

MQTT対応スマホアプリ

Apple App StoreGoogle Play Storeで「mqtt」を検索すると、結構いろんなアプリが表示されます。(やっぱりGoogle Play Storeの方が数が多いですが...)

Apple App Store

f:id:IntellectualCuriosity:20211027000812p:plain

Google Play Store

f:id:IntellectualCuriosity:20211027001020j:plain

 

ところが、両方のストアに登録されているアプリは少なく、今のところ良さそうに感じたのが「IoT OnOff」でした。

 

今回のテスト環境

ハードウェア(MQTTクライアント)

  • M5Stack Atom Lite
  • M5Stack用温湿度気圧センサユニット Ver. 3(ENV Ⅲ)

f:id:IntellectualCuriosity:20211027014120j:plain

ソフトウェア(MQTTクライアント)

  • Arduino IDE 1.8.15 (開発環境)
  • Arduino core for the ESP32 1.0.6(ボード)
  • PubSubClient 2.8.0 (ライブラリ)
    MQTTクライアントライブラリ。対応バージョンはMQTT 3.1.1。
  • Adafruit SHT31 Library 2.0.0 (ライブラリ)
  • UNIT_ENV 0.0.2 (ライブラリ)
    気圧センサQMP6988のライブラリが入っています。

MQTTブローカー

  • HIVEMQ Public Broker (broker.hivemq.com)

HIVEMQは商用サービスやソフトウェア提供も行なっているMQTT専業企業です。

次のサイトでこのPublic Brokerの使用状況が確認できます。

MQTTスマホアプリ(MQTTクライアント)

以下は、用意されているウィジットのキャプチャです。

f:id:IntellectualCuriosity:20211027040805p:plain

f:id:IntellectualCuriosity:20211027040841p:plain

f:id:IntellectualCuriosity:20211027040907p:plain

Blynkと比べるとかなり少なく、ゲージ(Circle)は字が小さくて見づらいし、グラフもあまりできが良くないのですが、全体的な作りは悪くなく今後のアップデートに期待する感じです。

アプリの設定

Settings > Configuration > Device

f:id:IntellectualCuriosity:20211027045154p:plain

Settings > Configuration > MQTT Broker

f:id:IntellectualCuriosity:20211027045415p:plain

DashBoard > Test

f:id:IntellectualCuriosity:20211027045541p:plain

DashBoards > Test > Value (温度)

f:id:IntellectualCuriosity:20211027045728p:plain

DashBoards > Test > Value (温度) > Subscribe

f:id:IntellectualCuriosity:20211027050423p:plain

※Device IDを付加することでユニークなTopicにしています。

DashBoards > Test > Value (湿度)

f:id:IntellectualCuriosity:20211027050746p:plain

DashBoards > Test > Value (湿度)  > Subscribe

f:id:IntellectualCuriosity:20211027050947p:plain

※Device IDを付加することでユニークなTopicにしています。

DashBoards > Test > Value (気圧)

f:id:IntellectualCuriosity:20211027051133p:plain

DashBoards > Test > Value (気圧) > Subscribe

f:id:IntellectualCuriosity:20211027051442p:plain

※Device IDを付加することでユニークなTopicにしています。

 

ソースコード

※2021年11月9日更新 client.loop();を加えました。

#include <Adafruit_SHT31.h>             // 温湿度センサーライブラリ
#include <QMP6988.h>                    // 気圧センサーライブラリ
#include <WiFi.h>                       // WiFiライブラリ
#include <PubSubClient.h>               // MQTTライブラリ

#define WIFI_SSID     "2.4GHzのSSID"
#define WIFI_PASS     "2.4GHzのSSIDのパスワード"

// MQTT設定
#define MQTT_TOPIC_ID   "デバイスIDをここに設定します"  // トピック識別用ID
#define MQTT_DEVICE_ID  MQTT_TOPIC_ID "-1"   // デバイスID(ユニークなID)
#define MQTT_BROKER_ADDR "broker.hivemq.com" // MQTTブローカーのアドレス
#define MQTT_TOPIC_TEMP MQTT_TOPIC_ID "/test/temperature" // 温度のトピック
#define MQTT_TOPIC_HUMI MQTT_TOPIC_ID "/test/humidity"    // 湿度のトピック
#define MQTT_TOPIC_PRES MQTT_TOPIC_ID "/test/pressure"    // 気圧のトピック

// グローバル変数/オブジェクト
Adafruit_SHT31 sht31 = Adafruit_SHT31();  // SHT3x用オブジェクト
QMP6988 qmp6988;                        // QMP6988用オブジェクト
WiFiClient wclient;                     // WiFi接続用オブジェクト
PubSubClient client(MQTT_BROKER_ADDR, 1883, wclient); // MQTT接続用オブジェクト
float temperature, humidity, pressure;  // 温度、湿度、気圧の変数
unsigned long lastSend = 0;             // 前回の送信時間

// 計測
void measurement() {
  temperature = NAN;                    // 温度の変数
  humidity = NAN;                       // 湿度の変数
  pressure = NAN;                       // 気圧の変数

  // 計測
  while (isnan(temperature) || isnan(humidity) || isnan(pressure)) {  // 変数に値が入るまでループする
    delay(100);                         // 100ミリ秒待つ
    temperature = sht31.readTemperature();     // 温度を取得
    humidity = sht31.readHumidity();           // 湿度を取得
    pressure = qmp6988.calcPressure() / 100.0; // 気圧を取得
  }

  // 値の四捨五入
  temperature = ((int)((temperature*100+5)/10))/10.0; // 温度を少数第2位で四捨五入
  humidity = (int)(humidity*10+5)/10;                 // 湿度を少数第1位で四捨五入
  pressure = (int)(pressure*10+5)/10;                 // 気圧を少数第1位で四捨五入

  // ログ表示
  Serial.printf("temperature : %.2f\n", temperature);
  Serial.printf("humidity    : %.2f\n", humidity);
  Serial.printf("pressure    : %.2f\n", pressure);
  Serial.println();
}

// WiFiに接続
void connectWiFi() {
  if (WiFi.status() != WL_CONNECTED) {  // WiFiが繋がっていなかったら
    Serial.print("\nConnecting to " WIFI_SSID); // ログにWiFiSSIDを表示
    WiFi.begin(WIFI_SSID, WIFI_PASS);   // WiFiに接続
    while (WiFi.status() != WL_CONNECTED) { // WiFiが繋がるまでループ
      delay(1000);                      // 1秒待つ
      Serial.print(".");                // ログに「.」を表示
    }
    Serial.println("connected");        // ログに「WiFi connected」を表示
  }
}

// データ送信
void sendData() {
  while (!client.connected()) {           // MQTTブローカーの接続確認
    Serial.print("Attempting MQTT connection...");
    if (client.connect(MQTT_DEVICE_ID)) { // MQTT接続ブローカーに接続
      Serial.println("connected");
    } else {
      Serial.print("Retying in 5 seconds - failed, rc=");
      Serial.println(client.state());
      delay(5000);
    }
  }
  // MQTTブローカーにデータを送信(パブリッシュ)
  client.publish(MQTT_TOPIC_TEMP, String(temperature).c_str()); // 温度
  client.publish(MQTT_TOPIC_HUMI, String(humidity).c_str());    // 湿度
  client.publish(MQTT_TOPIC_PRES, String(pressure).c_str());    // 気圧
  client.loop();                        // 接続維持用
}

// 初期化
void setup() {
  Serial.begin(115200);
  delay(100);

  // センサー関連
  Wire.begin(26, 32);                   // SDA:26 SCL:32でI2Cを初期化
  sht31.begin(0x44);                    // SHT3xの初期化
  qmp6988.init();                       // QMP6988の初期化
}

// メイン
void loop() {
  unsigned long now = millis();         // 起動してからの時間取得

  // millis()オーバーフロー対策(約50日で0に戻る)
  if (now < lastSend) lastSend = 0;       // 前回の送信時間を0にする

  // 1秒毎にデータ送信
  if (now - lastSend > 1000) {          // 1秒より経っていたら
    measurement();                      // 計測
    connectWiFi();                      // WiFiの接続確認
    sendData();                         // データ送信
    lastSend = now;                     // 前回時間更新
  }
}