feat(main): 添加OCPP配置的持久化支持和LED状态管理功能

This commit is contained in:
2026-03-16 00:52:17 +08:00
parent 2de43d5fbb
commit 37c5cfe5a9

View File

@@ -1,5 +1,6 @@
#include <Arduino.h>
#include <WiFiManager.h>
#include <Preferences.h>
#include <string.h>
#include <MicroOcpp.h>
@@ -18,7 +19,9 @@ enum LEDState
LED_INITIALIZING, // Blue blinking - Initialization and WiFi connecting
LED_WIFI_CONNECTED, // Blue solid - WiFi connected, connecting to OCPP server
LED_OCPP_CONNECTED, // Green solid - Successfully connected to OCPP server
LED_ERROR // Red - Error state
LED_ERROR, // Red solid - Error state
LED_RESET_TX, // Yellow solid - 3s BOOT button hold (Ready to clear transaction)
LED_FACTORY_RESET // Magenta fast blink - 7s BOOT button hold (Ready to factory reset)
};
static int s_retry_num = 0;
@@ -31,6 +34,19 @@ static const unsigned long BLINK_INTERVAL = 200; // 200ms blink interval
uint8_t mac[6];
char cpSerial[13];
// OCPP Configuration Variables
char ocpp_backend[128];
char cp_identifier[64];
char auth_key[64];
bool shouldSaveConfig = false;
// callback notifying us of the need to save config
void saveConfigCallback()
{
Serial.println("Should save config");
shouldSaveConfig = true;
}
struct mg_mgr mgr;
/**
@@ -86,6 +102,38 @@ void updateLED()
leds[0] = Rgb{255, 0, 0}; // Red solid
leds.show();
break;
case LED_RESET_TX:
// Yellow fast blink - Ready to clear transaction
if (current_time - s_blink_last_time >= 100)
{
s_blink_last_time = current_time;
s_blink_on = !s_blink_on;
if (s_blink_on)
leds[0] = Rgb{150, 150, 0}; // Yellow
else
leds[0] = Rgb{0, 0, 0};
leds.show();
}
break;
case LED_FACTORY_RESET:
// Magenta fast blink - Ready to factory reset
if (current_time - s_blink_last_time >= 100)
{
s_blink_last_time = current_time;
s_blink_on = !s_blink_on;
if (s_blink_on)
leds[0] = Rgb{255, 0, 255}; // Magenta
else
leds[0] = Rgb{0, 0, 0};
leds.show();
}
break;
}
}
@@ -113,7 +161,30 @@ void setup()
leds[0] = Rgb{255, 255, 0};
leds.show();
// Load configuration from Preferences
Preferences preferences;
preferences.begin("ocpp-config", false);
String b = preferences.getString("backend", CFG_OCPP_BACKEND);
String i = preferences.getString("identifier", CFG_CP_IDENTIFIER);
String a = preferences.getString("auth_key", "");
strncpy(ocpp_backend, b.c_str(), sizeof(ocpp_backend));
strncpy(cp_identifier, i.c_str(), sizeof(cp_identifier));
strncpy(auth_key, a.c_str(), sizeof(auth_key));
WiFiManager wm;
wm.setSaveConfigCallback(saveConfigCallback);
wm.setSaveParamsCallback(saveConfigCallback);
wm.setParamsPage(true);
WiFiManagerParameter custom_ocpp_backend("backend", "OCPP Backend URL", ocpp_backend, 128);
WiFiManagerParameter custom_cp_identifier("identifier", "Charge Point ID", cp_identifier, 64);
WiFiManagerParameter custom_auth_key("auth_key", "Auth Key (leave blank if none)", auth_key, 64);
wm.addParameter(&custom_ocpp_backend);
wm.addParameter(&custom_cp_identifier);
wm.addParameter(&custom_auth_key);
const char *customHeadElement = R"rawliteral(
<style>
:root {
@@ -256,6 +327,20 @@ void setup()
)rawliteral";
wm.setCustomHeadElement(customHeadElement);
bool autoConnectRet = wm.autoConnect((String("HLCP_") + String(cpSerial).substring(String(cpSerial).length() - 6)).c_str(), cpSerial);
if (shouldSaveConfig)
{
strncpy(ocpp_backend, custom_ocpp_backend.getValue(), sizeof(ocpp_backend));
strncpy(cp_identifier, custom_cp_identifier.getValue(), sizeof(cp_identifier));
strncpy(auth_key, custom_auth_key.getValue(), sizeof(auth_key));
preferences.putString("backend", ocpp_backend);
preferences.putString("identifier", cp_identifier);
preferences.putString("auth_key", auth_key);
Serial.println("Saved new OCPP config to Preferences");
}
preferences.end();
if (!autoConnectRet)
{
Serial.println("Failed to connect and hit timeout");
@@ -267,8 +352,20 @@ void setup()
s_led_state = LED_WIFI_CONNECTED;
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));
const char *final_auth_key = (strlen(auth_key) > 0) ? auth_key : nullptr;
MicroOcpp::MOcppMongooseClient *client = new MicroOcpp::MOcppMongooseClient(&mgr, ocpp_backend, cp_identifier, final_auth_key, "", MicroOcpp::makeDefaultFilesystemAdapter(MicroOcpp::FilesystemOpt::Use_Mount_FormatOnFail), MicroOcpp::ProtocolVersion(1, 6));
mocpp_initialize(*client, ChargerCredentials(CFG_CP_MODAL, CFG_CP_VENDOR, CFG_CP_FW_VERSION, cpSerial, nullptr, nullptr, CFG_CB_SERIAL, nullptr, nullptr), MicroOcpp::makeDefaultFilesystemAdapter(MicroOcpp::FilesystemOpt::Use_Mount_FormatOnFail));
// For development/recovery: Set up BOOT button (GPIO 0)
pinMode(0, INPUT_PULLUP);
// Forcefully accept rejected RemoteStopTransaction (if hardware goes out of sync with CSMS)
setOnSendConf("RemoteStopTransaction", [](JsonObject payload)
{
if (!strcmp(payload["status"], "Rejected")) {
Serial.println("[main] MicroOcpp rejected RemoteStopTransaction! Force overriding and stopping charging...");
endTransaction(nullptr, "Remote", 1);
} });
}
}
@@ -277,19 +374,80 @@ void loop()
mg_mgr_poll(&mgr, 10);
mocpp_loop();
auto ctx = getOcppContext();
if (ctx && ctx->getConnection().isConnected())
// Handle BOOT button (GPIO 0) interactions for recovery
bool is_btn_pressed = (digitalRead(0) == LOW);
static unsigned long boot_press_time = 0;
static bool boot_was_pressed = false;
if (is_btn_pressed)
{
if (s_led_state != LED_OCPP_CONNECTED)
if (!boot_was_pressed)
{
s_led_state = LED_OCPP_CONNECTED;
boot_was_pressed = true;
boot_press_time = millis();
}
unsigned long held_time = millis() - boot_press_time;
if (held_time >= 7000)
{
s_led_state = LED_FACTORY_RESET;
}
else if (held_time >= 3000)
{
s_led_state = LED_RESET_TX;
}
}
else
{
if (s_led_state != LED_WIFI_CONNECTED)
if (boot_was_pressed)
{
s_led_state = LED_WIFI_CONNECTED;
unsigned long held_time = millis() - boot_press_time;
if (held_time >= 7000)
{
Serial.println("BOOT button held for > 7s! Clearing WiFi and OCPP settings, then restarting...");
// Clear WiFi
WiFiManager wm;
wm.resetSettings();
// Clear Preferences
Preferences preferences;
preferences.begin("ocpp-config", false);
preferences.clear();
preferences.end();
// Give time for serial to print
delay(500);
ESP.restart();
}
else if (held_time >= 3000)
{
Serial.println("BOOT button held for > 3s! Forcefully ending dangling transaction...");
endTransaction(nullptr, "PowerLoss", 1);
}
boot_was_pressed = false;
// Temporarily set to init so the logic below restores the actual network state accurately
s_led_state = LED_INITIALIZING;
}
}
// Only update default LED states if button is not overriding them
if (!is_btn_pressed)
{
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;
}
}
}