feat(firmware): add Mongoose client implementation for OCPP communication

This commit is contained in:
2026-03-17 01:25:41 +08:00
parent e1fb43d57b
commit 8371b2a76b
8 changed files with 1168 additions and 12 deletions

View File

@@ -4,12 +4,12 @@
// For production: ws://csms.helios.bh8.ga:8180/steve/websocket/CentralSystemService
#define CFG_OCPP_BACKEND "wss://csms-server.uniiem.com/ocpp"
// #define CFG_CP_IDENTIFIER "CQWU_HHB_0001"
#define CFG_CP_IDENTIFIER "ICP_906A28"
#define CFG_CP_IDENTIFIER ""
#define CFG_CB_SERIAL "REDAone_prototype00"
#define CFG_CP_FW_VERSION "1.0.0"
#define CFG_CP_MODAL "Helios DA One"
#define CFG_CP_VENDOR "RayineElec"
// OCPP Security Profile 1: Basic Auth password (username = chargePointIdentifier)
// OCPP Security Profile 1: sent as Authorization: Basic base64(<chargePointIdentifier>:<password>)
// Set to nullptr to disable authentication
// #define CFG_OCPP_PASSWORD "my_password"
#define CFG_OCPP_PASSWORD nullptr

View File

@@ -145,8 +145,16 @@ void setup()
"%02X%02X%02X%02X%02X%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
// Auto-generate Charge Point Identifier based on MAC (e.g. HLCP_A1B2C3)
snprintf(cp_identifier, sizeof(cp_identifier), "HLCP_%s", cpSerial + 6);
if (strlen(CFG_CP_IDENTIFIER) > 0)
{
strncpy(cp_identifier, CFG_CP_IDENTIFIER, sizeof(cp_identifier) - 1);
cp_identifier[sizeof(cp_identifier) - 1] = '\0';
}
else
{
// Auto-generate Charge Point Identifier based on MAC (e.g. HLCP_A1B2C3)
snprintf(cp_identifier, sizeof(cp_identifier), "HLCP_%s", cpSerial + 6);
}
// reset LED
leds[0] = Rgb{0, 0, 0};
@@ -155,6 +163,7 @@ void setup()
Serial.begin(115200);
delay(1000);
Serial.printf("\n\n%s(%s) made by %s\n", CFG_CP_MODAL, cpSerial, CFG_CP_VENDOR);
Serial.printf("Charge Point Identifier: %s\n", cp_identifier);
Serial.println("Initializing firmware...\n");
// Initialize LED
@@ -174,8 +183,10 @@ void setup()
Serial.printf("\n[OCPP] Loaded Backend URL: %s\n", b.c_str());
Serial.printf("[OCPP] Loaded Password length: %d\n", p.length());
strncpy(ocpp_backend, b.c_str(), sizeof(ocpp_backend));
strncpy(ocpp_password, p.c_str(), sizeof(ocpp_password));
strncpy(ocpp_backend, b.c_str(), sizeof(ocpp_backend) - 1);
ocpp_backend[sizeof(ocpp_backend) - 1] = '\0';
strncpy(ocpp_password, p.c_str(), sizeof(ocpp_password) - 1);
ocpp_password[sizeof(ocpp_password) - 1] = '\0';
WiFiManager wm;
wm.setSaveConfigCallback(saveConfigCallback);
@@ -184,7 +195,7 @@ void setup()
// Use autocomplete=off to prevent browsers from autofilling old URLs after a reset
WiFiManagerParameter custom_ocpp_backend("backend", "OCPP Backend URL", ocpp_backend, 128, "autocomplete=\"off\"");
WiFiManagerParameter custom_ocpp_password("ocpp_password", "OCPP 连接密码 (Basic Auth, 没有请留空)", ocpp_password, 64, "autocomplete=\"off\" type=\"password\"");
WiFiManagerParameter custom_ocpp_password("ocpp_password", "OCPP Basic Auth 密码 (Authorization 头, 没有请留空)", ocpp_password, 64, "autocomplete=\"off\" type=\"password\"");
wm.addParameter(&custom_ocpp_backend);
wm.addParameter(&custom_ocpp_password);
@@ -334,8 +345,10 @@ void setup()
if (shouldSaveConfig)
{
strncpy(ocpp_backend, custom_ocpp_backend.getValue(), sizeof(ocpp_backend));
strncpy(ocpp_password, custom_ocpp_password.getValue(), sizeof(ocpp_password));
strncpy(ocpp_backend, custom_ocpp_backend.getValue(), sizeof(ocpp_backend) - 1);
ocpp_backend[sizeof(ocpp_backend) - 1] = '\0';
strncpy(ocpp_password, custom_ocpp_password.getValue(), sizeof(ocpp_password) - 1);
ocpp_password[sizeof(ocpp_password) - 1] = '\0';
preferences.putString("backend", ocpp_backend);
preferences.putString("ocpp_password", ocpp_password);
@@ -354,8 +367,40 @@ void setup()
s_led_state = LED_WIFI_CONNECTED;
mg_mgr_init(&mgr);
const char *final_ocpp_password = (strlen(ocpp_password) > 0) ? ocpp_password : nullptr;
MicroOcpp::MOcppMongooseClient *client = new MicroOcpp::MOcppMongooseClient(&mgr, ocpp_backend, cp_identifier, final_ocpp_password, "", MicroOcpp::makeDefaultFilesystemAdapter(MicroOcpp::FilesystemOpt::Use_Mount_FormatOnFail), MicroOcpp::ProtocolVersion(1, 6));
const char *basic_auth_password = (strlen(ocpp_password) > 0) ? ocpp_password : nullptr;
unsigned char *basic_auth_password_bytes = nullptr;
size_t basic_auth_password_len = 0;
if (basic_auth_password)
{
basic_auth_password_bytes = reinterpret_cast<unsigned char *>(const_cast<char *>(basic_auth_password));
basic_auth_password_len = strlen(basic_auth_password);
}
MicroOcpp::MOcppMongooseClient *client = new MicroOcpp::MOcppMongooseClient(
&mgr,
ocpp_backend,
cp_identifier,
nullptr,
0,
"",
MicroOcpp::makeDefaultFilesystemAdapter(MicroOcpp::FilesystemOpt::Use_Mount_FormatOnFail),
MicroOcpp::ProtocolVersion(1, 6));
// Preferences and firmware config are the source of truth. Override any stale
// values cached in MicroOcpp's ws-conn storage before the first reconnect cycle.
client->setBackendUrl(ocpp_backend);
client->setChargeBoxId(cp_identifier);
if (basic_auth_password_bytes)
{
client->setAuthKey(basic_auth_password_bytes, basic_auth_password_len);
}
else
{
client->setAuthKey(reinterpret_cast<const unsigned char *>(""), 0);
}
client->reloadConfigs();
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)