From d583370c6d6f167d90538e22df857dc223cc6158 Mon Sep 17 00:00:00 2001 From: Timothy Yin Date: Mon, 24 Nov 2025 21:02:04 +0800 Subject: [PATCH] feat(firmware): implement smart configuration for WiFi setup and connection --- hardware/firmware/src/main.cpp | 110 ++++++++++--------- hardware/firmware/src/smartconfig.cpp | 152 ++++++++++++++++++++++++++ hardware/firmware/src/smartconfig.h | 16 +++ 3 files changed, 228 insertions(+), 50 deletions(-) create mode 100644 hardware/firmware/src/smartconfig.cpp create mode 100644 hardware/firmware/src/smartconfig.h diff --git a/hardware/firmware/src/main.cpp b/hardware/firmware/src/main.cpp index d840776..ec2d630 100644 --- a/hardware/firmware/src/main.cpp +++ b/hardware/firmware/src/main.cpp @@ -10,6 +10,7 @@ #include #include "esp_system.h" +#include "smartconfig.h" #include "config.h" /* LED State Enum */ @@ -30,6 +31,9 @@ static volatile unsigned long s_blink_last_time = 0; static volatile bool s_blink_on = false; static const unsigned long BLINK_INTERVAL = 200; // 200ms blink interval +uint8_t mac[6]; +char cpSerial[13]; + struct mg_mgr mgr; // MicroOcpp::MOcppMongooseClient *client = nullptr; @@ -68,6 +72,7 @@ static void WiFiEvent(WiFiEvent_t event) break; case ARDUINO_EVENT_WIFI_STA_CONNECTED: Serial.println("WiFi connected"); + Serial.printf("- Hostname: %s\n", WiFi.getHostname()); break; case ARDUINO_EVENT_WIFI_STA_GOT_IP: Serial.println("Got IP: " + WiFi.localIP().toString()); @@ -128,13 +133,19 @@ void updateLED() void setup() { + // Get MAC address and set as Charge Point Serial Number + esp_efuse_mac_get_default(mac); + snprintf(cpSerial, sizeof(cpSerial), + "%02X%02X%02X%02X%02X%02X", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); // reset LED leds[0] = Rgb{0, 0, 0}; leds.show(); // initialize Serial Serial.begin(115200); delay(1000); - Serial.println("\n\nInitializing firmware..."); + Serial.printf("\n\n%s(%s) made by %s\n", CFG_CP_MODAL, cpSerial, CFG_CP_VENDOR); + Serial.println("Initializing firmware...\n"); // Initialize LED s_led_state = LED_INITIALIZING; @@ -142,73 +153,72 @@ void setup() s_blink_on = false; // Initialize WiFi - WiFi.onEvent(WiFiEvent); - WiFi.mode(WIFI_STA); - WiFi.begin(CFG_WIFI_SSID, CFG_WIFI_PASS); - Serial.println("WiFi connecting..."); + // WiFi.onEvent(WiFiEvent); + // WiFi.mode(WIFI_STA); + // WiFi.setHostname((CFG_CP_MODAL + String("_") + String(cpSerial).substring(String(cpSerial).length() - 6)).c_str()); + // WiFi.begin(CFG_WIFI_SSID, CFG_WIFI_PASS); // Wait for WiFi connection with LED updates - int retry = 0; - while (WiFi.status() != WL_CONNECTED && retry < 20) - { - delay(200); - updateLED(); // Update LED while waiting for WiFi - Serial.print("."); - retry++; - } - Serial.println(); + // int retry = 0; + // while (WiFi.status() != WL_CONNECTED && retry < 20) + // { + // delay(200); + // updateLED(); // Update LED while waiting for WiFi + // Serial.print("."); + // retry++; + // } + // Serial.println(); - if (WiFi.status() == WL_CONNECTED) + if (!connectToSavedWiFi()) { - Serial.println("WiFi connected"); - Serial.println("IP address: " + WiFi.localIP().toString()); - s_led_state = LED_WIFI_CONNECTED; - } - else - { - Serial.println("WiFi connection failed"); - s_led_state = LED_ERROR; + String ssidPrefix = CFG_CP_MODAL + String("_") + String(cpSerial).substring(String(cpSerial).length() - 6); + char ssidPrefixBuffer[32]; + strcpy(ssidPrefixBuffer, ssidPrefix.c_str()); + startSmartConfig(ssidPrefixBuffer, cpSerial); } mg_mgr_init(&mgr); MicroOcpp::MOcppMongooseClient *client = new MicroOcpp::MOcppMongooseClient(&mgr, CFG_OCPP_BACKEND, CFG_CP_IDENTIFIER, CFG_AUTHORIZATIONKEY, "", MicroOcpp::makeDefaultFilesystemAdapter(MicroOcpp::FilesystemOpt::Use_Mount_FormatOnFail), MicroOcpp::ProtocolVersion(1, 6)); - - uint8_t mac[6]; - esp_efuse_mac_get_default(mac); // read hardware MAC from efuse - char cpSerial[13]; - snprintf(cpSerial, sizeof(cpSerial), - "%02X%02X%02X%02X%02X%02X", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - Serial.printf("Charge Point Serial Number: %s\n", cpSerial); - mocpp_initialize(*client, ChargerCredentials(CFG_CP_MODAL, CFG_CP_VENDOR, "1.0.0", cpSerial, nullptr, nullptr, CFG_CB_SERIAL, nullptr, nullptr), MicroOcpp::makeDefaultFilesystemAdapter(MicroOcpp::FilesystemOpt::Use_Mount_FormatOnFail)); } void loop() { + if (!smartconfig_done) + { + dnsServer.processNextRequest(); + server.handleClient(); + } + if (should_reboot) + { + Serial.println("Rebooting..."); + delay(1000); + ESP.restart(); + } + mg_mgr_poll(&mgr, 10); mocpp_loop(); // Check OCPP connection status - if (s_wifi_connected) - { - auto ctx = getOcppContext(); - if (ctx && ctx->getConnection().isConnected()) - { - if (s_led_state != LED_OCPP_CONNECTED) - { - s_led_state = LED_OCPP_CONNECTED; - } - } - else - { - if (s_led_state != LED_WIFI_CONNECTED) - { - s_led_state = LED_WIFI_CONNECTED; - } - } - } + // if (s_wifi_connected) + // { + // auto ctx = getOcppContext(); + // if (ctx && ctx->getConnection().isConnected()) + // { + // if (s_led_state != LED_OCPP_CONNECTED) + // { + // s_led_state = LED_OCPP_CONNECTED; + // } + // } + // else + // { + // if (s_led_state != LED_WIFI_CONNECTED) + // { + // s_led_state = LED_WIFI_CONNECTED; + // } + // } + // } updateLED(); diff --git a/hardware/firmware/src/smartconfig.cpp b/hardware/firmware/src/smartconfig.cpp new file mode 100644 index 0000000..c26008d --- /dev/null +++ b/hardware/firmware/src/smartconfig.cpp @@ -0,0 +1,152 @@ +#include "smartconfig.h" + +#include +#include + +WebServer server(80); +DNSServer dnsServer; +Preferences preferences; + +IPAddress apIP(192, 168, 4, 1); + +bool should_reboot = false; +bool smartconfig_done = false; + +const char *HTML_FORM = R"rawliteral( + + + + + + Helios Charge Point 配网 + + + +
+

配置 WiFi

+
+ + + + + +
+
+ + +)rawliteral"; + +void handleCaptivePortal() +{ + String host = server.hostHeader(); + server.sendHeader("Location", String("http://") + apIP.toString(), true); + server.send(302, "text/plain", ""); +} + +void handleRoot() +{ + server.send(200, "text/html", HTML_FORM); +} + +void handleSave() +{ + if (server.hasArg("ssid") && server.hasArg("password")) + { + String ssid = server.arg("ssid"); + String password = server.arg("password"); + + Serial.printf("Received SSID: %s, PASSWORD: %s\n", ssid.c_str(), password.c_str()); + + // save to NVS + preferences.begin("wifi", false); + preferences.putString("ssid", ssid); + preferences.putString("password", password); + preferences.end(); + + String response = "

配置已保存,设备正在尝试连接网络...

"; + server.send(200, "text/html", response); + + // flag to reboot + should_reboot = true; + } + else + { + server.send(400, "text/plain", "缺少 SSID 或密码"); + } +} + +void startSmartConfig(char *ssid, char *psk) +{ + WiFi.mode(WIFI_AP); + WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); + WiFi.softAP(ssid, psk); + + dnsServer.start(53, "*", apIP); + + server.on("/", handleRoot); + server.on("/save", handleSave); + + server.on("/generate_204", HTTP_GET, handleCaptivePortal); // Android + server.on("/ncsi.txt", HTTP_GET, handleCaptivePortal); // Windows + server.on("/hotspot-detect.html", HTTP_GET, handleCaptivePortal); // iOS + server.onNotFound(handleCaptivePortal); + + server.begin(); + Serial.println("HTTP server started"); +} + +bool connectToSavedWiFi() +{ + preferences.begin("wifi", true); + String ssid = preferences.getString("ssid", ""); + String password = preferences.getString("password", ""); + preferences.end(); + + if (ssid == "") + { + Serial.println("No saved WiFi credentials"); + return false; + } + + Serial.printf("Connecting to SSID: %s\n", ssid.c_str()); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid.c_str(), password.c_str()); + + unsigned long start = millis(); + const unsigned long timeout = 15000; // 15 secs + + while (WiFi.status() != WL_CONNECTED && millis() - start < timeout) + { + Serial.print("."); + delay(500); + } + + if (WiFi.status() == WL_CONNECTED) + { + Serial.println("\nWiFi connected!"); + Serial.printf("IP address: %s\n", WiFi.localIP().toString().c_str()); + smartconfig_done = true; + return true; + } + else + { + Serial.println("\nWiFi connect failed."); + return false; + } +} diff --git a/hardware/firmware/src/smartconfig.h b/hardware/firmware/src/smartconfig.h new file mode 100644 index 0000000..ddcaa79 --- /dev/null +++ b/hardware/firmware/src/smartconfig.h @@ -0,0 +1,16 @@ +#if !defined(SMARTCONFIG_H) +#define SMARTCONFIG_H + +#include +#include + +extern bool should_reboot; +extern WebServer server; +extern DNSServer dnsServer; + +extern bool smartconfig_done; + +void startSmartConfig(char *ssid, char *psk); +bool connectToSavedWiFi(); + +#endif // SMARTCONFIG_H