From 6e8042bb92a243f1770893ac8c131fd83f6c884f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 24 Sep 2025 19:00:31 +0300 Subject: [PATCH 001/536] Add skeleton for mach-e --- Software/src/battery/BATTERIES.cpp | 2 + Software/src/battery/BATTERIES.h | 1 + Software/src/battery/Battery.h | 1 + Software/src/battery/FORD-MACH-E-BATTERY.cpp | 166 +++++++++++++++++++ Software/src/battery/FORD-MACH-E-BATTERY.h | 32 ++++ 5 files changed, 202 insertions(+) create mode 100644 Software/src/battery/FORD-MACH-E-BATTERY.cpp create mode 100644 Software/src/battery/FORD-MACH-E-BATTERY.h diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index 1c954d13c..53d0a7b75 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -166,6 +166,8 @@ Battery* create_battery(BatteryType type) { return new ChademoBattery(); case BatteryType::CmfaEv: return new CmfaEvBattery(); + case BatteryType::FordMachE: + return new FordMachEBattery(); case BatteryType::Foxess: return new FoxessBattery(); case BatteryType::GeelyGeometryC: diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index cb24124e4..a5f33e8f3 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -23,6 +23,7 @@ void setup_can_shunt(); #include "CMFA-EV-BATTERY.h" #include "DALY-BMS.h" #include "ECMP-BATTERY.h" +#include "FORD-MACH-E-BATTERY.h" #include "FOXESS-BATTERY.h" #include "GEELY-GEOMETRY-C-BATTERY.h" #include "HYUNDAI-IONIQ-28-BATTERY.h" diff --git a/Software/src/battery/Battery.h b/Software/src/battery/Battery.h index c929519fa..507f3d07e 100644 --- a/Software/src/battery/Battery.h +++ b/Software/src/battery/Battery.h @@ -48,6 +48,7 @@ enum class BatteryType { Kia64FD = 40, RelionBattery = 41, RivianBattery = 42, + FordMachE = 43, Highest }; diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp new file mode 100644 index 000000000..5496dc17e --- /dev/null +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -0,0 +1,166 @@ +#include "FORD-MACH-E-BATTERY.h" +#include +#include "../datalayer/datalayer.h" +#include "../devboard/utils/logging.h" + +void FordMachEBattery::update_values() { + + datalayer.battery.status.real_soc; + + datalayer.battery.status.soh_pptt; + + datalayer.battery.status.voltage_dV; + + datalayer.battery.status.current_dA; + + datalayer.battery.info.total_capacity_Wh; + + datalayer.battery.status.remaining_capacity_Wh; + + datalayer.battery.status.cell_max_voltage_mV; + + datalayer.battery.status.cell_min_voltage_mV; + + datalayer.battery.status.temperature_min_dC; + + datalayer.battery.status.temperature_max_dC; + + datalayer.battery.status.max_discharge_power_W; + + datalayer.battery.status.max_charge_power_W; +} + +void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { + switch (rx_frame.ID) { //These frames are transmitted by the battery + case 0x07a: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x07b: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x073: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x24b: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x24c: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x24d: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x24e: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x24f: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x2e4: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x444: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x458: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x47d: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x490: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x491: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x492: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x493: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x494: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x495: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x496: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x497: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x498: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x499: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x49a: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x49b: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x49c: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x49d: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x49e: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x49f: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x4a0: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x4a1: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x4a2: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x4a3: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x4a4: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x4a5: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x4a7: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x46f: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + default: + break; + } +} + +void FordMachEBattery::transmit_can(unsigned long currentMillis) { + // Send 100ms CAN Message + if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { + previousMillis100 = currentMillis; + //transmit_can_frame(&TEST); + } +} + +void FordMachEBattery::setup(void) { // Performs one time setup at startup + strncpy(datalayer.system.info.battery_protocol, Name, 63); + datalayer.system.info.battery_protocol[63] = '\0'; + datalayer.battery.info.number_of_cells = 108; //TODO, how many cells do the battery have? + 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; + datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; + datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; + datalayer.system.status.battery_allows_contactor_closing = true; +} diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h new file mode 100644 index 000000000..b2197a6d4 --- /dev/null +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -0,0 +1,32 @@ +#ifndef FORD_MACH_E_BATTERY_H +#define FORD_MACH_E_BATTERY_H +#include "../datalayer/datalayer.h" +#include "CanBattery.h" + +class FordMachEBattery : 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 = "Ford Mustang Mach-E battery"; + + private: + static const int MAX_PACK_VOLTAGE_DV = 5000; //TODO SET + static const int MIN_PACK_VOLTAGE_DV = 2000; //TODO SET + static const int MAX_CELL_DEVIATION_MV = 250; + static const int MAX_CELL_VOLTAGE_MV = 4250; + static const int MIN_CELL_VOLTAGE_MV = 2900; + + unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send + unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send + unsigned long previousMillis10s = 0; // will store last time a 1s CAN Message was send + + CAN_frame TEST = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x123, + .data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}}; +}; + +#endif From d1d651b6674b6ab2a566a3b9fe15738d44bf170c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 24 Sep 2025 19:41:28 +0300 Subject: [PATCH 002/536] Add cellvoltages and temps --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 128 ++++++++++--------- Software/src/battery/FORD-MACH-E-BATTERY.h | 4 + 2 files changed, 70 insertions(+), 62 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 5496dc17e..28ae57997 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -17,127 +17,131 @@ void FordMachEBattery::update_values() { datalayer.battery.status.remaining_capacity_Wh; - datalayer.battery.status.cell_max_voltage_mV; + datalayer.battery.status.max_discharge_power_W; - datalayer.battery.status.cell_min_voltage_mV; + datalayer.battery.status.max_charge_power_W; - datalayer.battery.status.temperature_min_dC; + datalayer.battery.status.cell_max_voltage_mV; - datalayer.battery.status.temperature_max_dC; + datalayer.battery.status.cell_min_voltage_mV; - datalayer.battery.status.max_discharge_power_W; + // Initialize highest and lowest to the first element + maximum_temperature = cell_temperature[0]; + minimum_temperature = cell_temperature[0]; + + // Iterate through the array to find the highest and lowest values + for (uint8_t i = 1; i < 6; ++i) { + if (cell_temperature[i] > maximum_temperature) { + maximum_temperature = cell_temperature[i]; + } + if (cell_temperature[i] < minimum_temperature) { + minimum_temperature = cell_temperature[i]; + } + } + datalayer.battery.status.temperature_min_dC = minimum_temperature * 10; - datalayer.battery.status.max_charge_power_W; + datalayer.battery.status.temperature_max_dC = maximum_temperature * 10; } void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { switch (rx_frame.ID) { //These frames are transmitted by the battery - case 0x07a: + case 0x07a: //10ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x07b: + case 0x07b: //10ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x073: + case 0x073: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x24b: + case 0x24b: //100ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x24c: + case 0x24c: //100ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x24d: + case 0x24d: //100ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x24e: + case 0x24e: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x24f: + case 0x24f: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x2e4: + case 0x2e4: //100ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x444: + case 0x444: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x458: + case 0x458: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x47d: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; - case 0x490: + case 0x47d: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; + case 0x490: //1s Cellvoltages case 0x491: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x492: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x493: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x494: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x495: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x496: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x497: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x498: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x499: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x49a: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x49b: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x49c: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x49d: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x49e: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x49f: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x4a0: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; case 0x4a1: + case 0x4a2: { datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + + // Calculate starting cell index: (CAN_ID - 0x490) * 5 (Ends at 0x4A2) + const uint8_t start_index = (rx_frame.ID - 0x490) * 5; + + // Each CAN message contains 5 cellvoltages + for (uint8_t i = 0; i < 5; i++) { + const uint8_t byte_offset = i * 3; + + if (i % 2 == 0) { + // Even indices: bytes 0,1 | 3,4 | 6,7 + datalayer.battery.status.cell_voltages_mV[start_index + i] = + (rx_frame.data.u8[byte_offset] << 4) | (rx_frame.data.u8[byte_offset + 1] >> 4); + } else { + // Odd indices: bytes 1,2 | 4,5 + datalayer.battery.status.cell_voltages_mV[start_index + i] = + ((rx_frame.data.u8[byte_offset] & 0x0F) << 8) | rx_frame.data.u8[byte_offset + 1]; + } + } break; - case 0x4a2: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - break; - case 0x4a3: + } + case 0x4a3: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + datalayer.battery.status.cell_voltages_mV[95] = (rx_frame.data.u8[0] << 4) | (rx_frame.data.u8[1] >> 4); + //Celltemperatures + cell_temperature[0] = ((rx_frame.data.u8[2] - 40) / 2); + cell_temperature[1] = ((rx_frame.data.u8[2] - 40) / 2); + cell_temperature[2] = ((rx_frame.data.u8[2] - 40) / 2); + cell_temperature[3] = ((rx_frame.data.u8[2] - 40) / 2); + cell_temperature[4] = ((rx_frame.data.u8[2] - 40) / 2); + cell_temperature[5] = ((rx_frame.data.u8[2] - 40) / 2); break; - case 0x4a4: + case 0x4a4: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x4a5: + case 0x4a5: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x4a7: + case 0x4a7: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; - case 0x46f: + case 0x46f: //100ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; default: @@ -156,7 +160,7 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { void FordMachEBattery::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; - datalayer.battery.info.number_of_cells = 108; //TODO, how many cells do the battery have? + datalayer.battery.info.number_of_cells = 96; //TODO, Are all mach-e batteries 96S? 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/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index b2197a6d4..5cabc8ed5 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -22,6 +22,10 @@ class FordMachEBattery : public CanBattery { unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send unsigned long previousMillis10s = 0; // will store last time a 1s CAN Message was send + int16_t cell_temperature[6] = {0}; + int16_t maximum_temperature = 0; + int16_t minimum_temperature = 0; + CAN_frame TEST = {.FD = false, .ext_ID = false, .DLC = 8, From d1510cd66179a2161e85bcb4cf13af760effb079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 24 Sep 2025 23:05:59 +0300 Subject: [PATCH 003/536] Make ford test pass --- test/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e00fd2b9c..794007d79 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -105,6 +105,7 @@ add_executable(tests ../Software/src/battery/CMFA-EV-BATTERY.cpp ../Software/src/battery/DALY-BMS.cpp ../Software/src/battery/ECMP-BATTERY.cpp + ../Software/src/battery/FORD-MACH-E-BATTERY.cpp ../Software/src/battery/FOXESS-BATTERY.cpp ../Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp ../Software/src/battery/HYUNDAI-IONIQ-28-BATTERY-HTML.cpp From 668a225e7fca64ece0b555726b3d3e633aaf41a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 28 Sep 2025 22:59:10 +0300 Subject: [PATCH 004/536] Improve mappings --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 25 ++++++++++++++++---- Software/src/battery/FORD-MACH-E-BATTERY.h | 4 ++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 28ae57997..144024b07 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -5,9 +5,9 @@ void FordMachEBattery::update_values() { - datalayer.battery.status.real_soc; + datalayer.battery.status.real_soc = battery_soc; - datalayer.battery.status.soh_pptt; + datalayer.battery.status.soh_pptt = battery_soh * 100; datalayer.battery.status.voltage_dV; @@ -25,6 +25,21 @@ void FordMachEBattery::update_values() { datalayer.battery.status.cell_min_voltage_mV; + maximum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[0]; + minimum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[0]; + + // Loop through the array to find min and max cellvoltages, ignoring 0 values + for (uint8_t i = 0; i < MAX_AMOUNT_CELLS; i++) { + if (datalayer.battery.status.cell_voltages_mV[i] != 0) { // Ignore unavailable values + if (datalayer.battery.status.cell_voltages_mV[i] < minimum_cellvoltage_mV) { + minimum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[i]; + } + if (datalayer.battery.status.cell_voltages_mV[i] > maximum_cellvoltage_mV) { + maximum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[i]; + } + } + } + // Initialize highest and lowest to the first element maximum_temperature = cell_temperature[0]; minimum_temperature = cell_temperature[0]; @@ -59,6 +74,7 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { break; case 0x24c: //100ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + battery_soc = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]; break; case 0x24d: //100ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; @@ -80,6 +96,7 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { break; case 0x47d: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + battery_soh = rx_frame.data.u8[1]; break; case 0x490: //1s Cellvoltages case 0x491: @@ -112,11 +129,11 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { if (i % 2 == 0) { // Even indices: bytes 0,1 | 3,4 | 6,7 datalayer.battery.status.cell_voltages_mV[start_index + i] = - (rx_frame.data.u8[byte_offset] << 4) | (rx_frame.data.u8[byte_offset + 1] >> 4); + (((rx_frame.data.u8[byte_offset] << 4) | (rx_frame.data.u8[byte_offset + 1] >> 4)) + 1000); } else { // Odd indices: bytes 1,2 | 4,5 datalayer.battery.status.cell_voltages_mV[start_index + i] = - ((rx_frame.data.u8[byte_offset] & 0x0F) << 8) | rx_frame.data.u8[byte_offset + 1]; + ((((rx_frame.data.u8[byte_offset] & 0x0F) << 8) | rx_frame.data.u8[byte_offset + 1]) + 1000); } } break; diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index 5cabc8ed5..91018fc16 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -25,6 +25,10 @@ class FordMachEBattery : public CanBattery { int16_t cell_temperature[6] = {0}; int16_t maximum_temperature = 0; int16_t minimum_temperature = 0; + uint16_t battery_soc = 5000; + uint16_t battery_soh = 99; + uint16_t maximum_cellvoltage_mV = 3700; + uint16_t minimum_cellvoltage_mV = 3700; CAN_frame TEST = {.FD = false, .ext_ID = false, From 5cbfe92de98e6707950077f4aae14f3ded3b3c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 29 Sep 2025 17:59:17 +0300 Subject: [PATCH 005/536] Add more CAN message sending towards battery --- Software/src/battery/BATTERIES.cpp | 2 + Software/src/battery/FORD-MACH-E-BATTERY.cpp | 48 ++++++++++- Software/src/battery/FORD-MACH-E-BATTERY.h | 85 ++++++++++++++++++-- 3 files changed, 126 insertions(+), 9 deletions(-) diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index 53d0a7b75..34ea26992 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -68,6 +68,8 @@ const char* name_for_battery_type(BatteryType type) { return ChademoBattery::Name; case BatteryType::CmfaEv: return CmfaEvBattery::Name; + case BatteryType::FordMachE: + return FordMachEBattery::Name; case BatteryType::Foxess: return FoxessBattery::Name; case BatteryType::GeelyGeometryC: diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 144024b07..b6ac96e73 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -167,10 +167,56 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } void FordMachEBattery::transmit_can(unsigned long currentMillis) { + // Send 10ms CAN Message + if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { + previousMillis10 = currentMillis; + + counter_10ms = (counter_10ms + 1) % 16; // cycles 0-15 + + // Byte 2: upper nibble = 0xF, lower nibble = (0xF - counter_10ms) % 16 + FORD_7D.data.u8[2] = 0xF0 | ((0xF - counter_10ms) & 0x0F); + // Byte 3: upper nibble = counter_10ms, lower nibble = 0x0 + FORD_7D.data.u8[3] = (counter_10ms << 4) & 0xF0; + + // Byte 0: starts at 0xC0 and increments by 4 each step, wraps every 16 steps + FORD_204.data.u8[0] = 0xC0 + (counter_10ms * 4); + // Byte 5: starts at 0xFC and decrements by 1 each step, wraps every 16 steps + FORD_204.data.u8[5] = 0xFC - counter_10ms; + + // Byte 4: upper nibble = counter_10ms, lower nibble = 0x0 + FORD_4B0.data.u8[4] = (counter_10ms << 4) & 0xF0; + // Byte 7: upper nibble = 0xF, lower nibble = (0xF - counter_10ms) % 16 + FORD_4B0.data.u8[7] = 0xF0 | ((0xF - counter_10ms) & 0x0F); + + transmit_can_frame(&FORD_77); + transmit_can_frame(&FORD_7D); + transmit_can_frame(&FORD_167); + transmit_can_frame(&FORD_48F); //Only sent in AC charging logs! + transmit_can_frame(&FORD_204); + transmit_can_frame(&FORD_4B0); + } + // Send 50ms CAN Message + if (currentMillis - previousMillis50 >= INTERVAL_50_MS) { + previousMillis50 = currentMillis; + transmit_can_frame(&FORD_42C); + transmit_can_frame(&FORD_42F); + } + // Send 100ms CAN Message if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { previousMillis100 = currentMillis; - //transmit_can_frame(&TEST); + + transmit_can_frame( + &FORD_12F); //This message actually has checksum/counter, but it seems to close contactors without those + transmit_can_frame(&FORD_332); + transmit_can_frame(&FORD_333); + transmit_can_frame(&FORD_42B); + } + // Send 1s CAN Message + if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { + previousMillis1000 = currentMillis; + transmit_can_frame(&FORD_3C3); + transmit_can_frame(&FORD_581); } } diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index 91018fc16..ce30f00d2 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -18,9 +18,11 @@ class FordMachEBattery : public CanBattery { static const int MAX_CELL_VOLTAGE_MV = 4250; static const int MIN_CELL_VOLTAGE_MV = 2900; - unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send - unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send - unsigned long previousMillis10s = 0; // will store last time a 1s CAN Message was send + unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send + unsigned long previousMillis50 = 0; // will store last time a 100ms CAN Message was send + unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send + unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was send + unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send int16_t cell_temperature[6] = {0}; int16_t maximum_temperature = 0; @@ -30,11 +32,78 @@ class FordMachEBattery : public CanBattery { uint16_t maximum_cellvoltage_mV = 3700; uint16_t minimum_cellvoltage_mV = 3700; - CAN_frame TEST = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x123, - .data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}}; + uint8_t counter_10ms = 0; + + CAN_frame FORD_77 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x077, + .data = {0x00, 0x00, 0x0F, 0xFE, 0xFF, 0xFF, 0xFB, 0xFE}}; + CAN_frame FORD_7D = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x07D, + .data = {0x00, 0x00, 0xF0, 0xF0, 0x00, 0x3F, 0xEF, 0xFE}}; + CAN_frame FORD_167 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x167, + .data = {0x00, 0x80, 0x00, 0x11, 0xFF, 0xE0, 0x00, 0x00}}; + CAN_frame FORD_12F = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x12F, + .data = {0x0A, 0xF8, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; + CAN_frame FORD_204 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x204, + .data = {0xD4, 0x00, 0x7D, 0x00, 0x00, 0xF7, 0x00, 0x00}}; + CAN_frame FORD_332 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x332, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_333 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x333, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_3C3 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x3C3, + .data = {0x5C, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}}; + CAN_frame FORD_42B = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x42B, + .data = {0xCB, 0xBE, 0x00, 0x02, 0x00, 0x00, 0xCE, 0x00}}; + CAN_frame FORD_42C = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x42C, + .data = {0x80, 0x02, 0x00, 0x00, 0x19, 0xA0, 0x00, 0x00}}; + CAN_frame FORD_42F = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x42F, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_48F = {.FD = false, //Only sent in active charging logs (OBC?) + .ext_ID = false, + .DLC = 8, + .ID = 0x48F, + .data = {0x30, 0x4E, 0x20, 0x80, 0x00, 0x00, 0x80, 0x00}}; + CAN_frame FORD_4B0 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x4B0, + .data = {0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0xF0}}; + CAN_frame FORD_581 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x4B0, + .data = {0x81, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; }; #endif From 657656b1f578e71d63cd62eba55cf3fb588696c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 29 Sep 2025 18:25:47 +0300 Subject: [PATCH 006/536] Improve CAN message sending routine towards battery --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 30 +++++++++++++----- Software/src/battery/FORD-MACH-E-BATTERY.h | 33 +++++++++++++++++++- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index b6ac96e73..16afdeedd 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -171,22 +171,35 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { previousMillis10 = currentMillis; - counter_10ms = (counter_10ms + 1) % 16; // cycles 0-15 + transmit_can_frame(&FORD_217); + transmit_can_frame(&FORD_442); + } + + // Send 30ms CAN Message + if (currentMillis - previousMillis30 >= INTERVAL_30_MS) { + previousMillis30 = currentMillis; + + counter_30ms = (counter_30ms + 1) % 16; // cycles 0-15 // Byte 2: upper nibble = 0xF, lower nibble = (0xF - counter_10ms) % 16 - FORD_7D.data.u8[2] = 0xF0 | ((0xF - counter_10ms) & 0x0F); + FORD_7D.data.u8[2] = 0xF0 | ((0xF - counter_30ms) & 0x0F); // Byte 3: upper nibble = counter_10ms, lower nibble = 0x0 - FORD_7D.data.u8[3] = (counter_10ms << 4) & 0xF0; + FORD_7D.data.u8[3] = (counter_30ms << 4) & 0xF0; // Byte 0: starts at 0xC0 and increments by 4 each step, wraps every 16 steps - FORD_204.data.u8[0] = 0xC0 + (counter_10ms * 4); + FORD_204.data.u8[0] = 0xC0 + (counter_30ms * 4); // Byte 5: starts at 0xFC and decrements by 1 each step, wraps every 16 steps - FORD_204.data.u8[5] = 0xFC - counter_10ms; + FORD_204.data.u8[5] = 0xFC - counter_30ms; // Byte 4: upper nibble = counter_10ms, lower nibble = 0x0 - FORD_4B0.data.u8[4] = (counter_10ms << 4) & 0xF0; + FORD_4B0.data.u8[4] = (counter_30ms << 4) & 0xF0; // Byte 7: upper nibble = 0xF, lower nibble = (0xF - counter_10ms) % 16 - FORD_4B0.data.u8[7] = 0xF0 | ((0xF - counter_10ms) & 0x0F); + FORD_4B0.data.u8[7] = 0xF0 | ((0xF - counter_30ms) & 0x0F); + + // Byte 2: starts at 0xC0 and increments by 4 each step, wraps every 16 steps + FORD_415.data.u8[2] = 0xC0 + (counter_30ms * 4); + // Byte 3: starts at 0xFC and decrements by 1 each step, wraps every 16 steps + FORD_415.data.u8[3] = 0xFC - counter_30ms; transmit_can_frame(&FORD_77); transmit_can_frame(&FORD_7D); @@ -194,6 +207,9 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { transmit_can_frame(&FORD_48F); //Only sent in AC charging logs! transmit_can_frame(&FORD_204); transmit_can_frame(&FORD_4B0); + transmit_can_frame(&FORD_47); + transmit_can_frame(&FORD_230); + transmit_can_frame(&FORD_415); } // Send 50ms CAN Message if (currentMillis - previousMillis50 >= INTERVAL_50_MS) { diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index ce30f00d2..733916ff5 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -19,6 +19,7 @@ class FordMachEBattery : public CanBattery { static const int MIN_CELL_VOLTAGE_MV = 2900; unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send + unsigned long previousMillis30 = 0; // will store last time a 10ms CAN Message was send unsigned long previousMillis50 = 0; // will store last time a 100ms CAN Message was send unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was send @@ -32,8 +33,13 @@ class FordMachEBattery : public CanBattery { uint16_t maximum_cellvoltage_mV = 3700; uint16_t minimum_cellvoltage_mV = 3700; - uint8_t counter_10ms = 0; + uint8_t counter_30ms = 0; + CAN_frame FORD_47 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x047, + .data = {0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; CAN_frame FORD_77 = {.FD = false, .ext_ID = false, .DLC = 8, @@ -59,6 +65,21 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x204, .data = {0xD4, 0x00, 0x7D, 0x00, 0x00, 0xF7, 0x00, 0x00}}; + CAN_frame FORD_217 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x217, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_230 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x230, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}}; + CAN_frame FORD_25B = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x25B, + .data = {0x01, 0xF4, 0x09, 0xF4, 0xE0, 0x00, 0x80, 0x00}}; CAN_frame FORD_332 = {.FD = false, .ext_ID = false, .DLC = 8, @@ -74,6 +95,11 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x3C3, .data = {0x5C, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}}; + CAN_frame FORD_415 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x415, + .data = {0x00, 0x00, 0xC0, 0xFC, 0x0F, 0xFE, 0xEF, 0xFE}}; CAN_frame FORD_42B = {.FD = false, .ext_ID = false, .DLC = 8, @@ -89,6 +115,11 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x42F, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_442 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x442, + .data = {0x4E, 0x20, 0x78, 0x7E, 0x7C, 0x00, 0x00, 0x40}}; CAN_frame FORD_48F = {.FD = false, //Only sent in active charging logs (OBC?) .ext_ID = false, .DLC = 8, From ff71cadcb2cfe96643e7ab3520fea65f705b8e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 29 Sep 2025 19:01:56 +0300 Subject: [PATCH 007/536] Further improve sending --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 13 +++++ Software/src/battery/FORD-MACH-E-BATTERY.h | 50 ++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 16afdeedd..e44325f47 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -201,6 +201,8 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { // Byte 3: starts at 0xFC and decrements by 1 each step, wraps every 16 steps FORD_415.data.u8[3] = 0xFC - counter_30ms; + //TODO: handle FORD_4C properly very odd looping + transmit_can_frame(&FORD_77); transmit_can_frame(&FORD_7D); transmit_can_frame(&FORD_167); @@ -210,12 +212,17 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { transmit_can_frame(&FORD_47); transmit_can_frame(&FORD_230); transmit_can_frame(&FORD_415); + transmit_can_frame(&FORD_4C); + transmit_can_frame(&FORD_7E); + transmit_can_frame(&FORD_48); + transmit_can_frame(&FORD_165); } // Send 50ms CAN Message if (currentMillis - previousMillis50 >= INTERVAL_50_MS) { previousMillis50 = currentMillis; transmit_can_frame(&FORD_42C); transmit_can_frame(&FORD_42F); + transmit_can_frame(&FORD_43D); } // Send 100ms CAN Message @@ -227,6 +234,12 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { transmit_can_frame(&FORD_332); transmit_can_frame(&FORD_333); transmit_can_frame(&FORD_42B); + transmit_can_frame(&FORD_2EC); + transmit_can_frame(&FORD_156); + transmit_can_frame( + &FORD_5A); //This message actually has checksum/counter, but it seems to close contactors without those + transmit_can_frame(&FORD_166); + transmit_can_frame(&FORD_175); } // Send 1s CAN Message if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index 733916ff5..540afe98c 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -40,6 +40,21 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x047, .data = {0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_48 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x048, + .data = {0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_4C = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x04C, + .data = {0x70, 0xAA, 0xBF, 0xDE, 0xCC, 0xEC, 0x00, 0x00}}; + CAN_frame FORD_5A = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x05A, + .data = {0x00, 0x00, 0x00, 0x0B, 0xF2, 0x90, 0x10, 0x00}}; CAN_frame FORD_77 = {.FD = false, .ext_ID = false, .DLC = 8, @@ -50,11 +65,36 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x07D, .data = {0x00, 0x00, 0xF0, 0xF0, 0x00, 0x3F, 0xEF, 0xFE}}; + CAN_frame FORD_7E = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x07E, + .data = {0x00, 0x00, 0x3E, 0x80, 0x00, 0x04, 0x00, 0x00}}; + CAN_frame FORD_156 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x156, + .data = {0x4B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}}; + CAN_frame FORD_165 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x165, + .data = {0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_166 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x166, + .data = {0x00, 0x00, 0x00, 0x01, 0x5C, 0x89, 0x00, 0x00}}; CAN_frame FORD_167 = {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x167, .data = {0x00, 0x80, 0x00, 0x11, 0xFF, 0xE0, 0x00, 0x00}}; + CAN_frame FORD_175 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x175, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; CAN_frame FORD_12F = {.FD = false, .ext_ID = false, .DLC = 8, @@ -80,6 +120,11 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x25B, .data = {0x01, 0xF4, 0x09, 0xF4, 0xE0, 0x00, 0x80, 0x00}}; + CAN_frame FORD_2EC = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x2EC, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}}; CAN_frame FORD_332 = {.FD = false, .ext_ID = false, .DLC = 8, @@ -115,6 +160,11 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x42F, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_43D = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x43D, + .data = {0x00, 0x00, 0xDC, 0x00, 0x00, 0x77, 0x00, 0x00}}; CAN_frame FORD_442 = {.FD = false, .ext_ID = false, .DLC = 8, From 380497ad040f27479b9470db1cc4ff192918c704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 30 Sep 2025 13:21:40 +0300 Subject: [PATCH 008/536] Fix cellvoltage mapping --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 37 +++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index e44325f47..396e2070f 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -21,16 +21,12 @@ void FordMachEBattery::update_values() { datalayer.battery.status.max_charge_power_W; - datalayer.battery.status.cell_max_voltage_mV; - - datalayer.battery.status.cell_min_voltage_mV; - maximum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[0]; minimum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[0]; // Loop through the array to find min and max cellvoltages, ignoring 0 values for (uint8_t i = 0; i < MAX_AMOUNT_CELLS; i++) { - if (datalayer.battery.status.cell_voltages_mV[i] != 0) { // Ignore unavailable values + if (datalayer.battery.status.cell_voltages_mV[i] > 1000) { // Ignore unavailable values if (datalayer.battery.status.cell_voltages_mV[i] < minimum_cellvoltage_mV) { minimum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[i]; } @@ -40,6 +36,10 @@ void FordMachEBattery::update_values() { } } + datalayer.battery.status.cell_max_voltage_mV = maximum_cellvoltage_mV; + + datalayer.battery.status.cell_min_voltage_mV = minimum_cellvoltage_mV; + // Initialize highest and lowest to the first element maximum_temperature = cell_temperature[0]; minimum_temperature = cell_temperature[0]; @@ -119,22 +119,25 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { case 0x4a2: { datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - // Calculate starting cell index: (CAN_ID - 0x490) * 5 (Ends at 0x4A2) const uint8_t start_index = (rx_frame.ID - 0x490) * 5; - // Each CAN message contains 5 cellvoltages + // Process 5 cells per message for (uint8_t i = 0; i < 5; i++) { - const uint8_t byte_offset = i * 3; - - if (i % 2 == 0) { - // Even indices: bytes 0,1 | 3,4 | 6,7 - datalayer.battery.status.cell_voltages_mV[start_index + i] = - (((rx_frame.data.u8[byte_offset] << 4) | (rx_frame.data.u8[byte_offset + 1] >> 4)) + 1000); - } else { - // Odd indices: bytes 1,2 | 4,5 - datalayer.battery.status.cell_voltages_mV[start_index + i] = - ((((rx_frame.data.u8[byte_offset] & 0x0F) << 8) | rx_frame.data.u8[byte_offset + 1]) + 1000); + uint16_t voltage = 0; + + if (i == 0) { + voltage = (rx_frame.data.u8[0] << 4) | (rx_frame.data.u8[1] >> 4); + } else if (i == 1) { + voltage = ((rx_frame.data.u8[1] & 0x0F) << 8) | rx_frame.data.u8[2]; + } else if (i == 2) { + voltage = (rx_frame.data.u8[3] << 4) | (rx_frame.data.u8[4] >> 4); + } else if (i == 3) { + voltage = ((rx_frame.data.u8[4] & 0x0F) << 8) | rx_frame.data.u8[5]; + } else if (i == 4) { + voltage = (rx_frame.data.u8[6] << 4) | (rx_frame.data.u8[7] >> 4); } + + datalayer.battery.status.cell_voltages_mV[start_index + i] = voltage + 1000; } break; } From 437f05525a55830506bcda6bb8f3c1d4991d9b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 30 Sep 2025 13:52:43 +0300 Subject: [PATCH 009/536] Improve CAN sending --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 15 ++++++++++ Software/src/battery/FORD-MACH-E-BATTERY.h | 31 ++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 396e2070f..0f6158c99 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -206,6 +206,14 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { //TODO: handle FORD_4C properly very odd looping + counter_8_30ms = (counter_8_30ms + 1) % 8; // cycles 0-7 (8 values before wrap) + + // Byte 6: counts up by 2 each step (0, 2, 4, 6, 8, 10, 12, 14) + FORD_200.data.u8[6] = counter_8_30ms * 2; + + // Byte 7: counts down by 2 each step, maintaining byte6 + byte7 = 0x7F + FORD_200.data.u8[7] = 0x7F - (counter_8_30ms * 2); + transmit_can_frame(&FORD_77); transmit_can_frame(&FORD_7D); transmit_can_frame(&FORD_167); @@ -219,6 +227,8 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { transmit_can_frame(&FORD_7E); transmit_can_frame(&FORD_48); transmit_can_frame(&FORD_165); + transmit_can_frame(&FORD_7F); + transmit_can_frame(&FORD_200); } // Send 50ms CAN Message if (currentMillis - previousMillis50 >= INTERVAL_50_MS) { @@ -243,6 +253,11 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { &FORD_5A); //This message actually has checksum/counter, but it seems to close contactors without those transmit_can_frame(&FORD_166); transmit_can_frame(&FORD_175); + transmit_can_frame(&FORD_178); + transmit_can_frame(&FORD_203); + transmit_can_frame( + &FORD_176); //This message actually has checksum/counter, but it seems to close contactors without those + transmit_can_frame(&FORD_185); } // Send 1s CAN Message if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index 540afe98c..67e4dc36b 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -34,6 +34,7 @@ class FordMachEBattery : public CanBattery { uint16_t minimum_cellvoltage_mV = 3700; uint8_t counter_30ms = 0; + uint8_t counter_8_30ms = 0; CAN_frame FORD_47 = {.FD = false, .ext_ID = false, @@ -70,6 +71,11 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x07E, .data = {0x00, 0x00, 0x3E, 0x80, 0x00, 0x04, 0x00, 0x00}}; + CAN_frame FORD_7F = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x07F, + .data = {0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00}}; CAN_frame FORD_156 = {.FD = false, .ext_ID = false, .DLC = 8, @@ -95,11 +101,36 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x175, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_176 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x176, + .data = {0x00, 0x0E, 0xF0, 0x10, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_178 = {.FD = false, //Static content + .ext_ID = false, + .DLC = 8, + .ID = 0x175, + .data = {0x01, 0xB6, 0x02, 0x00, 0x4E, 0x46, 0xC6, 0x17}}; CAN_frame FORD_12F = {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x12F, .data = {0x0A, 0xF8, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; + CAN_frame FORD_185 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x185, + .data = {0x03, 0x4E, 0x75, 0x32, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_200 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x200, + .data = {0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x70}}; + CAN_frame FORD_203 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x203, + .data = {0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; CAN_frame FORD_204 = {.FD = false, .ext_ID = false, .DLC = 8, From f5e71945df4a07ef413ddc8096fb8e3ece783128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 30 Sep 2025 14:38:53 +0300 Subject: [PATCH 010/536] Fix cellvoltager, remove unnecessary sending --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 0f6158c99..c040c6c24 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -143,7 +143,7 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } case 0x4a3: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - datalayer.battery.status.cell_voltages_mV[95] = (rx_frame.data.u8[0] << 4) | (rx_frame.data.u8[1] >> 4); + datalayer.battery.status.cell_voltages_mV[95] = ((rx_frame.data.u8[0] << 4) | (rx_frame.data.u8[1] >> 4)) + 1000; //Celltemperatures cell_temperature[0] = ((rx_frame.data.u8[2] - 40) / 2); cell_temperature[1] = ((rx_frame.data.u8[2] - 40) / 2); @@ -174,8 +174,8 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { previousMillis10 = currentMillis; - transmit_can_frame(&FORD_217); - transmit_can_frame(&FORD_442); + //transmit_can_frame(&FORD_217); Not needed for contactor closing + //transmit_can_frame(&FORD_442); Not needed for contactor closing } // Send 30ms CAN Message @@ -214,16 +214,16 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { // Byte 7: counts down by 2 each step, maintaining byte6 + byte7 = 0x7F FORD_200.data.u8[7] = 0x7F - (counter_8_30ms * 2); - transmit_can_frame(&FORD_77); - transmit_can_frame(&FORD_7D); - transmit_can_frame(&FORD_167); - transmit_can_frame(&FORD_48F); //Only sent in AC charging logs! - transmit_can_frame(&FORD_204); - transmit_can_frame(&FORD_4B0); - transmit_can_frame(&FORD_47); - transmit_can_frame(&FORD_230); - transmit_can_frame(&FORD_415); - transmit_can_frame(&FORD_4C); + //transmit_can_frame(&FORD_77); Not needed for contactor closing + //transmit_can_frame(&FORD_7D); Not needed for contactor closing + //transmit_can_frame(&FORD_167); Not needed for contactor closing + //transmit_can_frame(&FORD_48F); //Only sent in AC charging logs! Not needed for contactor closing + //transmit_can_frame(&FORD_204); Not needed for contactor closing + //transmit_can_frame(&FORD_4B0); Not needed for contactor closing + //transmit_can_frame(&FORD_47); Not needed for contactor closing + //transmit_can_frame(&FORD_230); Not needed for contactor closing + //transmit_can_frame(&FORD_415); Not needed for contactor closing + //transmit_can_frame(&FORD_4C); Not needed for contactor closing transmit_can_frame(&FORD_7E); transmit_can_frame(&FORD_48); transmit_can_frame(&FORD_165); From d595367db5ba7f5dda4bf871658fb4389de23084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 30 Sep 2025 22:10:01 +0300 Subject: [PATCH 011/536] Further improvements --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index c040c6c24..ae14d77ea 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -224,17 +224,17 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { //transmit_can_frame(&FORD_230); Not needed for contactor closing //transmit_can_frame(&FORD_415); Not needed for contactor closing //transmit_can_frame(&FORD_4C); Not needed for contactor closing - transmit_can_frame(&FORD_7E); - transmit_can_frame(&FORD_48); - transmit_can_frame(&FORD_165); - transmit_can_frame(&FORD_7F); + //transmit_can_frame(&FORD_7E); Not needed for contactor closing + //transmit_can_frame(&FORD_48); Not needed for contactor closing + //transmit_can_frame(&FORD_165); Not needed for contactor closing + //transmit_can_frame(&FORD_7F); Not needed for contactor closing transmit_can_frame(&FORD_200); } // Send 50ms CAN Message if (currentMillis - previousMillis50 >= INTERVAL_50_MS) { previousMillis50 = currentMillis; - transmit_can_frame(&FORD_42C); - transmit_can_frame(&FORD_42F); + //transmit_can_frame(&FORD_42C); Not needed for contactor closing + //transmit_can_frame(&FORD_42F); Not needed for contactor closing transmit_can_frame(&FORD_43D); } @@ -254,7 +254,7 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { transmit_can_frame(&FORD_166); transmit_can_frame(&FORD_175); transmit_can_frame(&FORD_178); - transmit_can_frame(&FORD_203); + transmit_can_frame(&FORD_203); //MANDATORY FOR CONTACTOR OPERATION transmit_can_frame( &FORD_176); //This message actually has checksum/counter, but it seems to close contactors without those transmit_can_frame(&FORD_185); From 895fd9a9ace649016efb0c8dbc896965e45e07f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 1 Oct 2025 10:21:11 +0300 Subject: [PATCH 012/536] Minimize CAN sending to only contactor closing --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 26 ++++++++++++++++---- Software/src/battery/FORD-MACH-E-BATTERY.h | 26 ++++++++++++-------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index ae14d77ea..156965f3b 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -170,18 +170,26 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } void FordMachEBattery::transmit_can(unsigned long currentMillis) { - // Send 10ms CAN Message - if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { - previousMillis10 = currentMillis; + // Send 20ms CAN Message + if (currentMillis - previousMillis20 >= INTERVAL_20_MS) { + previousMillis20 = currentMillis; + transmit_can_frame(&FORD_25B); + + //Full vehicle emulation, not required + /* //transmit_can_frame(&FORD_217); Not needed for contactor closing //transmit_can_frame(&FORD_442); Not needed for contactor closing + */ } // Send 30ms CAN Message if (currentMillis - previousMillis30 >= INTERVAL_30_MS) { previousMillis30 = currentMillis; + //Full vehicle emulation, not required + /* + counter_30ms = (counter_30ms + 1) % 16; // cycles 0-15 // Byte 2: upper nibble = 0xF, lower nibble = (0xF - counter_10ms) % 16 @@ -229,19 +237,24 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { //transmit_can_frame(&FORD_165); Not needed for contactor closing //transmit_can_frame(&FORD_7F); Not needed for contactor closing transmit_can_frame(&FORD_200); + */ } // Send 50ms CAN Message if (currentMillis - previousMillis50 >= INTERVAL_50_MS) { previousMillis50 = currentMillis; //transmit_can_frame(&FORD_42C); Not needed for contactor closing //transmit_can_frame(&FORD_42F); Not needed for contactor closing - transmit_can_frame(&FORD_43D); + //transmit_can_frame(&FORD_43D); } // Send 100ms CAN Message if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { previousMillis100 = currentMillis; + transmit_can_frame(&FORD_185); // Required to close contactors + + //Full vehicle emulation, not required + /* transmit_can_frame( &FORD_12F); //This message actually has checksum/counter, but it seems to close contactors without those transmit_can_frame(&FORD_332); @@ -257,13 +270,16 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { transmit_can_frame(&FORD_203); //MANDATORY FOR CONTACTOR OPERATION transmit_can_frame( &FORD_176); //This message actually has checksum/counter, but it seems to close contactors without those - transmit_can_frame(&FORD_185); +*/ } // Send 1s CAN Message if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { previousMillis1000 = currentMillis; + //Full vehicle emulation, not required + /* transmit_can_frame(&FORD_3C3); transmit_can_frame(&FORD_581); + */ } } diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index 67e4dc36b..8779f1e66 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -36,6 +36,19 @@ class FordMachEBattery : public CanBattery { uint8_t counter_30ms = 0; uint8_t counter_8_30ms = 0; + //Message needed for contactor closing + CAN_frame FORD_25B = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x25B, + .data = {0x01, 0xF4, 0x09, 0xF4, 0xE0, 0x00, 0x80, 0x00}}; + CAN_frame FORD_185 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x185, + .data = {0x03, 0x4E, 0x75, 0x32, 0x00, 0x00, 0x00, 0x00}}; + //Messages to emulate full vehicle + /* CAN_frame FORD_47 = {.FD = false, .ext_ID = false, .DLC = 8, @@ -116,11 +129,7 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x12F, .data = {0x0A, 0xF8, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; - CAN_frame FORD_185 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x185, - .data = {0x03, 0x4E, 0x75, 0x32, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_200 = {.FD = false, .ext_ID = false, .DLC = 8, @@ -146,11 +155,7 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x230, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}}; - CAN_frame FORD_25B = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x25B, - .data = {0x01, 0xF4, 0x09, 0xF4, 0xE0, 0x00, 0x80, 0x00}}; + CAN_frame FORD_2EC = {.FD = false, .ext_ID = false, .DLC = 8, @@ -216,6 +221,7 @@ class FordMachEBattery : public CanBattery { .DLC = 8, .ID = 0x4B0, .data = {0x81, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; + */ }; #endif From e235f69effdf82661db08a741698b9b9338ba043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 1 Oct 2025 10:25:12 +0300 Subject: [PATCH 013/536] Implement contactor opening in FAULT --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 6 ++++++ Software/src/battery/FORD-MACH-E-BATTERY.h | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 156965f3b..99022a41b 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -174,6 +174,12 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { if (currentMillis - previousMillis20 >= INTERVAL_20_MS) { previousMillis20 = currentMillis; + if (datalayer.battery.status.bms_status == FAULT) { + FORD_25B.data.u8[2] = 0x01; + } else { + FORD_25B.data.u8[2] = 0x09; + } + transmit_can_frame(&FORD_25B); //Full vehicle emulation, not required diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index 8779f1e66..e3abbf0ea 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -18,7 +18,7 @@ class FordMachEBattery : public CanBattery { static const int MAX_CELL_VOLTAGE_MV = 4250; static const int MIN_CELL_VOLTAGE_MV = 2900; - unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send + unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send unsigned long previousMillis30 = 0; // will store last time a 10ms CAN Message was send unsigned long previousMillis50 = 0; // will store last time a 100ms CAN Message was send unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send @@ -46,7 +46,7 @@ class FordMachEBattery : public CanBattery { .ext_ID = false, .DLC = 8, .ID = 0x185, - .data = {0x03, 0x4E, 0x75, 0x32, 0x00, 0x00, 0x00, 0x00}}; + .data = {0x03, 0x4E, 0x75, 0x32, 0x00, 0x80, 0x00, 0x00}}; //Messages to emulate full vehicle /* CAN_frame FORD_47 = {.FD = false, From 80e290651dae73c9604f347bbe025cccb9ff0339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 1 Oct 2025 10:55:20 +0300 Subject: [PATCH 014/536] Add voltage candidate --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 3 ++- Software/src/battery/FORD-MACH-E-BATTERY.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 99022a41b..57a53111f 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -9,7 +9,7 @@ void FordMachEBattery::update_values() { datalayer.battery.status.soh_pptt = battery_soh * 100; - datalayer.battery.status.voltage_dV; + datalayer.battery.status.voltage_dV = battery_voltage * 10; datalayer.battery.status.current_dA; @@ -62,6 +62,7 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { switch (rx_frame.ID) { //These frames are transmitted by the battery case 0x07a: //10ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + battery_voltage = (((rx_frame.data.u8[2] & 0x03) << 8) | rx_frame.data.u8[3]) / 2; break; case 0x07b: //10ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index e3abbf0ea..501fb7f94 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -30,6 +30,7 @@ class FordMachEBattery : public CanBattery { int16_t minimum_temperature = 0; uint16_t battery_soc = 5000; uint16_t battery_soh = 99; + uint16_t battery_voltage = 370; uint16_t maximum_cellvoltage_mV = 3700; uint16_t minimum_cellvoltage_mV = 3700; From 5e704c972710b2ec2163a4a0eb40d17c67f6ded4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 1 Oct 2025 13:47:28 +0300 Subject: [PATCH 015/536] Update 12V via polling --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 41 ++++++++++++++++++-- Software/src/battery/FORD-MACH-E-BATTERY.h | 17 +++++++- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 57a53111f..1dcd25d15 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -1,6 +1,7 @@ #include "FORD-MACH-E-BATTERY.h" #include #include "../datalayer/datalayer.h" +#include "../devboard/utils/events.h" #include "../devboard/utils/logging.h" void FordMachEBattery::update_values() { @@ -17,9 +18,9 @@ void FordMachEBattery::update_values() { datalayer.battery.status.remaining_capacity_Wh; - datalayer.battery.status.max_discharge_power_W; + datalayer.battery.status.max_discharge_power_W = 5000; //TODO, fix - datalayer.battery.status.max_charge_power_W; + datalayer.battery.status.max_charge_power_W = 5000; //TODO, fix maximum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[0]; minimum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[0]; @@ -56,6 +57,11 @@ void FordMachEBattery::update_values() { datalayer.battery.status.temperature_min_dC = minimum_temperature * 10; datalayer.battery.status.temperature_max_dC = maximum_temperature * 10; + + // Check vehicle specific safeties + if (polled_12V < 11800) { + set_event(EVENT_12V_LOW, 0); + } } void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { @@ -164,6 +170,26 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { break; case 0x46f: //100ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x7EC: //OBD2 diag reply from BMS (Replies to both 7DF and 7E4) + + if (rx_frame.data.u8[0] < 0x10) { //One line response + pid_reply = ((rx_frame.data.u8[1] & 0x0F) << 8) | rx_frame.data.u8[2]; + } + + if (rx_frame.data.u8[0] == 0x10) { //Multiframe response, send ACK + //transmit_can_frame(&FORD_PID_ACK); //Not seen yet + //pid_reply = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]; + } + + switch (pid_reply) { + case 0x142: //12V battery + polled_12V = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]; + break; + default: + break; + } + break; default: break; @@ -279,6 +305,14 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { &FORD_176); //This message actually has checksum/counter, but it seems to close contactors without those */ } + + // Send 250ms CAN Message + if (currentMillis - previousMillis250 >= INTERVAL_250_MS) { + previousMillis250 = currentMillis; + + transmit_can_frame(&FORD_PID_REQUEST_7DF); + } + // Send 1s CAN Message if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { previousMillis1000 = currentMillis; @@ -293,7 +327,8 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { void FordMachEBattery::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; - datalayer.battery.info.number_of_cells = 96; //TODO, Are all mach-e batteries 96S? + datalayer.battery.info.number_of_cells = 96; //TODO, Are all mach-e batteries 96S? + datalayer.battery.info.total_capacity_Wh = 88000; //Start in 88kWh mode, update later 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/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index 501fb7f94..e48e910ff 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -12,7 +12,7 @@ class FordMachEBattery : public CanBattery { static constexpr const char* Name = "Ford Mustang Mach-E battery"; private: - static const int MAX_PACK_VOLTAGE_DV = 5000; //TODO SET + static const int MAX_PACK_VOLTAGE_DV = 4140; static const int MIN_PACK_VOLTAGE_DV = 2000; //TODO SET static const int MAX_CELL_DEVIATION_MV = 250; static const int MAX_CELL_VOLTAGE_MV = 4250; @@ -22,6 +22,7 @@ class FordMachEBattery : public CanBattery { unsigned long previousMillis30 = 0; // will store last time a 10ms CAN Message was send unsigned long previousMillis50 = 0; // will store last time a 100ms CAN Message was send unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send + unsigned long previousMillis250 = 0; // will store last time a 100ms CAN Message was send unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was send unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send @@ -36,6 +37,20 @@ class FordMachEBattery : public CanBattery { uint8_t counter_30ms = 0; uint8_t counter_8_30ms = 0; + uint16_t pid_reply = 0; + + uint16_t polled_12V = 12000; + + CAN_frame FORD_PID_REQUEST_7DF = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x7DF, + .data = {0x02, 0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame FORD_PID_ACK = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x7DF, + .data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Message needed for contactor closing CAN_frame FORD_25B = {.FD = false, From 49024d9590c67f022d7aed02b82898ad5eab2b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 2 Oct 2025 14:03:18 +0300 Subject: [PATCH 016/536] Raise limits for testing --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 1dcd25d15..79d3025d7 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -18,9 +18,9 @@ void FordMachEBattery::update_values() { datalayer.battery.status.remaining_capacity_Wh; - datalayer.battery.status.max_discharge_power_W = 5000; //TODO, fix + datalayer.battery.status.max_discharge_power_W = 12000; //TODO, fix - datalayer.battery.status.max_charge_power_W = 5000; //TODO, fix + datalayer.battery.status.max_charge_power_W = 12000; //TODO, fix maximum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[0]; minimum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[0]; From 0a792305c27f0c364168f1a8c8f26280168fb026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 5 Oct 2025 14:22:45 +0300 Subject: [PATCH 017/536] Add configurable NC contactor option --- .../comm_contactorcontrol.cpp | 23 +++++++++---------- .../contactorcontrol/comm_contactorcontrol.h | 1 + Software/src/communication/nvm/comm_nvm.cpp | 1 + .../src/devboard/webserver/settings_html.cpp | 8 +++++++ Software/src/devboard/webserver/webserver.cpp | 2 +- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index c9ac25ca3..41c4f46e5 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -5,9 +5,10 @@ // TODO: Ensure valid values at run-time // User can update all these values via Settings page -bool contactor_control_enabled = false; //Should GPIO contactor control be performed? -uint16_t precharge_time_ms = 100; //Precharge time in ms. Adjust depending on capacitance in inverter -bool pwm_contactor_control = false; //Should the contactors be economized via PWM after they are engaged? +bool contactor_control_enabled = false; //Should GPIO contactor control be performed? +bool contactor_control_inverted_logic = false; //Should we control NC contactors? Extremely rare option +uint16_t precharge_time_ms = 100; //Precharge time in ms. Adjust depending on capacitance in inverter +bool pwm_contactor_control = false; //Should the contactors be economized via PWM after they are engaged? bool contactor_control_enabled_double_battery = false; //Should a contactor for the secondary battery be operated? bool remote_bms_reset = false; //Is it possible to actuate BMS reset via MQTT? bool periodic_bms_reset = false; //Should periodic BMS reset be performed each 24h? @@ -16,15 +17,8 @@ bool periodic_bms_reset = false; //Should periodic BMS re enum State { DISCONNECTED, START_PRECHARGE, PRECHARGE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED }; State contactorStatus = DISCONNECTED; -const int ON = 1; -const int OFF = 0; - -#ifdef NC_CONTACTORS //Normally closed contactors use inverted logic -#undef ON -#define ON 0 -#undef OFF -#define OFF 1 -#endif //NC_CONTACTORS +const uint8_t ON = 1; +const uint8_t OFF = 0; #define MAX_ALLOWED_FAULT_TICKS 1000 //1000 = 10 seconds #define NEGATIVE_CONTACTOR_TIME_MS \ @@ -49,6 +43,11 @@ const unsigned long powerRemovalInterval = 24 * 60 * 60 * 1000; // 24 hours in const unsigned long bmsWarmupDuration = 3000; void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) { + + if (contactor_control_inverted_logic) { + direction = !direction; //Invert direction for NC contactors + } + if (pwm_contactor_control) { if (pwm_freq != 0xFFFF) { ledcWrite(pin, pwm_freq); diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.h b/Software/src/communication/contactorcontrol/comm_contactorcontrol.h index 9441c1211..f6644539d 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.h +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.h @@ -6,6 +6,7 @@ // Settings that can be changed at run-time extern bool contactor_control_enabled; +extern bool contactor_control_inverted_logic; extern bool contactor_control_enabled_double_battery; extern bool pwm_contactor_control; extern bool periodic_bms_reset; diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index e30812ff6..dc035c2d3 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -137,6 +137,7 @@ void init_stored_settings() { equipment_stop_behavior = (STOP_BUTTON_BEHAVIOR)settings.getUInt("EQSTOP", (int)STOP_BUTTON_BEHAVIOR::NOT_CONNECTED); user_selected_second_battery = settings.getBool("DBLBTR", false); contactor_control_enabled = settings.getBool("CNTCTRL", false); + contactor_control_inverted_logic = settings.getBool("NCCONTACTOR", false); precharge_time_ms = settings.getUInt("PRECHGMS", 100); contactor_control_enabled_double_battery = settings.getBool("CNTCTRLDBL", false); pwm_contactor_control = settings.getBool("PWMCNTCTRL", false); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 08ca1929d..7d4ff8863 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -259,6 +259,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getBool("CNTCTRL") ? "checked" : ""; } + if (var == "NCCONTACTOR") { + return settings.getBool("NCCONTACTOR") ? "checked" : ""; + } + if (var == "CNTCTRLDBL") { return settings.getBool("CNTCTRLDBL") ? "checked" : ""; } @@ -1202,6 +1206,10 @@ const char* getCANInterfaceName(CAN_Interface interface) { min="1" max="65000" step="1" title="Time in milliseconds the precharge should be active" /> + + + diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 8bf9e71b4..8e60f2e74 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -397,7 +397,7 @@ void init_webserver() { "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "SDLOGENABLED", "STATICIP", "REMBMSRESET", "EXTPRECHARGE", "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", "CANLOGSD", "WIFIAPENABLED", "MQTTENABLED", "NOINVDISC", "HADISC", "MQTTTOPICS", "MQTTCELLV", "INVICNT", - "GTWRHD", "DIGITALHVIL", "PERFPROFILE", "INTERLOCKREQ", "SOCESTIMATED", + "GTWRHD", "DIGITALHVIL", "PERFPROFILE", "INTERLOCKREQ", "SOCESTIMATED", "NCCONTACTOR", }; // Handles the form POST from UI to save settings of the common image From 9d934ba5000a48c9dd7278a0b3201b1e42dd9b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 6 Oct 2025 21:06:25 +0300 Subject: [PATCH 018/536] Add 94/96S autodetection --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 79d3025d7..0d97a5381 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -26,7 +26,7 @@ void FordMachEBattery::update_values() { minimum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[0]; // Loop through the array to find min and max cellvoltages, ignoring 0 values - for (uint8_t i = 0; i < MAX_AMOUNT_CELLS; i++) { + for (uint8_t i = 0; i < datalayer.battery.info.number_of_cells; i++) { if (datalayer.battery.status.cell_voltages_mV[i] > 1000) { // Ignore unavailable values if (datalayer.battery.status.cell_voltages_mV[i] < minimum_cellvoltage_mV) { minimum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[i]; @@ -150,7 +150,14 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } case 0x4a3: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - datalayer.battery.status.cell_voltages_mV[95] = ((rx_frame.data.u8[0] << 4) | (rx_frame.data.u8[1] >> 4)) + 1000; + if ((rx_frame.data.u8[0] == 0xFF) && (rx_frame.data.u8[1] == 0xE0)) { + datalayer.battery.info.number_of_cells = 94; + } else { //96S battery + datalayer.battery.status.cell_voltages_mV[95] = + ((rx_frame.data.u8[0] << 4) | (rx_frame.data.u8[1] >> 4)) + 1000; + datalayer.battery.info.number_of_cells = 96; + } + //Celltemperatures cell_temperature[0] = ((rx_frame.data.u8[2] - 40) / 2); cell_temperature[1] = ((rx_frame.data.u8[2] - 40) / 2); @@ -327,7 +334,6 @@ void FordMachEBattery::transmit_can(unsigned long currentMillis) { void FordMachEBattery::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; - datalayer.battery.info.number_of_cells = 96; //TODO, Are all mach-e batteries 96S? datalayer.battery.info.total_capacity_Wh = 88000; //Start in 88kWh mode, update later datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; From c8312fc590a976a8212d715074ffde960929645a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 7 Oct 2025 10:18:22 +0300 Subject: [PATCH 019/536] Correct capacity, SOH and voltage limits --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 22 +++++++++++++------- Software/src/battery/FORD-MACH-E-BATTERY.h | 6 ++++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 0d97a5381..96a85fc99 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -8,15 +8,14 @@ void FordMachEBattery::update_values() { datalayer.battery.status.real_soc = battery_soc; - datalayer.battery.status.soh_pptt = battery_soh * 100; + datalayer.battery.status.soh_pptt; datalayer.battery.status.voltage_dV = battery_voltage * 10; datalayer.battery.status.current_dA; - datalayer.battery.info.total_capacity_Wh; - - datalayer.battery.status.remaining_capacity_Wh; + 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_discharge_power_W = 12000; //TODO, fix @@ -58,6 +57,14 @@ void FordMachEBattery::update_values() { datalayer.battery.status.temperature_max_dC = maximum_temperature * 10; + if (datalayer.battery.info.number_of_cells == 94) { + datalayer.battery.info.total_capacity_Wh = 98000; + } + + if (datalayer.battery.info.number_of_cells == 96) { + datalayer.battery.info.total_capacity_Wh = 88000; + } + // Check vehicle specific safeties if (polled_12V < 11800) { set_event(EVENT_12V_LOW, 0); @@ -103,7 +110,7 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { break; case 0x47d: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - battery_soh = rx_frame.data.u8[1]; + battery_soh = rx_frame.data.u8[1]; //Not correct! break; case 0x490: //1s Cellvoltages case 0x491: @@ -338,7 +345,8 @@ void FordMachEBattery::setup(void) { // Performs one time setup at startup 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; - 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_design_voltage_dV = + MAX_PACK_VOLTAGE_96S_DV; //Startup in extreme end of max voltage diff allowed + datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_94S_DV; datalayer.system.status.battery_allows_contactor_closing = true; } diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index e48e910ff..236d6a52c 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -12,8 +12,10 @@ class FordMachEBattery : public CanBattery { static constexpr const char* Name = "Ford Mustang Mach-E battery"; private: - static const int MAX_PACK_VOLTAGE_DV = 4140; - static const int MIN_PACK_VOLTAGE_DV = 2000; //TODO SET + static const int MAX_PACK_VOLTAGE_96S_DV = 4140; + static const int MIN_PACK_VOLTAGE_96S_DV = 2680; + static const int MAX_PACK_VOLTAGE_94S_DV = 4072; + static const int MIN_PACK_VOLTAGE_94S_DV = 2612; static const int MAX_CELL_DEVIATION_MV = 250; static const int MAX_CELL_VOLTAGE_MV = 4250; static const int MIN_CELL_VOLTAGE_MV = 2900; From 18da3655aba380f7841ce153a0a3db7f607b6eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 9 Oct 2025 17:16:25 +0300 Subject: [PATCH 020/536] Make successful BMS reset increment event counter properly --- .../src/communication/contactorcontrol/comm_contactorcontrol.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index c9ac25ca3..44946230c 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -292,6 +292,7 @@ void handle_BMSpower() { datalayer.system.status.BMS_startup_in_progress = false; // Reset the BMS warmup removal flag set_event(EVENT_PERIODIC_BMS_RESET, 0); + clear_event(EVENT_PERIODIC_BMS_RESET); } } } From 84ca6ba656a52f98967c55a87d00cec61380154f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 10 Oct 2025 19:11:44 +0300 Subject: [PATCH 021/536] Implement interconnect contactor opening if voltage diff appears --- Software/Software.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Software/Software.cpp b/Software/Software.cpp index bf618e002..4c49ef278 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -127,9 +127,11 @@ void check_interconnect_available() { } uint16_t voltage_diff = abs(datalayer.battery.status.voltage_dV - datalayer.battery2.status.voltage_dV); + uint8_t secondsOutOfVoltageSync = 0; if (voltage_diff <= 30) { // If we are within 3.0V between the batteries clear_event(EVENT_VOLTAGE_DIFFERENCE); + secondsOutOfVoltageSync = 0; if (datalayer.battery.status.bms_status == FAULT) { // If main battery is in fault state, disengage the second battery datalayer.system.status.battery2_allowed_contactor_closing = false; @@ -138,6 +140,13 @@ void check_interconnect_available() { } } else { //Voltage between the two packs is too large set_event(EVENT_VOLTAGE_DIFFERENCE, (uint8_t)(voltage_diff / 10)); + + //If we start to drift out of sync between the two packs for more than 10 seconds, open contactors + if (secondsOutOfVoltageSync < 10) { + secondsOutOfVoltageSync++; + } else { + datalayer.system.status.battery2_allowed_contactor_closing = false; + } } } From 1f9895fa9594a5b567b0d0d550f1ba1c0d196469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 14 Oct 2025 22:27:15 +0300 Subject: [PATCH 022/536] Add current value --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 6 +++++- Software/src/battery/FORD-MACH-E-BATTERY.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 96a85fc99..78fb55d17 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -12,7 +12,7 @@ void FordMachEBattery::update_values() { datalayer.battery.status.voltage_dV = battery_voltage * 10; - datalayer.battery.status.current_dA; + datalayer.battery.status.current_dA = battery_current * 2; datalayer.battery.status.remaining_capacity_Wh = static_cast( (static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh); @@ -79,6 +79,10 @@ void FordMachEBattery::handle_incoming_can_frame(CAN_frame rx_frame) { break; case 0x07b: //10ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + battery_current = (((rx_frame.data.u8[0] & 0x7F) << 8) | rx_frame.data.u8[1]); + if ((rx_frame.data.u8[0] & 0x80) >> 7) { + battery_current = battery_current * -1; + } break; case 0x073: //1s datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index 236d6a52c..ce9454992 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -34,6 +34,7 @@ class FordMachEBattery : public CanBattery { uint16_t battery_soc = 5000; uint16_t battery_soh = 99; uint16_t battery_voltage = 370; + int16_t battery_current = 0; uint16_t maximum_cellvoltage_mV = 3700; uint16_t minimum_cellvoltage_mV = 3700; From 25190696598d80708b407098c943d13934523d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 15 Oct 2025 19:22:17 +0300 Subject: [PATCH 023/536] Remove unecessary library files --- .../src/BackPort_SHA1Builder.cpp | 284 ------------------ .../src/BackPort_SHA1Builder.h | 44 --- .../eModbus-eModbus/ModbusServerEthernet.h | 26 -- 3 files changed, 354 deletions(-) delete mode 100644 Software/src/lib/ESP32Async-ESPAsyncWebServer/src/BackPort_SHA1Builder.cpp delete mode 100644 Software/src/lib/ESP32Async-ESPAsyncWebServer/src/BackPort_SHA1Builder.h delete mode 100644 Software/src/lib/eModbus-eModbus/ModbusServerEthernet.h diff --git a/Software/src/lib/ESP32Async-ESPAsyncWebServer/src/BackPort_SHA1Builder.cpp b/Software/src/lib/ESP32Async-ESPAsyncWebServer/src/BackPort_SHA1Builder.cpp deleted file mode 100644 index 06a73a5b8..000000000 --- a/Software/src/lib/ESP32Async-ESPAsyncWebServer/src/BackPort_SHA1Builder.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* - * FIPS-180-1 compliant SHA-1 implementation - * - * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This file is part of mbed TLS (https://tls.mbed.org) - * Modified for esp32 by Lucas Saavedra Vaz on 11 Jan 2024 - */ - -#include -#if ESP_IDF_VERSION_MAJOR < 5 - -#include "BackPort_SHA1Builder.h" - -// 32-bit integer manipulation macros (big endian) - -#ifndef GET_UINT32_BE -#define GET_UINT32_BE(n, b, i) \ - { (n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | ((uint32_t)(b)[(i) + 2] << 8) | ((uint32_t)(b)[(i) + 3]); } -#endif - -#ifndef PUT_UINT32_BE -#define PUT_UINT32_BE(n, b, i) \ - { \ - (b)[(i)] = (uint8_t)((n) >> 24); \ - (b)[(i) + 1] = (uint8_t)((n) >> 16); \ - (b)[(i) + 2] = (uint8_t)((n) >> 8); \ - (b)[(i) + 3] = (uint8_t)((n)); \ - } -#endif - -// Constants - -static const uint8_t sha1_padding[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -// Private methods - -void SHA1Builder::process(const uint8_t *data) { - uint32_t temp, W[16], A, B, C, D, E; - - GET_UINT32_BE(W[0], data, 0); - GET_UINT32_BE(W[1], data, 4); - GET_UINT32_BE(W[2], data, 8); - GET_UINT32_BE(W[3], data, 12); - GET_UINT32_BE(W[4], data, 16); - GET_UINT32_BE(W[5], data, 20); - GET_UINT32_BE(W[6], data, 24); - GET_UINT32_BE(W[7], data, 28); - GET_UINT32_BE(W[8], data, 32); - GET_UINT32_BE(W[9], data, 36); - GET_UINT32_BE(W[10], data, 40); - GET_UINT32_BE(W[11], data, 44); - GET_UINT32_BE(W[12], data, 48); - GET_UINT32_BE(W[13], data, 52); - GET_UINT32_BE(W[14], data, 56); - GET_UINT32_BE(W[15], data, 60); - -#define sha1_S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) - -#define sha1_R(t) (temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ W[(t - 14) & 0x0F] ^ W[t & 0x0F], (W[t & 0x0F] = sha1_S(temp, 1))) - -#define sha1_P(a, b, c, d, e, x) \ - { \ - e += sha1_S(a, 5) + sha1_F(b, c, d) + sha1_K + x; \ - b = sha1_S(b, 30); \ - } - - A = state[0]; - B = state[1]; - C = state[2]; - D = state[3]; - E = state[4]; - -#define sha1_F(x, y, z) (z ^ (x & (y ^ z))) -#define sha1_K 0x5A827999 - - sha1_P(A, B, C, D, E, W[0]); - sha1_P(E, A, B, C, D, W[1]); - sha1_P(D, E, A, B, C, W[2]); - sha1_P(C, D, E, A, B, W[3]); - sha1_P(B, C, D, E, A, W[4]); - sha1_P(A, B, C, D, E, W[5]); - sha1_P(E, A, B, C, D, W[6]); - sha1_P(D, E, A, B, C, W[7]); - sha1_P(C, D, E, A, B, W[8]); - sha1_P(B, C, D, E, A, W[9]); - sha1_P(A, B, C, D, E, W[10]); - sha1_P(E, A, B, C, D, W[11]); - sha1_P(D, E, A, B, C, W[12]); - sha1_P(C, D, E, A, B, W[13]); - sha1_P(B, C, D, E, A, W[14]); - sha1_P(A, B, C, D, E, W[15]); - sha1_P(E, A, B, C, D, sha1_R(16)); - sha1_P(D, E, A, B, C, sha1_R(17)); - sha1_P(C, D, E, A, B, sha1_R(18)); - sha1_P(B, C, D, E, A, sha1_R(19)); - -#undef sha1_K -#undef sha1_F - -#define sha1_F(x, y, z) (x ^ y ^ z) -#define sha1_K 0x6ED9EBA1 - - sha1_P(A, B, C, D, E, sha1_R(20)); - sha1_P(E, A, B, C, D, sha1_R(21)); - sha1_P(D, E, A, B, C, sha1_R(22)); - sha1_P(C, D, E, A, B, sha1_R(23)); - sha1_P(B, C, D, E, A, sha1_R(24)); - sha1_P(A, B, C, D, E, sha1_R(25)); - sha1_P(E, A, B, C, D, sha1_R(26)); - sha1_P(D, E, A, B, C, sha1_R(27)); - sha1_P(C, D, E, A, B, sha1_R(28)); - sha1_P(B, C, D, E, A, sha1_R(29)); - sha1_P(A, B, C, D, E, sha1_R(30)); - sha1_P(E, A, B, C, D, sha1_R(31)); - sha1_P(D, E, A, B, C, sha1_R(32)); - sha1_P(C, D, E, A, B, sha1_R(33)); - sha1_P(B, C, D, E, A, sha1_R(34)); - sha1_P(A, B, C, D, E, sha1_R(35)); - sha1_P(E, A, B, C, D, sha1_R(36)); - sha1_P(D, E, A, B, C, sha1_R(37)); - sha1_P(C, D, E, A, B, sha1_R(38)); - sha1_P(B, C, D, E, A, sha1_R(39)); - -#undef sha1_K -#undef sha1_F - -#define sha1_F(x, y, z) ((x & y) | (z & (x | y))) -#define sha1_K 0x8F1BBCDC - - sha1_P(A, B, C, D, E, sha1_R(40)); - sha1_P(E, A, B, C, D, sha1_R(41)); - sha1_P(D, E, A, B, C, sha1_R(42)); - sha1_P(C, D, E, A, B, sha1_R(43)); - sha1_P(B, C, D, E, A, sha1_R(44)); - sha1_P(A, B, C, D, E, sha1_R(45)); - sha1_P(E, A, B, C, D, sha1_R(46)); - sha1_P(D, E, A, B, C, sha1_R(47)); - sha1_P(C, D, E, A, B, sha1_R(48)); - sha1_P(B, C, D, E, A, sha1_R(49)); - sha1_P(A, B, C, D, E, sha1_R(50)); - sha1_P(E, A, B, C, D, sha1_R(51)); - sha1_P(D, E, A, B, C, sha1_R(52)); - sha1_P(C, D, E, A, B, sha1_R(53)); - sha1_P(B, C, D, E, A, sha1_R(54)); - sha1_P(A, B, C, D, E, sha1_R(55)); - sha1_P(E, A, B, C, D, sha1_R(56)); - sha1_P(D, E, A, B, C, sha1_R(57)); - sha1_P(C, D, E, A, B, sha1_R(58)); - sha1_P(B, C, D, E, A, sha1_R(59)); - -#undef sha1_K -#undef sha1_F - -#define sha1_F(x, y, z) (x ^ y ^ z) -#define sha1_K 0xCA62C1D6 - - sha1_P(A, B, C, D, E, sha1_R(60)); - sha1_P(E, A, B, C, D, sha1_R(61)); - sha1_P(D, E, A, B, C, sha1_R(62)); - sha1_P(C, D, E, A, B, sha1_R(63)); - sha1_P(B, C, D, E, A, sha1_R(64)); - sha1_P(A, B, C, D, E, sha1_R(65)); - sha1_P(E, A, B, C, D, sha1_R(66)); - sha1_P(D, E, A, B, C, sha1_R(67)); - sha1_P(C, D, E, A, B, sha1_R(68)); - sha1_P(B, C, D, E, A, sha1_R(69)); - sha1_P(A, B, C, D, E, sha1_R(70)); - sha1_P(E, A, B, C, D, sha1_R(71)); - sha1_P(D, E, A, B, C, sha1_R(72)); - sha1_P(C, D, E, A, B, sha1_R(73)); - sha1_P(B, C, D, E, A, sha1_R(74)); - sha1_P(A, B, C, D, E, sha1_R(75)); - sha1_P(E, A, B, C, D, sha1_R(76)); - sha1_P(D, E, A, B, C, sha1_R(77)); - sha1_P(C, D, E, A, B, sha1_R(78)); - sha1_P(B, C, D, E, A, sha1_R(79)); - -#undef sha1_K -#undef sha1_F - - state[0] += A; - state[1] += B; - state[2] += C; - state[3] += D; - state[4] += E; -} - -// Public methods - -void SHA1Builder::begin() { - total[0] = 0; - total[1] = 0; - - state[0] = 0x67452301; - state[1] = 0xEFCDAB89; - state[2] = 0x98BADCFE; - state[3] = 0x10325476; - state[4] = 0xC3D2E1F0; - - memset(buffer, 0x00, sizeof(buffer)); - memset(hash, 0x00, sizeof(hash)); -} - -void SHA1Builder::add(const uint8_t *data, size_t len) { - size_t fill; - uint32_t left; - - if (len == 0) { - return; - } - - left = total[0] & 0x3F; - fill = 64 - left; - - total[0] += (uint32_t)len; - total[0] &= 0xFFFFFFFF; - - if (total[0] < (uint32_t)len) { - total[1]++; - } - - if (left && len >= fill) { - memcpy((void *)(buffer + left), data, fill); - process(buffer); - data += fill; - len -= fill; - left = 0; - } - - while (len >= 64) { - process(data); - data += 64; - len -= 64; - } - - if (len > 0) { - memcpy((void *)(buffer + left), data, len); - } -} - -void SHA1Builder::calculate(void) { - uint32_t last, padn; - uint32_t high, low; - uint8_t msglen[8]; - - high = (total[0] >> 29) | (total[1] << 3); - low = (total[0] << 3); - - PUT_UINT32_BE(high, msglen, 0); - PUT_UINT32_BE(low, msglen, 4); - - last = total[0] & 0x3F; - padn = (last < 56) ? (56 - last) : (120 - last); - - add((uint8_t *)sha1_padding, padn); - add(msglen, 8); - - PUT_UINT32_BE(state[0], hash, 0); - PUT_UINT32_BE(state[1], hash, 4); - PUT_UINT32_BE(state[2], hash, 8); - PUT_UINT32_BE(state[3], hash, 12); - PUT_UINT32_BE(state[4], hash, 16); -} - -void SHA1Builder::getBytes(uint8_t *output) { - memcpy(output, hash, SHA1_HASH_SIZE); -} - -#endif // ESP_IDF_VERSION_MAJOR < 5 diff --git a/Software/src/lib/ESP32Async-ESPAsyncWebServer/src/BackPort_SHA1Builder.h b/Software/src/lib/ESP32Async-ESPAsyncWebServer/src/BackPort_SHA1Builder.h deleted file mode 100644 index e7eafbef9..000000000 --- a/Software/src/lib/ESP32Async-ESPAsyncWebServer/src/BackPort_SHA1Builder.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2024 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#if ESP_IDF_VERSION_MAJOR < 5 - -#ifndef SHA1Builder_h -#define SHA1Builder_h - -#include -#include - -#define SHA1_HASH_SIZE 20 - -class SHA1Builder { -private: - uint32_t total[2]; /* number of bytes processed */ - uint32_t state[5]; /* intermediate digest state */ - unsigned char buffer[64]; /* data block being processed */ - uint8_t hash[SHA1_HASH_SIZE]; /* SHA-1 result */ - - void process(const uint8_t *data); - -public: - void begin(); - void add(const uint8_t *data, size_t len); - void calculate(); - void getBytes(uint8_t *output); -}; - -#endif // SHA1Builder_h - -#endif // ESP_IDF_VERSION_MAJOR < 5 diff --git a/Software/src/lib/eModbus-eModbus/ModbusServerEthernet.h b/Software/src/lib/eModbus-eModbus/ModbusServerEthernet.h deleted file mode 100644 index c520c9f77..000000000 --- a/Software/src/lib/eModbus-eModbus/ModbusServerEthernet.h +++ /dev/null @@ -1,26 +0,0 @@ -// ================================================================================================= -// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus -// MIT license - see license.md for details -// ================================================================================================= -#ifndef _MODBUS_SERVER_ETHERNET_H -#define _MODBUS_SERVER_ETHERNET_H -#include "options.h" -#if HAS_ETHERNET == 1 -#include -#include - -#undef SERVER_END -#define SERVER_END // NIL for Ethernet - -// Create own non-virtual EthernetServer class -class EthernetServerEM : public EthernetServer { -public: - explicit EthernetServerEM(uint16_t port) : EthernetServer(port) { } - void begin(uint16_t port = 0) override { } -}; - -#include "ModbusServerTCPtemp.h" -using ModbusServerEthernet = ModbusServerTCP; -#endif - -#endif From ac2973a43225ae7b646a752be764457621fd260a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 15 Oct 2025 19:22:33 +0300 Subject: [PATCH 024/536] Optimize datatype for IP settings --- Software/src/devboard/wifi/wifi.cpp | 24 ++++++++++++------------ Software/src/devboard/wifi/wifi.h | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Software/src/devboard/wifi/wifi.cpp b/Software/src/devboard/wifi/wifi.cpp index 64b31be08..41135b2d0 100644 --- a/Software/src/devboard/wifi/wifi.cpp +++ b/Software/src/devboard/wifi/wifi.cpp @@ -16,18 +16,18 @@ std::string passwordAP; // Set your Static IP address. Only used incase Static address option is set bool static_IP_enabled = false; -uint16_t static_local_IP1 = 0; -uint16_t static_local_IP2 = 0; -uint16_t static_local_IP3 = 0; -uint16_t static_local_IP4 = 0; -uint16_t static_gateway1 = 0; -uint16_t static_gateway2 = 0; -uint16_t static_gateway3 = 0; -uint16_t static_gateway4 = 0; -uint16_t static_subnet1 = 0; -uint16_t static_subnet2 = 0; -uint16_t static_subnet3 = 0; -uint16_t static_subnet4 = 0; +uint8_t static_local_IP1 = 0; +uint8_t static_local_IP2 = 0; +uint8_t static_local_IP3 = 0; +uint8_t static_local_IP4 = 0; +uint8_t static_gateway1 = 0; +uint8_t static_gateway2 = 0; +uint8_t static_gateway3 = 0; +uint8_t static_gateway4 = 0; +uint8_t static_subnet1 = 0; +uint8_t static_subnet2 = 0; +uint8_t static_subnet3 = 0; +uint8_t static_subnet4 = 0; // Configuration Parameters static const uint16_t WIFI_CHECK_INTERVAL = 2000; // 1 seconds normal check interval when last connected diff --git a/Software/src/devboard/wifi/wifi.h b/Software/src/devboard/wifi/wifi.h index ed6a9a89d..42840f677 100644 --- a/Software/src/devboard/wifi/wifi.h +++ b/Software/src/devboard/wifi/wifi.h @@ -38,17 +38,17 @@ extern bool wifi_enabled; extern bool wifiap_enabled; extern bool mdns_enabled; extern bool static_IP_enabled; -extern uint16_t static_local_IP1; -extern uint16_t static_local_IP2; -extern uint16_t static_local_IP3; -extern uint16_t static_local_IP4; -extern uint16_t static_gateway1; -extern uint16_t static_gateway2; -extern uint16_t static_gateway3; -extern uint16_t static_gateway4; -extern uint16_t static_subnet1; -extern uint16_t static_subnet2; -extern uint16_t static_subnet3; -extern uint16_t static_subnet4; +extern uint8_t static_local_IP1; +extern uint8_t static_local_IP2; +extern uint8_t static_local_IP3; +extern uint8_t static_local_IP4; +extern uint8_t static_gateway1; +extern uint8_t static_gateway2; +extern uint8_t static_gateway3; +extern uint8_t static_gateway4; +extern uint8_t static_subnet1; +extern uint8_t static_subnet2; +extern uint8_t static_subnet3; +extern uint8_t static_subnet4; #endif From ef1dbd5e3996ab4de17d29a00fa45cbde86d6f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 15 Oct 2025 19:53:13 +0300 Subject: [PATCH 025/536] Comment out CAN sending not required for RJXZS --- Software/src/battery/CELLPOWER-BMS.cpp | 10 +++++----- Software/src/battery/CELLPOWER-BMS.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Software/src/battery/CELLPOWER-BMS.cpp b/Software/src/battery/CELLPOWER-BMS.cpp index 8d5d089dd..1c365b98a 100644 --- a/Software/src/battery/CELLPOWER-BMS.cpp +++ b/Software/src/battery/CELLPOWER-BMS.cpp @@ -216,18 +216,18 @@ void CellPowerBms::handle_incoming_can_frame(CAN_frame rx_frame) { } void CellPowerBms::transmit_can(unsigned long currentMillis) { - - // Send 1s CAN Message + /* + // Send 1s CAN Message. NOTE; Not required to keep BMS happy if (currentMillis - previousMillis1s >= INTERVAL_1_S) { previousMillis1s = currentMillis; - - /* + transmit_can_frame(&CELLPOWER_18FF50E9); transmit_can_frame(&CELLPOWER_18FF50E8); transmit_can_frame(&CELLPOWER_18FF50E7); transmit_can_frame(&CELLPOWER_18FF50E5); - */ + } + */ } void CellPowerBms::setup(void) { // Performs one time setup at startup diff --git a/Software/src/battery/CELLPOWER-BMS.h b/Software/src/battery/CELLPOWER-BMS.h index 752518482..f2e752451 100644 --- a/Software/src/battery/CELLPOWER-BMS.h +++ b/Software/src/battery/CELLPOWER-BMS.h @@ -21,7 +21,7 @@ class CellPowerBms : public CanBattery { unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was sent - //Actual content messages + /*Actual content messages // Optional add-on charger module. Might not be needed to send these towards the BMS to keep it happy. CAN_frame CELLPOWER_18FF50E9 = {.FD = false, .ext_ID = true, @@ -43,7 +43,7 @@ class CellPowerBms : public CanBattery { .DLC = 5, .ID = 0x18FF50E5, .data = {0x00, 0x00, 0x00, 0x00, 0x00}}; - + */ bool system_state_discharge = false; bool system_state_charge = false; bool system_state_cellbalancing = false; From fb4e447c7296aa305c8916c40ad87cdc4bd5c2cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 15 Oct 2025 19:53:47 +0300 Subject: [PATCH 026/536] Optimize cellvoltage handling for CMFA implementation --- Software/src/battery/CMFA-EV-BATTERY.cpp | 230 +---------------------- Software/src/battery/CMFA-EV-BATTERY.h | 1 - 2 files changed, 9 insertions(+), 222 deletions(-) diff --git a/Software/src/battery/CMFA-EV-BATTERY.cpp b/Software/src/battery/CMFA-EV-BATTERY.cpp index 107970039..52e12143f 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.cpp +++ b/Software/src/battery/CMFA-EV-BATTERY.cpp @@ -53,9 +53,6 @@ void CmfaEvBattery:: datalayer_battery->status.cell_max_voltage_mV = highest_cell_voltage_mv; - //Map all cell voltages to the global array - memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_mv, 72 * sizeof(uint16_t)); - if (lead_acid_voltage < 11000) { //11.000V set_event(EVENT_12V_LOW, lead_acid_voltage); } @@ -210,227 +207,18 @@ void CmfaEvBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cumulative_energy_in_regen = (uint64_t)((rx_frame.data.u8[4] << 24) | (rx_frame.data.u8[5] << 16) | (rx_frame.data.u8[6] << 8) | (rx_frame.data.u8[7])); break; - case PID_POLL_CELL_1: - cellvoltages_mv[0] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_2: - cellvoltages_mv[1] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_3: - cellvoltages_mv[2] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_4: - cellvoltages_mv[3] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_5: - cellvoltages_mv[4] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_6: - cellvoltages_mv[5] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_7: - cellvoltages_mv[6] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_8: - cellvoltages_mv[7] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_9: - cellvoltages_mv[8] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_10: - cellvoltages_mv[9] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_11: - cellvoltages_mv[10] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_12: - cellvoltages_mv[11] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_13: - cellvoltages_mv[12] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_14: - cellvoltages_mv[13] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_15: - cellvoltages_mv[14] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_16: - cellvoltages_mv[15] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_17: - cellvoltages_mv[16] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_18: - cellvoltages_mv[17] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_19: - cellvoltages_mv[18] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_20: - cellvoltages_mv[19] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_21: - cellvoltages_mv[20] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_22: - cellvoltages_mv[21] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_23: - cellvoltages_mv[22] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_24: - cellvoltages_mv[23] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_25: - cellvoltages_mv[24] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_26: - cellvoltages_mv[25] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_27: - cellvoltages_mv[26] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_28: - cellvoltages_mv[27] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_29: - cellvoltages_mv[28] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_30: - cellvoltages_mv[29] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_31: - cellvoltages_mv[30] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_32: - cellvoltages_mv[31] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_33: - cellvoltages_mv[32] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_34: - cellvoltages_mv[33] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_35: - cellvoltages_mv[34] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_36: - cellvoltages_mv[35] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_37: - cellvoltages_mv[36] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + case PID_POLL_DIDS_SUPPORTED_IN_RANGE_9041_9060: break; - case PID_POLL_CELL_38: - cellvoltages_mv[37] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_39: - cellvoltages_mv[38] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_40: - cellvoltages_mv[39] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_41: - cellvoltages_mv[40] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_42: - cellvoltages_mv[41] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_43: - cellvoltages_mv[42] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_44: - cellvoltages_mv[43] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_45: - cellvoltages_mv[44] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_46: - cellvoltages_mv[45] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_47: - cellvoltages_mv[46] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_48: - cellvoltages_mv[47] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_49: - cellvoltages_mv[48] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_50: - cellvoltages_mv[49] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_51: - cellvoltages_mv[50] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_52: - cellvoltages_mv[51] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_53: - cellvoltages_mv[52] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_54: - cellvoltages_mv[53] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_55: - cellvoltages_mv[54] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_56: - cellvoltages_mv[55] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_57: - cellvoltages_mv[56] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_58: - cellvoltages_mv[57] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_59: - cellvoltages_mv[58] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_60: - cellvoltages_mv[59] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_61: - cellvoltages_mv[60] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_62: - cellvoltages_mv[61] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_63: - cellvoltages_mv[62] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_64: - cellvoltages_mv[63] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_65: - cellvoltages_mv[64] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_66: - cellvoltages_mv[65] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_67: - cellvoltages_mv[66] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_68: - cellvoltages_mv[67] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_69: - cellvoltages_mv[68] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_70: - cellvoltages_mv[69] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_71: - cellvoltages_mv[70] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - case PID_POLL_CELL_72: - cellvoltages_mv[71] = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); - break; - break; - default: + default: //Unknown pid_reply, or a cellvoltage + if (pid_reply >= PID_POLL_CELL_1 && pid_reply <= PID_POLL_CELL_72) { //Cellvoltage PID reply + uint8_t cellnumber = (pid_reply - PID_POLL_CELL_1); + if (cellnumber < MAX_AMOUNT_CELLS) { //Prevent out of bounds array write + datalayer_battery->status.cell_voltages_mV[cellnumber] = + (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + } + } break; } - break; default: break; diff --git a/Software/src/battery/CMFA-EV-BATTERY.h b/Software/src/battery/CMFA-EV-BATTERY.h index 3f6492405..254e6d879 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.h +++ b/Software/src/battery/CMFA-EV-BATTERY.h @@ -206,7 +206,6 @@ class CmfaEvBattery : public CanBattery { uint64_t cumulative_energy_when_charging = 0; uint64_t cumulative_energy_in_regen = 0; uint16_t soh_average = 10000; - uint16_t cellvoltages_mv[72]; uint32_t poll_pid = PID_POLL_SOH_AVERAGE; uint16_t pid_reply = 0; From 55aa537ed6bf837254c63cff636b84ecc0701830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 15 Oct 2025 20:05:22 +0300 Subject: [PATCH 027/536] Optimize cellvoltage array writing --- Software/src/battery/FOXESS-BATTERY.cpp | 259 ++++++++++++------------ Software/src/battery/FOXESS-BATTERY.h | 1 - 2 files changed, 128 insertions(+), 132 deletions(-) diff --git a/Software/src/battery/FOXESS-BATTERY.cpp b/Software/src/battery/FOXESS-BATTERY.cpp index 22a9d3b8c..b05f99c2a 100644 --- a/Software/src/battery/FOXESS-BATTERY.cpp +++ b/Software/src/battery/FOXESS-BATTERY.cpp @@ -23,9 +23,6 @@ void FoxessBattery:: datalayer.battery.status.max_charge_power_W = ((datalayer.battery.status.voltage_dV * max_charge_power_dA) / 100); - //Map all cell voltages to the global array - memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_mV, 128 * sizeof(uint16_t)); - switch (NUMBER_OF_PACKS) { case 1: //HS2.6 (48V invalid combo for most HV inverters) datalayer.battery.info.number_of_cells = 16; @@ -206,196 +203,196 @@ void FoxessBattery::handle_incoming_can_frame(CAN_frame rx_frame) { case 0x0D59: break; case 0x0C1D: // Cellvolts 1 - cellvoltages_mV[0] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[1] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[2] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[3] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[0] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[1] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[2] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[3] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C21: // Cellvolts 2 - cellvoltages_mV[4] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[5] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[6] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[7] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[4] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[5] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[6] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[7] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C25: // Cellvolts 3 - cellvoltages_mV[8] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[9] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[10] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[11] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[8] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[9] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[10] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[11] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C29: // Cellvolts 4 - cellvoltages_mV[12] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[13] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[14] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[15] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[12] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[13] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[14] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[15] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C2D: // Cellvolts 5 - cellvoltages_mV[16] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[17] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[18] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[19] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[16] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[17] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[18] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[19] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C31: // Cellvolts 6 - cellvoltages_mV[20] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[21] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[22] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[23] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[20] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[21] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[22] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[23] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C35: // Cellvolts 7 - cellvoltages_mV[24] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[25] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[26] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[27] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[24] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[25] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[26] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[27] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C39: // Cellvolts 8 - cellvoltages_mV[28] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[29] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[30] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[31] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[28] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[29] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[30] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[31] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C3D: // Cellvolts 9 - cellvoltages_mV[32] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[33] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[34] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[35] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[32] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[33] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[34] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[35] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C41: // Cellvolts 10 - cellvoltages_mV[36] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[37] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[38] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[39] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[36] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[37] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[38] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[39] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C45: // Cellvolts 11 - cellvoltages_mV[40] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[41] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[42] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[43] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[40] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[41] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[42] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[43] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C49: // Cellvolts 12 - cellvoltages_mV[44] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[45] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[46] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[47] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[44] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[45] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[46] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[47] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C4D: // Cellvolts 13 - cellvoltages_mV[48] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[49] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[50] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[51] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[48] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[49] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[50] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[51] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C51: // Cellvolts 14 - cellvoltages_mV[52] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[53] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[54] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[55] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[52] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[53] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[54] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[55] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C55: // Cellvolts 15 - cellvoltages_mV[56] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[57] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[58] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[59] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[56] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[57] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[58] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[59] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C59: // Cellvolts 16 - cellvoltages_mV[60] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[61] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[62] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[63] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[60] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[61] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[62] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[63] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C5D: // Cellvolts 17 - cellvoltages_mV[64] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[65] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[66] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[67] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[64] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[65] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[66] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[67] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C61: // Cellvolts 18 - cellvoltages_mV[68] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[69] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[70] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[71] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[68] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[69] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[70] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[71] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C65: // Cellvolts 19 - cellvoltages_mV[72] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[73] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[74] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[75] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[72] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[73] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[74] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[75] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C69: // Cellvolts 20 - cellvoltages_mV[76] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[77] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[78] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[79] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[76] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[77] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[78] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[79] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C6D: // Cellvolts 21 - cellvoltages_mV[80] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[81] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[82] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[83] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[80] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[81] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[82] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[83] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C71: // Cellvolts 22 - cellvoltages_mV[84] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[85] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[86] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[87] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[84] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[85] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[86] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[87] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C75: // Cellvolts 23 - cellvoltages_mV[88] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[89] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[90] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[91] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[88] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[89] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[90] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[91] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C79: // Cellvolts 24 - cellvoltages_mV[92] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[93] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[94] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[95] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[92] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[93] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[94] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[95] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C7D: // Cellvolts 25 - cellvoltages_mV[96] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[97] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[98] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[99] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[96] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[97] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[98] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[99] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C81: // Cellvolts 26 - cellvoltages_mV[100] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[101] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[102] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[103] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[100] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[101] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[102] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[103] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C85: // Cellvolts 27 - cellvoltages_mV[104] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[105] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[106] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[107] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[104] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[105] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[106] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[107] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C89: // Cellvolts 28 - cellvoltages_mV[108] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[109] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[110] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[111] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[108] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[109] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[110] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[111] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C8D: // Cellvolts 29 - cellvoltages_mV[112] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[113] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[114] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[115] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[112] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[113] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[114] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[115] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C91: // Cellvolts 30 - cellvoltages_mV[116] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[117] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[118] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[119] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[116] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[117] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[118] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[119] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C95: // Cellvolts 31 - cellvoltages_mV[120] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[121] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[122] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[123] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[120] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[121] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[122] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[123] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; case 0x0C99: // Cellvolts 32 - cellvoltages_mV[124] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); - cellvoltages_mV[125] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); - cellvoltages_mV[126] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); - cellvoltages_mV[127] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); + datalayer.battery.status.cell_voltages_mV[124] = (uint16_t)(rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]); + datalayer.battery.status.cell_voltages_mV[125] = (uint16_t)(rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); + datalayer.battery.status.cell_voltages_mV[126] = (uint16_t)(rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]); + datalayer.battery.status.cell_voltages_mV[127] = (uint16_t)(rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]); break; default: break; diff --git a/Software/src/battery/FOXESS-BATTERY.h b/Software/src/battery/FOXESS-BATTERY.h index a10d62fd0..2d6e23417 100644 --- a/Software/src/battery/FOXESS-BATTERY.h +++ b/Software/src/battery/FOXESS-BATTERY.h @@ -31,7 +31,6 @@ class FoxessBattery : public CanBattery { uint16_t cut_mv_min = 0; uint16_t cycle_count = 0; uint16_t max_ac_voltage = 0; - uint16_t cellvoltages_mV[128] = {0}; int16_t temperature_average = 0; int16_t pack1_current_sensor = 0; int16_t pack2_current_sensor = 0; From 76ba3c87e74fa2ea60c903f707f8548dabdb36ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 15 Oct 2025 23:52:39 +0300 Subject: [PATCH 028/536] Remove unused code --- Software/src/battery/RJXZS-BMS.cpp | 14 -------------- Software/src/battery/TEST-FAKE-BATTERY.cpp | 9 ++------- Software/src/battery/TEST-FAKE-BATTERY.h | 10 ---------- 3 files changed, 2 insertions(+), 31 deletions(-) diff --git a/Software/src/battery/RJXZS-BMS.cpp b/Software/src/battery/RJXZS-BMS.cpp index 3017ae8e2..da783bb59 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -89,20 +89,6 @@ void RjxzsBms::update_values() { void RjxzsBms::handle_incoming_can_frame(CAN_frame rx_frame) { - /* - // All CAN messages recieved will be logged via serial - logging.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D - logging.print(" "); - logging.print(rx_frame.ID, HEX); - logging.print(" "); - logging.print(rx_frame.DLC); - logging.print(" "); - for (int i = 0; i < rx_frame.DLC; ++i) { - logging.print(rx_frame.data.u8[i], HEX); - logging.print(" "); - } - logging.println(""); - */ switch (rx_frame.ID) { case 0xF5: // This is the only message is sent from BMS setup_completed = true; // Let the function know we no longer need to send startup messages diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index 9803e7eeb..68b178b0d 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -10,7 +10,7 @@ void TestFakeBattery:: datalayer_battery->status.soh_pptt = 9900; // 99.00% - //datalayer_battery->status.voltage_dV = 3700; // 370.0V , value set in startup in .ino file, editable via webUI + //datalayer_battery->status.voltage_dV = 3700; // 370.0V , value editable via webserver datalayer_battery->status.current_dA = 0; // 0 A @@ -43,12 +43,7 @@ void TestFakeBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } void TestFakeBattery::transmit_can(unsigned long currentMillis) { - // Send 100ms CAN Message - if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { - previousMillis100 = currentMillis; - // Put fake messages here incase you want to test sending CAN - //transmit_can_frame(&TEST); - } + // Fake battery has no CAN sending } void TestFakeBattery::setup(void) { // Performs one time setup at startup diff --git a/Software/src/battery/TEST-FAKE-BATTERY.h b/Software/src/battery/TEST-FAKE-BATTERY.h index b2d6bae55..6dc84006d 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.h +++ b/Software/src/battery/TEST-FAKE-BATTERY.h @@ -33,16 +33,6 @@ class TestFakeBattery : public CanBattery { bool* allows_contactor_closing; static const int MAX_CELL_DEVIATION_MV = 9999; - - unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send - unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send - unsigned long previousMillis10s = 0; // will store last time a 1s CAN Message was send - - CAN_frame TEST = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x123, - .data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}}; }; #endif From 4c303ea6990756ea0cedcb0ddf1cb0f604697330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 16 Oct 2025 00:11:53 +0300 Subject: [PATCH 029/536] Reduce resource usage in RJXZS CAN send --- Software/src/battery/RJXZS-BMS.cpp | 6 ++++-- Software/src/battery/RJXZS-BMS.h | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/RJXZS-BMS.cpp b/Software/src/battery/RJXZS-BMS.cpp index da783bb59..b7b7eff2b 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -245,8 +245,10 @@ void RjxzsBms::transmit_can(unsigned long currentMillis) { } if (!setup_completed) { - transmit_can_frame(&RJXZS_10); // Communication connected flag - transmit_can_frame(&RJXZS_1C); // CAN OK + RJXZS_F4.data.u8[0] = 0x10; // Communication connected flag + transmit_can_frame(&RJXZS_F4); + RJXZS_F4.data.u8[0] = 0x1C; //CAN OK + transmit_can_frame(&RJXZS_F4); } } } diff --git a/Software/src/battery/RJXZS-BMS.h b/Software/src/battery/RJXZS-BMS.h index aadc4f8d9..713e9512e 100644 --- a/Software/src/battery/RJXZS-BMS.h +++ b/Software/src/battery/RJXZS-BMS.h @@ -22,8 +22,7 @@ class RjxzsBms : public CanBattery { unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was sent //Actual content messages - CAN_frame RJXZS_1C = {.FD = false, .ext_ID = true, .DLC = 3, .ID = 0xF4, .data = {0x1C, 0x00, 0x02}}; - CAN_frame RJXZS_10 = {.FD = false, .ext_ID = true, .DLC = 3, .ID = 0xF4, .data = {0x10, 0x00, 0x02}}; + CAN_frame RJXZS_F4 = {.FD = false, .ext_ID = true, .DLC = 3, .ID = 0xF4, .data = {0x1C, 0x00, 0x02}}; static const int FIVE_MINUTES = 60; From a762803426555eac50e908d5062b37662c034422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 16 Oct 2025 12:12:02 +0300 Subject: [PATCH 030/536] Add rampdown on SOC% --- Software/src/battery/FORD-MACH-E-BATTERY.cpp | 28 +++++++++++++++---- Software/src/battery/FORD-MACH-E-BATTERY.h | 5 ++++ .../src/devboard/webserver/settings_html.cpp | 3 +- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.cpp b/Software/src/battery/FORD-MACH-E-BATTERY.cpp index 78fb55d17..ac93c9e85 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.cpp +++ b/Software/src/battery/FORD-MACH-E-BATTERY.cpp @@ -17,9 +17,23 @@ void FordMachEBattery::update_values() { 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_discharge_power_W = 12000; //TODO, fix - - datalayer.battery.status.max_charge_power_W = 12000; //TODO, fix + datalayer.battery.status.max_discharge_power_W = + datalayer.battery.status.override_discharge_power_W; //TODO, fix when v alue is found + + //We have not found allowed charge power yet. Estimate it for now absed on UI setting. TODO. remove this once found + if (battery_soc > 9900) { + datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W; + } else if ((battery_soc / 10) > + RAMPDOWN_SOC) { // When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0 + datalayer.battery.status.max_charge_power_W = + RAMPDOWNPOWERALLOWED * (1 - ((battery_soc / 10) - RAMPDOWN_SOC) / (1000.0 - RAMPDOWN_SOC)); + //If the cellvoltages start to reach overvoltage, only allow a small amount of power in + if (maximum_cellvoltage_mV > (MAX_CELL_VOLTAGE_MV - FLOAT_START_MV)) { + datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W; + } + } else { // No limits, max charging power allowed + datalayer.battery.status.max_charge_power_W = datalayer.battery.status.override_charge_power_W; + } maximum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[0]; minimum_cellvoltage_mV = datalayer.battery.status.cell_voltages_mV[0]; @@ -36,9 +50,13 @@ void FordMachEBattery::update_values() { } } - datalayer.battery.status.cell_max_voltage_mV = maximum_cellvoltage_mV; + if (maximum_cellvoltage_mV > 0) { + datalayer.battery.status.cell_max_voltage_mV = maximum_cellvoltage_mV; + } - datalayer.battery.status.cell_min_voltage_mV = minimum_cellvoltage_mV; + if (minimum_cellvoltage_mV > 0) { + datalayer.battery.status.cell_min_voltage_mV = minimum_cellvoltage_mV; + } // Initialize highest and lowest to the first element maximum_temperature = cell_temperature[0]; diff --git a/Software/src/battery/FORD-MACH-E-BATTERY.h b/Software/src/battery/FORD-MACH-E-BATTERY.h index ce9454992..625e19902 100644 --- a/Software/src/battery/FORD-MACH-E-BATTERY.h +++ b/Software/src/battery/FORD-MACH-E-BATTERY.h @@ -20,6 +20,11 @@ class FordMachEBattery : public CanBattery { static const int MAX_CELL_VOLTAGE_MV = 4250; static const int MIN_CELL_VOLTAGE_MV = 2900; + static const int RAMPDOWN_SOC = 900; // 90.0 SOC% to start ramping down from max charge power towards 0 at 100.00% + static const int RAMPDOWNPOWERALLOWED = 8000; // What power we ramp down from towards top balancing + static const int FLOAT_MAX_POWER_W = 200; // W, what power to allow for top balancing battery + static const int FLOAT_START_MV = 20; // mV, how many mV under overvoltage to start float charging + unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send unsigned long previousMillis30 = 0; // will store last time a 10ms CAN Message was send unsigned long previousMillis50 = 0; // will store last time a 100ms CAN Message was send diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 51af917e4..327cb2a13 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -939,7 +939,8 @@ const char* getCANInterfaceName(CAN_Interface interface) { form[data-battery="16"] .if-estimated, form[data-battery="24"] .if-estimated, form[data-battery="32"] .if-estimated, - form[data-battery="33"] .if-estimated { + form[data-battery="33"] .if-estimated, + form[data-battery="44"] .if-estimated { display: contents; } From fe649b9a405204c343187b7748b9189c159cc02b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 16 Oct 2025 21:26:51 +0300 Subject: [PATCH 031/536] Enable Wall compiler flag --- platformio.ini | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index 246c69193..d6cc57951 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,7 +18,7 @@ monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder board_build.partitions = min_spiffs.csv framework = arduino -build_flags = -I include -DHW_DEVKIT -Wimplicit-fallthrough -Wextra +build_flags = -I include -DHW_DEVKIT -Wimplicit-fallthrough -Wextra -Wall lib_deps = [env:lilygo_330] @@ -31,7 +31,7 @@ board_build.f_flash = 80000000 board_build.arduino.memory_type = qio_qspi board_build.partitions = min_spiffs.csv framework = arduino -build_flags = -I include -DHW_LILYGO -Wimplicit-fallthrough -Wextra +build_flags = -I include -DHW_LILYGO -Wimplicit-fallthrough -Wextra -Wall lib_deps = [env:stark_330] @@ -44,7 +44,7 @@ board_build.f_flash = 80000000 board_build.arduino.memory_type = qio_qspi board_build.partitions = min_spiffs.csv framework = arduino -build_flags = -I include -DHW_STARK -Wimplicit-fallthrough -Wextra +build_flags = -I include -DHW_STARK -Wimplicit-fallthrough -Wextra -Wall lib_deps = [env:lilygo_2CAN_330] @@ -62,6 +62,7 @@ build_flags = -I include -Wimplicit-fallthrough -Wextra + -Wall -D HW_LILYGO2CAN -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=1 From 9f9901465e4d556986e3a064cf9d4191e503a928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 16 Oct 2025 23:46:28 +0300 Subject: [PATCH 032/536] Fix compilation warnings --- Software/src/battery/BMW-I3-BATTERY.cpp | 2 + Software/src/battery/BMW-IX-BATTERY.cpp | 99 +++++++------ Software/src/battery/BMW-PHEV-BATTERY.cpp | 6 +- Software/src/battery/DALY-BMS.cpp | 7 +- Software/src/battery/ECMP-BATTERY.cpp | 6 +- Software/src/battery/ECMP-BATTERY.h | 2 +- .../src/battery/GEELY-GEOMETRY-C-BATTERY.cpp | 6 +- Software/src/battery/MG-5-BATTERY.cpp | 18 +-- Software/src/battery/MG-HS-PHEV-BATTERY.cpp | 8 +- Software/src/battery/MG-HS-PHEV-BATTERY.h | 5 +- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 8 +- .../src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp | 3 +- Software/src/battery/RJXZS-BMS.cpp | 6 +- Software/src/battery/TESLA-BATTERY.h | 2 +- Software/src/battery/TESLA-HTML.h | 38 ++--- .../src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp | 1 - Software/src/charger/CHARGERS.cpp | 4 + Software/src/charger/NISSAN-LEAF-CHARGER.cpp | 2 +- Software/src/devboard/mqtt/mqtt.cpp | 14 ++ Software/src/inverter/KOSTAL-RS485.cpp | 2 +- Software/src/inverter/KOSTAL-RS485.h | 4 +- Software/src/inverter/SOLAX-CAN.cpp | 137 +++++++++--------- Software/src/inverter/SOLXPOW-CAN.cpp | 117 +-------------- Software/src/inverter/SOLXPOW-CAN.h | 57 -------- .../src/lib/adafruit-Adafruit_NeoPixel/esp.c | 115 --------------- 25 files changed, 202 insertions(+), 467 deletions(-) diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp index 5ae9d5100..48177c762 100644 --- a/Software/src/battery/BMW-I3-BATTERY.cpp +++ b/Software/src/battery/BMW-I3-BATTERY.cpp @@ -283,6 +283,8 @@ void BmwI3Battery::handle_incoming_can_frame(CAN_frame rx_frame) { battery_soc_hvmin = (message_data[4] << 8 | message_data[5]); } break; + default: + break; } } break; diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 32397ddf1..2ec5a05f2 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -169,10 +169,10 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) { if (rx_frame.DLC > 6 && rx_frame.data.u8[0] == 0xF4 && rx_frame.data.u8[1] == 0x10 && rx_frame.data.u8[2] == 0xE3 && rx_frame.data.u8[3] == 0x62 && rx_frame.data.u8[4] == 0xE5) { //First of multi frame data - Parse the first frame - if (rx_frame.DLC = 64 && rx_frame.data.u8[5] == 0x54) { //Individual Cell Voltages - First Frame - int start_index = 6; //Data starts here - int voltage_index = 0; //Start cell ID - int num_voltages = 29; // number of voltage readings to get + if ((rx_frame.DLC == 64) && (rx_frame.data.u8[5] == 0x54)) { //Individual Cell Voltages - First Frame + int start_index = 6; //Data starts here + int voltage_index = 0; //Start cell ID + int num_voltages = 29; // number of voltage readings to get for (int i = start_index; i < (start_index + num_voltages * 2); i += 2) { uint16_t voltage = (rx_frame.data.u8[i] << 8) | rx_frame.data.u8[i + 1]; if (voltage < 10000) { //Check reading is plausible - otherwise ignore @@ -186,11 +186,11 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) { transmit_can_frame(&BMWiX_6F4_CONTINUE_DATA); } - if (rx_frame.DLC = 64 && rx_frame.data.u8[0] == 0xF4 && - rx_frame.data.u8[1] == 0x21) { //Individual Cell Voltages - 1st Continue frame - int start_index = 2; //Data starts here - int voltage_index = 29; //Start cell ID - int num_voltages = 31; // number of voltage readings to get + if ((rx_frame.DLC == 64) && (rx_frame.data.u8[0] == 0xF4) && + (rx_frame.data.u8[1] == 0x21)) { //Individual Cell Voltages - 1st Continue frame + int start_index = 2; //Data starts here + int voltage_index = 29; //Start cell ID + int num_voltages = 31; // number of voltage readings to get for (int i = start_index; i < (start_index + num_voltages * 2); i += 2) { uint16_t voltage = (rx_frame.data.u8[i] << 8) | rx_frame.data.u8[i + 1]; if (voltage < 10000) { //Check reading is plausible - otherwise ignore @@ -200,11 +200,11 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } } - if (rx_frame.DLC = 64 && rx_frame.data.u8[0] == 0xF4 && - rx_frame.data.u8[1] == 0x22) { //Individual Cell Voltages - 2nd Continue frame - int start_index = 2; //Data starts here - int voltage_index = 60; //Start cell ID - int num_voltages = 31; // number of voltage readings to get + if ((rx_frame.DLC == 64) && (rx_frame.data.u8[0] == 0xF4) && + (rx_frame.data.u8[1] == 0x22)) { //Individual Cell Voltages - 2nd Continue frame + int start_index = 2; //Data starts here + int voltage_index = 60; //Start cell ID + int num_voltages = 31; // number of voltage readings to get for (int i = start_index; i < (start_index + num_voltages * 2); i += 2) { uint16_t voltage = (rx_frame.data.u8[i] << 8) | rx_frame.data.u8[i + 1]; if (voltage < 10000) { //Check reading is plausible - otherwise ignore @@ -214,10 +214,10 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } } - if (rx_frame.DLC = 64 && rx_frame.data.u8[0] == 0xF4 && - rx_frame.data.u8[1] == 0x23) { //Individual Cell Voltages - 3rd Continue frame - int start_index = 2; //Data starts here - int voltage_index = 91; //Start cell ID + if ((rx_frame.DLC == 64) && (rx_frame.data.u8[0] == 0xF4) && + (rx_frame.data.u8[1] == 0x23)) { //Individual Cell Voltages - 3rd Continue frame + int start_index = 2; //Data starts here + int voltage_index = 91; //Start cell ID int num_voltages; if (rx_frame.data.u8[12] == 0xFF && rx_frame.data.u8[13] == 0xFF) { //97th cell is blank - assume 96S Battery num_voltages = 5; // number of voltage readings to get - 6 more to get on 96S @@ -235,56 +235,56 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) { voltage_index++; } } - if (rx_frame.DLC = 7 && rx_frame.data.u8[4] == 0x4D) { //Main Battery Voltage (Pre Contactor) + if ((rx_frame.DLC == 7) && (rx_frame.data.u8[4] == 0x4D)) { //Main Battery Voltage (Pre Contactor) battery_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[6]) / 10; } - if (rx_frame.DLC = 7 && rx_frame.data.u8[4] == 0x4A) { //Main Battery Voltage (After Contactor) + if ((rx_frame.DLC == 7) && (rx_frame.data.u8[4] == 0x4A)) { //Main Battery Voltage (After Contactor) battery_voltage_after_contactor = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[6]) / 10; } - if (rx_frame.DLC = 12 && rx_frame.data.u8[4] == 0xE5 && - rx_frame.data.u8[5] == 0x61) { //Current amps 32bit signed MSB. dA . negative is discharge + if ((rx_frame.DLC == 12) && (rx_frame.data.u8[4] == 0xE5) && + (rx_frame.data.u8[5] == 0x61)) { //Current amps 32bit signed MSB. dA . negative is discharge battery_current = ((int32_t)((rx_frame.data.u8[6] << 24) | (rx_frame.data.u8[7] << 16) | (rx_frame.data.u8[8] << 8) | rx_frame.data.u8[9])) * 0.1; } - if (rx_frame.DLC = 64 && rx_frame.data.u8[4] == 0xE4 && rx_frame.data.u8[5] == 0xCA) { //Balancing Data + if ((rx_frame.DLC == 64) && (rx_frame.data.u8[4] == 0xE4) && (rx_frame.data.u8[5] == 0xCA)) { //Balancing Data balancing_status = (rx_frame.data.u8[6]); //4 = No symmetry mode active, invalid qualifier } - if (rx_frame.DLC = 7 && rx_frame.data.u8[4] == 0xE5 && rx_frame.data.u8[5] == 0xCE) { //Min/Avg/Max SOC% + if ((rx_frame.DLC == 7) && (rx_frame.data.u8[4] == 0xE5) && (rx_frame.data.u8[5] == 0xCE)) { //Min/Avg/Max SOC% min_soc_state = (rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]); avg_soc_state = (rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]); max_soc_state = (rx_frame.data.u8[10] << 8 | rx_frame.data.u8[11]); } - if (rx_frame.DLC = - 12 && rx_frame.data.u8[4] == 0xE5 && - rx_frame.data.u8[5] == 0xC7) { //Current and max capacity kWh. Stored in kWh as 0.01 scale with -50 bias + if ((rx_frame.DLC == 12) && (rx_frame.data.u8[4] == 0xE5) && + (rx_frame.data.u8[5] == 0xC7)) { //Current and max capacity kWh. Stored in kWh as 0.01 scale with -50 bias remaining_capacity = ((rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]) * 10) - 50000; max_capacity = ((rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]) * 10) - 50000; } - if (rx_frame.DLC = 20 && rx_frame.data.u8[4] == 0xE5 && rx_frame.data.u8[5] == 0x45) { //SOH Max Min Mean Request + if ((rx_frame.DLC == 20) && (rx_frame.data.u8[4] == 0xE5) && + (rx_frame.data.u8[5] == 0x45)) { //SOH Max Min Mean Request min_soh_state = ((rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9])); avg_soh_state = ((rx_frame.data.u8[10] << 8 | rx_frame.data.u8[11])); max_soh_state = ((rx_frame.data.u8[12] << 8 | rx_frame.data.u8[13])); } - if (rx_frame.DLC = 10 && rx_frame.data.u8[4] == 0xE5 && - rx_frame.data.u8[5] == 0x62) { //Max allowed charge and discharge current - Signed 16bit + if ((rx_frame.DLC == 10) && (rx_frame.data.u8[4] == 0xE5) && + (rx_frame.data.u8[5] == 0x62)) { //Max allowed charge and discharge current - Signed 16bit allowable_charge_amps = (int16_t)((rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7])) / 10; allowable_discharge_amps = (int16_t)((rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9])) / 10; } - if (rx_frame.DLC = 9 && rx_frame.data.u8[4] == 0xE5 && - rx_frame.data.u8[5] == 0x4B) { //Max allowed charge and discharge current - Signed 16bit + if ((rx_frame.DLC == 9) && (rx_frame.data.u8[4] == 0xE5) && + (rx_frame.data.u8[5] == 0x4B)) { //Max allowed charge and discharge current - Signed 16bit voltage_qualifier_status = (rx_frame.data.u8[8]); // Request HV Voltage Qualifier } - if (rx_frame.DLC = - 48 && rx_frame.data.u8[4] == 0xA8 && rx_frame.data.u8[5] == 0x60) { // Safety Isolation Measurements + if ((rx_frame.DLC == 48) && (rx_frame.data.u8[4] == 0xA8) && + (rx_frame.data.u8[5] == 0x60)) { // Safety Isolation Measurements iso_safety_positive = (rx_frame.data.u8[34] << 24) | (rx_frame.data.u8[35] << 16) | (rx_frame.data.u8[36] << 8) | rx_frame.data.u8[37]; //Assuming 32bit iso_safety_negative = (rx_frame.data.u8[38] << 24) | (rx_frame.data.u8[39] << 16) | @@ -293,20 +293,20 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) { (rx_frame.data.u8[44] << 8) | rx_frame.data.u8[45]; //Assuming 32bit } - if (rx_frame.DLC = - 48 && rx_frame.data.u8[4] == 0xE4 && rx_frame.data.u8[5] == 0xC0) { // Uptime and Vehicle Time Status + if ((rx_frame.DLC == 48) && (rx_frame.data.u8[4] == 0xE4) && + (rx_frame.data.u8[5] == 0xC0)) { // Uptime and Vehicle Time Status sme_uptime = (rx_frame.data.u8[10] << 24) | (rx_frame.data.u8[11] << 16) | (rx_frame.data.u8[12] << 8) | rx_frame.data.u8[13]; //Assuming 32bit } - if (rx_frame.DLC = 8 && rx_frame.data.u8[3] == 0xAC && rx_frame.data.u8[4] == 0x93) { // Pyro Status + if ((rx_frame.DLC == 8) && (rx_frame.data.u8[3] == 0xAC) && (rx_frame.data.u8[4] == 0x93)) { // Pyro Status pyro_status_pss1 = (rx_frame.data.u8[5]); pyro_status_pss4 = (rx_frame.data.u8[6]); pyro_status_pss6 = (rx_frame.data.u8[7]); } - if (rx_frame.DLC = 12 && rx_frame.data.u8[4] == 0xE5 && - rx_frame.data.u8[5] == 0x53) { //Min and max cell voltage 10V = Qualifier Invalid + if ((rx_frame.DLC == 12) && (rx_frame.data.u8[4] == 0xE5) && + (rx_frame.data.u8[5] == 0x53)) { //Min and max cell voltage 10V = Qualifier Invalid datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; //This is the most important safety values, if we receive this we reset CAN alive counter. @@ -322,25 +322,27 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } } - if (rx_frame.DLC = 16 && rx_frame.data.u8[4] == 0xDD && rx_frame.data.u8[5] == 0xC0) { //Battery Temperature + if ((rx_frame.DLC == 16) && (rx_frame.data.u8[4] == 0xDD) && + (rx_frame.data.u8[5] == 0xC0)) { //Battery Temperature min_battery_temperature = (rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]) / 10; avg_battery_temperature = (rx_frame.data.u8[10] << 8 | rx_frame.data.u8[11]) / 10; max_battery_temperature = (rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]) / 10; } - if (rx_frame.DLC = 7 && rx_frame.data.u8[4] == 0xA3) { //Main Contactor Temperature CHECK FINGERPRINT 2 LEVEL + if ((rx_frame.DLC == 7) && + (rx_frame.data.u8[4] == 0xA3)) { //Main Contactor Temperature CHECK FINGERPRINT 2 LEVEL main_contactor_temperature = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[6]); } - if (rx_frame.DLC = 7 && rx_frame.data.u8[4] == 0xA7) { //Terminal 30 Voltage (12V SME supply) + if ((rx_frame.DLC == 7) && (rx_frame.data.u8[4] == 0xA7)) { //Terminal 30 Voltage (12V SME supply) terminal30_12v_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[6]); } - if (rx_frame.DLC = 6 && rx_frame.data.u8[0] == 0xF4 && rx_frame.data.u8[1] == 0x04 && - rx_frame.data.u8[2] == 0x62 && rx_frame.data.u8[3] == 0xE5 && - rx_frame.data.u8[4] == 0x69) { //HVIL Status + if ((rx_frame.DLC == 6) && (rx_frame.data.u8[0] == 0xF4) && (rx_frame.data.u8[1] == 0x04) && + (rx_frame.data.u8[2] == 0x62) && (rx_frame.data.u8[3] == 0xE5) && + (rx_frame.data.u8[4] == 0x69)) { //HVIL Status hvil_status = (rx_frame.data.u8[5]); } - if (rx_frame.DLC = 12 && rx_frame.data.u8[2] == 0x07 && rx_frame.data.u8[3] == 0x62 && - rx_frame.data.u8[4] == 0xE5 && rx_frame.data.u8[5] == 0x4C) { //Pack Voltage Limits + if ((rx_frame.DLC == 12) && (rx_frame.data.u8[2] == 0x07) && (rx_frame.data.u8[3] == 0x62) && + (rx_frame.data.u8[4] == 0xE5) && (rx_frame.data.u8[5] == 0x4C)) { //Pack Voltage Limits if ((rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]) < 4700 && (rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]) > 2600) { //Make sure values are plausible battery_info_available = true; @@ -349,7 +351,8 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } } - if (rx_frame.DLC = 16 && rx_frame.data.u8[3] == 0xF1 && rx_frame.data.u8[4] == 0x8C) { //Battery Serial Number + if ((rx_frame.DLC == 16) && (rx_frame.data.u8[3] == 0xF1) && + (rx_frame.data.u8[4] == 0x8C)) { //Battery Serial Number //Convert hex bytes to ASCII characters and combine them into a string char numberString[11]; // 10 characters + null terminator for (int i = 0; i < 10; i++) { diff --git a/Software/src/battery/BMW-PHEV-BATTERY.cpp b/Software/src/battery/BMW-PHEV-BATTERY.cpp index 343ef1cde..f8fe9eb1d 100644 --- a/Software/src/battery/BMW-PHEV-BATTERY.cpp +++ b/Software/src/battery/BMW-PHEV-BATTERY.cpp @@ -483,7 +483,7 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { { // UDS Multi Frame vars - Top nibble indicates Frame Type: SF (0), FF (1), CF (2), FC (3) // Extended addressing => data[0] is ext address, data[1] is PCI - uint8_t extAddr = rx_frame.data.u8[0]; // e.g., 0xF1 + //uint8_t extAddr = rx_frame.data.u8[0]; // e.g., 0xF1 uint8_t pciByte = rx_frame.data.u8[1]; // e.g., 0x10, 0x21, etc. uint8_t pciType = pciByte >> 4; // top nibble => 0=SF,1=FF,2=CF,3=FC uint8_t pciLower = pciByte & 0x0F; // bottom nibble => length nibble or sequence @@ -492,8 +492,8 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { case 0x0: { // Single Frame reponse // SF payload length is in pciLower - uint8_t sfLength = pciLower; - uint8_t moduleID = rx_frame.data.u8[5]; + //uint8_t sfLength = pciLower; + //uint8_t moduleID = rx_frame.data.u8[5]; if (rx_frame.DLC == 8 && rx_frame.data.u8[2] == 0x62 && rx_frame.data.u8[3] == 0xDD && rx_frame.data.u8[4] == 0xC4) { // SOC% diff --git a/Software/src/battery/DALY-BMS.cpp b/Software/src/battery/DALY-BMS.cpp index 74f927039..1f328d45c 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -195,9 +195,10 @@ void DalyBms::receive() { recv_len++; - if (recv_len > 0 && recv_buff[0] != 0xA5 || recv_len > 1 && recv_buff[1] != 0x01 || - recv_len > 2 && (recv_buff[2] < 0x90 || recv_buff[2] > 0x98) || recv_len > 3 && recv_buff[3] != 8 || - recv_len > 12 && recv_buff[12] != calculate_checksum(recv_buff)) { + if (((recv_len > 0) && (recv_buff[0] != 0xA5)) || ((recv_len > 1) && (recv_buff[1] != 0x01)) || + ((recv_len > 2) && ((recv_buff[2] < 0x90) || (recv_buff[2] > 0x98))) || + ((recv_len > 3) && (recv_buff[3] != 8)) || + ((recv_len > 12) && (recv_buff[12] != calculate_checksum(recv_buff)))) { dump_buff("dropping partial rx: ", recv_buff, recv_len); recv_len = 0; } diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index ebcd0c27e..68f59d4a9 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -34,7 +34,7 @@ void EcmpBattery::update_values() { if (!MysteryVan) { //Normal eCMP platform datalayer.battery.status.real_soc = battery_soc * 10; - datalayer.battery.status.soh_pptt; + //datalayer.battery.status.soh_pptt; //TODO: Find SOH% datalayer.battery.status.voltage_dV = battery_voltage * 10; @@ -84,7 +84,7 @@ void EcmpBattery::update_values() { 10000); } - datalayer.battery.status.soh_pptt; + //datalayer.battery.status.soh_pptt; //TODO: Find SOH% if (pid_pack_voltage != NOT_SAMPLED_YET) { datalayer.battery.status.voltage_dV = pid_pack_voltage + 800; @@ -171,7 +171,7 @@ void EcmpBattery::update_values() { datalayer_extended.stellantisECMP.pid_sw_version_num = pid_sw_version_num; datalayer_extended.stellantisECMP.pid_factory_mode_control = pid_factory_mode_control; memcpy(datalayer_extended.stellantisECMP.pid_battery_serial, pid_battery_serial, sizeof(pid_battery_serial)); - uint8_t pid_battery_serial[13] = {0}; + //uint8_t pid_battery_serial[13] = {0}; datalayer_extended.stellantisECMP.pid_aux_fuse_state = pid_aux_fuse_state; datalayer_extended.stellantisECMP.pid_battery_state = pid_battery_state; datalayer_extended.stellantisECMP.pid_precharge_short_circuit = pid_precharge_short_circuit; diff --git a/Software/src/battery/ECMP-BATTERY.h b/Software/src/battery/ECMP-BATTERY.h index 9a5ad7e15..0b3a6bf11 100644 --- a/Software/src/battery/ECMP-BATTERY.h +++ b/Software/src/battery/ECMP-BATTERY.h @@ -112,7 +112,7 @@ class EcmpBattery : public CanBattery { uint32_t pid_hw_version_num = NOT_SAMPLED_YET; uint32_t pid_sw_version_num = NOT_SAMPLED_YET; uint32_t pid_factory_mode_control = NOT_SAMPLED_YET; - uint8_t pid_battery_serial[13] = {0}; + uint8_t pid_battery_serial[14] = {0}; uint32_t pid_aux_fuse_state = NOT_SAMPLED_YET; uint32_t pid_battery_state = NOT_SAMPLED_YET; uint32_t pid_precharge_short_circuit = NOT_SAMPLED_YET; diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp index 35b458294..560b6944d 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp @@ -55,13 +55,13 @@ smart booster, ESC, airbag control module, automatic parking module /* Do not change code below unless you are sure what you are doing */ void GeelyGeometryCBattery::update_values() { - datalayer_battery->status.soh_pptt; + //datalayer_battery->status.soh_pptt; //TODO: Fix datalayer_battery->status.real_soc = poll_soc * 10; datalayer_battery->status.voltage_dV = battery_voltage; - datalayer_battery->status.current_dA; + //datalayer_battery->status.current_dA; //TODO: Fix if (poll_amount_cells == 102) { // We have determined that we are on 70kWh pack datalayer_battery->info.total_capacity_Wh = 70000; @@ -75,7 +75,7 @@ void GeelyGeometryCBattery::update_values() { datalayer_battery->status.max_discharge_power_W = discharge_power_allowed * 100; - datalayer_battery->status.max_charge_power_W; + //datalayer_battery->status.max_charge_power_W; //TODO: Fix datalayer_battery->status.cell_min_voltage_mV = maximum_cell_voltage - 10; //TODO: Fix once we have min value diff --git a/Software/src/battery/MG-5-BATTERY.cpp b/Software/src/battery/MG-5-BATTERY.cpp index c5cd6cefc..f82003301 100644 --- a/Software/src/battery/MG-5-BATTERY.cpp +++ b/Software/src/battery/MG-5-BATTERY.cpp @@ -14,23 +14,23 @@ void Mg5Battery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus - datalayer.battery.status.real_soc; + //datalayer.battery.status.real_soc; - datalayer.battery.status.voltage_dV; + //datalayer.battery.status.voltage_dV; - datalayer.battery.status.current_dA; + //datalayer.battery.status.current_dA; - datalayer.battery.info.total_capacity_Wh; + //datalayer.battery.info.total_capacity_Wh; - datalayer.battery.status.remaining_capacity_Wh; + //datalayer.battery.status.remaining_capacity_Wh; - datalayer.battery.status.max_discharge_power_W; + //datalayer.battery.status.max_discharge_power_W; - datalayer.battery.status.max_charge_power_W; + //datalayer.battery.status.max_charge_power_W; - datalayer.battery.status.temperature_min_dC; + //datalayer.battery.status.temperature_min_dC; - datalayer.battery.status.temperature_max_dC; + //datalayer.battery.status.temperature_max_dC; } void Mg5Battery::handle_incoming_can_frame(CAN_frame rx_frame) { diff --git a/Software/src/battery/MG-HS-PHEV-BATTERY.cpp b/Software/src/battery/MG-HS-PHEV-BATTERY.cpp index 8d7466ce5..d88b330d5 100644 --- a/Software/src/battery/MG-HS-PHEV-BATTERY.cpp +++ b/Software/src/battery/MG-HS-PHEV-BATTERY.cpp @@ -122,11 +122,9 @@ void MgHsPHEVBattery::update_soc(uint16_t soc_times_ten) { } void MgHsPHEVBattery::handle_incoming_can_frame(CAN_frame rx_frame) { - uint16_t soc1, soc2, cell_id, v; switch (rx_frame.ID) { case 0x173: // Contains cell min/max voltages - v = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; if (v > 0 && v < 0x2000) { datalayer.battery.status.cell_max_voltage_mV = v; @@ -220,9 +218,9 @@ void MgHsPHEVBattery::handle_incoming_can_frame(CAN_frame rx_frame) { datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; } - if (((rx_frame.data.u8[4] << 8) & 0xf00 | rx_frame.data.u8[5]) != 0) { + if ((((rx_frame.data.u8[4] & 0x0F) << 8) | rx_frame.data.u8[5]) != 0) { // 3AC message contains a nonzero voltage (so must have come from CAN1) - v = (rx_frame.data.u8[4] << 8) & 0xf00 | rx_frame.data.u8[5]; + v = (((rx_frame.data.u8[4] & 0x0F) << 8) | rx_frame.data.u8[5]); if (v > 0 && v < 4000) { datalayer.battery.status.voltage_dV = v * 2.5; } @@ -238,7 +236,7 @@ void MgHsPHEVBattery::handle_incoming_can_frame(CAN_frame rx_frame) { // Per-cell voltages and temps cell_id = rx_frame.data.u8[5]; if (cell_id < 90) { - v = 1000 + (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + v = 1000 + ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); datalayer.battery.status.cell_voltages_mV[cell_id] = v < 10000 ? v : 0; // cell temperature is rx_frame.data.u8[1]-40 but BE doesn't use it } diff --git a/Software/src/battery/MG-HS-PHEV-BATTERY.h b/Software/src/battery/MG-HS-PHEV-BATTERY.h index 7a9ade7bd..9f8642535 100644 --- a/Software/src/battery/MG-HS-PHEV-BATTERY.h +++ b/Software/src/battery/MG-HS-PHEV-BATTERY.h @@ -33,7 +33,10 @@ class MgHsPHEVBattery : public CanBattery { static const uint16_t CELL_VOLTAGE_TIMEOUT = 10; // in seconds uint16_t cellVoltageValidTime = 0; - + uint16_t soc1 = 0; + uint16_t soc2 = 0; + uint16_t cell_id = 0; + uint16_t v = 0; uint8_t transmitIndex = 0; //For polling switchcase const int MaxChargePower = 3000; // Maximum allowable charge power, excluding the taper diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 2bebb92f4..95dd484a5 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -965,13 +965,13 @@ unsigned int CyclicXorHash16Bit(unsigned int param_1, unsigned int param_2) { return uVar10; } unsigned int ComputeMaskedXorProduct(unsigned int param_1, unsigned int param_2, unsigned int param_3) { - return (param_3 ^ 0x7F88 | param_2 ^ 0x8FE7) * ((param_1 & 0xffff) >> 8 ^ param_1 & 0xff) & 0xffff; + return ((param_3 ^ 0x7F88) | (param_2 ^ 0x8FE7)) * ((((param_1 & 0xffff) >> 8) ^ (param_1 & 0xff))) & 0xffff; } short ShortMaskedSumAndProduct(short param_1, short param_2) { unsigned short uVar1; - uVar1 = param_2 + param_1 * 0x0006 & 0xff; + uVar1 = (param_2 + (param_1 * 0x0006)) & 0xff; return (uVar1 + param_1) * (uVar1 + param_2); } @@ -981,8 +981,8 @@ unsigned int MaskedBitwiseRotateMultiply(unsigned int param_1, unsigned int para param_1 = param_1 & 0xffff; param_2 = param_2 & 0xffff; uVar1 = param_2 & (param_1 | 0x0006) & 0xf; - return ((unsigned int)param_1 >> uVar1 | param_1 << (0x10 - uVar1 & 0x1f)) * - (param_2 << uVar1 | (unsigned int)param_2 >> (0x10 - uVar1 & 0x1f)) & + return ((unsigned int)param_1 >> uVar1 | param_1 << (0x10 - (uVar1 & 0x1f))) * + (param_2 << uVar1 | (unsigned int)param_2 >> (0x10 - (uVar1 & 0x1f))) & 0xffff; } diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp index 71fc5e855..7cd6ff69e 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp @@ -9,9 +9,8 @@ The Zoe BMS apparently does not send total pack voltage, so we use the polled 96x cellvoltages summed up as total voltage Still TODO: - Automatically detect what vehicle and battery size we are on (Zoe 22/41 , Kangoo 33, Fluence ZE 22/36) -/* -/* Do not change code below unless you are sure what you are doing */ + Do not change code below unless you are sure what you are doing */ CAN_frame ZOE_423 = {.FD = false, .ext_ID = false, diff --git a/Software/src/battery/RJXZS-BMS.cpp b/Software/src/battery/RJXZS-BMS.cpp index 3017ae8e2..cf876b53a 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -21,7 +21,7 @@ void RjxzsBms::update_values() { 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.soh_pptt; // This BMS does not have a SOH% formula + //datalayer.battery.status.soh_pptt; // This BMS does not have a SOH% formula datalayer.battery.status.voltage_dV = total_voltage; @@ -78,9 +78,9 @@ void RjxzsBms::update_values() { datalayer.battery.info.number_of_cells = populated_cellvoltages; // 1-192S - datalayer.battery.info.max_design_voltage_dV; // Set according to cells? + //datalayer.battery.info.max_design_voltage_dV; // TODO: Set according to cells? - datalayer.battery.info.min_design_voltage_dV; // Set according to cells? + //datalayer.battery.info.min_design_voltage_dV; // TODO: Set according to cells? datalayer.battery.status.cell_max_voltage_mV = maximum_cell_voltage; diff --git a/Software/src/battery/TESLA-BATTERY.h b/Software/src/battery/TESLA-BATTERY.h index 851522cc4..6713bc7f2 100644 --- a/Software/src/battery/TESLA-BATTERY.h +++ b/Software/src/battery/TESLA-BATTERY.h @@ -632,7 +632,7 @@ class TeslaBattery : public CanBattery { bool BMS_pcsNoFlowRequest = false; bool BMS_noFlowRequest = false; //0x3C4: PCS_info - uint8_t PCS_partNumber[12] = {0}; //stores raw HEX values for ASCII chars + uint8_t PCS_partNumber[13] = {0}; //stores raw HEX values for ASCII chars bool parsed_PCS_partNumber = false; uint16_t PCS_info_buildConfigId = 0; uint16_t PCS_info_hardwareId = 0; diff --git a/Software/src/battery/TESLA-HTML.h b/Software/src/battery/TESLA-HTML.h index c5f270dc0..da2d80bad 100644 --- a/Software/src/battery/TESLA-HTML.h +++ b/Software/src/battery/TESLA-HTML.h @@ -50,8 +50,8 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer { float soc_ui = static_cast(datalayer_extended.tesla.battery_soc_ui) * 0.1; float BrickVoltageMax = static_cast(datalayer_extended.tesla.battery_BrickVoltageMax) * 0.002; float BrickVoltageMin = static_cast(datalayer_extended.tesla.battery_BrickVoltageMin) * 0.002; - float BrickModelTMax = static_cast(datalayer_extended.tesla.battery_BrickModelTMax) * 0.5 - 40; - float BrickModelTMin = static_cast(datalayer_extended.tesla.battery_BrickModelTMin) * 0.5 - 40; + //float BrickModelTMax = static_cast(datalayer_extended.tesla.battery_BrickModelTMax) * 0.5 - 40; + //float BrickModelTMin = static_cast(datalayer_extended.tesla.battery_BrickModelTMin) * 0.5 - 40; float isolationResistance = static_cast(datalayer_extended.tesla.BMS_isolationResistance) * 10; float PCS_dcdcMaxOutputCurrentAllowed = static_cast(datalayer_extended.tesla.PCS_dcdcMaxOutputCurrentAllowed) * 0.1; @@ -62,8 +62,8 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer { float PCS_chgPhCTemp = static_cast(datalayer_extended.tesla.PCS_chgPhCTemp) * 0.1 + 40; float BMS_maxRegenPower = static_cast(datalayer_extended.tesla.BMS_maxRegenPower) * 0.01; float BMS_maxDischargePower = static_cast(datalayer_extended.tesla.BMS_maxDischargePower) * 0.013; - float BMS_maxStationaryHeatPower = static_cast(datalayer_extended.tesla.BMS_maxStationaryHeatPower) * 0.01; - float BMS_hvacPowerBudget = static_cast(datalayer_extended.tesla.BMS_hvacPowerBudget) * 0.02; + //float BMS_maxStationaryHeatPower = static_cast(datalayer_extended.tesla.BMS_maxStationaryHeatPower) * 0.01; + //float BMS_hvacPowerBudget = static_cast(datalayer_extended.tesla.BMS_hvacPowerBudget) * 0.02; float BMS_powerDissipation = static_cast(datalayer_extended.tesla.BMS_powerDissipation) * 0.02; float BMS_flowRequest = static_cast(datalayer_extended.tesla.BMS_flowRequest) * 0.3; float BMS_inletActiveCoolTargetT = @@ -106,25 +106,25 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer { float HVP_shuntCurrentDebug = static_cast(datalayer_extended.tesla.HVP_shuntCurrentDebug) * 0.1; float HVP_dcLinkVoltage = static_cast(datalayer_extended.tesla.HVP_dcLinkVoltage) * 0.1; float HVP_packVoltage = static_cast(datalayer_extended.tesla.HVP_packVoltage) * 0.1; - float HVP_fcLinkVoltage = static_cast(datalayer_extended.tesla.HVP_fcLinkVoltage) * 0.1; + //float HVP_fcLinkVoltage = static_cast(datalayer_extended.tesla.HVP_fcLinkVoltage) * 0.1; float HVP_packContVoltage = static_cast(datalayer_extended.tesla.HVP_packContVoltage) * 0.1; - float HVP_packNegativeV = static_cast(datalayer_extended.tesla.HVP_packNegativeV) * 0.1; - float HVP_packPositiveV = static_cast(datalayer_extended.tesla.HVP_packPositiveV) * 0.1; + //float HVP_packNegativeV = static_cast(datalayer_extended.tesla.HVP_packNegativeV) * 0.1; + //float HVP_packPositiveV = static_cast(datalayer_extended.tesla.HVP_packPositiveV) * 0.1; float HVP_pyroAnalog = static_cast(datalayer_extended.tesla.HVP_pyroAnalog) * 0.1; - float HVP_dcLinkNegativeV = static_cast(datalayer_extended.tesla.HVP_dcLinkNegativeV) * 0.1; - float HVP_dcLinkPositiveV = static_cast(datalayer_extended.tesla.HVP_dcLinkPositiveV) * 0.1; - float HVP_fcLinkNegativeV = static_cast(datalayer_extended.tesla.HVP_fcLinkNegativeV) * 0.1; - float HVP_fcContCoilCurrent = static_cast(datalayer_extended.tesla.HVP_fcContCoilCurrent) * 0.1; - float HVP_fcContVoltage = static_cast(datalayer_extended.tesla.HVP_fcContVoltage) * 0.1; + //float HVP_dcLinkNegativeV = static_cast(datalayer_extended.tesla.HVP_dcLinkNegativeV) * 0.1; + //float HVP_dcLinkPositiveV = static_cast(datalayer_extended.tesla.HVP_dcLinkPositiveV) * 0.1; + //float HVP_fcLinkNegativeV = static_cast(datalayer_extended.tesla.HVP_fcLinkNegativeV) * 0.1; + //float HVP_fcContCoilCurrent = static_cast(datalayer_extended.tesla.HVP_fcContCoilCurrent) * 0.1; + //float HVP_fcContVoltage = static_cast(datalayer_extended.tesla.HVP_fcContVoltage) * 0.1; float HVP_hvilInVoltage = static_cast(datalayer_extended.tesla.HVP_hvilInVoltage) * 0.1; float HVP_hvilOutVoltage = static_cast(datalayer_extended.tesla.HVP_hvilOutVoltage) * 0.1; - float HVP_fcLinkPositiveV = static_cast(datalayer_extended.tesla.HVP_fcLinkPositiveV) * 0.1; + //float HVP_fcLinkPositiveV = static_cast(datalayer_extended.tesla.HVP_fcLinkPositiveV) * 0.1; float HVP_packContCoilCurrent = static_cast(datalayer_extended.tesla.HVP_packContCoilCurrent) * 0.1; float HVP_battery12V = static_cast(datalayer_extended.tesla.HVP_battery12V) * 0.1; - float HVP_shuntRefVoltageDbg = static_cast(datalayer_extended.tesla.HVP_shuntRefVoltageDbg) * 0.001; - float HVP_shuntAuxCurrentDbg = static_cast(datalayer_extended.tesla.HVP_shuntAuxCurrentDbg) * 0.1; - float HVP_shuntBarTempDbg = static_cast(datalayer_extended.tesla.HVP_shuntBarTempDbg) * 0.01; - float HVP_shuntAsicTempDbg = static_cast(datalayer_extended.tesla.HVP_shuntAsicTempDbg) * 0.01; + //float HVP_shuntRefVoltageDbg = static_cast(datalayer_extended.tesla.HVP_shuntRefVoltageDbg) * 0.001; + //float HVP_shuntAuxCurrentDbg = static_cast(datalayer_extended.tesla.HVP_shuntAuxCurrentDbg) * 0.1; + //float HVP_shuntBarTempDbg = static_cast(datalayer_extended.tesla.HVP_shuntBarTempDbg) * 0.01; + //float HVP_shuntAsicTempDbg = static_cast(datalayer_extended.tesla.HVP_shuntAsicTempDbg) * 0.01; static const char* contactorText[] = {"UNKNOWN(0)", "OPEN", "CLOSING", "BLOCKED", "OPENING", "CLOSED", "UNKNOWN(6)", "WELDED", "POS_CL", "NEG_CL", @@ -178,7 +178,7 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer { "FAULTED", "NUM"}; static const char* BMS_powerLimitState[] = {"NOT_CALCULATED_FOR_DRIVE", "CALCULATED_FOR_DRIVE"}; - static const char* HVP_status[] = {"INVALID", "NOT_AVAILABLE", "STALE", "VALID"}; + //static const char* HVP_status[] = {"INVALID", "NOT_AVAILABLE", "STALE", "VALID"}; static const char* HVP_contactor[] = {"NOT_ACTIVE", "ACTIVE", "COMPLETED"}; static const char* falseTrue[] = {"False", "True"}; static const char* noYes[] = {"No", "Yes"}; @@ -199,7 +199,7 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer { char readablePCSPartNumber[13]; // One extra space for null terminator memcpy(readablePCSPartNumber, datalayer_extended.tesla.PCS_partNumber, sizeof(datalayer_extended.tesla.PCS_partNumber)); - readablePCSPartNumber[13] = '\0'; // Null terminate the string + readablePCSPartNumber[12] = '\0'; // Null terminate the string content += "

PCS Part Number: " + String(readablePCSPartNumber) + "

"; content += "

Battery Manufacture Date: " + String(datalayer_extended.tesla.battery_manufactureDate) + "

"; content += "

Battery Pack Mass: " + String(packMass) + " KG

"; diff --git a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp index d4c68d072..89b5bfb7e 100644 --- a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp @@ -8,7 +8,6 @@ void VolvoSpaHybridBattery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter - uint8_t cnt = 0; // Update webserver datalayer datalayer_extended.VolvoHybrid.soc_bms = SOC_BMS; diff --git a/Software/src/charger/CHARGERS.cpp b/Software/src/charger/CHARGERS.cpp index 8c9080e74..e971de71e 100644 --- a/Software/src/charger/CHARGERS.cpp +++ b/Software/src/charger/CHARGERS.cpp @@ -32,6 +32,7 @@ extern const char* name_for_charger_type(ChargerType type) { case ChargerType::NissanLeaf: return NissanLeafCharger::Name; case ChargerType::None: + case ChargerType::Highest: return "None"; } @@ -47,5 +48,8 @@ void setup_charger() { case ChargerType::NissanLeaf: charger = new NissanLeafCharger(); break; + case ChargerType::None: + case ChargerType::Highest: + break; } } diff --git a/Software/src/charger/NISSAN-LEAF-CHARGER.cpp b/Software/src/charger/NISSAN-LEAF-CHARGER.cpp index 92f7a7802..3fa58dcfa 100644 --- a/Software/src/charger/NISSAN-LEAF-CHARGER.cpp +++ b/Software/src/charger/NISSAN-LEAF-CHARGER.cpp @@ -67,7 +67,7 @@ void NissanLeafCharger::map_can_frame_to_variable(CAN_frame rx_frame) { case 0x390: datalayer.charger.CAN_charger_still_alive = CAN_STILL_ALIVE; // Let system know charger is sending CAN OBC_Charge_Status = ((rx_frame.data.u8[5] & 0x7E) >> 1); - if (OBC_Charge_Status == PLUGGED_IN_WAITING_ON_TIMER || CHARGING_OR_INTERRUPTED) { + if ((OBC_Charge_Status == PLUGGED_IN_WAITING_ON_TIMER) || (OBC_Charge_Status == CHARGING_OR_INTERRUPTED)) { PPStatus = true; //plug inserted } else { PPStatus = false; //plug not inserted diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index fcce2ea6c..5e2d676e0 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -639,6 +639,20 @@ static void mqtt_event_handler(void* handler_args, esp_event_base_t base, int32_ logging.print("captured as transport's socket errno"); logging.println(strerror(event->error_handle->esp_transport_sock_errno)); break; + case MQTT_EVENT_SUBSCRIBED: + break; + case MQTT_EVENT_UNSUBSCRIBED: + break; + case MQTT_EVENT_PUBLISHED: + break; + case MQTT_EVENT_BEFORE_CONNECT: + break; + case MQTT_EVENT_DELETED: + break; + case MQTT_USER_EVENT: + break; + case MQTT_EVENT_ANY: + break; } } diff --git a/Software/src/inverter/KOSTAL-RS485.cpp b/Software/src/inverter/KOSTAL-RS485.cpp index 425e9d292..3caf14208 100644 --- a/Software/src/inverter/KOSTAL-RS485.cpp +++ b/Software/src/inverter/KOSTAL-RS485.cpp @@ -212,7 +212,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th if (datalayer.system.status.battery_allows_contactor_closing & !contactorMillis) { contactorMillis = currentMillis; } - if (currentMillis - contactorMillis >= INTERVAL_2_S & !RX_allow) { + if ((currentMillis - contactorMillis >= INTERVAL_2_S) && !RX_allow) { dbg_message("RX_allow -> true"); RX_allow = true; } diff --git a/Software/src/inverter/KOSTAL-RS485.h b/Software/src/inverter/KOSTAL-RS485.h index d67b74380..f86ef58ca 100644 --- a/Software/src/inverter/KOSTAL-RS485.h +++ b/Software/src/inverter/KOSTAL-RS485.h @@ -15,8 +15,8 @@ class KostalInverterProtocol : public Rs485InverterProtocol { int baud_rate() { return 57600; } void float2frame(uint8_t* arr, float value, uint8_t framepointer); bool check_kostal_frame_crc(int len); - // How many value updates we can go without inverter gets reported as missing \ - // e.g. value set to 12, 12*5sec=60seconds without comm before event is raised + /* How many value updates we can go without inverter gets reported as missing + e.g. value set to 12, 12*5sec=60seconds without comm before event is raised */ const int RS485_HEALTHY = 12; const uint8_t KOSTAL_FRAMEHEADER[5] = {0x62, 0xFF, 0x02, 0xFF, 0x29}; diff --git a/Software/src/inverter/SOLAX-CAN.cpp b/Software/src/inverter/SOLAX-CAN.cpp index 4117a2a90..c4b73fcc7 100644 --- a/Software/src/inverter/SOLAX-CAN.cpp +++ b/Software/src/inverter/SOLAX-CAN.cpp @@ -120,71 +120,14 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) { if (rx_frame.ID == 0x1871) { datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; - } - 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; - } + if ((rx_frame.data.u8[0] == (0x01)) || (rx_frame.data.u8[0] == (0x02))) { + LastFrameTime = millis(); - switch (STATE) { - case (BATTERY_ANNOUNCE): - logging.println("Solax Battery State: Announce"); - datalayer.system.status.inverter_allows_contactor_closing = false; - SOLAX_1875.data.u8[4] = (0x00); // Inform Inverter: Contactor 0=off, 1=on. - for (uint8_t i = 0; i < number_of_batteries; i++) { - 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); //BMS Announce - // Message from the inverter to proceed to contactor closing - // Byte 4 changes from 0 to 1 - if (rx_frame.data.u64 == Contactor_Close_Payload) - STATE = WAITING_FOR_CONTACTOR; - break; - - case (WAITING_FOR_CONTACTOR): - SOLAX_1875.data.u8[4] = (0x00); // 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_1801); // Announce that the battery will be connected - STATE = CONTACTOR_CLOSED; // Jump to Contactor Closed State - logging.println("Solax Battery State: Contactor Closed"); - break; + 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. - case (CONTACTOR_CLOSED): 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); @@ -196,13 +139,69 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) { transmit_can_frame(&SOLAX_1876); transmit_can_frame(&SOLAX_1877); transmit_can_frame(&SOLAX_1878); - // Message from the inverter to open contactor - // Byte 4 changes from 1 to 0 - if (rx_frame.data.u64 == Contactor_Open_Payload) { - set_event(EVENT_INVERTER_OPEN_CONTACTOR, 0); - STATE = BATTERY_ANNOUNCE; - } - break; + transmit_can_frame(&SOLAX_100A001); + return; + } + + switch (STATE) { + case (BATTERY_ANNOUNCE): + logging.println("Solax Battery State: Announce"); + datalayer.system.status.inverter_allows_contactor_closing = false; + SOLAX_1875.data.u8[4] = (0x00); // Inform Inverter: Contactor 0=off, 1=on. + for (uint8_t i = 0; i < number_of_batteries; i++) { + 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); //BMS Announce + // Message from the inverter to proceed to contactor closing + // Byte 4 changes from 0 to 1 + if (rx_frame.data.u64 == Contactor_Close_Payload) + STATE = WAITING_FOR_CONTACTOR; + break; + + case (WAITING_FOR_CONTACTOR): + SOLAX_1875.data.u8[4] = (0x00); // 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_1801); // Announce that the battery will be connected + STATE = CONTACTOR_CLOSED; // Jump to Contactor Closed State + logging.println("Solax Battery State: Contactor Closed"); + break; + + case (CONTACTOR_CLOSED): + 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); + // Message from the inverter to open contactor + // Byte 4 changes from 1 to 0 + if (rx_frame.data.u64 == Contactor_Open_Payload) { + set_event(EVENT_INVERTER_OPEN_CONTACTOR, 0); + STATE = BATTERY_ANNOUNCE; + } + break; + } } } diff --git a/Software/src/inverter/SOLXPOW-CAN.cpp b/Software/src/inverter/SOLXPOW-CAN.cpp index e54c0dfee..512e43ca3 100644 --- a/Software/src/inverter/SOLXPOW-CAN.cpp +++ b/Software/src/inverter/SOLXPOW-CAN.cpp @@ -3,10 +3,7 @@ #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) -#define INVERT_LOW_HIGH_BYTES //If defined, certain frames will have inverted low/high bytes \ - //useful for some inverters like Sofar that report the voltages incorrect otherwise +#define INVERT_LOW_HIGH_BYTES //If defined, certain frames will have inverted low/high bytes //#define SET_30K_OFFSET //If defined, current values are sent with a 30k offest (useful for ferroamp) void SolxpowInverter:: @@ -22,278 +19,193 @@ void SolxpowInverter:: } //There are more mappings that could be added, but this should be enough to use as a starting point - // Note we map both 0 and 1 messages //Charge / Discharge allowed flags if (datalayer.battery.status.max_charge_current_dA == 0) { SOLXPOW_4280.data.u8[0] = 0xAA; //Charge forbidden - SOLXPOW_4281.data.u8[0] = 0xAA; } else { SOLXPOW_4280.data.u8[0] = 0; //Charge allowed - SOLXPOW_4281.data.u8[0] = 0; } if (datalayer.battery.status.max_discharge_current_dA == 0) { SOLXPOW_4280.data.u8[1] = 0xAA; //Discharge forbidden - SOLXPOW_4281.data.u8[1] = 0xAA; } else { SOLXPOW_4280.data.u8[1] = 0; //Discharge allowed - SOLXPOW_4281.data.u8[1] = 0; } //In case run into a FAULT state, let inverter know to stop any charge/discharge if (datalayer.battery.status.bms_status == FAULT) { SOLXPOW_4280.data.u8[0] = 0xAA; //Charge forbidden SOLXPOW_4280.data.u8[1] = 0xAA; //Discharge forbidden - SOLXPOW_4281.data.u8[0] = 0xAA; //Charge forbidden - SOLXPOW_4281.data.u8[1] = 0xAA; //Discharge forbidden } //Voltage (370.0) SOLXPOW_4210.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8); SOLXPOW_4210.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF); - SOLXPOW_4211.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8); - SOLXPOW_4211.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF); //Current (15.0) SOLXPOW_4210.data.u8[2] = (datalayer.battery.status.current_dA >> 8); SOLXPOW_4210.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF); - SOLXPOW_4211.data.u8[2] = (datalayer.battery.status.current_dA >> 8); - SOLXPOW_4211.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF); // BMS Temperature (We dont have BMS temp, send max cell voltage instead) #ifdef INVERT_LOW_HIGH_BYTES //Useful for Sofar inverters SOLXPOW_4210.data.u8[4] = ((datalayer.battery.status.temperature_max_dC + 1000) & 0x00FF); SOLXPOW_4210.data.u8[5] = ((datalayer.battery.status.temperature_max_dC + 1000) >> 8); - SOLXPOW_4211.data.u8[4] = ((datalayer.battery.status.temperature_max_dC + 1000) & 0x00FF); - SOLXPOW_4211.data.u8[5] = ((datalayer.battery.status.temperature_max_dC + 1000) >> 8); #else // Not INVERT_LOW_HIGH_BYTES SOLXPOW_4210.data.u8[4] = ((datalayer.battery.status.temperature_max_dC + 1000) >> 8); SOLXPOW_4210.data.u8[5] = ((datalayer.battery.status.temperature_max_dC + 1000) & 0x00FF); - SOLXPOW_4211.data.u8[4] = ((datalayer.battery.status.temperature_max_dC + 1000) >> 8); - SOLXPOW_4211.data.u8[5] = ((datalayer.battery.status.temperature_max_dC + 1000) & 0x00FF); #endif // INVERT_LOW_HIGH_BYTES //SOC (100.00%) SOLXPOW_4210.data.u8[6] = (datalayer.battery.status.reported_soc / 100); //Remove decimals - SOLXPOW_4211.data.u8[6] = (datalayer.battery.status.reported_soc / 100); //Remove decimals //StateOfHealth (100.00%) SOLXPOW_4210.data.u8[7] = (datalayer.battery.status.soh_pptt / 100); - SOLXPOW_4211.data.u8[7] = (datalayer.battery.status.soh_pptt / 100); // Status=Bit 0,1,2= 0:Sleep, 1:Charge, 2:Discharge 3:Idle. Bit3 ForceChargeReq. Bit4 Balance charge Request if (datalayer.battery.status.bms_status == FAULT) { SOLXPOW_4250.data.u8[0] = (0x00); // Sleep - SOLXPOW_4251.data.u8[0] = (0x00); // Sleep } else if (datalayer.battery.status.current_dA < 0) { SOLXPOW_4250.data.u8[0] = (0x01); // Charge - SOLXPOW_4251.data.u8[0] = (0x01); // Charge } else if (datalayer.battery.status.current_dA > 0) { SOLXPOW_4250.data.u8[0] = (0x02); // Discharge - SOLXPOW_4251.data.u8[0] = (0x02); // Discharge } else if (datalayer.battery.status.current_dA == 0) { SOLXPOW_4250.data.u8[0] = (0x03); // Idle - SOLXPOW_4251.data.u8[0] = (0x03); // Idle } #ifdef INVERT_LOW_HIGH_BYTES //Useful for Sofar inverters //Voltage (370.0) SOLXPOW_4210.data.u8[0] = (datalayer.battery.status.voltage_dV & 0x00FF); SOLXPOW_4210.data.u8[1] = (datalayer.battery.status.voltage_dV >> 8); - SOLXPOW_4211.data.u8[0] = (datalayer.battery.status.voltage_dV & 0x00FF); - SOLXPOW_4211.data.u8[1] = (datalayer.battery.status.voltage_dV >> 8); #ifdef SET_30K_OFFSET //Current (15.0) SOLXPOW_4210.data.u8[2] = ((datalayer.battery.status.current_dA + 30000) & 0x00FF); SOLXPOW_4210.data.u8[3] = ((datalayer.battery.status.current_dA + 30000) >> 8); - SOLXPOW_4211.data.u8[2] = ((datalayer.battery.status.current_dA + 30000) & 0x00FF); - SOLXPOW_4211.data.u8[3] = ((datalayer.battery.status.current_dA + 30000) >> 8); #else // Not SET_30K_OFFSET SOLXPOW_4210.data.u8[2] = (datalayer.battery.status.current_dA & 0x00FF); SOLXPOW_4210.data.u8[3] = (datalayer.battery.status.current_dA >> 8); - SOLXPOW_4211.data.u8[2] = (datalayer.battery.status.current_dA & 0x00FF); - SOLXPOW_4211.data.u8[3] = (datalayer.battery.status.current_dA >> 8); #endif //SET_30K_OFFSET // BMS Temperature (We dont have BMS temp, send max cell voltage instead) SOLXPOW_4210.data.u8[4] = ((datalayer.battery.status.temperature_max_dC + 1000) & 0x00FF); SOLXPOW_4210.data.u8[5] = ((datalayer.battery.status.temperature_max_dC + 1000) >> 8); - SOLXPOW_4211.data.u8[4] = ((datalayer.battery.status.temperature_max_dC + 1000) & 0x00FF); - SOLXPOW_4211.data.u8[5] = ((datalayer.battery.status.temperature_max_dC + 1000) >> 8); //Maxvoltage (eg 400.0V = 4000 , 16bits long) Charge Cutoff Voltage SOLXPOW_4220.data.u8[0] = (charge_cutoff_voltage_dV & 0x00FF); SOLXPOW_4220.data.u8[1] = (charge_cutoff_voltage_dV >> 8); - SOLXPOW_4221.data.u8[0] = (charge_cutoff_voltage_dV & 0x00FF); - SOLXPOW_4221.data.u8[1] = (charge_cutoff_voltage_dV >> 8); //Minvoltage (eg 300.0V = 3000 , 16bits long) Discharge Cutoff Voltage SOLXPOW_4220.data.u8[2] = (discharge_cutoff_voltage_dV & 0x00FF); SOLXPOW_4220.data.u8[3] = (discharge_cutoff_voltage_dV >> 8); - SOLXPOW_4221.data.u8[2] = (discharge_cutoff_voltage_dV & 0x00FF); - SOLXPOW_4221.data.u8[3] = (discharge_cutoff_voltage_dV >> 8); #ifdef SET_30K_OFFSET //Max ChargeCurrent SOLXPOW_4220.data.u8[4] = ((datalayer.battery.status.max_charge_current_dA + 30000) & 0x00FF); SOLXPOW_4220.data.u8[5] = ((datalayer.battery.status.max_charge_current_dA + 30000) >> 8); - SOLXPOW_4221.data.u8[4] = ((datalayer.battery.status.max_charge_current_dA + 30000) & 0x00FF); - SOLXPOW_4221.data.u8[5] = ((datalayer.battery.status.max_charge_current_dA + 30000) >> 8); //Max DischargeCurrent SOLXPOW_4220.data.u8[6] = ((30000 - datalayer.battery.status.max_discharge_current_dA) & 0x00FF); SOLXPOW_4220.data.u8[7] = ((30000 - datalayer.battery.status.max_discharge_current_dA) >> 8); - SOLXPOW_4221.data.u8[6] = ((30000 - datalayer.battery.status.max_discharge_current_dA) & 0x00FF); - SOLXPOW_4221.data.u8[7] = ((30000 - datalayer.battery.status.max_discharge_current_dA) >> 8); #else // Not SET_30K_OFFSET //Max ChargeCurrent SOLXPOW_4220.data.u8[4] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); SOLXPOW_4220.data.u8[5] = (datalayer.battery.status.max_charge_current_dA >> 8); - SOLXPOW_4221.data.u8[4] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); - SOLXPOW_4221.data.u8[5] = (datalayer.battery.status.max_charge_current_dA >> 8); //Max DishargeCurrent SOLXPOW_4220.data.u8[6] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); SOLXPOW_4220.data.u8[7] = (datalayer.battery.status.max_discharge_current_dA >> 8); - SOLXPOW_4221.data.u8[6] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); - SOLXPOW_4221.data.u8[7] = (datalayer.battery.status.max_discharge_current_dA >> 8); #endif // SET_30K_OFFSET //Max cell voltage SOLXPOW_4230.data.u8[0] = (datalayer.battery.status.cell_max_voltage_mV & 0x00FF); SOLXPOW_4230.data.u8[1] = (datalayer.battery.status.cell_max_voltage_mV >> 8); - SOLXPOW_4231.data.u8[0] = (datalayer.battery.status.cell_max_voltage_mV & 0x00FF); - SOLXPOW_4231.data.u8[1] = (datalayer.battery.status.cell_max_voltage_mV >> 8); //Min cell voltage SOLXPOW_4230.data.u8[2] = (datalayer.battery.status.cell_min_voltage_mV & 0x00FF); SOLXPOW_4230.data.u8[3] = (datalayer.battery.status.cell_min_voltage_mV >> 8); - SOLXPOW_4231.data.u8[2] = (datalayer.battery.status.cell_min_voltage_mV & 0x00FF); - SOLXPOW_4231.data.u8[3] = (datalayer.battery.status.cell_min_voltage_mV >> 8); //Max temperature per cell SOLXPOW_4240.data.u8[0] = (datalayer.battery.status.temperature_max_dC & 0x00FF); SOLXPOW_4240.data.u8[1] = (datalayer.battery.status.temperature_max_dC >> 8); - SOLXPOW_4241.data.u8[0] = (datalayer.battery.status.temperature_max_dC & 0x00FF); - SOLXPOW_4241.data.u8[1] = (datalayer.battery.status.temperature_max_dC >> 8); //Max/Min temperature per cell SOLXPOW_4240.data.u8[2] = (datalayer.battery.status.temperature_min_dC & 0x00FF); SOLXPOW_4240.data.u8[3] = (datalayer.battery.status.temperature_min_dC >> 8); - SOLXPOW_4241.data.u8[2] = (datalayer.battery.status.temperature_min_dC & 0x00FF); - SOLXPOW_4241.data.u8[3] = (datalayer.battery.status.temperature_min_dC >> 8); //Max temperature per module SOLXPOW_4270.data.u8[0] = (datalayer.battery.status.temperature_max_dC & 0x00FF); SOLXPOW_4270.data.u8[1] = (datalayer.battery.status.temperature_max_dC >> 8); - SOLXPOW_4271.data.u8[0] = (datalayer.battery.status.temperature_max_dC & 0x00FF); - SOLXPOW_4271.data.u8[1] = (datalayer.battery.status.temperature_max_dC >> 8); //Min temperature per module SOLXPOW_4270.data.u8[2] = (datalayer.battery.status.temperature_min_dC & 0x00FF); SOLXPOW_4270.data.u8[3] = (datalayer.battery.status.temperature_min_dC >> 8); - SOLXPOW_4271.data.u8[2] = (datalayer.battery.status.temperature_min_dC & 0x00FF); - SOLXPOW_4271.data.u8[3] = (datalayer.battery.status.temperature_min_dC >> 8); #else // Not INVERT_LOW_HIGH_BYTES //Voltage (370.0) SOLXPOW_4210.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8); SOLXPOW_4210.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF); - SOLXPOW_4211.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8); - SOLXPOW_4211.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF); #ifdef SET_30K_OFFSET //Current (15.0) SOLXPOW_4210.data.u8[2] = ((datalayer.battery.status.current_dA + 30000) >> 8); SOLXPOW_4210.data.u8[3] = ((datalayer.battery.status.current_dA + 30000) & 0x00FF); - SOLXPOW_4211.data.u8[2] = ((datalayer.battery.status.current_dA + 30000) >> 8); - SOLXPOW_4211.data.u8[3] = ((datalayer.battery.status.current_dA + 30000) & 0x00FF); #else // Not SET_30K_OFFSET SOLXPOW_4210.data.u8[2] = (datalayer.battery.status.current_dA >> 8); SOLXPOW_4210.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF); - SOLXPOW_4211.data.u8[2] = (datalayer.battery.status.current_dA >> 8); - SOLXPOW_4211.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF); #endif //SET_30K_OFFSET // BMS Temperature (We dont have BMS temp, send max cell voltage instead) SOLXPOW_4210.data.u8[4] = ((datalayer.battery.status.temperature_max_dC + 1000) >> 8); SOLXPOW_4210.data.u8[5] = ((datalayer.battery.status.temperature_max_dC + 1000) & 0x00FF); - SOLXPOW_4211.data.u8[4] = ((datalayer.battery.status.temperature_max_dC + 1000) >> 8); - SOLXPOW_4211.data.u8[5] = ((datalayer.battery.status.temperature_max_dC + 1000) & 0x00FF); //Maxvoltage (eg 400.0V = 4000 , 16bits long) Charge Cutoff Voltage SOLXPOW_4220.data.u8[0] = (charge_cutoff_voltage_dV >> 8); SOLXPOW_4220.data.u8[1] = (charge_cutoff_voltage_dV & 0x00FF); - SOLXPOW_4221.data.u8[0] = (charge_cutoff_voltage_dV >> 8); - SOLXPOW_4221.data.u8[1] = (charge_cutoff_voltage_dV & 0x00FF); //Minvoltage (eg 300.0V = 3000 , 16bits long) Discharge Cutoff Voltage SOLXPOW_4220.data.u8[2] = (discharge_cutoff_voltage_dV >> 8); SOLXPOW_4220.data.u8[3] = (discharge_cutoff_voltage_dV & 0x00FF); - SOLXPOW_4221.data.u8[2] = (discharge_cutoff_voltage_dV >> 8); - SOLXPOW_4221.data.u8[3] = (discharge_cutoff_voltage_dV & 0x00FF); #ifdef SET_30K_OFFSET //Max ChargeCurrent SOLXPOW_4220.data.u8[4] = ((datalayer.battery.status.max_charge_current_dA + 30000) >> 8); SOLXPOW_4220.data.u8[5] = ((datalayer.battery.status.max_charge_current_dA + 30000) & 0x00FF); - SOLXPOW_4221.data.u8[4] = ((datalayer.battery.status.max_charge_current_dA + 30000) >> 8); - SOLXPOW_4221.data.u8[5] = ((datalayer.battery.status.max_charge_current_dA + 30000) & 0x00FF); //Max DischargeCurrent SOLXPOW_4220.data.u8[6] = ((30000 - datalayer.battery.status.max_discharge_current_dA) >> 8); SOLXPOW_4220.data.u8[7] = ((30000 - datalayer.battery.status.max_discharge_current_dA) & 0x00FF); - SOLXPOW_4221.data.u8[6] = ((30000 - datalayer.battery.status.max_discharge_current_dA) >> 8); - SOLXPOW_4221.data.u8[7] = ((30000 - datalayer.battery.status.max_discharge_current_dA) & 0x00FF); #else // Not SET_30K_OFFSET //Max ChargeCurrent SOLXPOW_4220.data.u8[4] = (datalayer.battery.status.max_charge_current_dA >> 8); SOLXPOW_4220.data.u8[5] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); - SOLXPOW_4221.data.u8[4] = (datalayer.battery.status.max_charge_current_dA >> 8); - SOLXPOW_4221.data.u8[5] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); //Max DishargeCurrent SOLXPOW_4220.data.u8[6] = (datalayer.battery.status.max_discharge_current_dA >> 8); SOLXPOW_4220.data.u8[7] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); - SOLXPOW_4221.data.u8[6] = (datalayer.battery.status.max_discharge_current >> 8); - SOLXPOW_4221.data.u8[7] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); #endif //SET_30K_OFFSET //Max cell voltage SOLXPOW_4230.data.u8[0] = (datalayer.battery.status.cell_max_voltage_mV >> 8); SOLXPOW_4230.data.u8[1] = (datalayer.battery.status.cell_max_voltage_mV & 0x00FF); - SOLXPOW_4231.data.u8[0] = (datalayer.battery.status.cell_max_voltage_mV >> 8); - SOLXPOW_4231.data.u8[1] = (datalayer.battery.status.cell_max_voltage_mV & 0x00FF); //Min cell voltage SOLXPOW_4230.data.u8[2] = (datalayer.battery.status.cell_min_voltage_mV >> 8); SOLXPOW_4230.data.u8[3] = (datalayer.battery.status.cell_min_voltage_mV & 0x00FF); - SOLXPOW_4231.data.u8[2] = (datalayer.battery.status.cell_min_voltage_mV >> 8); - SOLXPOW_4231.data.u8[3] = (datalayer.battery.status.cell_min_voltage_mV & 0x00FF); //Max temperature per cell SOLXPOW_4240.data.u8[0] = (datalayer.battery.status.temperature_max_dC >> 8); SOLXPOW_4240.data.u8[1] = (datalayer.battery.status.temperature_max_dC & 0x00FF); - SOLXPOW_4241.data.u8[0] = (datalayer.battery.status.temperature_max_dC >> 8); - SOLXPOW_4241.data.u8[1] = (datalayer.battery.status.temperature_max_dC & 0x00FF); //Max/Min temperature per cell SOLXPOW_4240.data.u8[2] = (datalayer.battery.status.temperature_min_dC >> 8); SOLXPOW_4240.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF); - SOLXPOW_4241.data.u8[2] = (datalayer.battery.status.temperature_min_dC >> 8); - SOLXPOW_4241.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF); //Max temperature per module SOLXPOW_4270.data.u8[0] = (datalayer.battery.status.temperature_max_dC >> 8); SOLXPOW_4270.data.u8[1] = (datalayer.battery.status.temperature_max_dC & 0x00FF); - SOLXPOW_4271.data.u8[0] = (datalayer.battery.status.temperature_max_dC >> 8); - SOLXPOW_4271.data.u8[1] = (datalayer.battery.status.temperature_max_dC & 0x00FF); //Min temperature per module SOLXPOW_4270.data.u8[2] = (datalayer.battery.status.temperature_min_dC >> 8); SOLXPOW_4270.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF); - SOLXPOW_4271.data.u8[2] = (datalayer.battery.status.temperature_min_dC >> 8); - SOLXPOW_4271.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF); #endif // Not INVERT_LOW_HIGH_BYTES } @@ -318,20 +230,13 @@ void SolxpowInverter::transmit_can(unsigned long currentMillis) { } void SolxpowInverter::send_setup_info() { //Ensemble information -#ifdef SEND_0 transmit_can_frame(&SOLXPOW_7310); transmit_can_frame(&SOLXPOW_7320); transmit_can_frame(&SOLXPOW_7330); transmit_can_frame(&SOLXPOW_7340); -#endif -#ifdef SEND_1 - transmit_can_frame(&SOLXPOW_7311); - transmit_can_frame(&SOLXPOW_7321); -#endif } void SolxpowInverter::send_system_data() { //System equipment information -#ifdef SEND_0 transmit_can_frame(&SOLXPOW_4210); transmit_can_frame(&SOLXPOW_4220); transmit_can_frame(&SOLXPOW_4230); @@ -341,50 +246,30 @@ void SolxpowInverter::send_system_data() { //System equipment information transmit_can_frame(&SOLXPOW_4270); transmit_can_frame(&SOLXPOW_4280); transmit_can_frame(&SOLXPOW_4290); -#endif -#ifdef SEND_1 - transmit_can_frame(&SOLXPOW_4211); - transmit_can_frame(&SOLXPOW_4221); - transmit_can_frame(&SOLXPOW_4231); - transmit_can_frame(&SOLXPOW_4241); - transmit_can_frame(&SOLXPOW_4251); - transmit_can_frame(&SOLXPOW_4261); - transmit_can_frame(&SOLXPOW_4271); - transmit_can_frame(&SOLXPOW_4281); - 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 8010c944f..63d954d7e 100644 --- a/Software/src/inverter/SOLXPOW-CAN.h +++ b/Software/src/inverter/SOLXPOW-CAN.h @@ -41,11 +41,6 @@ class SolxpowInverter : public CanInverterProtocol { .DLC = 8, .ID = 0x7310, .data = {0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00, 0x00}}; - CAN_frame SOLXPOW_7311 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x7311, - .data = {0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00, 0x00}}; CAN_frame SOLXPOW_7320 = {.FD = false, .ext_ID = true, .DLC = 8, @@ -53,13 +48,6 @@ class SolxpowInverter : public CanInverterProtocol { .data = {TOTAL_CELL_AMOUNT, (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES, CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8), (uint8_t)(AH_CAPACITY & 0x00FF), (uint8_t)(AH_CAPACITY >> 8)}}; - CAN_frame SOLXPOW_7321 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x7321, - .data = {TOTAL_CELL_AMOUNT, (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES, - CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8), - (uint8_t)(AH_CAPACITY & 0x00FF), (uint8_t)(AH_CAPACITY >> 8)}}; CAN_frame SOLXPOW_4210 = {.FD = false, .ext_ID = true, .DLC = 8, @@ -105,51 +93,6 @@ class SolxpowInverter : public CanInverterProtocol { .DLC = 8, .ID = 0x4290, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - CAN_frame SOLXPOW_4211 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x4211, - .data = {0xA5, 0x09, 0x30, 0x75, 0x9D, 0x04, 0x2E, 0x64}}; - CAN_frame SOLXPOW_4221 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x4221, - .data = {0x8C, 0x0A, 0xE9, 0x07, 0x4A, 0x79, 0x4A, 0x79}}; - CAN_frame SOLXPOW_4231 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x4231, - .data = {0xDF, 0x0C, 0xDA, 0x0C, 0x03, 0x00, 0x06, 0x00}}; - CAN_frame SOLXPOW_4241 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x4241, - .data = {0x7E, 0x04, 0x62, 0x04, 0x11, 0x00, 0x03, 0x00}}; - CAN_frame SOLXPOW_4251 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x4251, - .data = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - CAN_frame SOLXPOW_4261 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x4261, - .data = {0xAC, 0xC7, 0x74, 0x27, 0x03, 0x00, 0x02, 0x00}}; - CAN_frame SOLXPOW_4271 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x4271, - .data = {0x7E, 0x04, 0x62, 0x04, 0x05, 0x00, 0x01, 0x00}}; - CAN_frame SOLXPOW_4281 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x4281, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - CAN_frame SOLXPOW_4291 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x4291, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; uint16_t discharge_cutoff_voltage_dV = 0; uint16_t charge_cutoff_voltage_dV = 0; diff --git a/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c b/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c index d056fda69..97d31216a 100644 --- a/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c +++ b/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c @@ -23,118 +23,6 @@ typedef struct { rmt_symbol_word_t reset_code; } rmt_led_strip_encoder_t; -static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, - const void *primary_data, size_t data_size, - rmt_encode_state_t *ret_state); -static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder); -static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder); - -static size_t IRAM_ATTR rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, - const void *primary_data, size_t data_size, - rmt_encode_state_t *ret_state) { - rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); - rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder; - rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder; - rmt_encode_state_t session_state = 0; - rmt_encode_state_t state = 0; - size_t encoded_symbols = 0; - - switch (led_encoder->state) { - case 0: // send RGB data - encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state); - if (session_state & RMT_ENCODING_COMPLETE) { - led_encoder->state = 1; - } - if (session_state & RMT_ENCODING_MEM_FULL) { - state |= RMT_ENCODING_MEM_FULL; - goto out; - } - // fall-through - case 1: // send reset code - encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code, - sizeof(led_encoder->reset_code), &session_state); - if (session_state & RMT_ENCODING_COMPLETE) { - led_encoder->state = 0; - state |= RMT_ENCODING_COMPLETE; - } - if (session_state & RMT_ENCODING_MEM_FULL) { - state |= RMT_ENCODING_MEM_FULL; - goto out; - } - } -out: - *ret_state = state; - return encoded_symbols; -} - -static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder) { - rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); - rmt_del_encoder(led_encoder->bytes_encoder); - rmt_del_encoder(led_encoder->copy_encoder); - free(led_encoder); - return ESP_OK; -} - -static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder) { - rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); - rmt_encoder_reset(led_encoder->bytes_encoder); - rmt_encoder_reset(led_encoder->copy_encoder); - led_encoder->state = 0; - return ESP_OK; -} - -static esp_err_t rmt_new_led_strip_encoder(rmt_encoder_handle_t *ret_encoder) { - esp_err_t ret = ESP_OK; - rmt_led_strip_encoder_t *led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t)); - if (!led_encoder) return ESP_ERR_NO_MEM; - - led_encoder->base.encode = rmt_encode_led_strip; - led_encoder->base.del = rmt_del_led_strip_encoder; - led_encoder->base.reset = rmt_led_strip_encoder_reset; - - rmt_bytes_encoder_config_t bytes_encoder_config = { - .bit0 = { - .level0 = 1, - .duration0 = (WS2812_T0H_NS + 50) / 100, - .level1 = 0, - .duration1 = (WS2812_T0L_NS + 50) / 100, - }, - .bit1 = { - .level0 = 1, - .duration0 = (WS2812_T1H_NS + 50) / 100, - .level1 = 0, - .duration1 = (WS2812_T1L_NS + 50) / 100, - }, - .flags.msb_first = 1 - }; - if ((ret = rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder)) != ESP_OK) { - goto err; - } - - rmt_copy_encoder_config_t copy_encoder_config = {}; - if ((ret = rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder)) != ESP_OK) { - goto err; - } - - led_encoder->reset_code = (rmt_symbol_word_t) { - .level0 = 0, - .duration0 = (WS2812_RESET_US * 1000) / 100, - .level1 = 0, - .duration1 = 0, - }; - - *ret_encoder = &led_encoder->base; - return ESP_OK; - -err: - if (led_encoder) { - if (led_encoder->bytes_encoder) rmt_del_encoder(led_encoder->bytes_encoder); - if (led_encoder->copy_encoder) rmt_del_encoder(led_encoder->copy_encoder); - free(led_encoder); - } - return ret; -} - void espShow(uint8_t pin, uint8_t* pixels, uint32_t numBytes) { static bool initialized = false; @@ -163,7 +51,4 @@ void espShow(uint8_t pin, uint8_t* pixels, uint32_t numBytes) { rmt_transmit_config_t tx_config = { .loop_count = 0 }; ESP_ERROR_CHECK(rmt_transmit(led_chan, led_encoder, pixels, numBytes, &tx_config)); - - // optional: only wait if strict timing required - // ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY)); } From eff5064862d87cd5e7b395e94fe323899eefb80c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 10:32:42 +0300 Subject: [PATCH 033/536] Make time macro unused to remove compiler warning --- Software/src/devboard/utils/time_meas.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/devboard/utils/time_meas.h b/Software/src/devboard/utils/time_meas.h index aeac69dad..58b7ed3da 100644 --- a/Software/src/devboard/utils/time_meas.h +++ b/Software/src/devboard/utils/time_meas.h @@ -6,7 +6,7 @@ /** Start time measurement in microseconds * Input parameter must be a unique "tag", e.g: START_TIME_MEASUREMENT(wifi); */ -#define START_TIME_MEASUREMENT(x) int64_t start_time_##x = esp_timer_get_time() +#define START_TIME_MEASUREMENT(x) int64_t start_time_##x __attribute__((unused)) = esp_timer_get_time() /** End time measurement in microseconds * Input parameters are the unique tag and the name of the ALREADY EXISTING * destination variable (int64_t), e.g: END_TIME_MEASUREMENT(wifi, my_wifi_time_int64_t); From 6ad8e2a549c77a90308587a36ee45cd5fc5106c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 17:35:12 +0300 Subject: [PATCH 034/536] BMW PHEV: Change sprintf to avoid compiler warning --- Software/src/battery/BMW-PHEV-HTML.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/BMW-PHEV-HTML.h b/Software/src/battery/BMW-PHEV-HTML.h index 4c07ce926..a8f752d54 100644 --- a/Software/src/battery/BMW-PHEV-HTML.h +++ b/Software/src/battery/BMW-PHEV-HTML.h @@ -506,7 +506,7 @@ class BmwPhevHtmlRenderer : public BatteryHtmlRenderer { uint8_t status = datalayer_extended.bmwphev.dtc_status[i]; char dtcStr[12]; - sprintf(dtcStr, "%06X", code); + sprintf(dtcStr, "%06lX", code); String statusStr = "Stored"; String statusColor = "#757575"; From 42285a6a5b1890b19aaca80503882e6275262258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 17:35:42 +0300 Subject: [PATCH 035/536] Tesla: Fix timeformatting of year to avoid compiler warning --- Software/src/battery/TESLA-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index b83e18616..e02ffb51d 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -413,7 +413,7 @@ char* dayOfYearToDate(int year, int dayOfYear) { static char dateString[11]; // For "YYYY-MM-DD\0" // Format the date string in "YYYY-MM-DD" format - snprintf(dateString, sizeof(dateString), "%d-%02d-%02d", year, month + 1, dayOfYear); + snprintf(dateString, sizeof(dateString), "%04d-%02d-%02d", year % 10000, month + 1, dayOfYear); return dateString; } From 8060418fb3beb96cd8d53e40fb8ec8eca39102f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 17:45:51 +0300 Subject: [PATCH 036/536] Add back Modbus/RS485 for each hardware --- Software/src/devboard/hal/hw_lilygo.h | 6 ++++++ Software/src/devboard/hal/hw_lilygo2can.h | 6 ++++++ Software/src/devboard/hal/hw_stark.h | 7 +++++++ 3 files changed, 19 insertions(+) diff --git a/Software/src/devboard/hal/hw_lilygo.h b/Software/src/devboard/hal/hw_lilygo.h index 06d53a742..31a47c770 100644 --- a/Software/src/devboard/hal/hw_lilygo.h +++ b/Software/src/devboard/hal/hw_lilygo.h @@ -90,6 +90,12 @@ class LilyGoHal : public Esp32Hal { return "CAN (MCP2515 add-on)"; case comm_interface::CanFdAddonMcp2518: return "CAN FD (MCP2518 add-on)"; + case comm_interface::Modbus: + return "Modbus"; + case comm_interface::RS485: + return "RS485"; + case comm_interface::Highest: + return ""; } return Esp32Hal::name_for_comm_interface(comm); } diff --git a/Software/src/devboard/hal/hw_lilygo2can.h b/Software/src/devboard/hal/hw_lilygo2can.h index 84e3f898e..2f72797e5 100644 --- a/Software/src/devboard/hal/hw_lilygo2can.h +++ b/Software/src/devboard/hal/hw_lilygo2can.h @@ -104,6 +104,12 @@ class LilyGo2CANHal : public Esp32Hal { return ""; case comm_interface::CanAddonMcp2515: return "CAN A (MCP2515)"; + case comm_interface::Modbus: + return "Modbus"; + case comm_interface::RS485: + return "RS485"; + case comm_interface::Highest: + return ""; } return Esp32Hal::name_for_comm_interface(comm); } diff --git a/Software/src/devboard/hal/hw_stark.h b/Software/src/devboard/hal/hw_stark.h index ab86707b8..91aa0f4a5 100644 --- a/Software/src/devboard/hal/hw_stark.h +++ b/Software/src/devboard/hal/hw_stark.h @@ -95,6 +95,13 @@ class StarkHal : public Esp32Hal { return ""; case comm_interface::CanFdAddonMcp2518: return ""; + case comm_interface::Modbus: + return "Modbus"; + case comm_interface::RS485: + return "RS485"; + case comm_interface::Highest: + return ""; + break; } return Esp32Hal::name_for_comm_interface(comm); } From 1fd2920569fb5e7aa668a57bfd0428b1b0623e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 17:49:08 +0300 Subject: [PATCH 037/536] Chademo: Comment out unused variable --- Software/src/battery/CHADEMO-BATTERY.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index 40205fbe6..6d622bda4 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -47,12 +47,15 @@ void ChademoBattery::update_values() { * CAN frames. */ + /* + if (vehicle_can_received) { uint8_t chargingrate = 0; if (x100_chg_lim.ConstantOfChargingRateIndication > 0) { chargingrate = x102_chg_session.StateOfCharge / x100_chg_lim.ConstantOfChargingRateIndication * 100; } } +*/ //Update extended datalayer for easier visualization of what's going on datalayer_extended.chademo.CHADEMO_Status = CHADEMO_Status; @@ -86,7 +89,7 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) { uint16_t newTargetBatteryVoltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[1]); uint16_t priorTargetBatteryVoltage = x102_chg_session.TargetBatteryVoltage; uint8_t newChargingCurrentRequest = rx_frame.data.u8[3]; - uint8_t priorChargingCurrentRequest = x102_chg_session.ChargingCurrentRequest; + //uint8_t priorChargingCurrentRequest = x102_chg_session.ChargingCurrentRequest; vehicle_can_initialized = true; From 458064e6bf91f2479a3a09b3e603191ac2936318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 17:51:33 +0300 Subject: [PATCH 038/536] BMW iX: Comment out unused codesection --- Software/src/battery/BMW-IX-BATTERY.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 2ec5a05f2..d1e30a541 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -36,7 +36,7 @@ uint8_t BmwIXBattery::increment_alive_counter(uint8_t counter) { } return counter; } - +/* static uint8_t increment_C0_counter(uint8_t counter) { counter++; // Reset to 0xF0 if it exceeds 0xFE @@ -45,7 +45,7 @@ static uint8_t increment_C0_counter(uint8_t counter) { } return counter; } - +*/ void BmwIXBattery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer datalayer.battery.status.real_soc = avg_soc_state; From 82b74bc89678dbd6213061c9f129a12623e6e17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 18:21:51 +0300 Subject: [PATCH 039/536] Daly BMS: Fix startup cellvoltage max value --- Software/src/battery/DALY-BMS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/DALY-BMS.cpp b/Software/src/battery/DALY-BMS.cpp index 1f328d45c..4945976c4 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -16,7 +16,7 @@ static uint16_t voltage_dV = 0; static uint32_t remaining_capacity_mAh = 0; static uint16_t cellvoltages_mV[48] = {0}; static uint16_t cellvoltage_min_mV = 3700; -static uint16_t cellvoltage_max_mV = 0; +static uint16_t cellvoltage_max_mV = 3700; static uint16_t cell_count = 0; static uint16_t SOC = 0; static bool has_fault = false; From a393a07163bd48b02c3eb306959a05a2b0daece3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 18:22:17 +0300 Subject: [PATCH 040/536] Fix comments causing compiler warnings --- Software/src/inverter/AFORE-CAN.cpp | 2 +- Software/src/inverter/GROWATT-HV-CAN.cpp | 6 +++--- Software/src/inverter/INVERTERS.cpp | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Software/src/inverter/AFORE-CAN.cpp b/Software/src/inverter/AFORE-CAN.cpp index ade4756ba..f6d7cffa9 100644 --- a/Software/src/inverter/AFORE-CAN.cpp +++ b/Software/src/inverter/AFORE-CAN.cpp @@ -78,7 +78,7 @@ void AforeCanInverter:: 15 InterlockOpen AFORE_353.data.u8[0] = Fault H table & 0x00FF AFORE_353.data.u8[1] = Fault H table >> 8); - /* Fault L, bit, definitions + Fault L, bit, definitions 8 VoltageInterlockShortCircuit 9 SystemFailure 10 ErrorChargeReferenceOvervoltage diff --git a/Software/src/inverter/GROWATT-HV-CAN.cpp b/Software/src/inverter/GROWATT-HV-CAN.cpp index 0a4ab2e58..9e9e70f69 100644 --- a/Software/src/inverter/GROWATT-HV-CAN.cpp +++ b/Software/src/inverter/GROWATT-HV-CAN.cpp @@ -4,9 +4,9 @@ /* TODO: This protocol has not been tested with any inverter. Proceed with extreme caution. -Search the file for "TODO" to see all the places that might require work /* +Search the file for "TODO" to see all the places that might require work -/* Growatt BMS CAN-Bus-protocol High Voltage V1.10 2023-11-06 +Growatt BMS CAN-Bus-protocol High Voltage V1.10 2023-11-06 29-bit identifier 500kBit/sec Big-endian @@ -18,7 +18,7 @@ Module - A battery module composed of 16 strings of cells Pack - A battery pack composed of the BMS and battery modules connected in parallel and series, which can work independently FCC - Full charge capacity RM - Remaining capacity -BMS - Battery Information Collector*/ +BMS - Battery Information Collector */ void GrowattHvInverter:: update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages diff --git a/Software/src/inverter/INVERTERS.cpp b/Software/src/inverter/INVERTERS.cpp index eab145080..9a630ca2a 100644 --- a/Software/src/inverter/INVERTERS.cpp +++ b/Software/src/inverter/INVERTERS.cpp @@ -96,6 +96,9 @@ extern const char* name_for_inverter_type(InverterProtocolType type) { case InverterProtocolType::Sungrow: return SungrowInverter::Name; + + case InverterProtocolType::Highest: + return "None"; } return nullptr; } From 72fccd06561d66c2eaaf677cec39bfe6893629ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 18:22:44 +0300 Subject: [PATCH 041/536] Fix missing switch case options for CAN interfaces --- Software/src/communication/nvm/comm_nvm.cpp | 6 +++++- Software/src/devboard/utils/types.h | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index c42c01ad4..e6f9ee88d 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -117,9 +117,13 @@ void init_stored_settings() { return CAN_Interface::CAN_ADDON_MCP2515; case comm_interface::CanFdAddonMcp2518: return CAN_Interface::CANFD_ADDON_MCP2518; + case comm_interface::RS485: + case comm_interface::Modbus: + case comm_interface::Highest: + return CAN_Interface::NO_CAN_INTERFACE; } - return CAN_Interface::CAN_NATIVE; + return CAN_Interface::CAN_NATIVE; //Failed to determine, return CAN native }; can_config.battery = readIf("BATTCOMM"); diff --git a/Software/src/devboard/utils/types.h b/Software/src/devboard/utils/types.h index fab3d4d97..3e83f2747 100644 --- a/Software/src/devboard/utils/types.h +++ b/Software/src/devboard/utils/types.h @@ -68,7 +68,10 @@ enum CAN_Interface { CAN_ADDON_MCP2515 = 2, // Add-on CAN-FD MCP2518 connected to GPIO pins - CANFD_ADDON_MCP2518 = 3 + CANFD_ADDON_MCP2518 = 3, + + // No CAN interface + NO_CAN_INTERFACE = 4 }; extern const char* getCANInterfaceName(CAN_Interface interface); From a877080e9208790e646af214c3d569acfb6b7541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 18:22:52 +0300 Subject: [PATCH 042/536] Fix missing switch case options for CAN interfaces --- Software/src/communication/nvm/comm_nvm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index e6f9ee88d..6a2f539be 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -123,7 +123,7 @@ void init_stored_settings() { return CAN_Interface::NO_CAN_INTERFACE; } - return CAN_Interface::CAN_NATIVE; //Failed to determine, return CAN native + return CAN_Interface::CAN_NATIVE; //Failed to determine, return CAN native }; can_config.battery = readIf("BATTCOMM"); From 00151ffb4c61817fc010844796618b186e9349ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 18:25:37 +0300 Subject: [PATCH 043/536] Jaguar iPACE: Remove unused codesection --- Software/src/battery/JAGUAR-IPACE-BATTERY.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp index 4bda3f0c5..6735541af 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp @@ -57,12 +57,6 @@ CAN_frame ipace_keep_alive = {.FD = false, .ID = 0x59e, .data = {0x9E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};*/ -static void print_units(const char* header, int value, const char* units) { - logging.printf(header); - logging.printf("%d", value); - logging.printf(units); -} - void JaguarIpaceBattery::update_values() { datalayer.battery.status.real_soc = HVBattAvgSOC * 100; //Add two decimals From 83fe6d6d4ef079053fea259b44b3e80a96609256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 18:31:32 +0300 Subject: [PATCH 044/536] Remove unused code section t o avoid compiler warning --- Software/src/battery/RIVIAN-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/RIVIAN-BATTERY.cpp b/Software/src/battery/RIVIAN-BATTERY.cpp index 8b4c429ba..68fa27eec 100644 --- a/Software/src/battery/RIVIAN-BATTERY.cpp +++ b/Software/src/battery/RIVIAN-BATTERY.cpp @@ -17,7 +17,7 @@ void RivianBattery::update_values() { datalayer.battery.status.real_soc = battery_SOC; - datalayer.battery.status.soh_pptt; + //datalayer.battery.status.soh_pptt; //TODO: Find usable SOH datalayer.battery.status.voltage_dV = battery_voltage; datalayer.battery.status.current_dA = ((int16_t)battery_current / 10.0 - 3200) * 10; From 6bc4ac462afbc6ff3673447f50368356606f325e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 19:07:45 +0300 Subject: [PATCH 045/536] Tesla: Make dayOfYearToDate function safer --- Software/src/battery/TESLA-BATTERY.cpp | 37 ++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index e02ffb51d..8db01e292 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -406,14 +406,47 @@ char* dayOfYearToDate(int year, int dayOfYear) { int month = 0; // Find the month and the day within the month - while (dayOfYear > daysInMonth[month]) { + while (month < 12 && dayOfYear > daysInMonth[month]) { dayOfYear -= daysInMonth[month]; month++; } + // Ensure month is in valid range [0, 11] + if (month >= 12) { + month = 11; + dayOfYear = daysInMonth[11]; // Set to last day of December + } + + // Ensure day is in valid range [1, daysInMonth[month]] + if (dayOfYear < 1) { + dayOfYear = 1; + } else if (dayOfYear > daysInMonth[month]) { + dayOfYear = daysInMonth[month]; + } + static char dateString[11]; // For "YYYY-MM-DD\0" + + // Clamp values to ensure they fit in the expected number of digits + int safeYear = year % 10000; + if (safeYear < 0) + safeYear = 0; + if (safeYear > 9999) + safeYear = 9999; + + int safeMonth = month + 1; + if (safeMonth < 1) + safeMonth = 1; + if (safeMonth > 12) + safeMonth = 12; + + int safeDay = dayOfYear; + if (safeDay < 1) + safeDay = 1; + if (safeDay > 31) + safeDay = 31; + // Format the date string in "YYYY-MM-DD" format - snprintf(dateString, sizeof(dateString), "%04d-%02d-%02d", year % 10000, month + 1, dayOfYear); + snprintf(dateString, sizeof(dateString), "%04d-%02d-%02d", safeYear, safeMonth, safeDay); return dateString; } From 05eba252beacc498610673c57689c6f7a0eec6ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 19:11:06 +0300 Subject: [PATCH 046/536] Remove unused variable from logging --- Software/src/devboard/utils/logging.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Software/src/devboard/utils/logging.cpp b/Software/src/devboard/utils/logging.cpp index 2b4b1d30a..316c2ebc6 100644 --- a/Software/src/devboard/utils/logging.cpp +++ b/Software/src/devboard/utils/logging.cpp @@ -13,7 +13,6 @@ void Logging::add_timestamp(size_t size) { return; } - char* message_string = datalayer.system.info.logged_can_messages; int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer size_t message_string_size = sizeof(datalayer.system.info.logged_can_messages); unsigned long currentTime = millis(); From b58097315ca0b55d7dd1558427fae302c62d4714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 19:26:43 +0300 Subject: [PATCH 047/536] Fix more logging compiler warnings --- Software/src/communication/can/comm_can.cpp | 2 +- Software/src/devboard/sdcard/sdcard.cpp | 6 +++--- Software/src/devboard/utils/logging.cpp | 2 +- Software/src/inverter/GROWATT-WIT-CAN.cpp | 16 ++++------------ 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index a9e3e6158..4fe4f8792 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -442,7 +442,7 @@ void dump_can_frame(CAN_frame& frame, CAN_Interface interface, frameDirection ms (int)(interface * 2) + (msgDir == MSG_RX ? 0 : 1)); // Add ID and DLC - offset += snprintf(message_string + offset, message_string_size - offset, "%X [%u] ", frame.ID, frame.DLC); + offset += snprintf(message_string + offset, message_string_size - offset, "%lX [%u] ", frame.ID, frame.DLC); // Add data bytes for (uint8_t i = 0; i < frame.DLC; i++) { diff --git a/Software/src/devboard/sdcard/sdcard.cpp b/Software/src/devboard/sdcard/sdcard.cpp index 5dfddebd3..72f1ef22e 100644 --- a/Software/src/devboard/sdcard/sdcard.cpp +++ b/Software/src/devboard/sdcard/sdcard.cpp @@ -58,9 +58,9 @@ void add_can_frame_to_buffer(CAN_frame frame, frameDirection msgDir) { unsigned long currentTime = millis(); static char messagestr_buffer[32]; - size_t size = - snprintf(messagestr_buffer + size, sizeof(messagestr_buffer) - size, "(%lu.%03lu) %s %X [%u] ", - currentTime / 1000, currentTime % 1000, (msgDir == MSG_RX ? "RX0" : "TX1"), frame.ID, frame.DLC); + size_t size = 0; + size = snprintf(messagestr_buffer + size, sizeof(messagestr_buffer) - size, "(%lu.%03lu) %s %lX [%u] ", + currentTime / 1000, currentTime % 1000, (msgDir == MSG_RX ? "RX0" : "TX1"), frame.ID, frame.DLC); if (xRingbufferSend(can_bufferHandle, &messagestr_buffer, size, pdMS_TO_TICKS(2)) != pdTRUE) { logging.println("Failed to send message to can ring buffer!"); diff --git a/Software/src/devboard/utils/logging.cpp b/Software/src/devboard/utils/logging.cpp index 316c2ebc6..191c119e8 100644 --- a/Software/src/devboard/utils/logging.cpp +++ b/Software/src/devboard/utils/logging.cpp @@ -117,7 +117,7 @@ void Logging::printf(const char* fmt, ...) { message_buffer = buffer; } - va_list(args); + va_list args; va_start(args, fmt); int size = min(MAX_LINE_LENGTH_PRINTF - 1, vsnprintf(message_buffer, MAX_LINE_LENGTH_PRINTF, fmt, args)); va_end(args); diff --git a/Software/src/inverter/GROWATT-WIT-CAN.cpp b/Software/src/inverter/GROWATT-WIT-CAN.cpp index 4bb4b2dd6..6cf52d1cf 100644 --- a/Software/src/inverter/GROWATT-WIT-CAN.cpp +++ b/Software/src/inverter/GROWATT-WIT-CAN.cpp @@ -84,24 +84,16 @@ void GrowattWitInverter::update_values() { //SOH (%) (Bit 0~ Bit6 SOH Counters) Bit7 low SOH flag (Indicates that battery is in unsafe use) GROWATT_1AC6XXXX.data.u8[1] = (datalayer.battery.status.soh_pptt / 100); //Rated battery capacity when new (0-50000) dAH - GROWATT_1AC6XXXX.data.u8[2]; //TODO - GROWATT_1AC6XXXX.data.u8[3]; //TODO + //GROWATT_1AC6XXXX.data.u8[2]; //TODO + //GROWATT_1AC6XXXX.data.u8[3]; //TODO //Battery voltage, 0-15000 dV GROWATT_1AC7XXXX.data.u8[2] = (datalayer.battery.status.voltage_dV >> 8); GROWATT_1AC7XXXX.data.u8[3] = (datalayer.battery.status.voltage_dV & 0x00FF); //Battery current, 0-20000dA (offset -1000A) // Apply the -1000 offset (add 1000 to the dA value) - uint32_t current_value = (int32_t)datalayer.battery.status.current_dA + 1000; - - // Clamp to uint16_t range (0 to 65535) - if (current_value < 0) { - current_value = 0; - } else if (current_value > 65535) { - current_value = 65535; - } - GROWATT_1AC7XXXX.data.u8[4] = (current_value >> 8); - GROWATT_1AC7XXXX.data.u8[5] = (current_value & 0x00FF); + GROWATT_1AC7XXXX.data.u8[4] = ((datalayer.battery.status.current_dA + 1000) >> 8); + GROWATT_1AC7XXXX.data.u8[5] = ((datalayer.battery.status.current_dA + 1000) & 0x00FF); } void GrowattWitInverter::map_can_frame_to_variable(CAN_frame rx_frame) { From b07c7d1a8b62f2f0824b922a063eda7902f3b9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 21:45:08 +0300 Subject: [PATCH 048/536] Volvo SPA: Rewrite cellvoltage handler to avoid warnings --- Software/src/battery/VOLVO-SPA-BATTERY.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index 0ed7a29c4..202d3f725 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -254,7 +254,8 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { datalayer_extended.VolvoPolestar.DTCcount = 0; } } else if ((rx_frame.data.u8[0] == 0x21) && (rxConsecutiveFrames)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; From 002e1f69cf6916a1a4f72bff2da4b9bcc7cec459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 21:47:10 +0300 Subject: [PATCH 049/536] Volvo Hybrid: Refactor cellvoltage handler to avoid compilation warning --- .../src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp index 89b5bfb7e..2894ee900 100644 --- a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp @@ -238,7 +238,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { { transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x21) && (rxConsecutiveFrames == 1)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -250,7 +251,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x23) && (rxConsecutiveFrames == 1)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -262,7 +264,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x25) && (rxConsecutiveFrames == 1)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -274,7 +277,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x27) && (rxConsecutiveFrames == 1)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -286,7 +290,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x29) && (rxConsecutiveFrames == 1)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -298,7 +303,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x2B) && (rxConsecutiveFrames == 1)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -310,7 +316,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x2D) && (rxConsecutiveFrames == 1)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -322,7 +329,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x2F) && (rxConsecutiveFrames == 1)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -335,7 +343,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x21) && (rxConsecutiveFrames == 2)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -347,7 +356,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x23) && (rxConsecutiveFrames == 2)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -359,7 +369,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x25) && (rxConsecutiveFrames == 2)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -371,7 +382,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x27) && (rxConsecutiveFrames == 2)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -383,7 +395,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x29) && (rxConsecutiveFrames == 2)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -395,7 +408,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x2B) && (rxConsecutiveFrames == 2)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; @@ -407,7 +421,8 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); //transmit_can_frame(&VOLVO_FlowControl); // Send flow control } else if ((rx_frame.data.u8[0] == 0x2D) && (rxConsecutiveFrames == 2)) { - cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx] |= rx_frame.data.u8[1]; + battery_request_idx++; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; //cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; From 0619571e872b39a0d16b842393d219cddada3cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 22:10:14 +0300 Subject: [PATCH 050/536] Enable Werror compilation flag --- platformio.ini | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index d6cc57951..9931ef81d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,7 +18,7 @@ monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder board_build.partitions = min_spiffs.csv framework = arduino -build_flags = -I include -DHW_DEVKIT -Wimplicit-fallthrough -Wextra -Wall +build_flags = -I include -DHW_DEVKIT -Wimplicit-fallthrough -Wextra -Wall -Werror lib_deps = [env:lilygo_330] @@ -31,7 +31,7 @@ board_build.f_flash = 80000000 board_build.arduino.memory_type = qio_qspi board_build.partitions = min_spiffs.csv framework = arduino -build_flags = -I include -DHW_LILYGO -Wimplicit-fallthrough -Wextra -Wall +build_flags = -I include -DHW_LILYGO -Wimplicit-fallthrough -Wextra -Wall -Werror lib_deps = [env:stark_330] @@ -44,7 +44,7 @@ board_build.f_flash = 80000000 board_build.arduino.memory_type = qio_qspi board_build.partitions = min_spiffs.csv framework = arduino -build_flags = -I include -DHW_STARK -Wimplicit-fallthrough -Wextra -Wall +build_flags = -I include -DHW_STARK -Wimplicit-fallthrough -Wextra -Wall -Werror lib_deps = [env:lilygo_2CAN_330] @@ -63,6 +63,7 @@ build_flags = -Wimplicit-fallthrough -Wextra -Wall + -Werror -D HW_LILYGO2CAN -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=1 From 91c35e2272034a804bf95dfecc1dcbfca6eb89f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 22:42:35 +0300 Subject: [PATCH 051/536] Update ESP32 from v3.3.0 to v3.3.1 --- platformio.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index 9931ef81d..cf2b14c70 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,7 +12,7 @@ src_dir = ./Software [env:esp32dev] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31/platform-espressif32.zip board = esp32dev monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder @@ -22,7 +22,7 @@ build_flags = -I include -DHW_DEVKIT -Wimplicit-fallthrough -Wextra -Wall -Werro lib_deps = [env:lilygo_330] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31/platform-espressif32.zip board = esp32dev monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder @@ -35,7 +35,7 @@ build_flags = -I include -DHW_LILYGO -Wimplicit-fallthrough -Wextra -Wall -Werro lib_deps = [env:stark_330] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31/platform-espressif32.zip board = esp32dev monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder @@ -48,7 +48,7 @@ build_flags = -I include -DHW_STARK -Wimplicit-fallthrough -Wextra -Wall -Werror lib_deps = [env:lilygo_2CAN_330] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31/platform-espressif32.zip board = esp32s3_flash_16MB monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder From d4d0e846f4ba811781eee1c8dccecb8b120cea4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 17 Oct 2025 22:48:29 +0300 Subject: [PATCH 052/536] Update names for interfaces for 2CAN --- Software/src/devboard/hal/hw_lilygo2can.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Software/src/devboard/hal/hw_lilygo2can.h b/Software/src/devboard/hal/hw_lilygo2can.h index 2f72797e5..9f64e581b 100644 --- a/Software/src/devboard/hal/hw_lilygo2can.h +++ b/Software/src/devboard/hal/hw_lilygo2can.h @@ -104,10 +104,12 @@ class LilyGo2CANHal : public Esp32Hal { return ""; case comm_interface::CanAddonMcp2515: return "CAN A (MCP2515)"; + case comm_interface::CanFdAddonMcp2518: + return "CAN FD (MCP2518 add-on)"; case comm_interface::Modbus: - return "Modbus"; + return "Modbus (Add-on)"; case comm_interface::RS485: - return "RS485"; + return "RS485 (Add-on)"; case comm_interface::Highest: return ""; } From b5815494f3ca46c944f4a09e1c359dc7066a817d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 18 Oct 2025 22:50:34 +0300 Subject: [PATCH 053/536] Add configurable cutoff filter --- Software/src/communication/can/comm_can.cpp | 8 +++--- Software/src/communication/can/comm_can.h | 1 + .../devboard/webserver/can_logging_html.cpp | 26 ++++++++++++++++++- Software/src/devboard/webserver/webserver.cpp | 3 +++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index a9e3e6158..1beb14b94 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -57,9 +57,9 @@ uint8_t user_selected_canfd_addon_crystal_frequency_mhz = 0; ACAN2517FD* canfd; ACAN2517FDSettings* settings2517; bool use_canfd_as_can = false; -// Initialization functions - bool native_can_initialized = false; +//CAN logging filter settings +uint16_t user_selected_CAN_ID_cutoff_filter = 0; //Messages below this ID will not be logged in webserver bool init_CAN() { @@ -395,7 +395,9 @@ void print_can_frame(CAN_frame frame, CAN_Interface interface, frameDirection ms } if (datalayer.system.info.can_logging_active) { // If user clicked on CAN Logging page in webserver, start recording - dump_can_frame(frame, interface, msgDir); + if (frame.ID > user_selected_CAN_ID_cutoff_filter) { //Only log the message if CAN ID is higher than user set value + dump_can_frame(frame, interface, msgDir); + } } } diff --git a/Software/src/communication/can/comm_can.h b/Software/src/communication/can/comm_can.h index e77e86c25..a098c464c 100644 --- a/Software/src/communication/can/comm_can.h +++ b/Software/src/communication/can/comm_can.h @@ -6,6 +6,7 @@ extern bool use_canfd_as_can; extern uint8_t user_selected_can_addon_crystal_frequency_mhz; extern uint8_t user_selected_canfd_addon_crystal_frequency_mhz; +extern uint16_t user_selected_CAN_ID_cutoff_filter; void dump_can_frame(CAN_frame& frame, CAN_Interface interface, frameDirection msgDir); void transmit_can_frame_to_interface(const CAN_frame* tx_frame, CAN_Interface interface); diff --git a/Software/src/devboard/webserver/can_logging_html.cpp b/Software/src/devboard/webserver/can_logging_html.cpp index 146566c7d..a54888e2d 100644 --- a/Software/src/devboard/webserver/can_logging_html.cpp +++ b/Software/src/devboard/webserver/can_logging_html.cpp @@ -1,5 +1,6 @@ #include "can_logging_html.h" #include +#include "../../communication/can/comm_can.h" #include "../../datalayer/datalayer.h" #include "index_html.h" @@ -21,13 +22,21 @@ String can_logger_processor(void) { content += ".can-message { background-color: #404E57; margin-bottom: 5px; padding: 10px; border-radius: 5px; font-family: " "monospace; }"; + content += ".config-item { margin: 15px 0; }"; content += ""; + + // Configuration section + content += "
"; + content += "

CAN Logger Configuration

"; + content += "
CAN ID Cutoff Filter: " + String(user_selected_CAN_ID_cutoff_filter) + + "
"; content += " "; content += " "; #ifdef LOG_CAN_TO_SD content += " "; #endif content += ""; + content += "
"; // Start a new block for the CAN messages content += "
"; @@ -51,7 +60,7 @@ String can_logger_processor(void) { content += "
"; - // Add JavaScript for navigation + // Add JavaScript for navigation and configuration content += ""; content += index_html_footer; return content; diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index b8547dde9..45af2a004 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -658,6 +658,9 @@ void init_webserver() { datalayer.battery.settings.max_percentage = static_cast(value.toFloat() * 100); }); + // Route for editing CAN ID cutoff filter + update_int_setting("/set_can_id_cutoff", [](int value) { user_selected_CAN_ID_cutoff_filter = value; }); + // Route for pause/resume Battery emulator update_string("/pause", [](String value) { setBatteryPause(value == "true" || value == "1", false); }); From d4a33396913205d6c3a2a4cfad43f57cfa3f23b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 21 Oct 2025 20:57:28 +0300 Subject: [PATCH 054/536] Update ESP32 version to v3.3.2 --- platformio.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index cf2b14c70..61c19947f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,7 +12,7 @@ src_dir = ./Software [env:esp32dev] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.32/platform-espressif32.zip board = esp32dev monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder @@ -22,7 +22,7 @@ build_flags = -I include -DHW_DEVKIT -Wimplicit-fallthrough -Wextra -Wall -Werro lib_deps = [env:lilygo_330] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.32/platform-espressif32.zip board = esp32dev monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder @@ -35,7 +35,7 @@ build_flags = -I include -DHW_LILYGO -Wimplicit-fallthrough -Wextra -Wall -Werro lib_deps = [env:stark_330] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.32/platform-espressif32.zip board = esp32dev monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder @@ -48,7 +48,7 @@ build_flags = -I include -DHW_STARK -Wimplicit-fallthrough -Wextra -Wall -Werror lib_deps = [env:lilygo_2CAN_330] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.32/platform-espressif32.zip board = esp32s3_flash_16MB monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder From 24482fe129c7a97d9dcc4d77c715fc8f00e0af43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 21 Oct 2025 23:11:07 +0300 Subject: [PATCH 055/536] Optimize padding for datalayer structs --- Software/src/battery/KIA-HYUNDAI-64-BATTERY.h | 2 +- Software/src/datalayer/datalayer.h | 185 +++-- Software/src/datalayer/datalayer_extended.h | 719 +++++++++--------- 3 files changed, 438 insertions(+), 468 deletions(-) diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h index 832c0e9a8..86631e00a 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h @@ -70,7 +70,7 @@ class KiaHyundai64Battery : public CanBattery { uint16_t inverterVoltageFrameHigh = 0; uint16_t inverterVoltage = 0; uint16_t cellvoltages_mv[98]; - int16_t leadAcidBatteryVoltage = 120; + uint16_t leadAcidBatteryVoltage = 120; int16_t batteryAmps = 0; int16_t temperatureMax = 0; int16_t temperatureMin = 0; diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 579ce13ab..0dd39dc2d 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -4,6 +4,8 @@ #include "../devboard/utils/types.h" #include "../system_settings.h" +/*Note when editing this file. Order of datatypes matter heavily to keep padding and flash size in check*/ + struct DATALAYER_BATTERY_INFO_TYPE { /** uint32_t */ /** Total energy capacity in Watt-hours @@ -34,41 +36,18 @@ struct DATALAYER_BATTERY_INFO_TYPE { }; struct DATALAYER_BATTERY_STATUS_TYPE { - /** int32_t */ - /** Instantaneous battery power in Watts. Calculated based on voltage_dV and current_dA */ - /* Positive value = Battery Charging */ - /* Negative value = Battery Discharging */ - int32_t active_power_W; - /** uint32_t */ /** Remaining energy capacity in Watt-hours */ - uint32_t remaining_capacity_Wh; + uint32_t remaining_capacity_Wh = 0; /** The remaining capacity reported to the inverter based on min percentage setting, in Watt-hours * This value will either be scaled or not scaled depending on the value of * battery.settings.soc_scaling_active */ uint32_t reported_remaining_capacity_Wh; - - int32_t total_charged_battery_Wh = 0; - int32_t total_discharged_battery_Wh = 0; - /** Maximum allowed battery discharge power in Watts. Set by battery */ uint32_t max_discharge_power_W = 0; /** Maximum allowed battery charge power in Watts. Set by battery */ uint32_t max_charge_power_W = 0; - /** Maximum allowed battery discharge current in dA. Calculated based on allowed W and Voltage */ - uint16_t max_discharge_current_dA = 0; - /** Maximum allowed battery charge current in dA. Calculated based on allowed W and Voltage */ - uint16_t max_charge_current_dA = 0; - - /** int16_t */ - /** Maximum temperature currently measured in the pack, in d°C. 150 = 15.0 °C */ - int16_t temperature_max_dC; - /** Minimum temperature currently measured in the pack, in d°C. 150 = 15.0 °C */ - int16_t temperature_min_dC; - /** Instantaneous battery current in deciAmpere. 95 = 9.5 A */ - int16_t current_dA; - /* Some early integrations do not support reading allowed charge power from battery On these integrations we need to have the user specify what limits the battery can take */ /** Overriden allowed battery discharge power in Watts. Set by user */ @@ -76,7 +55,19 @@ struct DATALAYER_BATTERY_STATUS_TYPE { /** Overriden allowed battery charge power in Watts. Set by user */ uint32_t override_charge_power_W = 0; + /** int32_t */ + /** Instantaneous battery power in Watts. Calculated based on voltage_dV and current_dA */ + /* Positive value = Battery Charging */ + /* Negative value = Battery Discharging */ + int32_t active_power_W = 0; + int32_t total_charged_battery_Wh = 0; + int32_t total_discharged_battery_Wh = 0; + /** uint16_t */ + /** Maximum allowed battery discharge current in dA. Calculated based on allowed W and Voltage */ + uint16_t max_discharge_current_dA = 0; + /** Maximum allowed battery charge current in dA. Calculated based on allowed W and Voltage */ + uint16_t max_charge_current_dA = 0; /** State of health in integer-percent x 100. 9900 = 99.00% */ uint16_t soh_pptt = 9900; /** Instantaneous battery voltage in deciVolts. 3700 = 370.0 V */ @@ -85,15 +76,6 @@ struct DATALAYER_BATTERY_STATUS_TYPE { uint16_t cell_max_voltage_mV = 3700; /** Minimum cell voltage currently measured in the pack, in mV */ uint16_t cell_min_voltage_mV = 3700; - /** All cell voltages currently measured in the pack, in mV. - * Use with battery.info.number_of_cells to get valid data. - */ - uint16_t cell_voltages_mV[MAX_AMOUNT_CELLS]; - /** All balancing resistors status inside the pack, either on(1) or off(0). - * Use with battery.info.number_of_cells to get valid data. - * Not available for all battery manufacturers. - */ - bool cell_balancing_status[MAX_AMOUNT_CELLS]; /** The "real" SOC reported from the battery, in integer-percent x 100. 9550 = 95.50% */ uint16_t real_soc; /** The SOC reported to the inverter, in integer-percent x 100. 9550 = 95.50%. @@ -103,37 +85,53 @@ struct DATALAYER_BATTERY_STATUS_TYPE { uint16_t reported_soc; /** A counter that increases incase a CAN CRC read error occurs */ uint16_t CAN_error_counter; + + /** int16_t */ + /** Maximum temperature currently measured in the pack, in d°C. 150 = 15.0 °C */ + int16_t temperature_max_dC; + /** Minimum temperature currently measured in the pack, in d°C. 150 = 15.0 °C */ + int16_t temperature_min_dC; + /** Instantaneous battery current in deciAmpere. 95 = 9.5 A */ + int16_t current_dA; + /** uint8_t */ /** A counter set each time a new message comes from battery. * This value then gets decremented every second. Incase we reach 0 * we report the battery as missing entirely on the CAN bus. */ uint8_t CAN_battery_still_alive = CAN_STILL_ALIVE; - - /** Other */ /** The current system status, which for now still has the name bms_status */ bms_status_enum bms_status = ACTIVE; - /** The current battery status, which for now has the name real_bms_status */ real_bms_status_enum real_bms_status = BMS_DISCONNECTED; - /** LED mode, customizable by user */ led_mode_enum led_mode = CLASSIC; + + /** All cell voltages currently measured in the pack, in mV. + * Use with battery.info.number_of_cells to get valid data. + */ + uint16_t cell_voltages_mV[MAX_AMOUNT_CELLS]; + /** All balancing resistors status inside the pack, either on(1) or off(0). + * Use with battery.info.number_of_cells to get valid data. + * Not available for all battery manufacturers. + */ + bool cell_balancing_status[MAX_AMOUNT_CELLS]; }; struct DATALAYER_BATTERY_SETTINGS_TYPE { - /** SOC scaling setting. Increases battery life. - * If true will rescale SOC between the configured min/max-percentage */ - bool soc_scaling_active = true; - /** Minimum percentage setting. Set this value to the lowest real SOC - * you want the inverter to be able to use. At this real SOC, the inverter - * will "see" 0% , Example 2000 = 20.0%*/ - int16_t min_percentage = 2000; + + /** Last time a remote set command was received to enable timeout of settings */ + unsigned long remote_set_timestamp = 0; + /** Timeout time for remote limits */ + unsigned long remote_set_timeout = 0; + /* Forced balancing max time & start timestamp */ + uint32_t balancing_time_ms = 3600000; //1h default, (60min*60sec*1000ms) + uint32_t balancing_start_time_ms = 0; //For keeping track when balancing started + /** Maximum percentage setting. Set this value to the highest real SOC * you want the inverter to be able to use. At this real SOC, the inverter * will "see" 100% Example 8000 = 80.0%*/ uint16_t max_percentage = 8000; - /** The user specified maximum allowed charge rate, in deciAmpere. 300 = 30.0 A, persisted to memory * Updates later on via Settings */ @@ -142,27 +140,39 @@ struct DATALAYER_BATTERY_SETTINGS_TYPE { * Updates later on via Settings */ uint16_t max_user_set_discharge_dA = 300; - - /** Last time a remote set command was received to enable timeout of settings */ - unsigned long remote_set_timestamp = 0; - /** Timeout time for remote limits */ - unsigned long remote_set_timeout = 0; /** The remote specified maximum allowed charge rate, in deciAmpere. 300 = 30.0 A, NOT persisted to memory */ uint16_t max_remote_set_charge_dA = max_user_set_charge_dA; /** The remote specified maximum allowed discharge rate, in deciAmpere. 300 = 30.0 A, NOT persisted to memory */ uint16_t max_remote_set_discharge_dA = max_user_set_discharge_dA; - - /** User specified discharge/charge voltages in use. Set to true to use user specified values */ - /** Some inverters like to see a specific target voltage for charge/discharge. Use these values to override automatic voltage limits*/ - bool user_set_voltage_limits_active = false; /** The user specified maximum allowed charge voltage, in deciVolt. 4000 = 400.0 V */ uint16_t max_user_set_charge_voltage_dV = 4500; /** The user specified maximum allowed discharge voltage, in deciVolt. 3000 = 300.0 V */ uint16_t max_user_set_discharge_voltage_dV = 3000; - /** The user specified BMS reset period. Keeps track on how many milliseconds should we keep power off during daily BMS reset */ uint16_t user_set_bms_reset_duration_ms = 30000; + /* Max cell voltage during forced balancing */ + uint16_t balancing_max_cell_voltage_mV = 3650; + /* Max cell deviation allowed during forced balancing */ + uint16_t balancing_max_deviation_cell_voltage_mV = 400; + /* Float max power during forced balancing */ + uint16_t balancing_float_power_W = 1000; + /* Maximum voltage for entire battery pack during forced balancing */ + uint16_t balancing_max_pack_voltage_dV = 3940; + /** Minimum percentage setting. Set this value to the lowest real SOC + * you want the inverter to be able to use. At this real SOC, the inverter + * will "see" 0% , Example 2000 = 20.0%*/ + int16_t min_percentage = 2000; + + /** Sofar CAN Battery ID (0-15) used to parallel multiple packs */ + uint8_t sofar_user_specified_battery_id = 0; + + /** User specified discharge/charge voltages in use. Set to true to use user specified values */ + /** Some inverters like to see a specific target voltage for charge/discharge. Use these values to override automatic voltage limits*/ + bool user_set_voltage_limits_active = false; + /** SOC scaling setting. Increases battery life. + * If true will rescale SOC between the configured min/max-percentage */ + bool soc_scaling_active = true; /** Parameters for keeping track of the limiting factor in the system */ bool user_settings_limit_discharge = false; bool user_settings_limit_charge = false; @@ -170,27 +180,12 @@ struct DATALAYER_BATTERY_SETTINGS_TYPE { bool remote_settings_limit_charge = false; bool inverter_limits_discharge = false; bool inverter_limits_charge = false; - /** Tesla specific settings that are edited on the fly when manually forcing a balance charge for LFP chemistry */ /* Bool for specifying if user has requested manual function */ bool user_requests_balancing = false; bool user_requests_tesla_isolation_clear = false; bool user_requests_tesla_bms_reset = false; bool user_requests_tesla_soc_reset = false; - /* Forced balancing max time & start timestamp */ - uint32_t balancing_time_ms = 3600000; //1h default, (60min*60sec*1000ms) - uint32_t balancing_start_time_ms = 0; //For keeping track when balancing started - /* Max cell voltage during forced balancing */ - uint16_t balancing_max_cell_voltage_mV = 3650; - /* Max cell deviation allowed during forced balancing */ - uint16_t balancing_max_deviation_cell_voltage_mV = 400; - /* Float max power during forced balancing */ - uint16_t balancing_float_power_W = 1000; - /* Maximum voltage for entire battery pack during forced balancing */ - uint16_t balancing_max_pack_voltage_dV = 3940; - - /** Sofar CAN Battery ID (0-15) used to parallel multiple packs */ - uint8_t sofar_user_specified_battery_id = 0; }; typedef struct { @@ -218,31 +213,34 @@ struct DATALAYER_CHARGER_TYPE { float charger_stat_LVcur = 0; /** Measured LV voltage from charger **/ float charger_stat_LVvol = 0; - /** True if charger is enabled */ - bool charger_HV_enabled = false; - /** True if the 12V DC/DC output is enabled */ - bool charger_aux12V_enabled = false; /** uint8_t */ /** A counter set each time a new message comes from charger. * This value then gets decremented every second. Incase we reach 0 * we report the battery as missing entirely on the CAN bus. */ uint8_t CAN_charger_still_alive = CAN_STILL_ALIVE; + /** True if charger is enabled */ + bool charger_HV_enabled = false; + /** True if the 12V DC/DC output is enabled */ + bool charger_aux12V_enabled = false; }; struct DATALAYER_SHUNT_TYPE { - /** measured voltage in deciVolts. 4200 = 420.0 V */ - uint16_t measured_voltage_dV = 0; - /** measured amperage in deciAmperes. 300 = 30.0 A */ - uint16_t measured_amperage_dA = 0; /** measured battery voltage in mV (S-BOX) **/ uint32_t measured_voltage_mV = 0; /** measured output voltage in mV (eg. S-BOX) **/ uint32_t measured_outvoltage_mV = 0; + /** measured amperage in mA (eg. S-BOX) **/ int32_t measured_amperage_mA = 0; /** Average current from last 1s **/ int32_t measured_avg1S_amperage_mA = 0; + + /** measured voltage in deciVolts. 4200 = 420.0 V */ + uint16_t measured_voltage_dV = 0; + /** measured amperage in deciAmperes. 300 = 30.0 A */ + uint16_t measured_amperage_dA = 0; + /** True if contactors are precharging state */ bool precharging = false; /** True if the contactor controlled by battery-emulator is closed */ @@ -252,17 +250,22 @@ struct DATALAYER_SHUNT_TYPE { }; struct DATALAYER_SYSTEM_INFO_TYPE { - /** ESP32 main CPU temperature, for displaying on webserver and for safeties */ - float CPU_temperature = 0; + /** array with incoming CAN messages, for displaying on webserver */ + char logged_can_messages[15000] = {0}; /** array with type of battery used, for displaying on webserver */ char battery_protocol[64] = {0}; /** array with type of battery used, for displaying on webserver */ char shunt_protocol[64] = {0}; /** array with type of inverter brand used, for displaying on webserver */ char inverter_brand[8] = {0}; - /** array with incoming CAN messages, for displaying on webserver */ - char logged_can_messages[15000] = {0}; + size_t logged_can_messages_offset = 0; + /** ESP32 main CPU temperature, for displaying on webserver and for safeties */ + float CPU_temperature = 0; + + /** uint8_t, enumeration which CAN interface should be used for log playback */ + uint8_t can_replay_interface = CAN_NATIVE; + /** bool, determines if CAN messages should be logged for webserver */ bool can_logging_active = false; /** bool, determines if USB serial logging should occur */ @@ -275,8 +278,6 @@ struct DATALAYER_SYSTEM_INFO_TYPE { bool web_logging_active = false; /** bool, determines if general logging to SD card should be active */ bool SD_logging_active = false; - /** uint8_t, enumeration which CAN interface should be used for log playback */ - uint8_t can_replay_interface = CAN_NATIVE; /** bool, determines if CAN replay should loop or not */ bool loop_playback = false; /** bool, Native CAN failed to send flag */ @@ -298,7 +299,6 @@ struct DATALAYER_SYSTEM_STATUS_TYPE { int64_t mqtt_task_10s_max_us = 0; /** Wifi sub-task measurement variable, reset each 10 seconds */ int64_t wifi_task_10s_max_us = 0; - /** OTA handling function measurement variable */ int64_t time_ota_us = 0; /** CAN RX or serial link function measurement variable */ @@ -309,7 +309,6 @@ struct DATALAYER_SYSTEM_STATUS_TYPE { int64_t time_values_us = 0; /** CAN TX function measurement variable */ int64_t time_cantx_us = 0; - /** Function measurement snapshot variable. * This will show the performance of OTA handling when the total time reached a new worst case */ @@ -330,33 +329,29 @@ struct DATALAYER_SYSTEM_STATUS_TYPE { * This will show the performance of CAN TX when the total time reached a new worst case */ int64_t time_snap_cantx_us = 0; + /** uint8_t */ /** A counter set each time a new message comes from inverter. * This value then gets decremented every second. Incase we reach 0 * we report the inverter as missing entirely on the CAN bus. */ uint8_t CAN_inverter_still_alive = CAN_STILL_ALIVE; + /** 0 if starting up, 1 if contactors engaged, 2 if the contactors controlled by battery-emulator is opened */ + uint8_t contactors_engaged = 0; + /** State of automatic precharge sequence */ + PrechargeState precharge_status = AUTO_PRECHARGE_IDLE; /** True if the primary battery allows for the contactors to close */ bool battery_allows_contactor_closing = false; - /** True if the second battery is allowed to close the contactors */ bool battery2_allowed_contactor_closing = false; - /** True if the inverter allows for the contactors to close */ bool inverter_allows_contactor_closing = true; - - /** 0 if starting up, 1 if contactors engaged, 2 if the contactors controlled by battery-emulator is opened */ - uint8_t contactors_engaged = 0; /** True if the contactor controlled by battery-emulator is closed. Determined by check_interconnect_available(); if voltage is OK */ bool contactors_battery2_engaged = false; - /** True if the BMS is being reset, by cutting power towards it */ bool BMS_reset_in_progress = false; /** True if the BMS is starting up */ bool BMS_startup_in_progress = false; - - /** State of automatic precharge sequence */ - PrechargeState precharge_status = AUTO_PRECHARGE_IDLE; }; struct DATALAYER_SYSTEM_SETTINGS_TYPE { diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 1fca32d8c..c06582f16 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -7,17 +7,6 @@ struct DATALAYER_INFO_BOLTAMPERA { /** uint16_t */ /** PID polling parameters */ uint16_t battery_5V_ref = 0; - int16_t battery_module_temp_1 = 0; - int16_t battery_module_temp_2 = 0; - int16_t battery_module_temp_3 = 0; - int16_t battery_module_temp_4 = 0; - int16_t battery_module_temp_5 = 0; - int16_t battery_module_temp_6 = 0; - uint16_t battery_cell_average_voltage = 0; - uint16_t battery_cell_average_voltage_2 = 0; - uint16_t battery_terminal_voltage = 0; - uint16_t battery_ignition_power_mode = 0; - int16_t battery_current_7E7 = 0; uint16_t battery_capacity_my17_18 = 0; uint16_t battery_capacity_my19plus = 0; uint16_t battery_SOC_display = 0; @@ -36,10 +25,46 @@ struct DATALAYER_INFO_BOLTAMPERA { uint16_t battery_crash_event = 0; uint16_t battery_HVIL = 0; uint16_t battery_HVIL_status = 0; + uint16_t battery_cell_average_voltage = 0; + uint16_t battery_cell_average_voltage_2 = 0; + uint16_t battery_terminal_voltage = 0; + uint16_t battery_ignition_power_mode = 0; + + int16_t battery_module_temp_1 = 0; + int16_t battery_module_temp_2 = 0; + int16_t battery_module_temp_3 = 0; + int16_t battery_module_temp_4 = 0; + int16_t battery_module_temp_5 = 0; + int16_t battery_module_temp_6 = 0; + int16_t battery_current_7E7 = 0; int16_t battery_current_7E4 = 0; }; struct DATALAYER_INFO_BMWPHEV { + uint64_t min_cell_voltage_data_age = 0; + uint64_t max_cell_voltage_data_age = 0; + + int32_t allowable_charge_amps = 0; + int32_t allowable_discharge_amps = 0; + int32_t iso_safety_int_kohm = 0; //STAT_ISOWIDERSTAND_INT_WERT + int32_t iso_safety_ext_kohm = 0; //STAT_ISOWIDERSTAND_EXT_STD_WERT + int32_t iso_safety_trg_kohm = 0; + int32_t iso_safety_ext_plausible = 0; //STAT_ISOWIDERSTAND_EXT_TRG_PLAUS + int32_t iso_safety_int_plausible = 0; + int32_t iso_safety_trg_plausible = 0; + int32_t iso_safety_kohm = 0; //STAT_R_ISO_ROH_01_WERT + int32_t iso_safety_kohm_quality = 0; //STAT_R_ISO_ROH_QAL_01_INFO Quality of measurement 0-21 (higher better) + + /** uint16_t */ + /** Terminal 30 - 12V SME Supply Voltage */ + uint16_t T30_Voltage = 0; + /** Min/Max Cell SOH*/ + uint16_t min_soh_state = 0; + uint16_t max_soh_state = 0; + + int16_t balancing_status = 0; + int16_t battery_voltage_after_contactor = 0; + /** uint8_t */ /** Status isolation external, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal*/ uint8_t ST_iso_ext = 0; @@ -70,58 +95,24 @@ struct DATALAYER_INFO_BMWPHEV { /** uint8_t */ /** Status cold shutoff valve, 0 OK, 1 Short circuit to GND, 2 Short circuit to 12V, 3 Line break, 6 Driver error, 12 Stuck, 13 Stuck, 15 Invalid Signal*/ uint8_t ST_cold_shutoff_valve = 0; - /** uint16_t */ - /** Terminal 30 - 12V SME Supply Voltage */ - uint16_t T30_Voltage = 0; /** Status HVIL, 1 HVIL OK, 0 HVIL disconnected*/ uint8_t hvil_status = 0; - /** Min/Max Cell SOH*/ - uint16_t min_soh_state = 0; - uint16_t max_soh_state = 0; - int32_t allowable_charge_amps = 0; - int32_t allowable_discharge_amps = 0; - int16_t balancing_status = 0; - int16_t battery_voltage_after_contactor = 0; - uint64_t min_cell_voltage_data_age = 0; - uint64_t max_cell_voltage_data_age = 0; - int32_t iso_safety_int_kohm = 0; //STAT_ISOWIDERSTAND_INT_WERT - int32_t iso_safety_ext_kohm = 0; //STAT_ISOWIDERSTAND_EXT_STD_WERT - int32_t iso_safety_trg_kohm = 0; - int32_t iso_safety_ext_plausible = 0; //STAT_ISOWIDERSTAND_EXT_TRG_PLAUS - int32_t iso_safety_int_plausible = 0; - int32_t iso_safety_trg_plausible = 0; - int32_t iso_safety_kohm = 0; //STAT_R_ISO_ROH_01_WERT - int32_t iso_safety_kohm_quality = 0; //STAT_R_ISO_ROH_QAL_01_INFO Quality of measurement 0-21 (higher better) }; struct DATALAYER_INFO_BYDATTO3 { - /** bool */ - /** User requesting crash reset via WebUI*/ - bool UserRequestCrashReset = false; - /** bool */ - /** Which SOC method currently used. 0 = Estimated, 1 = Measured */ - bool SOC_method = 0; - /** uint16_t */ + uint32_t unknown0 = 0; //Unknown polled value + uint32_t unknown1 = 0; //Unknown polled value + /** SOC% estimate. Estimated from total pack voltage */ uint16_t SOC_estimated = 0; - /** uint16_t */ /** SOC% raw battery value. Highprecision. Can be locked if pack is crashed */ uint16_t SOC_highprec = 0; - /** uint16_t */ /** SOC% polled OBD2 value. Can be locked if pack is crashed */ uint16_t SOC_polled = 0; - /** uint16_t */ /** Voltage raw battery value */ uint16_t voltage_periodic = 0; - /** uint16_t */ /** Voltage polled OBD2*/ uint16_t voltage_polled = 0; - /** int16_t */ - /** All the temperature sensors inside the battery pack*/ - int16_t battery_temperatures[10]; - /** unknown values polled */ - uint32_t unknown0 = 0; - uint32_t unknown1 = 0; uint16_t chargePower = 0; uint16_t charge_times = 0; uint16_t dischargePower = 0; @@ -130,10 +121,21 @@ struct DATALAYER_INFO_BYDATTO3 { uint16_t total_charged_kwh = 0; uint16_t total_discharged_kwh = 0; uint16_t times_full_power = 0; - uint8_t unknown10 = 0; - uint8_t unknown11 = 0; - uint8_t unknown12 = 0; - uint8_t unknown13 = 0; + + /** int16_t */ + /** All the temperature sensors inside the battery pack*/ + int16_t battery_temperatures[10]; + + uint8_t unknown10 = 0; //Unknown polled value + uint8_t unknown11 = 0; //Unknown polled value + uint8_t unknown12 = 0; //Unknown polled value + uint8_t unknown13 = 0; //Unknown polled value + /** bool */ + /** User requesting crash reset via WebUI*/ + bool UserRequestCrashReset = false; + /** bool */ + /** Which SOC method currently used. 0 = Estimated, 1 = Measured */ + bool SOC_method = 0; }; struct DATALAYER_INFO_CELLPOWER { @@ -203,6 +205,8 @@ struct DATALAYER_INFO_CELLPOWER { }; struct DATALAYER_INFO_CHADEMO { + uint8_t CHADEMO_Status = 0; + uint8_t ControlProtocolNumberEV = 0; bool UserRequestRestart = false; bool UserRequestStop = false; bool FaultBatteryVoltageDeviation = false; @@ -210,70 +214,33 @@ struct DATALAYER_INFO_CHADEMO { bool FaultBatteryCurrentDeviation = false; bool FaultBatteryUnderVoltage = false; bool FaultBatteryOverVoltage = false; - uint8_t CHADEMO_Status = 0; - uint8_t ControlProtocolNumberEV = 0; }; struct DATALAYER_INFO_CMFAEV { + uint64_t cumulative_energy_when_discharging = 0; + uint64_t cumulative_energy_when_charging = 0; + uint64_t cumulative_energy_in_regen = 0; + uint16_t soc_z = 0; uint16_t soc_u = 0; uint16_t soh_average = 0; uint16_t max_regen_power = 0; uint16_t max_discharge_power = 0; - int16_t average_temperature = 0; - int16_t minimum_temperature = 0; - int16_t maximum_temperature = 0; uint16_t maximum_charge_power = 0; uint16_t SOH_available_power = 0; uint16_t SOH_generated_power = 0; uint16_t lead_acid_voltage = 0; + + int16_t average_temperature = 0; + int16_t minimum_temperature = 0; + int16_t maximum_temperature = 0; + uint8_t highest_cell_voltage_number = 0; uint8_t lowest_cell_voltage_number = 0; - uint64_t cumulative_energy_when_discharging = 0; - uint64_t cumulative_energy_when_charging = 0; - uint64_t cumulative_energy_in_regen = 0; }; struct DATALAYER_INFO_ECMP { - //mysteryvan parameters - bool MysteryVan = false; - bool CrashMemorized = false; - uint8_t CONTACTOR_OPENING_REASON = 0; - uint8_t TBMU_FAULT_TYPE = 0; - uint8_t CONTACTORS_STATE = 0; - uint16_t HV_BATT_FC_INSU_MINUS_RES, HV_BATT_FC_INSU_PLUS_RES, HV_BATT_FC_VHL_INSU_PLUS_RES, - HV_BATT_ONLY_INSU_MINUS_RES = 0; - bool ALERT_CELL_POOR_CONSIST, ALERT_OVERCHARGE, ALERT_BATT, ALERT_LOW_SOC, ALERT_HIGH_SOC, ALERT_SOC_JUMP, - ALERT_TEMP_DIFF, ALERT_HIGH_TEMP, ALERT_OVERVOLTAGE, ALERT_CELL_OVERVOLTAGE, ALERT_CELL_UNDERVOLTAGE = false; - //ecmp below - uint8_t MainConnectorState = 0; - uint16_t InsulationResistance = 0; - uint8_t InsulationDiag = 0; - bool InterlockOpen = false; - bool UserRequestContactorReset = false; - bool UserRequestCollisionReset = false; - bool UserRequestIsolationReset = false; - bool UserRequestDisableIsoMonitoring = false; - uint8_t pid_welding_detection = 0; - uint8_t pid_reason_open = 0; - uint8_t pid_contactor_status = 0; - uint8_t pid_negative_contactor_control = 0; - uint8_t pid_negative_contactor_status = 0; - uint8_t pid_positive_contactor_control = 0; - uint8_t pid_positive_contactor_status = 0; - uint8_t pid_contactor_negative = 0; - uint8_t pid_contactor_positive = 0; - uint8_t pid_precharge_relay_control = 0; - uint8_t pid_precharge_relay_status = 0; - uint8_t pid_recharge_status = 0; - uint8_t pid_delta_temperature = 0; - uint8_t pid_coldest_module = 0; - uint8_t pid_lowest_temperature = 0; - uint8_t pid_average_temperature = 0; - uint8_t pid_highest_temperature = 0; - uint8_t pid_hottest_module = 0; - uint16_t pid_avg_cell_voltage = 0; - int32_t pid_current = 0; + uint32_t pid_insulation_res_neg = 0; uint32_t pid_insulation_res_pos = 0; uint32_t pid_max_current_10s = 0; @@ -282,26 +249,14 @@ struct DATALAYER_INFO_ECMP { uint32_t pid_max_charge_10s = 0; uint32_t pid_max_charge_30s = 0; uint32_t pid_energy_capacity = 0; - uint8_t pid_highest_cell_voltage_num = 0; - uint8_t pid_lowest_cell_voltage_num = 0; - uint16_t pid_sum_of_cells = 0; - uint16_t pid_cell_min_capacity = 0; - uint8_t pid_cell_voltage_measurement_status = 0; uint32_t pid_insulation_res = 0; - uint16_t pid_pack_voltage = 0; - uint16_t pid_high_cell_voltage = 0; - uint16_t pid_low_cell_voltage = 0; - uint8_t pid_battery_energy = 0; uint32_t pid_crash_counter = 0; - uint8_t pid_wire_crash = 0; - uint8_t pid_CAN_crash = 0; uint32_t pid_history_data = 0; uint32_t pid_lowsoc_counter = 0; uint32_t pid_last_can_failure_detail = 0; uint32_t pid_hw_version_num = 0; uint32_t pid_sw_version_num = 0; uint32_t pid_factory_mode_control = 0; - uint8_t pid_battery_serial[13] = {0}; uint32_t pid_aux_fuse_state = 0; uint32_t pid_battery_state = 0; uint32_t pid_precharge_short_circuit = 0; @@ -320,18 +275,85 @@ struct DATALAYER_INFO_ECMP { uint32_t pid_time_spent_over_55c = 0; uint32_t pid_contactor_closing_counter = 0; uint32_t pid_date_of_manufacture = 0; + + int32_t pid_current = 0; + + uint16_t HV_BATT_FC_INSU_MINUS_RES = 0; //mysteryvan parameters + uint16_t HV_BATT_FC_INSU_PLUS_RES = 0; //mysteryvan parameters + uint16_t HV_BATT_FC_VHL_INSU_PLUS_RES = 0; //mysteryvan parameters + uint16_t HV_BATT_ONLY_INSU_MINUS_RES = 0; //mysteryvan parameters + uint16_t InsulationResistance = 0; + uint16_t pid_avg_cell_voltage = 0; + uint16_t pid_sum_of_cells = 0; + uint16_t pid_cell_min_capacity = 0; + uint16_t pid_pack_voltage = 0; + uint16_t pid_high_cell_voltage = 0; + uint16_t pid_low_cell_voltage = 0; uint16_t pid_SOH_cell_1 = 0; + + uint8_t InsulationDiag = 0; + uint8_t MainConnectorState = 0; + uint8_t CONTACTOR_OPENING_REASON = 0; //mysteryvan parameters + uint8_t TBMU_FAULT_TYPE = 0; //mysteryvan parameters + uint8_t CONTACTORS_STATE = 0; //mysteryvan parameters + uint8_t pid_welding_detection = 0; + uint8_t pid_reason_open = 0; + uint8_t pid_contactor_status = 0; + uint8_t pid_negative_contactor_control = 0; + uint8_t pid_negative_contactor_status = 0; + uint8_t pid_positive_contactor_control = 0; + uint8_t pid_positive_contactor_status = 0; + uint8_t pid_contactor_negative = 0; + uint8_t pid_contactor_positive = 0; + uint8_t pid_precharge_relay_control = 0; + uint8_t pid_precharge_relay_status = 0; + uint8_t pid_recharge_status = 0; + uint8_t pid_delta_temperature = 0; + uint8_t pid_coldest_module = 0; + uint8_t pid_lowest_temperature = 0; + uint8_t pid_average_temperature = 0; + uint8_t pid_highest_temperature = 0; + uint8_t pid_hottest_module = 0; + + bool MysteryVan = false; //mysteryvan parameters + bool CrashMemorized = false; //mysteryvan parameters + bool InterlockOpen = false; + bool UserRequestContactorReset = false; + bool UserRequestCollisionReset = false; + bool UserRequestIsolationReset = false; + bool UserRequestDisableIsoMonitoring = false; + bool ALERT_CELL_POOR_CONSIST = false; //mysteryvan parameters + bool ALERT_OVERCHARGE = false; //mysteryvan parameters + bool ALERT_BATT = false; //mysteryvan parameters + bool ALERT_LOW_SOC = false; //mysteryvan parameters + bool ALERT_HIGH_SOC = false; //mysteryvan parameters + bool ALERT_SOC_JUMP = false; //mysteryvan parameters + bool ALERT_TEMP_DIFF = false; //mysteryvan parameters + bool ALERT_HIGH_TEMP = false; //mysteryvan parameters + bool ALERT_OVERVOLTAGE = false; //mysteryvan parameters + bool ALERT_CELL_OVERVOLTAGE = false; //mysteryvan parameters + bool ALERT_CELL_UNDERVOLTAGE = false; //mysteryvan parameters + + uint8_t pid_battery_energy = 0; + uint8_t pid_wire_crash = 0; + uint8_t pid_CAN_crash = 0; + uint8_t pid_highest_cell_voltage_num = 0; + uint8_t pid_lowest_cell_voltage_num = 0; + uint8_t pid_cell_voltage_measurement_status = 0; + uint8_t pid_battery_serial[13] = {0}; }; struct DATALAYER_INFO_GEELY_GEOMETRY_C { + /** int16_t */ + /** Module temperatures 1-6 */ + int16_t ModuleTemperatures[6] = {0}; + /** uint8_t */ /** Battery software/hardware/serial versions, stores raw HEX values for ASCII chars */ uint8_t BatterySoftwareVersion[16] = {0}; uint8_t BatteryHardwareVersion[16] = {0}; uint8_t BatterySerialNumber[28] = {0}; - /** int16_t */ - /** Module temperatures 1-6 */ - int16_t ModuleTemperatures[6] = {0}; + /** uint16_t */ /** Various values polled via OBD2 PIDs */ uint16_t soc = 0; @@ -351,78 +373,65 @@ struct DATALAYER_INFO_GEELY_GEOMETRY_C { }; struct DATALAYER_INFO_KIAHYUNDAI64 { - uint8_t total_cell_count = 0; - int16_t battery_12V = 0; - uint8_t waterleakageSensor = 0; - int8_t temperature_water_inlet = 0; - int8_t powerRelayTemperature = 0; - uint8_t batteryManagementMode = 0; - uint8_t BMS_ign = 0; - uint8_t batteryRelay = 0; - uint16_t inverterVoltage = 0; - uint8_t ecu_serial_number[16] = {0}; - uint8_t ecu_version_number[16] = {0}; uint32_t cumulative_charge_current_ah = 0; uint32_t cumulative_discharge_current_ah = 0; uint32_t cumulative_energy_charged_kWh = 0; uint32_t cumulative_energy_discharged_kWh = 0; uint32_t powered_on_total_time = 0; + + uint16_t inverterVoltage = 0; uint16_t isolation_resistance_kOhm = 0; uint16_t number_of_standard_charging_sessions = 0; uint16_t number_of_fastcharging_sessions = 0; uint16_t accumulated_normal_charging_energy_kWh = 0; uint16_t accumulated_fastcharging_energy_kWh = 0; + uint16_t battery_12V = 0; + + int8_t temperature_water_inlet = 0; + int8_t powerRelayTemperature = 0; + + uint8_t total_cell_count = 0; + uint8_t waterleakageSensor = 0; + uint8_t batteryManagementMode = 0; + uint8_t BMS_ign = 0; + uint8_t batteryRelay = 0; + + uint8_t ecu_serial_number[16] = {0}; + uint8_t ecu_version_number[16] = {0}; }; struct DATALAYER_INFO_TESLA { - /** uint8_t */ - /** Contactor status */ - //uint8_t status_contactor = 0; - /** uint8_t */ - /** HVIL status */ - uint8_t hvil_status = 0; - /** uint8_t */ - /** Negative contactor state */ - uint8_t packContNegativeState = 0; - /** uint8_t */ - /** Positive contactor state */ - uint8_t packContPositiveState = 0; - /** uint8_t */ - /** HVP set state of contactors */ - uint8_t packContactorSetState = 0; - /** bool */ - /** Battery pack allows closing of contactors */ - bool packCtrsClosingBlocked = false; - /** bool */ - /** Pyro test in progress */ - bool pyroTestInProgress = false; - bool battery_packCtrsOpenNowRequested = false; - bool battery_packCtrsOpenRequested = false; - uint8_t battery_packCtrsRequestStatus = 0; - bool battery_packCtrsResetRequestRequired = false; - bool battery_dcLinkAllowedToEnergize = false; - uint8_t BMS_partNumber[12] = {0}; //stores raw HEX values for ASCII chars - uint16_t BMS_info_buildConfigId = 0; - uint16_t BMS_info_hardwareId = 0; - uint16_t BMS_info_componentId = 0; - uint8_t BMS_info_pcbaId = 0; - uint8_t BMS_info_assemblyId = 0; - uint16_t BMS_info_usageId = 0; - uint16_t BMS_info_subUsageId = 0; - uint8_t BMS_info_platformType = 0; - uint32_t BMS_info_appCrc = 0; uint64_t BMS_info_bootGitHash = 0; - uint8_t BMS_info_bootUdsProtoVersion = 0; + uint64_t PCS_info_bootGitHash = 0; + uint64_t HVP_info_bootGitHash = 0; + + uint32_t HVP_info_bootCrc = 0; + uint32_t HVP_info_appCrc = 0; + uint32_t PCS_info_appCrc = 0; + uint32_t PCS_info_cpu2AppCrc = 0; + uint32_t PCS_info_bootCrc = 0; + uint32_t PCS_dcdc12vSupportLifetimekWh = 0; + uint32_t BMS_info_appCrc = 0; uint32_t BMS_info_bootCrc = 0; - uint8_t battery_serialNumber[15] = {0}; //stores raw HEX values for ASCII chars - uint8_t battery_partNumber[12] = {0}; //stores raw HEX values for ASCII chars - char* battery_manufactureDate; - uint8_t battery_beginning_of_life = 0; - uint8_t battery_battTempPct = 0; + uint32_t battery_packMass = 0; + uint32_t battery_platformMaxBusVoltage = 0; + uint32_t BMS_min_voltage = 0; + uint32_t BMS_max_voltage = 0; + uint32_t battery_max_charge_current = 0; + uint32_t battery_max_discharge_current = 0; + uint32_t battery_soc_min = 0; + uint32_t battery_soc_max = 0; + uint32_t battery_soc_ave = 0; + uint32_t battery_soc_ui = 0; + + uint16_t BMS_info_buildConfigId = 0; + uint16_t BMS_info_hardwareId = 0; + uint16_t BMS_info_componentId = 0; + uint16_t BMS_info_usageId = 0; + uint16_t BMS_info_subUsageId = 0; uint16_t battery_dcdcLvBusVolt = 0; uint16_t battery_dcdcHvBusVolt = 0; uint16_t battery_dcdcLvOutputCurrent = 0; - bool BMS352_mux = false; // variable to store when 0x352 mux is present uint16_t battery_nominal_full_pack_energy = 0; uint16_t battery_nominal_full_pack_energy_m0 = 0; uint16_t battery_nominal_energy_remaining = 0; @@ -435,62 +444,112 @@ struct DATALAYER_INFO_TESLA { uint16_t battery_energy_buffer_m1 = 0; uint16_t battery_expected_energy_remaining = 0; uint16_t battery_expected_energy_remaining_m1 = 0; - bool battery_full_charge_complete = false; - bool battery_fully_charged = false; uint16_t battery_BrickVoltageMax = 0; uint16_t battery_BrickVoltageMin = 0; - uint8_t battery_BrickVoltageMaxNum = 0; - uint8_t battery_BrickVoltageMinNum = 0; - uint8_t battery_BrickTempMaxNum = 0; - uint8_t battery_BrickTempMinNum = 0; - uint8_t battery_BrickModelTMax = 0; - uint8_t battery_BrickModelTMin = 0; + uint16_t HVP_hvp1v5Ref = 0; + uint16_t HVP_shuntCurrentDebug = 0; + uint16_t PCS_dcdcTemp = 0; + uint16_t PCS_ambientTemp = 0; + uint16_t PCS_chgPhATemp = 0; + uint16_t PCS_chgPhBTemp = 0; + uint16_t PCS_chgPhCTemp = 0; + uint16_t PCS_dcdcMaxLvOutputCurrent = 0; + uint16_t PCS_dcdcCurrentLimit = 0; + uint16_t PCS_dcdcLvOutputCurrentTempLimit = 0; + uint16_t PCS_dcdcUnifiedCommand = 0; + uint16_t PCS_dcdcCLAControllerOutput = 0; + uint16_t PCS_dcdcTankVoltage = 0; + uint16_t PCS_dcdcTankVoltageTarget = 0; + uint16_t PCS_dcdcClaCurrentFreq = 0; + uint16_t PCS_dcdcTCommMeasured = 0; + uint16_t PCS_dcdcShortTimeUs = 0; + uint16_t PCS_dcdcHalfPeriodUs = 0; + uint16_t PCS_dcdcIntervalMaxFrequency = 0; + uint16_t PCS_dcdcIntervalMaxHvBusVolt = 0; + uint16_t PCS_dcdcIntervalMaxLvBusVolt = 0; + uint16_t PCS_dcdcIntervalMaxLvOutputCurr = 0; + uint16_t PCS_dcdcIntervalMinFrequency = 0; + uint16_t PCS_dcdcIntervalMinHvBusVolt = 0; + uint16_t PCS_dcdcIntervalMinLvBusVolt = 0; + uint16_t PCS_dcdcIntervalMinLvOutputCurr = 0; uint16_t battery_packConfigMultiplexer = 0; uint16_t battery_moduleType = 0; uint16_t battery_reservedConfig = 0; - uint32_t battery_packMass = 0; - uint32_t battery_platformMaxBusVoltage = 0; - uint32_t BMS_min_voltage = 0; - uint32_t BMS_max_voltage = 0; - uint32_t battery_max_charge_current = 0; - uint32_t battery_max_discharge_current = 0; - uint32_t battery_soc_min = 0; - uint32_t battery_soc_max = 0; - uint32_t battery_soc_ave = 0; - uint32_t battery_soc_ui = 0; - bool BMS_hvilFault = false; - uint8_t BMS_contactorState = 0; - uint8_t BMS_state = 0; - uint8_t BMS_hvState = 0; uint16_t BMS_isolationResistance = 0; - uint8_t BMS_uiChargeStatus = 0; - bool BMS_diLimpRequest = false; uint16_t BMS_chgPowerAvailable = 0; - bool BMS_pcsPwmEnabled = false; uint16_t BMS_maxRegenPower = 0; uint16_t BMS_maxDischargePower = 0; uint16_t BMS_maxStationaryHeatPower = 0; uint16_t BMS_hvacPowerBudget = 0; - uint8_t BMS_notEnoughPowerForHeatPump = 0; - uint8_t BMS_powerLimitState = 0; - uint8_t BMS_inverterTQF = 0; uint16_t BMS_powerDissipation = 0; - uint8_t BMS_flowRequest = 0; uint16_t BMS_inletActiveCoolTargetT = 0; uint16_t BMS_inletPassiveTargetT = 0; uint16_t BMS_inletActiveHeatTargetT = 0; uint16_t BMS_packTMin = 0; uint16_t BMS_packTMax = 0; - bool BMS_pcsNoFlowRequest = false; - bool BMS_noFlowRequest = false; + uint16_t PCS_info_buildConfigId = 0; + uint16_t PCS_info_hardwareId = 0; + uint16_t PCS_info_componentId = 0; + uint16_t PCS_dcdcMaxOutputCurrentAllowed = 0; + uint16_t PCS_info_usageId = 0; + uint16_t PCS_info_subUsageId = 0; + uint16_t HVP_dcLinkVoltage = 0; + uint16_t HVP_packVoltage = 0; + uint16_t HVP_fcLinkVoltage = 0; + uint16_t HVP_packContVoltage = 0; + uint16_t HVP_packNegativeV = 0; + uint16_t HVP_packPositiveV = 0; + uint16_t HVP_pyroAnalog = 0; + uint16_t HVP_dcLinkNegativeV = 0; + uint16_t HVP_dcLinkPositiveV = 0; + uint16_t HVP_fcLinkNegativeV = 0; + uint16_t HVP_fcContCoilCurrent = 0; + uint16_t HVP_fcContVoltage = 0; + uint16_t HVP_hvilInVoltage = 0; + uint16_t HVP_hvilOutVoltage = 0; + uint16_t HVP_fcLinkPositiveV = 0; + uint16_t HVP_packContCoilCurrent = 0; + uint16_t HVP_battery12V = 0; + uint16_t HVP_shuntRefVoltageDbg = 0; + uint16_t HVP_shuntAuxCurrentDbg = 0; + uint16_t HVP_shuntBarTempDbg = 0; + uint16_t HVP_shuntAsicTempDbg = 0; + uint16_t HVP_info_buildConfigId = 0; + uint16_t HVP_info_hardwareId = 0; + uint16_t HVP_info_componentId = 0; + uint16_t HVP_info_usageId = 0; + uint16_t HVP_info_subUsageId = 0; + + uint8_t hvil_status = 0; + uint8_t packContNegativeState = 0; + uint8_t packContPositiveState = 0; + uint8_t packContactorSetState = 0; + uint8_t battery_packCtrsRequestStatus = 0; + uint8_t BMS_info_pcbaId = 0; + uint8_t BMS_info_assemblyId = 0; + uint8_t BMS_info_platformType = 0; + uint8_t BMS_info_bootUdsProtoVersion = 0; + uint8_t battery_beginning_of_life = 0; + uint8_t battery_battTempPct = 0; + uint8_t battery_BrickVoltageMaxNum = 0; + uint8_t battery_BrickVoltageMinNum = 0; + uint8_t battery_BrickTempMaxNum = 0; + uint8_t battery_BrickTempMinNum = 0; + uint8_t battery_BrickModelTMax = 0; + uint8_t battery_BrickModelTMin = 0; + uint8_t BMS_flowRequest = 0; + uint8_t BMS_uiChargeStatus = 0; + uint8_t BMS_contactorState = 0; + uint8_t BMS_state = 0; + uint8_t BMS_hvState = 0; + uint8_t BMS_notEnoughPowerForHeatPump = 0; + uint8_t BMS_powerLimitState = 0; + uint8_t BMS_inverterTQF = 0; uint8_t PCS_dcdcPrechargeStatus = 0; uint8_t PCS_dcdc12VSupportStatus = 0; uint8_t PCS_dcdcHvBusDischargeStatus = 0; uint8_t PCS_dcdcMainState = 0; uint8_t PCS_dcdcSubState = 0; - bool PCS_dcdcFaulted = false; - bool PCS_dcdcOutputIsLimited = false; - uint16_t PCS_dcdcMaxOutputCurrentAllowed = 0; uint8_t PCS_dcdcPrechargeRtyCnt = 0; uint8_t PCS_dcdc12VSupportRtyCnt = 0; uint8_t PCS_dcdcDischargeRtyCnt = 0; @@ -498,45 +557,35 @@ struct DATALAYER_INFO_TESLA { uint8_t PCS_dcdcSupportingFixedLvTarget = 0; uint8_t PCS_dcdcPrechargeRestartCnt = 0; uint8_t PCS_dcdcInitialPrechargeSubState = 0; - uint8_t PCS_partNumber[13] = {0}; //stores raw HEX values for ASCII chars - uint16_t PCS_info_buildConfigId = 0; - uint16_t PCS_info_hardwareId = 0; - uint16_t PCS_info_componentId = 0; uint8_t PCS_info_pcbaId = 0; uint8_t PCS_info_assemblyId = 0; - uint16_t PCS_info_usageId = 0; - uint16_t PCS_info_subUsageId = 0; uint8_t PCS_info_platformType = 0; - uint32_t PCS_info_appCrc = 0; - uint32_t PCS_info_cpu2AppCrc = 0; - uint64_t PCS_info_bootGitHash = 0; uint8_t PCS_info_bootUdsProtoVersion = 0; - uint32_t PCS_info_bootCrc = 0; - uint16_t PCS_dcdcTemp = 0; - uint16_t PCS_ambientTemp = 0; - uint16_t PCS_chgPhATemp = 0; - uint16_t PCS_chgPhBTemp = 0; - uint16_t PCS_chgPhCTemp = 0; - uint16_t PCS_dcdcMaxLvOutputCurrent = 0; - uint16_t PCS_dcdcCurrentLimit = 0; - uint16_t PCS_dcdcLvOutputCurrentTempLimit = 0; - uint16_t PCS_dcdcUnifiedCommand = 0; - uint16_t PCS_dcdcCLAControllerOutput = 0; - uint16_t PCS_dcdcTankVoltage = 0; - uint16_t PCS_dcdcTankVoltageTarget = 0; - uint16_t PCS_dcdcClaCurrentFreq = 0; - uint16_t PCS_dcdcTCommMeasured = 0; - uint16_t PCS_dcdcShortTimeUs = 0; - uint16_t PCS_dcdcHalfPeriodUs = 0; - uint16_t PCS_dcdcIntervalMaxFrequency = 0; - uint16_t PCS_dcdcIntervalMaxHvBusVolt = 0; - uint16_t PCS_dcdcIntervalMaxLvBusVolt = 0; - uint16_t PCS_dcdcIntervalMaxLvOutputCurr = 0; - uint16_t PCS_dcdcIntervalMinFrequency = 0; - uint16_t PCS_dcdcIntervalMinHvBusVolt = 0; - uint16_t PCS_dcdcIntervalMinLvBusVolt = 0; - uint16_t PCS_dcdcIntervalMinLvOutputCurr = 0; - uint32_t PCS_dcdc12vSupportLifetimekWh = 0; + uint8_t HVP_info_platformType = 0; + uint8_t HVP_info_pcbaId = 0; + uint8_t HVP_info_assemblyId = 0; + uint8_t HVP_info_bootUdsProtoVersion = 0; + uint8_t HVP_shuntHwMia = 0; + uint8_t HVP_shuntAuxCurrentStatus = 0; + uint8_t HVP_shuntBarTempStatus = 0; + uint8_t HVP_shuntAsicTempStatus = 0; + + bool packCtrsClosingBlocked = false; + bool pyroTestInProgress = false; + bool battery_packCtrsOpenNowRequested = false; + bool battery_packCtrsOpenRequested = false; + bool battery_packCtrsResetRequestRequired = false; + bool battery_dcLinkAllowedToEnergize = false; + bool BMS352_mux = false; // variable to store when 0x352 mux is present + bool battery_full_charge_complete = false; + bool battery_fully_charged = false; + bool BMS_hvilFault = false; + bool BMS_diLimpRequest = false; + bool BMS_pcsPwmEnabled = false; + bool BMS_pcsNoFlowRequest = false; + bool BMS_noFlowRequest = false; + bool PCS_dcdcFaulted = false; + bool PCS_dcdcOutputIsLimited = false; bool HVP_gpioPassivePyroDepl = false; bool HVP_gpioPyroIsoEn = false; bool HVP_gpioCpFaultIn = false; @@ -561,150 +610,87 @@ struct DATALAYER_INFO_TESLA { bool HVP_gpioFcContPowerEnable = false; bool HVP_gpioHvilEnable = false; bool HVP_gpioSecDrdy = false; - uint16_t HVP_hvp1v5Ref = 0; - uint16_t HVP_shuntCurrentDebug = 0; bool HVP_packCurrentMia = false; bool HVP_auxCurrentMia = false; bool HVP_currentSenseMia = false; bool HVP_shuntRefVoltageMismatch = false; bool HVP_shuntThermistorMia = false; - uint8_t HVP_partNumber[13] = {0}; //stores raw HEX values for ASCII chars - uint16_t HVP_info_buildConfigId = 0; - uint16_t HVP_info_hardwareId = 0; - uint16_t HVP_info_componentId = 0; - uint8_t HVP_info_pcbaId = 0; - uint8_t HVP_info_assemblyId = 0; - uint16_t HVP_info_usageId = 0; - uint16_t HVP_info_subUsageId = 0; - uint8_t HVP_info_platformType = 0; - uint32_t HVP_info_appCrc = 0; - uint64_t HVP_info_bootGitHash = 0; - uint8_t HVP_info_bootUdsProtoVersion = 0; - uint32_t HVP_info_bootCrc = 0; - uint8_t HVP_shuntHwMia = 0; - uint16_t HVP_dcLinkVoltage = 0; - uint16_t HVP_packVoltage = 0; - uint16_t HVP_fcLinkVoltage = 0; - uint16_t HVP_packContVoltage = 0; - uint16_t HVP_packNegativeV = 0; - uint16_t HVP_packPositiveV = 0; - uint16_t HVP_pyroAnalog = 0; - uint16_t HVP_dcLinkNegativeV = 0; - uint16_t HVP_dcLinkPositiveV = 0; - uint16_t HVP_fcLinkNegativeV = 0; - uint16_t HVP_fcContCoilCurrent = 0; - uint16_t HVP_fcContVoltage = 0; - uint16_t HVP_hvilInVoltage = 0; - uint16_t HVP_hvilOutVoltage = 0; - uint16_t HVP_fcLinkPositiveV = 0; - uint16_t HVP_packContCoilCurrent = 0; - uint16_t HVP_battery12V = 0; - uint16_t HVP_shuntRefVoltageDbg = 0; - uint16_t HVP_shuntAuxCurrentDbg = 0; - uint16_t HVP_shuntBarTempDbg = 0; - uint16_t HVP_shuntAsicTempDbg = 0; - uint8_t HVP_shuntAuxCurrentStatus = 0; - uint8_t HVP_shuntBarTempStatus = 0; - uint8_t HVP_shuntAsicTempStatus = 0; + + uint8_t BMS_partNumber[12] = {0}; //stores raw HEX values for ASCII chars + uint8_t battery_serialNumber[15] = {0}; //stores raw HEX values for ASCII chars + uint8_t battery_partNumber[12] = {0}; //stores raw HEX values for ASCII chars + uint8_t PCS_partNumber[13] = {0}; //stores raw HEX values for ASCII chars + uint8_t HVP_partNumber[13] = {0}; //stores raw HEX values for ASCII chars + char* battery_manufactureDate; }; struct DATALAYER_INFO_NISSAN_LEAF { - /** uint8_t */ - /** Battery info, stores raw HEX values for ASCII chars */ - uint8_t BatterySerialNumber[15] = {0}; - uint8_t BatteryPartNumber[7] = {0}; - uint8_t BMSIDcode[8] = {0}; - /** uint8_t */ - /** Enum, ZE0 = 0, AZE0 = 1, ZE1 = 2 */ - uint8_t LEAF_gen = 0; - /** uint16_t */ + /** Cryptographic challenge to be solved */ + uint32_t CryptoChallenge = 0; + /** Solution for crypto challenge, MSBs */ + uint32_t SolvedChallengeMSB = 0; + /** Solution for crypto challenge, LSBs */ + uint32_t SolvedChallengeLSB = 0; + /** 77Wh per gid. LEAF specific unit */ uint16_t GIDS = 0; - /** uint16_t */ /** Max regen power in kW */ uint16_t ChargePowerLimit = 0; - /** uint16_t */ /** Internal resistance in percentage */ uint16_t battery_HX = 0; - /** int16_t */ - /** Max charge power in kW */ - int16_t MaxPowerForCharger = 0; - /** bool */ - /** Interlock status */ - bool Interlock = false; - /** int16_t */ /** Insulation resistance, most likely kOhm */ uint16_t Insulation = 0; - /** int16_t */ + + /** Max charge power in kW */ + int16_t MaxPowerForCharger = 0; /** Temperature sensoros 1-4 */ int16_t temperature1 = 0; int16_t temperature2 = 0; int16_t temperature3 = 0; // This sensor not available on 2013+ packs int16_t temperature4 = 0; - /** uint8_t */ + + /** Enum, ZE0 = 0, AZE0 = 1, ZE1 = 2 */ + uint8_t LEAF_gen = 0; /** battery_FAIL status */ uint8_t RelayCutRequest = 0; - /** uint8_t */ /** battery_STATUS status */ uint8_t FailsafeStatus = 0; - /** bool */ + + /** Interlock status */ + bool Interlock = false; /** True if fully charged */ bool Full = false; - /** bool */ /** True if battery empty */ bool Empty = false; - /** bool */ /** Battery pack allows closing of contacors */ bool MainRelayOn = false; - /** bool */ /** True if heater exists */ bool HeatExist = false; - /** bool */ /** Heater stopped */ bool HeatingStop = false; - /** bool */ /** Heater starting */ bool HeatingStart = false; - /** bool */ /** Heat request sent*/ bool HeaterSendRequest = false; - /** bool */ /** User requesting SOH reset via WebUI*/ bool UserRequestSOHreset = false; - /** bool */ /** True if the crypto challenge response from BMS is signalling a failed attempt*/ bool challengeFailed = false; - /** uint32_t */ - /** Cryptographic challenge to be solved */ - uint32_t CryptoChallenge = 0; - /** uint32_t */ - /** Solution for crypto challenge, MSBs */ - uint32_t SolvedChallengeMSB = 0; - /** uint32_t */ - /** Solution for crypto challenge, LSBs */ - uint32_t SolvedChallengeLSB = 0; + + /** Battery info, stores raw HEX values for ASCII chars */ + uint8_t BatterySerialNumber[15] = {0}; + uint8_t BatteryPartNumber[7] = {0}; + uint8_t BMSIDcode[8] = {0}; }; struct DATALAYER_INFO_MEB { - /** uint8_t */ - /** Service disconnect switch status */ - bool SDSW = 0; - /** uint8_t */ - /** Pilotline status */ - bool pilotline = 0; - /** uint8_t */ - /** Transportation mode status */ - bool transportmode = 0; - /** uint8_t */ - /** Componentprotection mode status */ - bool componentprotection = 0; - /** uint8_t */ - /** Shutdown status */ - bool shutdown_active = 0; - /** uint8_t */ - /** Battery heating status */ - bool battery_heating = 0; - /** uint8_t */ + /** Isolation resistance in kOhm */ + uint32_t isolation_resistance = 0; + int32_t BMS_voltage_intermediate_dV = 0; + int32_t BMS_voltage_dV = 0; + + uint16_t battery_temperature_dC = 0; + /** All realtime_ warnings have same enumeration, 0 = no fault, 1 = error level 1, 2 error level 2, 3 error level 3 */ uint8_t rt_overcurrent = 0; uint8_t rt_CAN_fault = 0; @@ -721,55 +707,46 @@ struct DATALAYER_INFO_MEB { uint8_t rt_cell_undervol = 0; uint8_t rt_cell_imbalance = 0; uint8_t rt_battery_unathorized = 0; - /** uint8_t */ /** HVIL status, 0 = Init, 1 = Closed, 2 = Open!, 3 = Fault */ uint8_t HVIL = 0; - /** uint8_t */ /** 0 = HV inactive, 1 = HV active, 2 = Balancing, 3 = Extern charging, 4 = AC charging, 5 = Battery error, 6 = DC charging, 7 = init */ uint8_t BMS_mode = 0; - /** uint8_t */ /** 1 = Battery display, 4 = Battery display OK, 4 = Display battery charging, 6 = Display battery check, 7 = Fault */ uint8_t battery_diagnostic = 0; - /** uint8_t */ /** 0 = init, 1 = no open HV line detected, 2 = open HV line , 3 = fault */ uint8_t status_HV_line = 0; - /** uint8_t */ /** 0 = OK, 1 = Not OK, 0x06 = init, 0x07 = fault */ - bool BMS_fault_performance = false; //Error: Battery performance is limited (e.g. due to sensor or fan failure) - bool BMS_fault_emergency_shutdown_crash = - false; //Error: Safety-critical error (crash detection) Battery contactors are already opened / will be opened immediately Signal is read directly by the EMS and initiates an AKS of the PWR and an active discharge of the DC link - - bool BMS_error_shutdown_request = - false; // Fault: Fault condition, requires battery contactors to be opened internal battery error; Advance notification of an impending opening of the battery contactors by the BMS - bool BMS_error_shutdown = - false; // Fault: Fault condition, requires battery contactors to be opened Internal battery error, battery contactors opened without notice by the BMS uint8_t warning_support = 0; - /** uint32_t */ - /** Isolation resistance in kOhm */ - uint32_t isolation_resistance = 0; - /** uint8_t */ /** 0=Init, 1=BMS intermediate circuit voltage-free (U_Zwkr < 20V), 2=BMS intermediate circuit not voltage-free (U_Zwkr >/= 25V, hysteresis), 3=Error */ uint8_t BMS_status_voltage_free = 0; - /** uint8_t */ /** 0 Component_IO, 1 Restricted_CompFkt_Isoerror_I, 2 Restricted_CompFkt_Isoerror_II, 3 Restricted_CompFkt_Interlock, 4 Restricted_CompFkt_SD, 5 Restricted_CompFkt_Performance red, 6 = No component function, 7 = Init */ uint8_t BMS_error_status = 0; - /** uint8_t */ /** 0 init, 1 closed, 2 open, 3 fault */ uint8_t BMS_Kl30c_Status = 0; - /** bool */ - /** true if BMS requests error/warning light */ - bool BMS_OBD_MIL = 0; - bool BMS_error_lamp_req = 0; - bool BMS_warning_lamp_req = 0; - int32_t BMS_voltage_intermediate_dV = 0; - int32_t BMS_voltage_dV = 0; uint8_t balancing_active = 0; + uint8_t BMS_welded_contactors_status = 0; + bool balancing_request = 0; bool charging_active = 0; + bool BMS_OBD_MIL = 0; /** true if BMS requests error/warning light */ + bool BMS_error_lamp_req = 0; + bool BMS_warning_lamp_req = 0; + bool BMS_fault_performance = false; //Error: Battery performance is limited (e.g. due to sensor or fan failure) + bool BMS_fault_emergency_shutdown_crash = + false; //Error: Safety-critical error (crash detection) Battery contactors are already opened / will be opened immediately Signal is read directly by the EMS and initiates an AKS of the PWR and an active discharge of the DC link + bool BMS_error_shutdown_request = + false; // Fault: Fault condition, requires battery contactors to be opened internal battery error; Advance notification of an impending opening of the battery contactors by the BMS + bool BMS_error_shutdown = + false; // Fault: Fault condition, requires battery contactors to be opened Internal battery error, battery contactors opened without notice by the BMS + bool SDSW = 0; /** Service disconnect switch status */ + bool pilotline = 0; /** Pilotline status */ + bool transportmode = 0; /** Transportation mode status */ + bool componentprotection = 0; /** Componentprotection mode status */ + bool shutdown_active = 0; /** Shutdown status */ + bool battery_heating = 0; /** Battery heating status */ + float temp_points[18] = {0}; uint16_t celltemperature_dC[56] = {0}; - uint16_t battery_temperature_dC = 0; - uint8_t BMS_welded_contactors_status = 0; }; struct DATALAYER_INFO_VOLVO_POLESTAR { @@ -778,17 +755,16 @@ struct DATALAYER_INFO_VOLVO_POLESTAR { uint16_t soc_rescaled = 0; uint16_t soh_bms = 0; uint16_t BECMsupplyVoltage = 12000; - uint16_t BECMBatteryVoltage = 0; - int16_t BECMBatteryCurrent = 0; uint16_t BECMUDynMaxLim = 0; uint16_t BECMUDynMinLim = 0; - uint16_t HvBattPwrLimDcha1 = 0; uint16_t HvBattPwrLimDchaSoft = 0; uint16_t HvBattPwrLimDchaSlowAgi = 0; uint16_t HvBattPwrLimChrgSlowAgi = 0; + int16_t BECMBatteryCurrent = 0; + uint8_t HVSysRlySts = 0; uint8_t HVSysDCRlySts1 = 0; uint8_t HVSysDCRlySts2 = 0; @@ -833,7 +809,9 @@ struct DATALAYER_INFO_VOLVO_HYBRID { }; struct DATALAYER_INFO_ZOE { - /** uint8_t */ + uint16_t mileage_km = 0; + uint16_t alltime_kWh = 0; + uint8_t CUV = 0; uint8_t HVBIR = 0; uint8_t HVBUV = 0; @@ -842,15 +820,10 @@ struct DATALAYER_INFO_ZOE { uint8_t HVBOT = 0; uint8_t HVBOV = 0; uint8_t COV = 0; - uint16_t mileage_km = 0; - uint16_t alltime_kWh = 0; }; struct DATALAYER_INFO_ZOE_PH2 { - /** User requesting NVROL reset via WebUI*/ - bool UserRequestNVROLReset = false; /** uint16_t */ - /** Values WIP*/ uint16_t battery_soc = 0; uint16_t battery_usable_soc = 0; uint16_t battery_soh = 0; @@ -891,6 +864,8 @@ struct DATALAYER_INFO_ZOE_PH2 { uint16_t battery_pack_time = 0; uint16_t battery_soc_min = 0; uint16_t battery_soc_max = 0; + /** User requesting NVROL reset via WebUI*/ + bool UserRequestNVROLReset = false; }; class DataLayerExtended { From dc59a9f83350c5d596363462f691311dd304d04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 21 Oct 2025 23:15:28 +0300 Subject: [PATCH 056/536] Simplify datalayer by removing one category --- Software/src/battery/BMW-I3-BATTERY.cpp | 2 +- Software/src/battery/BMW-SBOX.cpp | 6 ++---- Software/src/battery/MEB-BATTERY.cpp | 10 +++++----- Software/src/battery/TESLA-BATTERY.cpp | 2 +- .../contactorcontrol/comm_contactorcontrol.cpp | 6 ++---- Software/src/communication/nvm/comm_nvm.cpp | 6 +++--- .../precharge_control/precharge_control.cpp | 12 ++++++------ Software/src/datalayer/datalayer.h | 8 ++------ Software/src/devboard/safety/safety.cpp | 8 ++++---- Software/src/devboard/webserver/webserver.cpp | 6 +++--- 10 files changed, 29 insertions(+), 37 deletions(-) diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp index 5ae9d5100..97c334eac 100644 --- a/Software/src/battery/BMW-I3-BATTERY.cpp +++ b/Software/src/battery/BMW-I3-BATTERY.cpp @@ -42,7 +42,7 @@ uint8_t BmwI3Battery::increment_alive_counter(uint8_t counter) { } void BmwI3Battery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer - if (datalayer.system.settings.equipment_stop_active == true) { + if (datalayer.system.info.equipment_stop_active == true) { digitalWrite(wakeup_pin, LOW); // Turn off wakeup pin } else if (millis() > INTERVAL_1_S) { digitalWrite(wakeup_pin, HIGH); // Wake up the battery diff --git a/Software/src/battery/BMW-SBOX.cpp b/Software/src/battery/BMW-SBOX.cpp index ff90ce446..d233a9bf8 100644 --- a/Software/src/battery/BMW-SBOX.cpp +++ b/Software/src/battery/BMW-SBOX.cpp @@ -103,8 +103,7 @@ void BmwSbox::transmit_can(unsigned long currentMillis) { SBOX_100.data.u8[0] = 0x55; // All open if (datalayer.system.status.battery_allows_contactor_closing && - datalayer.system.status.inverter_allows_contactor_closing && - !datalayer.system.settings.equipment_stop_active && + datalayer.system.status.inverter_allows_contactor_closing && !datalayer.system.info.equipment_stop_active && (datalayer.shunt.measured_voltage_mV > MINIMUM_INPUT_VOLTAGE * 1000)) { contactorStatus = PRECHARGE; } @@ -112,8 +111,7 @@ void BmwSbox::transmit_can(unsigned long currentMillis) { // In case the inverter requests contactors to open, set the state accordingly if (contactorStatus == COMPLETED) { //Incase inverter (or estop) requests contactors to open, make state machine jump to Disconnected state (recoverable) - if (!datalayer.system.status.inverter_allows_contactor_closing || - datalayer.system.settings.equipment_stop_active) { + if (!datalayer.system.status.inverter_allows_contactor_closing || datalayer.system.info.equipment_stop_active) { contactorStatus = DISCONNECTED; } } diff --git a/Software/src/battery/MEB-BATTERY.cpp b/Software/src/battery/MEB-BATTERY.cpp index bda14b14d..f4bffbed5 100644 --- a/Software/src/battery/MEB-BATTERY.cpp +++ b/Software/src/battery/MEB-BATTERY.cpp @@ -1355,7 +1355,7 @@ void MebBattery::transmit_can(unsigned long currentMillis) { //HV request and DC/DC control lies in 0x503 - if ((!datalayer.system.settings.equipment_stop_active) && datalayer.battery.status.real_bms_status != BMS_FAULT && + if ((!datalayer.system.info.equipment_stop_active) && datalayer.battery.status.real_bms_status != BMS_FAULT && (datalayer.battery.status.real_bms_status == BMS_ACTIVE || (datalayer.battery.status.real_bms_status == BMS_STANDBY && (hv_requested || @@ -1363,7 +1363,7 @@ void MebBattery::transmit_can(unsigned long currentMillis) { labs(((int32_t)datalayer.battery.status.voltage_dV) - ((int32_t)datalayer_extended.meb.BMS_voltage_intermediate_dV)) < 200))))) { hv_requested = true; - datalayer.system.settings.start_precharging = false; + datalayer.system.info.start_precharging = false; if (MEB_503.data.u8[3] == BMS_TARGET_HV_OFF) { logging.printf("MEB: Requesting HV\n"); } @@ -1381,11 +1381,11 @@ void MebBattery::transmit_can(unsigned long currentMillis) { MEB_503.data.u8[5] = 0x82; // Bordnetz Active MEB_503.data.u8[6] = 0xE0; // Request emergency shutdown HV system == 0, false } else if ((first_can_msg > 0 && currentMillis > first_can_msg + 1000 && BMS_mode != 7) || - datalayer.system.settings.equipment_stop_active) { //FAULT STATE, open contactors + datalayer.system.info.equipment_stop_active) { //FAULT STATE, open contactors if (datalayer.battery.status.bms_status != FAULT && datalayer.battery.status.real_bms_status == BMS_STANDBY && - !datalayer.system.settings.equipment_stop_active) { - datalayer.system.settings.start_precharging = true; + !datalayer.system.info.equipment_stop_active) { + datalayer.system.info.start_precharging = true; } if (MEB_503.data.u8[3] != BMS_TARGET_HV_OFF) { diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index b83e18616..dd562faea 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -799,7 +799,7 @@ void TeslaBattery:: //Safety checks for CAN message sending if ((datalayer.system.status.inverter_allows_contactor_closing == true) && - (datalayer.battery.status.bms_status != FAULT) && (!datalayer.system.settings.equipment_stop_active)) { + (datalayer.battery.status.bms_status != FAULT) && (!datalayer.system.info.equipment_stop_active)) { // Carry on: 0x221 DRIVE state & reset power down timer vehicleState = CAR_DRIVE; powerDownSeconds = 9; diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index c9ac25ca3..d851e51ff 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -175,8 +175,7 @@ void handle_contactors() { set(posPin, OFF, PWM_OFF_DUTY); datalayer.system.status.contactors_engaged = 0; - if (datalayer.system.status.inverter_allows_contactor_closing && - !datalayer.system.settings.equipment_stop_active) { + if (datalayer.system.status.inverter_allows_contactor_closing && !datalayer.system.info.equipment_stop_active) { contactorStatus = START_PRECHARGE; } } @@ -184,8 +183,7 @@ void handle_contactors() { // In case the inverter requests contactors to open, set the state accordingly if (contactorStatus == COMPLETED) { //Incase inverter (or estop) requests contactors to open, make state machine jump to Disconnected state (recoverable) - if (!datalayer.system.status.inverter_allows_contactor_closing || - datalayer.system.settings.equipment_stop_active) { + if (!datalayer.system.status.inverter_allows_contactor_closing || datalayer.system.info.equipment_stop_active) { contactorStatus = DISCONNECTED; } // Skip running the state machine below if it has already completed diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index c42c01ad4..f80cfd6e8 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -22,8 +22,8 @@ void init_stored_settings() { settings.begin("batterySettings", false); // Always get the equipment stop status - datalayer.system.settings.equipment_stop_active = settings.getBool("EQUIPMENT_STOP", false); - if (datalayer.system.settings.equipment_stop_active) { + datalayer.system.info.equipment_stop_active = settings.getBool("EQUIPMENT_STOP", false); + if (datalayer.system.info.equipment_stop_active) { DEBUG_PRINTF("Equipment stop status set in boot."); set_event(EVENT_EQUIPMENT_STOP, 1); } @@ -191,7 +191,7 @@ void init_stored_settings() { void store_settings_equipment_stop() { settings.begin("batterySettings", false); - settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active); + settings.putBool("EQUIPMENT_STOP", datalayer.system.info.equipment_stop_active); settings.end(); } diff --git a/Software/src/communication/precharge_control/precharge_control.cpp b/Software/src/communication/precharge_control/precharge_control.cpp index 5117f5e4a..260419b3b 100644 --- a/Software/src/communication/precharge_control/precharge_control.cpp +++ b/Software/src/communication/precharge_control/precharge_control.cpp @@ -66,7 +66,7 @@ void handle_precharge_control(unsigned long currentMillis) { switch (datalayer.system.status.precharge_status) { case AUTO_PRECHARGE_IDLE: - if (datalayer.system.settings.start_precharging) { + if (datalayer.system.info.start_precharging) { datalayer.system.status.precharge_status = AUTO_PRECHARGE_START; } break; @@ -115,10 +115,10 @@ void handle_precharge_control(unsigned long currentMillis) { logging.printf("Precharge: CRITICAL FAILURE (timeout/BMS fault) -> REQUIRES REBOOT\n"); set_event(EVENT_AUTOMATIC_PRECHARGE_FAILURE, 0); // Force stop any further precharge attempts - datalayer.system.settings.start_precharging = false; + datalayer.system.info.start_precharging = false; } else if ((datalayer.battery.status.real_bms_status != BMS_STANDBY && datalayer.battery.status.real_bms_status != BMS_ACTIVE) || - datalayer.battery.status.bms_status != ACTIVE || datalayer.system.settings.equipment_stop_active) { + datalayer.battery.status.bms_status != ACTIVE || datalayer.system.info.equipment_stop_active) { pinMode(hia4v1_pin, OUTPUT); digitalWrite(hia4v1_pin, LOW); digitalWrite(inverter_disconnect_contactor_pin, CONTACTOR_ON); @@ -134,7 +134,7 @@ void handle_precharge_control(unsigned long currentMillis) { break; case AUTO_PRECHARGE_COMPLETED: - if (datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != ACTIVE) { + if (datalayer.system.info.equipment_stop_active || datalayer.battery.status.bms_status != ACTIVE) { datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE; logging.printf("Precharge: equipment stop activated -> IDLE\n"); } @@ -142,8 +142,8 @@ void handle_precharge_control(unsigned long currentMillis) { case AUTO_PRECHARGE_OFF: if (!datalayer.system.status.battery_allows_contactor_closing || - !datalayer.system.status.inverter_allows_contactor_closing || - datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != FAULT) { + !datalayer.system.status.inverter_allows_contactor_closing || datalayer.system.info.equipment_stop_active || + datalayer.battery.status.bms_status != FAULT) { datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE; pinMode(hia4v1_pin, OUTPUT); digitalWrite(hia4v1_pin, LOW); diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 0dd39dc2d..a3ca841a1 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -288,6 +288,8 @@ struct DATALAYER_SYSTEM_INFO_TYPE { bool can_2518_send_fail = false; /** bool, determines if detailed performance measurement should be shown on webserver */ bool performance_measurement_active = false; + bool equipment_stop_active = false; //Has user enabled equipment stop? + bool start_precharging = false; //Is precharge ongoing? }; struct DATALAYER_SYSTEM_STATUS_TYPE { @@ -354,15 +356,9 @@ struct DATALAYER_SYSTEM_STATUS_TYPE { bool BMS_startup_in_progress = false; }; -struct DATALAYER_SYSTEM_SETTINGS_TYPE { - bool equipment_stop_active = false; - bool start_precharging = false; -}; - struct DATALAYER_SYSTEM_TYPE { DATALAYER_SYSTEM_INFO_TYPE info; DATALAYER_SYSTEM_STATUS_TYPE status; - DATALAYER_SYSTEM_SETTINGS_TYPE settings; }; class DataLayer { diff --git a/Software/src/devboard/safety/safety.cpp b/Software/src/devboard/safety/safety.cpp index a07d3fa98..b103f4d4f 100644 --- a/Software/src/devboard/safety/safety.cpp +++ b/Software/src/devboard/safety/safety.cpp @@ -337,15 +337,15 @@ void setBatteryPause(bool pause_battery, bool pause_CAN, bool equipment_stop, bo DEBUG_PRINTF("Battery pause begin %d %d %d %d\n", pause_battery, pause_CAN, equipment_stop, store_settings); // First handle equipment stop / resume - if (equipment_stop && !datalayer.system.settings.equipment_stop_active) { - datalayer.system.settings.equipment_stop_active = true; + if (equipment_stop && !datalayer.system.info.equipment_stop_active) { + datalayer.system.info.equipment_stop_active = true; if (store_settings) { store_settings_equipment_stop(); } set_event(EVENT_EQUIPMENT_STOP, 1); - } else if (!equipment_stop && datalayer.system.settings.equipment_stop_active) { - datalayer.system.settings.equipment_stop_active = false; + } else if (!equipment_stop && datalayer.system.info.equipment_stop_active) { + datalayer.system.info.equipment_stop_active = false; if (store_settings) { store_settings_equipment_stop(); } diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index b8547dde9..9eee1b856 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1094,7 +1094,7 @@ String processor(const String& var) { else content += formatPowerValue("Remaining capacity", datalayer.battery.status.remaining_capacity_Wh, "h", 1); - if (datalayer.system.settings.equipment_stop_active) { + if (datalayer.system.info.equipment_stop_active) { content += formatPowerValue("Max discharge power", datalayer.battery.status.max_discharge_power_W, "", 1, "red"); content += formatPowerValue("Max charge power", datalayer.battery.status.max_charge_power_W, "", 1, "red"); @@ -1262,7 +1262,7 @@ String processor(const String& var) { else content += formatPowerValue("Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1); - if (datalayer.system.settings.equipment_stop_active) { + if (datalayer.system.info.equipment_stop_active) { content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1, "red"); content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1, "red"); @@ -1448,7 +1448,7 @@ String processor(const String& var) { content += ""; if (webserver_auth) content += ""; - if (!datalayer.system.settings.equipment_stop_active) + if (!datalayer.system.info.equipment_stop_active) content += "