|
@@ -6,6 +6,8 @@
|
|
|
#include <BLEUtils.h>
|
|
#include <BLEUtils.h>
|
|
|
#include <BLE2902.h>
|
|
#include <BLE2902.h>
|
|
|
#include <Preferences.h>
|
|
#include <Preferences.h>
|
|
|
|
|
+#include <HTTPClient.h>
|
|
|
|
|
+#include <Update.h>
|
|
|
|
|
|
|
|
// =====================================================
|
|
// =====================================================
|
|
|
// ESP32-C3 SuperMini + 原玩具公共正极灯板:BLE + MQTT 双模式
|
|
// ESP32-C3 SuperMini + 原玩具公共正极灯板:BLE + MQTT 双模式
|
|
@@ -30,32 +32,39 @@
|
|
|
// "mqtt_pass": "pass",
|
|
// "mqtt_pass": "pass",
|
|
|
// "mqtt_client": "AI-Light",
|
|
// "mqtt_client": "AI-Light",
|
|
|
// "mqtt_topic": "opencode/status",
|
|
// "mqtt_topic": "opencode/status",
|
|
|
-// "mqtt_status": "openCodeLight/status"
|
|
|
|
|
|
|
+// "mqtt_status": "openCodeLight/status",
|
|
|
|
|
+// "pin_red": 4,
|
|
|
|
|
+// "pin_green": 3,
|
|
|
|
|
+// "pin_yellow": 2,
|
|
|
|
|
+// "ota_url": "https://example.com/firmware.bin"
|
|
|
// }
|
|
// }
|
|
|
//
|
|
//
|
|
|
-// 接线方式(V3 版本):
|
|
|
|
|
|
|
+// 接线方式(默认引脚,可通过配置修改):
|
|
|
// ESP32 3.3V -> 原灯板 + / 原电池正极
|
|
// ESP32 3.3V -> 原灯板 + / 原电池正极
|
|
|
-// ESP32 IO2 -> 220Ω -> L1 控制点 = 黄灯
|
|
|
|
|
-// ESP32 IO3 -> 220Ω -> L2 控制点 = 绿灯
|
|
|
|
|
-// ESP32 IO4 -> 220Ω -> L3 控制点 = 红灯
|
|
|
|
|
|
|
+// ESP32 IO4 -> 220Ω -> L3 控制点 = 红灯(默认)
|
|
|
|
|
+// ESP32 IO3 -> 220Ω -> L2 控制点 = 绿灯(默认)
|
|
|
|
|
+// ESP32 IO2 -> 220Ω -> L1 控制点 = 黄灯(默认)
|
|
|
|
|
+// ESP32 IO8 -> 状态 LED(固定)
|
|
|
|
|
+// ESP32 IO9 -> BOOT 按钮(固定)
|
|
|
// =====================================================
|
|
// =====================================================
|
|
|
|
|
|
|
|
// =====================================================
|
|
// =====================================================
|
|
|
// BLE 配置
|
|
// BLE 配置
|
|
|
// =====================================================
|
|
// =====================================================
|
|
|
const char* BLE_DEVICE_NAME = "AI-Light";
|
|
const char* BLE_DEVICE_NAME = "AI-Light";
|
|
|
|
|
+const char* FW_VERSION = "1.0.0";
|
|
|
#define SERVICE_UUID "b8b7e001-7a6b-4f4f-9a8b-11c0ffee0001"
|
|
#define SERVICE_UUID "b8b7e001-7a6b-4f4f-9a8b-11c0ffee0001"
|
|
|
#define MODE_CHAR_UUID "b8b7e002-7a6b-4f4f-9a8b-11c0ffee0001"
|
|
#define MODE_CHAR_UUID "b8b7e002-7a6b-4f4f-9a8b-11c0ffee0001"
|
|
|
#define CONFIG_CHAR_UUID "b8b7e003-7a6b-4f4f-9a8b-11c0ffee0001"
|
|
#define CONFIG_CHAR_UUID "b8b7e003-7a6b-4f4f-9a8b-11c0ffee0001"
|
|
|
|
|
|
|
|
// =====================================================
|
|
// =====================================================
|
|
|
-// 引脚定义(V3 版本)
|
|
|
|
|
|
|
+// 引脚定义(红绿黄可通过配置动态修改,状态灯和按钮固定)
|
|
|
// =====================================================
|
|
// =====================================================
|
|
|
-const int RED_PIN = 4;
|
|
|
|
|
-const int GREEN_PIN = 3;
|
|
|
|
|
-const int YELLOW_PIN = 2;
|
|
|
|
|
-const int WIFI_LED_PIN = 8;
|
|
|
|
|
-const int BTN_BOOT_PIN = 9;
|
|
|
|
|
|
|
+int redPin = 4;
|
|
|
|
|
+int greenPin = 3;
|
|
|
|
|
+int yellowPin = 2;
|
|
|
|
|
+const int STATUS_PIN = 8;
|
|
|
|
|
+const int BUTTON_PIN = 9;
|
|
|
|
|
|
|
|
const int PWM_FREQ = 5000;
|
|
const int PWM_FREQ = 5000;
|
|
|
const int PWM_RESOLUTION = 8;
|
|
const int PWM_RESOLUTION = 8;
|
|
@@ -95,6 +104,8 @@ String cfgMqttPass = "";
|
|
|
String cfgMqttClient = "AI-Light";
|
|
String cfgMqttClient = "AI-Light";
|
|
|
String cfgMqttTopic = "opencode/status";
|
|
String cfgMqttTopic = "opencode/status";
|
|
|
String cfgMqttStatus = "openCodeLight/status";
|
|
String cfgMqttStatus = "openCodeLight/status";
|
|
|
|
|
+String cfgOtaUrl = "";
|
|
|
|
|
+bool otaInProgress = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
// =====================================================
|
|
// =====================================================
|
|
@@ -111,6 +122,10 @@ void loadConfig() {
|
|
|
cfgMqttClient = preferences.getString("mqtt_client", "AI-Light");
|
|
cfgMqttClient = preferences.getString("mqtt_client", "AI-Light");
|
|
|
cfgMqttTopic = preferences.getString("mqtt_topic", "opencode/status");
|
|
cfgMqttTopic = preferences.getString("mqtt_topic", "opencode/status");
|
|
|
cfgMqttStatus = preferences.getString("mqtt_status", "openCodeLight/status");
|
|
cfgMqttStatus = preferences.getString("mqtt_status", "openCodeLight/status");
|
|
|
|
|
+ redPin = preferences.getUInt("pin_red", 4);
|
|
|
|
|
+ greenPin = preferences.getUInt("pin_green", 3);
|
|
|
|
|
+ yellowPin = preferences.getUInt("pin_yellow", 2);
|
|
|
|
|
+ cfgOtaUrl = preferences.getString("ota_url", "");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool isConfigComplete() {
|
|
bool isConfigComplete() {
|
|
@@ -119,6 +134,7 @@ bool isConfigComplete() {
|
|
|
|
|
|
|
|
String getConfigJson() {
|
|
String getConfigJson() {
|
|
|
JsonDocument doc;
|
|
JsonDocument doc;
|
|
|
|
|
+ doc["fw_version"] = FW_VERSION;
|
|
|
doc["wifi_ssid"] = cfgWifiSsid;
|
|
doc["wifi_ssid"] = cfgWifiSsid;
|
|
|
doc["mqtt_broker"] = cfgMqttBroker;
|
|
doc["mqtt_broker"] = cfgMqttBroker;
|
|
|
doc["mqtt_port"] = cfgMqttPort;
|
|
doc["mqtt_port"] = cfgMqttPort;
|
|
@@ -127,6 +143,10 @@ String getConfigJson() {
|
|
|
doc["mqtt_topic"] = cfgMqttTopic;
|
|
doc["mqtt_topic"] = cfgMqttTopic;
|
|
|
doc["mqtt_status"] = cfgMqttStatus;
|
|
doc["mqtt_status"] = cfgMqttStatus;
|
|
|
doc["comm_mode"] = useMQTT ? 1 : 0;
|
|
doc["comm_mode"] = useMQTT ? 1 : 0;
|
|
|
|
|
+ doc["pin_red"] = redPin;
|
|
|
|
|
+ doc["pin_green"] = greenPin;
|
|
|
|
|
+ doc["pin_yellow"] = yellowPin;
|
|
|
|
|
+ doc["ota_url"] = cfgOtaUrl;
|
|
|
String out;
|
|
String out;
|
|
|
serializeJson(doc, out);
|
|
serializeJson(doc, out);
|
|
|
return out;
|
|
return out;
|
|
@@ -159,6 +179,14 @@ void saveConfigFromJson(const String& json) {
|
|
|
preferences.putString("mqtt_topic", doc["mqtt_topic"].as<String>());
|
|
preferences.putString("mqtt_topic", doc["mqtt_topic"].as<String>());
|
|
|
if (doc.containsKey("mqtt_status"))
|
|
if (doc.containsKey("mqtt_status"))
|
|
|
preferences.putString("mqtt_status", doc["mqtt_status"].as<String>());
|
|
preferences.putString("mqtt_status", doc["mqtt_status"].as<String>());
|
|
|
|
|
+ if (doc.containsKey("pin_red"))
|
|
|
|
|
+ preferences.putUInt("pin_red", doc["pin_red"].as<uint8_t>());
|
|
|
|
|
+ if (doc.containsKey("pin_green"))
|
|
|
|
|
+ preferences.putUInt("pin_green", doc["pin_green"].as<uint8_t>());
|
|
|
|
|
+ if (doc.containsKey("pin_yellow"))
|
|
|
|
|
+ preferences.putUInt("pin_yellow", doc["pin_yellow"].as<uint8_t>());
|
|
|
|
|
+ if (doc.containsKey("ota_url"))
|
|
|
|
|
+ preferences.putString("ota_url", doc["ota_url"].as<String>());
|
|
|
|
|
|
|
|
Serial.println("Config saved. Restarting...");
|
|
Serial.println("Config saved. Restarting...");
|
|
|
delay(500);
|
|
delay(500);
|
|
@@ -177,15 +205,15 @@ void writeLed(int pin, int value) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void allOff() {
|
|
void allOff() {
|
|
|
- writeLed(RED_PIN, 0);
|
|
|
|
|
- writeLed(YELLOW_PIN, 0);
|
|
|
|
|
- writeLed(GREEN_PIN, 0);
|
|
|
|
|
|
|
+ writeLed(redPin, 0);
|
|
|
|
|
+ writeLed(yellowPin, 0);
|
|
|
|
|
+ writeLed(greenPin, 0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void setOnly(int red, int yellow, int green) {
|
|
void setOnly(int red, int yellow, int green) {
|
|
|
- writeLed(RED_PIN, constrain(red, 0, RED_MAX));
|
|
|
|
|
- writeLed(YELLOW_PIN, constrain(yellow, 0, YELLOW_MAX));
|
|
|
|
|
- writeLed(GREEN_PIN, constrain(green, 0, GREEN_MAX));
|
|
|
|
|
|
|
+ writeLed(redPin, constrain(red, 0, RED_MAX));
|
|
|
|
|
+ writeLed(yellowPin, constrain(yellow, 0, YELLOW_MAX));
|
|
|
|
|
+ writeLed(greenPin, constrain(green, 0, GREEN_MAX));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int triWave(unsigned long t, unsigned long period, int maxValue) {
|
|
int triWave(unsigned long t, unsigned long period, int maxValue) {
|
|
@@ -413,7 +441,7 @@ bool bootWasPressed = false;
|
|
|
bool switchTriggered = false;
|
|
bool switchTriggered = false;
|
|
|
|
|
|
|
|
void checkBootButton() {
|
|
void checkBootButton() {
|
|
|
- bool pressed = (digitalRead(BTN_BOOT_PIN) == LOW);
|
|
|
|
|
|
|
+ bool pressed = (digitalRead(BUTTON_PIN) == LOW);
|
|
|
if (pressed && !bootWasPressed) {
|
|
if (pressed && !bootWasPressed) {
|
|
|
bootPressStart = millis();
|
|
bootPressStart = millis();
|
|
|
bootWasPressed = true;
|
|
bootWasPressed = true;
|
|
@@ -450,12 +478,12 @@ bool statusLedState = false;
|
|
|
void updateStatusLed() {
|
|
void updateStatusLed() {
|
|
|
bool connected = (useMQTT && wifiConnected) ? (WiFi.status() == WL_CONNECTED) : bleDeviceConnected;
|
|
bool connected = (useMQTT && wifiConnected) ? (WiFi.status() == WL_CONNECTED) : bleDeviceConnected;
|
|
|
if (connected) {
|
|
if (connected) {
|
|
|
- digitalWrite(WIFI_LED_PIN, LOW);
|
|
|
|
|
|
|
+ digitalWrite(STATUS_PIN, LOW);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
if (millis() - lastStatusLedToggle >= 500) {
|
|
if (millis() - lastStatusLedToggle >= 500) {
|
|
|
statusLedState = !statusLedState;
|
|
statusLedState = !statusLedState;
|
|
|
- digitalWrite(WIFI_LED_PIN, statusLedState ? LOW : HIGH);
|
|
|
|
|
|
|
+ digitalWrite(STATUS_PIN, statusLedState ? LOW : HIGH);
|
|
|
lastStatusLedToggle = millis();
|
|
lastStatusLedToggle = millis();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -471,7 +499,14 @@ class ServerCallbacks : public BLEServerCallbacks {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
class ModeCharCallbacks : public BLECharacteristicCallbacks {
|
|
class ModeCharCallbacks : public BLECharacteristicCallbacks {
|
|
|
- void onWrite(BLECharacteristic* c) { setMode(c->getValue()); }
|
|
|
|
|
|
|
+ void onWrite(BLECharacteristic* c) {
|
|
|
|
|
+ String val = c->getValue();
|
|
|
|
|
+ if (val == "ota") {
|
|
|
|
|
+ performOTA();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ setMode(val);
|
|
|
|
|
+ }
|
|
|
void onRead(BLECharacteristic* c) { c->setValue(currentMode.c_str()); }
|
|
void onRead(BLECharacteristic* c) { c->setValue(currentMode.c_str()); }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -529,6 +564,10 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
|
|
|
else if (c == "reasoning") setMode("thinking");
|
|
else if (c == "reasoning") setMode("thinking");
|
|
|
else if (c == "using_tool") setMode("ai");
|
|
else if (c == "using_tool") setMode("ai");
|
|
|
else if (c == "error") setMode("error");
|
|
else if (c == "error") setMode("error");
|
|
|
|
|
+ else if (c == "ota") {
|
|
|
|
|
+ performOTA();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
else setMode(c);
|
|
else setMode(c);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -546,7 +585,7 @@ bool connectWiFi() {
|
|
|
}
|
|
}
|
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
|
Serial.printf("\nWiFi OK. IP: %s\n", WiFi.localIP().toString().c_str());
|
|
Serial.printf("\nWiFi OK. IP: %s\n", WiFi.localIP().toString().c_str());
|
|
|
- digitalWrite(WIFI_LED_PIN, LOW);
|
|
|
|
|
|
|
+ digitalWrite(STATUS_PIN, LOW);
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
Serial.println("\nFailed, retrying..."); delay(2000);
|
|
Serial.println("\nFailed, retrying..."); delay(2000);
|
|
@@ -581,6 +620,114 @@ void checkMQTTConnection() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+// =====================================================
|
|
|
|
|
+// OTA 固件更新
|
|
|
|
|
+// =====================================================
|
|
|
|
|
+
|
|
|
|
|
+void notifyOtaStatus(const String& status) {
|
|
|
|
|
+ if (pModeCharacteristic && bleDeviceConnected) {
|
|
|
|
|
+ pModeCharacteristic->setValue(status.c_str());
|
|
|
|
|
+ pModeCharacteristic->notify();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (useMQTT && mqttClient.connected()) {
|
|
|
|
|
+ mqttClient.publish(cfgMqttStatus.c_str(), status.c_str(), true);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void performOTA() {
|
|
|
|
|
+ Serial.println("=== OTA Start ===");
|
|
|
|
|
+
|
|
|
|
|
+ if (otaInProgress) {
|
|
|
|
|
+ Serial.println("OTA already in progress");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (cfgOtaUrl.length() == 0) {
|
|
|
|
|
+ Serial.println("OTA URL not configured");
|
|
|
|
|
+ notifyOtaStatus("ota:no_url");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (WiFi.status() != WL_CONNECTED) {
|
|
|
|
|
+ Serial.println("WiFi not connected");
|
|
|
|
|
+ notifyOtaStatus("ota:no_wifi");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ otaInProgress = true;
|
|
|
|
|
+ notifyOtaStatus("ota:downloading");
|
|
|
|
|
+
|
|
|
|
|
+ HTTPClient http;
|
|
|
|
|
+ http.begin(cfgOtaUrl);
|
|
|
|
|
+ http.setTimeout(30000);
|
|
|
|
|
+
|
|
|
|
|
+ int httpCode = http.GET();
|
|
|
|
|
+ Serial.printf("HTTP GET: %d\n", httpCode);
|
|
|
|
|
+
|
|
|
|
|
+ if (httpCode != 200) {
|
|
|
|
|
+ Serial.printf("HTTP error: %d\n", httpCode);
|
|
|
|
|
+ notifyOtaStatus("ota:error");
|
|
|
|
|
+ http.end();
|
|
|
|
|
+ otaInProgress = false;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int contentLength = http.getSize();
|
|
|
|
|
+ Serial.printf("Firmware size: %d bytes\n", contentLength);
|
|
|
|
|
+
|
|
|
|
|
+ if (contentLength <= 0) {
|
|
|
|
|
+ Serial.println("Invalid content length");
|
|
|
|
|
+ notifyOtaStatus("ota:error");
|
|
|
|
|
+ http.end();
|
|
|
|
|
+ otaInProgress = false;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!Update.begin(contentLength)) {
|
|
|
|
|
+ Serial.println("Update.begin failed");
|
|
|
|
|
+ notifyOtaStatus("ota:error");
|
|
|
|
|
+ http.end();
|
|
|
|
|
+ otaInProgress = false;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ WiFiClient* stream = http.getStreamPtr();
|
|
|
|
|
+ uint8_t buf[1024];
|
|
|
|
|
+ int written = 0;
|
|
|
|
|
+ unsigned long lastProgress = millis();
|
|
|
|
|
+
|
|
|
|
|
+ while (http.connected() && written < contentLength) {
|
|
|
|
|
+ size_t size = stream->available();
|
|
|
|
|
+ if (size) {
|
|
|
|
|
+ int bytesRead = stream->readBytes(buf, min(size, sizeof(buf)));
|
|
|
|
|
+ size_t bytesWritten = Update.write(buf, bytesRead);
|
|
|
|
|
+ written += bytesWritten;
|
|
|
|
|
+
|
|
|
|
|
+ if (millis() - lastProgress >= 1000) {
|
|
|
|
|
+ Serial.printf("Progress: %d/%d (%.1f%%)\n", written, contentLength, (float)written / contentLength * 100);
|
|
|
|
|
+ lastProgress = millis();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ delay(1);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Serial.printf("Written: %d/%d\n", written, contentLength);
|
|
|
|
|
+
|
|
|
|
|
+ if (Update.end(true)) {
|
|
|
|
|
+ Serial.println("OTA success!");
|
|
|
|
|
+ notifyOtaStatus("ota:success");
|
|
|
|
|
+ delay(1000);
|
|
|
|
|
+ ESP.restart();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Serial.printf("OTA error: %s\n", Update.errorString());
|
|
|
|
|
+ notifyOtaStatus("ota:error");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ http.end();
|
|
|
|
|
+ otaInProgress = false;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
// =====================================================
|
|
// =====================================================
|
|
|
// 初始化
|
|
// 初始化
|
|
|
// =====================================================
|
|
// =====================================================
|
|
@@ -589,23 +736,24 @@ void setup() {
|
|
|
Serial.begin(115200);
|
|
Serial.begin(115200);
|
|
|
delay(500);
|
|
delay(500);
|
|
|
|
|
|
|
|
- ledcAttach(RED_PIN, PWM_FREQ, PWM_RESOLUTION);
|
|
|
|
|
- ledcAttach(YELLOW_PIN, PWM_FREQ, PWM_RESOLUTION);
|
|
|
|
|
- ledcAttach(GREEN_PIN, PWM_FREQ, PWM_RESOLUTION);
|
|
|
|
|
- pinMode(WIFI_LED_PIN, OUTPUT);
|
|
|
|
|
- pinMode(BTN_BOOT_PIN, INPUT_PULLUP);
|
|
|
|
|
- digitalWrite(WIFI_LED_PIN, HIGH);
|
|
|
|
|
- allOff();
|
|
|
|
|
-
|
|
|
|
|
preferences.begin("ai-light", false);
|
|
preferences.begin("ai-light", false);
|
|
|
useMQTT = (preferences.getUInt("comm_mode", 1) != 0);
|
|
useMQTT = (preferences.getUInt("comm_mode", 1) != 0);
|
|
|
loadConfig();
|
|
loadConfig();
|
|
|
|
|
|
|
|
|
|
+ ledcAttach(redPin, PWM_FREQ, PWM_RESOLUTION);
|
|
|
|
|
+ ledcAttach(yellowPin, PWM_FREQ, PWM_RESOLUTION);
|
|
|
|
|
+ ledcAttach(greenPin, PWM_FREQ, PWM_RESOLUTION);
|
|
|
|
|
+ pinMode(STATUS_PIN, OUTPUT);
|
|
|
|
|
+ pinMode(BUTTON_PIN, INPUT_PULLUP);
|
|
|
|
|
+ digitalWrite(STATUS_PIN, HIGH);
|
|
|
|
|
+ allOff();
|
|
|
|
|
+
|
|
|
Serial.println();
|
|
Serial.println();
|
|
|
Serial.println("=== AI-Light ===");
|
|
Serial.println("=== AI-Light ===");
|
|
|
Serial.printf("Mode: %s\n", useMQTT ? "MQTT" : "BLE-only");
|
|
Serial.printf("Mode: %s\n", useMQTT ? "MQTT" : "BLE-only");
|
|
|
Serial.printf("WiFi: %s\n", cfgWifiSsid.length() > 0 ? cfgWifiSsid.c_str() : "(not set)");
|
|
Serial.printf("WiFi: %s\n", cfgWifiSsid.length() > 0 ? cfgWifiSsid.c_str() : "(not set)");
|
|
|
Serial.printf("MQTT: %s\n", cfgMqttBroker.length() > 0 ? cfgMqttBroker.c_str() : "(not set)");
|
|
Serial.printf("MQTT: %s\n", cfgMqttBroker.length() > 0 ? cfgMqttBroker.c_str() : "(not set)");
|
|
|
|
|
+ Serial.printf("Pins: R=%d G=%d Y=%d\n", redPin, greenPin, yellowPin);
|
|
|
|
|
|
|
|
currentMode = "init";
|
|
currentMode = "init";
|
|
|
modeStart = millis();
|
|
modeStart = millis();
|