emmmm, HamCQ does not support uploading files in the .ino format.
Then, everyone should create a new file in the Arduino IDE and copy the following code into it. Modify the Wi-Fi name, password, BafaYun key, and FMOIP. Compile and flash the ESP-01S. Select "ESP8266" as the development board and choose the port number that you found in Device Manager.
/*
ESP01S + Baofa Cloud + HTTP API → FMO Device Remote Controller
Functionality:
1. Control FMO devices through Baofa Cloud/Mijia
2. Direct control via local HTTP API (backup option)
Required Arduino libraries:
1. PubSubClient by Nick O'Leary
2. WebSockets by Markus Sattler
Hardware: ESP-01S
GPIO2 for onboard LED (also status indicator)
*/
#include <ESP8266WiFi.h>
#include <WebSocketsClient.h>
#include <PubSubClient.h>
#include <ESP8266WebServer.h>
// ================== User Configuration Section ==================
const char* ssid = "WIFI NAME"; // Only supports 2.4G WiFi
const char* password = "WIFI PASSWORD";
// Bemfa Cloud configuration (https://cloud.bemfa.com)
const char* mqtt_server = "bemfa.com"; // Bemfa Cloud MQTT server
const int mqtt_port = 9501; // Non-SSL port
const char* mqtt_client_id = "Bemfa Cloud Key"; // Key obtained from the Bemfa Cloud console
// It is recommended to use a name that is friendly to Chinese, such as "zhongji001" or "diantai001"
const char* mqtt_topic = "FMO001"; // Topics ending with 001 = outlet, 002 = light
// HTTP server port
const int HTTP_PORT = 80;
// FMO device WebSocket configuration
const char* fmo_host = "FMO IP ADDRESS";
const int fmo_ws_port = 80;
const char* fmo_ws_path = "/ws";
// =================================================
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
WebSocketsClient wsClient;
ESP8266WebServer httpServer(HTTP_PORT);
// Status variables
bool wsConnected = false;
bool mqttConnectedFlag = false;
String pendingCmd = ""; // Cache the command to be sent
unsigned long lastHeartbeat = 0;
const unsigned long HEARTBEAT_INTERVAL = 30000; // 30-second heartbeat
// LED pin (GPIO2 on ESP-01S, onboard blue LED, turns on with low level)
const int LED_PIN = 2;
void setup() {
Serial.begin(115200);
delay(100);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH); // Turn LED off
Serial.println("\n[FMO Controller] Starting...");
setupWiFi();
// Configure MQTT
mqttClient.setServer(mqtt_server, mqtt_port);
mqttClient.setCallback(mqttCallback);
mqttClient.setBufferSize(512);
// Configure the WebSocket client
wsClient.begin(fmo_host, fmo_ws_port, fmo_ws_path);
wsClient.onEvent(webSocketEvent);
wsClient.setReconnectInterval(5000); // Reconnect after 5 seconds of disconnection
wsClient.enableHeartbeat(15000, 3000, 2); // WebSocket heartbeat
// Configure HTTP API
setupHTTPServer();
}
void loop() {
// Maintain WiFi
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi lost, reconnecting...");
setupWiFi();
}
// Maintain MQTT
if (!mqttClient.connected()) {
mqttConnectedFlag = false;
reconnectMQTT();
} else {
if (!mqttConnectedFlag) {
mqttConnectedFlag = true;
Serial.println("MQTT ready.");
}
}
mqttClient.loop();
// Maintain WebSocket
wsClient.loop();
// Handle HTTP requests
httpServer.handleClient();
// Bafa heartbeat (keep the device online)
unsigned long now = millis();
if (now - lastHeartbeat >= HEARTBEAT_INTERVAL) {
lastHeartbeat = now;
if (mqttClient.connected()) {
mqttClient.publish(mqtt_topic, "ping");
Serial.println("[MQTT] Heartbeat sent");
}
}
delay(10); // Brief sleep, to prevent watchdog reset
}
// ================== WiFi ==================
void setupWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 40) {
delay(500);
Serial.print(".");
digitalWrite(LED_PIN, !digitalRead(LED_BUILTIN));(LED_PIN)); // LED blinking
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println();
Serial.print("WiFi connected, IP: ");
Serial.println(WiFi.localIP());
digitalWrite(LED_PIN, LOW); // LED stays on to indicate WiFi is connected
} else {
Serial.println("\nWiFi connection failed, will retry...");
}
}
// ================== MQTT (Bafun) ==================
void reconnectMQTT() {
Serial.print("Connecting to MQTT...");
if (mqttClient.connect(mqtt_client_id)) {
Serial.println("connected");
mqttClient.subscribe(mqtt_topic);
Serial.printf("Subscribed to: %s\n", mqtt_topic);
} else {
Serial.print("failed, rc=");
Serial.print(mqttClient.state());
Serial.println(" retry in 5s");
delay(5000);
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
String msg = "";
for (unsigned int i = 0; i < length; i++) {
msg += (char)(payload[i]);
}
Serial.print("Topic: ");
Serial.println(topic);
Serial.print("Message: ");
Serial.println(msg);
} < length; i++) {
msg += (char)payload[i];
}
msg.trim();
Serial.print("[MQTT] Received: ");
Serial.println(msg);
if (msg == "on") {
Serial.println("Command: Switch to STANDARD mode");
sendFMOCommand(0);
} else if (msg == "off") {
Serial.println("Command: Switch to STANDBY mode");
sendFMOCommand(1);
}
// Ignore heartbeat messages like "heartbeat/down"
}
// ================== WebSocket (FMO) ```c++
void webSocketEvent(WStype_t type, uint8_t* payload, size_t length) {
switch (type) {
case WStype_DISCONNECTED:
wsConnected = false;
Serial.println("[WS] Disconnected from FMO");
digitalWrite(LED_PIN, HIGH); // LED turns off to indicate WS disconnection
break;
case WStype_CONNECTED:
wsConnected = true;
Serial.println("[WS] Connected to FMO");
digitalWrite(LED_PIN, LOW); // LED turns on to indicate a successful connection
// If there are pending commands, send them immediately
if (pendingCmd.length() > 0) {
// Send the command over the WebSocket
wsSend(pendingCmd.c_str());
pendingCmd.clear(); // Clear the pending command buffer
}
break;
case WStype_TEXT:
// Process text messages received from FMO
Serial.print("[WS] Received text message: ");
Serial.println(payload, length);
break;
case WStype_BINARY:
// Process binary data received from FMO
Serial.print("[WS] Received binary data: ");
Serial.println(payload, length);
break;
default:
Serial.print("[WS] Unknown WebSocket event type: ");
Serial.println(type);
break;
}
}
``` > ```
0) {
sendFMOCommand(pendingCmd.toInt());
pendingCmd = "";
}
break;
case WStype_TEXT:
Serial.printf("[WS] Response: %s\n", payload);
// You can parse setScreenModeResponse here to confirm success
break;
case WStype_ERROR:
Serial.println("[WS] An error occurred");
break;
default:
break;
}
}
void sendFMOCommand(int mode) {
// mode: 0 = Normal mode (on), 1 = Standby mode (off)
if (!wsConnected) {
Serial.println("[WS] Not connected, caching command...");
pendingCmd = String(mode);
return;
}
String json = "{\"type\":\"ui\",\"subType\":\"setScreenMode\",\"data\":{\"mode\":" + String(mode) + "}}";
wsClient.sendTXT(json);
Serial.print("[WS] Sent: ");
Serial.println(json);
}
// ================== HTTP API ==================
void setupHTTPServer() {
httpServer.on("/", HTTP_GET, handleRoot);
httpServer.on("/on", HTTP_GET, handleOn);
httpServer.on("/off", HTTP_GET, handleOff);
httpServer.on("/status", HTTP_GET, handleStatus);
httpServer.onNotFound(handleNotFound);
httpServer.begin();
Serial.println("[HTTP] Server started on port " + String
```(HTTP_PORT));
}
void handleRoot() {
String html = "<html><head><meta charset="UTF-8"><title>FMO Operator</title></head>";
html += "<" + "div" + ">" + "This is a sample HTML code snippet.";
html += "<" + "/div" + ">" + "<" + "/script" + ">" + "<" + "/style" + ">" + "<" + "/html" + ">";<body style='font-family:sans-serif;max-width:400px;margin:40px auto;text-align:center;'>";
html += "<"<h2>FMO Remote Controller<<h2>>";
html += "<"<p>Device IP: " + WiFi.localIP().toString() + "</p>";
html += "<"<p>WebSocket: " + String(wsConnected ? "Connected" : "Not Connected") + "</p>";
html += "<"<p>MQTT: " + String(mqttClient.connected() ? "Connected" : "Not Connected") + "</p>";
html += "<"<br><<a href='/on' style='display:inline-block;padding:20px 40px;margin:10px;font-size:18px;background:#4CAF50;color:white;text-decoration:none;border-radius:8px;'></a>>Start (Standard Mode)</a>";
html += "<<br><a href='/off' style='display:inline-block;padding:20px 40px;margin:10px;font-size:18px;background:#f44336;color:white;text-decoration:none;border-radius:8px;'>Turn off (Standby mode)</a>";
html += "<</body></html>";
httpServer.send(200, "text/html", html);
}
void handleOn() {
Serial.println("[HTTP] Request: /on");
sendFMOCommand(0);
String json = "{\"status\":\"ok\",\"action\":\"off\",\"mode\":\"standard\"}";
httpServer.send(200, "application/json", json);
}
void handleOff() {
Serial.println("[HTTP] Request: /off");
sendFMOCommand(1);
String json = "{\"status\":\"ok\",\"action\":\"off\",\"mode\":\"standby\"}";
httpServer.send(200, "application/json", json);
}
void handleStatus() {
String json = "{\"status\":\"ok\",\"wifi\":\"connected\",\"ip\":\"" + WiFi.localIP().toString() + "\",\"websocket\":" + (wsConnected ? "true" : "false") + ",\"mqtt\":" + (mqttClient.connected() ? "true" : "false") + "}";
httpServer.send(200, "application/json", json);
}
void handleNotFound() {
httpServer.send(404, "application/json", "{\"status\":\"error\",\"message\":\"Not Found\"}");
}