From 5ca9d3d88f79881f26054c33d3b9a7ef5e56e25d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 27 Jun 2023 18:44:31 +0200 Subject: [PATCH 01/10] rework lifecycle mgmt, config sync and issues --- platformio.ini | 4 +- src/ocpp.cpp | 413 ++++++++++++++++++++----------------------------- src/ocpp.h | 16 +- 3 files changed, 176 insertions(+), 257 deletions(-) diff --git a/platformio.ini b/platformio.ini index 3953cd58..af05b2ee 100644 --- a/platformio.ini +++ b/platformio.ini @@ -38,8 +38,8 @@ lib_deps = jeremypoulter/ESPAL@0.0.3 jeremypoulter/StreamSpy@0.0.1 jeremypoulter/MicroTasks@0.0.3 - matth-x/ArduinoOcpp@0.2.0 - matth-x/ArduinoOcppMongoose@0.0.1 + https://github.com/matth-x/ArduinoOcpp#develop + https://github.com/matth-x/ArduinoOcppMongoose lib_ignore = WebSockets ; ArduinoOcpp: don't compile built-in WS library extra_scripts = pre:scripts/auto_fw_version.py diff --git a/src/ocpp.cpp b/src/ocpp.cpp index b0b045c6..24f73369 100644 --- a/src/ocpp.cpp +++ b/src/ocpp.cpp @@ -6,9 +6,6 @@ #include "ocpp.h" #include -#include -#include -#include #include #include "app_config.h" @@ -50,91 +47,89 @@ void ArduinoOcppTask::begin(EvseManager &evse, LcdTask &lcd, EventLog &eventLog, void ArduinoOcppTask::reconfigure() { - if (arduinoOcppInitialized) { - arduinoOcppInitialized = false; - bootNotificationAccepted = false; + if (getOcppContext() && !config_ocpp_enabled()) { + //library initialized but now disabled + deinitializeArduinoOcpp(); + return; + } - if (!config_ocpp_enabled() && ocppSocket) { - delete ocppSocket; - ocppSocket = nullptr; - } - OCPP_deinitialize(); + if (!getOcppContext() && config_ocpp_enabled()) { + //library not initialized yet but enabled via config (e.g. during system startup) + initializeArduinoOcpp(); + return; } - if (config_ocpp_enabled()) { - if (ocppSocket) { - //update URL storage with credentials from UI - ocppSocket->setBackendUrl(ocpp_server.c_str()); - ocppSocket->setChargeBoxId(ocpp_chargeBoxId.c_str()); - ocppSocket->setAuthKey(ocpp_authkey.c_str()); - ocppSocket->reconnect(); - } else { - ocppSocket = new ArduinoOcpp::AOcppMongooseClient(Mongoose.getMgr(), - ocpp_server.c_str(), //fallback URL. Normally, OcppSocket loads URL from own store - ocpp_chargeBoxId.c_str(), - ocpp_authkey.c_str(), - root_ca, //defined in root_ca.cpp - ArduinoOcpp::makeDefaultFilesystemAdapter(ArduinoOcpp::FilesystemOpt::Use)); - - //override values in UI with URL storage. Unfortunately, editing URL in UI and enabling OCPP at the same time - //is not possible - bool updated = !ocpp_server.equals(ocppSocket->getBackendUrl()) || - !ocpp_chargeBoxId.equals(ocppSocket->getChargeBoxId()) || - !ocpp_authkey.equals(ocppSocket->getAuthKey()); - - if (updated) { - DynamicJsonDocument updateQuery (JSON_OBJECT_SIZE(3)); //use JSON in no-copy mode - updateQuery["ocpp_server"] = ocppSocket->getBackendUrl(); - updateQuery["ocpp_chargeBoxId"] = ocppSocket->getChargeBoxId(); - updateQuery["ocpp_authkey"] = ocppSocket->getAuthKey(); - config_deserialize(updateQuery); - config_commit(); - } + if (getOcppContext() && config_ocpp_enabled()) { + //library already running. Apply changed settings + ocppSocket->setBackendUrl(ocpp_server.c_str()); + ocppSocket->setChargeBoxId(ocpp_chargeBoxId.c_str()); + ocppSocket->setAuthKey(ocpp_authkey.c_str()); + ocppSocket->reconnect(); + + *freevendActive = config_ocpp_auto_authorization(); + *freevendIdTag = ocpp_idtag.c_str(); + *allowOfflineTxForUnknownId = config_ocpp_offline_authorization(); + if (config_ocpp_auto_authorization()) { + *silentOfflineTx = true; //recommended to disable transaction journaling when being offline in Freevend mode } - initializeArduinoOcpp(); - - arduinoOcppInitialized = true; + ArduinoOcpp::configuration_save(); } } void ArduinoOcppTask::initializeArduinoOcpp() { - OCPP_initialize(*ocppSocket, (float) VOLTAGE_DEFAULT, ArduinoOcpp::FilesystemOpt::Use); + auto filesystem = ArduinoOcpp::makeDefaultFilesystemAdapter(ArduinoOcpp::FilesystemOpt::Use); + + ocppSocket = new ArduinoOcpp::AOcppMongooseClient(Mongoose.getMgr(), + ocpp_server.c_str(), //factory default URL. Normally, OcppSocket loads URL from own store + ocpp_chargeBoxId.c_str(), //factory default + ocpp_authkey.c_str(), //factory default + root_ca, //defined in root_ca.cpp + filesystem); + + /* + * Load OCPP configs and apply factory defaults for OpenEVSE + */ + ArduinoOcpp::configuration_init(filesystem); + freevendActive = ArduinoOcpp::declareConfiguration("AO_FreeVendActive", true, CONFIGURATION_FN, true, true, true, true); + freevendIdTag = ArduinoOcpp::declareConfiguration("AO_FreeVendIdTag", "DefaultIdTag", CONFIGURATION_FN, true, true, true, true); + allowOfflineTxForUnknownId = ArduinoOcpp::declareConfiguration("AllowOfflineTxForUnknownId", false, CONFIGURATION_FN, true, true, true, true); + silentOfflineTx = ArduinoOcpp::declareConfiguration("AO_SilentOfflineTransactions", true, CONFIGURATION_FN, true, true, true, true); + ArduinoOcpp::declareConfiguration("MeterValuesSampledData", "Power.Active.Import,Energy.Active.Import.Register,Current.Import,Current.Offered,Voltage,Temperature", CONFIGURATION_FN); + ArduinoOcpp::declareConfiguration("AO_PreBootTransactions", true, CONFIGURATION_FN, true, true, true, true); + /* + * Initialize the OCPP library and provide it with the charger credentials + */ + OCPP_initialize(*ocppSocket, ChargerCredentials( + "Advanced Series", //chargePointModel + "OpenEVSE", //chargePointVendor + currentfirmware.c_str(), //firmwareVersion + serial.c_str(), //chargePointSerialNumber + evse->getFirmwareVersion() //meterSerialNumber + ), ArduinoOcpp::FilesystemOpt::Use); + + //override values in UI with stored values + DynamicJsonDocument updateQuery (JSON_OBJECT_SIZE(6)); + updateQuery["ocpp_server"] = ocppSocket->getBackendUrl(); + updateQuery["ocpp_chargeBoxId"] = ocppSocket->getChargeBoxId(); + updateQuery["ocpp_authkey"] = ocppSocket->getAuthKey(); + updateQuery["ocpp_auth_auto"] = *freevendActive ? 1 : 0; + updateQuery["ocpp_idtag"] = (const char*) *freevendIdTag; + updateQuery["ocpp_auth_offline"] = *allowOfflineTxForUnknownId ? 1 : 0; + config_deserialize(updateQuery); + config_commit(); loadEvseBehavior(); initializeDiagnosticsService(); initializeFwService(); +} - /* - * BootNotification: provide the OCPP backend with relevant data about the OpenEVSE - * see https://github.com/OpenEVSE/ESP32_WiFi_V4.x/issues/219 - */ - String evseFirmwareVersion = String(evse->getFirmwareVersion()); - - auto evseDetailsDoc = std::unique_ptr(new DynamicJsonDocument( - JSON_OBJECT_SIZE(5) - + serial.length() + 1 - + currentfirmware.length() + 1 - + evseFirmwareVersion.length() + 1)); - JsonObject evseDetails = evseDetailsDoc->to(); - evseDetails["chargePointModel"] = "Advanced Series"; - evseDetails["chargePointSerialNumber"] = serial; //see https://github.com/OpenEVSE/ESP32_WiFi_V4.x/issues/218 - evseDetails["chargePointVendor"] = "OpenEVSE"; - evseDetails["firmwareVersion"] = currentfirmware; - evseDetails["meterSerialNumber"] = evseFirmwareVersion; - - bootNotification(std::move(evseDetailsDoc), [this](JsonObject response) { //ArduinoOcpp will delete evseDetailsDoc - if (response["status"].as().equals("Accepted")) { - LCD_DISPLAY("OCPP connected!"); - bootNotificationAccepted = true; - } else { - LCD_DISPLAY("OCPP refused EVSE"); - } - }); - - ocppTxIdDisplay = getTransactionId(); - ocppSessionDisplay = getTransactionIdTag(); +void ArduinoOcppTask::deinitializeArduinoOcpp() { + rfid->setOnCardScanned(nullptr); + OCPP_deinitialize(); + delete ocppSocket; + ocppSocket = nullptr; } void ArduinoOcppTask::setup() { @@ -167,7 +162,7 @@ void ArduinoOcppTask::loadEvseBehavior() { "A"); addMeterValueInput([this] () { - return (int32_t) charging_limit; + return (int32_t) evse->getChargeCurrent(); }, "Current.Offered", "A"); @@ -184,23 +179,28 @@ void ArduinoOcppTask::loadEvseBehavior() { "Temperature", "C"); - auto patchChargingProfileUnit = ArduinoOcpp::declareConfiguration("OE_CSProfileUnitMode", "W", CONFIGURATION_FN, false, false); - - setSmartChargingOutput([this, patchChargingProfileUnit] (float limit) { //limit = maximum charge rate in Watts - if (patchChargingProfileUnit && - ((*patchChargingProfileUnit)[0] == 'a' || (*patchChargingProfileUnit)[0] == 'A')) { - charging_limit = limit; //already A + setSmartChargingOutput([this] (float power, float current, int nphases) { + if (power >= 0.f && current >= 0.f) { + //both defined, take smaller value + charging_limit = std::min(power / VOLTAGE_DEFAULT, current); + } else if (current >= 0.f) { + //current defined + charging_limit = current; + } else if (power >= 0.f) { + //power defined + charging_limit = power / (float) VOLTAGE_DEFAULT; } else { - charging_limit = limit / VOLTAGE_DEFAULT; //convert W to A + //Smart charging disabled / limit undefined + charging_limit = -1.f; } }); setConnectorPluggedInput([this] () { - return (bool) evse->isVehicleConnected(); + return evse->isVehicleConnected(); }); setEvReadyInput([this] () { - return (bool) evse->isCharging(); + return evse->isCharging(); }); setEvseReadyInput([this] () { @@ -211,78 +211,58 @@ void ArduinoOcppTask::loadEvseBehavior() { * Report failures to central system. Note that the error codes are standardized in OCPP */ - addErrorCodeInput([this] () { - if (evse->getEvseState() == OPENEVSE_STATE_GFI_FAULT || - evse->getEvseState() == OPENEVSE_STATE_GFI_SELF_TEST_FAILED || - evse->getEvseState() == OPENEVSE_STATE_NO_EARTH_GROUND || - evse->getEvseState() == OPENEVSE_STATE_DIODE_CHECK_FAILED) { - return "GroundFailure"; - } - return (const char *) NULL; - }); - - addErrorCodeInput([this] () { + addErrorCodeInput([this] () -> ChargePointErrorCode { if (evse->getEvseState() == OPENEVSE_STATE_OVER_TEMPERATURE) { - return "HighTemperature"; + return ChargePointErrorCode::HighTemperature; } - return (const char *) NULL; + return ChargePointErrorCode::NoError; }); - addErrorCodeInput([this] () { + addErrorCodeInput([this] () -> ChargePointErrorCode { if (evse->getEvseState() == OPENEVSE_STATE_OVER_CURRENT) { - return "OverCurrentFailure"; + return ChargePointErrorCode::OverCurrentFailure; } - return (const char *) NULL; + return ChargePointErrorCode::NoError; }); - addErrorCodeInput([this] () { + addErrorCodeInput([this] () -> ChargePointErrorCode { if (evse->getEvseState() == OPENEVSE_STATE_STUCK_RELAY) { - return "PowerSwitchFailure"; + return ChargePointErrorCode::PowerSwitchFailure; } - return (const char *) NULL; + return ChargePointErrorCode::NoError; }); - addErrorCodeInput([this] () { + addErrorCodeInput([this] () -> ChargePointErrorCode { if (rfid->communicationFails()) { - return "ReaderFailure"; + return ChargePointErrorCode::ReaderFailure; } - return (const char *) nullptr; + return ChargePointErrorCode::NoError; }); - /* - * CP behavior definition: How will plugging and unplugging the EV start or stop OCPP transactions - */ - - freevendActive = ArduinoOcpp::declareConfiguration("AO_FreeVendActive", false, CONFIGURATION_FN); - freevendIdTag = ArduinoOcpp::declareConfiguration("AO_FreeVendIdTag", "", CONFIGURATION_FN); - allowOfflineTxForUnknownId = ArduinoOcpp::declareConfiguration("AllowOfflineTxForUnknownId", false, CONFIGURATION_FN); - - if (!*freevendActive && config_ocpp_auto_authorization()) { - //recommended to stop capturing transactions when being offline in Freevend mode - auto silentOfflineTx = ArduinoOcpp::declareConfiguration("AO_SilentOfflineTransactions", false, CONFIGURATION_FN); - *silentOfflineTx = true; - } - - *freevendActive = config_ocpp_auto_authorization(); - *freevendIdTag = ocpp_idtag.c_str(); - *allowOfflineTxForUnknownId = config_ocpp_offline_authorization(); + addErrorDataInput([this] () -> ArduinoOcpp::ErrorData { + if (evse->getEvseState() == OPENEVSE_STATE_DIODE_CHECK_FAILED || + evse->getEvseState() == OPENEVSE_STATE_GFI_FAULT || + evse->getEvseState() == OPENEVSE_STATE_NO_EARTH_GROUND || + evse->getEvseState() == OPENEVSE_STATE_GFI_SELF_TEST_FAILED) { + + ArduinoOcpp::ErrorData error = ChargePointErrorCode::GroundFailure; - ArduinoOcpp::configuration_save(); - - trackConfigRevision = freevendActive->getValueRevision() + - freevendIdTag->getValueRevision() + - allowOfflineTxForUnknownId->getValueRevision(); + error.info = evse->getEvseState() == OPENEVSE_STATE_DIODE_CHECK_FAILED ? "diode check failed" : + evse->getEvseState() == OPENEVSE_STATE_GFI_FAULT ? "GFI fault" : + evse->getEvseState() == OPENEVSE_STATE_NO_EARTH_GROUND ? "no earth / ground" : + evse->getEvseState() == OPENEVSE_STATE_GFI_SELF_TEST_FAILED ? "GFI self test failed" : nullptr; + return error; + } + return ChargePointErrorCode::NoError; + }); onIdTagInput = [this] (const String& idInput) { - if (!config_ocpp_enabled()) { - return false; - } if (idInput.isEmpty()) { DBUGLN("[ocpp] empty idTag"); return true; } - if (!isOperative() || !arduinoOcppInitialized) { - LCD_DISPLAY("OCPP inoperative"); + if (!isOperative()) { + LCD_DISPLAY("Out of service"); DBUGLN(F("[ocpp] present card but inoperative")); return true; } @@ -299,22 +279,7 @@ void ArduinoOcppTask::loadEvseBehavior() { } else { //idle mode LCD_DISPLAY("Card read"); - String idInputCapture = idInput; - authorize(idInput.c_str(), [this, idInputCapture] (JsonObject payload) { - if (idTagIsAccepted(payload)) { - beginTransaction(idInputCapture.c_str()); - LCD_DISPLAY("Card accepted"); - } else { - LCD_DISPLAY("Card unknown"); - } - }, nullptr, [this, idInputCapture] () { - if (*allowOfflineTxForUnknownId) { - LCD_DISPLAY("Offline mode"); - beginTransaction(idInputCapture.c_str()); - } else { - LCD_DISPLAY("OCPP timeout"); - } - }); + beginTransaction(idInput.c_str()); } return true; @@ -324,114 +289,85 @@ void ArduinoOcppTask::loadEvseBehavior() { setOnResetExecute([this] (bool resetHard) { if (resetHard) { - //TODO send reset command to all peripherals - //see https://github.com/OpenEVSE/ESP32_WiFi_V4.x/issues/228 + evse->restartEvse(); //hard reset applies to EVSE module and ESP32 } restart_system(); }); - setOnUnlockConnectorInOut([] () { - //TODO Send unlock command to peripherals. If successful, return true, otherwise false - //see https://github.com/OpenEVSE/ESP32_WiFi_V4.x/issues/230 - return false; - }); - - setOnSetChargingProfileRequest([this, patchChargingProfileUnit] (JsonObject request) { - const char *unit = request["csChargingProfiles"]["chargingSchedule"]["chargingRateUnit"] | "W"; - if (unit && (unit[0] == 'A' || unit[0] == 'a')) { - *patchChargingProfileUnit = "A"; - DBUGLN("[ocpp] ChargingRateUnit from now on A"); - } else { - *patchChargingProfileUnit = "W"; - DBUGLN("[ocpp] ChargingRateUnit from now on W"); + /* + * Give the user feedback about the status of the OCPP transaction + */ + setTxNotificationOutput([this] (ArduinoOcpp::TxNotification notification, ArduinoOcpp::Transaction*) { + switch (notification) { + case ArduinoOcpp::TxNotification::AuthorizationRejected: + LCD_DISPLAY("Card unkown"); + break; + case ArduinoOcpp::TxNotification::AuthorizationTimeout: + LCD_DISPLAY("Server timeout"); + break; + case ArduinoOcpp::TxNotification::Authorized: + LCD_DISPLAY("Card accepted"); + break; + case ArduinoOcpp::TxNotification::ConnectionTimeout: + LCD_DISPLAY("Aborted / no EV"); + break; + case ArduinoOcpp::TxNotification::DeAuthorized: + LCD_DISPLAY("Card unkown"); + break; + case ArduinoOcpp::TxNotification::RemoteStart: + if (!evse->isVehicleConnected()) { + LCD_DISPLAY("Plug in cable"); + } + break; + case ArduinoOcpp::TxNotification::ReservationConflict: + LCD_DISPLAY("EVSE reserved"); + break; + case ArduinoOcpp::TxNotification::StartTx: + LCD_DISPLAY("Tx started"); + break; + case ArduinoOcpp::TxNotification::StopTx: + LCD_DISPLAY("Tx stopped"); + break; + default: + break; } }); } unsigned long ArduinoOcppTask::loop(MicroTasks::WakeReason reason) { - if (arduinoOcppInitialized) { - OCPP_loop(); - } + if (getOcppContext()) { + //ArduinoOcpp is initialized - if (arduinoOcppInitialized) { + OCPP_loop(); /* * Generate messages for LCD */ - if (evse->isVehicleConnected() && !vehicleConnected) { + if (evse->isVehicleConnected() && !trackVehicleConnected) { //vehicle plugged - if (!getTransactionIdTag()) { + if (!isOperative()) { + LCD_DISPLAY("No OCPP service"); + } else if (!isTransactionActive()) { //vehicle plugged before authorization if (config_rfid_enabled()) { LCD_DISPLAY("Need card"); } else if (!config_ocpp_auto_authorization()) { //wait for RemoteStartTransaction - LCD_DISPLAY("Need authorization"); + LCD_DISPLAY("Wait for app"); } //if auto-authorize is on, transaction starts without further user interaction } } - vehicleConnected = evse->isVehicleConnected(); - - if (ocppSessionDisplay && !getTransactionIdTag()) { - //Session unauthorized. Show if StartTransaction didn't succeed - if (ocppTxIdDisplay < 0) { - if (config_rfid_enabled()) { - LCD_DISPLAY("Card timeout"); - LCD_DISPLAY("Present card again"); - } else { - LCD_DISPLAY("Auth timeout"); - } - } - } else if (!ocppSessionDisplay && getTransactionIdTag()) { - //Session recently authorized - if (!evse->isVehicleConnected()) { - LCD_DISPLAY("Plug in cable"); - } - } - ocppSessionDisplay = getTransactionIdTag(); - - if (ocppTxIdDisplay < 0 && getTransactionId() >= 0) { //tx started - LCD_DISPLAY("OCPP start tx"); - } - if (ocppTxIdDisplay <= 0 && getTransactionId() > 0) { //txId assigned - String txIdMsg = "TxID "; - txIdMsg += String(getTransactionId()); - LCD_DISPLAY(txIdMsg); - } - if (ocppTxIdDisplay >= 0 && getTransactionId() < 0) { //tx stopped - LCD_DISPLAY("OCPP stop tx"); - } - if (ocppTxIdDisplay > 0 && getTransactionId() < 0) { //stopped Tx had txId (not offline-only) - String txIdMsg = "TxID "; - txIdMsg += String(ocppTxIdDisplay); - txIdMsg += " end"; - LCD_DISPLAY(txIdMsg); - } - ocppTxIdDisplay = getTransactionId(); - - /* - * Synchronize OCPP config updates with OpenEVSE - */ + trackVehicleConnected = evse->isVehicleConnected(); - uint16_t configRev = freevendActive->getValueRevision() + - freevendIdTag->getValueRevision() + - allowOfflineTxForUnknownId->getValueRevision(); - - if (configRev != trackConfigRevision) { - DynamicJsonDocument updateQuery (JSON_OBJECT_SIZE(3)); //use JSON in no-copy mode - updateQuery["ocpp_auth_auto"] = *freevendActive ? 1 : 0; - updateQuery["ocpp_idtag"] = (const char*) *freevendIdTag; - updateQuery["ocpp_auth_offline"] = *allowOfflineTxForUnknownId ? 1 : 0; - config_deserialize(updateQuery); - config_commit(); - - trackConfigRevision = configRev; + if (isConnected() && !trackOcppConnected) { + LCD_DISPLAY("OCPP connected"); } + trackOcppConnected = isConnected(); } if (millis() - updateEvseClaimLast >= 1009) { @@ -439,7 +375,7 @@ unsigned long ArduinoOcppTask::loop(MicroTasks::WakeReason reason) { updateEvseClaim(); } - return arduinoOcppInitialized ? 0 : 1000; + return getOcppContext() ? 0 : 1000; } void ArduinoOcppTask::updateEvseClaim() { @@ -447,7 +383,7 @@ void ArduinoOcppTask::updateEvseClaim() { EvseState evseState; EvseProperties evseProperties; - if (!arduinoOcppInitialized || !config_ocpp_enabled()) { + if (!getOcppContext() || !config_ocpp_enabled()) { if (evse->clientHasClaim(EvseClient_OpenEVSE_OCPP)) { evse->release(EvseClient_OpenEVSE_OCPP); } @@ -474,11 +410,6 @@ void ArduinoOcppTask::updateEvseClaim() { evseProperties.setChargeCurrent(charging_limit); } - if (!bootNotificationAccepted) { - evseState = EvseState::Disabled; //override state - evseProperties = evseState; //renew properties - } - if (evseState == EvseState::Disabled && !config_ocpp_access_can_suspend()) { //OCPP is configured to never put the EVSE into sleep evseState = EvseState::None; @@ -522,7 +453,7 @@ void ArduinoOcppTask::notifyConfigChanged() { void ArduinoOcppTask::initializeDiagnosticsService() { ArduinoOcpp::DiagnosticsService *diagService = getDiagnosticsService(); if (diagService) { - diagService->setOnUploadStatusSampler([this] () { + diagService->setOnUploadStatusInput([this] () { if (diagFailure) { return ArduinoOcpp::UploadStatus::UploadFailed; } else if (diagSuccess) { @@ -532,7 +463,7 @@ void ArduinoOcppTask::initializeDiagnosticsService() { } }); - diagService->setOnUpload([this] (const std::string &location, ArduinoOcpp::OcppTimestamp &startTime, ArduinoOcpp::OcppTimestamp &stopTime) { + diagService->setOnUpload([this] (const std::string &location, ArduinoOcpp::Timestamp &startTime, ArduinoOcpp::Timestamp &stopTime) { //reset reported state diagSuccess = false; @@ -575,7 +506,7 @@ void ArduinoOcppTask::initializeDiagnosticsService() { eventLog->enumerate(index, [this, startTime, stopTime, &body, SUFFIX_RESERVED_AREA, &firstEntry, &overflow] (String time, EventType type, const String &logEntry, EvseState managerState, uint8_t evseState, uint32_t evseFlags, uint32_t pilot, double energy, uint32_t elapsed, double temperature, double temperatureMax, uint8_t divertMode, uint8_t shaper) { if (overflow) return; - ArduinoOcpp::OcppTimestamp timestamp = ArduinoOcpp::OcppTimestamp(); + ArduinoOcpp::Timestamp timestamp = ArduinoOcpp::Timestamp(); if (!timestamp.setTime(time.c_str())) { DBUG(F("[ocpp] Diagnostics upload, cannot parse timestamp format: ")); DBUGLN(time); @@ -642,9 +573,8 @@ void ArduinoOcppTask::initializeDiagnosticsService() { void ArduinoOcppTask::initializeFwService() { ArduinoOcpp::FirmwareService *fwService = getFirmwareService(); if (fwService) { - fwService->setBuildNumber(evse->getFirmwareVersion()); - - fwService->setInstallationStatusSampler([this] () { + + fwService->setInstallationStatusInput([this] () { if (updateFailure) { return ArduinoOcpp::InstallationStatus::InstallationFailed; } else if (updateSuccess) { @@ -682,8 +612,3 @@ bool ArduinoOcppTask::isConnected() { } return false; } - -bool ArduinoOcppTask::idTagIsAccepted(JsonObject payload) { - const char *status = payload["idTagInfo"]["status"] | "Invalid"; - return !strcmp(status, "Accepted"); -} diff --git a/src/ocpp.h b/src/ocpp.h index 10aced13..705d8100 100644 --- a/src/ocpp.h +++ b/src/ocpp.h @@ -25,11 +25,9 @@ class ArduinoOcppTask: public MicroTasks::Task { EventLog *eventLog; RfidTask *rfid; - float charging_limit = -1.f; //in Amps. chargingLimit < 0 means that there is no Smart Charging (and no restrictions ) - int ocppTxIdDisplay {-1}; - bool ocppSessionDisplay {false}; - - bool vehicleConnected = false; + float charging_limit = -1.f; //in Amps. charging_limit < 0 means that no charging limit is defined + bool trackOcppConnected = false; + bool trackVehicleConnected = false; std::function onIdTagInput {nullptr}; @@ -41,11 +39,9 @@ class ArduinoOcppTask: public MicroTasks::Task { void initializeFwService(); void initializeArduinoOcpp(); - bool arduinoOcppInitialized = false; + void deinitializeArduinoOcpp(); void loadEvseBehavior(); - bool bootNotificationAccepted = false; - ulong updateEvseClaimLast {0}; static ArduinoOcppTask *instance; @@ -53,10 +49,8 @@ class ArduinoOcppTask: public MicroTasks::Task { std::shared_ptr> freevendActive; //Authorize automatically std::shared_ptr> freevendIdTag; //idTag for auto-authorization std::shared_ptr> allowOfflineTxForUnknownId; //temporarily accept all NFC-cards while offline - uint16_t trackConfigRevision = 0; //track if OCPP configs have been updated + std::shared_ptr> silentOfflineTx; //stop transaction journaling in long offline periods - //helper functions - static bool idTagIsAccepted(JsonObject payload); protected: //hook method of MicroTask::Task From 27d2f3a1f030d87722520860f3a2a7ce8909111f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 28 Jun 2023 17:46:58 +0200 Subject: [PATCH 02/10] trace config updates --- src/ocpp.cpp | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/ocpp.cpp b/src/ocpp.cpp index 24f73369..c4e193ac 100644 --- a/src/ocpp.cpp +++ b/src/ocpp.cpp @@ -61,6 +61,20 @@ void ArduinoOcppTask::reconfigure() { if (getOcppContext() && config_ocpp_enabled()) { //library already running. Apply changed settings + DBUGF("[ocpp] Receiving new configs:\n" \ + " ocpp_server.c_str(): %s \n" \ + " ocpp_chargeBoxId.c_str(): %s \n" \ + " ocpp_authkey.c_str(): %s \n" \ + " config_ocpp_auto_authorization(): %s \n" \ + " ocpp_idtag.c_str(): %s \n" \ + " config_ocpp_offline_authorization(): %s", + ocpp_server.c_str(), + ocpp_chargeBoxId.c_str(), + ocpp_authkey.c_str(), + config_ocpp_auto_authorization() ? "true" : "false", + ocpp_idtag.c_str(), + config_ocpp_offline_authorization() ? "true" : "false" + ); ocppSocket->setBackendUrl(ocpp_server.c_str()); ocppSocket->setChargeBoxId(ocpp_chargeBoxId.c_str()); ocppSocket->setAuthKey(ocpp_authkey.c_str()); @@ -68,7 +82,7 @@ void ArduinoOcppTask::reconfigure() { *freevendActive = config_ocpp_auto_authorization(); *freevendIdTag = ocpp_idtag.c_str(); - *allowOfflineTxForUnknownId = config_ocpp_offline_authorization(); + // *allowOfflineTxForUnknownId = config_ocpp_offline_authorization(); if (config_ocpp_auto_authorization()) { *silentOfflineTx = true; //recommended to disable transaction journaling when being offline in Freevend mode } @@ -94,10 +108,10 @@ void ArduinoOcppTask::initializeArduinoOcpp() { ArduinoOcpp::configuration_init(filesystem); freevendActive = ArduinoOcpp::declareConfiguration("AO_FreeVendActive", true, CONFIGURATION_FN, true, true, true, true); freevendIdTag = ArduinoOcpp::declareConfiguration("AO_FreeVendIdTag", "DefaultIdTag", CONFIGURATION_FN, true, true, true, true); - allowOfflineTxForUnknownId = ArduinoOcpp::declareConfiguration("AllowOfflineTxForUnknownId", false, CONFIGURATION_FN, true, true, true, true); + allowOfflineTxForUnknownId = ArduinoOcpp::declareConfiguration("AllowOfflineTxForUnknownId", true, CONFIGURATION_FN, true, true, true, true); silentOfflineTx = ArduinoOcpp::declareConfiguration("AO_SilentOfflineTransactions", true, CONFIGURATION_FN, true, true, true, true); ArduinoOcpp::declareConfiguration("MeterValuesSampledData", "Power.Active.Import,Energy.Active.Import.Register,Current.Import,Current.Offered,Voltage,Temperature", CONFIGURATION_FN); - ArduinoOcpp::declareConfiguration("AO_PreBootTransactions", true, CONFIGURATION_FN, true, true, true, true); + ArduinoOcpp::declareConfiguration("AO_PreBootTransactions", true, CONFIGURATION_FN); /* * Initialize the OCPP library and provide it with the charger credentials */ @@ -117,6 +131,23 @@ void ArduinoOcppTask::initializeArduinoOcpp() { updateQuery["ocpp_auth_auto"] = *freevendActive ? 1 : 0; updateQuery["ocpp_idtag"] = (const char*) *freevendIdTag; updateQuery["ocpp_auth_offline"] = *allowOfflineTxForUnknownId ? 1 : 0; + DBUGF("[ocpp] Sending new configs:\n" \ + " ocppSocket->getBackendUrl(): %s \n" \ + " ocppSocket->getChargeBoxId(): %s \n" \ + " ocppSocket->getAuthKey(): %s \n" \ + " freevendActive: %s \n" \ + " freevendIdTag: %s \n" \ + " allowOfflineTxForUnknownId: %s", + ocppSocket->getBackendUrl(), + ocppSocket->getChargeBoxId(), + ocppSocket->getAuthKey(), + *freevendActive ? "true" : "false", + (const char*) *freevendIdTag, + *allowOfflineTxForUnknownId ? "true" : "false" + ); + DBUG("[ocpp] resulting JSON: "); + serializeJson(updateQuery, DEBUG_PORT); + DBUGLN(); config_deserialize(updateQuery); config_commit(); From 4200e2454041eab97e169565f52f58c6c03b68f4 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 4 Jul 2023 21:21:50 +0200 Subject: [PATCH 03/10] change OCPP factory defaults --- src/app_config.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app_config.cpp b/src/app_config.cpp index 633a9324..10784912 100644 --- a/src/app_config.cpp +++ b/src/app_config.cpp @@ -130,7 +130,11 @@ String esp_hostname_default = "openevse-"+ESPAL.getShortId(); void config_changed(String name); -ConfigOptDefenition flagsOpt = ConfigOptDefenition(flags, CONFIG_SERVICE_SNTP, "flags", "f"); +ConfigOptDefenition flagsOpt = ConfigOptDefenition(flags, + CONFIG_SERVICE_SNTP | + CONFIG_OCPP_AUTO_AUTH | + CONFIG_OCPP_OFFLINE_AUTH, + "flags", "f"); ConfigOpt *opts[] = { @@ -183,7 +187,7 @@ ConfigOpt *opts[] = new ConfigOptDefenition(ocpp_server, "", "ocpp_server", "ows"), new ConfigOptDefenition(ocpp_chargeBoxId, "", "ocpp_chargeBoxId", "cid"), new ConfigOptDefenition(ocpp_authkey, "", "ocpp_authkey", "oky"), - new ConfigOptDefenition(ocpp_idtag, "", "ocpp_idtag", "idt"), + new ConfigOptDefenition(ocpp_idtag, "DefaultIdTag", "ocpp_idtag", "idt"), // Ohm Connect Settings new ConfigOptDefenition(ohm, "", "ohm", "o"), From b45f275803fee3b8fb8b61ed4f8550e90c471749 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 4 Jul 2023 21:25:29 +0200 Subject: [PATCH 04/10] change configs sync (OpenEVSE prevails); update AO --- platformio.ini | 2 +- src/ocpp.cpp | 166 +++++++++++++++++++++++++------------------------ src/ocpp.h | 4 +- 3 files changed, 90 insertions(+), 82 deletions(-) diff --git a/platformio.ini b/platformio.ini index af05b2ee..02a5574e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -38,7 +38,7 @@ lib_deps = jeremypoulter/ESPAL@0.0.3 jeremypoulter/StreamSpy@0.0.1 jeremypoulter/MicroTasks@0.0.3 - https://github.com/matth-x/ArduinoOcpp#develop + https://github.com/matth-x/ArduinoOcpp#b8ccc8d https://github.com/matth-x/ArduinoOcppMongoose lib_ignore = WebSockets ; ArduinoOcpp: don't compile built-in WS library extra_scripts = diff --git a/src/ocpp.cpp b/src/ocpp.cpp index c4e193ac..b73d5a30 100644 --- a/src/ocpp.cpp +++ b/src/ocpp.cpp @@ -47,20 +47,44 @@ void ArduinoOcppTask::begin(EvseManager &evse, LcdTask &lcd, EventLog &eventLog, void ArduinoOcppTask::reconfigure() { - if (getOcppContext() && !config_ocpp_enabled()) { - //library initialized but now disabled - deinitializeArduinoOcpp(); - return; - } - - if (!getOcppContext() && config_ocpp_enabled()) { - //library not initialized yet but enabled via config (e.g. during system startup) - initializeArduinoOcpp(); - return; - } + if (config_ocpp_enabled()) { + //OCPP enabled via OpenEVSE config. Load library (if not done yet) and apply OpenEVSE configs + + if (!getOcppContext()) { + //first time execution, library not initialized yet + initializeArduinoOcpp(); + + //when the OCPP server updates the configs, the following callback will apply them to the OpenEVSE configs + setOnReceiveRequest("ChangeConfiguration", [this] (JsonObject) { + DynamicJsonDocument updateQuery (JSON_OBJECT_SIZE(6)); + updateQuery["ocpp_server"] = (const char*) *backendUrl; + updateQuery["ocpp_chargeBoxId"] = (const char*) *chargeBoxId; + updateQuery["ocpp_authkey"] = (const char*) *authKey; + updateQuery["ocpp_auth_auto"] = *freevendActive ? 1 : 0; + updateQuery["ocpp_idtag"] = (const char*) *freevendIdTag; + updateQuery["ocpp_auth_offline"] = *allowOfflineTxForUnknownId ? 1 : 0; + DBUGF("[ocpp] Sending new configs:\n" \ + " (const char*) *backendUrl: %s \n" \ + " (const char*) *chargeBoxId: %s \n" \ + " (const char*) *authKey: %s \n" \ + " freevendActive: %s \n" \ + " freevendIdTag: %s \n" \ + " allowOfflineTxForUnknownId: %s", + (const char*) *backendUrl, + (const char*) *chargeBoxId, + (const char*) *authKey, + *freevendActive ? "true" : "false", + (const char*) *freevendIdTag, + *allowOfflineTxForUnknownId ? "true" : "false" + ); + DBUG("[ocpp] resulting JSON: "); + serializeJson(updateQuery, DEBUG_PORT); + DBUGLN(); + config_deserialize(updateQuery); + config_commit(); + }); + } - if (getOcppContext() && config_ocpp_enabled()) { - //library already running. Apply changed settings DBUGF("[ocpp] Receiving new configs:\n" \ " ocpp_server.c_str(): %s \n" \ " ocpp_chargeBoxId.c_str(): %s \n" \ @@ -75,47 +99,45 @@ void ArduinoOcppTask::reconfigure() { ocpp_idtag.c_str(), config_ocpp_offline_authorization() ? "true" : "false" ); - ocppSocket->setBackendUrl(ocpp_server.c_str()); - ocppSocket->setChargeBoxId(ocpp_chargeBoxId.c_str()); - ocppSocket->setAuthKey(ocpp_authkey.c_str()); - ocppSocket->reconnect(); + + //apply new backend credentials if they have been updated via OCPP configs + if (!ocpp_server.equals((const char*) *backendUrl)) { + ocppSocket->setBackendUrl(ocpp_server.c_str()); + } + if (!ocpp_chargeBoxId.equals((const char*) *chargeBoxId)) { + ocppSocket->setChargeBoxId(ocpp_chargeBoxId.c_str()); + } + if (!ocpp_authkey.equals((const char*) *authKey)) { + ocppSocket->setAuthKey(ocpp_authkey.c_str()); + } *freevendActive = config_ocpp_auto_authorization(); *freevendIdTag = ocpp_idtag.c_str(); - // *allowOfflineTxForUnknownId = config_ocpp_offline_authorization(); + *allowOfflineTxForUnknownId = config_ocpp_offline_authorization(); if (config_ocpp_auto_authorization()) { *silentOfflineTx = true; //recommended to disable transaction journaling when being offline in Freevend mode } ArduinoOcpp::configuration_save(); + } else { + //OCPP disabled via OpenEVSE config + + if (getOcppContext()) { + //library still running. Deinitialize + deinitializeArduinoOcpp(); + } } } void ArduinoOcppTask::initializeArduinoOcpp() { - auto filesystem = ArduinoOcpp::makeDefaultFilesystemAdapter(ArduinoOcpp::FilesystemOpt::Use); - - ocppSocket = new ArduinoOcpp::AOcppMongooseClient(Mongoose.getMgr(), - ocpp_server.c_str(), //factory default URL. Normally, OcppSocket loads URL from own store - ocpp_chargeBoxId.c_str(), //factory default - ocpp_authkey.c_str(), //factory default - root_ca, //defined in root_ca.cpp - filesystem); + ocppSocket = new ArduinoOcpp::AOcppMongooseClient(Mongoose.getMgr(), nullptr, nullptr, nullptr, nullptr, + ArduinoOcpp::makeDefaultFilesystemAdapter(ArduinoOcpp::FilesystemOpt::Use)); - /* - * Load OCPP configs and apply factory defaults for OpenEVSE - */ - ArduinoOcpp::configuration_init(filesystem); - freevendActive = ArduinoOcpp::declareConfiguration("AO_FreeVendActive", true, CONFIGURATION_FN, true, true, true, true); - freevendIdTag = ArduinoOcpp::declareConfiguration("AO_FreeVendIdTag", "DefaultIdTag", CONFIGURATION_FN, true, true, true, true); - allowOfflineTxForUnknownId = ArduinoOcpp::declareConfiguration("AllowOfflineTxForUnknownId", true, CONFIGURATION_FN, true, true, true, true); - silentOfflineTx = ArduinoOcpp::declareConfiguration("AO_SilentOfflineTransactions", true, CONFIGURATION_FN, true, true, true, true); - ArduinoOcpp::declareConfiguration("MeterValuesSampledData", "Power.Active.Import,Energy.Active.Import.Register,Current.Import,Current.Offered,Voltage,Temperature", CONFIGURATION_FN); - ArduinoOcpp::declareConfiguration("AO_PreBootTransactions", true, CONFIGURATION_FN); /* * Initialize the OCPP library and provide it with the charger credentials */ - OCPP_initialize(*ocppSocket, ChargerCredentials( + ocpp_initialize(*ocppSocket, ChargerCredentials( "Advanced Series", //chargePointModel "OpenEVSE", //chargePointVendor currentfirmware.c_str(), //firmwareVersion @@ -123,33 +145,17 @@ void ArduinoOcppTask::initializeArduinoOcpp() { evse->getFirmwareVersion() //meterSerialNumber ), ArduinoOcpp::FilesystemOpt::Use); - //override values in UI with stored values - DynamicJsonDocument updateQuery (JSON_OBJECT_SIZE(6)); - updateQuery["ocpp_server"] = ocppSocket->getBackendUrl(); - updateQuery["ocpp_chargeBoxId"] = ocppSocket->getChargeBoxId(); - updateQuery["ocpp_authkey"] = ocppSocket->getAuthKey(); - updateQuery["ocpp_auth_auto"] = *freevendActive ? 1 : 0; - updateQuery["ocpp_idtag"] = (const char*) *freevendIdTag; - updateQuery["ocpp_auth_offline"] = *allowOfflineTxForUnknownId ? 1 : 0; - DBUGF("[ocpp] Sending new configs:\n" \ - " ocppSocket->getBackendUrl(): %s \n" \ - " ocppSocket->getChargeBoxId(): %s \n" \ - " ocppSocket->getAuthKey(): %s \n" \ - " freevendActive: %s \n" \ - " freevendIdTag: %s \n" \ - " allowOfflineTxForUnknownId: %s", - ocppSocket->getBackendUrl(), - ocppSocket->getChargeBoxId(), - ocppSocket->getAuthKey(), - *freevendActive ? "true" : "false", - (const char*) *freevendIdTag, - *allowOfflineTxForUnknownId ? "true" : "false" - ); - DBUG("[ocpp] resulting JSON: "); - serializeJson(updateQuery, DEBUG_PORT); - DBUGLN(); - config_deserialize(updateQuery); - config_commit(); + /* + * Load OCPP configs. Default values will be overwritten by OpenEVSE configs. Mark configs + * to require reboot if changed via OCPP server + */ + backendUrl = ArduinoOcpp::declareConfiguration("AO_BackendUrl", "", AO_FILENAME_PREFIX "ocpp-creds.jsn"); + chargeBoxId = ArduinoOcpp::declareConfiguration("AO_ChargeBoxId", "", AO_FILENAME_PREFIX "ocpp-creds.jsn"); + authKey = ArduinoOcpp::declareConfiguration("AuthorizationKey", "", AO_FILENAME_PREFIX "ocpp-creds.jsn"); + freevendActive = ArduinoOcpp::declareConfiguration("AO_FreeVendActive", true, CONFIGURATION_FN, true, true, true, true); + freevendIdTag = ArduinoOcpp::declareConfiguration("AO_FreeVendIdTag", "DefaultIdTag", CONFIGURATION_FN, true, true, true, true); + allowOfflineTxForUnknownId = ArduinoOcpp::declareConfiguration("AllowOfflineTxForUnknownId", true, CONFIGURATION_FN, true, true, true, true); + silentOfflineTx = ArduinoOcpp::declareConfiguration("AO_SilentOfflineTransactions", true, CONFIGURATION_FN, true, true, true, true); loadEvseBehavior(); initializeDiagnosticsService(); @@ -158,7 +164,7 @@ void ArduinoOcppTask::initializeArduinoOcpp() { void ArduinoOcppTask::deinitializeArduinoOcpp() { rfid->setOnCardScanned(nullptr); - OCPP_deinitialize(); + ocpp_deinitialize(); delete ocppSocket; ocppSocket = nullptr; } @@ -242,32 +248,32 @@ void ArduinoOcppTask::loadEvseBehavior() { * Report failures to central system. Note that the error codes are standardized in OCPP */ - addErrorCodeInput([this] () -> ChargePointErrorCode { + addErrorCodeInput([this] () -> const char* { if (evse->getEvseState() == OPENEVSE_STATE_OVER_TEMPERATURE) { - return ChargePointErrorCode::HighTemperature; + return "HighTemperature"; } - return ChargePointErrorCode::NoError; + return nullptr; }); - addErrorCodeInput([this] () -> ChargePointErrorCode { + addErrorCodeInput([this] () -> const char* { if (evse->getEvseState() == OPENEVSE_STATE_OVER_CURRENT) { - return ChargePointErrorCode::OverCurrentFailure; + return "OverCurrentFailure"; } - return ChargePointErrorCode::NoError; + return nullptr; }); - addErrorCodeInput([this] () -> ChargePointErrorCode { + addErrorCodeInput([this] () -> const char* { if (evse->getEvseState() == OPENEVSE_STATE_STUCK_RELAY) { - return ChargePointErrorCode::PowerSwitchFailure; + return "PowerSwitchFailure"; } - return ChargePointErrorCode::NoError; + return nullptr; }); - addErrorCodeInput([this] () -> ChargePointErrorCode { + addErrorCodeInput([this] () -> const char* { if (rfid->communicationFails()) { - return ChargePointErrorCode::ReaderFailure; + return "ReaderFailure"; } - return ChargePointErrorCode::NoError; + return nullptr; }); addErrorDataInput([this] () -> ArduinoOcpp::ErrorData { @@ -276,7 +282,7 @@ void ArduinoOcppTask::loadEvseBehavior() { evse->getEvseState() == OPENEVSE_STATE_NO_EARTH_GROUND || evse->getEvseState() == OPENEVSE_STATE_GFI_SELF_TEST_FAILED) { - ArduinoOcpp::ErrorData error = ChargePointErrorCode::GroundFailure; + ArduinoOcpp::ErrorData error = "GroundFailure"; error.info = evse->getEvseState() == OPENEVSE_STATE_DIODE_CHECK_FAILED ? "diode check failed" : evse->getEvseState() == OPENEVSE_STATE_GFI_FAULT ? "GFI fault" : @@ -284,7 +290,7 @@ void ArduinoOcppTask::loadEvseBehavior() { evse->getEvseState() == OPENEVSE_STATE_GFI_SELF_TEST_FAILED ? "GFI self test failed" : nullptr; return error; } - return ChargePointErrorCode::NoError; + return nullptr; }); onIdTagInput = [this] (const String& idInput) { @@ -371,7 +377,7 @@ unsigned long ArduinoOcppTask::loop(MicroTasks::WakeReason reason) { if (getOcppContext()) { //ArduinoOcpp is initialized - OCPP_loop(); + ocpp_loop(); /* * Generate messages for LCD diff --git a/src/ocpp.h b/src/ocpp.h index 705d8100..5ed23718 100644 --- a/src/ocpp.h +++ b/src/ocpp.h @@ -45,7 +45,9 @@ class ArduinoOcppTask: public MicroTasks::Task { ulong updateEvseClaimLast {0}; static ArduinoOcppTask *instance; - + std::shared_ptr> backendUrl; + std::shared_ptr> chargeBoxId; + std::shared_ptr> authKey; std::shared_ptr> freevendActive; //Authorize automatically std::shared_ptr> freevendIdTag; //idTag for auto-authorization std::shared_ptr> allowOfflineTxForUnknownId; //temporarily accept all NFC-cards while offline From f4570d1fec77699d43015d9b4b6f9d00be156bb1 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 5 Jul 2023 15:56:27 +0200 Subject: [PATCH 05/10] clean up configs sync --- platformio.ini | 2 +- src/ocpp.cpp | 86 ++++++++++++++++++++++---------------------------- src/ocpp.h | 2 ++ 3 files changed, 40 insertions(+), 50 deletions(-) diff --git a/platformio.ini b/platformio.ini index 02a5574e..a1e6d707 100644 --- a/platformio.ini +++ b/platformio.ini @@ -38,7 +38,7 @@ lib_deps = jeremypoulter/ESPAL@0.0.3 jeremypoulter/StreamSpy@0.0.1 jeremypoulter/MicroTasks@0.0.3 - https://github.com/matth-x/ArduinoOcpp#b8ccc8d + https://github.com/matth-x/ArduinoOcpp#00e42d7 https://github.com/matth-x/ArduinoOcppMongoose lib_ignore = WebSockets ; ArduinoOcpp: don't compile built-in WS library extra_scripts = diff --git a/src/ocpp.cpp b/src/ocpp.cpp index b73d5a30..1e8de033 100644 --- a/src/ocpp.cpp +++ b/src/ocpp.cpp @@ -53,54 +53,11 @@ void ArduinoOcppTask::reconfigure() { if (!getOcppContext()) { //first time execution, library not initialized yet initializeArduinoOcpp(); - - //when the OCPP server updates the configs, the following callback will apply them to the OpenEVSE configs - setOnReceiveRequest("ChangeConfiguration", [this] (JsonObject) { - DynamicJsonDocument updateQuery (JSON_OBJECT_SIZE(6)); - updateQuery["ocpp_server"] = (const char*) *backendUrl; - updateQuery["ocpp_chargeBoxId"] = (const char*) *chargeBoxId; - updateQuery["ocpp_authkey"] = (const char*) *authKey; - updateQuery["ocpp_auth_auto"] = *freevendActive ? 1 : 0; - updateQuery["ocpp_idtag"] = (const char*) *freevendIdTag; - updateQuery["ocpp_auth_offline"] = *allowOfflineTxForUnknownId ? 1 : 0; - DBUGF("[ocpp] Sending new configs:\n" \ - " (const char*) *backendUrl: %s \n" \ - " (const char*) *chargeBoxId: %s \n" \ - " (const char*) *authKey: %s \n" \ - " freevendActive: %s \n" \ - " freevendIdTag: %s \n" \ - " allowOfflineTxForUnknownId: %s", - (const char*) *backendUrl, - (const char*) *chargeBoxId, - (const char*) *authKey, - *freevendActive ? "true" : "false", - (const char*) *freevendIdTag, - *allowOfflineTxForUnknownId ? "true" : "false" - ); - DBUG("[ocpp] resulting JSON: "); - serializeJson(updateQuery, DEBUG_PORT); - DBUGLN(); - config_deserialize(updateQuery); - config_commit(); - }); } - DBUGF("[ocpp] Receiving new configs:\n" \ - " ocpp_server.c_str(): %s \n" \ - " ocpp_chargeBoxId.c_str(): %s \n" \ - " ocpp_authkey.c_str(): %s \n" \ - " config_ocpp_auto_authorization(): %s \n" \ - " ocpp_idtag.c_str(): %s \n" \ - " config_ocpp_offline_authorization(): %s", - ocpp_server.c_str(), - ocpp_chargeBoxId.c_str(), - ocpp_authkey.c_str(), - config_ocpp_auto_authorization() ? "true" : "false", - ocpp_idtag.c_str(), - config_ocpp_offline_authorization() ? "true" : "false" - ); - - //apply new backend credentials if they have been updated via OCPP configs + //apply new backend credentials if they have been updated via OpenEVSE GUI. Don't apply + //if the OCPP server updated them, because this closes the WS connection and can lead + //to the loss of the OCPP response if (!ocpp_server.equals((const char*) *backendUrl)) { ocppSocket->setBackendUrl(ocpp_server.c_str()); } @@ -111,6 +68,7 @@ void ArduinoOcppTask::reconfigure() { ocppSocket->setAuthKey(ocpp_authkey.c_str()); } + //apply further configs each time. They don't have potentially negative side effects *freevendActive = config_ocpp_auto_authorization(); *freevendIdTag = ocpp_idtag.c_str(); *allowOfflineTxForUnknownId = config_ocpp_offline_authorization(); @@ -131,8 +89,18 @@ void ArduinoOcppTask::reconfigure() { void ArduinoOcppTask::initializeArduinoOcpp() { - ocppSocket = new ArduinoOcpp::AOcppMongooseClient(Mongoose.getMgr(), nullptr, nullptr, nullptr, nullptr, - ArduinoOcpp::makeDefaultFilesystemAdapter(ArduinoOcpp::FilesystemOpt::Use)); + auto filesystem = ArduinoOcpp::makeDefaultFilesystemAdapter(ArduinoOcpp::FilesystemOpt::Use); + + ocppSocket = new ArduinoOcpp::AOcppMongooseClient(Mongoose.getMgr(), nullptr, nullptr, nullptr, nullptr, filesystem); + + /* + * Set OCPP-only factory defaults + */ + ArduinoOcpp::configuration_init(filesystem); + ArduinoOcpp::declareConfiguration( + "MeterValuesSampledData", "Power.Active.Import,Energy.Active.Import.Register,Current.Import,Current.Offered,Voltage,Temperature"); + ArduinoOcpp::declareConfiguration( + "AO_PreBootTransactions", true); /* * Initialize the OCPP library and provide it with the charger credentials @@ -143,7 +111,7 @@ void ArduinoOcppTask::initializeArduinoOcpp() { currentfirmware.c_str(), //firmwareVersion serial.c_str(), //chargePointSerialNumber evse->getFirmwareVersion() //meterSerialNumber - ), ArduinoOcpp::FilesystemOpt::Use); + ), filesystem); /* * Load OCPP configs. Default values will be overwritten by OpenEVSE configs. Mark configs @@ -157,12 +125,32 @@ void ArduinoOcppTask::initializeArduinoOcpp() { allowOfflineTxForUnknownId = ArduinoOcpp::declareConfiguration("AllowOfflineTxForUnknownId", true, CONFIGURATION_FN, true, true, true, true); silentOfflineTx = ArduinoOcpp::declareConfiguration("AO_SilentOfflineTransactions", true, CONFIGURATION_FN, true, true, true, true); + //when the OCPP server updates the configs, the following callback will apply them to the OpenEVSE configs + setOnReceiveRequest("ChangeConfiguration", [this] (JsonObject) { + DynamicJsonDocument updateQuery (JSON_OBJECT_SIZE(6)); + updateQuery["ocpp_server"] = (const char*) *backendUrl; + updateQuery["ocpp_chargeBoxId"] = (const char*) *chargeBoxId; + updateQuery["ocpp_authkey"] = (const char*) *authKey; + updateQuery["ocpp_auth_auto"] = *freevendActive ? 1 : 0; + updateQuery["ocpp_idtag"] = (const char*) *freevendIdTag; + updateQuery["ocpp_auth_offline"] = *allowOfflineTxForUnknownId ? 1 : 0; + config_deserialize(updateQuery); + config_commit(); + }); + loadEvseBehavior(); initializeDiagnosticsService(); initializeFwService(); } void ArduinoOcppTask::deinitializeArduinoOcpp() { + backendUrl.reset(); + chargeBoxId.reset(); + authKey.reset(); + freevendActive.reset(); + freevendIdTag.reset(); + allowOfflineTxForUnknownId.reset(); + silentOfflineTx.reset(); rfid->setOnCardScanned(nullptr); ocpp_deinitialize(); delete ocppSocket; diff --git a/src/ocpp.h b/src/ocpp.h index 5ed23718..23dcd5d6 100644 --- a/src/ocpp.h +++ b/src/ocpp.h @@ -45,6 +45,8 @@ class ArduinoOcppTask: public MicroTasks::Task { ulong updateEvseClaimLast {0}; static ArduinoOcppTask *instance; + + //OCPP configs std::shared_ptr> backendUrl; std::shared_ptr> chargeBoxId; std::shared_ptr> authKey; From d15dbd8b0d1fb8fade3960a85c2312128f0fab9b Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:22:48 +0200 Subject: [PATCH 06/10] stop looping when OCPP is disabled --- src/ocpp.cpp | 17 +++++++++++------ src/ocpp.h | 2 -- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/ocpp.cpp b/src/ocpp.cpp index 1e8de033..deef0f9f 100644 --- a/src/ocpp.cpp +++ b/src/ocpp.cpp @@ -13,6 +13,11 @@ #include "emonesp.h" #include "root_ca.h" +// Time between loop polls +#ifndef OCPP_LOOP_TIME +#define OCPP_LOOP_TIME 200 +#endif + #define LCD_DISPLAY(X) if (lcd) lcd->display((X), 0, 1, 5 * 1000, LCD_CLEAR_LINE); ArduinoOcppTask *ArduinoOcppTask::instance = NULL; @@ -39,10 +44,11 @@ void ArduinoOcppTask::begin(EvseManager &evse, LcdTask &lcd, EventLog &eventLog, ao_set_console_out(dbug_wrapper); + MicroTask.startTask(this); + reconfigure(); - instance = this; //cannot be in constructer because object is invalid before .begin() - MicroTask.startTask(this); + instance = this; //OcppTask is valid now } void ArduinoOcppTask::reconfigure() { @@ -85,6 +91,8 @@ void ArduinoOcppTask::reconfigure() { deinitializeArduinoOcpp(); } } + + MicroTask.wakeTask(this); } void ArduinoOcppTask::initializeArduinoOcpp() { @@ -395,12 +403,9 @@ unsigned long ArduinoOcppTask::loop(MicroTasks::WakeReason reason) { trackOcppConnected = isConnected(); } - if (millis() - updateEvseClaimLast >= 1009) { - updateEvseClaimLast = millis(); updateEvseClaim(); - } - return getOcppContext() ? 0 : 1000; + return config_ocpp_enabled() ? OCPP_LOOP_TIME : MicroTask.Infinate; } void ArduinoOcppTask::updateEvseClaim() { diff --git a/src/ocpp.h b/src/ocpp.h index 23dcd5d6..a62c40b2 100644 --- a/src/ocpp.h +++ b/src/ocpp.h @@ -42,8 +42,6 @@ class ArduinoOcppTask: public MicroTasks::Task { void deinitializeArduinoOcpp(); void loadEvseBehavior(); - ulong updateEvseClaimLast {0}; - static ArduinoOcppTask *instance; //OCPP configs From 77ec0cb46dff53de47481656382f44b5e0b91e65 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:23:23 +0200 Subject: [PATCH 07/10] minor clean up --- src/ocpp.cpp | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/ocpp.cpp b/src/ocpp.cpp index deef0f9f..fd77b028 100644 --- a/src/ocpp.cpp +++ b/src/ocpp.cpp @@ -120,14 +120,14 @@ void ArduinoOcppTask::initializeArduinoOcpp() { serial.c_str(), //chargePointSerialNumber evse->getFirmwareVersion() //meterSerialNumber ), filesystem); - + /* * Load OCPP configs. Default values will be overwritten by OpenEVSE configs. Mark configs * to require reboot if changed via OCPP server */ - backendUrl = ArduinoOcpp::declareConfiguration("AO_BackendUrl", "", AO_FILENAME_PREFIX "ocpp-creds.jsn"); - chargeBoxId = ArduinoOcpp::declareConfiguration("AO_ChargeBoxId", "", AO_FILENAME_PREFIX "ocpp-creds.jsn"); - authKey = ArduinoOcpp::declareConfiguration("AuthorizationKey", "", AO_FILENAME_PREFIX "ocpp-creds.jsn"); + backendUrl = ArduinoOcpp::declareConfiguration("AO_BackendUrl", "", AO_FILENAME_PREFIX "ocpp-creds.jsn", true, true, true, true); + chargeBoxId = ArduinoOcpp::declareConfiguration("AO_ChargeBoxId", "", AO_FILENAME_PREFIX "ocpp-creds.jsn", true, true, true, true); + authKey = ArduinoOcpp::declareConfiguration("AuthorizationKey", "", AO_FILENAME_PREFIX "ocpp-creds.jsn", true, true, true, true); freevendActive = ArduinoOcpp::declareConfiguration("AO_FreeVendActive", true, CONFIGURATION_FN, true, true, true, true); freevendIdTag = ArduinoOcpp::declareConfiguration("AO_FreeVendIdTag", "DefaultIdTag", CONFIGURATION_FN, true, true, true, true); allowOfflineTxForUnknownId = ArduinoOcpp::declareConfiguration("AllowOfflineTxForUnknownId", true, CONFIGURATION_FN, true, true, true, true); @@ -176,38 +176,38 @@ void ArduinoOcppTask::loadEvseBehavior() { */ addMeterValueInput([this] () { - return (int32_t) (evse->getAmps() * evse->getVoltage()); + return evse->getAmps() * evse->getVoltage(); }, "Power.Active.Import", "W"); addMeterValueInput([this] () { float activeImport = 0.f; - return (int32_t) evse->getTotalEnergy(); + return evse->getTotalEnergy(); }, "Energy.Active.Import.Register", "Wh"); addMeterValueInput([this] () { - return (int32_t) evse->getAmps(); + return evse->getAmps(); }, "Current.Import", "A"); addMeterValueInput([this] () { - return (int32_t) evse->getChargeCurrent(); + return (float) evse->getChargeCurrent(); }, "Current.Offered", "A"); addMeterValueInput([this] () { - return (int32_t) evse->getVoltage(); + return evse->getVoltage(); }, "Voltage", "V"); addMeterValueInput([this] () { - return (int32_t) evse->getTemperature(EVSE_MONITOR_TEMP_MONITOR); + return evse->getTemperature(EVSE_MONITOR_TEMP_MONITOR); }, "Temperature", "C"); @@ -403,7 +403,7 @@ unsigned long ArduinoOcppTask::loop(MicroTasks::WakeReason reason) { trackOcppConnected = isConnected(); } - updateEvseClaim(); + updateEvseClaim(); return config_ocpp_enabled() ? OCPP_LOOP_TIME : MicroTask.Infinate; } @@ -503,8 +503,7 @@ void ArduinoOcppTask::initializeDiagnosticsService() { unsigned int port_i = 0; struct mg_str scheme, query, fragment; if (mg_parse_uri(mg_mk_str(location.c_str()), &scheme, NULL, NULL, &port_i, NULL, &query, &fragment)) { - DBUG(F("[ocpp] Diagnostics upload, invalid URL: ")); - DBUGLN(location.c_str()); + DBUGF("[ocpp] Diagnostics upload, invalid URL: %s", location.c_str()); diagFailure = true; return false; } @@ -537,9 +536,8 @@ void ArduinoOcppTask::initializeDiagnosticsService() { eventLog->enumerate(index, [this, startTime, stopTime, &body, SUFFIX_RESERVED_AREA, &firstEntry, &overflow] (String time, EventType type, const String &logEntry, EvseState managerState, uint8_t evseState, uint32_t evseFlags, uint32_t pilot, double energy, uint32_t elapsed, double temperature, double temperatureMax, uint8_t divertMode, uint8_t shaper) { if (overflow) return; ArduinoOcpp::Timestamp timestamp = ArduinoOcpp::Timestamp(); - if (!timestamp.setTime(time.c_str())) { - DBUG(F("[ocpp] Diagnostics upload, cannot parse timestamp format: ")); - DBUGLN(time); + if (time.isEmpty() || !timestamp.setTime(time.c_str())) { + DBUGF("[ocpp] Diagnostics upload, cannot parse timestamp format: %s", time.c_str()); return; } @@ -572,8 +570,7 @@ void ArduinoOcppTask::initializeDiagnosticsService() { body += bodySuffix; - DBUG(F("[ocpp] POST diagnostics file to ")); - DBUGLN(location.c_str()); + DBUGF("[ocpp] POST diagnostics file to %s", location.c_str()); MongooseHttpClientRequest *request = diagClient.beginRequest(location.c_str()); From fdf4b9631914f303fb65f5d0c9202ca387a934cb Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 24 Jul 2023 15:29:37 +0200 Subject: [PATCH 08/10] convert kWh into Wh (addresses #703) --- src/ocpp.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ocpp.cpp b/src/ocpp.cpp index fd77b028..7fd31e49 100644 --- a/src/ocpp.cpp +++ b/src/ocpp.cpp @@ -182,8 +182,7 @@ void ArduinoOcppTask::loadEvseBehavior() { "W"); addMeterValueInput([this] () { - float activeImport = 0.f; - return evse->getTotalEnergy(); + return evse->getTotalEnergy() * 1000.; //convert kWh into Wh }, "Energy.Active.Import.Register", "Wh"); From 25474bf07695e5ff410875edf4d0345ec80bdda0 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 18 Aug 2023 23:19:39 +0200 Subject: [PATCH 09/10] change configs directly --- src/ocpp.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/ocpp.cpp b/src/ocpp.cpp index 7fd31e49..6e6fe6a6 100644 --- a/src/ocpp.cpp +++ b/src/ocpp.cpp @@ -135,14 +135,12 @@ void ArduinoOcppTask::initializeArduinoOcpp() { //when the OCPP server updates the configs, the following callback will apply them to the OpenEVSE configs setOnReceiveRequest("ChangeConfiguration", [this] (JsonObject) { - DynamicJsonDocument updateQuery (JSON_OBJECT_SIZE(6)); - updateQuery["ocpp_server"] = (const char*) *backendUrl; - updateQuery["ocpp_chargeBoxId"] = (const char*) *chargeBoxId; - updateQuery["ocpp_authkey"] = (const char*) *authKey; - updateQuery["ocpp_auth_auto"] = *freevendActive ? 1 : 0; - updateQuery["ocpp_idtag"] = (const char*) *freevendIdTag; - updateQuery["ocpp_auth_offline"] = *allowOfflineTxForUnknownId ? 1 : 0; - config_deserialize(updateQuery); + config_set("ocpp_server", String((const char*) *backendUrl)); + config_set("ocpp_chargeBoxId", String((const char*) *chargeBoxId)); + config_set("ocpp_authkey", String((const char*) *authKey)); + config_set("ocpp_auth_auto", (uint32_t) (*freevendActive ? 1 : 0)); + config_set("ocpp_idtag", String((const char*) *freevendIdTag)); + config_set("ocpp_auth_offline", (uint32_t) (*allowOfflineTxForUnknownId ? 1 : 0)); config_commit(); }); From 78f2a5b1b05381f82853b7c18650379c90d77314 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 18 Aug 2023 23:20:35 +0200 Subject: [PATCH 10/10] update OCPP lib --- platformio.ini | 4 ++-- src/ocpp.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index 2338a71d..1ea7ba75 100644 --- a/platformio.ini +++ b/platformio.ini @@ -38,8 +38,8 @@ lib_deps = jeremypoulter/ESPAL@0.0.3 jeremypoulter/StreamSpy@0.0.1 jeremypoulter/MicroTasks@0.0.3 - https://github.com/matth-x/ArduinoOcpp#00e42d7 - https://github.com/matth-x/ArduinoOcppMongoose + matth-x/ArduinoOcpp@0.3.0 + matth-x/ArduinoOcppMongoose@0.1.0 lib_ignore = WebSockets ; ArduinoOcpp: don't compile built-in WS library extra_scripts = pre:scripts/auto_fw_version.py diff --git a/src/ocpp.cpp b/src/ocpp.cpp index 6e6fe6a6..192ca25c 100644 --- a/src/ocpp.cpp +++ b/src/ocpp.cpp @@ -119,7 +119,7 @@ void ArduinoOcppTask::initializeArduinoOcpp() { currentfirmware.c_str(), //firmwareVersion serial.c_str(), //chargePointSerialNumber evse->getFirmwareVersion() //meterSerialNumber - ), filesystem); + ), ArduinoOcpp::FilesystemOpt::Use); /* * Load OCPP configs. Default values will be overwritten by OpenEVSE configs. Mark configs