From 3b51872aa05e5f2928b4fa345b10be0a17d9a431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 21 Apr 2025 12:47:46 +0300 Subject: [PATCH] Add multiframe polling and module temperatures --- .../src/battery/GEELY-GEOMETRY-C-BATTERY.cpp | 172 +++++++++++++----- .../src/battery/GEELY-GEOMETRY-C-BATTERY.h | 6 + Software/src/datalayer/datalayer_extended.h | 3 + .../webserver/advanced_battery_html.cpp | 12 ++ 4 files changed, 143 insertions(+), 50 deletions(-) diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp index 4749d5dd..911b41fa 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp @@ -226,6 +226,8 @@ static uint16_t poll_cap_module_max = 0; static uint16_t poll_cap_module_min = 0; static uint16_t poll_unknown7 = 0; static uint16_t poll_unknown8 = 0; +static int16_t poll_temperature[6] = {0}; +#define TEMP_OFFSET 30 //TODO, not calibrated yet, best guess void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus datalayer.battery.status.soh_pptt; @@ -250,14 +252,27 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_charge_power_W; - datalayer.battery.status.temperature_min_dC = minimum_temperature * 10; - - datalayer.battery.status.temperature_max_dC = maximum_temperature * 10; - datalayer.battery.status.cell_min_voltage_mV = maximum_cell_voltage - 10; //TODO: Fix once we have min value datalayer.battery.status.cell_max_voltage_mV = maximum_cell_voltage; + // Initialize highest and lowest to the first element + maximum_temperature = poll_temperature[0]; + minimum_temperature = poll_temperature[0]; + + // Iterate through the array to find the highest and lowest values + for (uint8_t i = 1; i < 6; ++i) { + if (poll_temperature[i] > maximum_temperature) { + maximum_temperature = poll_temperature[i]; + } + if (poll_temperature[i] < minimum_temperature) { + minimum_temperature = poll_temperature[i]; + } + } + datalayer.battery.status.temperature_min_dC = minimum_temperature * 10; + + datalayer.battery.status.temperature_max_dC = maximum_temperature * 10; + if (HVIL_signal > 0) { set_event(EVENT_HVIL_FAILURE, HVIL_signal); } else { @@ -266,6 +281,7 @@ void update_values_battery() { //This function maps all the values fetched via //Update webserver more battery info page memcpy(datalayer_extended.geometryC.BatterySerialNumber, serialnumbers, sizeof(serialnumbers)); + memcpy(datalayer_extended.geometryC.ModuleTemperatures, poll_temperature, sizeof(poll_temperature)); datalayer_extended.geometryC.soc = poll_soc; datalayer_extended.geometryC.CC2voltage = poll_cc2_voltage; datalayer_extended.geometryC.cellMaxVoltageNumber = poll_cell_max_voltage_number; @@ -374,11 +390,9 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { break; case 0x351: //100ms (4A 31 71 B8 6E F8 84 00) datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - maximum_temperature = ((rx_frame.data.u8[6] / 2) - 40); //TODO, not confirmed break; case 0x352: //500ms (76 78 00 00 82 FF FF 00) datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - minimum_temperature = ((rx_frame.data.u8[4] / 2) - 40); //TODO, not confirmed break; case 0x353: //500ms (00 00 00 00 62 00 EA 5D) (car 00 00 00 00 00 00 E6 04) datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; @@ -453,56 +467,84 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { case 0x7EA: if (rx_frame.data.u8[0] == 0x10) { //Multiframe response, send ACK transmit_can_frame(&GEELY_ACK, can_config.battery); + //Multiframe has the poll reply slightly different location + incoming_poll = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]; } - incoming_poll = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + if (rx_frame.data.u8[0] < 0x10) { //One line response + incoming_poll = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; - switch (incoming_poll) { - case POLL_SOC: - poll_soc = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; - break; - case POLL_CC2_VOLTAGE: - poll_cc2_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; - break; - case POLL_CELL_MAX_VOLTAGE_NUMBER: - poll_cell_max_voltage_number = rx_frame.data.u8[4]; - break; - case POLL_CELL_MIN_VOLTAGE_NUMBER: - poll_cell_min_voltage_number = rx_frame.data.u8[4]; - break; - case POLL_AMOUNT_CELLS: - poll_amount_cells = rx_frame.data.u8[4]; - datalayer.battery.info.number_of_cells = poll_amount_cells; - break; - case POLL_SPECIFICIAL_VOLTAGE: - poll_specificial_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; - break; - case POLL_UNKNOWN_1: - poll_unknown1 = rx_frame.data.u8[4]; - break; - case POLL_RAW_SOC_MAX: - poll_raw_soc_max = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; - break; - case POLL_RAW_SOC_MIN: - poll_raw_soc_min = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; - break; - case POLL_UNKNOWN_4: - poll_unknown4 = rx_frame.data.u8[4]; - break; - case POLL_CAPACITY_MODULE_MAX: - poll_cap_module_max = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; - break; - case POLL_CAPACITY_MODULE_MIN: - poll_cap_module_min = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; - break; - case POLL_UNKNOWN_7: - poll_unknown7 = rx_frame.data.u8[4]; - break; - case POLL_UNKNOWN_8: - poll_unknown8 = rx_frame.data.u8[4]; + switch (incoming_poll) { + case POLL_SOC: + poll_soc = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + break; + case POLL_CC2_VOLTAGE: + poll_cc2_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + break; + case POLL_CELL_MAX_VOLTAGE_NUMBER: + poll_cell_max_voltage_number = rx_frame.data.u8[4]; + break; + case POLL_CELL_MIN_VOLTAGE_NUMBER: + poll_cell_min_voltage_number = rx_frame.data.u8[4]; + break; + case POLL_AMOUNT_CELLS: + poll_amount_cells = rx_frame.data.u8[4]; + datalayer.battery.info.number_of_cells = poll_amount_cells; + break; + case POLL_SPECIFICIAL_VOLTAGE: + poll_specificial_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + break; + case POLL_UNKNOWN_1: + poll_unknown1 = rx_frame.data.u8[4]; + break; + case POLL_RAW_SOC_MAX: + poll_raw_soc_max = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + break; + case POLL_RAW_SOC_MIN: + poll_raw_soc_min = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + break; + case POLL_UNKNOWN_4: + poll_unknown4 = rx_frame.data.u8[4]; + break; + case POLL_CAPACITY_MODULE_MAX: + poll_cap_module_max = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + break; + case POLL_CAPACITY_MODULE_MIN: + poll_cap_module_min = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + break; + case POLL_UNKNOWN_7: + poll_unknown7 = rx_frame.data.u8[4]; + break; + case POLL_UNKNOWN_8: + poll_unknown8 = rx_frame.data.u8[4]; + break; + default: + break; + } + } + + switch (incoming_poll) //Multiframe response + { + case POLL_MULTI_TEMPS: + switch (rx_frame.data.u8[0]) { + case 0x10: + poll_temperature[0] = (rx_frame.data.u8[5] - TEMP_OFFSET); + poll_temperature[1] = (rx_frame.data.u8[6] - TEMP_OFFSET); + poll_temperature[2] = (rx_frame.data.u8[7] - TEMP_OFFSET); + break; + case 0x21: + poll_temperature[3] = (rx_frame.data.u8[1] - TEMP_OFFSET); + poll_temperature[4] = (rx_frame.data.u8[2] - TEMP_OFFSET); + poll_temperature[5] = (rx_frame.data.u8[3] - TEMP_OFFSET); + break; + default: + break; + } break; default: + //Not a multiframe response, do nothing break; } + break; default: break; @@ -674,6 +716,36 @@ void transmit_can_battery() { case POLL_UNKNOWN_8: GEELY_POLL.data.u8[2] = (uint8_t)(POLL_UNKNOWN_8 >> 8); GEELY_POLL.data.u8[3] = (uint8_t)POLL_UNKNOWN_8; + poll_pid = POLL_MULTI_TEMPS; + break; + case POLL_MULTI_TEMPS: + GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_TEMPS >> 8); + GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_TEMPS; + poll_pid = POLL_MULTI_UNKNOWN_2; + break; + case POLL_MULTI_UNKNOWN_2: + GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_UNKNOWN_2 >> 8); + GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_UNKNOWN_2; + poll_pid = POLL_MULTI_UNKNOWN_3; + break; + case POLL_MULTI_UNKNOWN_3: + GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_UNKNOWN_3 >> 8); + GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_UNKNOWN_3; + poll_pid = POLL_MULTI_UNKNOWN_4; + break; + case POLL_MULTI_UNKNOWN_4: + GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_UNKNOWN_4 >> 8); + GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_UNKNOWN_4; + poll_pid = POLL_MULTI_UNKNOWN_5; + break; + case POLL_MULTI_UNKNOWN_5: + GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_UNKNOWN_5 >> 8); + GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_UNKNOWN_5; + poll_pid = POLL_MULTI_UNKNOWN_6; + break; + case POLL_MULTI_UNKNOWN_6: + GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_UNKNOWN_6 >> 8); + GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_UNKNOWN_6; poll_pid = POLL_SOC; break; default: diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h index a3439dc8..2596f517 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h @@ -25,6 +25,12 @@ #define POLL_CAPACITY_MODULE_MIN 0x4B3E #define POLL_UNKNOWN_7 0x4B24 //1 (the other battery 23) #define POLL_UNKNOWN_8 0x4B26 //16 (the other battery 33) +#define POLL_MULTI_TEMPS 0x4B0F +#define POLL_MULTI_UNKNOWN_2 0x4B10 +#define POLL_MULTI_UNKNOWN_3 0x4B53 +#define POLL_MULTI_UNKNOWN_4 0x4B54 +#define POLL_MULTI_UNKNOWN_5 0x4B6B +#define POLL_MULTI_UNKNOWN_6 0x4B6C void setup_battery(void); void transmit_can_frame(CAN_frame* tx_frame, int interface); diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 7244f35d..de5ceb9a 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -276,6 +276,9 @@ typedef struct { /** uint8_t */ /** Battery serial numbers, stores raw HEX values for ASCII chars */ 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; diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index e15c7615..75e3f434 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -476,6 +476,18 @@ String advanced_battery_processor(const String& var) { content += "

Capacity module min: " + String((datalayer_extended.geometryC.capModMin / 10)) + "Ah

"; content += "

Unknown7: " + String(datalayer_extended.geometryC.unknown7) + "

"; content += "

Unknown8: " + String(datalayer_extended.geometryC.unknown8) + "

"; + content += + "

Module 1 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[0]) + " °C

"; + content += + "

Module 2 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[1]) + " °C

"; + content += + "

Module 3 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[2]) + " °C

"; + content += + "

Module 4 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[3]) + " °C

"; + content += + "

Module 5 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[4]) + " °C

"; + content += + "

Module 6 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[5]) + " °C

"; #endif //GEELY_GEOMETRY_C_BATTERY #ifdef KIA_HYUNDAI_64_BATTERY