AtomS3 から HiveMQ MQTT につないでディスプレイ ON / OFF 遠隔操作の表示系を M5Unified に変更したメモ

AtomS3 から HiveMQ MQTT につないでディスプレイ ON / OFF 遠隔操作の表示系を M5Unified に変更したメモ

AtomS3 から HiveMQ MQTT につないでディスプレイ ON / OFF 遠隔操作の仕組みについて表示系を M5Unified に変更したメモです。

背景

AtomS3 から HiveMQ MQTT につないでディスプレイ ON / OFF 遠隔操作の仕組みを作るメモ - 1ft-seabass.jp.MEMO

こちらの記事で書いていたものの表示系を汎用的にしたいので M5Unified に変更しました。

プログラム

以下がソースです。

AtomS3.Display を M5.Display に変更しました。

その他の対応は AtomS3 から HiveMQ MQTT につないでディスプレイ ON / OFF 遠隔操作の仕組みを作るメモ を参考にしてください。

#include <M5Unified.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

// ———— ファームウェア情報 ————
#define FW_NAME "AtomS3_HiveMQ_DisplaySwitch"
#define FW_VERSION "1.0.5"

// ———— Wi-Fi 設定 ————
const char* ssid     = "YOUR_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

// ———— MQTT ブローカー設定 ————
const char* mqttServer   = "*******************************.s1.eu.hivemq.cloud";
const int   mqttPort     = 8883;
const char* mqttUser     = "mqttUser";
const char* mqttPassword = "mqttPassword";

// ———— トピック定義 ————
char subTopic[64];
char pubTopic[64];

// ———— MQTT クライアント ————
WiFiClientSecure netClient;
PubSubClient mqttClient(netClient);

// ———— スイッチ状態 ————
bool switchState = false;

// ———— MQTT 接続/再接続 ————
void connectMQTT()
{
  // 表示: MQTT ...
  M5.Display.clear();
  M5.Display.setTextSize(1);
  M5.Display.setCursor(0, 0);
  M5.Display.println("MQTT ...");
  delay(200);

  while (!mqttClient.connected())
  {
    // クライアント ID ランダム生成
    String mac = WiFi.macAddress();
    mac.replace(":", "");
    String rnd = String(random(0x1000000), HEX);
    String cid = String(FW_NAME) + "-" + mac + "-" + rnd;
    char clientId[80];
    cid.toCharArray(clientId, sizeof(clientId));

    if (mqttClient.connect(clientId, mqttUser, mqttPassword))
    {
      mqttClient.subscribe(subTopic);
      // 表示: MQTT OK!
      M5.Display.clear();
      M5.Display.setTextSize(1);
      M5.Display.setCursor(0, 0);
      M5.Display.println("MQTT OK!");
      delay(2000);
      // 状態の発行
      StaticJsonDocument<128> doc;
      doc["command"] = "displaySwitch";
      doc["state"] = switchState;
      char buf[128];
      size_t len = serializeJson(doc, buf);
      mqttClient.publish(pubTopic, buf);
    }
    else
    {
      delay(5000);
    }
  }
}

// ———— MQTT 受信コールバック ————
void mqttCallback(char *topic, byte *payload, unsigned int length)
{
  StaticJsonDocument<128> doc;
  DeserializationError err = deserializeJson(doc, payload, length);
  if (err != DeserializationError::Ok)
    return;

  if (!doc.containsKey("command"))
    return;
  String cmd = doc["command"].as<const char *>();
  if (cmd == "displaySwitch" && doc.containsKey("state"))
  {
    switchState = doc["state"].as<bool>();
    // 表示の更新
    if (switchState)
    {
      M5.Display.fillScreen(WHITE);
    }
    else
    {
      M5.Display.fillScreen(BLACK);
    }
  }
}

// ———— MQTT ループ処理 ————
void mqttLoop()
{
  if (!mqttClient.connected())
  {
    connectMQTT();
  }
  mqttClient.loop();
}

void setup()
{
  // M5Stack初期設定用の構造体を代入
  auto cfg = M5.config();

  // M5デバイスの初期化
  M5.begin(cfg);

  Serial.begin(115200);
  delay(100);

  // テキストサイズ / 表示配置を一度設定
  M5.Display.setTextDatum(TL_DATUM);
  M5.Display.setTextSize(1);

  // 表示: FW_NAME / FW_VERSION
  M5.Display.clear();
  M5.Display.setCursor(0, 0);
  M5.Display.println(FW_NAME);
  M5.Display.println(FW_VERSION);
  delay(5000);

  // ———— Wi-Fi 接続 ————
  M5.Display.clear();
  M5.Display.setCursor(0, 0);
  M5.Display.println("Wi-Fi ...");
  delay(200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
  }
  M5.Display.clear();
  M5.Display.setCursor(0, 0);
  M5.Display.println("Wi-Fi OK");
  delay(2000);

  // トピック生成(MAC ベース)
  String mac = WiFi.macAddress();
  mac.replace(":", "");
  snprintf(subTopic, sizeof(subTopic), "atoms3/%s/displaySwitch", mac.c_str());
  snprintf(pubTopic, sizeof(pubTopic), "atoms3/%s/displaySwitchState", mac.c_str());

  // TLS 証明書検証スキップ(自己署名 OK)
  netClient.setInsecure();

  // MQTT 初期設定
  mqttClient.setServer(mqttServer, mqttPort);
  mqttClient.setCallback(mqttCallback);
  connectMQTT();

  // 表示の更新
  if (switchState)
  {
    M5.Display.fillScreen(WHITE);
  }
  else
  {
    M5.Display.fillScreen(BLACK);
  }
}

void loop()
{
  mqttLoop();

  M5.update();
  if (M5.BtnA.wasPressed())
  {
    switchState = !switchState;
    // 表示の更新
    if (switchState)
    {
      M5.Display.fillScreen(WHITE);
    }
    else
    {
      M5.Display.fillScreen(BLACK);
    }
    // 状態の発行
    StaticJsonDocument<128> doc;
    doc["command"] = "displaySwitch";
    doc["state"] = switchState;
    char buf[128];
    size_t len = serializeJson(doc, buf);
    mqttClient.publish(pubTopic, buf);
  }

  delay(10);
}