AtomS3 から HiveMQ MQTT につないでディスプレイ ON / OFF 遠隔操作の仕組みを作るメモ
AtomS3 から HiveMQ MQTT につないでディスプレイ ON / OFF 遠隔操作の仕組みを作るメモです。
背景

AtomS3 から HiveMQ MQTT につないでディスプレイ ON / OFF 遠隔操作の仕組みをまとめておきます。
最近では MCP サーバーで IoT 連携をするときに、このようなシンプルなディスプレイ ON / OFF 遠隔操作の仕組みがあるとデモしやすいので重宝しています。
今回のプログラム
今回のプログラムです。
#include "M5AtomS3.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 ...
AtomS3.Display.clear();
AtomS3.Display.setTextSize(1);
AtomS3.Display.setCursor(0, 0);
AtomS3.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!
AtomS3.Display.clear();
AtomS3.Display.setTextSize(1);
AtomS3.Display.setCursor(0, 0);
AtomS3.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) {
AtomS3.Display.fillScreen(WHITE);
} else {
AtomS3.Display.fillScreen(BLACK);
}
}
}
// ———— MQTT ループ処理 ————
void mqttLoop() {
if (!mqttClient.connected()) {
connectMQTT();
}
mqttClient.loop();
}
void setup() {
// AtomS3 初期化
auto cfg = M5.config();
AtomS3.begin(cfg);
// テキストサイズ / 表示配置を一度設定
AtomS3.Display.setTextDatum(TL_DATUM);
AtomS3.Display.setTextSize(1);
// 表示: FW_NAME / FW_VERSION
AtomS3.Display.clear();
AtomS3.Display.setCursor(0, 0);
AtomS3.Display.println(FW_NAME);
AtomS3.Display.println(FW_VERSION);
delay(400);
Serial.begin(115200);
delay(100);
// ———— Wi-Fi 接続 ————
AtomS3.Display.clear();
AtomS3.Display.setCursor(0, 0);
AtomS3.Display.println("Wi-Fi ...");
delay(200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
AtomS3.Display.clear();
AtomS3.Display.setCursor(0, 0);
AtomS3.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) {
AtomS3.Display.fillScreen(WHITE);
} else {
AtomS3.Display.fillScreen(BLACK);
}
}
void loop() {
mqttLoop();
AtomS3.update();
if (AtomS3.BtnA.wasPressed()) {
switchState = !switchState;
// 表示の更新
if (switchState) {
AtomS3.Display.fillScreen(WHITE);
} else {
AtomS3.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);
}
こちらを持ってきて、
// ———— Wi-Fi 設定 ————
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
つなげたい Wi-Fi への接続設定をして、
// ———— MQTT ブローカー設定 ————
const char* mqttServer = "mqttServer";
const int mqttPort = 1883;
const char* mqttUser = "mqttUser";
const char* mqttPassword = "mqttPassword";
つなげたい MQTT のブローカーの設定をしてください。
こちらを AtomS3 に書き込みます。
動かしてみる
電源入れて動かしてみます。

Wi-Fi 接続開始して、うまく Wi-Fi がつながって、

MQTT OK! で接続完了です。

接続時点で # トピックや atoms3/# などで、接続開始をとらえておくと atoms3/{MAC Address}/displaySwitchState で初期状態が届くので、以降のつなげたいデバイスの {MAC Address} が分かります。

MQTT 側で atoms3/{MAC Address}/displaySwitch のトピックに対して { "command":"displaySwitch", "state": true} という JSON を送ると ON になります。

MQTT 側で atoms3/{MAC Address}/displaySwitch のトピックに対して { "command":"displaySwitch", "state": false} という JSON を送ると OFF になります。

また、ディスプレイボタンをクリックすると、こちらでも ON/OFF できるので、ON にしたり

OFF にすると、

このように atoms3/{MAC Address}/displaySwitchState で、現在の状態が飛んできます。