|
|
@@ -51,7 +51,7 @@
|
|
|
// BLE 配置
|
|
|
// =====================================================
|
|
|
const char* BLE_DEVICE_NAME = "AI-Light";
|
|
|
-const char* FW_VERSION = "1.0.0";
|
|
|
+const char* FW_VERSION = "1.1.0";
|
|
|
#define SERVICE_UUID "b8b7e001-7a6b-4f4f-9a8b-11c0ffee0001"
|
|
|
#define MODE_CHAR_UUID "b8b7e002-7a6b-4f4f-9a8b-11c0ffee0001"
|
|
|
#define CONFIG_CHAR_UUID "b8b7e003-7a6b-4f4f-9a8b-11c0ffee0001"
|
|
|
@@ -78,6 +78,29 @@ const unsigned long LONG_PRESS_MS = 3000;
|
|
|
const unsigned long TRIPLE_PRESS_WINDOW_MS = 1500;
|
|
|
const int TRIPLE_PRESS_COUNT = 3;
|
|
|
|
|
|
+// 非阻塞连接超时
|
|
|
+const unsigned long WIFI_CONNECT_TIMEOUT_MS = 10000;
|
|
|
+const unsigned long MQTT_CONNECT_TIMEOUT_MS = 5000;
|
|
|
+const unsigned long MQTT_RECONNECT_INTERVAL_MS = 3000;
|
|
|
+
|
|
|
+// =====================================================
|
|
|
+// 连接状态机
|
|
|
+// =====================================================
|
|
|
+enum ConnState {
|
|
|
+ CONN_IDLE,
|
|
|
+ CONN_WIFI_CONNECTING,
|
|
|
+ CONN_MQTT_CONNECTING,
|
|
|
+ CONN_CONNECTED,
|
|
|
+ CONN_WIFI_FAILED,
|
|
|
+ CONN_MQTT_FAILED
|
|
|
+};
|
|
|
+
|
|
|
+ConnState connState = CONN_IDLE;
|
|
|
+unsigned long connStateStart = 0;
|
|
|
+unsigned long lastMqttReconnectAttempt = 0;
|
|
|
+int connRetryCount = 0;
|
|
|
+const int MAX_CONN_RETRIES = 3;
|
|
|
+
|
|
|
// =====================================================
|
|
|
// 全局状态
|
|
|
// =====================================================
|
|
|
@@ -413,24 +436,6 @@ void updateInit() {
|
|
|
setOnly(brightness, brightness, brightness);
|
|
|
}
|
|
|
|
|
|
-void breathingGreen(int times) {
|
|
|
- const unsigned long period = 2500;
|
|
|
- for (int i = 0; i < times; i++) {
|
|
|
- unsigned long start = millis();
|
|
|
- while (millis() - start < period) {
|
|
|
- unsigned long t = millis() - start;
|
|
|
- int brightness;
|
|
|
- if (t < 800) brightness = map(t, 0, 800, 0, GREEN_MAX);
|
|
|
- else if (t < 1200) brightness = GREEN_MAX;
|
|
|
- else if (t < 2000) brightness = map(t, 1200, 2000, GREEN_MAX, 0);
|
|
|
- else brightness = 0;
|
|
|
- setOnly(0, 0, brightness);
|
|
|
- delay(5);
|
|
|
- }
|
|
|
- }
|
|
|
- allOff();
|
|
|
-}
|
|
|
-
|
|
|
|
|
|
// =====================================================
|
|
|
// BOOT 按钮处理
|
|
|
@@ -504,7 +509,7 @@ void checkBootButton() {
|
|
|
|
|
|
void updateStatusLed() {
|
|
|
if (useMQTT) {
|
|
|
- if (wifiConnected) {
|
|
|
+ if (wifiConnected && mqttClient.connected()) {
|
|
|
digitalWrite(STATUS_PIN, LOW);
|
|
|
} else {
|
|
|
digitalWrite(STATUS_PIN, HIGH);
|
|
|
@@ -608,58 +613,93 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-bool connectWiFi() {
|
|
|
- Serial.print("Connecting WiFi");
|
|
|
- WiFi.mode(WIFI_STA);
|
|
|
- WiFi.begin(cfgWifiSsid.c_str(), cfgWifiPass.c_str());
|
|
|
+void mqttSubscribe() {
|
|
|
+ mqttClient.subscribe(cfgMqttTopic.c_str());
|
|
|
+ mqttClient.subscribe(cfgMqttTopicConfig.c_str());
|
|
|
+}
|
|
|
+
|
|
|
+void updateConnection() {
|
|
|
+ unsigned long now = millis();
|
|
|
+
|
|
|
+ switch (connState) {
|
|
|
+ case CONN_IDLE:
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CONN_WIFI_CONNECTING:
|
|
|
+ if (WiFi.status() == WL_CONNECTED) {
|
|
|
+ Serial.printf("\nWiFi OK. IP: %s\n", WiFi.localIP().toString().c_str());
|
|
|
+ wifiConnected = true;
|
|
|
+ digitalWrite(STATUS_PIN, LOW);
|
|
|
+ mqttClient.setServer(cfgMqttBroker.c_str(), cfgMqttPort);
|
|
|
+ mqttClient.setCallback(mqttCallback);
|
|
|
+ connState = CONN_MQTT_CONNECTING;
|
|
|
+ connStateStart = now;
|
|
|
+ Serial.println("MQTT connecting...");
|
|
|
+ } else if (now - connStateStart > WIFI_CONNECT_TIMEOUT_MS) {
|
|
|
+ Serial.println("\nWiFi connect timeout.");
|
|
|
+ WiFi.disconnect(true);
|
|
|
+ WiFi.mode(WIFI_OFF);
|
|
|
+ wifiConnected = false;
|
|
|
+ connState = CONN_WIFI_FAILED;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CONN_MQTT_CONNECTING:
|
|
|
+ if (mqttClient.connect(cfgMqttClient.c_str(), cfgMqttUser.c_str(), cfgMqttPass.c_str())) {
|
|
|
+ Serial.println("MQTT OK.");
|
|
|
+ mqttSubscribe();
|
|
|
+ setMode("traffic");
|
|
|
+ connState = CONN_CONNECTED;
|
|
|
+ connRetryCount = 0;
|
|
|
+ } else if (now - connStateStart > MQTT_CONNECT_TIMEOUT_MS) {
|
|
|
+ Serial.println("MQTT connect timeout.");
|
|
|
+ connState = CONN_MQTT_FAILED;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CONN_CONNECTED:
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CONN_WIFI_FAILED:
|
|
|
+ if (connRetryCount >= MAX_CONN_RETRIES) {
|
|
|
+ Serial.println("Max WiFi retries reached. Falling back to BLE mode...");
|
|
|
+ WiFi.disconnect(true);
|
|
|
+ WiFi.mode(WIFI_OFF);
|
|
|
+ wifiConnected = false;
|
|
|
+ connState = CONN_IDLE;
|
|
|
+ setMode("init");
|
|
|
+ setupBLE();
|
|
|
+ Serial.println("BLE advertising. Waiting for connection...");
|
|
|
+ }
|
|
|
+ break;
|
|
|
|
|
|
- for (int retry = 1; retry <= 5; retry++) {
|
|
|
- Serial.printf("\nWiFi %d/5", retry);
|
|
|
- int attempts = 0;
|
|
|
- while (WiFi.status() != WL_CONNECTED && attempts < 60) {
|
|
|
- delay(5);
|
|
|
- updateInit();
|
|
|
- updateStatusLed();
|
|
|
- Serial.print(".");
|
|
|
- attempts++;
|
|
|
- }
|
|
|
- if (WiFi.status() == WL_CONNECTED) {
|
|
|
- Serial.printf("\nWiFi OK. IP: %s\n", WiFi.localIP().toString().c_str());
|
|
|
- digitalWrite(STATUS_PIN, LOW);
|
|
|
- return true;
|
|
|
- }
|
|
|
- Serial.println("\nFailed, retrying..."); delay(2000);
|
|
|
+ case CONN_MQTT_FAILED:
|
|
|
+ if (currentMode != "error") setMode("error");
|
|
|
+ break;
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- Serial.println("\nWiFi failed. Turning off WiFi.");
|
|
|
- WiFi.disconnect(true);
|
|
|
- WiFi.mode(WIFI_OFF);
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-bool connectMQTT() {
|
|
|
- mqttClient.setServer(cfgMqttBroker.c_str(), cfgMqttPort);
|
|
|
- mqttClient.setCallback(mqttCallback);
|
|
|
- Serial.print("MQTT connecting");
|
|
|
- int attempts = 0;
|
|
|
- while (!mqttClient.connected()) {
|
|
|
- if (mqttClient.connect(cfgMqttClient.c_str(), cfgMqttUser.c_str(), cfgMqttPass.c_str())) {
|
|
|
- Serial.println("\nMQTT OK.");
|
|
|
- mqttClient.subscribe(cfgMqttTopic.c_str());
|
|
|
- mqttClient.subscribe(cfgMqttTopicConfig.c_str());
|
|
|
- allOff(); breathingGreen(3); publishStatus();
|
|
|
- return true;
|
|
|
- }
|
|
|
- Serial.print("."); delay(500);
|
|
|
- if (++attempts > 60) { Serial.println("\nMQTT failed!"); return false; }
|
|
|
- }
|
|
|
- return false;
|
|
|
+void startConnection() {
|
|
|
+ Serial.printf("Starting WiFi connection (attempt %d/%d)...\n", connRetryCount + 1, MAX_CONN_RETRIES);
|
|
|
+ WiFi.mode(WIFI_STA);
|
|
|
+ WiFi.begin(cfgWifiSsid.c_str(), cfgWifiPass.c_str());
|
|
|
+ connState = CONN_WIFI_CONNECTING;
|
|
|
+ connStateStart = millis();
|
|
|
}
|
|
|
|
|
|
-void checkMQTTConnection() {
|
|
|
- if (useMQTT && !mqttClient.connected()) {
|
|
|
- Serial.println("MQTT reconnecting...");
|
|
|
- connectMQTT();
|
|
|
+void handleMqttReconnect() {
|
|
|
+ if (connState != CONN_CONNECTED) return;
|
|
|
+ if (mqttClient.connected()) return;
|
|
|
+
|
|
|
+ unsigned long now = millis();
|
|
|
+ if (now - lastMqttReconnectAttempt < MQTT_RECONNECT_INTERVAL_MS) return;
|
|
|
+ lastMqttReconnectAttempt = now;
|
|
|
+
|
|
|
+ Serial.println("MQTT reconnecting...");
|
|
|
+ if (mqttClient.connect(cfgMqttClient.c_str(), cfgMqttUser.c_str(), cfgMqttPass.c_str())) {
|
|
|
+ Serial.println("MQTT reconnected.");
|
|
|
+ mqttSubscribe();
|
|
|
+ publishStatus();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -694,18 +734,12 @@ void setup() {
|
|
|
Serial.printf("Pins: R=%d G=%d Y=%d\n", redPin, greenPin, yellowPin);
|
|
|
|
|
|
if (useMQTT && isConfigComplete()) {
|
|
|
- wifiConnected = connectWiFi();
|
|
|
- if (wifiConnected) {
|
|
|
- connectMQTT();
|
|
|
- setMode("traffic");
|
|
|
- Serial.println("WiFi/MQTT mode. Long press BOOT (3s) to switch.");
|
|
|
- return;
|
|
|
- }
|
|
|
- Serial.println("WiFi failed. Entering BLE config mode.");
|
|
|
+ startConnection();
|
|
|
+ Serial.println("WiFi/MQTT mode. Long press BOOT (3s) to switch.");
|
|
|
+ } else {
|
|
|
+ setupBLE();
|
|
|
+ Serial.println("BLE advertising. Waiting for connection...");
|
|
|
}
|
|
|
-
|
|
|
- setupBLE();
|
|
|
- Serial.println("BLE advertising. Waiting for connection...");
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -717,18 +751,36 @@ void loop() {
|
|
|
updateStatusLed();
|
|
|
checkBootButton();
|
|
|
|
|
|
- if (useMQTT && wifiConnected) {
|
|
|
- if (WiFi.status() != WL_CONNECTED) {
|
|
|
- Serial.println("WiFi lost, reconnecting...");
|
|
|
- wifiConnected = false;
|
|
|
- wifiConnected = connectWiFi();
|
|
|
- if (!wifiConnected) {
|
|
|
- Serial.println("WiFi failed. BLE config mode active.");
|
|
|
+ if (useMQTT && isConfigComplete()) {
|
|
|
+ updateConnection();
|
|
|
+
|
|
|
+ if (connState == CONN_CONNECTED) {
|
|
|
+ if (WiFi.status() != WL_CONNECTED) {
|
|
|
+ Serial.println("WiFi lost, restarting connection...");
|
|
|
+ wifiConnected = false;
|
|
|
+ connState = CONN_IDLE;
|
|
|
+ startConnection();
|
|
|
+ } else {
|
|
|
+ handleMqttReconnect();
|
|
|
+ mqttClient.loop();
|
|
|
+ }
|
|
|
+ } else if (connState == CONN_WIFI_FAILED) {
|
|
|
+ if (connRetryCount < MAX_CONN_RETRIES) {
|
|
|
+ static unsigned long lastRetry = 0;
|
|
|
+ if (millis() - lastRetry > 5000) {
|
|
|
+ lastRetry = millis();
|
|
|
+ connRetryCount++;
|
|
|
+ startConnection();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (connState == CONN_MQTT_FAILED) {
|
|
|
+ static unsigned long lastMqttRetry = 0;
|
|
|
+ if (millis() - lastMqttRetry > MQTT_RECONNECT_INTERVAL_MS) {
|
|
|
+ lastMqttRetry = millis();
|
|
|
+ connState = CONN_MQTT_CONNECTING;
|
|
|
+ connStateStart = millis();
|
|
|
+ Serial.println("MQTT retrying...");
|
|
|
}
|
|
|
- }
|
|
|
- if (wifiConnected) {
|
|
|
- checkMQTTConnection();
|
|
|
- mqttClient.loop();
|
|
|
}
|
|
|
}
|
|
|
|