From 7a2e2519cd436a231f648cee36076ab01e37d6ef Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 12:14:42 +0100 Subject: [PATCH 01/13] Move SOFAR_ID to new setting mechanism. --- Software/src/communication/nvm/comm_nvm.cpp | 5 +---- .../src/devboard/webserver/settings_html.cpp | 20 +++++++++++++------ Software/src/devboard/webserver/webserver.cpp | 7 +++---- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 582316d8..688d961c 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -193,10 +193,7 @@ void store_settings() { if (!settings.putUInt("TARGETDISCHVOLT", datalayer.battery.settings.max_user_set_discharge_voltage_dV)) { set_event(EVENT_PERSISTENT_SAVE_INFO, 11); } - if (!settings.putUInt("SOFAR_ID", datalayer.battery.settings.sofar_user_specified_battery_id)) { - set_event(EVENT_PERSISTENT_SAVE_INFO, 12); - } - if (!settings.putUInt("BMSRESETDUR", datalayer.battery.settings.sofar_user_specified_battery_id)) { + if (!settings.putUInt("BMSRESETDUR", datalayer.battery.settings.user_set_bms_reset_duration_ms)) { set_event(EVENT_PERSISTENT_SAVE_INFO, 13); } diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index e9e52804..31936a55 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -468,6 +468,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return String(datalayer.charger.charger_setpoint_HV_IDC, 1); } + if (var == "SOFAR_ID") { + return String(settings.getUInt("SOFAR_ID", 0)); + } + return String(); } @@ -527,10 +531,6 @@ const char* getCANInterfaceName(CAN_Interface interface) { function editPassword(){var value=prompt('Enter new password:');if(value!==null){var xhr=new XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/updatePassword?value='+encodeURIComponent(value),true);xhr.send();}} - function editSofarID(){var value=prompt('For double battery setups. Which battery ID should this emulator send? Remember to reboot after configuring this! Enter new value between (0-15):'); - if(value!==null){if(value>=0&&value<=15){var xhr=new XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/updateSofarID?value='+value,true);xhr.send();} - else {alert('Invalid value. Please enter a value between 0 and 15.');}}} - function editWh(){var value=prompt('How much energy the battery can store. Enter new Wh value (1-400000):'); if(value!==null){if(value>=1&&value<=400000){var xhr=new XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/updateBatterySize?value='+value,true);xhr.send();}else{ @@ -663,6 +663,11 @@ const char* getCANInterfaceName(CAN_Interface interface) { display: contents; } + form .if-sofar { display: none; } + form[data-inverter="17"] .if-sofar { + display: contents; + } + form .if-mqtt { display: none; } form[data-mqttenabled="true"] .if-mqtt { display: contents; @@ -726,6 +731,11 @@ const char* getCANInterfaceName(CAN_Interface interface) { +
+ + +
+ @@ -826,8 +836,6 @@ const char* getCANInterfaceName(CAN_Interface interface) {

Battery interface: %BATTERY2INTF%

Inverter interface: %INVINTF%

- -

Battery ID: %INVBID%

Shunt interface: %SHUNTINTF%

diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 4949bd86..498ecc27 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -494,6 +494,9 @@ void init_webserver() { settings.saveString("MQTTDEVICENAME", p->value().c_str()); } else if (p->name() == "HADEVICEID") { settings.saveString("HADEVICEID", p->value().c_str()); + } else if (p->name() == "SOFAR_ID") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("SOFAR_ID", type); } for (auto& boolSetting : boolSettings) { @@ -579,10 +582,6 @@ void init_webserver() { update_string_setting(route, [setter](String value) { setter(value.toInt()); }); }; - // Route for editing Sofar ID - update_int_setting("/updateSofarID", - [](int value) { datalayer.battery.settings.sofar_user_specified_battery_id = value; }); - // Route for editing Wh update_int_setting("/updateBatterySize", [](int value) { datalayer.battery.info.total_capacity_Wh = value; }); From 9816417b21afe6ed41fee938ab96696f63c49f38 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 12:15:05 +0100 Subject: [PATCH 02/13] Pin Inverter enum values so they won't change in future --- Software/src/inverter/InverterProtocol.h | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Software/src/inverter/InverterProtocol.h b/Software/src/inverter/InverterProtocol.h index 3e48ea70..7b9a3f5f 100644 --- a/Software/src/inverter/InverterProtocol.h +++ b/Software/src/inverter/InverterProtocol.h @@ -5,27 +5,27 @@ enum class InverterProtocolType { None = 0, - AforeCan, - BydCan, - BydModbus, - FerroampCan, - Foxess, - GrowattHv, - GrowattLv, - GrowattWit, - Kostal, - Pylon, - PylonLv, - Schneider, - SmaBydH, - SmaBydHvs, - SmaLv, - SmaTripower, - Sofar, - Solax, - Solxpow, - SolArkLv, - Sungrow, + AforeCan = 1, + BydCan = 2, + BydModbus = 3, + FerroampCan = 4, + Foxess = 5, + GrowattHv = 6, + GrowattLv = 7, + GrowattWit = 8, + Kostal = 9, + Pylon = 10, + PylonLv = 11, + Schneider = 12, + SmaBydH = 13, + SmaBydHvs = 14, + SmaLv = 15, + SmaTripower = 16, + Sofar = 17, + Solax = 18, + Solxpow = 19, + SolArkLv = 20, + Sungrow = 21, Highest }; From 2f1ff9950ce8843bcfbc3dcbd03c060ffc392499 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 12:18:11 +0100 Subject: [PATCH 03/13] Add configurable cell/module/etc settings for Pylonish/Solax inverters to use --- Software/src/communication/nvm/comm_nvm.cpp | 7 ++ .../src/devboard/webserver/settings_html.cpp | 67 +++++++++++++++++++ Software/src/devboard/webserver/webserver.cpp | 20 +++++- Software/src/inverter/INVERTERS.cpp | 9 +++ Software/src/inverter/INVERTERS.h | 10 +++ 5 files changed, 112 insertions(+), 1 deletion(-) diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 688d961c..91adc099 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -96,6 +96,13 @@ void init_stored_settings() { user_selected_min_pack_voltage_dV = settings.getUInt("BATTPVMIN", 0); user_selected_max_cell_voltage_mV = settings.getUInt("BATTCVMAX", 0); user_selected_min_cell_voltage_mV = settings.getUInt("BATTCVMIN", 0); + user_selected_inverter_cells = settings.getUInt("INVCELLS", 0); + user_selected_inverter_modules = settings.getUInt("INVMODULES", 0); + user_selected_inverter_cells_per_module = settings.getUInt("INVCELLSPER", 0); + user_selected_inverter_voltage_level = settings.getUInt("INVVLEVEL", 0); + user_selected_inverter_ah_capacity = settings.getUInt("INVAHCAPACITY", 0); + user_selected_inverter_battery_type = settings.getUInt("INVBTYPE", 0); + user_selected_inverter_ignore_contactors = settings.getBool("INVICNT", false); auto readIf = [](const char* settingName) { auto batt1If = (comm_interface)settings.getUInt(settingName, (int)comm_interface::CanNative); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 31936a55..7ca338ce 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -472,6 +472,34 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return String(settings.getUInt("SOFAR_ID", 0)); } + if (var == "INVCELLS") { + return String(settings.getUInt("INVCELLS", 0)); + } + + if (var == "INVMODULES") { + return String(settings.getUInt("INVMODULES", 0)); + } + + if (var == "INVCELLSPER") { + return String(settings.getUInt("INVCELLSPER", 0)); + } + + if (var == "INVVLEVEL") { + return String(settings.getUInt("INVVLEVEL", 0)); + } + + if (var == "INVCAPACITY") { + return String(settings.getUInt("INVCAPACITY", 0)); + } + + if (var == "INVBTYPE") { + return String(settings.getUInt("INVBTYPE", 0)); + } + + if (var == "INVICNT") { + return settings.getBool("INVICNT") ? "checked" : ""; + } + return String(); } @@ -668,6 +696,16 @@ const char* getCANInterfaceName(CAN_Interface interface) { display: contents; } + form .if-pylonish { display: none; } + form[data-inverter="4"] .if-pylonish, form[data-inverter="10"] .if-pylonish, form[data-inverter="19"] .if-pylonish { + display: contents; + } + + form .if-solax { display: none; } + form[data-inverter="18"] .if-solax { + display: contents; + } + form .if-mqtt { display: none; } form[data-mqttenabled="true"] .if-mqtt { display: contents; @@ -736,6 +774,35 @@ const char* getCANInterfaceName(CAN_Interface interface) { +
+ + +
+ +
+ + +
+ +
+ + + + + + + + +
+ +
+ + + + + +
+ diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 498ecc27..cfa65a8a 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -414,7 +414,7 @@ void init_webserver() { const char* boolSettingNames[] = { "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET", - "CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", + "CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", "INVICNT", }; // Handles the form POST from UI to save settings of the common image @@ -497,6 +497,24 @@ void init_webserver() { } else if (p->name() == "SOFAR_ID") { auto type = atoi(p->value().c_str()); settings.saveUInt("SOFAR_ID", type); + } else if (p->name() == "INVCELLS") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVCELLS", type); + } else if (p->name() == "INVMODULES") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVMODULES", type); + } else if (p->name() == "INVCELLSPER") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVCELLSPER", type); + } else if (p->name() == "INVVLEVEL") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVVLEVEL", type); + } else if (p->name() == "INVCAPACITY") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVCAPACITY", type); + } else if (p->name() == "INVBTYPE") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVBTYPE", (int)type); } for (auto& boolSetting : boolSettings) { diff --git a/Software/src/inverter/INVERTERS.cpp b/Software/src/inverter/INVERTERS.cpp index aa762e75..f9b80d88 100644 --- a/Software/src/inverter/INVERTERS.cpp +++ b/Software/src/inverter/INVERTERS.cpp @@ -90,6 +90,15 @@ extern const char* name_for_inverter_type(InverterProtocolType type) { #error "Compile time SELECTED_INVERTER_CLASS should not be defined with COMMON_IMAGE" #endif +// Some settings that can be used by inverters +uint16_t user_selected_inverter_cells = 0; +uint16_t user_selected_inverter_modules = 0; +uint16_t user_selected_inverter_cells_per_module = 0; +uint16_t user_selected_inverter_voltage_level = 0; +uint16_t user_selected_inverter_ah_capacity = 0; +uint16_t user_selected_inverter_battery_type = 0; +bool user_selected_inverter_ignore_contactors = false; + bool setup_inverter() { if (inverter) { return true; diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h index c706c8a9..712b425a 100644 --- a/Software/src/inverter/INVERTERS.h +++ b/Software/src/inverter/INVERTERS.h @@ -36,4 +36,14 @@ extern InverterProtocol* inverter; // Call to initialize the build-time selected inverter. Safe to call even though inverter was not selected. bool setup_inverter(); +#ifdef COMMON_IMAGE +extern uint16_t user_selected_inverter_cells; +extern uint16_t user_selected_inverter_modules; +extern uint16_t user_selected_inverter_cells_per_module; +extern uint16_t user_selected_inverter_voltage_level; +extern uint16_t user_selected_inverter_ah_capacity; +extern uint16_t user_selected_inverter_battery_type; +extern bool user_selected_inverter_ignore_contactors; +#endif + #endif From efd6ca13490c5e2c534ddeee981fe470cc7b6647 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 12:19:30 +0100 Subject: [PATCH 04/13] Make Pylon/Solxpow/Ferroamp inverters use the new settings in COMMON_IMAGE mode --- Software/src/inverter/FERROAMP-CAN.cpp | 36 ++++++++++++++++++++++++++ Software/src/inverter/FERROAMP-CAN.h | 1 + Software/src/inverter/INVERTERS.cpp | 20 +++++++------- Software/src/inverter/INVERTERS.h | 2 -- Software/src/inverter/PYLON-CAN.cpp | 36 ++++++++++++++++++++++++++ Software/src/inverter/PYLON-CAN.h | 1 + Software/src/inverter/SOLXPOW-CAN.cpp | 36 ++++++++++++++++++++++++++ Software/src/inverter/SOLXPOW-CAN.h | 1 + 8 files changed, 122 insertions(+), 11 deletions(-) diff --git a/Software/src/inverter/FERROAMP-CAN.cpp b/Software/src/inverter/FERROAMP-CAN.cpp index 0ecdf69e..cb9757d5 100644 --- a/Software/src/inverter/FERROAMP-CAN.cpp +++ b/Software/src/inverter/FERROAMP-CAN.cpp @@ -1,6 +1,7 @@ #include "FERROAMP-CAN.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" +#include "../inverter/INVERTERS.h" //#define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters) #define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters) @@ -364,3 +365,38 @@ void FerroampCanInverter::send_system_data() { //System equipment information transmit_can_frame(&PYLON_4291); #endif } + +bool FerroampCanInverter::setup() { + if (user_selected_inverter_cells > 0) { + PYLON_7320.data.u8[0] = user_selected_inverter_cells & 0xff; + PYLON_7320.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + PYLON_7321.data.u8[0] = user_selected_inverter_cells & 0xff; + PYLON_7321.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + } + + if (user_selected_inverter_modules > 0) { + PYLON_7320.data.u8[2] = user_selected_inverter_modules; + PYLON_7321.data.u8[2] = user_selected_inverter_modules; + } + + if (user_selected_inverter_cells_per_module > 0) { + PYLON_7320.data.u8[3] = user_selected_inverter_cells_per_module; + PYLON_7321.data.u8[3] = user_selected_inverter_cells_per_module; + } + + if (user_selected_inverter_voltage_level > 0) { + PYLON_7320.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + PYLON_7320.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + PYLON_7321.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + PYLON_7321.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + } + + if (user_selected_inverter_ah_capacity > 0) { + PYLON_7320.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + PYLON_7320.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + PYLON_7321.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + PYLON_7321.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + } + + return true; +} diff --git a/Software/src/inverter/FERROAMP-CAN.h b/Software/src/inverter/FERROAMP-CAN.h index f45dabc4..00ec78bd 100644 --- a/Software/src/inverter/FERROAMP-CAN.h +++ b/Software/src/inverter/FERROAMP-CAN.h @@ -10,6 +10,7 @@ class FerroampCanInverter : public CanInverterProtocol { public: const char* name() override { return Name; } + bool setup() override; void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); diff --git a/Software/src/inverter/INVERTERS.cpp b/Software/src/inverter/INVERTERS.cpp index f9b80d88..cbd64f4b 100644 --- a/Software/src/inverter/INVERTERS.cpp +++ b/Software/src/inverter/INVERTERS.cpp @@ -4,6 +4,17 @@ InverterProtocol* inverter = nullptr; InverterProtocolType user_selected_inverter_protocol = InverterProtocolType::BydModbus; +// Some user-configurable settings that can be used by inverters. These +// inverters should use sensible defaults if the corresponding user_selected +// value is zero. +uint16_t user_selected_inverter_cells = 0; +uint16_t user_selected_inverter_modules = 0; +uint16_t user_selected_inverter_cells_per_module = 0; +uint16_t user_selected_inverter_voltage_level = 0; +uint16_t user_selected_inverter_ah_capacity = 0; +uint16_t user_selected_inverter_battery_type = 0; +bool user_selected_inverter_ignore_contactors = false; + std::vector supported_inverter_protocols() { std::vector types; @@ -90,15 +101,6 @@ extern const char* name_for_inverter_type(InverterProtocolType type) { #error "Compile time SELECTED_INVERTER_CLASS should not be defined with COMMON_IMAGE" #endif -// Some settings that can be used by inverters -uint16_t user_selected_inverter_cells = 0; -uint16_t user_selected_inverter_modules = 0; -uint16_t user_selected_inverter_cells_per_module = 0; -uint16_t user_selected_inverter_voltage_level = 0; -uint16_t user_selected_inverter_ah_capacity = 0; -uint16_t user_selected_inverter_battery_type = 0; -bool user_selected_inverter_ignore_contactors = false; - bool setup_inverter() { if (inverter) { return true; diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h index 712b425a..b8207f7d 100644 --- a/Software/src/inverter/INVERTERS.h +++ b/Software/src/inverter/INVERTERS.h @@ -36,7 +36,6 @@ extern InverterProtocol* inverter; // Call to initialize the build-time selected inverter. Safe to call even though inverter was not selected. bool setup_inverter(); -#ifdef COMMON_IMAGE extern uint16_t user_selected_inverter_cells; extern uint16_t user_selected_inverter_modules; extern uint16_t user_selected_inverter_cells_per_module; @@ -44,6 +43,5 @@ extern uint16_t user_selected_inverter_voltage_level; extern uint16_t user_selected_inverter_ah_capacity; extern uint16_t user_selected_inverter_battery_type; extern bool user_selected_inverter_ignore_contactors; -#endif #endif diff --git a/Software/src/inverter/PYLON-CAN.cpp b/Software/src/inverter/PYLON-CAN.cpp index 293e53cf..dabd778f 100644 --- a/Software/src/inverter/PYLON-CAN.cpp +++ b/Software/src/inverter/PYLON-CAN.cpp @@ -1,6 +1,7 @@ #include "PYLON-CAN.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" +#include "../inverter/INVERTERS.h" #define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters) //#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters) @@ -351,3 +352,38 @@ void PylonInverter::send_system_data() { //System equipment information transmit_can_frame(&PYLON_4291); #endif } + +bool PylonInverter::setup() { + if (user_selected_inverter_cells > 0) { + PYLON_7320.data.u8[0] = user_selected_inverter_cells & 0xff; + PYLON_7320.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + PYLON_7321.data.u8[0] = user_selected_inverter_cells & 0xff; + PYLON_7321.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + } + + if (user_selected_inverter_modules > 0) { + PYLON_7320.data.u8[2] = user_selected_inverter_modules; + PYLON_7321.data.u8[2] = user_selected_inverter_modules; + } + + if (user_selected_inverter_cells_per_module > 0) { + PYLON_7320.data.u8[3] = user_selected_inverter_cells_per_module; + PYLON_7321.data.u8[3] = user_selected_inverter_cells_per_module; + } + + if (user_selected_inverter_voltage_level > 0) { + PYLON_7320.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + PYLON_7320.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + PYLON_7321.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + PYLON_7321.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + } + + if (user_selected_inverter_ah_capacity > 0) { + PYLON_7320.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + PYLON_7320.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + PYLON_7321.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + PYLON_7321.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + } + + return true; +} diff --git a/Software/src/inverter/PYLON-CAN.h b/Software/src/inverter/PYLON-CAN.h index 5c55b039..33a8b1b6 100644 --- a/Software/src/inverter/PYLON-CAN.h +++ b/Software/src/inverter/PYLON-CAN.h @@ -10,6 +10,7 @@ class PylonInverter : public CanInverterProtocol { public: const char* name() override { return Name; } + bool setup() override; void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); diff --git a/Software/src/inverter/SOLXPOW-CAN.cpp b/Software/src/inverter/SOLXPOW-CAN.cpp index d6f2cdcc..b29946f1 100644 --- a/Software/src/inverter/SOLXPOW-CAN.cpp +++ b/Software/src/inverter/SOLXPOW-CAN.cpp @@ -1,6 +1,7 @@ #include "SOLXPOW-CAN.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" +#include "../inverter/INVERTERS.h" #define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters) //#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters) @@ -353,3 +354,38 @@ void SolxpowInverter::send_system_data() { //System equipment information transmit_can_frame(&SOLXPOW_4291); #endif } + +bool SolxpowInverter::setup() { + if (user_selected_inverter_cells > 0) { + SOLXPOW_7320.data.u8[0] = user_selected_inverter_cells & 0xff; + SOLXPOW_7320.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + SOLXPOW_7321.data.u8[0] = user_selected_inverter_cells & 0xff; + SOLXPOW_7321.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + } + + if (user_selected_inverter_modules > 0) { + SOLXPOW_7320.data.u8[2] = user_selected_inverter_modules; + SOLXPOW_7321.data.u8[2] = user_selected_inverter_modules; + } + + if (user_selected_inverter_cells_per_module > 0) { + SOLXPOW_7320.data.u8[3] = user_selected_inverter_cells_per_module; + SOLXPOW_7321.data.u8[3] = user_selected_inverter_cells_per_module; + } + + if (user_selected_inverter_voltage_level > 0) { + SOLXPOW_7320.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + SOLXPOW_7320.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + SOLXPOW_7321.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + SOLXPOW_7321.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + } + + if (user_selected_inverter_ah_capacity > 0) { + SOLXPOW_7320.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + SOLXPOW_7320.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + SOLXPOW_7321.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + SOLXPOW_7321.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + } + + return true; +} diff --git a/Software/src/inverter/SOLXPOW-CAN.h b/Software/src/inverter/SOLXPOW-CAN.h index 73bc7e13..f45970ee 100644 --- a/Software/src/inverter/SOLXPOW-CAN.h +++ b/Software/src/inverter/SOLXPOW-CAN.h @@ -10,6 +10,7 @@ class SolxpowInverter : public CanInverterProtocol { public: const char* name() override { return Name; } + bool setup() override; void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); From a718d99672bb174b6448e15be949de18dca47ff9 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 12:20:30 +0100 Subject: [PATCH 05/13] Make Solax use type settings in COMMON_IMAGE mode, add ignore contactors feature --- Software/src/inverter/SOLAX-CAN.cpp | 59 +++++++++++++++++++++++------ Software/src/inverter/SOLAX-CAN.h | 12 ++++++ 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/Software/src/inverter/SOLAX-CAN.cpp b/Software/src/inverter/SOLAX-CAN.cpp index a3f77a3e..16df4f2a 100644 --- a/Software/src/inverter/SOLAX-CAN.cpp +++ b/Software/src/inverter/SOLAX-CAN.cpp @@ -4,11 +4,7 @@ #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" #include "../devboard/utils/logging.h" - -#define NUMBER_OF_MODULES 0 -#define BATTERY_TYPE 0x50 -// If you are having BattVoltFault issues, configure the above values according to wiki page -// https://github.com/dalathegreat/Battery-Emulator/wiki/Solax-inverters +#include "../inverter/INVERTERS.h" // __builtin_bswap64 needed to convert to ESP32 little endian format // Byte[4] defines the requested contactor state: 1 = Closed , 0 = Open @@ -18,7 +14,7 @@ void SolaxInverter:: update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages // If not receiveing any communication from the inverter, open contactors and return to battery announce state - if (millis() - LastFrameTime >= SolaxTimeout) { + if (millis() - LastFrameTime >= SolaxTimeout && !configured_ignore_contactors) { datalayer.system.status.inverter_allows_contactor_closing = false; STATE = BATTERY_ANNOUNCE; } @@ -73,8 +69,8 @@ void SolaxInverter:: //BMS_Status SOLAX_1875.data.u8[0] = (uint8_t)temperature_average; SOLAX_1875.data.u8[1] = (temperature_average >> 8); - SOLAX_1875.data.u8[2] = (uint8_t)NUMBER_OF_MODULES; // Number of slave batteries - SOLAX_1875.data.u8[4] = (uint8_t)0; // Contactor Status 0=off, 1=on. + SOLAX_1875.data.u8[2] = (uint8_t)configured_number_of_modules; // Number of slave batteries + SOLAX_1875.data.u8[4] = (uint8_t)0; // Contactor Status 0=off, 1=on. //BMS_PackTemps (strange name, since it has voltages?) SOLAX_1876.data.u8[0] = (int8_t)datalayer.battery.status.temperature_max_dC; @@ -88,8 +84,8 @@ void SolaxInverter:: SOLAX_1876.data.u8[7] = (datalayer.battery.status.cell_min_voltage_mV >> 8); //Unknown - SOLAX_1877.data.u8[4] = (uint8_t)BATTERY_TYPE; // Battery type (Default 0x50) - SOLAX_1877.data.u8[6] = (uint8_t)0x22; // Firmware version? + SOLAX_1877.data.u8[4] = (uint8_t)configured_battery_type; // Battery type (Default 0x50) + SOLAX_1877.data.u8[6] = (uint8_t)0x22; // Firmware version? SOLAX_1877.data.u8[7] = (uint8_t)0x02; // The above firmware version applies to:02 = Master BMS, 10 = S1, 20 = S2, 30 = S3, 40 = S4 @@ -129,6 +125,26 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) { if (rx_frame.ID == 0x1871 && rx_frame.data.u8[0] == (0x01) || rx_frame.ID == 0x1871 && rx_frame.data.u8[0] == (0x02)) { LastFrameTime = millis(); + + if (configured_ignore_contactors) { + // Skip the state machine since we're not going to open/close contactors, + // and the Solax would otherwise wait forever for us to do so. + + datalayer.system.status.inverter_allows_contactor_closing = true; + SOLAX_1875.data.u8[4] = (0x01); // Inform Inverter: Contactor 0=off, 1=on. + transmit_can_frame(&SOLAX_187E); + transmit_can_frame(&SOLAX_187A); + transmit_can_frame(&SOLAX_1872); + transmit_can_frame(&SOLAX_1873); + transmit_can_frame(&SOLAX_1874); + transmit_can_frame(&SOLAX_1875); + transmit_can_frame(&SOLAX_1876); + transmit_can_frame(&SOLAX_1877); + transmit_can_frame(&SOLAX_1878); + transmit_can_frame(&SOLAX_100A001); + return; + } + switch (STATE) { case (BATTERY_ANNOUNCE): #ifdef DEBUG_LOG @@ -208,7 +224,26 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) { } } -bool SolaxInverter::setup(void) { // Performs one time setup at startup - datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first +bool SolaxInverter::setup(void) { // Performs one time setup at startup + // Use user selected values if nonzero, otherwise use defaults + if (user_selected_inverter_modules > 0) { + configured_number_of_modules = user_selected_inverter_modules; + } else { + configured_number_of_modules = NUMBER_OF_MODULES; + } + + if (user_selected_inverter_battery_type > 0) { + configured_battery_type = user_selected_inverter_battery_type; + } else { + configured_battery_type = BATTERY_TYPE; + } + + configured_ignore_contactors = user_selected_inverter_ignore_contactors; + + if (!configured_ignore_contactors) { + // Only prevent closing if we're not ignoring contactors + datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first + } + return true; } diff --git a/Software/src/inverter/SOLAX-CAN.h b/Software/src/inverter/SOLAX-CAN.h index 0a3d8efa..6a67e8bf 100644 --- a/Software/src/inverter/SOLAX-CAN.h +++ b/Software/src/inverter/SOLAX-CAN.h @@ -17,6 +17,11 @@ class SolaxInverter : public CanInverterProtocol { static constexpr const char* Name = "SolaX Triple Power LFP over CAN bus"; private: + static const int NUMBER_OF_MODULES = 0; + static const int BATTERY_TYPE = 0x50; + // If you are having BattVoltFault issues, configure the above values according to wiki page + // https://github.com/dalathegreat/Battery-Emulator/wiki/Solax-inverters + // Timeout in milliseconds static const int SolaxTimeout = 2000; @@ -34,6 +39,13 @@ class SolaxInverter : public CanInverterProtocol { uint16_t capped_capacity_Wh; uint16_t capped_remaining_capacity_Wh; + int configured_number_of_modules = 0; + int configured_battery_type = 0; + // If true, the integration will ignore the inverter's requests to open the + // battery contactors. Useful for batteries that can't open contactors on + // request. + bool configured_ignore_contactors = false; + //CAN message translations from this amazing repository: https://github.com/rand12345/solax_can_bus CAN_frame SOLAX_1801 = {.FD = false, From 0f647978fa54522fcb7047dca1f57addde6022ce Mon Sep 17 00:00:00 2001 From: Fredrik Date: Wed, 20 Aug 2025 11:23:22 +0200 Subject: [PATCH 06/13] Send correct data in 441 message --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 25 +++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index db969772..233b98d4 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -136,6 +136,16 @@ uint16_t estimateSOCstandard(uint16_t packVoltage) { // Linear interpolation fu return 0; // Default return for safety, should never reach here } +uint8_t compute441Checksum(const uint8_t* u8) // Computes the 441 checksum byte +{ + int sum = 0; + for (int i = 0; i < 7; ++i) { + sum += u8[i]; + } + uint8_t lsb = static_cast(sum & 0xFF); + return static_cast(~lsb & 0xFF); +} + void BydAttoBattery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus @@ -532,10 +542,17 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) { } if (counter_100ms > 3) { - ATTO_3_441.data.u8[4] = 0x9D; - ATTO_3_441.data.u8[5] = 0x01; - ATTO_3_441.data.u8[6] = 0xFF; - ATTO_3_441.data.u8[7] = 0xF5; + if (battery_voltage > 0){ + ATTO_3_441.data.u8[4] = (uint8_t)(battery_voltage - 1); + ATTO_3_441.data.u8[5] = ((battery_voltage - 1) >> 8); + ATTO_3_441.data.u8[6] = 0xFF; + ATTO_3_441.data.u8[7] = compute441Checksum(ATTO_3_441.data.u8); + } else { + ATTO_3_441.data.u8[4] = 0x0C; + ATTO_3_441.data.u8[5] = 0x00; + ATTO_3_441.data.u8[6] = 0xFF; + ATTO_3_441.data.u8[7] = 0x87; + } } transmit_can_frame(&ATTO_3_441); From b349482f20486c74ca21ec15fbc8b40d18130e81 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 10:27:01 +0000 Subject: [PATCH 07/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 233b98d4..cf13aa26 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -136,14 +136,14 @@ uint16_t estimateSOCstandard(uint16_t packVoltage) { // Linear interpolation fu return 0; // Default return for safety, should never reach here } -uint8_t compute441Checksum(const uint8_t* u8) // Computes the 441 checksum byte +uint8_t compute441Checksum(const uint8_t* u8) // Computes the 441 checksum byte { - int sum = 0; - for (int i = 0; i < 7; ++i) { - sum += u8[i]; - } - uint8_t lsb = static_cast(sum & 0xFF); - return static_cast(~lsb & 0xFF); + int sum = 0; + for (int i = 0; i < 7; ++i) { + sum += u8[i]; + } + uint8_t lsb = static_cast(sum & 0xFF); + return static_cast(~lsb & 0xFF); } void BydAttoBattery:: @@ -542,7 +542,7 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) { } if (counter_100ms > 3) { - if (battery_voltage > 0){ + if (battery_voltage > 0) { ATTO_3_441.data.u8[4] = (uint8_t)(battery_voltage - 1); ATTO_3_441.data.u8[5] = ((battery_voltage - 1) >> 8); ATTO_3_441.data.u8[6] = 0xFF; From 3853236023e32b9eccdbd6caa733605887e135d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 21 Aug 2025 19:59:16 +0300 Subject: [PATCH 08/13] Add support for 64kWh FD Kia battery --- Software/USER_SETTINGS.h | 1 + Software/src/battery/BATTERIES.cpp | 4 + Software/src/battery/BATTERIES.h | 1 + Software/src/battery/Battery.h | 1 + Software/src/battery/KIA-64FD-BATTERY.cpp | 423 ++++++++++++++ Software/src/battery/KIA-64FD-BATTERY.h | 635 ++++++++++++++++++++++ Software/src/battery/KIA-E-GMP-BATTERY.h | 2 - 7 files changed, 1065 insertions(+), 2 deletions(-) create mode 100644 Software/src/battery/KIA-64FD-BATTERY.cpp create mode 100644 Software/src/battery/KIA-64FD-BATTERY.h diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index a1d246fa..ff6ccfe0 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -25,6 +25,7 @@ //#define JAGUAR_IPACE_BATTERY //#define KIA_E_GMP_BATTERY //#define KIA_HYUNDAI_64_BATTERY +//#define KIA_HYUNDAI_64_FD_BATTERY //#define KIA_HYUNDAI_HYBRID_BATTERY //#define MEB_BATTERY //#define MG_5_BATTERY diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index 1aa07bc6..247e61e5 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -90,6 +90,8 @@ const char* name_for_battery_type(BatteryType type) { return KiaEGmpBattery::Name; case BatteryType::KiaHyundai64: return KiaHyundai64Battery::Name; + case BatteryType::Kia64FD: + return Kia64FDBattery::Name; case BatteryType::KiaHyundaiHybrid: return KiaHyundaiHybridBattery::Name; case BatteryType::Meb: @@ -187,6 +189,8 @@ Battery* create_battery(BatteryType type) { return new ImievCZeroIonBattery(); case BatteryType::JaguarIpace: return new JaguarIpaceBattery(); + case BatteryType::Kia64FD: + return new Kia64FDBattery(); case BatteryType::KiaEGmp: return new KiaEGmpBattery(); case BatteryType::KiaHyundai64: diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 3e89149c..5c5539a6 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -29,6 +29,7 @@ void setup_can_shunt(); #include "HYUNDAI-IONIQ-28-BATTERY.h" #include "IMIEV-CZERO-ION-BATTERY.h" #include "JAGUAR-IPACE-BATTERY.h" +#include "KIA-64FD-BATTERY.h" #include "KIA-E-GMP-BATTERY.h" #include "KIA-HYUNDAI-64-BATTERY.h" #include "KIA-HYUNDAI-HYBRID-BATTERY.h" diff --git a/Software/src/battery/Battery.h b/Software/src/battery/Battery.h index 2b163a95..d22fcb93 100644 --- a/Software/src/battery/Battery.h +++ b/Software/src/battery/Battery.h @@ -45,6 +45,7 @@ enum class BatteryType { MgHsPhev = 37, SamsungSdiLv = 38, HyundaiIoniq28 = 39, + Kia64FD = 40, Highest }; diff --git a/Software/src/battery/KIA-64FD-BATTERY.cpp b/Software/src/battery/KIA-64FD-BATTERY.cpp new file mode 100644 index 00000000..032b7c31 --- /dev/null +++ b/Software/src/battery/KIA-64FD-BATTERY.cpp @@ -0,0 +1,423 @@ +#include "KIA-64FD-BATTERY.h" +#include "../communication/can/comm_can.h" +#include "../datalayer/datalayer.h" +#include "../devboard/utils/events.h" +#include "../devboard/utils/logging.h" +#include "../system_settings.h" + +// Function to estimate SOC based on cell voltage +uint16_t Kia64FDBattery::estimateSOCFromCell(uint16_t cellVoltage) { + if (cellVoltage >= voltage[0]) { + return SOC[0]; + } + if (cellVoltage <= voltage[numPoints - 1]) { + return SOC[numPoints - 1]; + } + + for (int i = 1; i < numPoints; ++i) { + if (cellVoltage >= voltage[i]) { + // Cast to float for proper division + float t = (float)(cellVoltage - voltage[i]) / (float)(voltage[i - 1] - voltage[i]); + + // Calculate interpolated SOC value + uint16_t socDiff = SOC[i - 1] - SOC[i]; + uint16_t interpolatedValue = SOC[i] + (uint16_t)(t * socDiff); + + return interpolatedValue; + } + } + return 0; // Default return for safety, should never reach here +} + +// Simplified version of the pack-based SOC estimation with compensation +uint16_t Kia64FDBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps) { + + if (cellCount == 0) { + return 0; + } + + // Convert pack voltage (decivolts) to millivolts + uint32_t packVoltageMv = packVoltage * 100; + + // Apply internal resistance compensation + // Current is in deciamps (-150 = -15.0A, 150 = 15.0A) + // Resistance is in milliohms + int32_t voltageDrop = (currentAmps * PACK_INTERNAL_RESISTANCE_MOHM) / 10; + + // Compensate the pack voltage (add the voltage drop) + uint32_t compensatedPackVoltageMv = packVoltageMv + voltageDrop; + + // Calculate average cell voltage in millivolts + uint16_t avgCellVoltage = compensatedPackVoltageMv / cellCount; + + // Use the cell voltage lookup table to estimate SOC + return estimateSOCFromCell(avgCellVoltage); +} + +// Fix: Change parameter types to uint16_t to match SOC values +uint16_t Kia64FDBattery::selectSOC(uint16_t SOC_low, uint16_t SOC_high) { + if (SOC_low == 0 || SOC_high == 0) { + return 0; // If either value is 0, return 0 + } + if (SOC_low == 10000 || SOC_high == 10000) { + return 10000; // If either value is 100%, return 100% + } + return (SOC_low < SOC_high) ? SOC_low : SOC_high; // Otherwise, return the lowest value +} + +void write_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) { + for (size_t i = 0; i < length; i++) { + if ((rx_frame.data.u8[start + i] * 20) > 1000) { + datalayer.battery.status.cell_voltages_mV[startCell + i] = (rx_frame.data.u8[start + i] * 20); + } + } +} + +uint8_t Kia64FDBattery::calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value) { + uint8_t crc = initial_value; + for (uint8_t j = 1; j < length; j++) { //start at 1, since 0 is the CRC + crc = crc8_table[(crc ^ static_cast(rx_frame.data.u8[j])) % 256]; + } + return crc; +} + +void Kia64FDBattery::update_values() { + +#ifdef ESTIMATE_SOC_FROM_CELLVOLTAGE + // Use the simplified pack-based SOC estimation with proper compensation + datalayer.battery.status.real_soc = estimateSOC(batteryVoltage, datalayer.battery.info.number_of_cells, batteryAmps); + + // For comparison or fallback, we can still calculate from min/max cell voltages + SOC_estimated_lowest = estimateSOCFromCell(CellVoltMin_mV); + SOC_estimated_highest = estimateSOCFromCell(CellVoltMax_mV); +#else + datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00 +#endif + + datalayer.battery.status.soh_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00% + + datalayer.battery.status.voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0) + + datalayer.battery.status.current_dA = -batteryAmps; //value is *10 (150 = 15.0) + + datalayer.battery.status.remaining_capacity_Wh = static_cast( + (static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh); + + //datalayer.battery.status.max_charge_power_W = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts + //The allowed charge power is not available. We estimate this value for now + if (datalayer.battery.status.real_soc > 9900) { + datalayer.battery.status.max_charge_power_W = 0; + } else if (datalayer.battery.status.real_soc > + RAMPDOWN_SOC) { // When real SOC is between 90-99%, ramp the value between Max<->0 + datalayer.battery.status.max_charge_power_W = + RAMPDOWNPOWERALLOWED * (1 - (datalayer.battery.status.real_soc - RAMPDOWN_SOC) / (10000.0 - RAMPDOWN_SOC)); + } else { // No limits, max charging power allowed + datalayer.battery.status.max_charge_power_W = MAXCHARGEPOWERALLOWED; + } + + //datalayer.battery.status.max_discharge_power_W = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts + //The allowed discharge power is not available. We hardcode this value for now + datalayer.battery.status.max_discharge_power_W = MAXDISCHARGEPOWERALLOWED; + + datalayer.battery.status.temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C + + datalayer.battery.status.temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C + + datalayer.battery.status.cell_max_voltage_mV = CellVoltMax_mV; + + datalayer.battery.status.cell_min_voltage_mV = CellVoltMin_mV; + + if (leadAcidBatteryVoltage < 110) { + set_event(EVENT_12V_LOW, leadAcidBatteryVoltage); + } +} + +void Kia64FDBattery::handle_incoming_can_frame(CAN_frame rx_frame) { + startedUp = true; + switch (rx_frame.ID) { + case 0x055: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x150: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x1F5: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x215: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x21A: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x235: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x245: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x25A: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x275: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x2FA: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x325: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x330: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x335: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x360: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x365: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x3BA: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x3F5: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x7EC: + // print_canfd_frame(frame); + switch (rx_frame.data.u8[0]) { + case 0x10: //"PID Header" + // logging.println ("Send ack"); + poll_data_pid = rx_frame.data.u8[4]; + // if (rx_frame.data.u8[4] == poll_data_pid) { + transmit_can_frame(&KIA64FD_ack); //Send ack to BMS if the same frame is sent as polled + // } + break; + case 0x21: //First frame in PID group + if (poll_data_pid == 1) { + allowedChargePower = ((rx_frame.data.u8[3] << 8) + rx_frame.data.u8[4]); + allowedDischargePower = ((rx_frame.data.u8[5] << 8) + rx_frame.data.u8[6]); + SOC_BMS = rx_frame.data.u8[2] * 5; //100% = 200 ( 200 * 5 = 1000 ) + + } else if (poll_data_pid == 2) { + // set cell voltages data, start bite, data length from start, start cell + write_cell_voltages(rx_frame, 2, 6, 0); + } else if (poll_data_pid == 3) { + write_cell_voltages(rx_frame, 2, 6, 32); + } else if (poll_data_pid == 4) { + write_cell_voltages(rx_frame, 2, 6, 64); + } else if (poll_data_pid == 0x0A) { + write_cell_voltages(rx_frame, 2, 6, 96); + } else if (poll_data_pid == 0x0B) { + write_cell_voltages(rx_frame, 2, 6, 128); + } else if (poll_data_pid == 0x0C) { + write_cell_voltages(rx_frame, 2, 6, 160); + } + break; + case 0x22: //Second datarow in PID group + if (poll_data_pid == 1) { + batteryVoltage = (rx_frame.data.u8[3] << 8) + rx_frame.data.u8[4]; + batteryAmps = (rx_frame.data.u8[1] << 8) + rx_frame.data.u8[2]; + temperatureMax = rx_frame.data.u8[5]; + temperatureMin = rx_frame.data.u8[6]; + // temp1 = rx_frame.data.u8[7]; + } else if (poll_data_pid == 2) { + write_cell_voltages(rx_frame, 1, 7, 6); + } else if (poll_data_pid == 3) { + write_cell_voltages(rx_frame, 1, 7, 38); + } else if (poll_data_pid == 4) { + write_cell_voltages(rx_frame, 1, 7, 70); + } else if (poll_data_pid == 0x0A) { + write_cell_voltages(rx_frame, 1, 7, 102); + } else if (poll_data_pid == 0x0B) { + write_cell_voltages(rx_frame, 1, 7, 134); + } else if (poll_data_pid == 0x0C) { + write_cell_voltages(rx_frame, 1, 7, 166); + } else if (poll_data_pid == 6) { + batteryManagementMode = rx_frame.data.u8[5]; + } + break; + case 0x23: //Third datarow in PID group + if (poll_data_pid == 1) { + temperature_water_inlet = rx_frame.data.u8[6]; + CellVoltMax_mV = (rx_frame.data.u8[7] * 20); //(volts *50) *20 =mV + // temp2 = rx_frame.data.u8[1]; + // temp3 = rx_frame.data.u8[2]; + // temp4 = rx_frame.data.u8[3]; + } else if (poll_data_pid == 2) { + write_cell_voltages(rx_frame, 1, 7, 13); + } else if (poll_data_pid == 3) { + write_cell_voltages(rx_frame, 1, 7, 45); + } else if (poll_data_pid == 4) { + write_cell_voltages(rx_frame, 1, 7, 77); + } else if (poll_data_pid == 0x0A) { + write_cell_voltages(rx_frame, 1, 7, 109); + } else if (poll_data_pid == 0x0B) { + write_cell_voltages(rx_frame, 1, 7, 141); + } else if (poll_data_pid == 0x0C) { + write_cell_voltages(rx_frame, 1, 7, 173); + } else if (poll_data_pid == 5) { + // ac = rx_frame.data.u8[3]; + // Vdiff = rx_frame.data.u8[4]; + + // airbag = rx_frame.data.u8[6]; + heatertemp = rx_frame.data.u8[7]; + } + break; + case 0x24: //Fourth datarow in PID group + if (poll_data_pid == 1) { + CellVmaxNo = rx_frame.data.u8[1]; + CellVoltMin_mV = (rx_frame.data.u8[2] * 20); //(volts *50) *20 =mV + CellVminNo = rx_frame.data.u8[3]; + // fanMod = rx_frame.data.u8[4]; + // fanSpeed = rx_frame.data.u8[5]; + leadAcidBatteryVoltage = rx_frame.data.u8[6]; //12v Battery Volts + //cumulative_charge_current[0] = rx_frame.data.u8[7]; + } else if (poll_data_pid == 2) { + write_cell_voltages(rx_frame, 1, 7, 20); + } else if (poll_data_pid == 3) { + write_cell_voltages(rx_frame, 1, 7, 52); + } else if (poll_data_pid == 4) { + write_cell_voltages(rx_frame, 1, 7, 84); + } else if (poll_data_pid == 0x0A) { + write_cell_voltages(rx_frame, 1, 7, 116); + } else if (poll_data_pid == 0x0B) { + write_cell_voltages(rx_frame, 1, 7, 148); + } else if (poll_data_pid == 0x0C) { + write_cell_voltages(rx_frame, 1, 7, 180); + } else if (poll_data_pid == 5) { + batterySOH = ((rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3]); + // maxDetCell = rx_frame.data.u8[4]; + // minDet = (rx_frame.data.u8[5] << 8) + rx_frame.data.u8[6]; + // minDetCell = rx_frame.data.u8[7]; + } + break; + case 0x25: //Fifth datarow in PID group + if (poll_data_pid == 1) { + //cumulative_charge_current[1] = rx_frame.data.u8[1]; + //cumulative_charge_current[2] = rx_frame.data.u8[2]; + //cumulative_charge_current[3] = rx_frame.data.u8[3]; + //cumulative_discharge_current[0] = rx_frame.data.u8[4]; + //cumulative_discharge_current[1] = rx_frame.data.u8[5]; + //cumulative_discharge_current[2] = rx_frame.data.u8[6]; + //cumulative_discharge_current[3] = rx_frame.data.u8[7]; + //set_cumulative_charge_current(); + //set_cumulative_discharge_current(); + } else if (poll_data_pid == 2) { + write_cell_voltages(rx_frame, 1, 5, 27); + } else if (poll_data_pid == 3) { + write_cell_voltages(rx_frame, 1, 5, 59); + } else if (poll_data_pid == 4) { + write_cell_voltages(rx_frame, 1, 5, 91); + } else if (poll_data_pid == 0x0A) { + write_cell_voltages(rx_frame, 1, 5, 123); + } else if (poll_data_pid == 0x0B) { + write_cell_voltages(rx_frame, 1, 5, 155); + } else if (poll_data_pid == 0x0C) { + write_cell_voltages(rx_frame, 1, 5, 187); + //set_cell_count(); + } else if (poll_data_pid == 5) { + // datalayer.battery.info.number_of_cells = 98; + SOC_Display = rx_frame.data.u8[1] * 5; + } + break; + case 0x26: //Sixth datarow in PID group + if (poll_data_pid == 1) { + //cumulative_energy_charged[0] = rx_frame.data.u8[1]; + // cumulative_energy_charged[1] = rx_frame.data.u8[2]; + //cumulative_energy_charged[2] = rx_frame.data.u8[3]; + //cumulative_energy_charged[3] = rx_frame.data.u8[4]; + //cumulative_energy_discharged[0] = rx_frame.data.u8[5]; + //cumulative_energy_discharged[1] = rx_frame.data.u8[6]; + //cumulative_energy_discharged[2] = rx_frame.data.u8[7]; + // set_cumulative_energy_charged(); + } + break; + case 0x27: //Seventh datarow in PID group + if (poll_data_pid == 1) { + //cumulative_energy_discharged[3] = rx_frame.data.u8[1]; + + //opTimeBytes[0] = rx_frame.data.u8[2]; + //opTimeBytes[1] = rx_frame.data.u8[3]; + //opTimeBytes[2] = rx_frame.data.u8[4]; + //opTimeBytes[3] = rx_frame.data.u8[5]; + + BMS_ign = rx_frame.data.u8[6]; + inverterVoltageFrameHigh = rx_frame.data.u8[7]; // BMS Capacitoir + + // set_cumulative_energy_discharged(); + // set_opTime(); + } + break; + case 0x28: //Eighth datarow in PID group + if (poll_data_pid == 1) { + inverterVoltage = (inverterVoltageFrameHigh << 8) + rx_frame.data.u8[1]; // BMS Capacitoir + } + break; + } + break; + default: + break; + } +} + +void Kia64FDBattery::transmit_can(unsigned long currentMillis) { + if (startedUp) { + //Send Contactor closing message loop + // Check if we still have messages to send + if (messageIndex < sizeof(messageDelays) / sizeof(messageDelays[0])) { + + // Check if it's time to send the next message + if (currentMillis - startMillis >= messageDelays[messageIndex]) { + + // Transmit the current message + transmit_can_frame(messages[messageIndex]); + + // Move to the next message + messageIndex++; + } + } + + if (messageIndex >= 63) { + startMillis = currentMillis; // Start over! + messageIndex = 0; + } + + //Send 200ms CANFD message + if (currentMillis - previousMillis200ms >= INTERVAL_200_MS) { + previousMillis200ms = currentMillis; + + KIA64FD_7E4.data.u8[3] = KIA_7E4_COUNTER; + + if (ok_start_polling_battery) { + transmit_can_frame(&KIA64FD_7E4); + } + + KIA_7E4_COUNTER++; + if (KIA_7E4_COUNTER > 0x0D) { // gets up to 0x010C before repeating + KIA_7E4_COUNTER = 0x01; + } + } + //Send 10s CANFD message + if (currentMillis - previousMillis10s >= INTERVAL_10_S) { + previousMillis10s = currentMillis; + + ok_start_polling_battery = true; + } + } +} + +void Kia64FDBattery::setup(void) { // Performs one time setup at startup + strncpy(datalayer.system.info.battery_protocol, Name, 63); + datalayer.system.info.battery_protocol[63] = '\0'; + datalayer.system.status.battery_allows_contactor_closing = true; + datalayer.battery.info.number_of_cells = 96; + datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; + datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; + datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; + datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; +} diff --git a/Software/src/battery/KIA-64FD-BATTERY.h b/Software/src/battery/KIA-64FD-BATTERY.h new file mode 100644 index 00000000..2b2806c9 --- /dev/null +++ b/Software/src/battery/KIA-64FD-BATTERY.h @@ -0,0 +1,635 @@ +#ifndef KIA_64_FD_BATTERY_H +#define KIA_64_FD_BATTERY_H +#include +#include "CanBattery.h" + +#define ESTIMATE_SOC_FROM_CELLVOLTAGE + +#ifdef KIA_HYUNDAI_64_FD_BATTERY +#define SELECTED_BATTERY_CLASS Kia64FDBattery +#endif + +class Kia64FDBattery : public CanBattery { + public: + virtual void setup(void); + virtual void handle_incoming_can_frame(CAN_frame rx_frame); + virtual void update_values(); + virtual void transmit_can(unsigned long currentMillis); + static constexpr const char* Name = "Kia 64kWh FD battery"; + + private: + uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps); + uint16_t estimateSOCFromCell(uint16_t cellVoltage); + uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value); + uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high); + + static const int MAX_PACK_VOLTAGE_DV = 4032; //5000 = 500.0V + static const int MIN_PACK_VOLTAGE_DV = 2400; + static const int MAX_CELL_DEVIATION_MV = 150; + static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value + static const int MIN_CELL_VOLTAGE_MV = 2950; //Battery is put into emergency stop if one cell goes below this value + static const int MAXCHARGEPOWERALLOWED = 10000; + static const int MAXDISCHARGEPOWERALLOWED = 10000; + static const int RAMPDOWN_SOC = 9000; // 90.00 SOC% to start ramping down from max charge power towards 0 at 100.00% + static const int RAMPDOWNPOWERALLOWED = 10000; // What power we ramp down from towards top balancing + + // Used for SoC compensation - Define internal resistance value in milliohms for the entire pack + // How to calculate: voltage_drop_under_known_load [Volts] / load [Amps] = Resistance + static const int PACK_INTERNAL_RESISTANCE_MOHM = 200; // 200 milliohms for the whole pack + + unsigned long previousMillis200ms = 0; // will store last time a 200ms CAN Message was send + unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send + + uint16_t inverterVoltageFrameHigh = 0; + uint16_t inverterVoltage = 0; + uint16_t soc_calculated = 0; + uint16_t SOC_BMS = 0; + uint16_t SOC_Display = 0; + uint16_t SOC_estimated_lowest = 0; + uint16_t SOC_estimated_highest = 0; + uint16_t batterySOH = 1000; + uint16_t CellVoltMax_mV = 3700; + uint16_t CellVoltMin_mV = 3700; + uint16_t batteryVoltage = 0; + int16_t leadAcidBatteryVoltage = 120; + int16_t batteryAmps = 0; + int16_t temperatureMax = 0; + int16_t temperatureMin = 0; + int16_t allowedDischargePower = 0; + int16_t allowedChargePower = 0; + int16_t poll_data_pid = 0; + uint8_t CellVmaxNo = 0; + uint8_t CellVminNo = 0; + uint8_t batteryManagementMode = 0; + uint8_t BMS_ign = 0; + bool startedUp = false; + bool ok_start_polling_battery = false; + uint8_t KIA_7E4_COUNTER = 0x01; + int8_t temperature_water_inlet = 0; + int8_t heatertemp = 0; + unsigned long startMillis = 0; + uint8_t messageIndex = 0; + + const unsigned char crc8_table[256] = { + // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies + 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0, 0xF7, + 0xEA, 0xB9, 0xA4, 0x83, 0x9E, 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, + 0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19, 0xA2, + 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40, 0xFB, 0xE6, 0xC1, 0xDC, + 0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, + 0x65, 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F, 0x59, 0x44, + 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01, 0x52, + 0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D, 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, + 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, 0x49, 0x54, 0x73, + 0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, + 0xCA, 0xD7, 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, 0xF8, + 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB, 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95, + 0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, + 0x2C, 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4}; + + // Define the data points for %SOC depending on cell voltage + const uint8_t numPoints = 100; + + const uint16_t SOC[101] = {10000, 9900, 9800, 9700, 9600, 9500, 9400, 9300, 9200, 9100, 9000, 8900, 8800, 8700, 8600, + 8500, 8400, 8300, 8200, 8100, 8000, 7900, 7800, 7700, 7600, 7500, 7400, 7300, 7200, 7100, + 7000, 6900, 6800, 6700, 6600, 6500, 6400, 6300, 6200, 6100, 6000, 5900, 5800, 5700, 5600, + 5500, 5400, 5300, 5200, 5100, 5000, 4900, 4800, 4700, 4600, 4500, 4400, 4300, 4200, 4100, + 4000, 3900, 3800, 3700, 3600, 3500, 3400, 3300, 3200, 3100, 3000, 2900, 2800, 2700, 2600, + 2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700, 1600, 1500, 1400, 1300, 1200, 1100, + 1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 0}; + + const uint16_t voltage[101] = { + 4200, 4173, 4148, 4124, 4102, 4080, 4060, 4041, 4023, 4007, 3993, 3980, 3969, 3959, 3953, 3950, 3941, + 3932, 3924, 3915, 3907, 3898, 3890, 3881, 3872, 3864, 3855, 3847, 3838, 3830, 3821, 3812, 3804, 3795, + 3787, 3778, 3770, 3761, 3752, 3744, 3735, 3727, 3718, 3710, 3701, 3692, 3684, 3675, 3667, 3658, 3650, + 3641, 3632, 3624, 3615, 3607, 3598, 3590, 3581, 3572, 3564, 3555, 3547, 3538, 3530, 3521, 3512, 3504, + 3495, 3487, 3478, 3470, 3461, 3452, 3444, 3435, 3427, 3418, 3410, 3401, 3392, 3384, 3375, 3367, 3358, + 3350, 3338, 3325, 3313, 3299, 3285, 3271, 3255, 3239, 3221, 3202, 3180, 3156, 3127, 3090, 3000}; + /* These messages are needed for contactor closing */ + uint8_t messageDelays[63] = {0, 0, 5, 10, 10, 15, 19, 19, 20, 20, 25, 30, 30, 35, 40, 40, + 45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70, + 70, 75, 77, 77, 80, 80, 85, 90, 90, 95, 100, 100, 105, 110, 110, 115, + 119, 119, 120, 120, 125, 130, 130, 135, 140, 140, 145, 149, 149, 150, 150}; + static constexpr CAN_frame message_1 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x62, 0x36, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_2 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xd4, 0x1b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_3 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x24, 0x9b, 0x7b, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_4 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x24, 0x6f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_5 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x92, 0x42, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_6 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xd7, 0x05, 0x7c, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_7 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x30A, + .data = {0xb1, 0xe0, 0x26, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, + 0x4f, 0x06, 0x18, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; + + static constexpr CAN_frame message_8 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x320, + .data = {0xc6, 0xab, 0x26, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, + 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_9 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xee, 0x84, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_10 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x58, 0xa9, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_11 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x91, 0x5c, 0x7d, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_12 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xa8, 0xdd, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_13 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x1e, 0xf0, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_14 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x5b, 0xb7, 0x7e, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_15 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xec, 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_16 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x5a, 0x40, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_17 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x1d, 0xee, 0x7f, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_18 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2B5, + .data = {0xbd, 0xb2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, + 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_19 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E0, + .data = {0xc1, 0xf2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_20 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xaa, 0x34, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_21 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x1c, 0x19, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_22 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2D5, + .data = {0x79, 0xfb, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_23 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2EA, + .data = {0x6e, 0xbb, 0xa0, 0x0d, 0x04, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_24 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x306, + .data = {0x00, 0x00, 0x00, 0xd2, 0x06, 0x92, 0x05, 0x34, 0x07, 0x8e, 0x08, 0x73, 0x05, 0x80, 0x05, 0x83, + 0x05, 0x73, 0x05, 0x80, 0x05, 0xed, 0x01, 0xdd, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_25 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x308, + .data = {0xbe, 0x84, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x75, 0x6c, 0x86, 0x0d, 0xfb, 0xdf, 0x03, 0x36, 0xc3, 0x86, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_26 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x6b, 0xa2, 0x80, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_27 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x60, 0xdf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_28 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xd6, 0xf2, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_29 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x2d, 0xfb, 0x81, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_30 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x33A, + .data = {0x1a, 0x23, 0x26, 0x10, 0x27, 0x4f, 0x06, 0x00, 0xf8, 0x1b, 0x19, 0x04, 0x30, 0x01, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x2e, 0x2d, 0x81, 0x25, 0x20, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_31 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x350, + .data = {0x26, 0x82, 0x26, 0xf4, 0x01, 0x00, 0x00, 0x50, 0x90, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_32 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x26, 0x86, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_33 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x90, 0xab, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_34 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xe7, 0x10, 0x82, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_35 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E5, + .data = {0x69, 0x8a, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x15, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_36 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x3B5, + .data = {0xa3, 0xc8, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_37 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xd5, 0x18, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_38 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x63, 0x35, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_39 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xa1, 0x49, 0x83, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_40 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x93, 0x41, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_41 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x25, 0x6c, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_42 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x52, 0xd7, 0x84, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_43 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x59, 0xaa, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_44 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xef, 0x87, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_45 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x14, 0x8e, 0x85, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_46 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x1f, 0xf3, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_47 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xa9, 0xde, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_48 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xde, 0x65, 0x86, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_49 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x30A, + .data = {0xd3, 0x11, 0x27, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, + 0x4f, 0x06, 0x19, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; + + static constexpr CAN_frame message_50 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x320, + .data = {0x80, 0xf2, 0x27, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, + 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_51 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x9e, 0x87, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_52 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x28, 0xaa, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_53 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x98, 0x3c, 0x87, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_54 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xd8, 0xde, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_55 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x6e, 0xf3, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_56 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x19, 0x48, 0x88, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_57 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x12, 0x35, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_58 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xa4, 0x18, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_59 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x5f, 0x11, 0x89, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_60 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2B5, + .data = {0xfb, 0xeb, 0x43, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, + 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_61 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2C0, + .data = {0xcc, 0xcd, 0xa2, 0x21, 0x00, 0xa1, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_62 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E0, + .data = {0x87, 0xab, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_63 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x54, 0x6c, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + const CAN_frame* messages[64] = { + &message_1, &message_2, &message_3, &message_4, &message_5, &message_6, &message_7, &message_8, + &message_9, &message_10, &message_11, &message_12, &message_13, &message_14, &message_15, &message_16, + &message_17, &message_18, &message_19, &message_20, &message_21, &message_22, &message_23, &message_24, + &message_25, &message_26, &message_27, &message_28, &message_29, &message_30, &message_31, &message_32, + &message_33, &message_34, &message_35, &message_36, &message_37, &message_38, &message_39, &message_40, + &message_41, &message_42, &message_43, &message_44, &message_45, &message_46, &message_47, &message_48, + &message_49, &message_50, &message_51, &message_52, &message_53, &message_54, &message_55, &message_56, + &message_57, &message_58, &message_59, &message_60, &message_61, &message_62, &message_63}; + + /* PID polling messages */ + CAN_frame KIA64FD_7E4 = {.FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x7E4, + .data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 01 + CAN_frame KIA64FD_ack = { + .FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x7E4, + .data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned +}; + +#endif diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.h b/Software/src/battery/KIA-E-GMP-BATTERY.h index bf1c769e..b807db4c 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.h +++ b/Software/src/battery/KIA-E-GMP-BATTERY.h @@ -82,8 +82,6 @@ class KiaEGmpBattery : public CanBattery { int8_t heatertemp = 20; bool set_voltage_limits = false; uint8_t ticks_200ms_counter = 0; - uint8_t EGMP_1CF_counter = 0; - uint8_t EGMP_3XF_counter = 0; }; #endif From 2888402e5de4da553895b0279fc350831fd74c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 21 Aug 2025 20:06:48 +0300 Subject: [PATCH 09/13] Move variables to private section --- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 575 +-------------------- Software/src/battery/KIA-E-GMP-BATTERY.h | 509 +++++++++++++++++- 2 files changed, 513 insertions(+), 571 deletions(-) diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index 9a91a760..a4c8bd95 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -6,45 +6,8 @@ #include "../devboard/utils/logging.h" #include "../system_settings.h" -const unsigned char crc8_table[256] = - { // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies - 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0, - 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E, 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0, - 0xF3, 0xEE, 0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, - 0x04, 0x19, 0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40, - 0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B, - 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65, 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B, - 0x08, 0x15, 0x32, 0x2F, 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, - 0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D, - 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC, - 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, 0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B, - 0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7, 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, - 0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB, - 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47, - 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C, 0x97, 0x8A, 0xAD, 0xB0, - 0xE3, 0xFE, 0xD9, 0xC4}; - -// Define the data points for %SOC depending on cell voltage -const uint8_t numPoints = 100; - -const uint16_t SOC[] = {10000, 9900, 9800, 9700, 9600, 9500, 9400, 9300, 9200, 9100, 9000, 8900, 8800, 8700, 8600, - 8500, 8400, 8300, 8200, 8100, 8000, 7900, 7800, 7700, 7600, 7500, 7400, 7300, 7200, 7100, - 7000, 6900, 6800, 6700, 6600, 6500, 6400, 6300, 6200, 6100, 6000, 5900, 5800, 5700, 5600, - 5500, 5400, 5300, 5200, 5100, 5000, 4900, 4800, 4700, 4600, 4500, 4400, 4300, 4200, 4100, - 4000, 3900, 3800, 3700, 3600, 3500, 3400, 3300, 3200, 3100, 3000, 2900, 2800, 2700, 2600, - 2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700, 1600, 1500, 1400, 1300, 1200, 1100, - 1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 0}; - -const uint16_t voltage[] = {4200, 4173, 4148, 4124, 4102, 4080, 4060, 4041, 4023, 4007, 3993, 3980, 3969, 3959, 3953, - 3950, 3941, 3932, 3924, 3915, 3907, 3898, 3890, 3881, 3872, 3864, 3855, 3847, 3838, 3830, - 3821, 3812, 3804, 3795, 3787, 3778, 3770, 3761, 3752, 3744, 3735, 3727, 3718, 3710, 3701, - 3692, 3684, 3675, 3667, 3658, 3650, 3641, 3632, 3624, 3615, 3607, 3598, 3590, 3581, 3572, - 3564, 3555, 3547, 3538, 3530, 3521, 3512, 3504, 3495, 3487, 3478, 3470, 3461, 3452, 3444, - 3435, 3427, 3418, 3410, 3401, 3392, 3384, 3375, 3367, 3358, 3350, 3338, 3325, 3313, 3299, - 3285, 3271, 3255, 3239, 3221, 3202, 3180, 3156, 3127, 3090, 3000}; - // Function to estimate SOC based on cell voltage -uint16_t estimateSOCFromCell(uint16_t cellVoltage) { +uint16_t KiaEGmpBattery::estimateSOCFromCell(uint16_t cellVoltage) { if (cellVoltage >= voltage[0]) { return SOC[0]; } @@ -111,7 +74,7 @@ uint16_t KiaEGmpBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, i } // Fix: Change parameter types to uint16_t to match SOC values -uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high) { +uint16_t KiaEGmpBattery::selectSOC(uint16_t SOC_low, uint16_t SOC_high) { if (SOC_low == 0 || SOC_high == 0) { return 0; // If either value is 0, return 0 } @@ -121,534 +84,7 @@ uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high) { return (SOC_low < SOC_high) ? SOC_low : SOC_high; // Otherwise, return the lowest value } -/* These messages are needed for contactor closing */ -unsigned long startMillis = 0; -uint8_t messageIndex = 0; -uint8_t messageDelays[63] = {0, 0, 5, 10, 10, 15, 19, 19, 20, 20, 25, 30, 30, 35, 40, 40, - 45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70, - 70, 75, 77, 77, 80, 80, 85, 90, 90, 95, 100, 100, 105, 110, 110, 115, - 119, 119, 120, 120, 125, 130, 130, 135, 140, 140, 145, 149, 149, 150, 150}; -static constexpr CAN_frame message_1 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x62, 0x36, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_2 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0xd4, 0x1b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_3 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x24, 0x9b, 0x7b, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_4 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x24, 0x6f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_5 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x92, 0x42, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_6 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0xd7, 0x05, 0x7c, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_7 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x30A, - .data = {0xb1, 0xe0, 0x26, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, - 0x4f, 0x06, 0x18, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; - -static constexpr CAN_frame message_8 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x320, - .data = {0xc6, 0xab, 0x26, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, - 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_9 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xee, 0x84, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_10 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x58, 0xa9, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static constexpr CAN_frame message_11 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x91, 0x5c, 0x7d, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_12 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xa8, 0xdd, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_13 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x1e, 0xf0, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_14 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x5b, 0xb7, 0x7e, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_15 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xec, 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_16 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x5a, 0x40, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_17 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x1d, 0xee, 0x7f, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_18 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2B5, - .data = {0xbd, 0xb2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, - 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_19 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2E0, - .data = {0xc1, 0xf2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_20 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xaa, 0x34, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static constexpr CAN_frame message_21 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x1c, 0x19, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_22 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2D5, - .data = {0x79, 0xfb, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_23 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2EA, - .data = {0x6e, 0xbb, 0xa0, 0x0d, 0x04, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_24 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x306, - .data = {0x00, 0x00, 0x00, 0xd2, 0x06, 0x92, 0x05, 0x34, 0x07, 0x8e, 0x08, 0x73, 0x05, 0x80, 0x05, 0x83, - 0x05, 0x73, 0x05, 0x80, 0x05, 0xed, 0x01, 0xdd, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_25 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x308, - .data = {0xbe, 0x84, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, - 0x75, 0x6c, 0x86, 0x0d, 0xfb, 0xdf, 0x03, 0x36, 0xc3, 0x86, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_26 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x6b, 0xa2, 0x80, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_27 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x60, 0xdf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_28 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0xd6, 0xf2, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_29 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x2d, 0xfb, 0x81, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_30 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x33A, - .data = {0x1a, 0x23, 0x26, 0x10, 0x27, 0x4f, 0x06, 0x00, 0xf8, 0x1b, 0x19, 0x04, 0x30, 0x01, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x2e, 0x2d, 0x81, 0x25, 0x20, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static constexpr CAN_frame message_31 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x350, - .data = {0x26, 0x82, 0x26, 0xf4, 0x01, 0x00, 0x00, 0x50, 0x90, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_32 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x26, 0x86, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_33 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x90, 0xab, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_34 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0xe7, 0x10, 0x82, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_35 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2E5, - .data = {0x69, 0x8a, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x15, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_36 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x3B5, - .data = {0xa3, 0xc8, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_37 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xd5, 0x18, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_38 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x63, 0x35, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_39 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0xa1, 0x49, 0x83, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_40 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x93, 0x41, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static constexpr CAN_frame message_41 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x25, 0x6c, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_42 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x52, 0xd7, 0x84, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_43 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x59, 0xaa, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_44 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0xef, 0x87, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_45 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x14, 0x8e, 0x85, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_46 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x1f, 0xf3, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_47 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0xa9, 0xde, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_48 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0xde, 0x65, 0x86, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_49 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x30A, - .data = {0xd3, 0x11, 0x27, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, - 0x4f, 0x06, 0x19, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; - -static constexpr CAN_frame message_50 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x320, - .data = {0x80, 0xf2, 0x27, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, - 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static constexpr CAN_frame message_51 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x9e, 0x87, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_52 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x28, 0xaa, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_53 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x98, 0x3c, 0x87, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_54 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xd8, 0xde, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_55 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x6e, 0xf3, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_56 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x19, 0x48, 0x88, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_57 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x12, 0x35, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_58 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0xa4, 0x18, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_59 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x5f, 0x11, 0x89, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_60 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2B5, - .data = {0xfb, 0xeb, 0x43, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, - 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_61 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2C0, - .data = {0xcc, 0xcd, 0xa2, 0x21, 0x00, 0xa1, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_62 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2E0, - .data = {0x87, 0xab, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_63 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x54, 0x6c, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static const CAN_frame* messages[] = { - &message_1, &message_2, &message_3, &message_4, &message_5, &message_6, &message_7, &message_8, - &message_9, &message_10, &message_11, &message_12, &message_13, &message_14, &message_15, &message_16, - &message_17, &message_18, &message_19, &message_20, &message_21, &message_22, &message_23, &message_24, - &message_25, &message_26, &message_27, &message_28, &message_29, &message_30, &message_31, &message_32, - &message_33, &message_34, &message_35, &message_36, &message_37, &message_38, &message_39, &message_40, - &message_41, &message_42, &message_43, &message_44, &message_45, &message_46, &message_47, &message_48, - &message_49, &message_50, &message_51, &message_52, &message_53, &message_54, &message_55, &message_56, - &message_57, &message_58, &message_59, &message_60, &message_61, &message_62, &message_63}; -/* PID polling messages */ -CAN_frame EGMP_7E4 = {.FD = true, - .ext_ID = false, - .DLC = 8, - .ID = 0x7E4, - .data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 01 -static constexpr CAN_frame EGMP_7E4_ack = { - .FD = true, - .ext_ID = false, - .DLC = 8, - .ID = 0x7E4, - .data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned - -void set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) { +void KiaEGmpBattery::set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) { for (size_t i = 0; i < length; i++) { if ((rx_frame.data.u8[start + i] * 20) > 1000) { datalayer.battery.status.cell_voltages_mV[startCell + i] = (rx_frame.data.u8[start + i] * 20); @@ -682,7 +118,7 @@ void KiaEGmpBattery::set_voltage_minmax_limits() { } } -static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value) { +uint8_t KiaEGmpBattery::calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value) { uint8_t crc = initial_value; for (uint8_t j = 1; j < length; j++) { //start at 1, since 0 is the CRC crc = crc8_table[(crc ^ static_cast(rx_frame.data.u8[j])) % 256]; @@ -690,8 +126,7 @@ static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_ return crc; } -void KiaEGmpBattery:: - update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus +void KiaEGmpBattery::update_values() { #ifdef ESTIMATE_SOC_FROM_CELLVOLTAGE // Use the simplified pack-based SOC estimation with proper compensation diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.h b/Software/src/battery/KIA-E-GMP-BATTERY.h index b807db4c..461f7d4a 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.h +++ b/Software/src/battery/KIA-E-GMP-BATTERY.h @@ -30,6 +30,10 @@ class KiaEGmpBattery : public CanBattery { private: KiaEGMPHtmlRenderer renderer; uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps); + uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high); + uint16_t estimateSOCFromCell(uint16_t cellVoltage); + uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value); + void set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell); void set_voltage_minmax_limits(); static const int MAX_PACK_VOLTAGE_DV = 8064; //5000 = 500.0V @@ -81,7 +85,510 @@ class KiaEGmpBattery : public CanBattery { int8_t powerRelayTemperature = 10; int8_t heatertemp = 20; bool set_voltage_limits = false; - uint8_t ticks_200ms_counter = 0; + + const unsigned char crc8_table[256] = { + // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies + 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0, 0xF7, + 0xEA, 0xB9, 0xA4, 0x83, 0x9E, 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, + 0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19, 0xA2, + 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40, 0xFB, 0xE6, 0xC1, 0xDC, + 0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, + 0x65, 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F, 0x59, 0x44, + 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01, 0x52, + 0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D, 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, + 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, 0x49, 0x54, 0x73, + 0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, + 0xCA, 0xD7, 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, 0xF8, + 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB, 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95, + 0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, + 0x2C, 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4}; + // Define the data points for %SOC depending on cell voltage + const uint8_t numPoints = 100; + + const uint16_t SOC[101] = {10000, 9900, 9800, 9700, 9600, 9500, 9400, 9300, 9200, 9100, 9000, 8900, 8800, 8700, 8600, + 8500, 8400, 8300, 8200, 8100, 8000, 7900, 7800, 7700, 7600, 7500, 7400, 7300, 7200, 7100, + 7000, 6900, 6800, 6700, 6600, 6500, 6400, 6300, 6200, 6100, 6000, 5900, 5800, 5700, 5600, + 5500, 5400, 5300, 5200, 5100, 5000, 4900, 4800, 4700, 4600, 4500, 4400, 4300, 4200, 4100, + 4000, 3900, 3800, 3700, 3600, 3500, 3400, 3300, 3200, 3100, 3000, 2900, 2800, 2700, 2600, + 2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700, 1600, 1500, 1400, 1300, 1200, 1100, + 1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 0}; + + const uint16_t voltage[101] = { + 4200, 4173, 4148, 4124, 4102, 4080, 4060, 4041, 4023, 4007, 3993, 3980, 3969, 3959, 3953, 3950, 3941, + 3932, 3924, 3915, 3907, 3898, 3890, 3881, 3872, 3864, 3855, 3847, 3838, 3830, 3821, 3812, 3804, 3795, + 3787, 3778, 3770, 3761, 3752, 3744, 3735, 3727, 3718, 3710, 3701, 3692, 3684, 3675, 3667, 3658, 3650, + 3641, 3632, 3624, 3615, 3607, 3598, 3590, 3581, 3572, 3564, 3555, 3547, 3538, 3530, 3521, 3512, 3504, + 3495, 3487, 3478, 3470, 3461, 3452, 3444, 3435, 3427, 3418, 3410, 3401, 3392, 3384, 3375, 3367, 3358, + 3350, 3338, 3325, 3313, 3299, 3285, 3271, 3255, 3239, 3221, 3202, 3180, 3156, 3127, 3090, 3000}; + /* These messages are needed for contactor closing */ + unsigned long startMillis = 0; + uint8_t messageIndex = 0; + uint8_t messageDelays[63] = {0, 0, 5, 10, 10, 15, 19, 19, 20, 20, 25, 30, 30, 35, 40, 40, + 45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70, + 70, 75, 77, 77, 80, 80, 85, 90, 90, 95, 100, 100, 105, 110, 110, 115, + 119, 119, 120, 120, 125, 130, 130, 135, 140, 140, 145, 149, 149, 150, 150}; + static constexpr CAN_frame message_1 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x62, 0x36, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_2 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xd4, 0x1b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_3 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x24, 0x9b, 0x7b, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_4 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x24, 0x6f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_5 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x92, 0x42, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_6 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xd7, 0x05, 0x7c, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_7 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x30A, + .data = {0xb1, 0xe0, 0x26, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, + 0x4f, 0x06, 0x18, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; + static constexpr CAN_frame message_8 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x320, + .data = {0xc6, 0xab, 0x26, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, + 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_9 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xee, 0x84, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_10 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x58, 0xa9, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_11 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x91, 0x5c, 0x7d, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_12 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xa8, 0xdd, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_13 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x1e, 0xf0, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_14 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x5b, 0xb7, 0x7e, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_15 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xec, 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_16 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x5a, 0x40, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_17 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x1d, 0xee, 0x7f, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_18 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2B5, + .data = {0xbd, 0xb2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, + 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_19 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E0, + .data = {0xc1, 0xf2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_20 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xaa, 0x34, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_21 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x1c, 0x19, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_22 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2D5, + .data = {0x79, 0xfb, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_23 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2EA, + .data = {0x6e, 0xbb, 0xa0, 0x0d, 0x04, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_24 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x306, + .data = {0x00, 0x00, 0x00, 0xd2, 0x06, 0x92, 0x05, 0x34, 0x07, 0x8e, 0x08, 0x73, 0x05, 0x80, 0x05, 0x83, + 0x05, 0x73, 0x05, 0x80, 0x05, 0xed, 0x01, 0xdd, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_25 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x308, + .data = {0xbe, 0x84, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x75, 0x6c, 0x86, 0x0d, 0xfb, 0xdf, 0x03, 0x36, 0xc3, 0x86, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_26 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x6b, 0xa2, 0x80, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_27 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x60, 0xdf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_28 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xd6, 0xf2, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_29 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x2d, 0xfb, 0x81, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_30 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x33A, + .data = {0x1a, 0x23, 0x26, 0x10, 0x27, 0x4f, 0x06, 0x00, 0xf8, 0x1b, 0x19, 0x04, 0x30, 0x01, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x2e, 0x2d, 0x81, 0x25, 0x20, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_31 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x350, + .data = {0x26, 0x82, 0x26, 0xf4, 0x01, 0x00, 0x00, 0x50, 0x90, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_32 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x26, 0x86, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_33 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x90, 0xab, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_34 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xe7, 0x10, 0x82, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_35 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E5, + .data = {0x69, 0x8a, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x15, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_36 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x3B5, + .data = {0xa3, 0xc8, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_37 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xd5, 0x18, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_38 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x63, 0x35, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_39 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xa1, 0x49, 0x83, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_40 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x93, 0x41, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_41 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x25, 0x6c, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_42 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x52, 0xd7, 0x84, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_43 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x59, 0xaa, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_44 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xef, 0x87, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_45 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x14, 0x8e, 0x85, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_46 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x1f, 0xf3, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_47 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xa9, 0xde, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_48 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xde, 0x65, 0x86, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_49 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x30A, + .data = {0xd3, 0x11, 0x27, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, + 0x4f, 0x06, 0x19, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; + static constexpr CAN_frame message_50 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x320, + .data = {0x80, 0xf2, 0x27, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, + 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_51 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x9e, 0x87, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_52 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x28, 0xaa, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_53 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x98, 0x3c, 0x87, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_54 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xd8, 0xde, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_55 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x6e, 0xf3, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_56 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x19, 0x48, 0x88, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_57 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x12, 0x35, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_58 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xa4, 0x18, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_59 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x5f, 0x11, 0x89, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_60 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2B5, + .data = {0xfb, 0xeb, 0x43, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, + 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_61 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2C0, + .data = {0xcc, 0xcd, 0xa2, 0x21, 0x00, 0xa1, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_62 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E0, + .data = {0x87, 0xab, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_63 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x54, 0x6c, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + const CAN_frame* messages[63] = { + &message_1, &message_2, &message_3, &message_4, &message_5, &message_6, &message_7, &message_8, + &message_9, &message_10, &message_11, &message_12, &message_13, &message_14, &message_15, &message_16, + &message_17, &message_18, &message_19, &message_20, &message_21, &message_22, &message_23, &message_24, + &message_25, &message_26, &message_27, &message_28, &message_29, &message_30, &message_31, &message_32, + &message_33, &message_34, &message_35, &message_36, &message_37, &message_38, &message_39, &message_40, + &message_41, &message_42, &message_43, &message_44, &message_45, &message_46, &message_47, &message_48, + &message_49, &message_50, &message_51, &message_52, &message_53, &message_54, &message_55, &message_56, + &message_57, &message_58, &message_59, &message_60, &message_61, &message_62, &message_63}; + /* PID polling messages */ + CAN_frame EGMP_7E4 = {.FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x7E4, + .data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 01 + static constexpr CAN_frame EGMP_7E4_ack = { + .FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x7E4, + .data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned }; #endif From 100e7e6effc942cd22013e7ecd22f2b0779a78ae Mon Sep 17 00:00:00 2001 From: freddanastrom Date: Sat, 23 Aug 2025 14:33:51 +0200 Subject: [PATCH 10/13] Using a more robust way to check that voltage is available from BMS --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 3 ++- Software/src/battery/BYD-ATTO-3-BATTERY.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index cf13aa26..57acf348 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -393,6 +393,7 @@ void BydAttoBattery::handle_incoming_can_frame(CAN_frame rx_frame) { datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_voltage = ((rx_frame.data.u8[1] & 0x0F) << 8) | rx_frame.data.u8[0]; //battery_temperature_something = rx_frame.data.u8[7] - 40; resides in frame 7 + BMS_voltage_available = true; break; case 0x445: datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE; @@ -542,7 +543,7 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) { } if (counter_100ms > 3) { - if (battery_voltage > 0) { + if (BMS_voltage_available) { // Transmit battery voltage back to BMS when confirmed it's available, this closes the contactors ATTO_3_441.data.u8[4] = (uint8_t)(battery_voltage - 1); ATTO_3_441.data.u8[5] = ((battery_voltage - 1) >> 8); ATTO_3_441.data.u8[6] = 0xFF; diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.h b/Software/src/battery/BYD-ATTO-3-BATTERY.h index 3d409e84..9e8f7497 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.h +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.h @@ -98,6 +98,7 @@ class BydAttoBattery : public CanBattery { unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was send bool SOC_method = false; + bool BMS_voltage_available = false; uint8_t counter_50ms = 0; uint8_t counter_100ms = 0; uint8_t frame6_counter = 0xB; From 68da99c4a0414784211de87052726b2999a7bdb0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 23 Aug 2025 12:36:01 +0000 Subject: [PATCH 11/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 57acf348..14688bf9 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -543,7 +543,7 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) { } if (counter_100ms > 3) { - if (BMS_voltage_available) { // Transmit battery voltage back to BMS when confirmed it's available, this closes the contactors + if (BMS_voltage_available) { // Transmit battery voltage back to BMS when confirmed it's available, this closes the contactors ATTO_3_441.data.u8[4] = (uint8_t)(battery_voltage - 1); ATTO_3_441.data.u8[5] = ((battery_voltage - 1) >> 8); ATTO_3_441.data.u8[6] = 0xFF; From 7adee39d82d99ad5378b7d13c2b5d8f8170e7f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 25 Aug 2025 19:28:33 +0300 Subject: [PATCH 12/13] Swap precharge/positive state --- Software/src/battery/VOLVO-SPA-HTML.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/VOLVO-SPA-HTML.h b/Software/src/battery/VOLVO-SPA-HTML.h index 9d832d51..e1c503eb 100644 --- a/Software/src/battery/VOLVO-SPA-HTML.h +++ b/Software/src/battery/VOLVO-SPA-HTML.h @@ -55,7 +55,7 @@ class VolvoSpaHtmlRenderer : public BatteryHtmlRenderer { default: content += String("Closed"); } - content += "

Positive contactor status: "; + content += "

Precharge contactor status: "; switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x08) { case 0x08: content += String("Open"); @@ -63,7 +63,7 @@ class VolvoSpaHtmlRenderer : public BatteryHtmlRenderer { default: content += String("Closed"); } - content += "

Precharge Contactor status: "; + content += "

Positive Contactor status: "; switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x10) { case 0x10: content += String("Open"); From cd30370c85ad42eff4a80d501870e1700de23936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 26 Aug 2025 21:45:20 +0300 Subject: [PATCH 13/13] Update Software.ino --- Software/Software.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.ino b/Software/Software.ino index b3f8b66c..424f3979 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -41,7 +41,7 @@ volatile unsigned long long bmsResetTimeOffset = 0; // The current software version, shown on webserver -const char* version_number = "9.0.RC2experimental"; +const char* version_number = "9.0.RC3"; // Interval timers volatile unsigned long currentMillis = 0;