emmmm HamCQ并不支持上传ino格式的文件。
那大家就在Arduino IDE中新建一个文件,将以下代码复制进Arduino IDE中,修改WIFI名称,密码,巴法云密钥和FMOIP。编译并刷入ESP-01S,开发板选择ESP8266,端口号选择您在设备管理器中查找到的端口号。
/*
ESP01S + 巴法云 + HTTP API → FMO 设备远程控制器
功能:
1. 通过巴法云/米家控制 FMO 设备
2. 通过本地 HTTP API 直接控制(备用方案)
需要安装的 Arduino 库:
1. PubSubClient by Nick O'Leary
2. WebSockets by Markus Sattler
硬件:ESP-01S
GPIO2 为板载 LED(兼状态指示)
*/
#include <ESP8266WiFi.h>
#include <WebSocketsClient.h>
#include <PubSubClient.h>
#include <ESP8266WebServer.h>
// ================== 用户配置区 ==================
const char* ssid = "WIFI名称"; // 只支持2.4G WiFi
const char* password = "WIFI密码";
// 巴法云配置(https://cloud.bemfa.com)
const char* mqtt_server = "bemfa.com"; // 巴法云MQTT服务器
const int mqtt_port = 9501; // 非SSL端口
const char* mqtt_client_id = "巴法云密钥"; // 巴法云控制台获取的密钥
// 建议改成中文友好的名称,如 "zhongji001" 或 "diantai001"
const char* mqtt_topic = "FMO001"; // 以001结尾=插座,002=灯
// HTTP 服务器端口
const int HTTP_PORT = 80;
// FMO 设备 WebSocket 配置
const char* fmo_host = "FMOIP地址";
const int fmo_ws_port = 80;
const char* fmo_ws_path = "/ws";
// =================================================
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
WebSocketsClient wsClient;
ESP8266WebServer httpServer(HTTP_PORT);
// 状态变量
bool wsConnected = false;
bool mqttConnectedFlag = false;
String pendingCmd = ""; // 缓存待发送的命令
unsigned long lastHeartbeat = 0;
const unsigned long HEARTBEAT_INTERVAL = 30000; // 30秒心跳
// LED 引脚(ESP-01S 的 GPIO2,板载蓝色LED,低电平点亮)
const int LED_PIN = 2;
void setup() {
Serial.begin(115200);
delay(100);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH); // LED灭
Serial.println("\n[FMO Controller] Starting...");
setupWiFi();
// 配置 MQTT
mqttClient.setServer(mqtt_server, mqtt_port);
mqttClient.setCallback(mqttCallback);
mqttClient.setBufferSize(512);
// 配置 WebSocket
wsClient.begin(fmo_host, fmo_ws_port, fmo_ws_path);
wsClient.onEvent(webSocketEvent);
wsClient.setReconnectInterval(5000); // 断线后5秒重连
wsClient.enableHeartbeat(15000, 3000, 2); // WebSocket心跳
// 配置 HTTP API
setupHTTPServer();
}
void loop() {
// 维护 WiFi
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi lost, reconnecting...");
setupWiFi();
}
// 维护 MQTT
if (!mqttClient.connected()) {
mqttConnectedFlag = false;
reconnectMQTT();
} else {
if (!mqttConnectedFlag) {
mqttConnectedFlag = true;
Serial.println("MQTT ready.");
}
}
mqttClient.loop();
// 维护 WebSocket
wsClient.loop();
// 处理 HTTP 请求
httpServer.handleClient();
// 巴法云心跳(保持设备在线)
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); // 短暂休眠,防止看门狗复位
}
// ================== 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_PIN)); // LED闪烁
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println();
Serial.print("WiFi connected, IP: ");
Serial.println(WiFi.localIP());
digitalWrite(LED_PIN, LOW); // LED常亮表示WiFi已连接
} else {
Serial.println("\nWiFi connect failed, will retry...");
}
}
// ================== MQTT (巴法云) ==================
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];
}
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);
}
// 忽略心跳下行消息如 "heartbeat/down"
}
// ================== WebSocket (FMO) ==================
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灭表示WS断开
break;
case WStype_CONNECTED:
wsConnected = true;
Serial.println("[WS] Connected to FMO");
digitalWrite(LED_PIN, LOW); // LED亮表示双连接正常
// 如果有待发送的命令,立即发送
if (pendingCmd.length() > 0) {
sendFMOCommand(pendingCmd.toInt());
pendingCmd = "";
}
break;
case WStype_TEXT:
Serial.printf("[WS] Response: %s\n", payload);
// 可在此解析 setScreenModeResponse 确认成功
break;
case WStype_ERROR:
Serial.println("[WS] Error occurred");
break;
default:
break;
}
}
void sendFMOCommand(int mode) {
// mode: 0 = 标准模式(开), 1 = 待机模式(关)
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 Controller</title></head>";
html += "<body style='font-family:sans-serif;max-width:400px;margin:40px auto;text-align:center;'>";
html += "<h2>FMO 远程控制器</h2>";
html += "<p>设备 IP: " + WiFi.localIP().toString() + "</p>";
html += "<p>WebSocket: " + String(wsConnected ? "已连接" : "未连接") + "</p>";
html += "<p>MQTT: " + String(mqttClient.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>";
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;'>关闭 (待机模式)</a>";
html += "</body></html>";
httpServer.send(200, "text/html", html);
}
void handleOn() {
Serial.println("[HTTP] Request: /on");
sendFMOCommand(0);
String json = "{\"status\":\"ok\",\"action\":\"on\",\"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\":" + String(wsConnected ? "true" : "false") + ",\"mqtt\":" + String(mqttClient.connected() ? "true" : "false") + "}";
httpServer.send(200, "application/json", json);
}
void handleNotFound() {
httpServer.send(404, "application/json", "{\"status\":\"error\",\"message\":\"Not Found\"}");
}