From 09e366df958d89de68bfebb387ab898cc7ebeba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 16 Sep 2024 22:02:27 +0300 Subject: [PATCH 01/81] Add SOC candidates to USB printout --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 22 ++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index d36e41664..7dab03cc7 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -26,6 +26,8 @@ static int16_t highest_temperature = 0; static int16_t calc_min_temperature = 0; static int16_t calc_max_temperature = 0; +static uint16_t highprecision_SOC = 0; +static uint16_t lowprecision_SOC = 0; static uint16_t BMS_SOC = 0; static uint16_t BMS_voltage = 0; static int16_t BMS_current = 0; @@ -129,6 +131,14 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.temperature_min_dC = calc_min_temperature * 10; // Add decimals datalayer.battery.status.temperature_max_dC = calc_max_temperature * 10; + //TODO: Remove once confirmed which work + Serial.print("Polled: "); + Serial.println(BMS_SOC); + Serial.print("Highprec: "); + Serial.println(highprecision_SOC); + Serial.print("Lowprec: "); + Serial.println(lowprecision_SOC); + #ifdef DEBUG_VIA_USB #endif @@ -209,16 +219,18 @@ void receive_can_battery(CAN_frame rx_frame) { case 0x444: //9E,01,88,13,64,64,98,65 //9A,01,B6,13,64,64,98,3B //407.5V 18deg //9B,01,B8,13,64,64,98,38 //408.5V 14deg + //lowprecision_SOC = ??? break; case 0x445: //00,98,FF,FF,63,20,4E,98 - Static, values never changes between logs break; case 0x446: //2C,D4,0C,4D,21,DC,0C,9D - 0,1,7th frame varies a lot break; - case 0x447: // Seems to contain more temperatures, highest and lowest? - //06,38,01,3B,E0,03,39,69 - //06,36,02,36,E0,03,36,72, - lowest_temperature = (rx_frame.data.u8[1] - 40); //Best guess for now - highest_temperature = (rx_frame.data.u8[3] - 40); //Best guess for now + case 0x447: // Seems to contain more temperatures, highest and lowest? + //06,38,01,3B,E0,03,39,69 + //06,36,02,36,E0,03,36,72, + highprecision_SOC = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; // 03 E0 = 992 = 99.2% + lowest_temperature = (rx_frame.data.u8[1] - 40); //Best guess for now + highest_temperature = (rx_frame.data.u8[3] - 40); //Best guess for now break; case 0x47B: //01,FF,FF,FF,FF,FF,FF,FF - Static, values never changes between logs break; From 198e9ff945f6829fd35c23a859e2c50a329e739e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 17 Sep 2024 12:00:04 +0300 Subject: [PATCH 02/81] Add way to not use estimated SOC --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 18 +++++++----------- Software/src/battery/BYD-ATTO-3-BATTERY.h | 2 ++ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 7dab03cc7..a753d2618 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -93,10 +93,14 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.voltage_dV = BMS_voltage * 10; } - //datalayer.battery.status.real_soc = BMS_SOC * 100; //TODO: This is not yet found! - // We instead estimate the SOC% based on the battery voltage - // This is a very bad solution, and as soon as an usable SOC% value has been found on CAN, we should switch to that! +#ifdef USE_ESTIMATED_SOC + // When the battery is crashed hard, it locks itself and SOC becomes unavailable. + // We instead estimate the SOC% based on the battery voltage. + // This is a bad solution, you wont be able to use 100% of the battery datalayer.battery.status.real_soc = estimateSOC(datalayer.battery.status.voltage_dV); +#else // Pack is not crashed, we can use periodically transmitted SOC + datalayer.battery.status.real_soc = highprecision_SOC * 10; +#endif datalayer.battery.status.current_dA = -BMS_current; @@ -131,14 +135,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.temperature_min_dC = calc_min_temperature * 10; // Add decimals datalayer.battery.status.temperature_max_dC = calc_max_temperature * 10; - //TODO: Remove once confirmed which work - Serial.print("Polled: "); - Serial.println(BMS_SOC); - Serial.print("Highprec: "); - Serial.println(highprecision_SOC); - Serial.print("Lowprec: "); - Serial.println(lowprecision_SOC); - #ifdef DEBUG_VIA_USB #endif diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.h b/Software/src/battery/BYD-ATTO-3-BATTERY.h index 550975d5e..5f4a7d710 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.h +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.h @@ -5,6 +5,8 @@ #define BATTERY_SELECTED #define MAX_CELL_DEVIATION_MV 150 +#define USE_ESTIMATED_SOC // If enabled, SOC is estimated from pack voltage. Useful for locked packs. \ + // Uncomment this if you know your BMS is unlocked and able to send SOC% void setup_battery(void); void transmit_can(CAN_frame* tx_frame, int interface); From 4c9e1a3169e3002818b4d2b9ee7b5dd47a4826f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 25 Oct 2024 21:56:52 +0300 Subject: [PATCH 03/81] Add iX skeleton --- Software/USER_SETTINGS.h | 1 + Software/src/battery/BATTERIES.h | 4 + Software/src/battery/BMW-IX-BATTERY.cpp | 193 ++++++++++++++++++ Software/src/battery/BMW-IX-BATTERY.h | 17 ++ Software/src/devboard/webserver/webserver.cpp | 3 + 5 files changed, 218 insertions(+) create mode 100644 Software/src/battery/BMW-IX-BATTERY.cpp create mode 100644 Software/src/battery/BMW-IX-BATTERY.h diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index c5cbd65f1..9c127e624 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -10,6 +10,7 @@ /* Select battery used */ //#define BMW_I3_BATTERY +//#define BMW_IX_BATTERY //#define BYD_ATTO_3_BATTERY //#define CHADEMO_BATTERY //NOTE: inherently enables CONTACTOR_CONTROL below //#define IMIEV_CZERO_ION_BATTERY diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 8539c5514..d0af768f9 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -6,6 +6,10 @@ #include "BMW-I3-BATTERY.h" #endif +#ifdef BMW_IX_BATTERY +#include "BMW-IX-BATTERY.h" +#endif + #ifdef BYD_ATTO_3_BATTERY #include "BYD-ATTO-3-BATTERY.h" #endif diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp new file mode 100644 index 000000000..78559bce3 --- /dev/null +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -0,0 +1,193 @@ +#include "../include.h" +#ifdef BMW_IX_BATTERY +#include "../datalayer/datalayer.h" +#include "../datalayer/datalayer_extended.h" +#include "../devboard/utils/events.h" +#include "BMW-IX-BATTERY.h" + +/* Do not change code below unless you are sure what you are doing */ +static unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send +static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send +static unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was send +static unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send +static unsigned long previousMillis640 = 0; // will store last time a 600ms CAN Message was send +static unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was send +static unsigned long previousMillis5000 = 0; // will store last time a 5000ms CAN Message was send +static unsigned long previousMillis10000 = 0; // will store last time a 10000ms CAN Message was send + +#define ALIVE_MAX_VALUE 14 // BMW CAN messages contain alive counter, goes from 0...14 + +enum CmdState { SOH, CELL_VOLTAGE_MINMAX, SOC, CELL_VOLTAGE_CELLNO, CELL_VOLTAGE_CELLNO_LAST }; + +static CmdState cmdState = SOC; + +CAN_frame BMW_6F1_CELL = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x6F1, .data = {0x07, 0x03, 0x22, 0xDD, 0xBF}}; +CAN_frame BMW_6F1_SOH = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x6F1, .data = {0x07, 0x03, 0x22, 0x63, 0x35}}; +CAN_frame BMW_6F1_SOC = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x6F1, .data = {0x07, 0x03, 0x22, 0xDD, 0xBC}}; +CAN_frame BMW_6F1_CELL_VOLTAGE_AVG = {.FD = false, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F1, + .data = {0x07, 0x03, 0x22, 0xDF, 0xA0}}; +CAN_frame BMW_6F1_CONTINUE = {.FD = false, .ext_ID = false, .DLC = 4, .ID = 0x6F1, .data = {0x07, 0x30, 0x00, 0x02}}; +CAN_frame BMW_6F4_CELL_VOLTAGE_CELLNO = {.FD = false, + .ext_ID = false, + .DLC = 7, + .ID = 0x6F4, + .data = {0x07, 0x05, 0x31, 0x01, 0xAD, 0x6E, 0x01}}; +CAN_frame BMW_6F4_CELL_CONTINUE = {.FD = false, + .ext_ID = false, + .DLC = 6, + .ID = 0x6F4, + .data = {0x07, 0x04, 0x31, 0x03, 0xAD, 0x6E}}; + +static bool battery_awake = false; + +static uint8_t current_cell_polled = 0; + +static uint8_t increment_alive_counter(uint8_t counter) { + counter++; + if (counter > ALIVE_MAX_VALUE) { + counter = 0; + } + return counter; +} + +void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer + + datalayer.battery.status.real_soc; + + 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.soh_pptt; + + datalayer.battery.status.max_discharge_power_W; + + datalayer.battery.status.max_charge_power_W; + + datalayer.battery.status.active_power_W; + + datalayer.battery.status.temperature_min_dC; + + datalayer.battery.status.temperature_max_dC; +} + +void receive_can_battery(CAN_frame rx_frame) { + battery_awake = true; + switch (rx_frame.ID) { + case 0x112: + break; + default: + break; + } +} + +void send_can_battery() { + unsigned long currentMillis = millis(); + + if (battery_awake) { + //Send 20ms message + if (currentMillis - previousMillis20 >= INTERVAL_20_MS) { + // Check if sending of CAN messages has been delayed too much. + if ((currentMillis - previousMillis20 >= INTERVAL_20_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) { + set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis20)); + } else { + clear_event(EVENT_CAN_OVERRUN); + } + previousMillis20 = currentMillis; + } + // Send 100ms CAN Message + if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { + previousMillis100 = currentMillis; + } + // Send 200ms CAN Message + if (currentMillis - previousMillis200 >= INTERVAL_200_MS) { + previousMillis200 = currentMillis; + } + // Send 500ms CAN Message + if (currentMillis - previousMillis500 >= INTERVAL_500_MS) { + previousMillis500 = currentMillis; + } + // Send 640ms CAN Message + if (currentMillis - previousMillis640 >= INTERVAL_640_MS) { + previousMillis640 = currentMillis; + } + // Send 1000ms CAN Message + if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { + previousMillis1000 = currentMillis; + + switch (cmdState) { + case SOC: + transmit_can(&BMW_6F1_CELL, can_config.battery); + cmdState = CELL_VOLTAGE_MINMAX; + break; + case CELL_VOLTAGE_MINMAX: + transmit_can(&BMW_6F1_SOH, can_config.battery); + cmdState = SOH; + break; + case SOH: + transmit_can(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery); + cmdState = CELL_VOLTAGE_CELLNO; + current_cell_polled = 0; + + break; + case CELL_VOLTAGE_CELLNO: + current_cell_polled++; + if (current_cell_polled > 96) { + datalayer.battery.info.number_of_cells = 97; + cmdState = CELL_VOLTAGE_CELLNO_LAST; + } else { + cmdState = CELL_VOLTAGE_CELLNO; + + BMW_6F4_CELL_VOLTAGE_CELLNO.data.u8[6] = current_cell_polled; + transmit_can(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery); + } + break; + case CELL_VOLTAGE_CELLNO_LAST: + transmit_can(&BMW_6F1_SOC, can_config.battery); + cmdState = SOC; + break; + } + } + // Send 5000ms CAN Message + if (currentMillis - previousMillis5000 >= INTERVAL_5_S) { + previousMillis5000 = currentMillis; + } + // Send 10000ms CAN Message + if (currentMillis - previousMillis10000 >= INTERVAL_10_S) { + previousMillis10000 = currentMillis; + } + } else { + previousMillis20 = currentMillis; + previousMillis100 = currentMillis; + previousMillis200 = currentMillis; + previousMillis500 = currentMillis; + previousMillis640 = currentMillis; + previousMillis1000 = currentMillis; + previousMillis5000 = currentMillis; + previousMillis10000 = currentMillis; + } +} + +void setup_battery(void) { // Performs one time setup at startup +#ifdef DEBUG_VIA_USB + Serial.println("BMW iX battery selected"); +#endif //DEBUG_VIA_USB + + //Before we have started up and detected which battery is in use, use 60AH values + datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; + datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; + datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; + datalayer.system.status.battery_allows_contactor_closing = true; + + pinMode(WUP_PIN, OUTPUT); + digitalWrite(WUP_PIN, HIGH); // Wake up the battery +} + +#endif diff --git a/Software/src/battery/BMW-IX-BATTERY.h b/Software/src/battery/BMW-IX-BATTERY.h new file mode 100644 index 000000000..8c2e24c9f --- /dev/null +++ b/Software/src/battery/BMW-IX-BATTERY.h @@ -0,0 +1,17 @@ +#ifndef BMW_IX_BATTERY_H +#define BMW_IX_BATTERY_H +#include +#include "../include.h" + +#define BATTERY_SELECTED + +#define WUP_PIN 25 +#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V +#define MIN_PACK_VOLTAGE_DV 3000 +#define MAX_CELL_DEVIATION_MV 500 +#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value +#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value +void setup_battery(void); +void transmit_can(CAN_frame* tx_frame, int interface); + +#endif diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index f1b3230ad..ee321d9cd 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -513,6 +513,9 @@ String processor(const String& var) { #ifdef BMW_I3_BATTERY content += "BMW i3"; #endif // BMW_I3_BATTERY +#ifdef BMW_IX_BATTERY + content += "BMW iX and i4-7 platform"; +#endif // BMW_IX_BATTERY #ifdef BYD_ATTO_3_BATTERY content += "BYD Atto 3"; #endif // BYD_ATTO_3_BATTERY From 6b823e2c5b2fe40a41634241d3426c00364500f3 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sat, 26 Oct 2024 22:55:00 +0100 Subject: [PATCH 04/81] Update BMW-IX-BATTERY.cpp First functional version - SME wake line can be held high to use. Notes: - Bat capacity max/remain is temp scaled by 0.1 so fits in uint16 - No max charge/discharge power - No SOH - No contactor control --- Software/src/battery/BMW-IX-BATTERY.cpp | 381 ++++++++++++++++++++++-- 1 file changed, 358 insertions(+), 23 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 78559bce3..c13d0eb81 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -21,21 +21,149 @@ enum CmdState { SOH, CELL_VOLTAGE_MINMAX, SOC, CELL_VOLTAGE_CELLNO, CELL_VOLTAGE static CmdState cmdState = SOC; -CAN_frame BMW_6F1_CELL = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x6F1, .data = {0x07, 0x03, 0x22, 0xDD, 0xBF}}; -CAN_frame BMW_6F1_SOH = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x6F1, .data = {0x07, 0x03, 0x22, 0x63, 0x35}}; -CAN_frame BMW_6F1_SOC = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x6F1, .data = {0x07, 0x03, 0x22, 0xDD, 0xBC}}; -CAN_frame BMW_6F1_CELL_VOLTAGE_AVG = {.FD = false, +/* +Suspected Vehicle comms required: + + 0x06D DLC? 1000ms - counters? + 0x2F1 DLC? 1000ms during run : 0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0x00, 0xF3, 0xFF - at startup 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xF3, 0xFF. Suspect byte [4] is a counter + 0x439 DLC4 1000ms STATIC + 0x0C0 DLC2 200ms needs counter + 0x510 DLC8 100ms STATIC 40 10 40 00 6F DF 19 00 during run - Startup sends this once: 0x40 0x10 0x02 0x00 0x00 0x00 0x00 0x00 + 0x587 DLC8 appears at startup 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF , 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF, 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF, 0x06 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x82 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF + +No vehicle log available, SME asks for: + 0x125 (CCU) + 0x16 (CCU) + 0x91 (EME1) + 0xAA (EME2) + +SME Output: + + 0x08F DLC48 10ms - Appears to have analog readings like volt/temp/current + 0x1D2 DLC8 1000ms + 0x20B DLC8 1000ms + 0x2E2 DLC16 1000ms + 0x2F1 DLC8 1000ms + 0x31F DLC16 100ms - 2 downward counters? + 0x453 DLC20 200ms + 0x486 DLC48 1000ms + 0x49C DLC8 1000ms + 0x4A1 DLC8 1000ms + 0x4BB DLC64 200ms - seems multplexed on [0] + 0x4D0 DLC64 1000ms - some slow/flickering values + 0x607 UDS Response + + + +UDS Map: + 69 - service disconnect (1 = closed) + c7 - available energy - available energy charged + ce - min avg max SOC + 61 len12 = current sensor + 53 - min and max cell voltage + 4d- main battery voltage + 4a - after contactor voltage + a4 = charnging contactor temp + A3 - MAIN CONTACTOR VOLTAGE + f4 00 0a 62 DD = main battery temp + A7 - T30 12v voltage (SME input voltage) + B6 - T30C 12v voltage (12v pyro sensor) + 51 = release, switch contactors. 1 = control of switch contactors active + CD = charge contactors + + TX 07 03 22 E5 54 - UDS request cell voltages + RX F4 10 E3 62 E5 54 - 16bit cell voltages batch1 (29 cells) + TX 07 30 00 02 - UDS request continue data + RX F4 21 0F - 16bit cell voltages continue2 (31 cells) + RX F4 22 0F - 16bit cell voltages continue3 (31 cells) + RX F4 23 0F - 16bit cell voltages continue4 (17 cells) + + TX 07 03 22 E5 9A - UDS request cell SOC + RX F4 10 E3 62 E5 9A?? - 16bit cell SOC batch1 (29 cells) + + TX 07 03 22 E5 CA - UDS request cell temps + RX F4 10 81 62 E5 CA - 16bit cell temps batch1 + RX F4 21 81 62 E5 CA - 16bit cell temps continue2 + + TX 07 30 00 02 - UDS request continue data + + +*/ + + + +CAN_frame BMWiX_06D = {.FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x06D, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF}}; // 1000ms BDC Output - [0] static [1]-[4] counter x2? is needed? [5-7] static + + +CAN_frame BMWiX_2F1 = {.FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x2F1, + .data = {0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0x00, 0xF3, 0xFF}}; // 1000ms BDC Output - Static values - varies at startup + + + +CAN_frame BMWiX_439 = {.FD = true, + .ext_ID = false, + .DLC = 4, + .ID = 0x439, + .data = {0xFF, 0xBF, 0xFF, 0xFF}}; // 1000ms BDC Output - Static values + + + +CAN_frame BMWiX_510 = {.FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x510, + .data = {0x40, 0x10, 0x40, 0x00, 0x6F, 0xDF, 0x19, 0x00}}; // 100ms BDC Output - Static values + + + +CAN_frame BMWiX_6F4 = {.FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0xC7}}; // UDS Request data from SME. byte 4 selects requested value + + +CAN_frame BMWiX_0C0 = {.FD = true, + .ext_ID = false, + .DLC = 2, + .ID = 0x0C0, + .data = {0xF0, 0x08}}; // Keep Alive 2 BDC>SME 200ms First byte cycles F0 > FE second byte 08 static + + +CAN_frame BMWiX_6F4_CELL_VOLTAGE = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x54}}; +CAN_frame BMWiX_6F4_CELL_SOC = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x9A}}; +CAN_frame BMWiX_6F4_CELL_TEMP = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0xCA}}; + +CAN_frame BMWiX_6F4_CONTINUE_DATA = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x6F4, .data = {0x07, 0x30, 0x00, 0x02}}; + +CAN_frame BMW_6F4_CELL = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xDD, 0xBF}};// gives error F4 03 7F 22 31 +CAN_frame BMW_6F4_SOH = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0x63, 0x35}}; // gives error F4 03 7F 22 31 +CAN_frame BMW_6F4_SOC = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xDD, 0xBC}}; // gives blanks + +CAN_frame BMW_10B = {.FD = true, + .ext_ID = false, + .DLC = 3, + .ID = 0x10B, + .data = {0xCD, 0x00, 0xFC}}; // Contactor closing command + +CAN_frame BMW_6F4_CELL_VOLTAGE_AVG = {.FD = true, .ext_ID = false, .DLC = 5, - .ID = 0x6F1, + .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xDF, 0xA0}}; -CAN_frame BMW_6F1_CONTINUE = {.FD = false, .ext_ID = false, .DLC = 4, .ID = 0x6F1, .data = {0x07, 0x30, 0x00, 0x02}}; CAN_frame BMW_6F4_CELL_VOLTAGE_CELLNO = {.FD = false, .ext_ID = false, .DLC = 7, .ID = 0x6F4, - .data = {0x07, 0x05, 0x31, 0x01, 0xAD, 0x6E, 0x01}}; -CAN_frame BMW_6F4_CELL_CONTINUE = {.FD = false, + .data = {0x07, 0x05, 0x31, 0x01, 0xAD, 0x6E, 0x01}}; // gives error +CAN_frame BMW_6F4_CELL_CONTINUE = {.FD = true, .ext_ID = false, .DLC = 6, .ID = 0x6F4, @@ -43,8 +171,37 @@ CAN_frame BMW_6F4_CELL_CONTINUE = {.FD = false, static bool battery_awake = false; +//iX Intermediate vars +static int32_t battery_current = 0; +static int16_t battery_voltage = 0; +static int16_t battery_voltage_after_contactor = 0; +static int16_t min_soc_state = 0; +static int16_t avg_soc_state = 0; +static int16_t max_soc_state = 0; +static int16_t remaining_capacity = 0; +static int16_t max_capacity = 0; +static int16_t min_battery_temperature = 0; +static int16_t avg_battery_temperature = 0; +static int16_t max_battery_temperature = 0; +static int16_t main_contactor_temperature = 0; +static int16_t min_cell_voltage = 0; +static int16_t max_cell_voltage = 0; +static int16_t battery_power = 0; +static uint8_t uds_req_id_counter = 0; +static byte iX_0C0_counter = 0xF0; // Initialize to 0xF0 + +//End iX Intermediate vars + static uint8_t current_cell_polled = 0; +static uint8_t increment_uds_req_id_counter(uint8_t counter) { + counter++; + if (counter > 7) { + counter = 0; + } + return counter; +} + static uint8_t increment_alive_counter(uint8_t counter) { counter++; if (counter > ALIVE_MAX_VALUE) { @@ -53,29 +210,48 @@ static uint8_t increment_alive_counter(uint8_t counter) { return counter; } +static byte increment_0C0_counter(byte counter) { + counter++; + // Reset to 0xF0 if it exceeds 0xFE + if (counter > 0xFE) { + counter = 0xF0; + } + return counter; +} + void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer - datalayer.battery.status.real_soc; + datalayer.battery.status.real_soc = avg_soc_state; + + datalayer.battery.status.voltage_dV = battery_voltage; + + datalayer.battery.status.current_dA = battery_current; + + datalayer.battery.info.total_capacity_Wh = max_capacity; - datalayer.battery.status.voltage_dV; + datalayer.battery.status.remaining_capacity_Wh = remaining_capacity; - datalayer.battery.status.current_dA; + datalayer.battery.status.soh_pptt = 9900; - datalayer.battery.info.total_capacity_Wh; + datalayer.battery.status.max_discharge_power_W = 6000; - datalayer.battery.status.remaining_capacity_Wh; + datalayer.battery.status.max_charge_power_W = 6000; - datalayer.battery.status.soh_pptt; + battery_power = (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100)); - datalayer.battery.status.max_discharge_power_W; + datalayer.battery.status.active_power_W = battery_power; - datalayer.battery.status.max_charge_power_W; + datalayer.battery.status.temperature_min_dC = min_battery_temperature; - datalayer.battery.status.active_power_W; + datalayer.battery.status.temperature_max_dC = max_battery_temperature; - datalayer.battery.status.temperature_min_dC; + datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; - datalayer.battery.status.temperature_max_dC; + datalayer.battery.status.cell_max_voltage_mV = max_cell_voltage; + + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + + datalayer.battery.info.number_of_cells = 108; //Hardcoded for the SE26 battery until values found } void receive_can_battery(CAN_frame rx_frame) { @@ -83,6 +259,101 @@ void receive_can_battery(CAN_frame rx_frame) { switch (rx_frame.ID) { case 0x112: break; + case 0x607: //SME responds to UDS requests on 0x607 + + 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 + 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]; + datalayer.battery.status.cell_voltages_mV[voltage_index++] = voltage; + } + } + + //Frame has continued data - so request it + transmit_can(&BMWiX_6F4_CONTINUE_DATA, can_config.battery); + } + + 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]; + datalayer.battery.status.cell_voltages_mV[voltage_index++] = voltage; + } + } + + 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]; + datalayer.battery.status.cell_voltages_mV[voltage_index++] = voltage; + } + } + + 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 = 17; // 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]; + datalayer.battery.status.cell_voltages_mV[voltage_index++] = voltage; + } + } + 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) + battery_voltage_after_contactor = (rx_frame.data.u8[5] <<8 | rx_frame.data.u8[6]); + } + + + 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[5] << 24) | + (rx_frame.data.u8[6] << 16) | + (rx_frame.data.u8[7] << 8) | + rx_frame.data.u8[8]); + } + + 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 + remaining_capacity = ((rx_frame.data.u8[6] <<8 | rx_frame.data.u8[7]) *1) -5000; + max_capacity = ((rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9]) *1) -5000; //uint16 limits to 65 + #ifdef DEBUG_VIA_USB + Serial.print("Remaining Capacty: "); + Serial.println( remaining_capacity); + Serial.print("Max Capacty: "); + Serial.println(max_capacity); + #endif //DEBUG_VIA_USB + } + + if (rx_frame.DLC = 12 && rx_frame.data.u8[4] == 0xE5 && rx_frame.data.u8[5] == 0x53) { //Min and max cell voltage + min_cell_voltage = (rx_frame.data.u8[6] <<8 | rx_frame.data.u8[7]); + max_cell_voltage = (rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9]); + } + + 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 + main_contactor_temperature = (rx_frame.data.u8[5] <<8 | rx_frame.data.u8[6]); + + } + break; default: break; } @@ -105,10 +376,58 @@ void send_can_battery() { // Send 100ms CAN Message if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { previousMillis100 = currentMillis; + + uds_req_id_counter = increment_uds_req_id_counter(uds_req_id_counter); + switch (uds_req_id_counter){ + case 0: + //BMWiX_6F4.data.u8[3] = 0x22; + BMWiX_6F4.data.u8[3] = 0xDD; + BMWiX_6F4.data.u8[4] = 0xC0; + transmit_can(&BMWiX_6F4, can_config.battery); + break; + case 1: + BMWiX_6F4.data.u8[3] = 0xE5; + BMWiX_6F4.data.u8[4] = 0xCE; + transmit_can(&BMWiX_6F4, can_config.battery); + break; + case 2: + BMWiX_6F4.data.u8[4] = 0xC7; + transmit_can(&BMWiX_6F4, can_config.battery); + break; + case 3: + BMWiX_6F4.data.u8[4] = 0x53; + transmit_can(&BMWiX_6F4, can_config.battery); + break; + case 4: + BMWiX_6F4.data.u8[4] = 0x4A; + transmit_can(&BMWiX_6F4, can_config.battery); + break; + case 5: + BMWiX_6F4.data.u8[4] = 0x4D; + transmit_can(&BMWiX_6F4, can_config.battery); + break; + case 6: + BMWiX_6F4.data.u8[4] = 0x61; + transmit_can(&BMWiX_6F4, can_config.battery); + break; + case 7: + BMWiX_6F4.data.u8[4] = 0x54; + transmit_can(&BMWiX_6F4, can_config.battery); + break; + } + + + //Send SME Keep alive values 100ms + transmit_can(&BMWiX_510, can_config.battery); + } // Send 200ms CAN Message if (currentMillis - previousMillis200 >= INTERVAL_200_MS) { previousMillis200 = currentMillis; + + //Send SME Keep alive values 200ms + BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 + transmit_can(&BMWiX_0C0, can_config.battery); } // Send 500ms CAN Message if (currentMillis - previousMillis500 >= INTERVAL_500_MS) { @@ -122,17 +441,22 @@ void send_can_battery() { if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { previousMillis1000 = currentMillis; + //Send SME Keep alive values 1000ms + transmit_can(&BMWiX_06D, can_config.battery); + transmit_can(&BMWiX_2F1, can_config.battery); + transmit_can(&BMWiX_439, can_config.battery); + switch (cmdState) { case SOC: - transmit_can(&BMW_6F1_CELL, can_config.battery); + transmit_can(&BMW_6F4_CELL, can_config.battery); cmdState = CELL_VOLTAGE_MINMAX; break; case CELL_VOLTAGE_MINMAX: - transmit_can(&BMW_6F1_SOH, can_config.battery); + transmit_can(&BMW_6F4_SOH, can_config.battery); cmdState = SOH; break; case SOH: - transmit_can(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery); + transmit_can(&BMW_6F4_CELL_VOLTAGE_AVG, can_config.battery); cmdState = CELL_VOLTAGE_CELLNO; current_cell_polled = 0; @@ -150,7 +474,7 @@ void send_can_battery() { } break; case CELL_VOLTAGE_CELLNO_LAST: - transmit_can(&BMW_6F1_SOC, can_config.battery); + transmit_can(&BMW_6F4_SOC, can_config.battery); cmdState = SOC; break; } @@ -188,6 +512,17 @@ void setup_battery(void) { // Performs one time setup at startup pinMode(WUP_PIN, OUTPUT); digitalWrite(WUP_PIN, HIGH); // Wake up the battery + + //Wake Battery + //Send SME Keep alive values 100ms + transmit_can(&BMWiX_510, can_config.battery); + //Send SME Keep alive values 200ms + BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 + transmit_can(&BMWiX_0C0, can_config.battery); + //Send SME Keep alive values 1000ms + transmit_can(&BMWiX_06D, can_config.battery); + transmit_can(&BMWiX_2F1, can_config.battery); + transmit_can(&BMWiX_439, can_config.battery); } #endif From 61616628551a0d5ef6af3c5afcf54ad78b8575ea Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sun, 27 Oct 2024 11:31:22 +0000 Subject: [PATCH 05/81] Update BMW-IX-BATTERY.cpp Fix max/available capacity - now shows correct values. --- Software/src/battery/BMW-IX-BATTERY.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index c13d0eb81..ae17575e7 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -36,6 +36,7 @@ No vehicle log available, SME asks for: 0x16 (CCU) 0x91 (EME1) 0xAA (EME2) + 0x?? Suspect there is a drive mode flag somewhere - balancing might only be active in some modes SME Output: @@ -178,8 +179,8 @@ static int16_t battery_voltage_after_contactor = 0; static int16_t min_soc_state = 0; static int16_t avg_soc_state = 0; static int16_t max_soc_state = 0; -static int16_t remaining_capacity = 0; -static int16_t max_capacity = 0; +static int32_t remaining_capacity = 0; +static int32_t max_capacity = 0; static int16_t min_battery_temperature = 0; static int16_t avg_battery_temperature = 0; static int16_t max_battery_temperature = 0; @@ -251,7 +252,7 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - datalayer.battery.info.number_of_cells = 108; //Hardcoded for the SE26 battery until values found + datalayer.battery.info.number_of_cells = 108; //Hardcoded for the 108S SE27 battery. SE26 96S TODO } void receive_can_battery(CAN_frame rx_frame) { @@ -329,8 +330,8 @@ void receive_can_battery(CAN_frame rx_frame) { } 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]) *1) -5000; - max_capacity = ((rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9]) *1) -5000; //uint16 limits to 65 + 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; #ifdef DEBUG_VIA_USB Serial.print("Remaining Capacty: "); Serial.println( remaining_capacity); From f3f10b1db13577df1f4808cc66f218853f2b69d9 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sun, 27 Oct 2024 11:36:15 +0000 Subject: [PATCH 06/81] Removed some debugging (max/remain capacity fixed) --- Software/src/battery/BMW-IX-BATTERY.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index ae17575e7..c7d59f41f 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -332,12 +332,6 @@ void receive_can_battery(CAN_frame rx_frame) { 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; - #ifdef DEBUG_VIA_USB - Serial.print("Remaining Capacty: "); - Serial.println( remaining_capacity); - Serial.print("Max Capacty: "); - Serial.println(max_capacity); - #endif //DEBUG_VIA_USB } if (rx_frame.DLC = 12 && rx_frame.data.u8[4] == 0xE5 && rx_frame.data.u8[5] == 0x53) { //Min and max cell voltage From adfa9f13e7ba14474c8705678b2ad3ac277f3f43 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sun, 27 Oct 2024 12:29:19 +0000 Subject: [PATCH 07/81] iX Advanced Info support addition --- Software/src/devboard/webserver/advanced_battery_html.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index e8001d0c3..99ff2201b 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -16,6 +16,11 @@ String advanced_battery_processor(const String& var) { // Start a new block with a specific background color content += "
"; + +#ifdef BMW_IX_BATTERY + content += "

T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + "mV

"; +#endif //BMW_IX_BATTERY + #ifdef BMW_I3_BATTERY content += "

SOC raw: " + String(datalayer_extended.bmwi3.SOC_raw) + "

"; content += "

SOC dash: " + String(datalayer_extended.bmwi3.SOC_dash) + "

"; @@ -152,7 +157,7 @@ String advanced_battery_processor(const String& var) { #endif #if !defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && \ - !defined(BMW_I3_BATTERY) //Only the listed types have extra info + !defined(BMW_I3_BATTERY)&& !defined(BMW_IX_BATTERY) //Only the listed types have extra info content += "No extra information available for this battery type"; #endif From 4c15d35144b73393d4be5f05486916843795f0fe Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sun, 27 Oct 2024 12:29:53 +0000 Subject: [PATCH 08/81] Add iX advanced values support --- Software/src/datalayer/datalayer_extended.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 96bd5e075..aecbed703 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -3,6 +3,15 @@ #include "../include.h" +typedef struct { + /** 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; +} DATALAYER_INFO_BMWIX; + + typedef struct { /** uint16_t */ /** SOC% raw battery value. Might not always reach 100% */ @@ -118,6 +127,7 @@ typedef struct { class DataLayerExtended { public: + DATALAYER_INFO_BMWIX bmwix; DATALAYER_INFO_BMWI3 bmwi3; DATALAYER_INFO_TESLA tesla; DATALAYER_INFO_NISSAN_LEAF nissanleaf; From 8fbc4d829ea4f790c847e2d4d8681feee40ac1be Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sun, 27 Oct 2024 12:32:44 +0000 Subject: [PATCH 09/81] IX Advanced values support - Battery module Adds support for showing advanced values - T30 voltage (12v SME Supply) --- Software/src/battery/BMW-IX-BATTERY.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index c7d59f41f..0b0b5a69c 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -33,7 +33,10 @@ Suspected Vehicle comms required: No vehicle log available, SME asks for: 0x125 (CCU) - 0x16 (CCU) + 0x16E (CCU) + 0x340 (CCU) + 0x4F8 (CCU) + 0x188 (CCU) 0x91 (EME1) 0xAA (EME2) 0x?? Suspect there is a drive mode flag somewhere - balancing might only be active in some modes @@ -175,6 +178,7 @@ static bool battery_awake = false; //iX Intermediate vars static int32_t battery_current = 0; static int16_t battery_voltage = 0; +static int16_t terminal30_12v_voltage = 0; static int16_t battery_voltage_after_contactor = 0; static int16_t min_soc_state = 0; static int16_t avg_soc_state = 0; @@ -197,7 +201,7 @@ static uint8_t current_cell_polled = 0; static uint8_t increment_uds_req_id_counter(uint8_t counter) { counter++; - if (counter > 7) { + if (counter > 8) { counter = 0; } return counter; @@ -253,6 +257,8 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer.battery.info.number_of_cells = 108; //Hardcoded for the 108S SE27 battery. SE26 96S TODO + + datalayer_extended.bmwix.T30_Voltage = terminal30_12v_voltage; } void receive_can_battery(CAN_frame rx_frame) { @@ -346,7 +352,9 @@ void receive_can_battery(CAN_frame rx_frame) { } 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) + terminal30_12v_voltage = (rx_frame.data.u8[5] <<8 | rx_frame.data.u8[6]); } break; default: @@ -409,6 +417,10 @@ void send_can_battery() { BMWiX_6F4.data.u8[4] = 0x54; transmit_can(&BMWiX_6F4, can_config.battery); break; + case 8: + BMWiX_6F4.data.u8[4] = 0xA7; //Terminal 30 12v voltage + transmit_can(&BMWiX_6F4, can_config.battery); + break; } From 774c544bca3093c47cb78b56f4932fab7ced9edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 29 Oct 2024 20:38:24 +0200 Subject: [PATCH 10/81] Add contactor opening incase of FAULT --- Software/src/battery/TESLA-BATTERY.cpp | 33 ++++++++++++++++++-------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index a35751d98..3eac62ccf 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -22,7 +22,7 @@ CAN_frame TESLA_221_2 = { .DLC = 8, .ID = 0x221, .data = {0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA}}; //Contactor Frame 221 - hv_up_for_drive - +static uint16_t sendContactorClosingMessagesStill = 300; static uint32_t battery_total_discharge = 0; static uint32_t battery_total_charge = 0; static uint16_t battery_volts = 0; // V @@ -1003,7 +1003,7 @@ unsigned long lastSend118 = 0; int index_1CF = 0; int index_118 = 0; -#endif +#endif //defined(TESLA_MODEL_SX_BATTERY) || defined(EXP_TESLA_BMS_DIGITAL_HVIL) void send_can_battery() { /*From bielec: My fist 221 message, to close the contactors is 0x41, 0x11, 0x01, 0x00, 0x00, 0x00, 0x20, 0x96 and then, @@ -1014,13 +1014,13 @@ the first, for a few cycles, then stop all messages which causes the contactor unsigned long currentMillis = millis(); #if defined(TESLA_MODEL_SX_BATTERY) || defined(EXP_TESLA_BMS_DIGITAL_HVIL) - if (datalayer.system.status.inverter_allows_contactor_closing) { + if ((datalayer.system.status.inverter_allows_contactor_closing) && (datalayer.battery.status.bms_status != FAULT)) { if (currentMillis - lastSend1CF >= 10) { transmit_can(&can_msg_1CF[index_1CF], can_config.battery); #ifdef DOUBLE_BATTERY transmit_can(&can_msg_1CF[index_1CF], can_config.battery_double); -#endif +#endif // DOUBLE_BATTERY index_1CF = (index_1CF + 1) % 8; lastSend1CF = currentMillis; @@ -1030,7 +1030,7 @@ the first, for a few cycles, then stop all messages which causes the contactor transmit_can(&can_msg_118[index_118], can_config.battery); #ifdef DOUBLE_BATTERY transmit_can(&can_msg_1CF[index_1CF], can_config.battery_double); -#endif +#endif //DOUBLE_BATTERY index_118 = (index_118 + 1) % 16; lastSend118 = currentMillis; @@ -1039,7 +1039,7 @@ the first, for a few cycles, then stop all messages which causes the contactor index_1CF = 0; index_118 = 0; } -#endif +#endif //defined(TESLA_MODEL_SX_BATTERY) || defined(EXP_TESLA_BMS_DIGITAL_HVIL) //Send 30ms message if (currentMillis - previousMillis30 >= INTERVAL_30_MS) { @@ -1051,15 +1051,26 @@ the first, for a few cycles, then stop all messages which causes the contactor } previousMillis30 = currentMillis; - if (datalayer.system.status.inverter_allows_contactor_closing) { + if ((datalayer.system.status.inverter_allows_contactor_closing == true) && + (datalayer.battery.status.bms_status != FAULT)) { + sendContactorClosingMessagesStill = 300; transmit_can(&TESLA_221_1, can_config.battery); transmit_can(&TESLA_221_2, can_config.battery); #ifdef DOUBLE_BATTERY if (datalayer.system.status.battery2_allows_contactor_closing) { - transmit_can(&TESLA_221_1, can_config.battery_double); // CAN2 connected to battery 2 + transmit_can(&TESLA_221_1, can_config.battery_double); transmit_can(&TESLA_221_2, can_config.battery_double); } +#endif //DOUBLE_BATTERY + } else { // Faulted state, or inverter blocks contactor closing + if (sendContactorClosingMessagesStill > 0) { + transmit_can(&TESLA_221_1, can_config.battery); + sendContactorClosingMessagesStill--; + +#ifdef DOUBLE_BATTERY + transmit_can(&TESLA_221_1, can_config.battery_double); #endif //DOUBLE_BATTERY + } } } } @@ -1091,7 +1102,8 @@ void printFaultCodesIfActive() { } if (datalayer.system.status.inverter_allows_contactor_closing == false) { Serial.println( - "ERROR: Solar inverter does not allow for contactor closing. Check communication connection to the inverter OR " + "ERROR: Solar inverter does not allow for contactor closing. Check communication connection to the inverter " + "OR " "disable the inverter protocol to proceed with contactor closing"); } // Check each symbol and print debug information if its value is 1 @@ -1166,7 +1178,8 @@ void printFaultCodesIfActive_battery2() { } if (datalayer.system.status.inverter_allows_contactor_closing == false) { Serial.println( - "ERROR: Solar inverter does not allow for contactor closing. Check communication connection to the inverter OR " + "ERROR: Solar inverter does not allow for contactor closing. Check communication connection to the inverter " + "OR " "disable the inverter protocol to proceed with contactor closing"); } // Check each symbol and print debug information if its value is 1 From 280ac3cbee51e6ac371c32b0a50048d2ec38507f Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:40:04 +0000 Subject: [PATCH 11/81] 0.2alpha version - Update BMW-IX-BATTERY.h --- Software/src/battery/BMW-IX-BATTERY.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.h b/Software/src/battery/BMW-IX-BATTERY.h index 8c2e24c9f..46ea98203 100644 --- a/Software/src/battery/BMW-IX-BATTERY.h +++ b/Software/src/battery/BMW-IX-BATTERY.h @@ -5,8 +5,8 @@ #define BATTERY_SELECTED -#define WUP_PIN 25 -#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V +//#define WUP_PIN 25 //Not used +#define MAX_PACK_VOLTAGE_DV 4600 //4600 = 460.0V #define MIN_PACK_VOLTAGE_DV 3000 #define MAX_CELL_DEVIATION_MV 500 #define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value From 9ea8458bf7d4a14080695cf283e4c7455dc05166 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:40:25 +0000 Subject: [PATCH 12/81] 0.2alpha version - Update BMW-IX-BATTERY.cpp --- Software/src/battery/BMW-IX-BATTERY.cpp | 491 +++++++++++++----------- 1 file changed, 264 insertions(+), 227 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 0b0b5a69c..4f4627f84 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -23,27 +23,15 @@ static CmdState cmdState = SOC; /* Suspected Vehicle comms required: - 0x06D DLC? 1000ms - counters? 0x2F1 DLC? 1000ms during run : 0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0x00, 0xF3, 0xFF - at startup 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xF3, 0xFF. Suspect byte [4] is a counter 0x439 DLC4 1000ms STATIC 0x0C0 DLC2 200ms needs counter - 0x510 DLC8 100ms STATIC 40 10 40 00 6F DF 19 00 during run - Startup sends this once: 0x40 0x10 0x02 0x00 0x00 0x00 0x00 0x00 0x587 DLC8 appears at startup 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF , 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF, 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF, 0x06 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x82 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF -No vehicle log available, SME asks for: - 0x125 (CCU) - 0x16E (CCU) - 0x340 (CCU) - 0x4F8 (CCU) - 0x188 (CCU) - 0x91 (EME1) - 0xAA (EME2) - 0x?? Suspect there is a drive mode flag somewhere - balancing might only be active in some modes - SME Output: - 0x08F DLC48 10ms - Appears to have analog readings like volt/temp/current + 0x12B8D087 5000ms - Extended ID 0x1D2 DLC8 1000ms 0x20B DLC8 1000ms 0x2E2 DLC16 1000ms @@ -54,135 +42,134 @@ SME Output: 0x49C DLC8 1000ms 0x4A1 DLC8 1000ms 0x4BB DLC64 200ms - seems multplexed on [0] - 0x4D0 DLC64 1000ms - some slow/flickering values + 0x4D0 DLC64 1000ms - some slow/flickering values - possible change during fault + 0x510 DLC8 100ms STATIC 40 10 40 00 6F DF 19 00 during run - Startup sends this once: 0x40 0x10 0x02 0x00 0x00 0x00 0x00 0x00 0x607 UDS Response +No vehicle log available, SME asks for: + 0x125 (CCU) + 0x16E (CCU) + 0x340 (CCU) + 0x4F8 (CCU) + 0x188 (CCU) + 0x91 (EME1) + 0xAA (EME2) + 0x?? Suspect there is a drive mode flag somewhere - balancing might only be active in some modes -UDS Map: - 69 - service disconnect (1 = closed) - c7 - available energy - available energy charged - ce - min avg max SOC - 61 len12 = current sensor - 53 - min and max cell voltage - 4d- main battery voltage - 4a - after contactor voltage - a4 = charnging contactor temp - A3 - MAIN CONTACTOR VOLTAGE - f4 00 0a 62 DD = main battery temp - A7 - T30 12v voltage (SME input voltage) - B6 - T30C 12v voltage (12v pyro sensor) - 51 = release, switch contactors. 1 = control of switch contactors active - CD = charge contactors - - TX 07 03 22 E5 54 - UDS request cell voltages - RX F4 10 E3 62 E5 54 - 16bit cell voltages batch1 (29 cells) - TX 07 30 00 02 - UDS request continue data - RX F4 21 0F - 16bit cell voltages continue2 (31 cells) - RX F4 22 0F - 16bit cell voltages continue3 (31 cells) - RX F4 23 0F - 16bit cell voltages continue4 (17 cells) - - TX 07 03 22 E5 9A - UDS request cell SOC - RX F4 10 E3 62 E5 9A?? - 16bit cell SOC batch1 (29 cells) - - TX 07 03 22 E5 CA - UDS request cell temps - RX F4 10 81 62 E5 CA - 16bit cell temps batch1 - RX F4 21 81 62 E5 CA - 16bit cell temps continue2 - TX 07 30 00 02 - UDS request continue data +TODO +- Request batt serial number on F1 8C (already parsing RX) +- Check PWM required from ACSM +- Use voltage qualifier for extended data? +- More Balancing values +- Check for stale min/max values +- Prevent fault state on SME reset */ +//Vehicle CAN START +CAN_frame BMWiX_06D = {.FD = true, .ext_ID = false, .DLC = 8, .ID = 0x06D, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF}}; // 1000ms BDC Output - [0] static [1,2][3,4] counter x2. 3,4 is 9 higher than 1,2 is needed? [5-7] static +CAN_frame BMWiX_0C0 = {.FD = true, .ext_ID = false, .DLC = 2, .ID = 0x0C0, + .data = {0xF0, 0x08}}; // Keep Alive 2 BDC>SME 200ms First byte cycles F0 > FE second byte 08 static - MINIMUM ID TO KEEP SME AWAKE -CAN_frame BMWiX_06D = {.FD = true, - .ext_ID = false, - .DLC = 8, - .ID = 0x06D, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF}}; // 1000ms BDC Output - [0] static [1]-[4] counter x2? is needed? [5-7] static - +CAN_frame BMWiX_276 = {.FD = true, .ext_ID = false, .DLC = 8, .ID = 0x476, + .data = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC}}; // 5000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED -CAN_frame BMWiX_2F1 = {.FD = true, - .ext_ID = false, - .DLC = 8, - .ID = 0x2F1, +CAN_frame BMWiX_2F1 = {.FD = true, .ext_ID = false, .DLC = 8, .ID = 0x2F1, .data = {0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0x00, 0xF3, 0xFF}}; // 1000ms BDC Output - Static values - varies at startup - - -CAN_frame BMWiX_439 = {.FD = true, - .ext_ID = false, - .DLC = 4, - .ID = 0x439, +CAN_frame BMWiX_439 = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x439, .data = {0xFF, 0xBF, 0xFF, 0xFF}}; // 1000ms BDC Output - Static values +CAN_frame BMWiX_486 = {.FD = true, .ext_ID = false, .DLC = 48, .ID = 0x486, + .data = {0xFE, 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFF , 0x7F , 0x33 , 0xFD , 0xFD , 0xFD , 0xFD , 0xC0 , 0x41 , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF}}; // 1000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED +CAN_frame BMWiX_49C = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x49C, + .data = {0xD2, 0xF2, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; // 1000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED -CAN_frame BMWiX_510 = {.FD = true, - .ext_ID = false, - .DLC = 8, - .ID = 0x510, +CAN_frame BMWiX_510 = {.FD = true, .ext_ID = false, .DLC = 8, .ID = 0x510, .data = {0x40, 0x10, 0x40, 0x00, 0x6F, 0xDF, 0x19, 0x00}}; // 100ms BDC Output - Static values +CAN_frame BMWiX_12B8D087 = {.FD = true, .ext_ID = true, .DLC = 2, .ID = 0x12B8D087, + .data = {0xFC, 0xFF}}; // 5000ms SME Output - Static values +//Vehicle CAN END + +//Request Data CAN START +CAN_frame BMWiX_6F4 = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0xC7}}; // Generic UDS Request data from SME. byte 4 selects requested value +CAN_frame BMWiX_6F4_REQUEST_SLEEPMODE = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x6F4, .data = {0x07, 0x02, 0x11, 0x04}}; // UDS Request Request BMS/SME goes to Sleep Mode +CAN_frame BMWiX_6F4_REQUEST_HARD_RESET = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x6F4, .data = {0x07, 0x02, 0x11, 0x01}}; // UDS Request Hard reset of BMS/SME +CAN_frame BMWiX_6F4_REQUEST_CELL_TEMP = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xDD, 0xC0}}; // UDS Request Cell Temperatures +CAN_frame BMWiX_6F4_REQUEST_SOC = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0xCE}}; // Min/Avg/Max SOC% +CAN_frame BMWiX_6F4_REQUEST_CAPACITY = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0xC7}}; //Current and max capacity kWh. Stored in kWh as 0.01 scale with -50 bias +CAN_frame BMWiX_6F4_REQUEST_MINMAXCELLV = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x53}}; //Min and max cell voltage 10V = Qualifier Invalid +CAN_frame BMWiX_6F4_REQUEST_MAINVOLTAGE_POSTCONTACTOR = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x4A}}; //Main Battery Voltage (After Contactor) +CAN_frame BMWiX_6F4_REQUEST_MAINVOLTAGE_PRECONTACTOR = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x4D}}; //Main Battery Voltage (Pre Contactor) +CAN_frame BMWiX_6F4_REQUEST_BATTERYCURRENT = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x61}}; //Current amps 32bit signed MSB. dA . negative is discharge +CAN_frame BMWiX_6F4_REQUEST_CELL_VOLTAGE = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x54}}; //MultiFrameIndividual Cell Voltages +CAN_frame BMWiX_6F4_REQUEST_T30VOLTAGE = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0xA7}}; //Terminal 30 Voltage (12V SME supply) +CAN_frame BMWiX_6F4_REQUEST_EOL_ISO = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xA8, 0x60}}; //Request EOL Reading including ISO +CAN_frame BMWiX_6F4_REQUEST_SOH = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x45}}; //SOH Max Min Mean Request +CAN_frame BMWiX_6F4_REQUEST_DATASUMMARY = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x45}}; //MultiFrame Summary Request, includes SOC/SOH/MinMax/MaxCapac/RemainCapac/max v and t at last charge. slow refreshrate +CAN_frame BMWiX_6F4_REQUEST_PYRO = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xAC, 0x93}}; //Pyro Status +CAN_frame BMWiX_6F4_REQUEST_UPTIME = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE4, 0xC0}}; // Uptime and Vehicle Time Status +CAN_frame BMWiX_6F4_REQUEST_HVIL = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x69}}; // Request HVIL State +CAN_frame BMWiX_6F4_REQUEST_BALANCINGSTATUS = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE4, 0xCA}}; // Request Balancing Data +CAN_frame BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x62}}; // Request allowable charge discharge amps +CAN_frame BMWiX_6F4_REQUEST_QUALIFIER_CHECK = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x4B}}; // Request HV Voltage Qualifier +CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_CLOSE = {.FD = true, .ext_ID = false, .DLC = 6, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Close - Unconfirmed +CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_OPEN = {.FD = true, .ext_ID = false, .DLC = 6, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Open - Unconfirmed +CAN_frame BMWiX_6F4_REQUEST_BALANCING_START = {.FD = true, .ext_ID = false, .DLC = 6, .ID = 0x6F4, .data = {0xF4, 0x04, 0x71, 0x01, 0xAE, 0x77}}; // Request Balancing command? +CAN_frame BMWiX_6F4_CONTINUE_DATA = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x6F4, .data = {0x07, 0x30, 0x00, 0x02}}; -CAN_frame BMWiX_6F4 = {.FD = true, - .ext_ID = false, - .DLC = 5, - .ID = 0x6F4, - .data = {0x07, 0x03, 0x22, 0xE5, 0xC7}}; // UDS Request data from SME. byte 4 selects requested value - - -CAN_frame BMWiX_0C0 = {.FD = true, - .ext_ID = false, - .DLC = 2, - .ID = 0x0C0, - .data = {0xF0, 0x08}}; // Keep Alive 2 BDC>SME 200ms First byte cycles F0 > FE second byte 08 static - +//Action Requests: +CAN_frame BMW_10B = {.FD = true, .ext_ID = false, .DLC = 3, .ID = 0x10B, .data = {0xCD, 0x00, 0xFC}}; // Contactor closing command? -CAN_frame BMWiX_6F4_CELL_VOLTAGE = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x54}}; CAN_frame BMWiX_6F4_CELL_SOC = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x9A}}; CAN_frame BMWiX_6F4_CELL_TEMP = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0xCA}}; - -CAN_frame BMWiX_6F4_CONTINUE_DATA = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x6F4, .data = {0x07, 0x30, 0x00, 0x02}}; - -CAN_frame BMW_6F4_CELL = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xDD, 0xBF}};// gives error F4 03 7F 22 31 -CAN_frame BMW_6F4_SOH = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0x63, 0x35}}; // gives error F4 03 7F 22 31 -CAN_frame BMW_6F4_SOC = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xDD, 0xBC}}; // gives blanks - -CAN_frame BMW_10B = {.FD = true, - .ext_ID = false, - .DLC = 3, - .ID = 0x10B, - .data = {0xCD, 0x00, 0xFC}}; // Contactor closing command - -CAN_frame BMW_6F4_CELL_VOLTAGE_AVG = {.FD = true, - .ext_ID = false, - .DLC = 5, - .ID = 0x6F4, - .data = {0x07, 0x03, 0x22, 0xDF, 0xA0}}; -CAN_frame BMW_6F4_CELL_VOLTAGE_CELLNO = {.FD = false, - .ext_ID = false, - .DLC = 7, - .ID = 0x6F4, - .data = {0x07, 0x05, 0x31, 0x01, 0xAD, 0x6E, 0x01}}; // gives error -CAN_frame BMW_6F4_CELL_CONTINUE = {.FD = true, - .ext_ID = false, - .DLC = 6, - .ID = 0x6F4, - .data = {0x07, 0x04, 0x31, 0x03, 0xAD, 0x6E}}; +//Request Data CAN End static bool battery_awake = false; +//Setup UDS values to poll for +CAN_frame* UDS_REQUESTS100MS[] = { + &BMWiX_6F4_REQUEST_CELL_TEMP, + &BMWiX_6F4_REQUEST_SOC, + &BMWiX_6F4_REQUEST_CAPACITY, + &BMWiX_6F4_REQUEST_MINMAXCELLV, + &BMWiX_6F4_REQUEST_MAINVOLTAGE_POSTCONTACTOR, + &BMWiX_6F4_REQUEST_MAINVOLTAGE_PRECONTACTOR, + &BMWiX_6F4_REQUEST_BATTERYCURRENT, + &BMWiX_6F4_REQUEST_CELL_VOLTAGE, + &BMWiX_6F4_REQUEST_T30VOLTAGE, + &BMWiX_6F4_REQUEST_SOH, + &BMWiX_6F4_REQUEST_UPTIME, + &BMWiX_6F4_REQUEST_PYRO, + &BMWiX_6F4_REQUEST_EOL_ISO, + &BMWiX_6F4_REQUEST_HVIL, + &BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS, + &BMWiX_6F4_REQUEST_BALANCINGSTATUS + }; +int numUDSreqs = sizeof(UDS_REQUESTS100MS) / sizeof(UDS_REQUESTS100MS[0]); // Number of elements in the array + //iX Intermediate vars +static uint32_t battery_serial_number = 0; static int32_t battery_current = 0; -static int16_t battery_voltage = 0; +static int16_t battery_voltage = 370; static int16_t terminal30_12v_voltage = 0; static int16_t battery_voltage_after_contactor = 0; -static int16_t min_soc_state = 0; -static int16_t avg_soc_state = 0; -static int16_t max_soc_state = 0; +static int16_t min_soc_state = 50; +static int16_t avg_soc_state = 50; +static int16_t max_soc_state = 50; +static int16_t min_soh_state = 99;// Uses E5 45, also available in 78 73 +static int16_t avg_soh_state = 99;// Uses E5 45, also available in 78 73 +static int16_t max_soh_state = 99;// Uses E5 45, also available in 78 73 +static uint16_t battery_max_charge_voltage = 0; +static uint16_t battery_min_discharge_voltage = 0; static int32_t remaining_capacity = 0; static int32_t max_capacity = 0; static int16_t min_battery_temperature = 0; @@ -191,20 +178,41 @@ static int16_t max_battery_temperature = 0; static int16_t main_contactor_temperature = 0; static int16_t min_cell_voltage = 0; static int16_t max_cell_voltage = 0; +static unsigned min_cell_voltage_lastreceived = 0; +static unsigned max_cell_voltage_lastreceived = 0; static int16_t battery_power = 0; +static uint32_t sme_uptime = 0; //Uses E4 C0 +static int16_t allowable_charge_amps = 0; //E5 62 +static int16_t allowable_discharge_amps = 0; //E5 62 +static int32_t iso_safety_positive = 0; //Uses A8 60 +static int32_t iso_safety_negative = 0; //Uses A8 60 +static int32_t iso_safety_parallel = 0; //Uses A8 60 +static int16_t count_full_charges = 0; //TODO 42 +static int16_t count_charges = 0; //TODO 42 +static int16_t hvil_status = 0; +static int16_t voltage_qualifier_status = 0; //0 = Valid, 1 = Invalid +static int16_t balancing_status = 0; //4 = not active +static uint8_t contactors_closed = 0; //TODO E5 BF or E5 51 +static uint8_t contactor_status_precharge = 0; //TODO E5 BF +static uint8_t contactor_status_negative = 0; //TODO E5 BF +static uint8_t contactor_status_positive = 0; //TODO E5 BF +static uint8_t pyro_status_pss1 = 0; //Using AC 93 +static uint8_t pyro_status_pss4 = 0; //Using AC 93 +static uint8_t pyro_status_pss6 = 0; //Using AC 93 static uint8_t uds_req_id_counter = 0; + static byte iX_0C0_counter = 0xF0; // Initialize to 0xF0 //End iX Intermediate vars static uint8_t current_cell_polled = 0; -static uint8_t increment_uds_req_id_counter(uint8_t counter) { - counter++; - if (counter > 8) { - counter = 0; +static uint8_t increment_uds_req_id_counter(uint8_t index) { + index++; + if (index >= numUDSreqs) { + index = 0; } - return counter; + return index; } static uint8_t increment_alive_counter(uint8_t counter) { @@ -236,11 +244,11 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.remaining_capacity_Wh = remaining_capacity; - datalayer.battery.status.soh_pptt = 9900; + datalayer.battery.status.soh_pptt = min_soh_state; - datalayer.battery.status.max_discharge_power_W = 6000; + datalayer.battery.status.max_discharge_power_W = 10000; //Aux HV Port has 100A Fuse - datalayer.battery.status.max_charge_power_W = 6000; + datalayer.battery.status.max_charge_power_W = 10000; //Aux HV Port has 100A Fuse battery_power = (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100)); @@ -256,11 +264,34 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - datalayer.battery.info.number_of_cells = 108; //Hardcoded for the 108S SE27 battery. SE26 96S TODO + datalayer.battery.info.number_of_cells = 108; //init with 108S before autodetection datalayer_extended.bmwix.T30_Voltage = terminal30_12v_voltage; -} + datalayer_extended.bmwix.hvil_status= hvil_status; + + datalayer_extended.bmwix.bms_uptime = sme_uptime; + + datalayer_extended.bmwix.pyro_status_pss1 = pyro_status_pss1; + + datalayer_extended.bmwix.pyro_status_pss4 = pyro_status_pss4; + + datalayer_extended.bmwix.pyro_status_pss6 = pyro_status_pss6; + + datalayer_extended.bmwix.iso_safety_positive = iso_safety_positive; + + datalayer_extended.bmwix.iso_safety_negative = iso_safety_negative; + + datalayer_extended.bmwix.iso_safety_parallel= iso_safety_parallel; + + datalayer_extended.bmwix.allowable_charge_amps = allowable_charge_amps; + + datalayer_extended.bmwix.allowable_discharge_amps= allowable_discharge_amps; + + datalayer_extended.bmwix.balancing_status = balancing_status; + + +} void receive_can_battery(CAN_frame rx_frame) { battery_awake = true; switch (rx_frame.ID) { @@ -276,7 +307,10 @@ void receive_can_battery(CAN_frame rx_frame) { 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]; - datalayer.battery.status.cell_voltages_mV[voltage_index++] = voltage; + if (voltage < 10000) { //Check reading is plausible - otherwise ignore + datalayer.battery.status.cell_voltages_mV[voltage_index] = voltage; + } + voltage_index++; } } @@ -290,7 +324,10 @@ void receive_can_battery(CAN_frame rx_frame) { 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]; - datalayer.battery.status.cell_voltages_mV[voltage_index++] = voltage; + if (voltage < 10000) { //Check reading is plausible - otherwise ignore + datalayer.battery.status.cell_voltages_mV[voltage_index] = voltage; + } + voltage_index++; } } @@ -300,14 +337,25 @@ void receive_can_battery(CAN_frame rx_frame) { 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]; - datalayer.battery.status.cell_voltages_mV[voltage_index++] = voltage; + if (voltage < 10000) { //Check reading is plausible - otherwise ignore + datalayer.battery.status.cell_voltages_mV[voltage_index] = voltage; + } + voltage_index++; } } 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 = 17; // number of voltage readings to get + 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 + datalayer.battery.info.number_of_cells = 96; + } else { //We have data in 97th cell, assume 108S Battery + num_voltages = 17; // number of voltage readings to get - 17 more to get on 108S + datalayer.battery.info.number_of_cells = 108; + } + 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]; datalayer.battery.status.cell_voltages_mV[voltage_index++] = voltage; @@ -329,6 +377,9 @@ void receive_can_battery(CAN_frame rx_frame) { rx_frame.data.u8[8]); } + 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% 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]); @@ -340,12 +391,52 @@ void receive_can_battery(CAN_frame rx_frame) { max_capacity = ((rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9]) *10) -50000; } - if (rx_frame.DLC = 12 && rx_frame.data.u8[4] == 0xE5 && rx_frame.data.u8[5] == 0x53) { //Min and max cell voltage + 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 + allowable_charge_amps = (int16_t)((rx_frame.data.u8[6] <<8 | rx_frame.data.u8[7])); + allowable_discharge_amps = (int16_t)((rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9])); + } + + 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 + 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) | (rx_frame.data.u8[40] << 8) |rx_frame.data.u8[41]; //Assuming 32bit + iso_safety_parallel = (rx_frame.data.u8[42] << 24) | (rx_frame.data.u8[43] << 16) | (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 + 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 + 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.data.u8[6] <<8 | rx_frame.data.u8[7]) == 10000 && (rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9]) == 10000){ //Qualifier Invalid Mode - Request Reboot + #ifdef DEBUG_VIA_USB + Serial.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset"); + #endif + set_event(EVENT_SOC_UNAVAILABLE, (millis())); + transmit_can(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery); + } else{ //Only ingest values if they are not the 10V Error state min_cell_voltage = (rx_frame.data.u8[6] <<8 | rx_frame.data.u8[7]); max_cell_voltage = (rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9]); + } } - 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; @@ -356,6 +447,19 @@ void receive_can_battery(CAN_frame rx_frame) { 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[3] == 0xE5 && rx_frame.data.u8[4] == 0x69) { //HVIL Status + hvil_status = ( rx_frame.data.u8[5]); + } + 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++) { + numberString[i] = char(rx_frame.data.u8[i+6]); + } + numberString[10] = '\0'; // Null-terminate the string + // Step 3: Convert the string to an unsigned long integer + battery_serial_number = strtoul(numberString, NULL, 10); + } break; default: break; @@ -365,7 +469,7 @@ void receive_can_battery(CAN_frame rx_frame) { void send_can_battery() { unsigned long currentMillis = millis(); - if (battery_awake) { + //if (battery_awake) { //We can always send CAN as the iX BMS will wake up on vehicle comms //Send 20ms message if (currentMillis - previousMillis20 >= INTERVAL_20_MS) { // Check if sending of CAN messages has been delayed too much. @@ -380,61 +484,22 @@ void send_can_battery() { if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { previousMillis100 = currentMillis; - uds_req_id_counter = increment_uds_req_id_counter(uds_req_id_counter); - switch (uds_req_id_counter){ - case 0: - //BMWiX_6F4.data.u8[3] = 0x22; - BMWiX_6F4.data.u8[3] = 0xDD; - BMWiX_6F4.data.u8[4] = 0xC0; - transmit_can(&BMWiX_6F4, can_config.battery); - break; - case 1: - BMWiX_6F4.data.u8[3] = 0xE5; - BMWiX_6F4.data.u8[4] = 0xCE; - transmit_can(&BMWiX_6F4, can_config.battery); - break; - case 2: - BMWiX_6F4.data.u8[4] = 0xC7; - transmit_can(&BMWiX_6F4, can_config.battery); - break; - case 3: - BMWiX_6F4.data.u8[4] = 0x53; - transmit_can(&BMWiX_6F4, can_config.battery); - break; - case 4: - BMWiX_6F4.data.u8[4] = 0x4A; - transmit_can(&BMWiX_6F4, can_config.battery); - break; - case 5: - BMWiX_6F4.data.u8[4] = 0x4D; - transmit_can(&BMWiX_6F4, can_config.battery); - break; - case 6: - BMWiX_6F4.data.u8[4] = 0x61; - transmit_can(&BMWiX_6F4, can_config.battery); - break; - case 7: - BMWiX_6F4.data.u8[4] = 0x54; - transmit_can(&BMWiX_6F4, can_config.battery); - break; - case 8: - BMWiX_6F4.data.u8[4] = 0xA7; //Terminal 30 12v voltage - transmit_can(&BMWiX_6F4, can_config.battery); - break; - } + //Loop through and send a different UDS request each cycle + uds_req_id_counter = increment_uds_req_id_counter(uds_req_id_counter); + transmit_can(UDS_REQUESTS100MS[uds_req_id_counter], can_config.battery); - //Send SME Keep alive values 100ms - transmit_can(&BMWiX_510, can_config.battery); + //Send SME Keep alive values 100ms + transmit_can(&BMWiX_510, can_config.battery); } // Send 200ms CAN Message if (currentMillis - previousMillis200 >= INTERVAL_200_MS) { previousMillis200 = currentMillis; - //Send SME Keep alive values 200ms - BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 - transmit_can(&BMWiX_0C0, can_config.battery); + //Send SME Keep alive values 200ms + BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 + transmit_can(&BMWiX_0C0, can_config.battery); } // Send 500ms CAN Message if (currentMillis - previousMillis500 >= INTERVAL_500_MS) { @@ -448,43 +513,11 @@ void send_can_battery() { if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { previousMillis1000 = currentMillis; - //Send SME Keep alive values 1000ms - transmit_can(&BMWiX_06D, can_config.battery); - transmit_can(&BMWiX_2F1, can_config.battery); - transmit_can(&BMWiX_439, can_config.battery); - - switch (cmdState) { - case SOC: - transmit_can(&BMW_6F4_CELL, can_config.battery); - cmdState = CELL_VOLTAGE_MINMAX; - break; - case CELL_VOLTAGE_MINMAX: - transmit_can(&BMW_6F4_SOH, can_config.battery); - cmdState = SOH; - break; - case SOH: - transmit_can(&BMW_6F4_CELL_VOLTAGE_AVG, can_config.battery); - cmdState = CELL_VOLTAGE_CELLNO; - current_cell_polled = 0; - - break; - case CELL_VOLTAGE_CELLNO: - current_cell_polled++; - if (current_cell_polled > 96) { - datalayer.battery.info.number_of_cells = 97; - cmdState = CELL_VOLTAGE_CELLNO_LAST; - } else { - cmdState = CELL_VOLTAGE_CELLNO; - - BMW_6F4_CELL_VOLTAGE_CELLNO.data.u8[6] = current_cell_polled; - transmit_can(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery); - } - break; - case CELL_VOLTAGE_CELLNO_LAST: - transmit_can(&BMW_6F4_SOC, can_config.battery); - cmdState = SOC; - break; - } + //Send SME Keep alive values 1000ms + //test disable transmit_can(&BMWiX_06D, can_config.battery); + //test disable transmit_can(&BMWiX_2F1, can_config.battery); + //test disable transmit_can(&BMWiX_439, can_config.battery); + } // Send 5000ms CAN Message if (currentMillis - previousMillis5000 >= INTERVAL_5_S) { @@ -494,31 +527,33 @@ void send_can_battery() { if (currentMillis - previousMillis10000 >= INTERVAL_10_S) { previousMillis10000 = currentMillis; } - } else { - previousMillis20 = currentMillis; - previousMillis100 = currentMillis; - previousMillis200 = currentMillis; - previousMillis500 = currentMillis; - previousMillis640 = currentMillis; - previousMillis1000 = currentMillis; - previousMillis5000 = currentMillis; - previousMillis10000 = currentMillis; } -} + //We can always send CAN as the iX BMS will wake up on vehicle comms + // else { + // previousMillis20 = currentMillis; + // previousMillis100 = currentMillis; + // previousMillis200 = currentMillis; + // previousMillis500 = currentMillis; + // previousMillis640 = currentMillis; + // previousMillis1000 = currentMillis; + // previousMillis5000 = currentMillis; + // previousMillis10000 = currentMillis; + // } +//} //We can always send CAN as the iX BMS will wake up on vehicle comms void setup_battery(void) { // Performs one time setup at startup #ifdef DEBUG_VIA_USB Serial.println("BMW iX battery selected"); #endif //DEBUG_VIA_USB - //Before we have started up and detected which battery is in use, use 60AH values + //Before we have started up and detected which battery is in use, use 108S values datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.system.status.battery_allows_contactor_closing = true; - pinMode(WUP_PIN, OUTPUT); - digitalWrite(WUP_PIN, HIGH); // Wake up the battery + //pinMode(WUP_PIN, OUTPUT); // Not needed - can hold WUP pin High with iX BMS + //digitalWrite(WUP_PIN, HIGH); // Wake up the battery // Not needed - can hold WUP pin High with iX BMS //Wake Battery //Send SME Keep alive values 100ms @@ -526,10 +561,12 @@ void setup_battery(void) { // Performs one time setup at startup //Send SME Keep alive values 200ms BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 transmit_can(&BMWiX_0C0, can_config.battery); + + //Send SME Keep alive values 1000ms - transmit_can(&BMWiX_06D, can_config.battery); - transmit_can(&BMWiX_2F1, can_config.battery); - transmit_can(&BMWiX_439, can_config.battery); + //test disable transmit_can(&BMWiX_06D, can_config.battery); + //test disable transmit_can(&BMWiX_2F1, can_config.battery); + //test disable transmit_can(&BMWiX_439, can_config.battery); } #endif From 17a7e90b3d36ff46f79d31fb6d9288dc193e7497 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:40:45 +0000 Subject: [PATCH 13/81] 0.2alpha version - Update datalayer_extended.h --- Software/src/datalayer/datalayer_extended.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index aecbed703..18fd8f4e3 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -8,7 +8,21 @@ typedef struct { /** Terminal 30 - 12V SME Supply Voltage */ uint16_t T30_Voltage = 0; /** Status HVIL, 1 HVIL OK, 0 HVIL disconnected*/ - uint8_t HVIL_Status= 0; + uint8_t hvil_status= 0; + /** Min/Max Cell SOH*/ + uint16_t min_soh_state = 0; + uint16_t max_soh_state=0; + uint32_t bms_uptime=0; + uint8_t pyro_status_pss1=0; + uint8_t pyro_status_pss4=0; + uint8_t pyro_status_pss6=0; + int32_t iso_safety_positive=0; + int32_t iso_safety_negative=0; + int32_t iso_safety_parallel=0; + int32_t allowable_charge_amps = 0; + int32_t allowable_discharge_amps = 0; + int16_t balancing_status = 0; + } DATALAYER_INFO_BMWIX; From 450d690edf34f84a59f9cf2e30a9d9e55edbdf95 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:41:11 +0000 Subject: [PATCH 14/81] 0.2alpha version - Update advanced_battery_html.cpp --- .../webserver/advanced_battery_html.cpp | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index 99ff2201b..8a018ce7c 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -18,7 +18,29 @@ String advanced_battery_processor(const String& var) { #ifdef BMW_IX_BATTERY - content += "

T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + "mV

"; + content += "

T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + " mV

"; + content += "

Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "

"; + static const char* balanceText[5] = {"0 No balancing mode active", + "1 Voltage-Controlled Balancing Mode", + "2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging" , + "3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage" , + "4 No balancing mode active, qualifier invalid" + }; + content += "

Balancing Status: " + String((balanceText[datalayer_extended.bmwix.balancing_status])) + "

"; + static const char* hvilText[2] = {"Error (Loop Open)", + "OK (Loop Closed)"}; + content += "

HVIL Status: " + String(hvilText[datalayer_extended.bmwix.hvil_status]) + "

"; + content += "

BMS Uptime: " + String(datalayer_extended.bmwix.bms_uptime) + " seconds

"; + content += "

BMS Allowed Charge Amps: " + String(datalayer_extended.bmwix.allowable_charge_amps) + " A

"; + content += "

BMS Allowed Disharge Amps: " + String(datalayer_extended.bmwix.allowable_discharge_amps) + " A

"; + content += "
"; + content += "

HV Isolation (2147483647kOhm = maximum/invalid)

"; + content += "

Isolation Positive: " + String(datalayer_extended.bmwix.iso_safety_positive) + " kOhm

"; + content += "

Isolation Negative: " + String(datalayer_extended.bmwix.iso_safety_negative) + " kOhm

"; + content += "

Isolation Parallel: " + String(datalayer_extended.bmwix.iso_safety_parallel) + " kOhm

"; + content += "

Pyro Status PSS1: " + String(datalayer_extended.bmwix.pyro_status_pss1) + "

"; + content += "

Pyro Status PSS4: " + String(datalayer_extended.bmwix.pyro_status_pss4) + "

"; + content += "

Pyro Status PSS6: " + String(datalayer_extended.bmwix.pyro_status_pss6) + "

"; #endif //BMW_IX_BATTERY #ifdef BMW_I3_BATTERY From 16fc2fb4f17378e2f207db255f9ca167f6aace26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 3 Nov 2024 13:31:56 +0200 Subject: [PATCH 15/81] Add degradation data reset for LEAF --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 306 +++++++++++++++++- Software/src/battery/NISSAN-LEAF-BATTERY.h | 8 + Software/src/datalayer/datalayer_extended.h | 12 + .../webserver/advanced_battery_html.cpp | 13 + Software/src/devboard/webserver/webserver.cpp | 10 + 5 files changed, 348 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 67bb21f95..7c48fe375 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -174,6 +174,18 @@ static int16_t battery2_temp_polled_max = 0; static int16_t battery2_temp_polled_min = 0; #endif // DOUBLE_BATTERY +// Clear SOH values +static uint8_t stateMachineClearSOH = 0xFF; +static uint32_t incomingChallenge = 0xFFFFFFFF; +static uint8_t solvedChallenge[8]; +static bool challengeFailed = false; + +CAN_frame LEAF_CLEAR_SOH = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x79B, + .data = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; + void print_with_units(char* header, int value, char* units) { Serial.print(header); Serial.print(value); @@ -337,6 +349,18 @@ void update_values_battery() { /* This function maps all the values fetched via datalayer_extended.nissanleaf.HeatingStop = battery_Heating_Stop; datalayer_extended.nissanleaf.HeatingStart = battery_Heating_Start; datalayer_extended.nissanleaf.HeaterSendRequest = battery_Batt_Heater_Mail_Send_Request; + datalayer_extended.nissanleaf.CryptoChallenge = incomingChallenge; + datalayer_extended.nissanleaf.SolvedChallengeMSB = + ((solvedChallenge[7] << 24) | (solvedChallenge[6] << 16) | (solvedChallenge[5] << 8) | solvedChallenge[4]); + datalayer_extended.nissanleaf.SolvedChallengeLSB = + ((solvedChallenge[3] << 24) | (solvedChallenge[2] << 16) | (solvedChallenge[1] << 8) | solvedChallenge[0]); + + // Update requests from webserver datalayer + if (datalayer_extended.nissanleaf.UserRequestSOHreset) { + Serial.println("REQUEST FROM WEBSERVER"); + stateMachineClearSOH = 0; //Start the statemachine + datalayer_extended.nissanleaf.UserRequestSOHreset = false; + } /*Finally print out values to serial if configured to do so*/ #ifdef DEBUG_VIA_USB @@ -610,7 +634,7 @@ void receive_can_battery2(CAN_frame rx_frame) { } } - if (stop_battery_query) { //Leafspy is active, stop our own polling + if (stop_battery_query) { //Leafspy/Service request is active, stop our own polling break; } @@ -847,6 +871,34 @@ void receive_can_battery(CAN_frame rx_frame) { hold_off_with_polling_10seconds = 10; //Polling is paused for 100s break; case 0x7BB: + + // This section checks if we are doing a SOH reset towards BMS + if (stateMachineClearSOH < 255) { + //Intercept the messages based on state machine + if (rx_frame.data.u8[0] == 0x06) { // Incoming challenge data! + incomingChallenge = ((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[4] << 16) | (rx_frame.data.u8[5] << 8) | + rx_frame.data.u8[6]); + } + //Error checking + if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F)) { + challengeFailed = true; + Serial.print("Challenge solving failed"); + } + // All CAN messages recieved from BMS will be logged via serial during development of this function + Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D + Serial.print(" "); + Serial.print(rx_frame.ID, HEX); + Serial.print(" "); + Serial.print(rx_frame.DLC); + Serial.print(" "); + for (int i = 0; i < rx_frame.DLC; ++i) { + Serial.print(rx_frame.data.u8[i], HEX); + Serial.print(" "); + } + Serial.println(""); + break; + } + //First check which group data we are getting if (rx_frame.data.u8[0] == 0x10) { //First message of a group group_7bb = rx_frame.data.u8[3]; @@ -1127,6 +1179,10 @@ void send_can_battery() { if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { previousMillis100 = currentMillis; + if (stateMachineClearSOH < 255) { // Enter the ClearSOH statemachine only if we request it + clearSOH(); + } + //When battery requests heating pack status change, ack this if (battery_Batt_Heater_Mail_Send_Request) { LEAF_50B.data.u8[6] = 0x20; //Batt_Heater_Mail_Send_OK @@ -1230,6 +1286,254 @@ uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrib return static_cast(1094 + (309 - temperature) * 2.5714285714285715); } +void clearSOH(void) { + + stop_battery_query = true; + hold_off_with_polling_10seconds = 10; // Active battery polling is paused for 100 seconds + + switch (stateMachineClearSOH) { + case 0: // Wait until polling actually stops + stateMachineClearSOH = 1; + break; + case 1: // Set CAN_PROCESS_FLAG to 0xC0 + LEAF_CLEAR_SOH.data.u8[0] = 0x02; + LEAF_CLEAR_SOH.data.u8[1] = 0x10; + LEAF_CLEAR_SOH.data.u8[2] = 0xC0; + LEAF_CLEAR_SOH.data.u8[3] = 0x00; + LEAF_CLEAR_SOH.data.u8[4] = 0x00; + LEAF_CLEAR_SOH.data.u8[5] = 0x00; + LEAF_CLEAR_SOH.data.u8[6] = 0x00; + LEAF_CLEAR_SOH.data.u8[7] = 0x00; + transmit_can(&LEAF_CLEAR_SOH, can_config.battery); + // BMS should reply 02 50 C0 FF FF FF FF FF + stateMachineClearSOH = 2; + break; + case 2: // Set something ? + LEAF_CLEAR_SOH.data.u8[0] = 0x02; + LEAF_CLEAR_SOH.data.u8[1] = 0x3E; + LEAF_CLEAR_SOH.data.u8[2] = 0x01; + LEAF_CLEAR_SOH.data.u8[3] = 0x00; + LEAF_CLEAR_SOH.data.u8[4] = 0x00; + LEAF_CLEAR_SOH.data.u8[5] = 0x00; + LEAF_CLEAR_SOH.data.u8[6] = 0x00; + LEAF_CLEAR_SOH.data.u8[7] = 0x00; + transmit_can(&LEAF_CLEAR_SOH, can_config.battery); + // BMS should reply 7E FF FF FF FF FF FF + stateMachineClearSOH = 3; + break; + case 3: // Request challenge to solve + LEAF_CLEAR_SOH.data.u8[0] = 0x02; + LEAF_CLEAR_SOH.data.u8[1] = 0x27; + LEAF_CLEAR_SOH.data.u8[2] = 0x65; + LEAF_CLEAR_SOH.data.u8[3] = 0x00; + LEAF_CLEAR_SOH.data.u8[4] = 0x00; + LEAF_CLEAR_SOH.data.u8[5] = 0x00; + LEAF_CLEAR_SOH.data.u8[6] = 0x00; + LEAF_CLEAR_SOH.data.u8[7] = 0x00; + transmit_can(&LEAF_CLEAR_SOH, can_config.battery); + stateMachineClearSOH = 4; + break; + case 4: // Send back decoded challenge data + decodeChallengeData(incomingChallenge, solvedChallenge); + LEAF_CLEAR_SOH.data.u8[0] = 0x10; + LEAF_CLEAR_SOH.data.u8[1] = 0x0A; + LEAF_CLEAR_SOH.data.u8[2] = 0x27; + LEAF_CLEAR_SOH.data.u8[3] = 0x66; + LEAF_CLEAR_SOH.data.u8[4] = 0x77; + LEAF_CLEAR_SOH.data.u8[5] = solvedChallenge[0]; + LEAF_CLEAR_SOH.data.u8[6] = solvedChallenge[1]; + LEAF_CLEAR_SOH.data.u8[7] = solvedChallenge[2]; + transmit_can(&LEAF_CLEAR_SOH, can_config.battery); + // BMS should reply 7BB 8 30 01 00 FF FF FF FF FF // Proceed with more data (PID ACK) + stateMachineClearSOH = 5; + break; + case 5: // Reply with even more decoded challenge data + LEAF_CLEAR_SOH.data.u8[0] = solvedChallenge[3]; + LEAF_CLEAR_SOH.data.u8[1] = solvedChallenge[4]; + LEAF_CLEAR_SOH.data.u8[2] = solvedChallenge[5]; + LEAF_CLEAR_SOH.data.u8[3] = solvedChallenge[6]; + LEAF_CLEAR_SOH.data.u8[4] = solvedChallenge[7]; + LEAF_CLEAR_SOH.data.u8[5] = 0x00; + LEAF_CLEAR_SOH.data.u8[6] = 0x00; + LEAF_CLEAR_SOH.data.u8[7] = 0x00; + transmit_can(&LEAF_CLEAR_SOH, can_config.battery); + // BMS should reply 02 67 66 FF FF FF FF FF // Thank you for the data + stateMachineClearSOH = 6; + break; + case 6: // Check if solved data was OK + LEAF_CLEAR_SOH.data.u8[0] = 0x03; + LEAF_CLEAR_SOH.data.u8[1] = 0x31; + LEAF_CLEAR_SOH.data.u8[2] = 0x03; + LEAF_CLEAR_SOH.data.u8[3] = 0x00; + LEAF_CLEAR_SOH.data.u8[4] = 0x00; + LEAF_CLEAR_SOH.data.u8[5] = 0x00; + LEAF_CLEAR_SOH.data.u8[6] = 0x00; + LEAF_CLEAR_SOH.data.u8[7] = 0x00; + transmit_can(&LEAF_CLEAR_SOH, can_config.battery); + //7BB 8 03 71 03 01 FF FF FF FF // If all is well, BMS replies with 03 71 03 01. + //Incase you sent wrong challenge, you get 03 7f 31 12 + stateMachineClearSOH = 7; + break; + case 7: // Reset SOH% request + LEAF_CLEAR_SOH.data.u8[0] = 0x03; + LEAF_CLEAR_SOH.data.u8[1] = 0x31; + LEAF_CLEAR_SOH.data.u8[2] = 0x03; + LEAF_CLEAR_SOH.data.u8[3] = 0x01; + LEAF_CLEAR_SOH.data.u8[4] = 0x00; + LEAF_CLEAR_SOH.data.u8[5] = 0x00; + LEAF_CLEAR_SOH.data.u8[6] = 0x00; + LEAF_CLEAR_SOH.data.u8[7] = 0x00; + transmit_can(&LEAF_CLEAR_SOH, can_config.battery); + //7BB 8 03 71 03 02 FF FF FF FF // 03 71 03 02 means that BMS accepted command. + //7BB 03 7f 31 12 means your challenge was wrong, so command ignored + stateMachineClearSOH = 8; + break; + case 8: // Please proceed with resetting SOH + LEAF_CLEAR_SOH.data.u8[0] = 0x02; + LEAF_CLEAR_SOH.data.u8[1] = 0x10; + LEAF_CLEAR_SOH.data.u8[2] = 0x81; + LEAF_CLEAR_SOH.data.u8[3] = 0x00; + LEAF_CLEAR_SOH.data.u8[4] = 0x00; + LEAF_CLEAR_SOH.data.u8[5] = 0x00; + LEAF_CLEAR_SOH.data.u8[6] = 0x00; + LEAF_CLEAR_SOH.data.u8[7] = 0x00; + transmit_can(&LEAF_CLEAR_SOH, can_config.battery); + // 7BB 8 02 50 81 FF FF FF FF FF // SOH reset OK + stateMachineClearSOH = 255; + break; + default: + break; + } +} + +uint32_t CyclicXorHash16Bit(uint32_t param_1, uint32_t param_2) { + bool bVar1; + uint32_t uVar2; + uint32_t uVar3; + uint32_t uVar4; + uint32_t uVar5; + uint32_t uVar6; + uint32_t uVar7; + uint32_t uVar8; + uint32_t uVar9; + uint32_t uVar10; + uint32_t uVar11; + uint32_t iVar12; + + param_1 = param_1 & 0xffff; + param_2 = param_2 & 0xffff; + uVar10 = 0xffff; + iVar12 = 2; + do { + uVar2 = param_2; + if ((param_1 & 1) == 1) { + uVar2 = param_1 >> 1; + } + uVar3 = param_2; + if ((param_1 >> 1 & 1) == 1) { + uVar3 = param_1 >> 2; + } + uVar4 = param_2; + if ((param_1 >> 2 & 1) == 1) { + uVar4 = param_1 >> 3; + } + uVar5 = param_2; + if ((param_1 >> 3 & 1) == 1) { + uVar5 = param_1 >> 4; + } + uVar6 = param_2; + if ((param_1 >> 4 & 1) == 1) { + uVar6 = param_1 >> 5; + } + uVar7 = param_2; + if ((param_1 >> 5 & 1) == 1) { + uVar7 = param_1 >> 6; + } + uVar11 = param_1 >> 7; + uVar8 = param_2; + if ((param_1 >> 6 & 1) == 1) { + uVar8 = uVar11; + } + param_1 = param_1 >> 8; + uVar9 = param_2; + if ((uVar11 & 1) == 1) { + uVar9 = param_1; + } + uVar10 = + (((((((((((((((uVar10 & 0x7fff) << 1 ^ uVar2) & 0x7fff) << 1 ^ uVar3) & 0x7fff) << 1 ^ uVar4) & 0x7fff) << 1 ^ + uVar5) & + 0x7fff) + << 1 ^ + uVar6) & + 0x7fff) + << 1 ^ + uVar7) & + 0x7fff) + << 1 ^ + uVar8) & + 0x7fff) + << 1 ^ + uVar9; + bVar1 = iVar12 != 1; + iVar12 = iVar12 + -1; + } while (bVar1); + return uVar10; +} + +uint32_t ComputeMaskedXorProduct(uint32_t param_1, uint32_t param_2, uint32_t param_3) { + return (param_3 ^ 0x780 | param_2 ^ 0x116) * ((param_1 & 0xffff) >> 8 ^ param_1 & 0xff) & 0xffff; +} + +short ShortMaskedSumAndProduct(short param_1, short param_2) { + unsigned short uVar1; + + uVar1 = param_2 + param_1 * 0x5ba & 0xff; + return (uVar1 + param_1) * (uVar1 + param_2); +} + +uint32_t MaskedBitwiseRotateMultiply(uint32_t param_1, uint32_t param_2) { + uint32_t uVar1; + + param_1 = param_1 & 0xffff; + param_2 = param_2 & 0xffff; + uVar1 = param_2 & (param_1 | 0x5ba) & 0xf; + return ((uint32_t)param_1 >> uVar1 | param_1 << (0x10 - uVar1 & 0x1f)) * + (param_2 << uVar1 | (uint32_t)param_2 >> (0x10 - uVar1 & 0x1f)) & + 0xffff; +} + +uint32_t CryptAlgo(uint32_t param_1, uint32_t param_2, uint32_t param_3) { + uint32_t uVar1; + uint32_t uVar2; + uint32_t iVar3; + uint32_t iVar4; + + uVar1 = MaskedBitwiseRotateMultiply(param_2, param_3); + uVar2 = ShortMaskedSumAndProduct(param_2, param_3); + uVar1 = ComputeMaskedXorProduct(param_1, uVar1, uVar2); + uVar2 = ComputeMaskedXorProduct(param_1, uVar2, uVar1); + iVar3 = CyclicXorHash16Bit(uVar1, 0xffc4); + iVar4 = CyclicXorHash16Bit(uVar2, 0xffc4); + return iVar4 + iVar3 * 0x10000; +} + +void decodeChallengeData(uint32_t incomingChallenge, unsigned char* solvedChallenge) { + uint32_t uVar1; + uint32_t uVar2; + + uVar1 = CryptAlgo(0x609, 0xDD2, incomingChallenge >> 0x10); + uVar2 = CryptAlgo(incomingChallenge & 0xffff, incomingChallenge >> 0x10, 0x609); + *solvedChallenge = (unsigned char)uVar1; + solvedChallenge[1] = (unsigned char)uVar2; + solvedChallenge[2] = (unsigned char)((uint32_t)uVar2 >> 8); + solvedChallenge[3] = (unsigned char)((uint32_t)uVar1 >> 8); + solvedChallenge[4] = (unsigned char)((uint32_t)uVar2 >> 16); + solvedChallenge[5] = (unsigned char)((uint32_t)uVar1 >> 16); + solvedChallenge[6] = (unsigned char)((uint32_t)uVar2 >> 24); + solvedChallenge[7] = (unsigned char)((uint32_t)uVar1 >> 24); + return; +} + void setup_battery(void) { // Performs one time setup at startup #ifdef DEBUG_VIA_USB Serial.println("Nissan LEAF battery selected"); diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.h b/Software/src/battery/NISSAN-LEAF-BATTERY.h index 89ff4443a..2d29c0d7f 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.h +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.h @@ -14,5 +14,13 @@ uint16_t Temp_fromRAW_to_F(uint16_t temperature); bool is_message_corrupt(CAN_frame rx_frame); void setup_battery(void); void transmit_can(CAN_frame* tx_frame, int interface); +void clearSOH(void); +//Cryptographic functions +void decodeChallengeData(uint32_t incomingChallenge, unsigned char* solvedChallenge); +uint32_t CyclicXorHash16Bit(uint32_t param_1, uint32_t param_2); +uint32_t ComputeMaskedXorProduct(uint32_t param_1, uint32_t param_2, uint32_t param_3); +short ShortMaskedSumAndProduct(short param_1, short param_2); +uint32_t MaskedBitwiseRotateMultiply(uint32_t param_1, uint32_t param_2); +uint32_t CryptAlgo(uint32_t param_1, uint32_t param_2, uint32_t param_3); #endif diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index e1ed206c7..fb4df944a 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -198,6 +198,18 @@ typedef struct { /** bool */ /** Heat request sent*/ bool HeaterSendRequest = false; + /** bool */ + /** User requesting SOH reset via WebUI*/ + bool UserRequestSOHreset = 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; } DATALAYER_INFO_NISSAN_LEAF; diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index 9718f61e0..40b86f01b 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -292,6 +292,10 @@ String advanced_battery_processor(const String& var) { content += "

Heating stopped: " + String(datalayer_extended.nissanleaf.HeatingStop) + "

"; content += "

Heating started: " + String(datalayer_extended.nissanleaf.HeatingStart) + "

"; content += "

Heating requested: " + String(datalayer_extended.nissanleaf.HeaterSendRequest) + "

"; + content += ""; + content += "

CryptoChallenge: " + String(datalayer_extended.nissanleaf.CryptoChallenge) + "

"; + content += "

SolvedChallenge: " + String(datalayer_extended.nissanleaf.SolvedChallengeMSB) + + String(datalayer_extended.nissanleaf.SolvedChallengeLSB) + "

"; #endif #if !defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && \ @@ -302,6 +306,15 @@ String advanced_battery_processor(const String& var) { content += "
"; content += ""; return content; diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 525682718..63cd25714 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1,6 +1,7 @@ #include "webserver.h" #include #include "../../datalayer/datalayer.h" +#include "../../datalayer/datalayer_extended.h" #include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h" #include "../utils/events.h" #include "../utils/led_handler.h" @@ -231,6 +232,15 @@ void init_webserver() { } }); + // Route for resetting SOH on Nissan LEAF batteries + server.on("/resetSOH", HTTP_GET, [](AsyncWebServerRequest* request) { + if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) { + return request->requestAuthentication(); + } + datalayer_extended.nissanleaf.UserRequestSOHreset = true; + request->send(200, "text/plain", "Updated successfully"); + }); + #ifdef TEST_FAKE_BATTERY // Route for editing FakeBatteryVoltage server.on("/updateFakeBatteryVoltage", HTTP_GET, [](AsyncWebServerRequest* request) { From a2deca0f20ade19e79ce9170c4ae079d4b63dbb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 3 Nov 2024 19:36:52 +0200 Subject: [PATCH 16/81] Added info to webserver incase challenge failed --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 3 ++- Software/src/datalayer/datalayer_extended.h | 3 +++ Software/src/devboard/webserver/advanced_battery_html.cpp | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 7c48fe375..5c39dfd93 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -354,6 +354,7 @@ void update_values_battery() { /* This function maps all the values fetched via ((solvedChallenge[7] << 24) | (solvedChallenge[6] << 16) | (solvedChallenge[5] << 8) | solvedChallenge[4]); datalayer_extended.nissanleaf.SolvedChallengeLSB = ((solvedChallenge[3] << 24) | (solvedChallenge[2] << 16) | (solvedChallenge[1] << 8) | solvedChallenge[0]); + datalayer_extended.nissanleaf.challengeFailed = challengeFailed; // Update requests from webserver datalayer if (datalayer_extended.nissanleaf.UserRequestSOHreset) { @@ -1287,7 +1288,7 @@ uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrib } void clearSOH(void) { - + challengeFailed = false; stop_battery_query = true; hold_off_with_polling_10seconds = 10; // Active battery polling is paused for 100 seconds diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index fb4df944a..626cd8dbd 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -201,6 +201,9 @@ typedef struct { /** 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; diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index 40b86f01b..d15654b8f 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -296,6 +296,7 @@ String advanced_battery_processor(const String& var) { content += "

CryptoChallenge: " + String(datalayer_extended.nissanleaf.CryptoChallenge) + "

"; content += "

SolvedChallenge: " + String(datalayer_extended.nissanleaf.SolvedChallengeMSB) + String(datalayer_extended.nissanleaf.SolvedChallengeLSB) + "

"; + content += "

Challenge failed: " + String(datalayer_extended.nissanleaf.challengeFailed) + "

"; #endif #if !defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && \ From 3a9426dcf25c75f211fbd51ee3496fd4bf49f6e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 3 Nov 2024 20:03:11 +0200 Subject: [PATCH 17/81] chage from 66 to 65 PID --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 5c39dfd93..dd0822d73 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -1325,7 +1325,7 @@ void clearSOH(void) { case 3: // Request challenge to solve LEAF_CLEAR_SOH.data.u8[0] = 0x02; LEAF_CLEAR_SOH.data.u8[1] = 0x27; - LEAF_CLEAR_SOH.data.u8[2] = 0x65; + LEAF_CLEAR_SOH.data.u8[2] = 0x65; // 0x66 on 24kWh? LEAF_CLEAR_SOH.data.u8[3] = 0x00; LEAF_CLEAR_SOH.data.u8[4] = 0x00; LEAF_CLEAR_SOH.data.u8[5] = 0x00; @@ -1339,7 +1339,7 @@ void clearSOH(void) { LEAF_CLEAR_SOH.data.u8[0] = 0x10; LEAF_CLEAR_SOH.data.u8[1] = 0x0A; LEAF_CLEAR_SOH.data.u8[2] = 0x27; - LEAF_CLEAR_SOH.data.u8[3] = 0x66; + LEAF_CLEAR_SOH.data.u8[3] = 0x65; // 0x66 on 24kWh? LEAF_CLEAR_SOH.data.u8[4] = 0x77; LEAF_CLEAR_SOH.data.u8[5] = solvedChallenge[0]; LEAF_CLEAR_SOH.data.u8[6] = solvedChallenge[1]; From 362a57b6dc88d819f2cd78873309d7dcb19c0129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 3 Nov 2024 22:22:59 +0200 Subject: [PATCH 18/81] Added logging for CAN sending --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index dd0822d73..76bf7e13b 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -1288,12 +1288,12 @@ uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrib } void clearSOH(void) { - challengeFailed = false; stop_battery_query = true; hold_off_with_polling_10seconds = 10; // Active battery polling is paused for 100 seconds switch (stateMachineClearSOH) { case 0: // Wait until polling actually stops + challengeFailed = false; stateMachineClearSOH = 1; break; case 1: // Set CAN_PROCESS_FLAG to 0xC0 @@ -1325,7 +1325,7 @@ void clearSOH(void) { case 3: // Request challenge to solve LEAF_CLEAR_SOH.data.u8[0] = 0x02; LEAF_CLEAR_SOH.data.u8[1] = 0x27; - LEAF_CLEAR_SOH.data.u8[2] = 0x65; // 0x66 on 24kWh? + LEAF_CLEAR_SOH.data.u8[2] = 0x65; LEAF_CLEAR_SOH.data.u8[3] = 0x00; LEAF_CLEAR_SOH.data.u8[4] = 0x00; LEAF_CLEAR_SOH.data.u8[5] = 0x00; @@ -1339,7 +1339,7 @@ void clearSOH(void) { LEAF_CLEAR_SOH.data.u8[0] = 0x10; LEAF_CLEAR_SOH.data.u8[1] = 0x0A; LEAF_CLEAR_SOH.data.u8[2] = 0x27; - LEAF_CLEAR_SOH.data.u8[3] = 0x65; // 0x66 on 24kWh? + LEAF_CLEAR_SOH.data.u8[3] = 0x66; LEAF_CLEAR_SOH.data.u8[4] = 0x77; LEAF_CLEAR_SOH.data.u8[5] = solvedChallenge[0]; LEAF_CLEAR_SOH.data.u8[6] = solvedChallenge[1]; @@ -1405,6 +1405,18 @@ void clearSOH(void) { default: break; } + // All CAN messages semt will be logged via serial during development of this function + Serial.print(millis()); // Example printout, time, ID, length, data: 7553 7B9 8 FF C0 B9 EA 0 0 2 5D + Serial.print(" "); + Serial.print(LEAF_CLEAR_SOH.ID, HEX); + Serial.print(" "); + Serial.print(LEAF_CLEAR_SOH.DLC); + Serial.print(" "); + for (int i = 0; i < LEAF_CLEAR_SOH.DLC; ++i) { + Serial.print(LEAF_CLEAR_SOH.data.u8[i], HEX); + Serial.print(" "); + } + Serial.println(""); } uint32_t CyclicXorHash16Bit(uint32_t param_1, uint32_t param_2) { From 0cc490481b00ab7ab9da3f403de6e96b50cda897 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sun, 3 Nov 2024 21:25:26 +0000 Subject: [PATCH 19/81] 0.3alpha - Update BMW-IX-BATTERY.h Added more values including pack min/max. min/max cell voltage is now monitored for ensure it's changing (an extra safety check in case it goes stale) --- Software/src/battery/BMW-IX-BATTERY.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.h b/Software/src/battery/BMW-IX-BATTERY.h index 46ea98203..657811c82 100644 --- a/Software/src/battery/BMW-IX-BATTERY.h +++ b/Software/src/battery/BMW-IX-BATTERY.h @@ -6,11 +6,15 @@ #define BATTERY_SELECTED //#define WUP_PIN 25 //Not used -#define MAX_PACK_VOLTAGE_DV 4600 //4600 = 460.0V +#define MAX_PACK_VOLTAGE_DV 4650 //4650 = 465.0V #define MIN_PACK_VOLTAGE_DV 3000 -#define MAX_CELL_DEVIATION_MV 500 -#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value -#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value +#define MAX_CELL_DEVIATION_MV 250 +#define MAX_CELL_VOLTAGE_MV 4300 //Battery is put into emergency stop if one cell goes over this value +#define MIN_CELL_VOLTAGE_MV 2800 //Battery is put into emergency stop if one cell goes below this value +#define MAX_CHARGE_POWER_ALLOWED_W 5000 +#define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500 +#define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00% +#define STALE_PERIOD_CONFIG 180000; //Number of milliseconds before critical values are classed as stale/stuck 180000 = 180 seconds void setup_battery(void); void transmit_can(CAN_frame* tx_frame, int interface); From f1491dbe8177173195270818710e580bab939828 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sun, 3 Nov 2024 21:26:02 +0000 Subject: [PATCH 20/81] 0.3alpha - Update BMW-IX-BATTERY.cpp Added more values including pack min/max. min/max cell voltage is now monitored for ensure it's changing (an extra safety check in case it goes stale) --- Software/src/battery/BMW-IX-BATTERY.cpp | 153 ++++++++++++++++++------ 1 file changed, 117 insertions(+), 36 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 4f4627f84..f7569277a 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -56,16 +56,11 @@ No vehicle log available, SME asks for: 0xAA (EME2) 0x?? Suspect there is a drive mode flag somewhere - balancing might only be active in some modes - - TODO - - Request batt serial number on F1 8C (already parsing RX) -- Check PWM required from ACSM -- Use voltage qualifier for extended data? +- Check PWM state required from ACSM - More Balancing values -- Check for stale min/max values -- Prevent fault state on SME reset +- MIN /max cell voltag e- implement safety limits? */ @@ -119,10 +114,12 @@ CAN_frame BMWiX_6F4_REQUEST_UPTIME = {.FD = true, .ext_ID = false, .DLC = 5, .ID CAN_frame BMWiX_6F4_REQUEST_HVIL = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x69}}; // Request HVIL State CAN_frame BMWiX_6F4_REQUEST_BALANCINGSTATUS = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE4, 0xCA}}; // Request Balancing Data CAN_frame BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x62}}; // Request allowable charge discharge amps -CAN_frame BMWiX_6F4_REQUEST_QUALIFIER_CHECK = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x4B}}; // Request HV Voltage Qualifier +CAN_frame BMWiX_6F4_REQUEST_VOLTAGE_QUALIFIER_CHECK = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x4B}}; // Request HV Voltage Qualifier CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_CLOSE = {.FD = true, .ext_ID = false, .DLC = 6, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Close - Unconfirmed CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_OPEN = {.FD = true, .ext_ID = false, .DLC = 6, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Open - Unconfirmed CAN_frame BMWiX_6F4_REQUEST_BALANCING_START = {.FD = true, .ext_ID = false, .DLC = 6, .ID = 0x6F4, .data = {0xF4, 0x04, 0x71, 0x01, 0xAE, 0x77}}; // Request Balancing command? +CAN_frame BMWiX_6F4_REQUEST_PACK_VOLTAGE_LIMITS = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x4C}}; // Request pack voltage limits + CAN_frame BMWiX_6F4_CONTINUE_DATA = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x6F4, .data = {0x07, 0x30, 0x00, 0x02}}; @@ -152,11 +149,13 @@ CAN_frame* UDS_REQUESTS100MS[] = { &BMWiX_6F4_REQUEST_EOL_ISO, &BMWiX_6F4_REQUEST_HVIL, &BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS, - &BMWiX_6F4_REQUEST_BALANCINGSTATUS + &BMWiX_6F4_REQUEST_BALANCINGSTATUS, + &BMWiX_6F4_REQUEST_PACK_VOLTAGE_LIMITS }; int numUDSreqs = sizeof(UDS_REQUESTS100MS) / sizeof(UDS_REQUESTS100MS[0]); // Number of elements in the array //iX Intermediate vars +static bool battery_info_available = false; static uint32_t battery_serial_number = 0; static int32_t battery_current = 0; static int16_t battery_voltage = 370; @@ -168,8 +167,8 @@ static int16_t max_soc_state = 50; static int16_t min_soh_state = 99;// Uses E5 45, also available in 78 73 static int16_t avg_soh_state = 99;// Uses E5 45, also available in 78 73 static int16_t max_soh_state = 99;// Uses E5 45, also available in 78 73 -static uint16_t battery_max_charge_voltage = 0; -static uint16_t battery_min_discharge_voltage = 0; +static uint16_t max_design_voltage = 0; +static uint16_t min_design_voltage = 0; static int32_t remaining_capacity = 0; static int32_t max_capacity = 0; static int16_t min_battery_temperature = 0; @@ -178,6 +177,8 @@ static int16_t max_battery_temperature = 0; static int16_t main_contactor_temperature = 0; static int16_t min_cell_voltage = 0; static int16_t max_cell_voltage = 0; +static unsigned long min_cell_voltage_lastchanged = 0; +static unsigned long max_cell_voltage_lastchanged = 0; static unsigned min_cell_voltage_lastreceived = 0; static unsigned max_cell_voltage_lastreceived = 0; static int16_t battery_power = 0; @@ -200,6 +201,9 @@ static uint8_t pyro_status_pss1 = 0; //Using AC 93 static uint8_t pyro_status_pss4 = 0; //Using AC 93 static uint8_t pyro_status_pss6 = 0; //Using AC 93 static uint8_t uds_req_id_counter = 0; +const unsigned long STALE_PERIOD = STALE_PERIOD_CONFIG ; // Time in milliseconds to check for staleness (e.g., 5000 ms = 5 seconds) + + static byte iX_0C0_counter = 0xF0; // Initialize to 0xF0 @@ -207,6 +211,22 @@ static byte iX_0C0_counter = 0xF0; // Initialize to 0xF0 static uint8_t current_cell_polled = 0; +// Function to check if a value has gone stale over a specified time period +bool isStale(int16_t currentValue, uint16_t &lastValue, unsigned long &lastChangeTime) { + unsigned long currentTime = millis(); + + // Check if the value has changed + if (currentValue != lastValue) { + // Update the last change time and value + lastChangeTime = currentTime; + lastValue = currentValue; + return false; // Value is fresh because it has changed + } + + // Check if the value has stayed the same for the specified staleness period + return (currentTime - lastChangeTime >= STALE_PERIOD); +} + static uint8_t increment_uds_req_id_counter(uint8_t index) { index++; if (index >= numUDSreqs) { @@ -246,9 +266,23 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.soh_pptt = min_soh_state; - datalayer.battery.status.max_discharge_power_W = 10000; //Aux HV Port has 100A Fuse + datalayer.battery.status.max_discharge_power_W = 3200; //10000; //Aux HV Port has 100A Fuse + + //datalayer.battery.status.max_charge_power_W = 3200; //10000; //Aux HV Port has 100A Fuse Moved to Ramping + + + // Charge power is set in .h file + if (datalayer.battery.status.real_soc > 9900) { + datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_WHEN_TOPBALANCING_W; + } else if (datalayer.battery.status.real_soc > RAMPDOWN_SOC) { + // When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0 + datalayer.battery.status.max_charge_power_W = + MAX_CHARGE_POWER_ALLOWED_W * + (1 - (datalayer.battery.status.real_soc - RAMPDOWN_SOC) / (10000.0 - RAMPDOWN_SOC)); + } else { // No limits, max charging power allowed + datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_ALLOWED_W; + } - datalayer.battery.status.max_charge_power_W = 10000; //Aux HV Port has 100A Fuse battery_power = (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100)); @@ -258,11 +292,31 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.temperature_max_dC = max_battery_temperature; - datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; - - datalayer.battery.status.cell_max_voltage_mV = max_cell_voltage; - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + + if (isStale(min_cell_voltage, datalayer.battery.status.cell_min_voltage_mV, min_cell_voltage_lastchanged)) {//TODO prevent flipflop after error + Serial.println("min_cell_voltage has gone stale."); + datalayer.battery.status.cell_min_voltage_mV = 9999; //Stale values force stop + set_event(EVENT_CAN_RX_FAILURE,0); + } else { + datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; //Value is alive + } + + if (isStale(max_cell_voltage, datalayer.battery.status.cell_max_voltage_mV, max_cell_voltage_lastchanged)) { //TODO prevent flipflop after error + Serial.println("max_cell_voltage has gone stale."); + datalayer.battery.status.cell_max_voltage_mV = 9999; //Stale values force stop + set_event(EVENT_CAN_RX_FAILURE,0); + } else { + datalayer.battery.status.cell_max_voltage_mV = max_cell_voltage; //Value is alive + } + + datalayer_extended.bmwix.min_cell_voltage_data_age = (millis() - min_cell_voltage_lastchanged); + + datalayer_extended.bmwix.max_cell_voltage_data_age = (millis() - max_cell_voltage_lastchanged); + + datalayer.battery.info.max_design_voltage_dV = max_design_voltage; + + datalayer.battery.info.min_design_voltage_dV = min_design_voltage; datalayer.battery.info.number_of_cells = 108; //init with 108S before autodetection @@ -282,7 +336,7 @@ void update_values_battery() { //This function maps all the values fetched via datalayer_extended.bmwix.iso_safety_negative = iso_safety_negative; - datalayer_extended.bmwix.iso_safety_parallel= iso_safety_parallel; + datalayer_extended.bmwix.iso_safety_parallel = iso_safety_parallel; datalayer_extended.bmwix.allowable_charge_amps = allowable_charge_amps; @@ -290,6 +344,20 @@ void update_values_battery() { //This function maps all the values fetched via datalayer_extended.bmwix.balancing_status = balancing_status; + datalayer_extended.bmwix.battery_voltage_after_contactor = battery_voltage_after_contactor; + + + + + + if (battery_info_available) { + // If we have data from battery - override the defaults to suit + datalayer.battery.info.max_design_voltage_dV = max_design_voltage; + datalayer.battery.info.min_design_voltage_dV = min_design_voltage; + datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; + datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + } + } void receive_can_battery(CAN_frame rx_frame) { @@ -366,15 +434,15 @@ void receive_can_battery(CAN_frame rx_frame) { } 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]); + 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 - battery_current = (int32_t)((rx_frame.data.u8[5] << 24) | - (rx_frame.data.u8[6] << 16) | - (rx_frame.data.u8[7] << 8) | - rx_frame.data.u8[8]); + 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 @@ -398,8 +466,8 @@ void receive_can_battery(CAN_frame rx_frame) { } 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])); - allowable_discharge_amps = (int16_t)((rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9])); + 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 @@ -424,6 +492,9 @@ void receive_can_battery(CAN_frame rx_frame) { } 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. + if((rx_frame.data.u8[6] <<8 | rx_frame.data.u8[7]) == 10000 && (rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9]) == 10000){ //Qualifier Invalid Mode - Request Reboot #ifdef DEBUG_VIA_USB Serial.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset"); @@ -447,9 +518,18 @@ void receive_can_battery(CAN_frame rx_frame) { 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[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.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; + max_design_voltage = (rx_frame.data.u8[6] <<8 | rx_frame.data.u8[7]); + min_design_voltage = (rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9]); + } + } + 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 @@ -549,24 +629,25 @@ void setup_battery(void) { // Performs one time setup at startup //Before we have started up and detected which battery is in use, use 108S values datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; + datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; + datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.system.status.battery_allows_contactor_closing = true; - //pinMode(WUP_PIN, OUTPUT); // Not needed - can hold WUP pin High with iX BMS - //digitalWrite(WUP_PIN, HIGH); // Wake up the battery // Not needed - can hold WUP pin High with iX BMS + //pinMode(WUP_PIN, OUTPUT); // Not needed - can hold WUP pin High with iX BMS + //digitalWrite(WUP_PIN, HIGH); // Wake up the battery // Not needed - can hold WUP pin High with iX BMS - //Wake Battery - //Send SME Keep alive values 100ms + + //Send SME Keep alive values 100ms transmit_can(&BMWiX_510, can_config.battery); - //Send SME Keep alive values 200ms + //Send SME Keep alive values 200ms BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 transmit_can(&BMWiX_0C0, can_config.battery); - - //Send SME Keep alive values 1000ms - //test disable transmit_can(&BMWiX_06D, can_config.battery); - //test disable transmit_can(&BMWiX_2F1, can_config.battery); - //test disable transmit_can(&BMWiX_439, can_config.battery); + //Send SME Keep alive values 1000ms + //Not needed transmit_can(&BMWiX_06D, can_config.battery); + //Not needed transmit_can(&BMWiX_2F1, can_config.battery); + //Not needed transmit_can(&BMWiX_439, can_config.battery); } #endif From ed05b7602ab35eb616a82e424c52458810f0ed0f Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sun, 3 Nov 2024 21:26:33 +0000 Subject: [PATCH 21/81] 0.3alpha - Update datalayer_extended.h Added more values including pack min/max. min/max cell voltage is now monitored for ensure it's changing (an extra safety check in case it goes stale) --- Software/src/datalayer/datalayer_extended.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 18fd8f4e3..0608b2ccb 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -22,6 +22,9 @@ typedef struct { int32_t allowable_charge_amps = 0; int32_t allowable_discharge_amps = 0; int16_t balancing_status = 0; + int16_t battery_voltage_after_contactor = 0; + unsigned long min_cell_voltage_data_age = 0; + unsigned long max_cell_voltage_data_age = 0; } DATALAYER_INFO_BMWIX; From 724efbc5c5ac0272170c04235a04da1d5a435515 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sun, 3 Nov 2024 21:27:15 +0000 Subject: [PATCH 22/81] 0.3alpha - Update advanced_battery_html.cpp Added more values including pack min/max. min/max cell voltage is now monitored for ensure it's changing (an extra safety check in case it goes stale) --- .../webserver/advanced_battery_html.cpp | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index 8a018ce7c..eff6900ee 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -18,7 +18,16 @@ String advanced_battery_processor(const String& var) { #ifdef BMW_IX_BATTERY - content += "

T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + " mV

"; + content += "

Battery Voltage after Contactor: " + String(datalayer_extended.bmwix.battery_voltage_after_contactor) + " dV

"; + content += "

Max Design Voltage: " + String(datalayer.battery.info.max_design_voltage_dV) + " dV

"; + content += "

Min Design Voltage: " + String(datalayer.battery.info.min_design_voltage_dV) + " dV

"; + content += "

Max Cell Design Voltage: " + String(datalayer.battery.info.max_cell_voltage_mV) + " mV

"; + content += "

Min Cell Design Voltage: " + String(datalayer.battery.info.min_cell_voltage_mV) + " mV

"; + content += "

Min Cell Voltage Data Age: " + String(datalayer_extended.bmwix.min_cell_voltage_data_age) + " ms

"; + content += "

Max Cell Voltage Data Age: " + String(datalayer_extended.bmwix.max_cell_voltage_data_age) + " ms

"; + content += "

Currently allowed Discharge Power: " + String(datalayer.battery.status.max_discharge_power_W) + " W

"; + content += "

Currently allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W

"; + content += "

T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + " mV

"; content += "

Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "

"; static const char* balanceText[5] = {"0 No balancing mode active", "1 Voltage-Controlled Balancing Mode", @@ -38,9 +47,15 @@ String advanced_battery_processor(const String& var) { content += "

Isolation Positive: " + String(datalayer_extended.bmwix.iso_safety_positive) + " kOhm

"; content += "

Isolation Negative: " + String(datalayer_extended.bmwix.iso_safety_negative) + " kOhm

"; content += "

Isolation Parallel: " + String(datalayer_extended.bmwix.iso_safety_parallel) + " kOhm

"; - content += "

Pyro Status PSS1: " + String(datalayer_extended.bmwix.pyro_status_pss1) + "

"; - content += "

Pyro Status PSS4: " + String(datalayer_extended.bmwix.pyro_status_pss4) + "

"; - content += "

Pyro Status PSS6: " + String(datalayer_extended.bmwix.pyro_status_pss6) + "

"; + static const char* pyroText[5] = {"0 Value Invalid", + "1 Successfully Blown", + "2 Disconnected" , + "3 Not Activated - Pyro Intact" , + "4 Unknown" + }; + content += "

Pyro Status PSS1: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss1])) + "

"; + content += "

Pyro Status PSS4: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss4])) + "

"; + content += "

Pyro Status PSS6: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss6])) + "

"; #endif //BMW_IX_BATTERY #ifdef BMW_I3_BATTERY From 1f96681e943ab2e70f1dad02826f86772d7af924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 4 Nov 2024 10:17:55 +0200 Subject: [PATCH 23/81] Replace faulty cap check with welded contactor check --- Software/src/battery/BMW-I3-BATTERY.cpp | 12 ++++++------ Software/src/devboard/utils/events.cpp | 3 +++ Software/src/devboard/utils/events.h | 3 ++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp index 7a5dde113..c02a112e8 100644 --- a/Software/src/battery/BMW-I3-BATTERY.cpp +++ b/Software/src/battery/BMW-I3-BATTERY.cpp @@ -422,10 +422,10 @@ void update_values_battery2() { //This function maps all the values fetched via } else { clear_event(EVENT_HVIL_FAILURE); } - if (battery2_status_precharge_locked == 2) { // Capacitor seated? - set_event(EVENT_PRECHARGE_FAILURE, 2); + if (battery2_status_error_disconnecting_switch > 0) { // Check if contactors are sticking / welded + set_event(EVENT_CONTACTOR_WELDED, 0); } else { - clear_event(EVENT_PRECHARGE_FAILURE); + clear_event(EVENT_CONTACTOR_WELDED); } } @@ -490,10 +490,10 @@ void update_values_battery() { //This function maps all the values fetched via } else { clear_event(EVENT_HVIL_FAILURE); } - if (battery_status_precharge_locked == 2) { // Capacitor seated? - set_event(EVENT_PRECHARGE_FAILURE, 0); + if (battery_status_error_disconnecting_switch > 0) { // Check if contactors are sticking / welded + set_event(EVENT_CONTACTOR_WELDED, 0); } else { - clear_event(EVENT_PRECHARGE_FAILURE); + clear_event(EVENT_CONTACTOR_WELDED); } // Update webserver datalayer diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index 8bdf710ba..795848852 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -150,6 +150,7 @@ void init_events(void) { events.entries[EVENT_CAN_RX_WARNING].level = EVENT_LEVEL_WARNING; events.entries[EVENT_CAN_TX_FAILURE].level = EVENT_LEVEL_ERROR; events.entries[EVENT_CAN_INVERTER_MISSING].level = EVENT_LEVEL_WARNING; + events.entries[EVENT_CONTACTOR_WELDED].level = EVENT_LEVEL_WARNING; events.entries[EVENT_WATER_INGRESS].level = EVENT_LEVEL_ERROR; events.entries[EVENT_CHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO; events.entries[EVENT_DISCHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO; @@ -281,6 +282,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) { return "ERROR: CAN messages failed to transmit, or no one on the bus to ACK the message!"; case EVENT_CAN_INVERTER_MISSING: return "Warning: Inverter not sending messages on CAN bus. Check wiring!"; + case EVENT_CONTACTOR_WELDED: + return "Warning: Contactors sticking/welded. Inspect battery with caution!"; case EVENT_CHARGE_LIMIT_EXCEEDED: return "Info: Inverter is charging faster than battery is allowing."; case EVENT_DISCHARGE_LIMIT_EXCEEDED: diff --git a/Software/src/devboard/utils/events.h b/Software/src/devboard/utils/events.h index 157d05958..dcdcfd6a7 100644 --- a/Software/src/devboard/utils/events.h +++ b/Software/src/devboard/utils/events.h @@ -6,7 +6,7 @@ // #define INCLUDE_EVENTS_TEST // Enable to run an event test loop, see events_test_on_target.cpp -#define EE_MAGIC_HEADER_VALUE 0x0016 // 0x0000 to 0xFFFF +#define EE_MAGIC_HEADER_VALUE 0x0017 // 0x0000 to 0xFFFF #define GENERATE_ENUM(ENUM) ENUM, #define GENERATE_STRING(STRING) #STRING, @@ -37,6 +37,7 @@ XX(EVENT_CAN_TX_FAILURE) \ XX(EVENT_CAN_INVERTER_MISSING) \ XX(EVENT_CHARGE_LIMIT_EXCEEDED) \ + XX(EVENT_CONTACTOR_WELDED) \ XX(EVENT_DISCHARGE_LIMIT_EXCEEDED) \ XX(EVENT_WATER_INGRESS) \ XX(EVENT_12V_LOW) \ From 683e576d9957009b35761ae72bd02e9c32006ce3 Mon Sep 17 00:00:00 2001 From: amarofarinha <151563493+amarofarinha@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:33:32 +0000 Subject: [PATCH 24/81] Add Support for Double-Battery in MQTT Integration --- Software/src/devboard/mqtt/mqtt.cpp | 174 +++++++++++++++++++++------- 1 file changed, 134 insertions(+), 40 deletions(-) diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index df0863d6c..95f5444a7 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -66,15 +66,35 @@ SensorConfig sensorConfigs[] = { {"max_charge_power", "Battery Max Charge Power", "{{ value_json.max_charge_power }}", "W", "power"}, {"bms_status", "BMS Status", "{{ value_json.bms_status }}", "", ""}, {"pause_status", "Pause Status", "{{ value_json.pause_status }}", "", ""}, - +#ifdef DOUBLE_BATTERY + {"SOC_2", "SOC 2 (scaled)", "{{ value_json.SOC_2 }}", "%", "battery"}, + {"SOC_real_2", "SOC 2 (real)", "{{ value_json.SOC_real_2 }}", "%", "battery"}, + {"state_of_health_2", "State Of Health 2", "{{ value_json.state_of_health_2 }}", "%", "battery"}, + {"temperature_min_2", "Temperature Min 2", "{{ value_json.temperature_min_2 }}", "°C", "temperature"}, + {"temperature_max_2", "Temperature Max 2", "{{ value_json.temperature_max_2 }}", "°C", "temperature"}, + {"stat_batt_power_2", "Stat Batt Power 2", "{{ value_json.stat_batt_power_2 }}", "W", "power"}, + {"battery_current_2", "Battery 2 Current", "{{ value_json.battery_current_2 }}", "A", "current"}, + {"cell_max_voltage_2", "Cell Max Voltage 2", "{{ value_json.cell_max_voltage_2 }}", "V", "voltage"}, + {"cell_min_voltage_2", "Cell Min Voltage 2", "{{ value_json.cell_min_voltage_2 }}", "V", "voltage"}, + {"battery_voltage_2", "Battery 2 Voltage", "{{ value_json.battery_voltage_2 }}", "V", "voltage"}, + {"total_capacity_2", "Battery 2 Total Capacity", "{{ value_json.total_capacity_2 }}", "Wh", "energy"}, + {"remaining_capacity_2", "Battery 2 Remaining Capacity (scaled)", "{{ value_json.remaining_capacity_2 }}", "Wh", + "energy"}, + {"remaining_capacity_real_2", "Battery 2 Remaining Capacity (real)", "{{ value_json.remaining_capacity_real_2 }}", + "Wh", "energy"}, + {"max_discharge_power_2", "Battery 2 Max Discharge Power", "{{ value_json.max_discharge_power_2 }}", "W", "power"}, + {"max_charge_power_2", "Battery 2 Max Charge Power", "{{ value_json.max_charge_power_2 }}", "W", "power"}, + {"bms_status_2", "BMS 2 Status", "{{ value_json.bms_status_2 }}", "", ""}, + {"pause_status_2", "Pause Status 2", "{{ value_json.pause_status_2 }}", "", ""}, +#endif // DOUBLE_BATTERY }; static String generateCommonInfoAutoConfigTopic(const char* object_id) { return "homeassistant/sensor/" + topic_name + "/" + String(object_id) + "/config"; } -static String generateCellVoltageAutoConfigTopic(int cell_number) { - return "homeassistant/sensor/" + topic_name + "/cell_voltage" + String(cell_number) + "/config"; +static String generateCellVoltageAutoConfigTopic(int cell_number, String battery_suffix) { + return "homeassistant/sensor/" + topic_name + "/cell_voltage" + battery_suffix + String(cell_number) + "/config"; } static String generateEventsAutoConfigTopic(const char* object_id) { @@ -148,7 +168,30 @@ static void publish_common_info(void) { doc["max_discharge_power"] = ((float)datalayer.battery.status.max_discharge_power_W); doc["max_charge_power"] = ((float)datalayer.battery.status.max_charge_power_W); } - +#ifdef DOUBLE_BATTERY + //only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery) + if (datalayer.battery2.status.CAN_battery_still_alive && allowed_to_send_CAN && millis() > BOOTUP_TIME) { + doc["SOC_2"] = ((float)datalayer.battery2.status.reported_soc) / 100.0; + doc["SOC_real_2"] = ((float)datalayer.battery2.status.real_soc) / 100.0; + doc["state_of_health_2"] = ((float)datalayer.battery2.status.soh_pptt) / 100.0; + doc["temperature_min_2"] = ((float)((int16_t)datalayer.battery2.status.temperature_min_dC)) / 10.0; + doc["temperature_max_2"] = ((float)((int16_t)datalayer.battery2.status.temperature_max_dC)) / 10.0; + doc["stat_batt_power_2"] = ((float)((int32_t)datalayer.battery2.status.active_power_W)); + doc["battery_current_2"] = ((float)((int16_t)datalayer.battery2.status.current_dA)) / 10.0; + doc["battery_voltage_2"] = ((float)datalayer.battery2.status.voltage_dV) / 10.0; + // publish only if cell voltages have been populated... + if (datalayer.battery2.info.number_of_cells != 0u && + datalayer.battery2.status.cell_voltages_mV[datalayer.battery2.info.number_of_cells - 1] != 0u) { + doc["cell_max_voltage_2"] = ((float)datalayer.battery2.status.cell_max_voltage_mV) / 1000.0; + doc["cell_min_voltage_2"] = ((float)datalayer.battery2.status.cell_min_voltage_mV) / 1000.0; + } + doc["total_capacity_2"] = ((float)datalayer.battery2.info.total_capacity_Wh); + doc["remaining_capacity_real_2"] = ((float)datalayer.battery2.status.remaining_capacity_Wh); + doc["remaining_capacity_2"] = ((float)datalayer.battery2.status.reported_remaining_capacity_Wh); + doc["max_discharge_power_2"] = ((float)datalayer.battery2.status.max_discharge_power_W); + doc["max_charge_power_2"] = ((float)datalayer.battery2.status.max_charge_power_W); + } +#endif // DOUBLE_BATTERY serializeJson(doc, mqtt_msg); if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) { #ifdef DEBUG_VIA_USB @@ -167,47 +210,79 @@ static void publish_cell_voltages(void) { #endif // HA_AUTODISCOVERY static JsonDocument doc; static String state_topic = topic_name + "/spec_data"; +#ifdef DOUBLE_BATTERY + static String state_topic_2 = topic_name + "/spec_data_2"; + +#endif // DOUBLE_BATTERY - // If the cell voltage number isn't initialized... - if (datalayer.battery.info.number_of_cells == 0u) { - return; - } #ifdef HA_AUTODISCOVERY if (mqtt_first_transmission == true) { mqtt_first_transmission = false; - String topic = "homeassistant/sensor/battery-emulator/cell_voltage"; - - for (int i = 0; i < datalayer.battery.info.number_of_cells; i++) { - int cellNumber = i + 1; - doc["name"] = "Battery Cell Voltage " + String(cellNumber); - doc["object_id"] = object_id_prefix + "battery_voltage_cell" + String(cellNumber); - doc["unique_id"] = topic_name + "_battery_voltage_cell" + String(cellNumber); - doc["device_class"] = "voltage"; - doc["state_class"] = "measurement"; - doc["state_topic"] = state_topic; - doc["unit_of_measurement"] = "V"; - doc["enabled_by_default"] = true; - doc["expire_after"] = 240; - doc["value_template"] = "{{ value_json.cell_voltages[" + String(i) + "] }}"; - doc["device"]["identifiers"][0] = "battery-emulator"; - doc["device"]["manufacturer"] = "DalaTech"; - doc["device"]["model"] = "BatteryEmulator"; - doc["device"]["name"] = device_name; - doc["origin"]["name"] = "BatteryEmulator"; - doc["origin"]["sw"] = String(version_number) + "-mqtt"; - doc["origin"]["url"] = "https://github.com/dalathegreat/Battery-Emulator"; - serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); - mqtt_publish(generateCellVoltageAutoConfigTopic(cellNumber).c_str(), mqtt_msg, true); + // If the cell voltage number isn't initialized... + if (datalayer.battery.info.number_of_cells != 0u) { + + for (int i = 0; i < datalayer.battery.info.number_of_cells; i++) { + int cellNumber = i + 1; + doc["name"] = "Battery Cell Voltage " + String(cellNumber); + doc["object_id"] = object_id_prefix + "battery_voltage_cell" + String(cellNumber); + doc["unique_id"] = topic_name + "_battery_voltage_cell" + String(cellNumber); + doc["device_class"] = "voltage"; + doc["state_class"] = "measurement"; + doc["state_topic"] = state_topic; + doc["unit_of_measurement"] = "V"; + doc["enabled_by_default"] = true; + doc["expire_after"] = 240; + doc["value_template"] = "{{ value_json.cell_voltages[" + String(i) + "] }}"; + doc["device"]["identifiers"][0] = "battery-emulator"; + doc["device"]["manufacturer"] = "DalaTech"; + doc["device"]["model"] = "BatteryEmulator"; + doc["device"]["name"] = device_name; + doc["origin"]["name"] = "BatteryEmulator"; + doc["origin"]["sw"] = String(version_number) + "-mqtt"; + doc["origin"]["url"] = "https://github.com/dalathegreat/Battery-Emulator"; + + serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); + mqtt_publish(generateCellVoltageAutoConfigTopic(cellNumber, "").c_str(), mqtt_msg, true); + } + doc.clear(); // clear after sending autoconfig } - doc.clear(); // clear after sending autoconfig - } else { -#endif // HA_AUTODISCOVERY - // If cell voltages haven't been populated... - if (datalayer.battery.info.number_of_cells == 0u || - datalayer.battery.status.cell_voltages_mV[datalayer.battery.info.number_of_cells - 1] == 0u) { - return; +#ifdef DOUBLE_BATTERY + // If the cell voltage number isn't initialized... + if (datalayer.battery2.info.number_of_cells != 0u) { + + for (int i = 0; i < datalayer.battery.info.number_of_cells; i++) { + int cellNumber = i + 1; + doc["name"] = "Battery 2 Cell Voltage " + String(cellNumber); + doc["object_id"] = object_id_prefix + "battery_2_voltage_cell" + String(cellNumber); + doc["unique_id"] = topic_name + "_battery_2_voltage_cell" + String(cellNumber); + doc["device_class"] = "voltage"; + doc["state_class"] = "measurement"; + doc["state_topic"] = state_topic_2; + doc["unit_of_measurement"] = "V"; + doc["enabled_by_default"] = true; + doc["expire_after"] = 240; + doc["value_template"] = "{{ value_json.cell_voltages[" + String(i) + "] }}"; + doc["device"]["identifiers"][0] = "battery-emulator"; + doc["device"]["manufacturer"] = "DalaTech"; + doc["device"]["model"] = "BatteryEmulator"; + doc["device"]["name"] = device_name; + doc["origin"]["name"] = "BatteryEmulator"; + doc["origin"]["sw"] = String(version_number) + "-mqtt"; + doc["origin"]["url"] = "https://github.com/dalathegreat/Battery-Emulator"; + + serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); + mqtt_publish(generateCellVoltageAutoConfigTopic(cellNumber, "_2_").c_str(), mqtt_msg, true); + } + doc.clear(); // clear after sending autoconfig } +#endif // DOUBLE_BATTERY + } +#endif // HA_AUTODISCOVERY + + // If cell voltages have been populated... + if (datalayer.battery.info.number_of_cells != 0u && + datalayer.battery.status.cell_voltages_mV[datalayer.battery.info.number_of_cells - 1] != 0u) { JsonArray cell_voltages = doc["cell_voltages"].to(); for (size_t i = 0; i < datalayer.battery.info.number_of_cells; ++i) { @@ -222,9 +297,28 @@ static void publish_cell_voltages(void) { #endif // DEBUG_VIA_USB } doc.clear(); -#ifdef HA_AUTODISCOVERY } -#endif // HA_AUTODISCOVERY + +#ifdef DOUBLE_BATTERY + // If cell voltages have been populated... + if (datalayer.battery2.info.number_of_cells != 0u && + datalayer.battery2.status.cell_voltages_mV[datalayer.battery2.info.number_of_cells - 1] != 0u) { + + JsonArray cell_voltages = doc["cell_voltages"].to(); + for (size_t i = 0; i < datalayer.battery2.info.number_of_cells; ++i) { + cell_voltages.add(((float)datalayer.battery2.status.cell_voltages_mV[i]) / 1000.0); + } + + serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); + + if (!mqtt_publish(state_topic_2.c_str(), mqtt_msg, false)) { +#ifdef DEBUG_VIA_USB + Serial.println("Cell voltage MQTT msg could not be sent"); +#endif // DEBUG_VIA_USB + } + doc.clear(); + } +#endif // DOUBLE_BATTERY } void publish_events() { From deb7a46d16beb929eb90adc7e6e87fe998e6d4f0 Mon Sep 17 00:00:00 2001 From: amarofarinha <151563493+amarofarinha@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:21:36 +0000 Subject: [PATCH 25/81] add double battery support for TEST-FAKE-BATTERY --- Software/src/battery/TEST-FAKE-BATTERY.cpp | 96 +++++++++++++++++++--- 1 file changed, 84 insertions(+), 12 deletions(-) diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index 82249ad20..0faff90b1 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -71,21 +71,93 @@ void update_values_battery() { /* This function puts fake values onto the parame #endif } -void receive_can_battery(CAN_frame rx_frame) { - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - // All CAN messages recieved will be logged via serial - Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D - Serial.print(" "); - Serial.print(rx_frame.ID, HEX); - Serial.print(" "); - Serial.print(rx_frame.DLC); - Serial.print(" "); - for (int i = 0; i < rx_frame.DLC; ++i) { - Serial.print(rx_frame.data.u8[i], HEX); - Serial.print(" "); +#ifdef DOUBLE_BATTERY + +void update_values_battery2() { // Handle the values coming in from battery #2 + + datalayer.battery2.status.real_soc = 5000; // 50.00% + + datalayer.battery2.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.battery2.status.current_dA = 0; // 0 A + + datalayer.battery2.info.total_capacity_Wh = 30000; // 30kWh + + datalayer.battery2.status.remaining_capacity_Wh = 15000; // 15kWh + + datalayer.battery2.status.cell_max_voltage_mV = 3596; + + datalayer.battery2.status.cell_min_voltage_mV = 3500; + + datalayer.battery2.status.active_power_W = 0; // 0W + + datalayer.battery2.status.temperature_min_dC = 50; // 5.0*C + + datalayer.battery2.status.temperature_max_dC = 60; // 6.0*C + + datalayer.battery2.status.max_discharge_power_W = 5000; // 5kW + + datalayer.battery2.status.max_charge_power_W = 5000; // 5kW + + for (int i = 0; i < 97; ++i) { + datalayer.battery2.status.cell_voltages_mV[i] = 3500 + i; } + + //Fake that we get CAN messages + datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + +/*Finally print out values to serial if configured to do so*/ +#ifdef DEBUG_VIA_USB + Serial.println("FAKE Values battery 2 going to inverter"); + print_units("SOH 2 %: ", (datalayer.battery2.status.soh_pptt * 0.01), "% "); + print_units(", SOC 2 %: ", (datalayer.battery2.status.reported_soc * 0.01), "% "); + print_units(", Voltage 2: ", (datalayer.battery2.status.voltage_dV * 0.1), "V "); + print_units(", Max discharge power 2: ", datalayer.battery2.status.max_discharge_power_W, "W "); + print_units(", Max charge power 2: ", datalayer.battery2.status.max_charge_power_W, "W "); + print_units(", Max temp 2: ", (datalayer.battery2.status.temperature_max_dC * 0.1), "°C "); + print_units(", Min temp 2: ", (datalayer.battery2.status.temperature_min_dC * 0.1), "°C "); + print_units(", Max cell voltage 2: ", datalayer.battery2.status.cell_max_voltage_mV, "mV "); + print_units(", Min cell voltage 2: ", datalayer.battery2.status.cell_min_voltage_mV, "mV "); Serial.println(""); +#endif + } + + void receive_can_battery2(CAN_frame rx_frame) { + datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + // All CAN messages recieved will be logged via serial + Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D + Serial.print(" "); + Serial.print(rx_frame.ID, HEX); + Serial.print(" "); + Serial.print(rx_frame.DLC); + Serial.print(" "); + for (int i = 0; i < rx_frame.DLC; ++i) { + Serial.print(rx_frame.data.u8[i], HEX); + Serial.print(" "); + } + Serial.println(""); + } +#endif // DOUBLE_BATTERY + + + void receive_can_battery(CAN_frame rx_frame) { + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + // All CAN messages recieved will be logged via serial + Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D + Serial.print(" "); + Serial.print(rx_frame.ID, HEX); + Serial.print(" "); + Serial.print(rx_frame.DLC); + Serial.print(" "); + for (int i = 0; i < rx_frame.DLC; ++i) { + Serial.print(rx_frame.data.u8[i], HEX); + Serial.print(" "); + } + Serial.println(""); + } void send_can_battery() { unsigned long currentMillis = millis(); // Send 100ms CAN Message From 56fb9616b78c47de25b39db70d4231ca73d17fa1 Mon Sep 17 00:00:00 2001 From: amarofarinha <151563493+amarofarinha@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:35:21 +0000 Subject: [PATCH 26/81] pre-commit fix --- Software/src/battery/TEST-FAKE-BATTERY.cpp | 62 +++++++++++----------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index 0faff90b1..6e52bd707 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -122,42 +122,40 @@ void update_values_battery2() { // Handle the values coming in from battery #2 print_units(", Min cell voltage 2: ", datalayer.battery2.status.cell_min_voltage_mV, "mV "); Serial.println(""); #endif - } - void receive_can_battery2(CAN_frame rx_frame) { - datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - // All CAN messages recieved will be logged via serial - Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D - Serial.print(" "); - Serial.print(rx_frame.ID, HEX); - Serial.print(" "); - Serial.print(rx_frame.DLC); - Serial.print(" "); - for (int i = 0; i < rx_frame.DLC; ++i) { - Serial.print(rx_frame.data.u8[i], HEX); - Serial.print(" "); - } - Serial.println(""); +void receive_can_battery2(CAN_frame rx_frame) { + datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + // All CAN messages recieved will be logged via serial + Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D + Serial.print(" "); + Serial.print(rx_frame.ID, HEX); + Serial.print(" "); + Serial.print(rx_frame.DLC); + Serial.print(" "); + for (int i = 0; i < rx_frame.DLC; ++i) { + Serial.print(rx_frame.data.u8[i], HEX); + Serial.print(" "); } -#endif // DOUBLE_BATTERY - - - void receive_can_battery(CAN_frame rx_frame) { - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - // All CAN messages recieved will be logged via serial - Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D - Serial.print(" "); - Serial.print(rx_frame.ID, HEX); - Serial.print(" "); - Serial.print(rx_frame.DLC); - Serial.print(" "); - for (int i = 0; i < rx_frame.DLC; ++i) { - Serial.print(rx_frame.data.u8[i], HEX); - Serial.print(" "); - } - Serial.println(""); + Serial.println(""); +} +#endif // DOUBLE_BATTERY + +void receive_can_battery(CAN_frame rx_frame) { + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + // All CAN messages recieved will be logged via serial + Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D + Serial.print(" "); + Serial.print(rx_frame.ID, HEX); + Serial.print(" "); + Serial.print(rx_frame.DLC); + Serial.print(" "); + for (int i = 0; i < rx_frame.DLC; ++i) { + Serial.print(rx_frame.data.u8[i], HEX); + Serial.print(" "); } + Serial.println(""); +} void send_can_battery() { unsigned long currentMillis = millis(); // Send 100ms CAN Message From 9051293c43887253f7f8fec256e9bc7460c7864a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 6 Nov 2024 15:57:20 +0200 Subject: [PATCH 27/81] Update BYD FW to 3.29 --- Software/src/inverter/BYD-CAN.cpp | 14 +++++++------- Software/src/inverter/BYD-CAN.h | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Software/src/inverter/BYD-CAN.cpp b/Software/src/inverter/BYD-CAN.cpp index 825274696..a2cf2d44a 100644 --- a/Software/src/inverter/BYD-CAN.cpp +++ b/Software/src/inverter/BYD-CAN.cpp @@ -15,13 +15,13 @@ static uint8_t char5_151 = 0; static uint8_t char6_151 = 0; static uint8_t char7_151 = 0; -CAN_frame BYD_250 = { - .FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x250, - .data = {0x03, 0x16, 0x00, 0x66, (uint8_t)((BATTERY_WH_MAX / 100) >> 8), (uint8_t)(BATTERY_WH_MAX / 100), 0x02, - 0x09}}; //3.16 FW , Capacity kWh byte4&5 (example 24kWh = 240) +CAN_frame BYD_250 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x250, + .data = {FW_MAJOR_VERSION, FW_MINOR_VERSION, 0x00, 0x66, (uint8_t)((BATTERY_WH_MAX / 100) >> 8), + (uint8_t)(BATTERY_WH_MAX / 100), 0x02, + 0x09}}; //0-1 FW version , Capacity kWh byte4&5 (example 24kWh = 240) CAN_frame BYD_290 = {.FD = false, .ext_ID = false, .DLC = 8, diff --git a/Software/src/inverter/BYD-CAN.h b/Software/src/inverter/BYD-CAN.h index 198f10bb9..6a45317b9 100644 --- a/Software/src/inverter/BYD-CAN.h +++ b/Software/src/inverter/BYD-CAN.h @@ -3,6 +3,8 @@ #include "../include.h" #define CAN_INVERTER_SELECTED +#define FW_MAJOR_VERSION 0x03 +#define FW_MINOR_VERSION 0x29 void send_intial_data(); void transmit_can(CAN_frame* tx_frame, int interface); From f68539e9e29cb7f32a05a8531d409b4d60b78aaf Mon Sep 17 00:00:00 2001 From: amarofarinha <151563493+amarofarinha@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:34:29 +0000 Subject: [PATCH 28/81] Fake Battery and Web Server Adjustments --- Software/Software.ino | 23 ++++++++++-- Software/src/battery/TEST-FAKE-BATTERY.cpp | 10 ++++-- Software/src/devboard/webserver/webserver.cpp | 35 +++++++++++++++---- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index 588aa5e07..588f28cf0 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -860,6 +860,8 @@ void update_scaled_values() { * Before we use real_soc, we must make sure that it's within the range of min_percentage and max_percentage. */ uint32_t calc_soc; + uint32_t calc_max_capacity; + uint32_t calc_reserved_capacity; // Make sure that the SOC starts out between min and max percentages calc_soc = CONSTRAIN(datalayer.battery.status.real_soc, datalayer.battery.settings.min_percentage, datalayer.battery.settings.max_percentage); @@ -870,8 +872,6 @@ void update_scaled_values() { // Calculate the scaled remaining capacity in Wh if (datalayer.battery.info.total_capacity_Wh > 0 && datalayer.battery.status.real_soc > 0) { - uint32_t calc_max_capacity; - uint32_t calc_reserved_capacity; calc_max_capacity = (datalayer.battery.status.remaining_capacity_Wh * 10000 / datalayer.battery.status.real_soc); calc_reserved_capacity = calc_max_capacity * datalayer.battery.settings.min_percentage / 10000; // remove % capacity reserved in min_percentage to total_capacity_Wh @@ -881,9 +881,28 @@ void update_scaled_values() { datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh; } +#ifdef DOUBLE_BATTERY + + // Calculate the scaled remaining capacity in Wh + if (datalayer.battery2.info.total_capacity_Wh > 0 && datalayer.battery2.status.real_soc > 0) { + calc_max_capacity = + (datalayer.battery2.status.remaining_capacity_Wh * 10000 / datalayer.battery2.status.real_soc); + calc_reserved_capacity = calc_max_capacity * datalayer.battery2.settings.min_percentage / 10000; + // remove % capacity reserved in min_percentage to total_capacity_Wh + datalayer.battery2.status.reported_remaining_capacity_Wh = + datalayer.battery2.status.remaining_capacity_Wh - calc_reserved_capacity; + } else { + datalayer.battery2.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh; + } +#endif + } else { // No SOC window wanted. Set scaled to same as real. datalayer.battery.status.reported_soc = datalayer.battery.status.real_soc; datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh; +#ifdef DOUBLE_BATTERY + datalayer.battery2.status.reported_soc = datalayer.battery2.status.real_soc; + datalayer.battery2.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh; +#endif } #ifdef DOUBLE_BATTERY // Perform extra SOC sanity checks on double battery setups diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index 6e52bd707..630fa5360 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -22,6 +22,8 @@ void print_units(char* header, int value, char* units) { void update_values_battery() { /* This function puts fake values onto the parameters sent towards the inverter */ + datalayer.battery.info.number_of_cells = 96; + datalayer.battery.status.real_soc = 5000; // 50.00% datalayer.battery.status.soh_pptt = 9900; // 99.00% @@ -49,7 +51,7 @@ void update_values_battery() { /* This function puts fake values onto the parame datalayer.battery.status.max_charge_power_W = 5000; // 5kW for (int i = 0; i < 97; ++i) { - datalayer.battery.status.cell_voltages_mV[i] = 3500 + i; + datalayer.battery.status.cell_voltages_mV[i] = 3700 + random(-20, 21); } //Fake that we get CAN messages @@ -75,6 +77,8 @@ void update_values_battery() { /* This function puts fake values onto the parame void update_values_battery2() { // Handle the values coming in from battery #2 + datalayer.battery2.info.number_of_cells = 96; + datalayer.battery2.status.real_soc = 5000; // 50.00% datalayer.battery2.status.soh_pptt = 9900; // 99.00% @@ -102,7 +106,7 @@ void update_values_battery2() { // Handle the values coming in from battery #2 datalayer.battery2.status.max_charge_power_W = 5000; // 5kW for (int i = 0; i < 97; ++i) { - datalayer.battery2.status.cell_voltages_mV[i] = 3500 + i; + datalayer.battery2.status.cell_voltages_mV[i] = 3700 + random(-20, 21); } //Fake that we get CAN messages @@ -167,6 +171,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup + randomSeed(analogRead(0)); + #ifdef DEBUG_VIA_USB Serial.println("Test mode with fake battery selected"); #endif diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 274f2bf06..3f6bfbf5d 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -785,7 +785,9 @@ String processor(const String& var) { content += "

Current: " + String(currentFloat, 1) + " A

"; content += formatPowerValue("Power", powerFloat, "", 1); content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 0); - content += formatPowerValue("Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1); + content += formatPowerValue("Real Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1); + content += + formatPowerValue("Scaled Remaining capacity", datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1); content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1); content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1); content += "

Cell max: " + String(datalayer.battery2.status.cell_max_voltage_mV) + " mV

"; @@ -827,6 +829,11 @@ String processor(const String& var) { content += ""; } + if (emulator_pause_status == NORMAL) + content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; + else + content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; + #ifdef CONTACTOR_CONTROL content += "

Contactors controlled by Battery-Emulator: "; if (datalayer.system.status.contactor_control_closed) { @@ -835,12 +842,28 @@ String processor(const String& var) { content += "OFF"; } content += "

"; -#endif - if (emulator_pause_status == NORMAL) - content += "

Pause status: " + String(get_emulator_pause_status().c_str()) + "

"; - else - content += "

Pause status: " + String(get_emulator_pause_status().c_str()) + "

"; + content += "

Pre Charge: "; + if (digitalRead(PRECHARGE_PIN) == HIGH) { + content += ""; + } else { + content += ""; + } + content += " Cont. Neg.: "; + if (digitalRead(NEGATIVE_CONTACTOR_PIN) == HIGH) { + content += ""; + } else { + content += ""; + } + + content += " Cont. Pos.: "; + if (digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH) { + content += ""; + } else { + content += ""; + } + content += "

"; +#endif content += ""; content += ""; From e293d5dcce9145a6650080fee82870b046fe6e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 8 Nov 2024 10:11:31 +0200 Subject: [PATCH 29/81] Add initial HAL for v0.1 3LB --- Software/USER_SETTINGS.h | 1 + Software/src/devboard/hal/hal.h | 4 +- Software/src/devboard/hal/hw_3LB.h | 106 ++++++++++++++++++ Software/src/devboard/webserver/webserver.cpp | 3 + 4 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 Software/src/devboard/hal/hw_3LB.h diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 242c1264f..8085999fd 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -49,6 +49,7 @@ /* Select hardware used for Battery-Emulator */ #define HW_LILYGO //#define HW_STARK +//#define HW_3LB /* Other options */ //#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs (WARNING, raises CPU load, do not use for production) diff --git a/Software/src/devboard/hal/hal.h b/Software/src/devboard/hal/hal.h index 5e904d89c..dc3240cef 100644 --- a/Software/src/devboard/hal/hal.h +++ b/Software/src/devboard/hal/hal.h @@ -7,8 +7,8 @@ #include "hw_lilygo.h" #elif defined(HW_STARK) #include "hw_stark.h" -#elif defined(HW_SJB_V1) -#include "hw_sjb_v1.h" +#elif defined(HW_3LB) +#include "hw_3LB.h" #endif #endif diff --git a/Software/src/devboard/hal/hw_3LB.h b/Software/src/devboard/hal/hw_3LB.h new file mode 100644 index 000000000..137d83f38 --- /dev/null +++ b/Software/src/devboard/hal/hw_3LB.h @@ -0,0 +1,106 @@ +#ifndef __HW_3LB_H__ +#define __HW_3LB_H__ + +// Board boot-up time +#define BOOTUP_TIME 1000 // Time in ms it takes before system is considered fully started up + +// Core assignment +#define CORE_FUNCTION_CORE 1 +#define MODBUS_CORE 0 +#define WIFI_CORE 0 + +// RS485 +//#define PIN_5V_EN 16 +//#define RS485_EN_PIN 17 // 17 /RE +#define RS485_TX_PIN 1 // 21 +#define RS485_RX_PIN 3 // 22 +//#define RS485_SE_PIN 19 // 22 /SHDN + +// CAN settings. CAN_2 is not defined as it can be either MCP2515 or MCP2517, defined by the user settings +#define CAN_1_TYPE ESP32CAN + +// CAN1 PIN mappings, do not change these unless you are adding on extra hardware to the PCB +#define CAN_TX_PIN GPIO_NUM_27 +#define CAN_RX_PIN GPIO_NUM_26 +//#define CAN_SE_PIN 23 + +// CAN2 defines below + +// DUAL_CAN defines +#define MCP2515_SCK 12 // SCK input of MCP2515 +#define MCP2515_MOSI 5 // SDI input of MCP2515 +#define MCP2515_MISO 34 // SDO output of MCP2515 | Pin 34 is input only, without pullup/down resistors +#define MCP2515_CS 18 // CS input of MCP2515 +#define MCP2515_INT 35 // INT output of MCP2515 | | Pin 35 is input only, without pullup/down resistors + +// CAN_FD defines +#define MCP2517_SCK 17 // SCK input of MCP2517 +#define MCP2517_SDI 23 // SDI input of MCP2517 +#define MCP2517_SDO 39 // SDO output of MCP2517 +#define MCP2517_CS 21 // CS input of MCP2517 //21 or 22 +#define MCP2517_INT 34 // INT output of MCP2517 //34 or 35 + +// CHAdeMO support pin dependencies +#define CHADEMO_PIN_2 12 +#define CHADEMO_PIN_10 5 +#define CHADEMO_PIN_7 34 +#define CHADEMO_PIN_4 35 +#define CHADEMO_LOCK 18 + +// Contactor handling +#define POSITIVE_CONTACTOR_PIN 32 +#define NEGATIVE_CONTACTOR_PIN 33 +#define PRECHARGE_PIN 25 + +#define 2ND_POSITIVE_CONTACTOR_PIN 13 +#define 2ND_NEGATIVE_CONTACTOR_PIN 16 +#define 2ND_PRECHARGE_PIN 18 + +// SMA CAN contactor pins +#define INVERTER_CONTACTOR_ENABLE_PIN 36 + +// SD card +//#define SD_MISO_PIN 2 +//#define SD_MOSI_PIN 15 +//#define SD_SCLK_PIN 14 +//#define SD_CS_PIN 13 + +// LED +#define LED_PIN 4 +#define LED_MAX_BRIGHTNESS 40 + +// Equipment stop pin +#define EQUIPMENT_STOP_PIN 35 + +/* ----- Error checks below, don't change (can't be moved to separate file) ----- */ +#ifndef HW_CONFIGURED +#define HW_CONFIGURED +#else +#error Multiple HW defined! Please select a single HW +#endif + +#ifdef CHADEMO_BATTERY +#ifdef DUAL_CAN +#error CHADEMO and DUAL_CAN cannot coexist due to overlapping GPIO pin usage +#endif +#endif + +#ifdef EQUIPMENT_STOP_BUTTON +#ifdef DUAL_CAN +#error EQUIPMENT_STOP_BUTTON and DUAL_CAN cannot coexist due to overlapping GPIO pin usage +#endif +#ifdef CAN_FD +#error EQUIPMENT_STOP_BUTTON and CAN_FD cannot coexist due to overlapping GPIO pin usage +#endif +#ifdef CHADEMO_BATTERY +#error EQUIPMENT_STOP_BUTTON and CHADEMO_BATTERY cannot coexist due to overlapping GPIO pin usage +#endif +#endif + +#ifdef BMW_I3_BATTERY +#ifdef CONTACTOR_CONTROL +#error GPIO PIN 25 cannot be used for both BMWi3 Wakeup and contactor control. Disable CONTACTOR_CONTROL +#endif +#endif + +#endif diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 3f6bfbf5d..cf33bbf04 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -415,6 +415,9 @@ String get_firmware_info_processor(const String& var) { #ifdef HW_STARK doc["hardware"] = "Stark CMR Module"; #endif // HW_STARK +#ifdef HW_3LB + doc["hardware"] = "3LB board"; +#endif // HW_STARK doc["firmware"] = String(version_number); serializeJson(doc, content); From 5dd30f7d33cf97cfd3ea8983572f08f743b63f1d Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:55:07 +0000 Subject: [PATCH 30/81] Update advanced_battery_html.cpp --- .../webserver/advanced_battery_html.cpp | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index 5e6557e8b..ddad0304d 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -16,6 +16,47 @@ String advanced_battery_processor(const String& var) { // Start a new block with a specific background color content += "
"; +#ifdef BMW_IX_BATTERY + content += "

Battery Voltage after Contactor: " + String(datalayer_extended.bmwix.battery_voltage_after_contactor) + " dV

"; + content += "

Max Design Voltage: " + String(datalayer.battery.info.max_design_voltage_dV) + " dV

"; + content += "

Min Design Voltage: " + String(datalayer.battery.info.min_design_voltage_dV) + " dV

"; + content += "

Max Cell Design Voltage: " + String(datalayer.battery.info.max_cell_voltage_mV) + " mV

"; + content += "

Min Cell Design Voltage: " + String(datalayer.battery.info.min_cell_voltage_mV) + " mV

"; + content += "

Min Cell Voltage Data Age: " + String(datalayer_extended.bmwix.min_cell_voltage_data_age) + " ms

"; + content += "

Max Cell Voltage Data Age: " + String(datalayer_extended.bmwix.max_cell_voltage_data_age) + " ms

"; + content += "

Allowed Discharge Power: " + String(datalayer.battery.status.max_discharge_power_W) + " W

"; + content += "

Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W

"; + content += "

T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + " mV

"; + content += "

Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "

"; + static const char* balanceText[5] = {"0 No balancing mode active", + "1 Voltage-Controlled Balancing Mode", + "2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging" , + "3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage" , + "4 No balancing mode active, qualifier invalid" + }; + content += "

Balancing: " + String((balanceText[datalayer_extended.bmwix.balancing_status])) + "

"; + static const char* hvilText[2] = {"Error (Loop Open)", + "OK (Loop Closed)"}; + content += "

HVIL Status: " + String(hvilText[datalayer_extended.bmwix.hvil_status]) + "

"; + content += "

BMS Uptime: " + String(datalayer_extended.bmwix.bms_uptime) + " seconds

"; + content += "

BMS Allowed Charge Amps: " + String(datalayer_extended.bmwix.allowable_charge_amps) + " A

"; + content += "

BMS Allowed Disharge Amps: " + String(datalayer_extended.bmwix.allowable_discharge_amps) + " A

"; + content += "
"; + content += "

HV Isolation (2147483647kOhm = maximum/invalid)

"; + content += "

Isolation Positive: " + String(datalayer_extended.bmwix.iso_safety_positive) + " kOhm

"; + content += "

Isolation Negative: " + String(datalayer_extended.bmwix.iso_safety_negative) + " kOhm

"; + content += "

Isolation Parallel: " + String(datalayer_extended.bmwix.iso_safety_parallel) + " kOhm

"; + static const char* pyroText[5] = {"0 Value Invalid", + "1 Successfully Blown", + "2 Disconnected" , + "3 Not Activated - Pyro Intact" , + "4 Unknown" + }; + content += "

Pyro Status PSS1: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss1])) + "

"; + content += "

Pyro Status PSS4: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss4])) + "

"; + content += "

Pyro Status PSS6: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss6])) + "

"; +#endif //BMW_IX_BATTERY + #ifdef BMW_I3_BATTERY content += "

SOC raw: " + String(datalayer_extended.bmwi3.SOC_raw) + "

"; content += "

SOC dash: " + String(datalayer_extended.bmwi3.SOC_dash) + "

"; From f22806fd23720c161864ffc3bc9235ed2e48c933 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:04:53 +0000 Subject: [PATCH 31/81] Update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f4dd4582f..76d87ea2c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ compile.bat # Ignore binary files *.bin +Software/USER_SETTINGS.h +Software/USER_SETTINGS.cpp From c50fd918c12a6608f7abe2be9ec6059074afcfca Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:07:30 +0000 Subject: [PATCH 32/81] Update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 76d87ea2c..ea0bcd5ad 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ compile.bat *.bin Software/USER_SETTINGS.h Software/USER_SETTINGS.cpp +Software/USER_SETTINGS.h +Software/USER_SETTINGS.cpp From 23a6abfec20a247055f4d6c59062ce77ad3635cc Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:25:21 +0000 Subject: [PATCH 33/81] Update BMW-IX-BATTERY.cpp --- Software/src/battery/BMW-IX-BATTERY.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index f7569277a..c0880c2f1 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -58,9 +58,6 @@ No vehicle log available, SME asks for: TODO - Request batt serial number on F1 8C (already parsing RX) -- Check PWM state required from ACSM -- More Balancing values -- MIN /max cell voltag e- implement safety limits? */ From ae50634ee8fd808e222225a60302acf9e0b9856a Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:29:44 +0000 Subject: [PATCH 34/81] Update .gitignore --- .gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitignore b/.gitignore index ea0bcd5ad..f4dd4582f 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,3 @@ compile.bat # Ignore binary files *.bin -Software/USER_SETTINGS.h -Software/USER_SETTINGS.cpp -Software/USER_SETTINGS.h -Software/USER_SETTINGS.cpp From 271621f99763764c0db7364c9f27b487bc35f68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 8 Nov 2024 13:31:24 +0200 Subject: [PATCH 35/81] Revise bit placement in challenge reply --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 76bf7e13b..10b343631 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -877,6 +877,7 @@ void receive_can_battery(CAN_frame rx_frame) { if (stateMachineClearSOH < 255) { //Intercept the messages based on state machine if (rx_frame.data.u8[0] == 0x06) { // Incoming challenge data! + // BMS should reply with (challenge) 06 67 65 (02 DD 86 43) FF incomingChallenge = ((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[4] << 16) | (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]); } @@ -1332,6 +1333,7 @@ void clearSOH(void) { LEAF_CLEAR_SOH.data.u8[6] = 0x00; LEAF_CLEAR_SOH.data.u8[7] = 0x00; transmit_can(&LEAF_CLEAR_SOH, can_config.battery); + // BMS should reply with (challenge) 06 67 65 (02 DD 86 43) FF stateMachineClearSOH = 4; break; case 4: // Send back decoded challenge data @@ -1340,16 +1342,16 @@ void clearSOH(void) { LEAF_CLEAR_SOH.data.u8[1] = 0x0A; LEAF_CLEAR_SOH.data.u8[2] = 0x27; LEAF_CLEAR_SOH.data.u8[3] = 0x66; - LEAF_CLEAR_SOH.data.u8[4] = 0x77; - LEAF_CLEAR_SOH.data.u8[5] = solvedChallenge[0]; - LEAF_CLEAR_SOH.data.u8[6] = solvedChallenge[1]; - LEAF_CLEAR_SOH.data.u8[7] = solvedChallenge[2]; + LEAF_CLEAR_SOH.data.u8[4] = solvedChallenge[0]; + LEAF_CLEAR_SOH.data.u8[5] = solvedChallenge[1]; + LEAF_CLEAR_SOH.data.u8[6] = solvedChallenge[2]; + LEAF_CLEAR_SOH.data.u8[7] = solvedChallenge[3]; transmit_can(&LEAF_CLEAR_SOH, can_config.battery); // BMS should reply 7BB 8 30 01 00 FF FF FF FF FF // Proceed with more data (PID ACK) stateMachineClearSOH = 5; break; case 5: // Reply with even more decoded challenge data - LEAF_CLEAR_SOH.data.u8[0] = solvedChallenge[3]; + LEAF_CLEAR_SOH.data.u8[0] = 0x21; LEAF_CLEAR_SOH.data.u8[1] = solvedChallenge[4]; LEAF_CLEAR_SOH.data.u8[2] = solvedChallenge[5]; LEAF_CLEAR_SOH.data.u8[3] = solvedChallenge[6]; From 00f78ce76cf0682704779d57b3743709c8332ce5 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:08:08 +0000 Subject: [PATCH 36/81] Update formatting --- Software/src/battery/BMW-IX-BATTERY.cpp | 923 ++++++++++++++---------- Software/src/battery/BMW-IX-BATTERY.h | 5 +- 2 files changed, 555 insertions(+), 373 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index c0880c2f1..37110fb74 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -62,94 +62,269 @@ TODO */ //Vehicle CAN START -CAN_frame BMWiX_06D = {.FD = true, .ext_ID = false, .DLC = 8, .ID = 0x06D, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF}}; // 1000ms BDC Output - [0] static [1,2][3,4] counter x2. 3,4 is 9 higher than 1,2 is needed? [5-7] static - -CAN_frame BMWiX_0C0 = {.FD = true, .ext_ID = false, .DLC = 2, .ID = 0x0C0, - .data = {0xF0, 0x08}}; // Keep Alive 2 BDC>SME 200ms First byte cycles F0 > FE second byte 08 static - MINIMUM ID TO KEEP SME AWAKE - -CAN_frame BMWiX_276 = {.FD = true, .ext_ID = false, .DLC = 8, .ID = 0x476, - .data = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC}}; // 5000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED - -CAN_frame BMWiX_2F1 = {.FD = true, .ext_ID = false, .DLC = 8, .ID = 0x2F1, - .data = {0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0x00, 0xF3, 0xFF}}; // 1000ms BDC Output - Static values - varies at startup - -CAN_frame BMWiX_439 = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x439, - .data = {0xFF, 0xBF, 0xFF, 0xFF}}; // 1000ms BDC Output - Static values - -CAN_frame BMWiX_486 = {.FD = true, .ext_ID = false, .DLC = 48, .ID = 0x486, - .data = {0xFE, 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFE , 0xFF , 0xFF , 0x7F , 0x33 , 0xFD , 0xFD , 0xFD , 0xFD , 0xC0 , 0x41 , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF}}; // 1000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED - -CAN_frame BMWiX_49C = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x49C, - .data = {0xD2, 0xF2, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; // 1000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED - -CAN_frame BMWiX_510 = {.FD = true, .ext_ID = false, .DLC = 8, .ID = 0x510, - .data = {0x40, 0x10, 0x40, 0x00, 0x6F, 0xDF, 0x19, 0x00}}; // 100ms BDC Output - Static values - -CAN_frame BMWiX_12B8D087 = {.FD = true, .ext_ID = true, .DLC = 2, .ID = 0x12B8D087, - .data = {0xFC, 0xFF}}; // 5000ms SME Output - Static values +CAN_frame BMWiX_06D = { + .FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x06D, + .data = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xFF}}; // 1000ms BDC Output - [0] static [1,2][3,4] counter x2. 3,4 is 9 higher than 1,2 is needed? [5-7] static + +CAN_frame BMWiX_0C0 = { + .FD = true, + .ext_ID = false, + .DLC = 2, + .ID = 0x0C0, + .data = { + 0xF0, + 0x08}}; // Keep Alive 2 BDC>SME 200ms First byte cycles F0 > FE second byte 08 static - MINIMUM ID TO KEEP SME AWAKE + +CAN_frame BMWiX_276 = {.FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x476, + .data = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFC}}; // 5000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED + +CAN_frame BMWiX_2F1 = { + .FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x2F1, + .data = {0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0x00, 0xF3, 0xFF}}; // 1000ms BDC Output - Static values - varies at startup + +CAN_frame BMWiX_439 = {.FD = true, + .ext_ID = false, + .DLC = 4, + .ID = 0x439, + .data = {0xFF, 0xBF, 0xFF, 0xFF}}; // 1000ms BDC Output - Static values + +CAN_frame + BMWiX_486 = + { + .FD = true, + .ext_ID = false, + .DLC = 48, + .ID = 0x486, + .data = + { + 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, + 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, + 0xFE, 0xFF, 0xFF, 0x7F, 0x33, 0xFD, 0xFD, 0xFD, 0xFD, 0xC0, 0x41, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; // 1000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED + +CAN_frame BMWiX_49C = {.FD = true, + .ext_ID = false, + .DLC = 4, + .ID = 0x49C, + .data = {0xD2, 0xF2, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF}}; // 1000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED + +CAN_frame BMWiX_510 = {.FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x510, + .data = {0x40, 0x10, 0x40, 0x00, 0x6F, 0xDF, 0x19, 0x00}}; // 100ms BDC Output - Static values + +CAN_frame BMWiX_12B8D087 = {.FD = true, + .ext_ID = true, + .DLC = 2, + .ID = 0x12B8D087, + .data = {0xFC, 0xFF}}; // 5000ms SME Output - Static values //Vehicle CAN END //Request Data CAN START -CAN_frame BMWiX_6F4 = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0xC7}}; // Generic UDS Request data from SME. byte 4 selects requested value -CAN_frame BMWiX_6F4_REQUEST_SLEEPMODE = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x6F4, .data = {0x07, 0x02, 0x11, 0x04}}; // UDS Request Request BMS/SME goes to Sleep Mode -CAN_frame BMWiX_6F4_REQUEST_HARD_RESET = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x6F4, .data = {0x07, 0x02, 0x11, 0x01}}; // UDS Request Hard reset of BMS/SME -CAN_frame BMWiX_6F4_REQUEST_CELL_TEMP = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xDD, 0xC0}}; // UDS Request Cell Temperatures -CAN_frame BMWiX_6F4_REQUEST_SOC = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0xCE}}; // Min/Avg/Max SOC% -CAN_frame BMWiX_6F4_REQUEST_CAPACITY = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0xC7}}; //Current and max capacity kWh. Stored in kWh as 0.01 scale with -50 bias -CAN_frame BMWiX_6F4_REQUEST_MINMAXCELLV = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x53}}; //Min and max cell voltage 10V = Qualifier Invalid -CAN_frame BMWiX_6F4_REQUEST_MAINVOLTAGE_POSTCONTACTOR = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x4A}}; //Main Battery Voltage (After Contactor) -CAN_frame BMWiX_6F4_REQUEST_MAINVOLTAGE_PRECONTACTOR = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x4D}}; //Main Battery Voltage (Pre Contactor) -CAN_frame BMWiX_6F4_REQUEST_BATTERYCURRENT = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x61}}; //Current amps 32bit signed MSB. dA . negative is discharge -CAN_frame BMWiX_6F4_REQUEST_CELL_VOLTAGE = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x54}}; //MultiFrameIndividual Cell Voltages -CAN_frame BMWiX_6F4_REQUEST_T30VOLTAGE = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0xA7}}; //Terminal 30 Voltage (12V SME supply) -CAN_frame BMWiX_6F4_REQUEST_EOL_ISO = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xA8, 0x60}}; //Request EOL Reading including ISO -CAN_frame BMWiX_6F4_REQUEST_SOH = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x45}}; //SOH Max Min Mean Request -CAN_frame BMWiX_6F4_REQUEST_DATASUMMARY = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x45}}; //MultiFrame Summary Request, includes SOC/SOH/MinMax/MaxCapac/RemainCapac/max v and t at last charge. slow refreshrate -CAN_frame BMWiX_6F4_REQUEST_PYRO = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xAC, 0x93}}; //Pyro Status -CAN_frame BMWiX_6F4_REQUEST_UPTIME = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE4, 0xC0}}; // Uptime and Vehicle Time Status -CAN_frame BMWiX_6F4_REQUEST_HVIL = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x69}}; // Request HVIL State -CAN_frame BMWiX_6F4_REQUEST_BALANCINGSTATUS = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE4, 0xCA}}; // Request Balancing Data -CAN_frame BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x62}}; // Request allowable charge discharge amps -CAN_frame BMWiX_6F4_REQUEST_VOLTAGE_QUALIFIER_CHECK = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x4B}}; // Request HV Voltage Qualifier -CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_CLOSE = {.FD = true, .ext_ID = false, .DLC = 6, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Close - Unconfirmed -CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_OPEN = {.FD = true, .ext_ID = false, .DLC = 6, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Open - Unconfirmed -CAN_frame BMWiX_6F4_REQUEST_BALANCING_START = {.FD = true, .ext_ID = false, .DLC = 6, .ID = 0x6F4, .data = {0xF4, 0x04, 0x71, 0x01, 0xAE, 0x77}}; // Request Balancing command? -CAN_frame BMWiX_6F4_REQUEST_PACK_VOLTAGE_LIMITS = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x4C}}; // Request pack voltage limits - - -CAN_frame BMWiX_6F4_CONTINUE_DATA = {.FD = true, .ext_ID = false, .DLC = 4, .ID = 0x6F4, .data = {0x07, 0x30, 0x00, 0x02}}; +CAN_frame BMWiX_6F4 = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0xC7}}; // Generic UDS Request data from SME. byte 4 selects requested value +CAN_frame BMWiX_6F4_REQUEST_SLEEPMODE = { + .FD = true, + .ext_ID = false, + .DLC = 4, + .ID = 0x6F4, + .data = {0x07, 0x02, 0x11, 0x04}}; // UDS Request Request BMS/SME goes to Sleep Mode +CAN_frame BMWiX_6F4_REQUEST_HARD_RESET = {.FD = true, + .ext_ID = false, + .DLC = 4, + .ID = 0x6F4, + .data = {0x07, 0x02, 0x11, 0x01}}; // UDS Request Hard reset of BMS/SME +CAN_frame BMWiX_6F4_REQUEST_CELL_TEMP = {.FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xDD, 0xC0}}; // UDS Request Cell Temperatures +CAN_frame BMWiX_6F4_REQUEST_SOC = {.FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0xCE}}; // Min/Avg/Max SOC% +CAN_frame BMWiX_6F4_REQUEST_CAPACITY = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0xC7}}; //Current and max capacity kWh. Stored in kWh as 0.01 scale with -50 bias +CAN_frame BMWiX_6F4_REQUEST_MINMAXCELLV = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x53}}; //Min and max cell voltage 10V = Qualifier Invalid +CAN_frame BMWiX_6F4_REQUEST_MAINVOLTAGE_POSTCONTACTOR = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x4A}}; //Main Battery Voltage (After Contactor) +CAN_frame BMWiX_6F4_REQUEST_MAINVOLTAGE_PRECONTACTOR = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x4D}}; //Main Battery Voltage (Pre Contactor) +CAN_frame BMWiX_6F4_REQUEST_BATTERYCURRENT = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x61}}; //Current amps 32bit signed MSB. dA . negative is discharge +CAN_frame BMWiX_6F4_REQUEST_CELL_VOLTAGE = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x54}}; //MultiFrameIndividual Cell Voltages +CAN_frame BMWiX_6F4_REQUEST_T30VOLTAGE = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0xA7}}; //Terminal 30 Voltage (12V SME supply) +CAN_frame BMWiX_6F4_REQUEST_EOL_ISO = {.FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xA8, 0x60}}; //Request EOL Reading including ISO +CAN_frame BMWiX_6F4_REQUEST_SOH = {.FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x45}}; //SOH Max Min Mean Request +CAN_frame BMWiX_6F4_REQUEST_DATASUMMARY = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = { + 0x07, 0x03, 0x22, 0xE5, + 0x45}}; //MultiFrame Summary Request, includes SOC/SOH/MinMax/MaxCapac/RemainCapac/max v and t at last charge. slow refreshrate +CAN_frame BMWiX_6F4_REQUEST_PYRO = {.FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xAC, 0x93}}; //Pyro Status +CAN_frame BMWiX_6F4_REQUEST_UPTIME = {.FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE4, 0xC0}}; // Uptime and Vehicle Time Status +CAN_frame BMWiX_6F4_REQUEST_HVIL = {.FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x69}}; // Request HVIL State +CAN_frame BMWiX_6F4_REQUEST_BALANCINGSTATUS = {.FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE4, 0xCA}}; // Request Balancing Data +CAN_frame BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x62}}; // Request allowable charge discharge amps +CAN_frame BMWiX_6F4_REQUEST_VOLTAGE_QUALIFIER_CHECK = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x4B}}; // Request HV Voltage Qualifier +CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_CLOSE = { + .FD = true, + .ext_ID = false, + .DLC = 6, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Close - Unconfirmed +CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_OPEN = { + .FD = true, + .ext_ID = false, + .DLC = 6, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Open - Unconfirmed +CAN_frame BMWiX_6F4_REQUEST_BALANCING_START = { + .FD = true, + .ext_ID = false, + .DLC = 6, + .ID = 0x6F4, + .data = {0xF4, 0x04, 0x71, 0x01, 0xAE, 0x77}}; // Request Balancing command? +CAN_frame BMWiX_6F4_REQUEST_PACK_VOLTAGE_LIMITS = { + .FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x4C}}; // Request pack voltage limits + +CAN_frame BMWiX_6F4_CONTINUE_DATA = {.FD = true, + .ext_ID = false, + .DLC = 4, + .ID = 0x6F4, + .data = {0x07, 0x30, 0x00, 0x02}}; //Action Requests: -CAN_frame BMW_10B = {.FD = true, .ext_ID = false, .DLC = 3, .ID = 0x10B, .data = {0xCD, 0x00, 0xFC}}; // Contactor closing command? - -CAN_frame BMWiX_6F4_CELL_SOC = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0x9A}}; -CAN_frame BMWiX_6F4_CELL_TEMP = {.FD = true, .ext_ID = false, .DLC = 5, .ID = 0x6F4, .data = {0x07, 0x03, 0x22, 0xE5, 0xCA}}; +CAN_frame BMW_10B = {.FD = true, + .ext_ID = false, + .DLC = 3, + .ID = 0x10B, + .data = {0xCD, 0x00, 0xFC}}; // Contactor closing command? + +CAN_frame BMWiX_6F4_CELL_SOC = {.FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0x9A}}; +CAN_frame BMWiX_6F4_CELL_TEMP = {.FD = true, + .ext_ID = false, + .DLC = 5, + .ID = 0x6F4, + .data = {0x07, 0x03, 0x22, 0xE5, 0xCA}}; //Request Data CAN End static bool battery_awake = false; //Setup UDS values to poll for -CAN_frame* UDS_REQUESTS100MS[] = { - &BMWiX_6F4_REQUEST_CELL_TEMP, - &BMWiX_6F4_REQUEST_SOC, - &BMWiX_6F4_REQUEST_CAPACITY, - &BMWiX_6F4_REQUEST_MINMAXCELLV, - &BMWiX_6F4_REQUEST_MAINVOLTAGE_POSTCONTACTOR, - &BMWiX_6F4_REQUEST_MAINVOLTAGE_PRECONTACTOR, - &BMWiX_6F4_REQUEST_BATTERYCURRENT, - &BMWiX_6F4_REQUEST_CELL_VOLTAGE, - &BMWiX_6F4_REQUEST_T30VOLTAGE, - &BMWiX_6F4_REQUEST_SOH, - &BMWiX_6F4_REQUEST_UPTIME, - &BMWiX_6F4_REQUEST_PYRO, - &BMWiX_6F4_REQUEST_EOL_ISO, - &BMWiX_6F4_REQUEST_HVIL, - &BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS, - &BMWiX_6F4_REQUEST_BALANCINGSTATUS, - &BMWiX_6F4_REQUEST_PACK_VOLTAGE_LIMITS - }; -int numUDSreqs = sizeof(UDS_REQUESTS100MS) / sizeof(UDS_REQUESTS100MS[0]); // Number of elements in the array +CAN_frame* UDS_REQUESTS100MS[] = {&BMWiX_6F4_REQUEST_CELL_TEMP, + &BMWiX_6F4_REQUEST_SOC, + &BMWiX_6F4_REQUEST_CAPACITY, + &BMWiX_6F4_REQUEST_MINMAXCELLV, + &BMWiX_6F4_REQUEST_MAINVOLTAGE_POSTCONTACTOR, + &BMWiX_6F4_REQUEST_MAINVOLTAGE_PRECONTACTOR, + &BMWiX_6F4_REQUEST_BATTERYCURRENT, + &BMWiX_6F4_REQUEST_CELL_VOLTAGE, + &BMWiX_6F4_REQUEST_T30VOLTAGE, + &BMWiX_6F4_REQUEST_SOH, + &BMWiX_6F4_REQUEST_UPTIME, + &BMWiX_6F4_REQUEST_PYRO, + &BMWiX_6F4_REQUEST_EOL_ISO, + &BMWiX_6F4_REQUEST_HVIL, + &BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS, + &BMWiX_6F4_REQUEST_BALANCINGSTATUS, + &BMWiX_6F4_REQUEST_PACK_VOLTAGE_LIMITS}; +int numUDSreqs = sizeof(UDS_REQUESTS100MS) / sizeof(UDS_REQUESTS100MS[0]); // Number of elements in the array //iX Intermediate vars static bool battery_info_available = false; @@ -161,9 +336,9 @@ static int16_t battery_voltage_after_contactor = 0; static int16_t min_soc_state = 50; static int16_t avg_soc_state = 50; static int16_t max_soc_state = 50; -static int16_t min_soh_state = 99;// Uses E5 45, also available in 78 73 -static int16_t avg_soh_state = 99;// Uses E5 45, also available in 78 73 -static int16_t max_soh_state = 99;// Uses E5 45, also available in 78 73 +static int16_t min_soh_state = 99; // Uses E5 45, also available in 78 73 +static int16_t avg_soh_state = 99; // Uses E5 45, also available in 78 73 +static int16_t max_soh_state = 99; // Uses E5 45, also available in 78 73 static uint16_t max_design_voltage = 0; static uint16_t min_design_voltage = 0; static int32_t remaining_capacity = 0; @@ -179,37 +354,36 @@ static unsigned long max_cell_voltage_lastchanged = 0; static unsigned min_cell_voltage_lastreceived = 0; static unsigned max_cell_voltage_lastreceived = 0; static int16_t battery_power = 0; -static uint32_t sme_uptime = 0; //Uses E4 C0 -static int16_t allowable_charge_amps = 0; //E5 62 -static int16_t allowable_discharge_amps = 0; //E5 62 -static int32_t iso_safety_positive = 0; //Uses A8 60 -static int32_t iso_safety_negative = 0; //Uses A8 60 -static int32_t iso_safety_parallel = 0; //Uses A8 60 -static int16_t count_full_charges = 0; //TODO 42 -static int16_t count_charges = 0; //TODO 42 -static int16_t hvil_status = 0; -static int16_t voltage_qualifier_status = 0; //0 = Valid, 1 = Invalid -static int16_t balancing_status = 0; //4 = not active -static uint8_t contactors_closed = 0; //TODO E5 BF or E5 51 -static uint8_t contactor_status_precharge = 0; //TODO E5 BF -static uint8_t contactor_status_negative = 0; //TODO E5 BF -static uint8_t contactor_status_positive = 0; //TODO E5 BF -static uint8_t pyro_status_pss1 = 0; //Using AC 93 -static uint8_t pyro_status_pss4 = 0; //Using AC 93 -static uint8_t pyro_status_pss6 = 0; //Using AC 93 +static uint32_t sme_uptime = 0; //Uses E4 C0 +static int16_t allowable_charge_amps = 0; //E5 62 +static int16_t allowable_discharge_amps = 0; //E5 62 +static int32_t iso_safety_positive = 0; //Uses A8 60 +static int32_t iso_safety_negative = 0; //Uses A8 60 +static int32_t iso_safety_parallel = 0; //Uses A8 60 +static int16_t count_full_charges = 0; //TODO 42 +static int16_t count_charges = 0; //TODO 42 +static int16_t hvil_status = 0; +static int16_t voltage_qualifier_status = 0; //0 = Valid, 1 = Invalid +static int16_t balancing_status = 0; //4 = not active +static uint8_t contactors_closed = 0; //TODO E5 BF or E5 51 +static uint8_t contactor_status_precharge = 0; //TODO E5 BF +static uint8_t contactor_status_negative = 0; //TODO E5 BF +static uint8_t contactor_status_positive = 0; //TODO E5 BF +static uint8_t pyro_status_pss1 = 0; //Using AC 93 +static uint8_t pyro_status_pss4 = 0; //Using AC 93 +static uint8_t pyro_status_pss6 = 0; //Using AC 93 static uint8_t uds_req_id_counter = 0; -const unsigned long STALE_PERIOD = STALE_PERIOD_CONFIG ; // Time in milliseconds to check for staleness (e.g., 5000 ms = 5 seconds) +const unsigned long STALE_PERIOD = + STALE_PERIOD_CONFIG; // Time in milliseconds to check for staleness (e.g., 5000 ms = 5 seconds) - - -static byte iX_0C0_counter = 0xF0; // Initialize to 0xF0 +static byte iX_0C0_counter = 0xF0; // Initialize to 0xF0 //End iX Intermediate vars static uint8_t current_cell_polled = 0; // Function to check if a value has gone stale over a specified time period -bool isStale(int16_t currentValue, uint16_t &lastValue, unsigned long &lastChangeTime) { +bool isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChangeTime) { unsigned long currentTime = millis(); // Check if the value has changed @@ -244,7 +418,7 @@ static byte increment_0C0_counter(byte counter) { counter++; // Reset to 0xF0 if it exceeds 0xFE if (counter > 0xFE) { - counter = 0xF0; + counter = 0xF0; } return counter; } @@ -263,12 +437,11 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.soh_pptt = min_soh_state; - datalayer.battery.status.max_discharge_power_W = 3200; //10000; //Aux HV Port has 100A Fuse + datalayer.battery.status.max_discharge_power_W = 3200; //10000; //Aux HV Port has 100A Fuse //datalayer.battery.status.max_charge_power_W = 3200; //10000; //Aux HV Port has 100A Fuse Moved to Ramping - - // Charge power is set in .h file + // Charge power is set in .h file if (datalayer.battery.status.real_soc > 9900) { datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_WHEN_TOPBALANCING_W; } else if (datalayer.battery.status.real_soc > RAMPDOWN_SOC) { @@ -280,7 +453,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_ALLOWED_W; } - battery_power = (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100)); datalayer.battery.status.active_power_W = battery_power; @@ -289,22 +461,22 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.temperature_max_dC = max_battery_temperature; - - - if (isStale(min_cell_voltage, datalayer.battery.status.cell_min_voltage_mV, min_cell_voltage_lastchanged)) {//TODO prevent flipflop after error + if (isStale(min_cell_voltage, datalayer.battery.status.cell_min_voltage_mV, + min_cell_voltage_lastchanged)) { //TODO prevent flipflop after error Serial.println("min_cell_voltage has gone stale."); - datalayer.battery.status.cell_min_voltage_mV = 9999; //Stale values force stop - set_event(EVENT_CAN_RX_FAILURE,0); + datalayer.battery.status.cell_min_voltage_mV = 9999; //Stale values force stop + set_event(EVENT_CAN_RX_FAILURE, 0); } else { - datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; //Value is alive + datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; //Value is alive } - if (isStale(max_cell_voltage, datalayer.battery.status.cell_max_voltage_mV, max_cell_voltage_lastchanged)) { //TODO prevent flipflop after error + if (isStale(max_cell_voltage, datalayer.battery.status.cell_max_voltage_mV, + max_cell_voltage_lastchanged)) { //TODO prevent flipflop after error Serial.println("max_cell_voltage has gone stale."); - datalayer.battery.status.cell_max_voltage_mV = 9999; //Stale values force stop - set_event(EVENT_CAN_RX_FAILURE,0); + datalayer.battery.status.cell_max_voltage_mV = 9999; //Stale values force stop + set_event(EVENT_CAN_RX_FAILURE, 0); } else { - datalayer.battery.status.cell_max_voltage_mV = max_cell_voltage; //Value is alive + datalayer.battery.status.cell_max_voltage_mV = max_cell_voltage; //Value is alive } datalayer_extended.bmwix.min_cell_voltage_data_age = (millis() - min_cell_voltage_lastchanged); @@ -315,11 +487,11 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.info.min_design_voltage_dV = min_design_voltage; - datalayer.battery.info.number_of_cells = 108; //init with 108S before autodetection + datalayer.battery.info.number_of_cells = 108; //init with 108S before autodetection datalayer_extended.bmwix.T30_Voltage = terminal30_12v_voltage; - datalayer_extended.bmwix.hvil_status= hvil_status; + datalayer_extended.bmwix.hvil_status = hvil_status; datalayer_extended.bmwix.bms_uptime = sme_uptime; @@ -337,25 +509,19 @@ void update_values_battery() { //This function maps all the values fetched via datalayer_extended.bmwix.allowable_charge_amps = allowable_charge_amps; - datalayer_extended.bmwix.allowable_discharge_amps= allowable_discharge_amps; + datalayer_extended.bmwix.allowable_discharge_amps = allowable_discharge_amps; datalayer_extended.bmwix.balancing_status = balancing_status; datalayer_extended.bmwix.battery_voltage_after_contactor = battery_voltage_after_contactor; - - - - if (battery_info_available) { // If we have data from battery - override the defaults to suit - datalayer.battery.info.max_design_voltage_dV = max_design_voltage; - datalayer.battery.info.min_design_voltage_dV = min_design_voltage; - 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_design_voltage_dV = max_design_voltage; + datalayer.battery.info.min_design_voltage_dV = min_design_voltage; + datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; + datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; } - - } void receive_can_battery(CAN_frame rx_frame) { battery_awake = true; @@ -364,179 +530,198 @@ void receive_can_battery(CAN_frame rx_frame) { break; case 0x607: //SME responds to UDS requests on 0x607 - 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 - 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 - datalayer.battery.status.cell_voltages_mV[voltage_index] = voltage; - } - voltage_index++; - } + 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 + 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 + datalayer.battery.status.cell_voltages_mV[voltage_index] = voltage; } - + voltage_index++; + } + } + //Frame has continued data - so request it transmit_can(&BMWiX_6F4_CONTINUE_DATA, can_config.battery); } - 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 - datalayer.battery.status.cell_voltages_mV[voltage_index] = voltage; - } - voltage_index++; - } + 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 + datalayer.battery.status.cell_voltages_mV[voltage_index] = voltage; + } + voltage_index++; + } + } + + 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 + datalayer.battery.status.cell_voltages_mV[voltage_index] = voltage; + } + voltage_index++; + } + } + + 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 + datalayer.battery.info.number_of_cells = 96; + } else { //We have data in 97th cell, assume 108S Battery + num_voltages = 17; // number of voltage readings to get - 17 more to get on 108S + datalayer.battery.info.number_of_cells = 108; + } + + 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]; + datalayer.battery.status.cell_voltages_mV[voltage_index++] = voltage; + } + } + 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) + 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 + 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 + 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% + 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 + 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 + 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 + 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 = 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 - datalayer.battery.status.cell_voltages_mV[voltage_index] = voltage; - } - voltage_index++; - } - } - - 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 - datalayer.battery.info.number_of_cells = 96; - } else { //We have data in 97th cell, assume 108S Battery - num_voltages = 17; // number of voltage readings to get - 17 more to get on 108S - datalayer.battery.info.number_of_cells = 108; - } - - 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]; - datalayer.battery.status.cell_voltages_mV[voltage_index++] = voltage; - } - } - 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) - 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 - 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 - 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% - 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 - 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 - 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 - 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 - 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 - 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) | (rx_frame.data.u8[40] << 8) |rx_frame.data.u8[41]; //Assuming 32bit - iso_safety_parallel = (rx_frame.data.u8[42] << 24) | (rx_frame.data.u8[43] << 16) | (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 - 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 = 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 + 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) | + (rx_frame.data.u8[40] << 8) | rx_frame.data.u8[41]; //Assuming 32bit + iso_safety_parallel = (rx_frame.data.u8[42] << 24) | (rx_frame.data.u8[43] << 16) | + (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 + 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 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 - - 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. - - if((rx_frame.data.u8[6] <<8 | rx_frame.data.u8[7]) == 10000 && (rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9]) == 10000){ //Qualifier Invalid Mode - Request Reboot - #ifdef DEBUG_VIA_USB - Serial.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset"); - #endif - set_event(EVENT_SOC_UNAVAILABLE, (millis())); - transmit_can(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery); - } else{ //Only ingest values if they are not the 10V Error state - min_cell_voltage = (rx_frame.data.u8[6] <<8 | rx_frame.data.u8[7]); - max_cell_voltage = (rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9]); + 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 + + 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. + + if ((rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]) == 10000 && + (rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]) == 10000) { //Qualifier Invalid Mode - Request Reboot +#ifdef DEBUG_VIA_USB + Serial.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset"); +#endif + set_event(EVENT_SOC_UNAVAILABLE, (millis())); + transmit_can(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery); + } else { //Only ingest values if they are not the 10V Error state + min_cell_voltage = (rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]); + max_cell_voltage = (rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]); } - } - - 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 - 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) - 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 - 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.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; - max_design_voltage = (rx_frame.data.u8[6] <<8 | rx_frame.data.u8[7]); - min_design_voltage = (rx_frame.data.u8[8] <<8 | rx_frame.data.u8[9]); - } - } - - 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++) { - numberString[i] = char(rx_frame.data.u8[i+6]); - } - numberString[10] = '\0'; // Null-terminate the string - // Step 3: Convert the string to an unsigned long integer - battery_serial_number = strtoul(numberString, NULL, 10); - } + } + + 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 + 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) + 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 + 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.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; + max_design_voltage = (rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]); + min_design_voltage = (rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]); + } + } + + 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++) { + numberString[i] = char(rx_frame.data.u8[i + 6]); + } + numberString[10] = '\0'; // Null-terminate the string + // Step 3: Convert the string to an unsigned long integer + battery_serial_number = strtoul(numberString, NULL, 10); + } break; default: break; @@ -547,75 +732,72 @@ void send_can_battery() { unsigned long currentMillis = millis(); //if (battery_awake) { //We can always send CAN as the iX BMS will wake up on vehicle comms - //Send 20ms message - if (currentMillis - previousMillis20 >= INTERVAL_20_MS) { - // Check if sending of CAN messages has been delayed too much. - if ((currentMillis - previousMillis20 >= INTERVAL_20_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) { - set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis20)); - } else { - clear_event(EVENT_CAN_OVERRUN); - } - previousMillis20 = currentMillis; + //Send 20ms message + if (currentMillis - previousMillis20 >= INTERVAL_20_MS) { + // Check if sending of CAN messages has been delayed too much. + if ((currentMillis - previousMillis20 >= INTERVAL_20_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) { + set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis20)); + } else { + clear_event(EVENT_CAN_OVERRUN); } - // Send 100ms CAN Message - if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { - previousMillis100 = currentMillis; - - //Loop through and send a different UDS request each cycle - uds_req_id_counter = increment_uds_req_id_counter(uds_req_id_counter); - transmit_can(UDS_REQUESTS100MS[uds_req_id_counter], can_config.battery); - + previousMillis20 = currentMillis; + } + // Send 100ms CAN Message + if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { + previousMillis100 = currentMillis; - //Send SME Keep alive values 100ms - transmit_can(&BMWiX_510, can_config.battery); + //Loop through and send a different UDS request each cycle + uds_req_id_counter = increment_uds_req_id_counter(uds_req_id_counter); + transmit_can(UDS_REQUESTS100MS[uds_req_id_counter], can_config.battery); - } - // Send 200ms CAN Message - if (currentMillis - previousMillis200 >= INTERVAL_200_MS) { - previousMillis200 = currentMillis; - - //Send SME Keep alive values 200ms - BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 - transmit_can(&BMWiX_0C0, can_config.battery); - } - // Send 500ms CAN Message - if (currentMillis - previousMillis500 >= INTERVAL_500_MS) { - previousMillis500 = currentMillis; - } - // Send 640ms CAN Message - if (currentMillis - previousMillis640 >= INTERVAL_640_MS) { - previousMillis640 = currentMillis; - } - // Send 1000ms CAN Message - if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { - previousMillis1000 = currentMillis; + //Send SME Keep alive values 100ms + transmit_can(&BMWiX_510, can_config.battery); + } + // Send 200ms CAN Message + if (currentMillis - previousMillis200 >= INTERVAL_200_MS) { + previousMillis200 = currentMillis; - //Send SME Keep alive values 1000ms - //test disable transmit_can(&BMWiX_06D, can_config.battery); - //test disable transmit_can(&BMWiX_2F1, can_config.battery); - //test disable transmit_can(&BMWiX_439, can_config.battery); + //Send SME Keep alive values 200ms + BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 + transmit_can(&BMWiX_0C0, can_config.battery); + } + // Send 500ms CAN Message + if (currentMillis - previousMillis500 >= INTERVAL_500_MS) { + previousMillis500 = currentMillis; + } + // Send 640ms CAN Message + if (currentMillis - previousMillis640 >= INTERVAL_640_MS) { + previousMillis640 = currentMillis; + } + // Send 1000ms CAN Message + if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { + previousMillis1000 = currentMillis; - } - // Send 5000ms CAN Message - if (currentMillis - previousMillis5000 >= INTERVAL_5_S) { - previousMillis5000 = currentMillis; - } - // Send 10000ms CAN Message - if (currentMillis - previousMillis10000 >= INTERVAL_10_S) { - previousMillis10000 = currentMillis; - } + //Send SME Keep alive values 1000ms + //test disable transmit_can(&BMWiX_06D, can_config.battery); + //test disable transmit_can(&BMWiX_2F1, can_config.battery); + //test disable transmit_can(&BMWiX_439, can_config.battery); } - //We can always send CAN as the iX BMS will wake up on vehicle comms - // else { - // previousMillis20 = currentMillis; - // previousMillis100 = currentMillis; - // previousMillis200 = currentMillis; - // previousMillis500 = currentMillis; - // previousMillis640 = currentMillis; - // previousMillis1000 = currentMillis; - // previousMillis5000 = currentMillis; - // previousMillis10000 = currentMillis; - // } + // Send 5000ms CAN Message + if (currentMillis - previousMillis5000 >= INTERVAL_5_S) { + previousMillis5000 = currentMillis; + } + // Send 10000ms CAN Message + if (currentMillis - previousMillis10000 >= INTERVAL_10_S) { + previousMillis10000 = currentMillis; + } +} +//We can always send CAN as the iX BMS will wake up on vehicle comms +// else { +// previousMillis20 = currentMillis; +// previousMillis100 = currentMillis; +// previousMillis200 = currentMillis; +// previousMillis500 = currentMillis; +// previousMillis640 = currentMillis; +// previousMillis1000 = currentMillis; +// previousMillis5000 = currentMillis; +// previousMillis10000 = currentMillis; +// } //} //We can always send CAN as the iX BMS will wake up on vehicle comms void setup_battery(void) { // Performs one time setup at startup @@ -631,20 +813,19 @@ void setup_battery(void) { // Performs one time setup at startup datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.system.status.battery_allows_contactor_closing = true; - //pinMode(WUP_PIN, OUTPUT); // Not needed - can hold WUP pin High with iX BMS - //digitalWrite(WUP_PIN, HIGH); // Wake up the battery // Not needed - can hold WUP pin High with iX BMS + //pinMode(WUP_PIN, OUTPUT); // Not needed - can hold WUP pin High with iX BMS + //digitalWrite(WUP_PIN, HIGH); // Wake up the battery // Not needed - can hold WUP pin High with iX BMS + //Send SME Keep alive values 100ms + transmit_can(&BMWiX_510, can_config.battery); + //Send SME Keep alive values 200ms + BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 + transmit_can(&BMWiX_0C0, can_config.battery); - //Send SME Keep alive values 100ms - transmit_can(&BMWiX_510, can_config.battery); - //Send SME Keep alive values 200ms - BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 - transmit_can(&BMWiX_0C0, can_config.battery); - - //Send SME Keep alive values 1000ms - //Not needed transmit_can(&BMWiX_06D, can_config.battery); - //Not needed transmit_can(&BMWiX_2F1, can_config.battery); - //Not needed transmit_can(&BMWiX_439, can_config.battery); + //Send SME Keep alive values 1000ms + //Not needed transmit_can(&BMWiX_06D, can_config.battery); + //Not needed transmit_can(&BMWiX_2F1, can_config.battery); + //Not needed transmit_can(&BMWiX_439, can_config.battery); } #endif diff --git a/Software/src/battery/BMW-IX-BATTERY.h b/Software/src/battery/BMW-IX-BATTERY.h index 657811c82..450ed80a6 100644 --- a/Software/src/battery/BMW-IX-BATTERY.h +++ b/Software/src/battery/BMW-IX-BATTERY.h @@ -12,9 +12,10 @@ #define MAX_CELL_VOLTAGE_MV 4300 //Battery is put into emergency stop if one cell goes over this value #define MIN_CELL_VOLTAGE_MV 2800 //Battery is put into emergency stop if one cell goes below this value #define MAX_CHARGE_POWER_ALLOWED_W 5000 -#define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500 +#define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500 #define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00% -#define STALE_PERIOD_CONFIG 180000; //Number of milliseconds before critical values are classed as stale/stuck 180000 = 180 seconds +#define STALE_PERIOD_CONFIG \ + 180000; //Number of milliseconds before critical values are classed as stale/stuck 180000 = 180 seconds void setup_battery(void); void transmit_can(CAN_frame* tx_frame, int interface); From bb77d91791882cd21111f1d1a595dabdbf242716 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:14:35 +0000 Subject: [PATCH 37/81] Update formatting Part 2 --- .../webserver/advanced_battery_html.cpp | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index ddad0304d..5db011272 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -17,41 +17,39 @@ String advanced_battery_processor(const String& var) { content += "
"; #ifdef BMW_IX_BATTERY - content += "

Battery Voltage after Contactor: " + String(datalayer_extended.bmwix.battery_voltage_after_contactor) + " dV

"; + content += + "

Battery Voltage after Contactor: " + String(datalayer_extended.bmwix.battery_voltage_after_contactor) + + " dV

"; content += "

Max Design Voltage: " + String(datalayer.battery.info.max_design_voltage_dV) + " dV

"; content += "

Min Design Voltage: " + String(datalayer.battery.info.min_design_voltage_dV) + " dV

"; content += "

Max Cell Design Voltage: " + String(datalayer.battery.info.max_cell_voltage_mV) + " mV

"; content += "

Min Cell Design Voltage: " + String(datalayer.battery.info.min_cell_voltage_mV) + " mV

"; - content += "

Min Cell Voltage Data Age: " + String(datalayer_extended.bmwix.min_cell_voltage_data_age) + " ms

"; - content += "

Max Cell Voltage Data Age: " + String(datalayer_extended.bmwix.max_cell_voltage_data_age) + " ms

"; + content += + "

Min Cell Voltage Data Age: " + String(datalayer_extended.bmwix.min_cell_voltage_data_age) + " ms

"; + content += + "

Max Cell Voltage Data Age: " + String(datalayer_extended.bmwix.max_cell_voltage_data_age) + " ms

"; content += "

Allowed Discharge Power: " + String(datalayer.battery.status.max_discharge_power_W) + " W

"; content += "

Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W

"; - content += "

T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + " mV

"; + content += "

T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + " mV

"; content += "

Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "

"; - static const char* balanceText[5] = {"0 No balancing mode active", - "1 Voltage-Controlled Balancing Mode", - "2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging" , - "3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage" , - "4 No balancing mode active, qualifier invalid" - }; + static const char* balanceText[5] = {"0 No balancing mode active", "1 Voltage-Controlled Balancing Mode", + "2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging", + "3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage", + "4 No balancing mode active, qualifier invalid"}; content += "

Balancing: " + String((balanceText[datalayer_extended.bmwix.balancing_status])) + "

"; - static const char* hvilText[2] = {"Error (Loop Open)", - "OK (Loop Closed)"}; + static const char* hvilText[2] = {"Error (Loop Open)", "OK (Loop Closed)"}; content += "

HVIL Status: " + String(hvilText[datalayer_extended.bmwix.hvil_status]) + "

"; content += "

BMS Uptime: " + String(datalayer_extended.bmwix.bms_uptime) + " seconds

"; content += "

BMS Allowed Charge Amps: " + String(datalayer_extended.bmwix.allowable_charge_amps) + " A

"; - content += "

BMS Allowed Disharge Amps: " + String(datalayer_extended.bmwix.allowable_discharge_amps) + " A

"; + content += + "

BMS Allowed Disharge Amps: " + String(datalayer_extended.bmwix.allowable_discharge_amps) + " A

"; content += "
"; content += "

HV Isolation (2147483647kOhm = maximum/invalid)

"; content += "

Isolation Positive: " + String(datalayer_extended.bmwix.iso_safety_positive) + " kOhm

"; content += "

Isolation Negative: " + String(datalayer_extended.bmwix.iso_safety_negative) + " kOhm

"; content += "

Isolation Parallel: " + String(datalayer_extended.bmwix.iso_safety_parallel) + " kOhm

"; - static const char* pyroText[5] = {"0 Value Invalid", - "1 Successfully Blown", - "2 Disconnected" , - "3 Not Activated - Pyro Intact" , - "4 Unknown" - }; + static const char* pyroText[5] = {"0 Value Invalid", "1 Successfully Blown", "2 Disconnected", + "3 Not Activated - Pyro Intact", "4 Unknown"}; content += "

Pyro Status PSS1: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss1])) + "

"; content += "

Pyro Status PSS4: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss4])) + "

"; content += "

Pyro Status PSS6: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss6])) + "

"; From 8a4525c15c20344f3b00ef063fe39e7ed8ac04f0 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:20:27 +0000 Subject: [PATCH 38/81] Update formatting part 3 --- Software/src/datalayer/datalayer_extended.h | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 3f4347647..2009d60b3 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -8,17 +8,17 @@ typedef struct { /** Terminal 30 - 12V SME Supply Voltage */ uint16_t T30_Voltage = 0; /** Status HVIL, 1 HVIL OK, 0 HVIL disconnected*/ - uint8_t hvil_status= 0; + uint8_t hvil_status = 0; /** Min/Max Cell SOH*/ uint16_t min_soh_state = 0; - uint16_t max_soh_state=0; - uint32_t bms_uptime=0; - uint8_t pyro_status_pss1=0; - uint8_t pyro_status_pss4=0; - uint8_t pyro_status_pss6=0; - int32_t iso_safety_positive=0; - int32_t iso_safety_negative=0; - int32_t iso_safety_parallel=0; + uint16_t max_soh_state = 0; + uint32_t bms_uptime = 0; + uint8_t pyro_status_pss1 = 0; + uint8_t pyro_status_pss4 = 0; + uint8_t pyro_status_pss6 = 0; + int32_t iso_safety_positive = 0; + int32_t iso_safety_negative = 0; + int32_t iso_safety_parallel = 0; int32_t allowable_charge_amps = 0; int32_t allowable_discharge_amps = 0; int16_t balancing_status = 0; @@ -28,7 +28,6 @@ typedef struct { } DATALAYER_INFO_BMWIX; - typedef struct { /** uint16_t */ /** SOC% raw battery value. Might not always reach 100% */ From b08d93b6a79b213ed5ca54f60ba482b98bdfbaa9 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:44:12 +0000 Subject: [PATCH 39/81] Discharge rate defs and plausible check coverage --- Software/src/battery/BMW-IX-BATTERY.cpp | 7 +++++-- Software/src/battery/BMW-IX-BATTERY.h | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 37110fb74..3857f16e4 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -437,7 +437,7 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.soh_pptt = min_soh_state; - datalayer.battery.status.max_discharge_power_W = 3200; //10000; //Aux HV Port has 100A Fuse + datalayer.battery.status.max_discharge_power_W = MAX_DISCHARGE_POWER_ALLOWED_W; //datalayer.battery.status.max_charge_power_W = 3200; //10000; //Aux HV Port has 100A Fuse Moved to Ramping @@ -593,7 +593,10 @@ void receive_can_battery(CAN_frame rx_frame) { 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]; - datalayer.battery.status.cell_voltages_mV[voltage_index++] = voltage; + if (voltage < 10000) { //Check reading is plausible - otherwise ignore + datalayer.battery.status.cell_voltages_mV[voltage_index] = voltage; + } + voltage_index++; } } if (rx_frame.DLC = 7 && rx_frame.data.u8[4] == 0x4D) { //Main Battery Voltage (Pre Contactor) diff --git a/Software/src/battery/BMW-IX-BATTERY.h b/Software/src/battery/BMW-IX-BATTERY.h index 450ed80a6..72afe6d58 100644 --- a/Software/src/battery/BMW-IX-BATTERY.h +++ b/Software/src/battery/BMW-IX-BATTERY.h @@ -11,7 +11,8 @@ #define MAX_CELL_DEVIATION_MV 250 #define MAX_CELL_VOLTAGE_MV 4300 //Battery is put into emergency stop if one cell goes over this value #define MIN_CELL_VOLTAGE_MV 2800 //Battery is put into emergency stop if one cell goes below this value -#define MAX_CHARGE_POWER_ALLOWED_W 5000 +#define MAX_DISCHARGE_POWER_ALLOWED_W 10000 +#define MAX_CHARGE_POWER_ALLOWED_W 10000 #define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500 #define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00% #define STALE_PERIOD_CONFIG \ From 7efe4323fda6233aa6bd8b488568e4d7f5493a1f Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Fri, 8 Nov 2024 14:14:20 +0000 Subject: [PATCH 40/81] Various Review cleanups -unchecked debug logging removed -datalayer updates grouped -cleaned up message loops -cleaned up setup function --- Software/src/battery/BMW-IX-BATTERY.cpp | 54 +++++-------------------- 1 file changed, 9 insertions(+), 45 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 3857f16e4..33556d6c9 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -461,34 +461,30 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.temperature_max_dC = max_battery_temperature; - if (isStale(min_cell_voltage, datalayer.battery.status.cell_min_voltage_mV, - min_cell_voltage_lastchanged)) { //TODO prevent flipflop after error - Serial.println("min_cell_voltage has gone stale."); + if (isStale(min_cell_voltage, datalayer.battery.status.cell_min_voltage_mV, min_cell_voltage_lastchanged)) { datalayer.battery.status.cell_min_voltage_mV = 9999; //Stale values force stop set_event(EVENT_CAN_RX_FAILURE, 0); } else { datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; //Value is alive } - if (isStale(max_cell_voltage, datalayer.battery.status.cell_max_voltage_mV, - max_cell_voltage_lastchanged)) { //TODO prevent flipflop after error - Serial.println("max_cell_voltage has gone stale."); + if (isStale(max_cell_voltage, datalayer.battery.status.cell_max_voltage_mV, max_cell_voltage_lastchanged)) { datalayer.battery.status.cell_max_voltage_mV = 9999; //Stale values force stop set_event(EVENT_CAN_RX_FAILURE, 0); } else { datalayer.battery.status.cell_max_voltage_mV = max_cell_voltage; //Value is alive } - datalayer_extended.bmwix.min_cell_voltage_data_age = (millis() - min_cell_voltage_lastchanged); - - datalayer_extended.bmwix.max_cell_voltage_data_age = (millis() - max_cell_voltage_lastchanged); - datalayer.battery.info.max_design_voltage_dV = max_design_voltage; datalayer.battery.info.min_design_voltage_dV = min_design_voltage; datalayer.battery.info.number_of_cells = 108; //init with 108S before autodetection + datalayer_extended.bmwix.min_cell_voltage_data_age = (millis() - min_cell_voltage_lastchanged); + + datalayer_extended.bmwix.max_cell_voltage_data_age = (millis() - max_cell_voltage_lastchanged); + datalayer_extended.bmwix.T30_Voltage = terminal30_12v_voltage; datalayer_extended.bmwix.hvil_status = hvil_status; @@ -735,16 +731,6 @@ void send_can_battery() { unsigned long currentMillis = millis(); //if (battery_awake) { //We can always send CAN as the iX BMS will wake up on vehicle comms - //Send 20ms message - if (currentMillis - previousMillis20 >= INTERVAL_20_MS) { - // Check if sending of CAN messages has been delayed too much. - if ((currentMillis - previousMillis20 >= INTERVAL_20_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) { - set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis20)); - } else { - clear_event(EVENT_CAN_OVERRUN); - } - previousMillis20 = currentMillis; - } // Send 100ms CAN Message if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { previousMillis100 = currentMillis; @@ -764,22 +750,14 @@ void send_can_battery() { BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 transmit_can(&BMWiX_0C0, can_config.battery); } - // Send 500ms CAN Message - if (currentMillis - previousMillis500 >= INTERVAL_500_MS) { - previousMillis500 = currentMillis; - } - // Send 640ms CAN Message - if (currentMillis - previousMillis640 >= INTERVAL_640_MS) { - previousMillis640 = currentMillis; - } // Send 1000ms CAN Message if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { previousMillis1000 = currentMillis; //Send SME Keep alive values 1000ms - //test disable transmit_can(&BMWiX_06D, can_config.battery); - //test disable transmit_can(&BMWiX_2F1, can_config.battery); - //test disable transmit_can(&BMWiX_439, can_config.battery); + //Don't believe this is needed: transmit_can(&BMWiX_06D, can_config.battery); + //Don't believe this is needed: transmit_can(&BMWiX_2F1, can_config.battery); + //Don't believe this is needed: transmit_can(&BMWiX_439, can_config.battery); } // Send 5000ms CAN Message if (currentMillis - previousMillis5000 >= INTERVAL_5_S) { @@ -815,20 +793,6 @@ void setup_battery(void) { // Performs one time setup at startup datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.system.status.battery_allows_contactor_closing = true; - - //pinMode(WUP_PIN, OUTPUT); // Not needed - can hold WUP pin High with iX BMS - //digitalWrite(WUP_PIN, HIGH); // Wake up the battery // Not needed - can hold WUP pin High with iX BMS - - //Send SME Keep alive values 100ms - transmit_can(&BMWiX_510, can_config.battery); - //Send SME Keep alive values 200ms - BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1 - transmit_can(&BMWiX_0C0, can_config.battery); - - //Send SME Keep alive values 1000ms - //Not needed transmit_can(&BMWiX_06D, can_config.battery); - //Not needed transmit_can(&BMWiX_2F1, can_config.battery); - //Not needed transmit_can(&BMWiX_439, can_config.battery); } #endif From 8b14078b5222252a027ff7f8ef52356c05d0cd78 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Fri, 8 Nov 2024 15:25:49 +0000 Subject: [PATCH 41/81] Cleaner update of detected cell count --- Software/src/battery/BMW-IX-BATTERY.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 33556d6c9..a04cb0930 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -373,6 +373,7 @@ static uint8_t pyro_status_pss1 = 0; //Using AC 93 static uint8_t pyro_status_pss4 = 0; //Using AC 93 static uint8_t pyro_status_pss6 = 0; //Using AC 93 static uint8_t uds_req_id_counter = 0; +static uint8_t detected_number_of_cells = 108; const unsigned long STALE_PERIOD = STALE_PERIOD_CONFIG; // Time in milliseconds to check for staleness (e.g., 5000 ms = 5 seconds) @@ -479,7 +480,7 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.info.min_design_voltage_dV = min_design_voltage; - datalayer.battery.info.number_of_cells = 108; //init with 108S before autodetection + datalayer.battery.info.number_of_cells = detected_number_of_cells; datalayer_extended.bmwix.min_cell_voltage_data_age = (millis() - min_cell_voltage_lastchanged); @@ -581,10 +582,10 @@ void receive_can_battery(CAN_frame rx_frame) { 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 - datalayer.battery.info.number_of_cells = 96; + detected_number_of_cells = 96; } else { //We have data in 97th cell, assume 108S Battery num_voltages = 17; // number of voltage readings to get - 17 more to get on 108S - datalayer.battery.info.number_of_cells = 108; + detected_number_of_cells = 108; } for (int i = start_index; i < (start_index + num_voltages * 2); i += 2) { From c2e92aee029d9081bb9935260b5fbcf1730a5aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 8 Nov 2024 21:25:32 +0200 Subject: [PATCH 42/81] Clean up code --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 94 +++----------------- 1 file changed, 13 insertions(+), 81 deletions(-) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 10b343631..d5e788091 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -1298,108 +1298,54 @@ void clearSOH(void) { stateMachineClearSOH = 1; break; case 1: // Set CAN_PROCESS_FLAG to 0xC0 - LEAF_CLEAR_SOH.data.u8[0] = 0x02; - LEAF_CLEAR_SOH.data.u8[1] = 0x10; - LEAF_CLEAR_SOH.data.u8[2] = 0xC0; - LEAF_CLEAR_SOH.data.u8[3] = 0x00; - LEAF_CLEAR_SOH.data.u8[4] = 0x00; - LEAF_CLEAR_SOH.data.u8[5] = 0x00; - LEAF_CLEAR_SOH.data.u8[6] = 0x00; - LEAF_CLEAR_SOH.data.u8[7] = 0x00; + LEAF_CLEAR_SOH.data = {0x02, 0x10, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can(&LEAF_CLEAR_SOH, can_config.battery); // BMS should reply 02 50 C0 FF FF FF FF FF stateMachineClearSOH = 2; break; case 2: // Set something ? - LEAF_CLEAR_SOH.data.u8[0] = 0x02; - LEAF_CLEAR_SOH.data.u8[1] = 0x3E; - LEAF_CLEAR_SOH.data.u8[2] = 0x01; - LEAF_CLEAR_SOH.data.u8[3] = 0x00; - LEAF_CLEAR_SOH.data.u8[4] = 0x00; - LEAF_CLEAR_SOH.data.u8[5] = 0x00; - LEAF_CLEAR_SOH.data.u8[6] = 0x00; - LEAF_CLEAR_SOH.data.u8[7] = 0x00; + LEAF_CLEAR_SOH.data = {0x02, 0x3E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can(&LEAF_CLEAR_SOH, can_config.battery); // BMS should reply 7E FF FF FF FF FF FF stateMachineClearSOH = 3; break; case 3: // Request challenge to solve - LEAF_CLEAR_SOH.data.u8[0] = 0x02; - LEAF_CLEAR_SOH.data.u8[1] = 0x27; - LEAF_CLEAR_SOH.data.u8[2] = 0x65; - LEAF_CLEAR_SOH.data.u8[3] = 0x00; - LEAF_CLEAR_SOH.data.u8[4] = 0x00; - LEAF_CLEAR_SOH.data.u8[5] = 0x00; - LEAF_CLEAR_SOH.data.u8[6] = 0x00; - LEAF_CLEAR_SOH.data.u8[7] = 0x00; + LEAF_CLEAR_SOH.data = {0x02, 0x27, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can(&LEAF_CLEAR_SOH, can_config.battery); // BMS should reply with (challenge) 06 67 65 (02 DD 86 43) FF stateMachineClearSOH = 4; break; case 4: // Send back decoded challenge data decodeChallengeData(incomingChallenge, solvedChallenge); - LEAF_CLEAR_SOH.data.u8[0] = 0x10; - LEAF_CLEAR_SOH.data.u8[1] = 0x0A; - LEAF_CLEAR_SOH.data.u8[2] = 0x27; - LEAF_CLEAR_SOH.data.u8[3] = 0x66; - LEAF_CLEAR_SOH.data.u8[4] = solvedChallenge[0]; - LEAF_CLEAR_SOH.data.u8[5] = solvedChallenge[1]; - LEAF_CLEAR_SOH.data.u8[6] = solvedChallenge[2]; - LEAF_CLEAR_SOH.data.u8[7] = solvedChallenge[3]; + LEAF_CLEAR_SOH.data = { + 0x10, 0x0A, 0x27, 0x66, solvedChallenge[0], solvedChallenge[1], solvedChallenge[2], solvedChallenge[3]}; transmit_can(&LEAF_CLEAR_SOH, can_config.battery); // BMS should reply 7BB 8 30 01 00 FF FF FF FF FF // Proceed with more data (PID ACK) stateMachineClearSOH = 5; break; case 5: // Reply with even more decoded challenge data - LEAF_CLEAR_SOH.data.u8[0] = 0x21; - LEAF_CLEAR_SOH.data.u8[1] = solvedChallenge[4]; - LEAF_CLEAR_SOH.data.u8[2] = solvedChallenge[5]; - LEAF_CLEAR_SOH.data.u8[3] = solvedChallenge[6]; - LEAF_CLEAR_SOH.data.u8[4] = solvedChallenge[7]; - LEAF_CLEAR_SOH.data.u8[5] = 0x00; - LEAF_CLEAR_SOH.data.u8[6] = 0x00; - LEAF_CLEAR_SOH.data.u8[7] = 0x00; + LEAF_CLEAR_SOH.data = { + 0x21, solvedChallenge[4], solvedChallenge[5], solvedChallenge[6], solvedChallenge[7], 0x00, 0x00, 0x00}; transmit_can(&LEAF_CLEAR_SOH, can_config.battery); // BMS should reply 02 67 66 FF FF FF FF FF // Thank you for the data stateMachineClearSOH = 6; break; case 6: // Check if solved data was OK - LEAF_CLEAR_SOH.data.u8[0] = 0x03; - LEAF_CLEAR_SOH.data.u8[1] = 0x31; - LEAF_CLEAR_SOH.data.u8[2] = 0x03; - LEAF_CLEAR_SOH.data.u8[3] = 0x00; - LEAF_CLEAR_SOH.data.u8[4] = 0x00; - LEAF_CLEAR_SOH.data.u8[5] = 0x00; - LEAF_CLEAR_SOH.data.u8[6] = 0x00; - LEAF_CLEAR_SOH.data.u8[7] = 0x00; + LEAF_CLEAR_SOH.data = {0x03, 0x31, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can(&LEAF_CLEAR_SOH, can_config.battery); //7BB 8 03 71 03 01 FF FF FF FF // If all is well, BMS replies with 03 71 03 01. //Incase you sent wrong challenge, you get 03 7f 31 12 stateMachineClearSOH = 7; break; case 7: // Reset SOH% request - LEAF_CLEAR_SOH.data.u8[0] = 0x03; - LEAF_CLEAR_SOH.data.u8[1] = 0x31; - LEAF_CLEAR_SOH.data.u8[2] = 0x03; - LEAF_CLEAR_SOH.data.u8[3] = 0x01; - LEAF_CLEAR_SOH.data.u8[4] = 0x00; - LEAF_CLEAR_SOH.data.u8[5] = 0x00; - LEAF_CLEAR_SOH.data.u8[6] = 0x00; - LEAF_CLEAR_SOH.data.u8[7] = 0x00; + LEAF_CLEAR_SOH.data = {0x03, 0x31, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00}; transmit_can(&LEAF_CLEAR_SOH, can_config.battery); //7BB 8 03 71 03 02 FF FF FF FF // 03 71 03 02 means that BMS accepted command. //7BB 03 7f 31 12 means your challenge was wrong, so command ignored stateMachineClearSOH = 8; break; case 8: // Please proceed with resetting SOH - LEAF_CLEAR_SOH.data.u8[0] = 0x02; - LEAF_CLEAR_SOH.data.u8[1] = 0x10; - LEAF_CLEAR_SOH.data.u8[2] = 0x81; - LEAF_CLEAR_SOH.data.u8[3] = 0x00; - LEAF_CLEAR_SOH.data.u8[4] = 0x00; - LEAF_CLEAR_SOH.data.u8[5] = 0x00; - LEAF_CLEAR_SOH.data.u8[6] = 0x00; - LEAF_CLEAR_SOH.data.u8[7] = 0x00; + LEAF_CLEAR_SOH.data = {0x02, 0x10, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can(&LEAF_CLEAR_SOH, can_config.battery); // 7BB 8 02 50 81 FF FF FF FF FF // SOH reset OK stateMachineClearSOH = 255; @@ -1423,17 +1369,7 @@ void clearSOH(void) { uint32_t CyclicXorHash16Bit(uint32_t param_1, uint32_t param_2) { bool bVar1; - uint32_t uVar2; - uint32_t uVar3; - uint32_t uVar4; - uint32_t uVar5; - uint32_t uVar6; - uint32_t uVar7; - uint32_t uVar8; - uint32_t uVar9; - uint32_t uVar10; - uint32_t uVar11; - uint32_t iVar12; + uint32_t uVar2, uVar3, uVar4, uVar5, uVar6, uVar7, uVar8, uVar9, uVar10, uVar11, iVar12; param_1 = param_1 & 0xffff; param_2 = param_2 & 0xffff; @@ -1518,10 +1454,7 @@ uint32_t MaskedBitwiseRotateMultiply(uint32_t param_1, uint32_t param_2) { } uint32_t CryptAlgo(uint32_t param_1, uint32_t param_2, uint32_t param_3) { - uint32_t uVar1; - uint32_t uVar2; - uint32_t iVar3; - uint32_t iVar4; + uint32_t uVar1, uVar2, iVar3, iVar4; uVar1 = MaskedBitwiseRotateMultiply(param_2, param_3); uVar2 = ShortMaskedSumAndProduct(param_2, param_3); @@ -1533,8 +1466,7 @@ uint32_t CryptAlgo(uint32_t param_1, uint32_t param_2, uint32_t param_3) { } void decodeChallengeData(uint32_t incomingChallenge, unsigned char* solvedChallenge) { - uint32_t uVar1; - uint32_t uVar2; + uint32_t uVar1, uVar2; uVar1 = CryptAlgo(0x609, 0xDD2, incomingChallenge >> 0x10); uVar2 = CryptAlgo(incomingChallenge & 0xffff, incomingChallenge >> 0x10, 0x609); From 710a7339d881c28498fe0adc8ad09336c78ea6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 8 Nov 2024 22:56:53 +0200 Subject: [PATCH 43/81] Add charge/discharge current to datalayer --- Software/Software.ino | 22 +++++++- Software/src/datalayer/datalayer.h | 8 ++- Software/src/devboard/safety/safety.cpp | 8 +++ Software/src/devboard/webserver/webserver.cpp | 8 +++ Software/src/inverter/AFORE-CAN.cpp | 33 ++--------- Software/src/inverter/BYD-CAN.cpp | 36 ++---------- Software/src/inverter/BYD-SMA.cpp | 35 ++---------- Software/src/inverter/FOXESS-CAN.cpp | 56 ++----------------- Software/src/inverter/PYLON-CAN.cpp | 54 ++++++------------ Software/src/inverter/PYLON-LV-CAN.cpp | 28 ++++------ Software/src/inverter/SMA-CAN.cpp | 33 ++--------- Software/src/inverter/SMA-TRIPOWER-CAN.cpp | 33 ++--------- Software/src/inverter/SOFAR-CAN.cpp | 8 +-- Software/src/inverter/SOLAX-CAN.cpp | 54 ++---------------- 14 files changed, 104 insertions(+), 312 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index 588f28cf0..adc771e1f 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -293,7 +293,7 @@ void core_loop(void* task_time_us) { #ifdef DOUBLE_BATTERY update_values_battery2(); #endif - update_scaled_values(); // Check if real or calculated SOC% value should be sent + update_calculated_values(); #ifndef SERIAL_LINK_RECEIVER update_machineryprotection(); // Check safeties (Not on serial link reciever board) #endif @@ -837,7 +837,23 @@ void handle_contactors() { #endif // CONTACTOR_CONTROL } -void update_scaled_values() { +void update_calculated_values() { + /* Calculate allowed charge/discharge currents*/ + if (datalayer.battery.status.voltage_dV > 10) { + // Only update value when we have voltage available to avoid div0. TODO: This should be based on nominal voltage + datalayer.battery.status.max_charge_current_dA = + ((datalayer.battery.status.max_charge_power_W * 100) / datalayer.battery.status.voltage_dV); + datalayer.battery.status.max_discharge_current_dA = + ((datalayer.battery.status.max_discharge_power_W * 100) / datalayer.battery.status.voltage_dV); + } + /* Restrict values from user settings if needed*/ + if (datalayer.battery.status.max_charge_current_dA > datalayer.battery.info.max_charge_amp_dA) { + datalayer.battery.status.max_charge_current_dA = datalayer.battery.info.max_charge_amp_dA; + } + if (datalayer.battery.status.max_discharge_current_dA > datalayer.battery.info.max_discharge_amp_dA) { + datalayer.battery.status.max_discharge_current_dA = datalayer.battery.info.max_discharge_amp_dA; + } + if (datalayer.battery.settings.soc_scaling_active) { /** SOC Scaling * @@ -896,7 +912,7 @@ void update_scaled_values() { } #endif - } else { // No SOC window wanted. Set scaled to same as real. + } else { // soc_scaling_active == false. No SOC window wanted. Set scaled to same as real. datalayer.battery.status.reported_soc = datalayer.battery.status.real_soc; datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh; #ifdef DOUBLE_BATTERY diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index d0b6e840a..b4967f4b6 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -49,10 +49,14 @@ typedef struct { */ uint32_t reported_remaining_capacity_Wh; - /** Maximum allowed battery discharge power in Watts */ + /** Maximum allowed battery discharge power in Watts. Set by battery */ uint32_t max_discharge_power_W = 0; - /** Maximum allowed battery charge power in Watts */ + /** 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 */ diff --git a/Software/src/devboard/safety/safety.cpp b/Software/src/devboard/safety/safety.cpp index f2023f41d..ee72af88b 100644 --- a/Software/src/devboard/safety/safety.cpp +++ b/Software/src/devboard/safety/safety.cpp @@ -229,6 +229,14 @@ void update_machineryprotection() { } #endif // DOUBLE_BATTERY + + //Safeties verified, Zero charge/discharge ampere values incase any safety wrote the W to 0 + if (datalayer.battery.status.max_discharge_power_W == 0) { + datalayer.battery.status.max_discharge_current_dA = 0; + } + if (datalayer.battery.status.max_charge_power_W == 0) { + datalayer.battery.status.max_charge_current_dA = 0; + } } //battery pause status begin diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index d42bac4b5..4a1554f36 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -652,6 +652,10 @@ String processor(const String& var) { float powerFloat = static_cast(datalayer.battery.status.active_power_W); // Convert to float float tempMaxFloat = static_cast(datalayer.battery.status.temperature_max_dC) / 10.0; // Convert to float float tempMinFloat = static_cast(datalayer.battery.status.temperature_min_dC) / 10.0; // Convert to float + float maxCurrentChargeFloat = + static_cast(datalayer.battery.status.max_charge_current_dA) / 10.0; // Convert to float + float maxCurrentDischargeFloat = + static_cast(datalayer.battery.status.max_discharge_current_dA) / 10.0; // Convert to float uint16_t cell_delta_mv = datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV; @@ -669,9 +673,13 @@ String processor(const String& var) { if (emulator_pause_status == NORMAL) { content += formatPowerValue("Max discharge power", datalayer.battery.status.max_discharge_power_W, "", 1); content += formatPowerValue("Max charge power", datalayer.battery.status.max_charge_power_W, "", 1); + content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; + content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; } else { 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"); + content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; + content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; } content += "

Cell max: " + String(datalayer.battery.status.cell_max_voltage_mV) + " mV

"; diff --git a/Software/src/inverter/AFORE-CAN.cpp b/Software/src/inverter/AFORE-CAN.cpp index 6ac88873f..744351166 100644 --- a/Software/src/inverter/AFORE-CAN.cpp +++ b/Software/src/inverter/AFORE-CAN.cpp @@ -72,31 +72,10 @@ CAN_frame AFORE_35A = {.FD = false, .DLC = 8, .ID = 0x35A, .data = {0x65, 0x6D, 0x75, 0x6C, 0x61, 0x74, 0x6F, 0x72}}; // Emulator -static int16_t max_charge_current_dA = 0; -static int16_t max_discharge_current_dA = 0; void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages //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 - if (datalayer.battery.status.voltage_dV > 10) { //div0 safeguard - max_charge_current_dA = (datalayer.battery.status.max_charge_power_W * 100) / datalayer.battery.status.voltage_dV; - if (max_charge_current_dA > datalayer.battery.info.max_charge_amp_dA) { - max_charge_current_dA = - datalayer.battery.info - .max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - max_discharge_current_dA = - (datalayer.battery.status.max_discharge_power_W * 100) / datalayer.battery.status.voltage_dV; - if (max_discharge_current_dA > datalayer.battery.info.max_discharge_amp_dA) { - max_discharge_current_dA = - datalayer.battery.info - .max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - } else { - max_charge_current_dA = 0; - max_discharge_current_dA = 0; - } /*0x350 Operation Information*/ AFORE_350.data.u8[0] = (datalayer.battery.status.voltage_dV & 0x00FF); AFORE_350.data.u8[1] = (datalayer.battery.status.voltage_dV >> 8); @@ -115,11 +94,11 @@ void update_values_can_inverter() { //This function maps all the values fetched AFORE_351.data.u8[2] = SOCMAX; AFORE_351.data.u8[3] = SOCMIN; AFORE_351.data.u8[4] = 0x03; //Bit0 and Bit1 set - if ((max_charge_current_dA == 0) || (datalayer.battery.status.reported_soc == 10000) || + if ((datalayer.battery.status.max_charge_current_dA == 0) || (datalayer.battery.status.reported_soc == 10000) || (datalayer.battery.status.bms_status == FAULT)) { AFORE_351.data.u8[4] &= ~0x01; // Remove Bit0 (clear) Charge enable flag } - if ((max_discharge_current_dA == 0) || (datalayer.battery.status.reported_soc == 0) || + if ((datalayer.battery.status.max_discharge_current_dA == 0) || (datalayer.battery.status.reported_soc == 0) || (datalayer.battery.status.bms_status == FAULT)) { AFORE_351.data.u8[4] &= ~0x02; // Remove Bit1 (clear) Discharge enable flag } @@ -135,10 +114,10 @@ void update_values_can_inverter() { //This function maps all the values fetched AFORE_351.data.u8[7] = (datalayer.battery.info.number_of_cells >> 8); /*0x352 - Protection parameters*/ - AFORE_352.data.u8[0] = (max_charge_current_dA & 0x00FF); - AFORE_352.data.u8[1] = (max_charge_current_dA >> 8); - AFORE_352.data.u8[2] = (max_discharge_current_dA & 0x00FF); - AFORE_352.data.u8[3] = (max_discharge_current_dA >> 8); + AFORE_352.data.u8[0] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); + AFORE_352.data.u8[1] = (datalayer.battery.status.max_charge_current_dA >> 8); + AFORE_352.data.u8[2] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); + AFORE_352.data.u8[3] = (datalayer.battery.status.max_discharge_current_dA >> 8); AFORE_352.data.u8[4] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF); AFORE_352.data.u8[5] = (datalayer.battery.info.max_design_voltage_dV >> 8); AFORE_352.data.u8[6] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF); diff --git a/Software/src/inverter/BYD-CAN.cpp b/Software/src/inverter/BYD-CAN.cpp index 825274696..757547ec6 100644 --- a/Software/src/inverter/BYD-CAN.cpp +++ b/Software/src/inverter/BYD-CAN.cpp @@ -79,8 +79,6 @@ CAN_frame BYD_210 = {.FD = false, .ID = 0x210, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static uint16_t discharge_current = 0; -static uint16_t charge_current = 0; static int16_t temperature_average = 0; static uint16_t inverter_voltage = 0; static uint16_t inverter_SOC = 0; @@ -91,32 +89,6 @@ static bool initialDataSent = 0; void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages - /* Calculate allowed charge/discharge currents*/ - if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0 - charge_current = - ((datalayer.battery.status.max_charge_power_W * 10) / - datalayer.battery.status.voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) - //The above calculation results in (30 000*10)/3700=81A - charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) - - discharge_current = - ((datalayer.battery.status.max_discharge_power_W * 10) / - datalayer.battery.status.voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) - //The above calculation results in (30 000*10)/3700=81A - discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) - } - /* Restrict values from user settings if needed*/ - if (charge_current > datalayer.battery.info.max_charge_amp_dA) { - charge_current = - datalayer.battery.info - .max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - if (discharge_current > datalayer.battery.info.max_discharge_amp_dA) { - discharge_current = - datalayer.battery.info - .max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - /* Calculate temperature */ temperature_average = ((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2); @@ -137,11 +109,11 @@ void update_values_can_inverter() { //This function maps all the values fetched BYD_110.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV >> 8); BYD_110.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF); //Maximum discharge power allowed (Unit: A+1) - BYD_110.data.u8[4] = (discharge_current >> 8); - BYD_110.data.u8[5] = (discharge_current & 0x00FF); + BYD_110.data.u8[4] = (datalayer.battery.status.max_discharge_current_dA >> 8); + BYD_110.data.u8[5] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); //Maximum charge power allowed (Unit: A+1) - BYD_110.data.u8[6] = (charge_current >> 8); - BYD_110.data.u8[7] = (charge_current & 0x00FF); + BYD_110.data.u8[6] = (datalayer.battery.status.max_charge_current_dA >> 8); + BYD_110.data.u8[7] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); //SOC (100.00%) BYD_150.data.u8[0] = (datalayer.battery.status.reported_soc >> 8); diff --git a/Software/src/inverter/BYD-SMA.cpp b/Software/src/inverter/BYD-SMA.cpp index 162cfb3a1..230b61e79 100644 --- a/Software/src/inverter/BYD-SMA.cpp +++ b/Software/src/inverter/BYD-SMA.cpp @@ -80,30 +80,6 @@ static uint16_t ampere_hours_remaining = 0; void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages //Calculate values - - if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0 - discharge_current = - ((datalayer.battery.status.max_discharge_power_W * 10) / - datalayer.battery.status.voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) - discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) - charge_current = - ((datalayer.battery.status.max_charge_power_W * 10) / - datalayer.battery.status.voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) - charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) - } - - if (charge_current > datalayer.battery.info.max_charge_amp_dA) { - charge_current = - datalayer.battery.info - .max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - - if (discharge_current > datalayer.battery.info.max_discharge_amp_dA) { - discharge_current = - datalayer.battery.info - .max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - temperature_average = ((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2); @@ -118,15 +94,14 @@ void update_values_can_inverter() { //This function maps all the values fetched SMA_358.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8); SMA_358.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF); //Minvoltage (eg 300.0V = 3000 , 16bits long) - SMA_358.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV >> - 8); //Minvoltage behaves strange on SMA, cuts out at 56% of the set value? + SMA_358.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV >> 8); SMA_358.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF); //Discharge limited current, 500 = 50A, (0.1, A) - SMA_358.data.u8[4] = (discharge_current >> 8); - SMA_358.data.u8[5] = (discharge_current & 0x00FF); + SMA_358.data.u8[4] = (datalayer.battery.status.max_discharge_current_dA >> 8); + SMA_358.data.u8[5] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); //Charge limited current, 125 =12.5A (0.1, A) - SMA_358.data.u8[6] = (charge_current >> 8); - SMA_358.data.u8[7] = (charge_current & 0x00FF); + SMA_358.data.u8[6] = (datalayer.battery.status.max_charge_current_dA >> 8); + SMA_358.data.u8[7] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); //SOC (100.00%) SMA_3D8.data.u8[0] = (datalayer.battery.status.reported_soc >> 8); diff --git a/Software/src/inverter/FOXESS-CAN.cpp b/Software/src/inverter/FOXESS-CAN.cpp index 8b5679462..3909bbbe3 100644 --- a/Software/src/inverter/FOXESS-CAN.cpp +++ b/Software/src/inverter/FOXESS-CAN.cpp @@ -22,8 +22,6 @@ below that you can customize, incase you use a lower voltage battery with this p #define TOTAL_LIFETIME_WH_ACCUMULATED 0 //We dont have this value in the emulator /* Do not change code below unless you are sure what you are doing */ -static uint16_t max_charge_rate_amp = 0; -static uint16_t max_discharge_rate_amp = 0; static int16_t temperature_average = 0; static uint16_t voltage_per_pack = 0; static int16_t current_per_pack = 0; @@ -364,50 +362,6 @@ void update_values_can_inverter() { //This function maps all the CAN values fet temperature_average = ((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2); - //datalayer.battery.status.max_charge_power_W (30000W max) - if (datalayer.battery.status.reported_soc > 9999) { // 99.99% - // Additional safety incase SOC% is 100, then do not charge battery further - max_charge_rate_amp = 0; - } else { // We can pass on the battery charge rate (in W) to the inverter (that takes A) - if (datalayer.battery.status.max_charge_power_W >= 30000) { - max_charge_rate_amp = 75; // Incase battery can take over 30kW, cap value to 75A - } else { // Calculate the W value into A - if (datalayer.battery.status.voltage_dV > 10) { - max_charge_rate_amp = - datalayer.battery.status.max_charge_power_W / (datalayer.battery.status.voltage_dV * 0.1); // P/U=I - } else { // We avoid dividing by 0 and crashing the board - // If we have no voltage, something has gone wrong, do not allow charging - max_charge_rate_amp = 0; - } - } - } - - //datalayer.battery.status.max_discharge_power_W (30000W max) - if (datalayer.battery.status.reported_soc < 100) { // 1.00% - // Additional safety in case SOC% is below 1, then do not discharge battery further - max_discharge_rate_amp = 0; - } else { // We can pass on the battery discharge rate to the inverter - if (datalayer.battery.status.max_discharge_power_W >= 30000) { - max_discharge_rate_amp = 75; // Incase battery can be charged with over 30kW, cap value to 75A - } else { // Calculate the W value into A - if (datalayer.battery.status.voltage_dV > 10) { - max_discharge_rate_amp = - datalayer.battery.status.max_discharge_power_W / (datalayer.battery.status.voltage_dV * 0.1); // P/U=I - } else { // We avoid dividing by 0 and crashing the board - // If we have no voltage, something has gone wrong, do not allow discharging - max_discharge_rate_amp = 0; - } - } - } - - //Cap the value according to user settings. Some inverters cannot handle large values. - if ((max_charge_rate_amp * 10) > datalayer.battery.info.max_charge_amp_dA) { - max_charge_rate_amp = (datalayer.battery.info.max_charge_amp_dA / 10); - } - if ((max_discharge_rate_amp * 10) > datalayer.battery.info.max_discharge_amp_dA) { - max_discharge_rate_amp = (datalayer.battery.info.max_discharge_amp_dA / 10); - } - if (inverterStillAlive > 0) { inverterStillAlive--; } @@ -424,10 +378,10 @@ void update_values_can_inverter() { //This function maps all the CAN values fet FOXESS_1872.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV >> 8); FOXESS_1872.data.u8[2] = (uint8_t)datalayer.battery.info.min_design_voltage_dV; FOXESS_1872.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV >> 8); - FOXESS_1872.data.u8[4] = (uint8_t)(max_charge_rate_amp * 10); - FOXESS_1872.data.u8[5] = ((max_charge_rate_amp * 10) >> 8); - FOXESS_1872.data.u8[6] = (uint8_t)(max_discharge_rate_amp * 10); - FOXESS_1872.data.u8[7] = ((max_discharge_rate_amp * 10) >> 8); + FOXESS_1872.data.u8[4] = (uint8_t)datalayer.battery.status.max_charge_current_dA; + FOXESS_1872.data.u8[5] = (datalayer.battery.status.max_charge_current_dA >> 8); + FOXESS_1872.data.u8[6] = (uint8_t)datalayer.battery.status.max_discharge_current_dA; + FOXESS_1872.data.u8[7] = (datalayer.battery.status.max_discharge_current_dA >> 8); //BMS_PackData FOXESS_1873.data.u8[0] = (uint8_t)datalayer.battery.status.voltage_dV; // OK @@ -463,7 +417,7 @@ void update_values_can_inverter() { //This function maps all the CAN values fet // 0x1876 b0 bit 0 appears to be 1 when at maxsoc and BMS says charge is not allowed - // when at 0 indicates charge is possible - additional note there is something more to it than this, // it's not as straight forward - needs more testing to find what sets/unsets bit0 of byte0 - if ((max_charge_rate_amp == 0) || (datalayer.battery.status.reported_soc == 10000) || + if ((datalayer.battery.status.max_charge_current_dA == 0) || (datalayer.battery.status.reported_soc == 10000) || (datalayer.battery.status.bms_status == FAULT)) { FOXESS_1876.data.u8[0] = 0x01; } else { //continue using battery diff --git a/Software/src/inverter/PYLON-CAN.cpp b/Software/src/inverter/PYLON-CAN.cpp index 9706f759a..2b8e036c0 100644 --- a/Software/src/inverter/PYLON-CAN.cpp +++ b/Software/src/inverter/PYLON-CAN.cpp @@ -134,32 +134,10 @@ CAN_frame PYLON_4291 = {.FD = false, .ID = 0x4291, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static int16_t max_charge_current = 0; -static int16_t max_discharge_current = 0; - void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages //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 - if (datalayer.battery.status.voltage_dV > 10) { //div0 safeguard - max_charge_current = (datalayer.battery.status.max_charge_power_W * 100) / datalayer.battery.status.voltage_dV; - if (max_charge_current > datalayer.battery.info.max_charge_amp_dA) { - max_charge_current = - datalayer.battery.info - .max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - max_discharge_current = - (datalayer.battery.status.max_discharge_power_W * 100) / datalayer.battery.status.voltage_dV; - if (max_discharge_current > datalayer.battery.info.max_discharge_amp_dA) { - max_discharge_current = - datalayer.battery.info - .max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - } else { - max_charge_current = 0; - max_discharge_current = 0; - } - //Charge / Discharge allowed PYLON_4280.data.u8[0] = 0; PYLON_4280.data.u8[1] = 0; @@ -253,28 +231,28 @@ void update_values_can_inverter() { //This function maps all the values fetched #ifdef SET_30K_OFFSET //Max ChargeCurrent - PYLON_4220.data.u8[4] = ((max_charge_current + 30000) & 0x00FF); - PYLON_4220.data.u8[5] = ((max_charge_current + 30000) >> 8); - PYLON_4221.data.u8[4] = ((max_charge_current + 30000) & 0x00FF); - PYLON_4221.data.u8[5] = ((max_charge_current + 30000) >> 8); + PYLON_4220.data.u8[4] = ((datalayer.battery.status.max_charge_current_dA + 30000) & 0x00FF); + PYLON_4220.data.u8[5] = ((datalayer.battery.status.max_charge_current_dA + 30000) >> 8); + PYLON_4221.data.u8[4] = ((datalayer.battery.status.max_charge_current_dA + 30000) & 0x00FF); + PYLON_4221.data.u8[5] = ((datalayer.battery.status.max_charge_current_dA + 30000) >> 8); //Max DischargeCurrent - PYLON_4220.data.u8[6] = ((30000 - max_discharge_current) & 0x00FF); - PYLON_4220.data.u8[7] = ((30000 - max_discharge_current) >> 8); - PYLON_4221.data.u8[6] = ((30000 - max_discharge_current) & 0x00FF); - PYLON_4221.data.u8[7] = ((30000 - max_discharge_current) >> 8); + PYLON_4220.data.u8[6] = ((30000 - datalayer.battery.status.max_discharge_current_dA) & 0x00FF); + PYLON_4220.data.u8[7] = ((30000 - datalayer.battery.status.max_discharge_current_dA) >> 8); + PYLON_4221.data.u8[6] = ((30000 - datalayer.battery.status.max_discharge_current_dA) & 0x00FF); + PYLON_4221.data.u8[7] = ((30000 - datalayer.battery.status.max_discharge_current_dA) >> 8); #else //Max ChargeCurrent - PYLON_4220.data.u8[4] = (max_charge_current & 0x00FF); - PYLON_4220.data.u8[5] = (max_charge_current >> 8); - PYLON_4221.data.u8[4] = (max_charge_current & 0x00FF); - PYLON_4221.data.u8[5] = (max_charge_current >> 8); + PYLON_4220.data.u8[4] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); + PYLON_4220.data.u8[5] = (datalayer.battery.status.max_charge_current_dA >> 8); + PYLON_4221.data.u8[4] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); + PYLON_4221.data.u8[5] = (datalayer.battery.status.max_charge_current_dA >> 8); //Max DishargeCurrent - PYLON_4220.data.u8[6] = (max_discharge_current & 0x00FF); - PYLON_4220.data.u8[7] = (max_discharge_current >> 8); - PYLON_4221.data.u8[6] = (max_discharge_current & 0x00FF); - PYLON_4221.data.u8[7] = (max_discharge_current >> 8); + PYLON_4220.data.u8[6] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); + PYLON_4220.data.u8[7] = (datalayer.battery.status.max_discharge_current_dA >> 8); + PYLON_4221.data.u8[6] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); + PYLON_4221.data.u8[7] = (datalayer.battery.status.max_discharge_current_dA >> 8); #endif //Max cell voltage diff --git a/Software/src/inverter/PYLON-LV-CAN.cpp b/Software/src/inverter/PYLON-LV-CAN.cpp index d169146ea..458ac7a04 100644 --- a/Software/src/inverter/PYLON-LV-CAN.cpp +++ b/Software/src/inverter/PYLON-LV-CAN.cpp @@ -42,20 +42,13 @@ CAN_frame PYLON_35E = {.FD = false, void update_values_can_inverter() { // This function maps all the values fetched from battery CAN to the correct CAN messages - // do not update values unless we have some voltage, as we will run into IntegerDivideByZero exceptions otherwise - if (datalayer.battery.status.voltage_dV == 0) - return; - // TODO: officially this value is "battery charge voltage". Do we need to add something here to the actual voltage? PYLON_351.data.u8[0] = datalayer.battery.status.voltage_dV & 0xff; PYLON_351.data.u8[1] = datalayer.battery.status.voltage_dV >> 8; - int16_t maxChargeCurrent = datalayer.battery.status.max_charge_power_W * 100 / datalayer.battery.status.voltage_dV; - PYLON_351.data.u8[2] = maxChargeCurrent & 0xff; - PYLON_351.data.u8[3] = maxChargeCurrent >> 8; - int16_t maxDischargeCurrent = - datalayer.battery.status.max_discharge_power_W * 100 / datalayer.battery.status.voltage_dV; - PYLON_351.data.u8[4] = maxDischargeCurrent & 0xff; - PYLON_351.data.u8[5] = maxDischargeCurrent >> 8; + PYLON_351.data.u8[2] = datalayer.battery.status.max_charge_current_dA & 0xff; + PYLON_351.data.u8[3] = datalayer.battery.status.max_charge_current_dA >> 8; + PYLON_351.data.u8[4] = datalayer.battery.status.max_discharge_current_dA & 0xff; + PYLON_351.data.u8[5] = datalayer.battery.status.max_discharge_current_dA >> 8; PYLON_355.data.u8[0] = (datalayer.battery.status.reported_soc / 10) & 0xff; PYLON_355.data.u8[1] = (datalayer.battery.status.reported_soc / 10) >> 8; @@ -75,11 +68,11 @@ void update_values_can_inverter() { PYLON_359.data.u8[2] = 0x00; PYLON_359.data.u8[3] = 0x00; PYLON_359.data.u8[4] = PACK_NUMBER; - PYLON_359.data.u8[5] = 'P'; - PYLON_359.data.u8[6] = 'N'; + PYLON_359.data.u8[5] = 0x50; //P + PYLON_359.data.u8[6] = 0x4E; //N // ERRORS - if (datalayer.battery.status.current_dA >= maxDischargeCurrent) + if (datalayer.battery.status.current_dA >= (datalayer.battery.status.max_discharge_current_dA + 10)) PYLON_359.data.u8[0] |= 0x80; if (datalayer.battery.status.temperature_min_dC <= BATTERY_MINTEMPERATURE) PYLON_359.data.u8[0] |= 0x10; @@ -88,11 +81,11 @@ void update_values_can_inverter() { if (datalayer.battery.status.voltage_dV * 100 <= datalayer.battery.info.min_cell_voltage_mV) PYLON_359.data.u8[0] |= 0x04; // we never set PYLON_359.data.u8[1] |= 0x80 called "BMS internal" - if (datalayer.battery.status.current_dA <= -1 * maxChargeCurrent) + if (datalayer.battery.status.current_dA <= -1 * datalayer.battery.status.max_charge_current_dA) PYLON_359.data.u8[1] |= 0x01; // WARNINGS (using same rules as errors but reporting earlier) - if (datalayer.battery.status.current_dA >= maxDischargeCurrent * WARNINGS_PERCENT / 100) + if (datalayer.battery.status.current_dA >= datalayer.battery.status.max_discharge_current_dA * WARNINGS_PERCENT / 100) PYLON_359.data.u8[2] |= 0x80; if (datalayer.battery.status.temperature_min_dC <= BATTERY_MINTEMPERATURE * WARNINGS_PERCENT / 100) PYLON_359.data.u8[2] |= 0x10; @@ -101,7 +94,8 @@ void update_values_can_inverter() { if (datalayer.battery.status.voltage_dV * 100 <= datalayer.battery.info.min_cell_voltage_mV + 100) PYLON_359.data.u8[2] |= 0x04; // we never set PYLON_359.data.u8[3] |= 0x80 called "BMS internal" - if (datalayer.battery.status.current_dA <= -1 * maxChargeCurrent * WARNINGS_PERCENT / 100) + if (datalayer.battery.status.current_dA <= + -1 * datalayer.battery.status.max_charge_current_dA * WARNINGS_PERCENT / 100) PYLON_359.data.u8[3] |= 0x01; PYLON_35C.data.u8[0] = 0xC0; // enable charging and discharging diff --git a/Software/src/inverter/SMA-CAN.cpp b/Software/src/inverter/SMA-CAN.cpp index 4fabbd16a..cecae60ce 100644 --- a/Software/src/inverter/SMA-CAN.cpp +++ b/Software/src/inverter/SMA-CAN.cpp @@ -70,37 +70,12 @@ CAN_frame SMA_158 = {.FD = false, .ID = 0x158, .data = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xAA, 0xAA}}; -static int16_t discharge_current = 0; -static int16_t charge_current = 0; static int16_t temperature_average = 0; static uint16_t ampere_hours_remaining = 0; void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages //Calculate values - if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0 - discharge_current = - ((datalayer.battery.status.max_discharge_power_W * 10) / - datalayer.battery.status.voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) - discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) - charge_current = - ((datalayer.battery.status.max_charge_power_W * 10) / - datalayer.battery.status.voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) - charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) - } - - if (charge_current > datalayer.battery.info.max_charge_amp_dA) { - charge_current = - datalayer.battery.info - .max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - - if (discharge_current > datalayer.battery.info.max_discharge_amp_dA) { - discharge_current = - datalayer.battery.info - .max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - temperature_average = ((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2); @@ -119,11 +94,11 @@ void update_values_can_inverter() { //This function maps all the values fetched 8); //Minvoltage behaves strange on SMA, cuts out at 56% of the set value? SMA_358.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF); //Discharge limited current, 500 = 50A, (0.1, A) - SMA_358.data.u8[4] = (discharge_current >> 8); - SMA_358.data.u8[5] = (discharge_current & 0x00FF); + SMA_358.data.u8[4] = (datalayer.battery.status.max_discharge_current_dA >> 8); + SMA_358.data.u8[5] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); //Charge limited current, 125 =12.5A (0.1, A) - SMA_358.data.u8[6] = (charge_current >> 8); - SMA_358.data.u8[7] = (charge_current & 0x00FF); + SMA_358.data.u8[6] = (datalayer.battery.status.max_charge_current_dA >> 8); + SMA_358.data.u8[7] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); //SOC (100.00%) SMA_3D8.data.u8[0] = (datalayer.battery.status.reported_soc >> 8); diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp index 0cf0e0b70..9c0cc2794 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp @@ -81,8 +81,6 @@ CAN_frame SMA_018 = {.FD = false, .ID = 0x018, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static uint16_t discharge_current = 0; -static uint16_t charge_current = 0; static int16_t temperature_average = 0; static uint16_t ampere_hours_remaining = 0; static uint16_t ampere_hours_max = 0; @@ -123,29 +121,6 @@ InvInitState invInitState = SYSTEM_FREQUENCY; void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the inverter CAN //Calculate values - if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0 - charge_current = - ((datalayer.battery.status.max_charge_power_W * 10) / - datalayer.battery.status.voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) - charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) - discharge_current = - ((datalayer.battery.status.max_discharge_power_W * 10) / - datalayer.battery.status.voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) - discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) - } - - if (charge_current > datalayer.battery.info.max_charge_amp_dA) { - charge_current = - datalayer.battery.info - .max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - - if (discharge_current > datalayer.battery.info.max_discharge_amp_dA) { - discharge_current = - datalayer.battery.info - .max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. - } - temperature_average = ((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2); @@ -167,11 +142,11 @@ void update_values_can_inverter() { //This function maps all the values fetched SMA_00D.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV >> 8); SMA_00D.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF); //Discharge limited current, 500 = 50A, (0.1, A) - SMA_00D.data.u8[4] = (discharge_current >> 8); - SMA_00D.data.u8[5] = (discharge_current & 0x00FF); + SMA_00D.data.u8[4] = (datalayer.battery.status.max_discharge_current_dA >> 8); + SMA_00D.data.u8[5] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); //Charge limited current, 125 =12.5A (0.1, A) - SMA_00D.data.u8[6] = (charge_current >> 8); - SMA_00D.data.u8[7] = (charge_current & 0x00FF); + SMA_00D.data.u8[6] = (datalayer.battery.status.max_charge_current_dA >> 8); + SMA_00D.data.u8[7] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); // Battery State //SOC (100.00%) diff --git a/Software/src/inverter/SOFAR-CAN.cpp b/Software/src/inverter/SOFAR-CAN.cpp index 628db1a30..eb13b7c57 100644 --- a/Software/src/inverter/SOFAR-CAN.cpp +++ b/Software/src/inverter/SOFAR-CAN.cpp @@ -207,10 +207,10 @@ void update_values_can_inverter() { //This function maps all the values fetched //Maxvoltage (eg 400.0V = 4000 , 16bits long) Charge Cutoff Voltage SOFAR_351.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8); SOFAR_351.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF); - //SOFAR_351.data.u8[2] = DC charge current limitation (Default 25.0A) - //SOFAR_351.data.u8[3] = DC charge current limitation - //SOFAR_351.data.u8[4] = DC discharge current limitation (Default 25.0A) - //SOFAR_351.data.u8[5] = DC discharge current limitation + SOFAR_351.data.u8[2] = (datalayer.battery.status.max_charge_current_dA >> 8); + SOFAR_351.data.u8[3] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); + SOFAR_351.data.u8[4] = (datalayer.battery.status.max_discharge_current_dA >> 8); + SOFAR_351.data.u8[5] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); //Minvoltage (eg 300.0V = 3000 , 16bits long) Discharge Cutoff Voltage SOFAR_351.data.u8[6] = (datalayer.battery.info.min_design_voltage_dV >> 8); SOFAR_351.data.u8[7] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF); diff --git a/Software/src/inverter/SOLAX-CAN.cpp b/Software/src/inverter/SOLAX-CAN.cpp index 32c6207b2..790c85249 100644 --- a/Software/src/inverter/SOLAX-CAN.cpp +++ b/Software/src/inverter/SOLAX-CAN.cpp @@ -10,8 +10,6 @@ // https://github.com/dalathegreat/Battery-Emulator/wiki/Solax-inverters /* Do not change code below unless you are sure what you are doing */ -static uint16_t max_charge_rate_amp = 0; -static uint16_t max_discharge_rate_amp = 0; static int16_t temperature_average = 0; static uint8_t STATE = BATTERY_ANNOUNCE; static unsigned long LastFrameTime = 0; @@ -93,50 +91,6 @@ void update_values_can_inverter() { //This function maps all the values fetched temperature_average = ((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2); - //datalayer.battery.status.max_charge_power_W (30000W max) - if (datalayer.battery.status.reported_soc > 9999) { // 99.99% - // Additional safety incase SOC% is 100, then do not charge battery further - max_charge_rate_amp = 0; - } else { // We can pass on the battery charge rate (in W) to the inverter (that takes A) - if (datalayer.battery.status.max_charge_power_W >= 30000) { - max_charge_rate_amp = 75; // Incase battery can take over 30kW, cap value to 75A - } else { // Calculate the W value into A - if (datalayer.battery.status.voltage_dV > 10) { - max_charge_rate_amp = - datalayer.battery.status.max_charge_power_W / (datalayer.battery.status.voltage_dV * 0.1); // P/U=I - } else { // We avoid dividing by 0 and crashing the board - // If we have no voltage, something has gone wrong, do not allow charging - max_charge_rate_amp = 0; - } - } - } - - //datalayer.battery.status.max_discharge_power_W (30000W max) - if (datalayer.battery.status.reported_soc < 100) { // 1.00% - // Additional safety in case SOC% is below 1, then do not discharge battery further - max_discharge_rate_amp = 0; - } else { // We can pass on the battery discharge rate to the inverter - if (datalayer.battery.status.max_discharge_power_W >= 30000) { - max_discharge_rate_amp = 75; // Incase battery can be charged with over 30kW, cap value to 75A - } else { // Calculate the W value into A - if (datalayer.battery.status.voltage_dV > 10) { - max_discharge_rate_amp = - datalayer.battery.status.max_discharge_power_W / (datalayer.battery.status.voltage_dV * 0.1); // P/U=I - } else { // We avoid dividing by 0 and crashing the board - // If we have no voltage, something has gone wrong, do not allow discharging - max_discharge_rate_amp = 0; - } - } - } - - //Cap the value according to user settings. Some inverters cannot handle large values. - if ((max_charge_rate_amp * 10) > datalayer.battery.info.max_charge_amp_dA) { - max_charge_rate_amp = (datalayer.battery.info.max_charge_amp_dA / 10); - } - if ((max_discharge_rate_amp * 10) > datalayer.battery.info.max_discharge_amp_dA) { - max_discharge_rate_amp = (datalayer.battery.info.max_discharge_amp_dA / 10); - } - // Batteries might be larger than uint16_t value can take if (datalayer.battery.info.total_capacity_Wh > 65000) { capped_capacity_Wh = 65000; @@ -156,10 +110,10 @@ void update_values_can_inverter() { //This function maps all the values fetched SOLAX_1872.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV >> 8); SOLAX_1872.data.u8[2] = (uint8_t)datalayer.battery.info.min_design_voltage_dV; SOLAX_1872.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV >> 8); - SOLAX_1872.data.u8[4] = (uint8_t)(max_charge_rate_amp * 10); - SOLAX_1872.data.u8[5] = ((max_charge_rate_amp * 10) >> 8); - SOLAX_1872.data.u8[6] = (uint8_t)(max_discharge_rate_amp * 10); - SOLAX_1872.data.u8[7] = ((max_discharge_rate_amp * 10) >> 8); + SOLAX_1872.data.u8[4] = (uint8_t)datalayer.battery.status.max_charge_current_dA; + SOLAX_1872.data.u8[5] = (datalayer.battery.status.max_charge_current_dA >> 8); + SOLAX_1872.data.u8[6] = (uint8_t)datalayer.battery.status.max_discharge_current_dA; + SOLAX_1872.data.u8[7] = (datalayer.battery.status.max_discharge_current_dA >> 8); //BMS_PackData SOLAX_1873.data.u8[0] = (uint8_t)datalayer.battery.status.voltage_dV; // OK From 91f3c8caf92e2bdbf9fe43d633f3b3d148e14987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 8 Nov 2024 23:37:16 +0200 Subject: [PATCH 44/81] Rename user specified limit in datalayer --- Software/src/datalayer/datalayer.h | 8 ++++---- Software/src/devboard/webserver/settings_html.cpp | 8 ++++---- Software/src/devboard/webserver/webserver.cpp | 6 +++--- Software/src/inverter/BYD-MODBUS.cpp | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index b4967f4b6..a26097af6 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -19,10 +19,10 @@ typedef struct { uint16_t min_cell_voltage_mV = 2700; /** The maxumum allowed deviation between cells, in milliVolt. 500 = 0.500 V */ uint16_t max_cell_voltage_deviation_mV = 500; - /** BYD CAN specific setting, max charge in deciAmpere. 300 = 30.0 A */ - uint16_t max_charge_amp_dA = BATTERY_MAX_CHARGE_AMP; - /** BYD CAN specific setting, max discharge in deciAmpere. 300 = 30.0 A */ - uint16_t max_discharge_amp_dA = BATTERY_MAX_DISCHARGE_AMP; + /** The user specified maximum allowed charge rate, in deciAmpere. 300 = 30.0 A */ + uint16_t max_user_set_charge_dA = BATTERY_MAX_CHARGE_AMP; + /** The user specified maximum allowed discharge rate, in deciAmpere. 300 = 30.0 A */ + uint16_t max_user_set_discharge_dA = BATTERY_MAX_DISCHARGE_AMP; /** uint8_t */ /** Total number of cells in the pack */ diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 8d2d5ec76..5efe2493c 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -57,11 +57,11 @@ String settings_processor(const String& var) { content += "

SOC min percentage: " + String(datalayer.battery.settings.min_percentage / 100.0, 1) + "

"; - content += - "

Max charge speed: " + String(datalayer.battery.info.max_charge_amp_dA / 10.0, 1) + - " A

"; + content += "

Max charge speed: " + + String(datalayer.battery.info.max_user_set_charge_dA / 10.0, 1) + + " A

"; content += "

Max discharge speed: " + - String(datalayer.battery.info.max_discharge_amp_dA / 10.0, 1) + + String(datalayer.battery.info.max_user_set_discharge_dA / 10.0, 1) + " A

"; // Close the block content += "
"; diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 4a1554f36..244d88466 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -209,7 +209,7 @@ void init_webserver() { return request->requestAuthentication(); if (request->hasParam("value")) { String value = request->getParam("value")->value(); - datalayer.battery.info.max_charge_amp_dA = static_cast(value.toFloat() * 10); + datalayer.battery.info.max_user_set_charge_dA = static_cast(value.toFloat() * 10); storeSettings(); request->send(200, "text/plain", "Updated successfully"); } else { @@ -223,7 +223,7 @@ void init_webserver() { return request->requestAuthentication(); if (request->hasParam("value")) { String value = request->getParam("value")->value(); - datalayer.battery.info.max_discharge_amp_dA = static_cast(value.toFloat() * 10); + datalayer.battery.info.max_user_set_discharge_dA = static_cast(value.toFloat() * 10); storeSettings(); request->send(200, "text/plain", "Updated successfully"); } else { @@ -285,7 +285,7 @@ void init_webserver() { String value = request->getParam("value")->value(); float val = value.toFloat(); - if (!(val <= datalayer.battery.info.max_charge_amp_dA && val <= CHARGER_MAX_A)) { + if (!(val <= datalayer.battery.info.max_user_set_charge_dA && val <= CHARGER_MAX_A)) { request->send(400, "text/plain", "Bad Request"); } diff --git a/Software/src/inverter/BYD-MODBUS.cpp b/Software/src/inverter/BYD-MODBUS.cpp index c37a51396..3522e18b7 100644 --- a/Software/src/inverter/BYD-MODBUS.cpp +++ b/Software/src/inverter/BYD-MODBUS.cpp @@ -65,13 +65,13 @@ void handle_update_data_modbusp301_byd() { } // Convert max discharge Amp value to max Watt user_configured_max_discharge_W = - ((datalayer.battery.info.max_discharge_amp_dA * datalayer.battery.info.max_design_voltage_dV) / 100); + ((datalayer.battery.info.max_user_set_discharge_dA * datalayer.battery.info.max_design_voltage_dV) / 100); // Use the smaller value, battery reported value OR user configured value max_discharge_W = std::min(datalayer.battery.status.max_discharge_power_W, user_configured_max_discharge_W); // Convert max charge Amp value to max Watt user_configured_max_charge_W = - ((datalayer.battery.info.max_charge_amp_dA * datalayer.battery.info.max_design_voltage_dV) / 100); + ((datalayer.battery.info.max_user_set_charge_dA * datalayer.battery.info.max_design_voltage_dV) / 100); // Use the smaller value, battery reported value OR user configured value max_charge_W = std::min(datalayer.battery.status.max_charge_power_W, user_configured_max_charge_W); From ae34a9985cc50b0628b2445d7ae9e3ce834d03b4 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sat, 9 Nov 2024 21:03:37 +0000 Subject: [PATCH 45/81] Update BMW-IX-BATTERY.h --- Software/src/battery/BMW-IX-BATTERY.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.h b/Software/src/battery/BMW-IX-BATTERY.h index 72afe6d58..602411af6 100644 --- a/Software/src/battery/BMW-IX-BATTERY.h +++ b/Software/src/battery/BMW-IX-BATTERY.h @@ -16,7 +16,7 @@ #define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500 #define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00% #define STALE_PERIOD_CONFIG \ - 180000; //Number of milliseconds before critical values are classed as stale/stuck 180000 = 180 seconds + 210000; //Number of milliseconds before critical values are classed as stale/stuck 210000 = 210 seconds void setup_battery(void); void transmit_can(CAN_frame* tx_frame, int interface); From c3e67d2f62b14ca112e30e5dabb1f99ff77a39e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 10 Nov 2024 11:49:31 +0200 Subject: [PATCH 46/81] Make discharge limits under settings datalayer --- Software/Software.ino | 16 ++++++++-------- Software/src/datalayer/datalayer.h | 8 ++++---- .../src/devboard/webserver/settings_html.cpp | 4 ++-- Software/src/devboard/webserver/webserver.cpp | 6 +++--- Software/src/inverter/BYD-MODBUS.cpp | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index adc771e1f..2b45dc5a7 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -401,11 +401,11 @@ void init_stored_settings() { } temp = settings.getUInt("MAXCHARGEAMP", false); if (temp != 0) { - datalayer.battery.info.max_charge_amp_dA = temp; + datalayer.battery.settings.max_user_set_charge_dA = temp; } temp = settings.getUInt("MAXDISCHARGEAMP", false); if (temp != 0) { - datalayer.battery.info.max_discharge_amp_dA = temp; + datalayer.battery.settings.max_user_set_discharge_dA = temp; temp = settings.getBool("USE_SCALED_SOC", false); datalayer.battery.settings.soc_scaling_active = temp; //This bool needs to be checked inside the temp!= block } // No way to know if it wasnt reset otherwise @@ -847,11 +847,11 @@ void update_calculated_values() { ((datalayer.battery.status.max_discharge_power_W * 100) / datalayer.battery.status.voltage_dV); } /* Restrict values from user settings if needed*/ - if (datalayer.battery.status.max_charge_current_dA > datalayer.battery.info.max_charge_amp_dA) { - datalayer.battery.status.max_charge_current_dA = datalayer.battery.info.max_charge_amp_dA; + if (datalayer.battery.status.max_charge_current_dA > datalayer.battery.settings.max_user_set_charge_dA) { + datalayer.battery.status.max_charge_current_dA = datalayer.battery.settings.max_user_set_charge_dA; } - if (datalayer.battery.status.max_discharge_current_dA > datalayer.battery.info.max_discharge_amp_dA) { - datalayer.battery.status.max_discharge_current_dA = datalayer.battery.info.max_discharge_amp_dA; + if (datalayer.battery.status.max_discharge_current_dA > datalayer.battery.settings.max_user_set_discharge_dA) { + datalayer.battery.status.max_discharge_current_dA = datalayer.battery.settings.max_user_set_discharge_dA; } if (datalayer.battery.settings.soc_scaling_active) { @@ -991,8 +991,8 @@ void storeSettings() { datalayer.battery.settings.max_percentage / 10); // Divide by 10 for backwards compatibility settings.putUInt("MINPERCENTAGE", datalayer.battery.settings.min_percentage / 10); // Divide by 10 for backwards compatibility - settings.putUInt("MAXCHARGEAMP", datalayer.battery.info.max_charge_amp_dA); - settings.putUInt("MAXDISCHARGEAMP", datalayer.battery.info.max_discharge_amp_dA); + settings.putUInt("MAXCHARGEAMP", datalayer.battery.settings.max_user_set_charge_dA); + settings.putUInt("MAXDISCHARGEAMP", datalayer.battery.settings.max_user_set_discharge_dA); settings.putBool("USE_SCALED_SOC", datalayer.battery.settings.soc_scaling_active); settings.end(); } diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index a26097af6..7e1880af4 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -19,10 +19,6 @@ typedef struct { uint16_t min_cell_voltage_mV = 2700; /** The maxumum allowed deviation between cells, in milliVolt. 500 = 0.500 V */ uint16_t max_cell_voltage_deviation_mV = 500; - /** The user specified maximum allowed charge rate, in deciAmpere. 300 = 30.0 A */ - uint16_t max_user_set_charge_dA = BATTERY_MAX_CHARGE_AMP; - /** The user specified maximum allowed discharge rate, in deciAmpere. 300 = 30.0 A */ - uint16_t max_user_set_discharge_dA = BATTERY_MAX_DISCHARGE_AMP; /** uint8_t */ /** Total number of cells in the pack */ @@ -111,6 +107,10 @@ typedef struct { * you want the inverter to be able to use. At this real SOC, the inverter * will "see" 100% */ uint16_t max_percentage = BATTERY_MAXPERCENTAGE; + /** The user specified maximum allowed charge rate, in deciAmpere. 300 = 30.0 A */ + uint16_t max_user_set_charge_dA = BATTERY_MAX_CHARGE_AMP; + /** The user specified maximum allowed discharge rate, in deciAmpere. 300 = 30.0 A */ + uint16_t max_user_set_discharge_dA = BATTERY_MAX_DISCHARGE_AMP; } DATALAYER_BATTERY_SETTINGS_TYPE; typedef struct { diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 5efe2493c..caac64d40 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -58,10 +58,10 @@ String settings_processor(const String& var) { ";'>SOC min percentage: " + String(datalayer.battery.settings.min_percentage / 100.0, 1) + " "; content += "

Max charge speed: " + - String(datalayer.battery.info.max_user_set_charge_dA / 10.0, 1) + + String(datalayer.battery.settings.max_user_set_charge_dA / 10.0, 1) + " A

"; content += "

Max discharge speed: " + - String(datalayer.battery.info.max_user_set_discharge_dA / 10.0, 1) + + String(datalayer.battery.settings.max_user_set_discharge_dA / 10.0, 1) + " A

"; // Close the block content += "
"; diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 244d88466..81d77567d 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -209,7 +209,7 @@ void init_webserver() { return request->requestAuthentication(); if (request->hasParam("value")) { String value = request->getParam("value")->value(); - datalayer.battery.info.max_user_set_charge_dA = static_cast(value.toFloat() * 10); + datalayer.battery.settings.max_user_set_charge_dA = static_cast(value.toFloat() * 10); storeSettings(); request->send(200, "text/plain", "Updated successfully"); } else { @@ -223,7 +223,7 @@ void init_webserver() { return request->requestAuthentication(); if (request->hasParam("value")) { String value = request->getParam("value")->value(); - datalayer.battery.info.max_user_set_discharge_dA = static_cast(value.toFloat() * 10); + datalayer.battery.settings.max_user_set_discharge_dA = static_cast(value.toFloat() * 10); storeSettings(); request->send(200, "text/plain", "Updated successfully"); } else { @@ -285,7 +285,7 @@ void init_webserver() { String value = request->getParam("value")->value(); float val = value.toFloat(); - if (!(val <= datalayer.battery.info.max_user_set_charge_dA && val <= CHARGER_MAX_A)) { + if (!(val <= datalayer.battery.settings.max_user_set_charge_dA && val <= CHARGER_MAX_A)) { request->send(400, "text/plain", "Bad Request"); } diff --git a/Software/src/inverter/BYD-MODBUS.cpp b/Software/src/inverter/BYD-MODBUS.cpp index 3522e18b7..7cee6b8d3 100644 --- a/Software/src/inverter/BYD-MODBUS.cpp +++ b/Software/src/inverter/BYD-MODBUS.cpp @@ -65,13 +65,13 @@ void handle_update_data_modbusp301_byd() { } // Convert max discharge Amp value to max Watt user_configured_max_discharge_W = - ((datalayer.battery.info.max_user_set_discharge_dA * datalayer.battery.info.max_design_voltage_dV) / 100); + ((datalayer.battery.settings.max_user_set_discharge_dA * datalayer.battery.info.max_design_voltage_dV) / 100); // Use the smaller value, battery reported value OR user configured value max_discharge_W = std::min(datalayer.battery.status.max_discharge_power_W, user_configured_max_discharge_W); // Convert max charge Amp value to max Watt user_configured_max_charge_W = - ((datalayer.battery.info.max_user_set_charge_dA * datalayer.battery.info.max_design_voltage_dV) / 100); + ((datalayer.battery.settings.max_user_set_charge_dA * datalayer.battery.info.max_design_voltage_dV) / 100); // Use the smaller value, battery reported value OR user configured value max_charge_W = std::min(datalayer.battery.status.max_charge_power_W, user_configured_max_charge_W); From 5650c991abd592b265805ab1907e2ece2ded09f2 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Sun, 10 Nov 2024 14:24:12 +0000 Subject: [PATCH 47/81] Reduce iX stale value sensitivity --- Software/src/battery/BMW-IX-BATTERY.cpp | 2 +- Software/src/battery/BMW-IX-BATTERY.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index a04cb0930..2f316097a 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -672,7 +672,7 @@ void receive_can_battery(CAN_frame rx_frame) { 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. - if ((rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]) == 10000 && + if ((rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]) == 10000 || (rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]) == 10000) { //Qualifier Invalid Mode - Request Reboot #ifdef DEBUG_VIA_USB Serial.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset"); diff --git a/Software/src/battery/BMW-IX-BATTERY.h b/Software/src/battery/BMW-IX-BATTERY.h index 602411af6..a53367781 100644 --- a/Software/src/battery/BMW-IX-BATTERY.h +++ b/Software/src/battery/BMW-IX-BATTERY.h @@ -16,7 +16,7 @@ #define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500 #define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00% #define STALE_PERIOD_CONFIG \ - 210000; //Number of milliseconds before critical values are classed as stale/stuck 210000 = 210 seconds + 300000; //Number of milliseconds before critical values are classed as stale/stuck 300000 = 300 seconds void setup_battery(void); void transmit_can(CAN_frame* tx_frame, int interface); From ec00b9be8f0b09cbf60f004dd345e35b0d36541f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 11 Nov 2024 20:26:35 +0200 Subject: [PATCH 48/81] Refactor CAN logging --- Software/Software.ino | 42 ++++++++++++---------- Software/USER_SETTINGS.h | 4 +-- Software/src/battery/TEST-FAKE-BATTERY.cpp | 24 ------------- 3 files changed, 25 insertions(+), 45 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index 2b45dc5a7..c85c376d0 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -614,31 +614,32 @@ void init_equipment_stop_button() { #endif -#ifdef CAN_FD -// Functions -#ifdef DEBUG_CANFD_DATA -enum frameDirection { MSG_RX, MSG_TX }; -void print_canfd_frame(CANFDMessage rx_frame, frameDirection msgDir); // Needs to be declared before it is defined -void print_canfd_frame(CANFDMessage rx_frame, frameDirection msgDir) { - int i = 0; +enum frameDirection { MSG_RX, MSG_TX }; //RX = 0, TX = 1 +void print_can_frame(CAN_frame frame, frameDirection msgDir); +void print_can_frame(CAN_frame frame, frameDirection msgDir) { + uint8_t i = 0; + Serial.print(millis()); + Serial.print(" "); (msgDir == 0) ? Serial.print("RX ") : Serial.print("TX "); - Serial.print(rx_frame.id, HEX); + Serial.print(frame.ID, HEX); + Serial.print(" "); + Serial.print(frame.DLC); Serial.print(" "); - for (i = 0; i < rx_frame.len; i++) { - Serial.print(rx_frame.data[i] < 16 ? "0" : ""); - Serial.print(rx_frame.data[i], HEX); + for (i = 0; i < frame.DLC; i++) { + Serial.print(frame.data.u8[i] < 16 ? "0" : ""); + Serial.print(frame.data.u8[i], HEX); Serial.print(" "); } Serial.println(" "); } -#endif + +#ifdef CAN_FD +// Functions void receive_canfd() { // This section checks if we have a complete CAN-FD message incoming CANFDMessage frame; if (canfd.available()) { canfd.receive(frame); -#ifdef DEBUG_CANFD_DATA - print_canfd_frame(frame, frameDirection(MSG_RX)); -#endif + CAN_frame rx_frame; rx_frame.ID = frame.id; rx_frame.ext_ID = frame.ext; @@ -1078,6 +1079,9 @@ void transmit_can(CAN_frame* tx_frame, int interface) { if (!allowed_to_send_CAN) { return; } +#ifdef DEBUG_CAN_DATA + print_can_frame(*tx_frame, frameDirection(MSG_TX)); +#endif //DEBUG_CAN_DATA switch (interface) { case CAN_NATIVE: @@ -1120,10 +1124,6 @@ void transmit_can(CAN_frame* tx_frame, int interface) { send_ok = canfd.tryToSend(MCP2518Frame); if (!send_ok) { set_event(EVENT_CANFD_BUFFER_FULL, interface); - } else { -#ifdef DEBUG_CANFD_DATA - print_canfd_frame(MCP2518Frame, frameDirection(MSG_TX)); -#endif } #else // Interface not compiled, and settings try to use it set_event(EVENT_INTERFACE_MISSING, interface); @@ -1136,6 +1136,10 @@ void transmit_can(CAN_frame* tx_frame, int interface) { } void receive_can(CAN_frame* rx_frame, int interface) { +#ifdef DEBUG_CAN_DATA + print_can_frame(*rx_frame, frameDirection(MSG_RX)); +#endif //DEBUG_CAN_DATA + if (interface == can_config.battery) { receive_can_battery(*rx_frame); } diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 26272ca76..2f30bf26a 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -20,7 +20,7 @@ //#define KIA_E_GMP_BATTERY //#define KIA_HYUNDAI_HYBRID_BATTERY //#define MG_5_BATTERY -//#define NISSAN_LEAF_BATTERY +#define NISSAN_LEAF_BATTERY //#define PYLON_BATTERY //#define RJXZS_BMS //#define RENAULT_KANGOO_BATTERY @@ -54,7 +54,7 @@ /* Other options */ //#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs (WARNING, raises CPU load, do not use for production) -//#define DEBUG_CANFD_DATA //Enable this line to have the USB port output CAN-FD data while program runs (WARNING, raises CPU load, do not use for production) +//#define DEBUG_CAN_DATA //Enable this line to print incoming/outgoing CAN & CAN-FD messages to USB serial (WARNING, raises CPU load, do not use for production) //#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting //#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence //#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM for CONTACTOR_CONTROL, which lowers power consumption and heat generation. CONTACTOR_CONTROL must be enabled. diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index 630fa5360..423654aaa 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -130,35 +130,11 @@ void update_values_battery2() { // Handle the values coming in from battery #2 void receive_can_battery2(CAN_frame rx_frame) { datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - // All CAN messages recieved will be logged via serial - Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D - Serial.print(" "); - Serial.print(rx_frame.ID, HEX); - Serial.print(" "); - Serial.print(rx_frame.DLC); - Serial.print(" "); - for (int i = 0; i < rx_frame.DLC; ++i) { - Serial.print(rx_frame.data.u8[i], HEX); - Serial.print(" "); - } - Serial.println(""); } #endif // DOUBLE_BATTERY void receive_can_battery(CAN_frame rx_frame) { datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - // All CAN messages recieved will be logged via serial - Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D - Serial.print(" "); - Serial.print(rx_frame.ID, HEX); - Serial.print(" "); - Serial.print(rx_frame.DLC); - Serial.print(" "); - for (int i = 0; i < rx_frame.DLC; ++i) { - Serial.print(rx_frame.data.u8[i], HEX); - Serial.print(" "); - } - Serial.println(""); } void send_can_battery() { unsigned long currentMillis = millis(); From 538d7b6ac0013d8220ca543b7fea99a3f324881a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 11 Nov 2024 20:56:40 +0200 Subject: [PATCH 49/81] Remove LEAF --- Software/USER_SETTINGS.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 2f30bf26a..2f5428c9b 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -20,7 +20,7 @@ //#define KIA_E_GMP_BATTERY //#define KIA_HYUNDAI_HYBRID_BATTERY //#define MG_5_BATTERY -#define NISSAN_LEAF_BATTERY +//#define NISSAN_LEAF_BATTERY //#define PYLON_BATTERY //#define RJXZS_BMS //#define RENAULT_KANGOO_BATTERY From 72c2373cf4680913330ea3dd251618b0927b5cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 12 Nov 2024 00:02:14 +0200 Subject: [PATCH 50/81] Fix inverter missing event for Foxess --- Software/src/inverter/FOXESS-CAN.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/Software/src/inverter/FOXESS-CAN.cpp b/Software/src/inverter/FOXESS-CAN.cpp index 3909bbbe3..e4b3b34ca 100644 --- a/Software/src/inverter/FOXESS-CAN.cpp +++ b/Software/src/inverter/FOXESS-CAN.cpp @@ -28,7 +28,6 @@ static int16_t current_per_pack = 0; static uint8_t temperature_max_per_pack = 0; static uint8_t temperature_min_per_pack = 0; static uint8_t current_pack_info = 0; -static uint8_t inverterStillAlive = 60; // Inverter can be missing for 1minute on startup static bool send_cellvoltages = false; static unsigned long previousMillisCellvoltage = 0; // Store the last time a cellvoltage CAN messages were sent @@ -362,16 +361,6 @@ void update_values_can_inverter() { //This function maps all the CAN values fet temperature_average = ((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2); - if (inverterStillAlive > 0) { - inverterStillAlive--; - } - - if (!inverterStillAlive) { - set_event(EVENT_CAN_INVERTER_MISSING, 0); - } else { - clear_event(EVENT_CAN_INVERTER_MISSING); - } - //Put the values into the CAN messages //BMS_Limits FOXESS_1872.data.u8[0] = (uint8_t)datalayer.battery.info.max_design_voltage_dV; @@ -686,7 +675,7 @@ void send_can_inverter() { // This function loops as fast as possible void receive_can_inverter(CAN_frame rx_frame) { if (rx_frame.ID == 0x1871) { - inverterStillAlive = CAN_STILL_ALIVE; + datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; if (rx_frame.data.u8[0] == 0x03) { //0x1871 [0x03, 0x06, 0x17, 0x05, 0x09, 0x09, 0x28, 0x22] //This message is sent by the inverter every '6' seconds (0.5s after the pack serial numbers) //and contains a timestamp in bytes 2-7 i.e. ,,
,,, From ff496f3d4e5729fbc7f5c6ca16b49c8b2f350a11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 13 Nov 2024 12:11:07 +0200 Subject: [PATCH 51/81] Add Range Rover PHEV support --- Software/USER_SETTINGS.h | 1 + Software/src/battery/BATTERIES.h | 4 + .../src/battery/RANGE-ROVER-PHEV-BATTERY.cpp | 329 ++++++++++++++++++ .../src/battery/RANGE-ROVER-PHEV-BATTERY.h | 18 + Software/src/devboard/webserver/webserver.cpp | 3 + 5 files changed, 355 insertions(+) create mode 100644 Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp create mode 100644 Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 2f5428c9b..c6351a121 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -23,6 +23,7 @@ //#define NISSAN_LEAF_BATTERY //#define PYLON_BATTERY //#define RJXZS_BMS +//#define RANGE_ROVER_PHEV_BATTERY //#define RENAULT_KANGOO_BATTERY //#define RENAULT_TWIZY_BATTERY //#define RENAULT_ZOE_GEN1_BATTERY diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index ecc21ad1c..3bc651892 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -58,6 +58,10 @@ #include "RJXZS-BMS.h" #endif +#ifdef RANGE_ROVER_PHEV_BATTERY +#include "RANGE-ROVER-PHEV-BATTERY.h" +#endif + #ifdef RENAULT_KANGOO_BATTERY #include "RENAULT-KANGOO-BATTERY.h" #endif diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp new file mode 100644 index 000000000..d530c2d55 --- /dev/null +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp @@ -0,0 +1,329 @@ +#include "../include.h" +#ifdef RANGE_ROVER_PHEV_BATTERY +#include "../datalayer/datalayer.h" +#include "../devboard/utils/events.h" +#include "RANGE-ROVER-PHEV-BATTERY.h" + +/* TODO +- LOG files from vehicle needed to determine CAN content needed to send towards battery! + - BCCM_PMZ_A (0x18B 50ms) + - BCCMB_PMZ_A (0x224 90ms) + - BCM_CCP_RX_PMZCAN (0x601 non cyclic) + - EPIC_PMZ_B (0x009 non cyclic) + - GWM_FuelPumpEnableDataControl_PMZ (0x1F8 non cyclic) + - GWM_IgnitionAuthDataTarget_PMZ (0x004 non cyclic) + - GWM_PMZ_A (0x008 10ms cyclic) + - GWM_PMZ_B -F, G-I, Immo, K-P + - 0x010 10ms + - 0x090 10ms + - 0x108 20ms + - 0x110 20ms + - 0x1d0 80ms + - 0x490 900ms + - 0x1B0 80ms + - 0x460 720ms + - 0x006 non cyclic immo + - 0x450 600ms + - 0x2b8 180ms + - 0x388 200ms + - 0x2b0 180ms + - 0x380 80ms + - GWM_PMZ_V_HYBRID (0x18d 60ms) + - HVAC_PMZ_A-E + - 0x1a8 70ms + - 0x210 100ms + - 0x300 200ms + - 0x440 180ms + - 0x0c0 10ms + - PCM_PMZ_C_Hybrid C, D, H, M + - 0x030 15ms + - 0x304 180ms + - 0x1C0 80ms + - 0x434 350ms + - TCU_PMZ_A + - 0x014 non cyclic, command from TCU, most likely not needed +- Determine CRC calculation +- Figure out contactor closing requirements +*/ + +/* Do not change code below unless you are sure what you are doing */ +static unsigned long previousMillis50ms = 0; // will store last time a 50ms CAN Message was sent + +//CAN content from battery +static bool StatusCAT5BPOChg = false; +static bool StatusCAT4Derate = false; +static uint8_t OCMonitorStatus = 0; +static bool StatusCAT3 = false; +static bool IsolationStatus = false; +static bool HVILStatus = false; +static bool ContactorStatus = false; +static uint8_t StatusGpCounter = 0; +static bool WeldCheckStatus = false; +static bool StatusCAT7NowBPO = false; +static bool StatusCAT6DlyBPO = false; +static uint8_t StatusGpCS = 0; +static uint8_t CAT6Count = 0; +static bool EndOfCharge = false; +static bool DerateWarning = false; +static bool PrechargeAllowed = false; +static uint8_t DischargeExtGpCounter = 0; // Counter 0-15 +static uint8_t DischargeExtGpCS = 0; // CRC +static uint16_t DischargeVoltageLimit = 0; //Min voltage battery allows discharging to +static uint16_t DischargePowerLimitExt = 0; //Momentary Discharge power limit kW*0.01 (0-655) +static uint16_t DischargeContPwrLmt = 0; //Longterm Discharge power limit kW*0.01 (0-655) +static uint8_t PwrGpCS = 0; // CRC +static uint8_t PwrGpCounter = 0; // Counter 0-15 +static uint16_t VoltageExt = 370; // Voltage of the HV Battery +static uint16_t VoltageBus = 0; // Voltage on the high-voltage DC bus +static int32_t CurrentExt = + 209715; //Positive - discharge, Negative Charge (0 - 16777215) Scaling: 0.025 Offset: -209715.175 Units: Amps +static bool HVIsolationTestRunning = false; +static uint16_t VoltageOC = + 0; //The instantaneous equivalent open-circuit voltage of the high voltage battery. This is used by the high-voltage inverter in power prediction and derating calculations. +static uint16_t DchCurrentLimit = + 0; // A, 'Maximum current that can be delivered by the HV Battery during motoring mode i.e during discharging. +static uint16_t ChgCurrentLimit = + 0; // - 1023 A, Maximum current that can be transferred into the HV Battery during generating mode i.e during charging. Charging is neagtive and discharging is positive. +static uint16_t ChargeContPwrLmt = 0; //Longterm charge power limit kW*0.01 (0-655) +static uint16_t ChargePowerLimitExt = 0; //Momentary Charge power limit kW*0.01 (0-655) +static uint8_t ChgExtGpCS = 0; // CRC +static uint8_t ChgExtGpCounter = 0; //counter 0-15 +static uint16_t ChargeVoltageLimit = 500; //Max voltage limit during charging of the HV Battery. +static uint8_t CurrentWarning = 0; // 0 normal, 1 cell overcurrent, 2 cell undercurrent +static uint8_t TempWarning = 0; // 0 normal, 1 cell overtemp, 2 cell undertemp +static int8_t TempUpLimit = 0; //Upper temperature limit. +static uint8_t CellVoltWarning = 0; // 0 normal, 1 cell overvoltage, 2 cell undervoltage +static bool CCCVChargeMode = false; //0 CC, 1 = CV +static uint16_t CellVoltUpLimit = 0; //mV, Upper cell voltage limit +static uint16_t SOCHighestCell = 0; //0.01, % +static uint16_t SOCLowestCell = 0; //0.01, % +static uint16_t SOCAverage = 0; //0.01, % +static bool WakeUpTopUpReq = + false; //The HV Battery can trigger a vehicle wake-up to request its State of Charge to be increased. +static bool WakeUpThermalReq = + false; //The HV Battery can trigger a vehicle wake-up in order to be thermally managed (ie. cooled down OR warmed up). +static bool WakeUpDchReq = + false; //The HV Battery can trigger a vehicle wake-up to request its State of Charge to be reduced. +static uint16_t StateofHealth = 0; +static uint16_t EstimatedLossChg = + 0; //fact0.001, kWh Expected energy which will be lost during charging (at the rate given by VSCEstChargePower) due to resistance within the HV Battery. +static bool CoolingRequest = + false; //HV Battery cooling request to be cooled by the eAC/chiller as its cooling needs exceed the LTR cooling loop capability. +static uint16_t EstimatedLossDch = + 0; //fact0.001, kWh Expected energy which will be lost during discharging (at the rate given by VSCEstDischargePower) due to resistance within the HV Battery. +static uint8_t FanDutyRequest = + 0; //Request from the HV Battery cooling system to demand a change of duty for the electrical engine cooling fan speed (whilst using its LTR cooling loop). +static bool ValveCtrlStat = false; //0 Chiller/Heater cooling loop requested , 1 LTR cooling loop requested +static uint16_t EstLossDchTgtSoC = + 0; //fact0.001, kWh Expected energy which will be lost during discharging (at the rate given by VSCEstimatedDchPower) from the target charging SoC (PHEV: HVBattEnergyUsableMax, BEV: HVBattEnergyUsableBulk) down to HVBattEnergyUsableMin, due to resistance within the Traction Battery. +static uint8_t HeatPowerGenChg = + 0; //fact0.1, kW, Estimated average heat generated by battery if charged at the rate given by VSCEstimatedChgPower. +static uint8_t HeatPowerGenDch = + 0; //fact0.1, kW, Estimated average heat generated by battery if discharged at the rate given by VSCEstimatedDchPower. +static uint8_t WarmupRateChg = + 0; //fact0.1, C/min , Expected average rate at which the battery will self-heat if charged at the rate given by VSCEstimatedChgPower. +static uint8_t WarmupRateDch = + 0; //fact0.1, C/min , Expected average rate at which the battery will self-heat if discharged at the rate given by VSCEstimatedDchPower. +static uint16_t CellVoltageMax = 3700; +static uint16_t CellVoltageMin = 3700; +static int8_t CellTempAverage = 0; //factor0.5, -40 offset +static int8_t CellTempColdest = 0; //factor0.5, -40 offset +static int8_t CellTempHottest = 0; //factor0.5, -40 offset +static uint8_t HeaterCtrlStat = 0; //factor1, 0 offset +static bool ThermalOvercheck = false; // 0 OK, 1 NOT OK +static int8_t InletCoolantTemp = 0; //factor0.5, -40 offset +static bool ClntPumpDiagStat_UB = false; +static bool InletCoolantTemp_UB = false; +static bool CoolantLevel = false; // Coolant level OK , 1 NOT OK +static bool ClntPumpDiagStat = false; // 0 Pump OK, 1 NOT OK +static uint8_t MILRequest = 0; //No req, 1 ON, 2 FLASHING, 3 unused +static uint16_t EnergyAvailable = 0; //fac0.05 , The total energy available from the HV Battery +static uint16_t EnergyUsableMax = 0; //fac0.05 , The total energy available from the HV Battery at its maximum SOC +static uint16_t EnergyUsableMin = 0; //fac0.05 , The total energy available from the HV Battery at its minimum SOC +static uint16_t TotalCapacity = + 0; //fac0.1 , Total Battery capacity in Kwh. This will reduce over the lifetime of the HV Battery. + +//CAN messages needed by battery (LOG needed!) +CAN_frame RANGE_ROVER_18B = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x18B, //CONTENT??? TODO + .data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + +void update_values_battery() { + + datalayer.battery.status.real_soc = SOCAverage; + + datalayer.battery.status.soh_pptt = StateofHealth * 10; + + datalayer.battery.status.voltage_dV = VoltageExt * 10; + + datalayer.battery.status.current_dA = (CurrentExt * 0.025) - 209715; + + datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt + ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); + + datalayer.battery.status.max_charge_power_W = (ChargeContPwrLmt * 10) - 6550; + + datalayer.battery.status.max_discharge_power_W = (DischargeContPwrLmt * 10) - 6550; + + 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.cell_max_voltage_mV = CellVoltageMax; + + datalayer.battery.status.cell_min_voltage_mV = CellVoltageMin; + + datalayer.battery.status.temperature_min_dC = CellTempColdest * 10; + + datalayer.battery.status.temperature_max_dC = CellTempHottest * 10; + + datalayer.battery.info.max_design_voltage_dV = ChargeVoltageLimit * 10; + + datalayer.battery.info.min_design_voltage_dV = DischargeVoltageLimit * 10; +} + +void receive_can_battery(CAN_frame rx_frame) { + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + switch (rx_frame.ID) { + case 0x080: // 15ms + StatusCAT5BPOChg = (rx_frame.data.u8[0] & 0x01); + StatusCAT4Derate = (rx_frame.data.u8[0] & 0x02) >> 1; + OCMonitorStatus = (rx_frame.data.u8[0] & 0x0C) >> 2; + StatusCAT3 = (rx_frame.data.u8[0] & 0x10) >> 4; + IsolationStatus = (rx_frame.data.u8[0] & 0x20) >> 5; + HVILStatus = (rx_frame.data.u8[0] & 0x40) >> 6; + ContactorStatus = (rx_frame.data.u8[0] & 0x80) >> 7; + StatusGpCounter = (rx_frame.data.u8[1] & 0x0F); + WeldCheckStatus = (rx_frame.data.u8[1] & 0x20) >> 5; + StatusCAT7NowBPO = (rx_frame.data.u8[1] & 0x40) >> 6; + StatusCAT6DlyBPO = (rx_frame.data.u8[1] & 0x80) >> 7; + StatusGpCS = rx_frame.data.u8[2]; + CAT6Count = rx_frame.data.u8[3] & 0x7F; + EndOfCharge = (rx_frame.data.u8[6] & 0x04) >> 2; + DerateWarning = (rx_frame.data.u8[6] & 0x08) >> 3; + PrechargeAllowed = (rx_frame.data.u8[6] & 0x10) >> 4; + break; + case 0x100: // 20ms + DischargeExtGpCounter = (rx_frame.data.u8[0] & 0x0F); + DischargeExtGpCS = rx_frame.data.u8[1]; + DischargeVoltageLimit = (((rx_frame.data.u8[2] & 0x03) << 8) | rx_frame.data.u8[3]); + DischargePowerLimitExt = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + DischargeContPwrLmt = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]); + break; + case 0x102: // 20ms + PwrGpCS = rx_frame.data.u8[0]; + PwrGpCounter = (rx_frame.data.u8[1] & 0x3C) >> 2; + VoltageExt = (((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[2]); + VoltageBus = (((rx_frame.data.u8[3] & 0x03) << 8) | rx_frame.data.u8[4]); + CurrentExt = ((rx_frame.data.u8[5] << 8) | (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]); + break; + case 0x104: // 20ms + HVIsolationTestRunning = (rx_frame.data.u8[2] & 0x10) >> 4; + VoltageOC = (((rx_frame.data.u8[2] & 0x03) << 8) | rx_frame.data.u8[3]); + DchCurrentLimit = (((rx_frame.data.u8[4] & 0x03) << 8) | rx_frame.data.u8[5]); + ChgCurrentLimit = (((rx_frame.data.u8[6] & 0x03) << 8) | rx_frame.data.u8[7]); + break; + case 0x10A: // 20ms + ChargeContPwrLmt = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]); + ChargePowerLimitExt = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); + ChgExtGpCS = rx_frame.data.u8[4]; + ChgExtGpCounter = (rx_frame.data.u8[5] >> 4); + ChargeVoltageLimit = (((rx_frame.data.u8[6] & 0x03) << 8) | rx_frame.data.u8[7]); + break; + case 0x198: // 60ms + CurrentWarning = (rx_frame.data.u8[4] & 0x03); + TempWarning = ((rx_frame.data.u8[4] & 0x0C) >> 2); + TempUpLimit = (rx_frame.data.u8[5] / 2) - 40; + CellVoltWarning = ((rx_frame.data.u8[6] & 0x60) >> 5); + CCCVChargeMode = ((rx_frame.data.u8[6] & 0x80) >> 7); + CellVoltUpLimit = (((rx_frame.data.u8[6] & 0x1F) << 8) | rx_frame.data.u8[7]); + break; + case 0x220: // 100ms + SOCHighestCell = (((rx_frame.data.u8[0] & 0x3F) << 8) | rx_frame.data.u8[1]); + SOCLowestCell = (((rx_frame.data.u8[2] & 0x3F) << 8) | rx_frame.data.u8[3]); + SOCAverage = (((rx_frame.data.u8[4] & 0x3F) << 8) | rx_frame.data.u8[5]); + WakeUpTopUpReq = ((rx_frame.data.u8[6] & 0x04) >> 2); + WakeUpThermalReq = ((rx_frame.data.u8[6] & 0x08) >> 3); + WakeUpDchReq = ((rx_frame.data.u8[6] & 0x10) >> 4); + StateofHealth = (((rx_frame.data.u8[6] & 0x03) << 8) | rx_frame.data.u8[7]); + break; + case 0x308: // 190ms + EstimatedLossChg = (((rx_frame.data.u8[0] & 0x03) << 8) | rx_frame.data.u8[1]); + CoolingRequest = ((rx_frame.data.u8[6] & 0x04) >> 2); + EstimatedLossDch = (((rx_frame.data.u8[2] & 0x03) << 8) | rx_frame.data.u8[3]); + FanDutyRequest = (rx_frame.data.u8[4] & 0x7F); + ValveCtrlStat = ((rx_frame.data.u8[4] & 0x80) >> 7); + EstLossDchTgtSoC = (((rx_frame.data.u8[5] & 0x03) << 8) | rx_frame.data.u8[6]); + break; + case 0x424: // 280ms + HeatPowerGenChg = (rx_frame.data.u8[0] & 0x7F); + HeatPowerGenDch = (rx_frame.data.u8[1] & 0x7F); + WarmupRateChg = (rx_frame.data.u8[2] & 0x3F); + WarmupRateDch = (rx_frame.data.u8[3] & 0x3F); + CellVoltageMax = (((rx_frame.data.u8[4] & 0x1F) << 8) | rx_frame.data.u8[5]); + CellVoltageMin = (((rx_frame.data.u8[6] & 0x1F) << 8) | rx_frame.data.u8[7]); + break; + case 0x448: // 600ms + CellTempAverage = (rx_frame.data.u8[0] / 2) - 40; + CellTempColdest = (rx_frame.data.u8[1] / 2) - 40; + CellTempHottest = (rx_frame.data.u8[2] / 2) - 40; + HeaterCtrlStat = (rx_frame.data.u8[3] & 0x7F); + ThermalOvercheck = ((rx_frame.data.u8[3] & 0x80) >> 7); + InletCoolantTemp = rx_frame.data.u8[5]; + ClntPumpDiagStat_UB = ((rx_frame.data.u8[6] & 0x04) >> 2); + InletCoolantTemp_UB = ((rx_frame.data.u8[6] & 0x08) >> 3); + CoolantLevel = ((rx_frame.data.u8[6] & 0x10) >> 4); + ClntPumpDiagStat = ((rx_frame.data.u8[6] & 0x20) >> 5); + MILRequest = ((rx_frame.data.u8[6] & 0xC0) >> 6); + break; + case 0x464: // 800ms + EnergyAvailable = (((rx_frame.data.u8[0] & 0x07) << 8) | rx_frame.data.u8[1]); + EnergyUsableMax = (((rx_frame.data.u8[2] & 0x07) << 8) | rx_frame.data.u8[3]); + EnergyUsableMin = (((rx_frame.data.u8[4] & 0x07) << 8) | rx_frame.data.u8[5]); + TotalCapacity = (((rx_frame.data.u8[6] & 0x0F) << 8) | rx_frame.data.u8[7]); + break; + case 0x5A2: //Not periodically transferred + break; + case 0x656: //Not periodically transferred + break; + case 0x657: //Not periodically transferred + break; + case 0x6C8: //Not periodically transferred + break; + case 0x6C9: //Not periodically transferred + break; + case 0x6CA: //Not periodically transferred + break; + case 0x6CB: //Not periodically transferred + break; + case 0x7EC: //Not periodically transferred + break; + default: + break; + } +} + +void send_can_battery() { + unsigned long currentMillis = millis(); + // Send 50ms CAN Message + if (currentMillis - previousMillis50ms >= INTERVAL_50_MS) { + + previousMillis50ms = currentMillis; + + transmit_can(&RANGE_ROVER_18B, can_config.battery); + } +} + +void setup_battery(void) { // Performs one time setup at startup +#ifdef DEBUG_VIA_USB + Serial.println("Range Rover PHEV battery (L494 / L405) selected"); +#endif //DEBUG_VIA_USB + + datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; + datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; + datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; + datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; +} + +#endif //RANGE_ROVER_PHEV_BATTERY diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h new file mode 100644 index 000000000..c4cdcf07b --- /dev/null +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h @@ -0,0 +1,18 @@ +#ifndef RANGE_ROVER_PHEV_BATTERY_H +#define RANGE_ROVER_PHEV_BATTERY_H +#include +#include "../include.h" + +#define BATTERY_SELECTED + +/* Change the following to suit your battery */ +#define MAX_PACK_VOLTAGE_DV 5000 //TODO: Configure +#define MIN_PACK_VOLTAGE_DV 0 //TODO: Configure +#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value +#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value +#define MAX_CELL_DEVIATION_MV 500 //TODO: Configure + +void setup_battery(void); +void transmit_can(CAN_frame* tx_frame, int interface); + +#endif diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 09ebe9d47..8eab7631e 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -558,6 +558,9 @@ String processor(const String& var) { #ifdef RJXZS_BMS content += "RJXZS BMS, DIY battery"; #endif // RJXZS_BMS +#ifdef RANGE_ROVER_PHEV_BATTERY + content += "Range Rover 13kWh PHEV battery (L494/L405)"; +#endif //RANGE_ROVER_PHEV_BATTERY #ifdef RENAULT_KANGOO_BATTERY content += "Renault Kangoo"; #endif // RENAULT_KANGOO_BATTERY From 3a9aefd58c2668e9165231fb46f156ab488ff0e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 13 Nov 2024 21:28:46 +0200 Subject: [PATCH 52/81] Fix endianess, simplify inverter brand read --- Software/src/inverter/BYD-CAN.cpp | 39 +++++++++++-------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/Software/src/inverter/BYD-CAN.cpp b/Software/src/inverter/BYD-CAN.cpp index 5cf999a34..ae0bb560a 100644 --- a/Software/src/inverter/BYD-CAN.cpp +++ b/Software/src/inverter/BYD-CAN.cpp @@ -7,13 +7,6 @@ static unsigned long previousMillis2s = 0; // will store last time a 2s CAN Message was send static unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send static unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send -static uint8_t char1_151 = 0; -static uint8_t char2_151 = 0; -static uint8_t char3_151 = 0; -static uint8_t char4_151 = 0; -static uint8_t char5_151 = 0; -static uint8_t char6_151 = 0; -static uint8_t char7_151 = 0; CAN_frame BYD_250 = {.FD = false, .ext_ID = false, @@ -79,6 +72,7 @@ CAN_frame BYD_210 = {.FD = false, .ID = 0x210, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +static uint8_t inverter_name[7] = {0}; static int16_t temperature_average = 0; static uint16_t inverter_voltage = 0; static uint16_t inverter_SOC = 0; @@ -146,15 +140,12 @@ void update_values_can_inverter() { //This function maps all the values fetched BYD_210.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF); #ifdef DEBUG_VIA_USB - if (char1_151 != 0) { + if (inverter_name[0] != 0) { Serial.print("Detected inverter: "); - Serial.print((char)char1_151); - Serial.print((char)char2_151); - Serial.print((char)char3_151); - Serial.print((char)char4_151); - Serial.print((char)char5_151); - Serial.print((char)char6_151); - Serial.println((char)char7_151); + for (uint8_t i = 0; i < 7; i++) { + Serial.print((char)inverter_name[i]); + } + Serial.println(); } #endif } @@ -166,27 +157,23 @@ void receive_can_inverter(CAN_frame rx_frame) { if (rx_frame.data.u8[0] & 0x01) { //Battery requests identification send_intial_data(); } else { // We can identify what inverter type we are connected to - char1_151 = rx_frame.data.u8[1]; - char2_151 = rx_frame.data.u8[2]; - char3_151 = rx_frame.data.u8[3]; - char4_151 = rx_frame.data.u8[4]; - char5_151 = rx_frame.data.u8[5]; - char6_151 = rx_frame.data.u8[6]; - char7_151 = rx_frame.data.u8[7]; + for (uint8_t i = 0; i < 7; i++) { + inverter_name[i] = rx_frame.data.u8[i + 1]; + } } break; case 0x091: datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; - inverter_voltage = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.1; + inverter_voltage = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]) * 0.1; break; case 0x0D1: datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; - inverter_SOC = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.1; + inverter_SOC = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]) * 0.1; break; case 0x111: datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; - inverter_timestamp = ((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[2] << 16) | (rx_frame.data.u8[1] << 8) | - rx_frame.data.u8[0]); + inverter_timestamp = ((rx_frame.data.u8[0] << 24) | (rx_frame.data.u8[1] << 16) | (rx_frame.data.u8[2] << 8) | + rx_frame.data.u8[3]); break; default: break; From 1e50394b0f9643f870653461ffaae8ec99f55d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 13 Nov 2024 22:41:51 +0200 Subject: [PATCH 53/81] Centralize active_power_w writing --- Software/Software.ino | 6 ++++++ Software/src/battery/BMW-I3-BATTERY.cpp | 10 ---------- Software/src/battery/BMW-IX-BATTERY.cpp | 5 ----- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 6 ------ Software/src/battery/CELLPOWER-BMS.cpp | 3 --- Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 2 -- Software/src/battery/JAGUAR-IPACE-BATTERY.cpp | 4 ---- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 5 ----- Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 4 ---- Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp | 4 ---- Software/src/battery/MG-5-BATTERY.cpp | 2 -- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 7 ------- Software/src/battery/PYLON-BATTERY.cpp | 3 --- Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp | 3 --- Software/src/battery/RENAULT-KANGOO-BATTERY.cpp | 3 --- Software/src/battery/RENAULT-TWIZY.cpp | 3 --- Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp | 4 ---- Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp | 3 --- Software/src/battery/RJXZS-BMS.cpp | 3 --- Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp | 4 ---- Software/src/battery/TESLA-BATTERY.cpp | 4 ---- Software/src/battery/TEST-FAKE-BATTERY.cpp | 4 ---- Software/src/battery/VOLVO-SPA-BATTERY.cpp | 1 - 23 files changed, 6 insertions(+), 87 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index c85c376d0..babef4dd7 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -854,6 +854,9 @@ void update_calculated_values() { if (datalayer.battery.status.max_discharge_current_dA > datalayer.battery.settings.max_user_set_discharge_dA) { datalayer.battery.status.max_discharge_current_dA = datalayer.battery.settings.max_user_set_discharge_dA; } + /* Calculate active power based on voltage and current*/ + datalayer.battery.status.active_power_W = + (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100)); if (datalayer.battery.settings.soc_scaling_active) { /** SOC Scaling @@ -899,6 +902,9 @@ void update_calculated_values() { } #ifdef DOUBLE_BATTERY + /* Calculate active power based on voltage and current*/ + datalayer.battery2.status.active_power_W = + (datalayer.battery2.status.current_dA * (datalayer.battery2.status.voltage_dV / 100)); // Calculate the scaled remaining capacity in Wh if (datalayer.battery2.info.total_capacity_Wh > 0 && datalayer.battery2.status.real_soc > 0) { diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp index c02a112e8..a1b576d41 100644 --- a/Software/src/battery/BMW-I3-BATTERY.cpp +++ b/Software/src/battery/BMW-I3-BATTERY.cpp @@ -239,7 +239,6 @@ static int16_t battery_temperature_max = 0; static int16_t battery_temperature_min = 0; static int16_t battery_max_charge_amperage = 0; static int16_t battery_max_discharge_amperage = 0; -static int16_t battery_power = 0; static int16_t battery_current = 0; static uint8_t battery_status_error_isolation_external_Bordnetz = 0; static uint8_t battery_status_error_isolation_internal_Bordnetz = 0; @@ -308,7 +307,6 @@ static int16_t battery2_temperature_max = 0; static int16_t battery2_temperature_min = 0; static int16_t battery2_max_charge_amperage = 0; static int16_t battery2_max_discharge_amperage = 0; -static int16_t battery2_power = 0; static int16_t battery2_current = 0; static uint8_t battery2_status_error_isolation_external_Bordnetz = 0; static uint8_t battery2_status_error_isolation_internal_Bordnetz = 0; @@ -388,10 +386,6 @@ void update_values_battery2() { //This function maps all the values fetched via datalayer.battery2.status.max_charge_power_W = battery2_BEV_available_power_longterm_charge; } - battery2_power = (datalayer.battery2.status.current_dA * (datalayer.battery2.status.voltage_dV / 100)); - - datalayer.battery2.status.active_power_W = battery2_power; - datalayer.battery2.status.temperature_min_dC = battery2_temperature_min * 10; // Add a decimal datalayer.battery2.status.temperature_max_dC = battery2_temperature_max * 10; // Add a decimal @@ -456,10 +450,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_charge_power_W = battery_BEV_available_power_longterm_charge; - battery_power = (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100)); - - datalayer.battery.status.active_power_W = battery_power; - datalayer.battery.status.temperature_min_dC = battery_temperature_min * 10; // Add a decimal datalayer.battery.status.temperature_max_dC = battery_temperature_max * 10; // Add a decimal diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 2f316097a..a994e27de 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -353,7 +353,6 @@ static unsigned long min_cell_voltage_lastchanged = 0; static unsigned long max_cell_voltage_lastchanged = 0; static unsigned min_cell_voltage_lastreceived = 0; static unsigned max_cell_voltage_lastreceived = 0; -static int16_t battery_power = 0; static uint32_t sme_uptime = 0; //Uses E4 C0 static int16_t allowable_charge_amps = 0; //E5 62 static int16_t allowable_discharge_amps = 0; //E5 62 @@ -454,10 +453,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_ALLOWED_W; } - battery_power = (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100)); - - datalayer.battery.status.active_power_W = battery_power; - datalayer.battery.status.temperature_min_dC = min_battery_temperature; datalayer.battery.status.temperature_max_dC = max_battery_temperature; diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index f20f0c1f7..9a1e1f9bc 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -122,9 +122,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_charge_power_W = 10000; //TODO: Map from CAN later on - datalayer.battery.status.active_power_W = - (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100)); - datalayer.battery.status.cell_max_voltage_mV = BMS_highest_cell_voltage_mV; datalayer.battery.status.cell_min_voltage_mV = BMS_lowest_cell_voltage_mV; @@ -445,9 +442,6 @@ void update_values_battery2() { //This function maps all the values fetched via datalayer.battery2.status.max_charge_power_W = 10000; //TODO: Map from CAN later on - datalayer.battery2.status.active_power_W = - (datalayer.battery2.status.current_dA * (datalayer.battery2.status.voltage_dV / 100)); - datalayer.battery2.status.cell_max_voltage_mV = BMS2_highest_cell_voltage_mV; datalayer.battery2.status.cell_min_voltage_mV = BMS2_lowest_cell_voltage_mV; diff --git a/Software/src/battery/CELLPOWER-BMS.cpp b/Software/src/battery/CELLPOWER-BMS.cpp index 8bd5a1b53..ec4483122 100644 --- a/Software/src/battery/CELLPOWER-BMS.cpp +++ b/Software/src/battery/CELLPOWER-BMS.cpp @@ -124,9 +124,6 @@ void update_values_battery() { datalayer.battery.status.current_dA = battery_pack_current_dA; - datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - datalayer.battery.status.max_charge_power_W = 5000; //TODO, is this available via CAN? datalayer.battery.status.max_discharge_power_W = 5000; //TODO, is this available via CAN? diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp index c4094e3f3..43ef0b07f 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp @@ -51,8 +51,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_discharge_power_W = 10000; // 10kW //TODO: Fix when CAN is decoded - datalayer.battery.status.active_power_W = BMU_Power; //TODO: Scaling? - static int n = sizeof(cell_voltages) / sizeof(cell_voltages[0]); max_volt_cel = cell_voltages[0]; // Initialize max with the first element of the array for (int i = 1; i < n; i++) { diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp index e5f3fe9b8..a083a8ce7 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp @@ -81,10 +81,6 @@ void update_values_battery() { datalayer.battery.status.cell_min_voltage_mV = HVBattCellVoltageMinMv; - //Power in watts, Negative = charging batt - datalayer.battery.status.active_power_W = - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - datalayer.battery.status.temperature_min_dC = HVBattCellTempColdest * 10; // C to dC datalayer.battery.status.temperature_max_dC = HVBattCellTempHottest * 10; // C to dC diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index bb0c174d7..19db79921 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -38,7 +38,6 @@ static uint16_t CellVoltMin_mV = 3700; static uint16_t batteryVoltage = 6700; static int16_t leadAcidBatteryVoltage = 120; static int16_t batteryAmps = 0; -static int16_t powerWatt = 0; static int16_t temperatureMax = 0; static int16_t temperatureMin = 0; static int16_t allowedDischargePower = 0; @@ -660,10 +659,6 @@ void update_values_battery() { //This function maps all the values fetched via //The allowed discharge power is not available. We hardcode this value for now datalayer.battery.status.max_discharge_power_W = MAXDISCHARGEPOWERALLOWED; - powerWatt = ((batteryVoltage * batteryAmps) / 100); - - datalayer.battery.status.active_power_W = powerWatt; //Power in watts, Negative = charging batt - datalayer.battery.status.temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C datalayer.battery.status.temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp index a99be631d..b3f935a63 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp @@ -124,10 +124,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_discharge_power_W = allowedDischargePower * 10; - //Power in watts, Negative = charging batt - datalayer.battery.status.active_power_W = - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - datalayer.battery.status.temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C datalayer.battery.status.temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp index f2597d3d1..49a8dae6e 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp @@ -68,10 +68,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_charge_power_W = available_charge_power * 10; - //Power in watts, Negative = charging batt - datalayer.battery.status.active_power_W = - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - datalayer.battery.status.temperature_min_dC = (int16_t)(battery_module_min_temperature * 10); datalayer.battery.status.temperature_max_dC = (int16_t)(battery_module_max_temperature * 10); diff --git a/Software/src/battery/MG-5-BATTERY.cpp b/Software/src/battery/MG-5-BATTERY.cpp index b181edaff..8da1b9a23 100644 --- a/Software/src/battery/MG-5-BATTERY.cpp +++ b/Software/src/battery/MG-5-BATTERY.cpp @@ -39,8 +39,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_charge_power_W; - datalayer.battery.status.active_power_W; - datalayer.battery.status.temperature_min_dC; datalayer.battery.status.temperature_max_dC; diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 67bb21f95..f32a9ba6d 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -197,9 +197,6 @@ void update_values_battery() { /* This function maps all the values fetched via datalayer.battery.status.remaining_capacity_Wh = battery_Wh_Remaining; - datalayer.battery.status.active_power_W = ((battery_Total_Voltage2 * battery_Current2) / - 4); //P = U * I (Both values are 0.5 per bit so the math is non-intuitive) - //Update temperature readings. Method depends on which generation LEAF battery is used if (LEAF_battery_Type == ZE0_BATTERY) { //Since we only have average value, send the minimum as -1.0 degrees below average @@ -369,10 +366,6 @@ void update_values_battery2() { // Handle the values coming in from battery #2 datalayer.battery2.status.remaining_capacity_Wh = battery2_Wh_Remaining; - datalayer.battery2.status.active_power_W = - ((battery2_Total_Voltage2 * battery2_Current2) / - 4); //P = U * I (Both values are 0.5 per bit so the math is non-intuitive) - //Update temperature readings. Method depends on which generation LEAF battery is used if (LEAF_battery2_Type == ZE0_BATTERY) { //Since we only have average value, send the minimum as -1.0 degrees below average diff --git a/Software/src/battery/PYLON-BATTERY.cpp b/Software/src/battery/PYLON-BATTERY.cpp index 068a12b7a..169067226 100644 --- a/Software/src/battery/PYLON-BATTERY.cpp +++ b/Software/src/battery/PYLON-BATTERY.cpp @@ -60,9 +60,6 @@ void update_values_battery() { datalayer.battery.status.current_dA = current_dA; //value is *10 (150 = 15.0) , invert the sign - datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - datalayer.battery.status.max_charge_power_W = (max_charge_current * (voltage_dV / 10)); datalayer.battery.status.max_discharge_power_W = (-max_discharge_current * (voltage_dV / 10)); diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp index d530c2d55..4f6eb16c1 100644 --- a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp @@ -160,9 +160,6 @@ void update_values_battery() { datalayer.battery.status.current_dA = (CurrentExt * 0.025) - 209715; - datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - datalayer.battery.status.max_charge_power_W = (ChargeContPwrLmt * 10) - 6550; datalayer.battery.status.max_discharge_power_W = (DischargeContPwrLmt * 10) - 6550; diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp index bd8b16655..00b4e12b4 100644 --- a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp +++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp @@ -95,9 +95,6 @@ void update_values_battery() { //This function maps all the values fetched via //The above value is 0 on some packs. We instead hardcode this now. datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_W; - datalayer.battery.status.active_power_W = - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - datalayer.battery.status.temperature_min_dC = (LB_MIN_TEMPERATURE * 10); datalayer.battery.status.temperature_max_dC = (LB_MAX_TEMPERATURE * 10); diff --git a/Software/src/battery/RENAULT-TWIZY.cpp b/Software/src/battery/RENAULT-TWIZY.cpp index 98f27a833..69a83316e 100644 --- a/Software/src/battery/RENAULT-TWIZY.cpp +++ b/Software/src/battery/RENAULT-TWIZY.cpp @@ -46,9 +46,6 @@ void update_values_battery() { datalayer.battery.status.current_dA = current_dA; //value is *10 (150 = 15.0) datalayer.battery.status.remaining_capacity_Wh = remaining_capacity_Wh; - datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - // The twizy provides two values: one for the maximum charge provided by the on-board charger // and one for the maximum charge during recuperation. // For now we use the lower of the two (usually the charger one) diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp index 917e78929..1044729fb 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp @@ -104,10 +104,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_charge_power_W = 50; } - //Power in watts, Negative = charging batt - datalayer.battery.status.active_power_W = - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - int16_t temperatures[] = {cell_1_temperature_polled, cell_2_temperature_polled, cell_3_temperature_polled, cell_4_temperature_polled, cell_5_temperature_polled, cell_6_temperature_polled, cell_7_temperature_polled, cell_8_temperature_polled, cell_9_temperature_polled, diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp index 38c875b1f..50b09d2e2 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp @@ -166,9 +166,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_charge_power_W = battery_max_generated * 10; - datalayer.battery.status.active_power_W = - (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100)); - datalayer.battery.status.temperature_min_dC = ((battery_min_temp - 640) * 0.625); datalayer.battery.status.temperature_max_dC = ((battery_max_temp - 640) * 0.625); diff --git a/Software/src/battery/RJXZS-BMS.cpp b/Software/src/battery/RJXZS-BMS.cpp index ed23fb404..b75934c8f 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -98,9 +98,6 @@ void update_values_battery() { datalayer.battery.status.current_dA = total_current; - datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - // Charge power is set in .h file if (datalayer.battery.status.real_soc > 9900) { datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_WHEN_TOPBALANCING_W; diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp index dbe69b55a..36b1a649d 100644 --- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp +++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp @@ -85,10 +85,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_charge_power_W = allowedChargePower * 10; - //Power in watts, Negative = charging batt - datalayer.battery.status.active_power_W = - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - datalayer.battery.status.cell_max_voltage_mV = CellVoltMax_mV; datalayer.battery.status.cell_min_voltage_mV = CellVoltMin_mV; diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 3eac62ccf..eaa01e1b4 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -297,8 +297,6 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.max_charge_power_W = MAXCHARGEPOWERALLOWED; } - datalayer.battery.status.active_power_W = ((battery_volts / 10) * battery_amps); - datalayer.battery.status.temperature_min_dC = battery_min_temp; datalayer.battery.status.temperature_max_dC = battery_max_temp; @@ -857,8 +855,6 @@ void update_values_battery2() { //This function maps all the values fetched via datalayer.battery2.status.max_charge_power_W = MAXCHARGEPOWERALLOWED; } - datalayer.battery2.status.active_power_W = ((battery2_volts / 10) * battery2_amps); - datalayer.battery2.status.temperature_min_dC = battery2_min_temp; datalayer.battery2.status.temperature_max_dC = battery2_max_temp; diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index 423654aaa..fdc35294e 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -40,8 +40,6 @@ void update_values_battery() { /* This function puts fake values onto the parame datalayer.battery.status.cell_min_voltage_mV = 3500; - datalayer.battery.status.active_power_W = 0; // 0W - datalayer.battery.status.temperature_min_dC = 50; // 5.0*C datalayer.battery.status.temperature_max_dC = 60; // 6.0*C @@ -95,8 +93,6 @@ void update_values_battery2() { // Handle the values coming in from battery #2 datalayer.battery2.status.cell_min_voltage_mV = 3500; - datalayer.battery2.status.active_power_W = 0; // 0W - datalayer.battery2.status.temperature_min_dC = 50; // 5.0*C datalayer.battery2.status.temperature_max_dC = 60; // 6.0*C diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index d321ad386..efdbc0a00 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -83,7 +83,6 @@ void update_values_battery() { //This function maps all the values fetched via //datalayer.battery.status.max_discharge_power_W = HvBattPwrLimDchaSoft * 1000; // Use power limit reported from BMS, not trusted ATM datalayer.battery.status.max_discharge_power_W = 30000; datalayer.battery.status.max_charge_power_W = 30000; - datalayer.battery.status.active_power_W = (BATT_U)*BATT_I; datalayer.battery.status.temperature_min_dC = BATT_T_MIN; datalayer.battery.status.temperature_max_dC = BATT_T_MAX; From 1398c6213b36cdfc7f5441e94fa157979d2a3cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 13 Nov 2024 23:07:05 +0200 Subject: [PATCH 54/81] Update datalayer comment --- Software/src/datalayer/datalayer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 7e1880af4..5b1981585 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -31,7 +31,7 @@ typedef struct { typedef struct { /** int32_t */ - /** Instantaneous battery power in Watts */ + /** 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; From ca83af81dc943a58c8d84955582fd56371d0028d Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:09:26 +0000 Subject: [PATCH 55/81] Stale value improvement Stale values still overly sensitive in idle conditions. Now requires both min/max to go stale (ie: the whole frame) --- Software/src/battery/BMW-IX-BATTERY.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 2f316097a..245fd66cd 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -462,17 +462,13 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.temperature_max_dC = max_battery_temperature; - if (isStale(min_cell_voltage, datalayer.battery.status.cell_min_voltage_mV, min_cell_voltage_lastchanged)) { + //Check stale values. As values dont change much during idle only consider stale if both parts of this message freeze. + if (isStale(min_cell_voltage, datalayer.battery.status.cell_min_voltage_mV, min_cell_voltage_lastchanged) && isStale(max_cell_voltage, datalayer.battery.status.cell_max_voltage_mV, max_cell_voltage_lastchanged)) { datalayer.battery.status.cell_min_voltage_mV = 9999; //Stale values force stop - set_event(EVENT_CAN_RX_FAILURE, 0); - } else { - datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; //Value is alive - } - - if (isStale(max_cell_voltage, datalayer.battery.status.cell_max_voltage_mV, max_cell_voltage_lastchanged)) { datalayer.battery.status.cell_max_voltage_mV = 9999; //Stale values force stop set_event(EVENT_CAN_RX_FAILURE, 0); } else { + datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; //Value is alive datalayer.battery.status.cell_max_voltage_mV = max_cell_voltage; //Value is alive } @@ -677,7 +673,7 @@ void receive_can_battery(CAN_frame rx_frame) { #ifdef DEBUG_VIA_USB Serial.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset"); #endif - set_event(EVENT_SOC_UNAVAILABLE, (millis())); + //set_event(EVENT_BATTERY_VALUE_UNAVAILABLE, (millis())); //Eventually need new Info level event type transmit_can(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery); } else { //Only ingest values if they are not the 10V Error state min_cell_voltage = (rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]); From 578cb07263488828fe582388c95373ee59476f08 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:51:24 +0000 Subject: [PATCH 56/81] Update BMW-IX-BATTERY.cpp Code formatting --- Software/src/battery/BMW-IX-BATTERY.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 245fd66cd..ebc7e2ab6 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -463,7 +463,8 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.temperature_max_dC = max_battery_temperature; //Check stale values. As values dont change much during idle only consider stale if both parts of this message freeze. - if (isStale(min_cell_voltage, datalayer.battery.status.cell_min_voltage_mV, min_cell_voltage_lastchanged) && isStale(max_cell_voltage, datalayer.battery.status.cell_max_voltage_mV, max_cell_voltage_lastchanged)) { + if (isStale(min_cell_voltage, datalayer.battery.status.cell_min_voltage_mV, min_cell_voltage_lastchanged) && + isStale(max_cell_voltage, datalayer.battery.status.cell_max_voltage_mV, max_cell_voltage_lastchanged)) { datalayer.battery.status.cell_min_voltage_mV = 9999; //Stale values force stop datalayer.battery.status.cell_max_voltage_mV = 9999; //Stale values force stop set_event(EVENT_CAN_RX_FAILURE, 0); From f83b714824ef555d96278d5b6f6cf6239912c823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 14 Nov 2024 13:44:22 +0200 Subject: [PATCH 57/81] Add Schneider CAN inverter protocol --- .github/workflows/compile-all-inverters.yml | 1 + Software/USER_SETTINGS.h | 1 + Software/src/devboard/webserver/webserver.cpp | 3 + Software/src/inverter/INVERTERS.h | 4 + Software/src/inverter/SCHNEIDER-CAN.cpp | 300 ++++++++++++++++++ Software/src/inverter/SCHNEIDER-CAN.h | 32 ++ 6 files changed, 341 insertions(+) create mode 100644 Software/src/inverter/SCHNEIDER-CAN.cpp create mode 100644 Software/src/inverter/SCHNEIDER-CAN.h diff --git a/.github/workflows/compile-all-inverters.yml b/.github/workflows/compile-all-inverters.yml index 85beb3847..a0bed8717 100644 --- a/.github/workflows/compile-all-inverters.yml +++ b/.github/workflows/compile-all-inverters.yml @@ -47,6 +47,7 @@ jobs: - BYD_MODBUS - FOXESS_CAN - PYLON_CAN + - SCHNEIDER_CAN - SMA_CAN - SMA_TRIPOWER_CAN - SOFAR_CAN diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index c6351a121..cbd51f4c2 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -43,6 +43,7 @@ //#define FOXESS_CAN //Enable this line to emulate a "HV2600/ECS4100 battery" over CAN bus //#define PYLON_LV_CAN //Enable this line to emulate a "48V Pylontech battery" over CAN bus //#define PYLON_CAN //Enable this line to emulate a "High Voltage Pylontech battery" over CAN bus +//#define SCHNEIDER_CAN //Enable this line to emulate a "Schneider Version 2: SE BMS" over CAN bus //#define SMA_CAN //Enable this line to emulate a "BYD Battery-Box H 8.9kWh, 7 mod" over CAN bus //#define SMA_TRIPOWER_CAN //Enable this line to emulate a "SMA Home Storage battery" over CAN bus //#define SOFAR_CAN //Enable this line to emulate a "Sofar Energy Storage Inverter High Voltage BMS General Protocol (Extended Frame)" over CAN bus diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 8eab7631e..39051ed65 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -501,6 +501,9 @@ String processor(const String& var) { #ifdef PYLON_LV_CAN content += "Pylontech LV battery over CAN bus"; #endif // PYLON_LV_CAN +#ifdef SCHNEIDER_CAN + content += "Schneider V2 BMS protocol over CAN bus"; +#endif // SCHNEIDER_CAN #ifdef SERIAL_LINK_TRANSMITTER content += "Serial link to another LilyGo board"; #endif // SERIAL_LINK_TRANSMITTER diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h index d1922b825..18089fe8f 100644 --- a/Software/src/inverter/INVERTERS.h +++ b/Software/src/inverter/INVERTERS.h @@ -31,6 +31,10 @@ #include "PYLON-LV-CAN.h" #endif +#ifdef SCHNEIDER_CAN +#include "SCHNEIDER-CAN.h" +#endif + #ifdef SMA_CAN #include "SMA-CAN.h" #endif diff --git a/Software/src/inverter/SCHNEIDER-CAN.cpp b/Software/src/inverter/SCHNEIDER-CAN.cpp new file mode 100644 index 000000000..c13d3e87b --- /dev/null +++ b/Software/src/inverter/SCHNEIDER-CAN.cpp @@ -0,0 +1,300 @@ +#include "../include.h" +#ifdef SCHNEIDER_CAN +#include "../datalayer/datalayer.h" +#include "SCHNEIDER-CAN.h" + +/* Version 2: SE BMS Communication Protocol +Protocol: CAN 2.0 Specification +Frame: Extended CAN Bus Frame (29 bit identifier) +Bitrate: 500 kbps +Endian: Big Endian (MSB, most significant byte of a value received first)*/ + +/* TODOs +- Figure out how to reply with protocol version in 0x320 +- Figure out what to set Battery Manufacturer ID to in 0x330 +- Figure out what to set Battery Model ID in 0x330 + - We will need CAN logs from existing battery OR contact Schneider for one free number +*/ + +/* Do not change code below unless you are sure what you are doing */ +static unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send +static unsigned long previousMillis2s = 0; // will store last time a 2s CAN Message was send +static unsigned long previousMillis500ms = 0; // will store last time a 500ms CAN Message was send + +CAN_frame SE_320 = {.FD = false, //SE BMS Protocol Version + .ext_ID = true, + .DLC = 2, + .ID = 0x320, + .data = {0x00, 0x02}}; //TODO: How do we reply with Protocol Version: 0x0002 ? +CAN_frame SE_321 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x321, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame SE_322 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x322, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame SE_323 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x323, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame SE_324 = {.FD = false, .ext_ID = true, .DLC = 4, .ID = 0x324, .data = {0x00, 0x00, 0x00, 0x00}}; +CAN_frame SE_325 = {.FD = false, .ext_ID = true, .DLC = 6, .ID = 0x325, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame SE_326 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x326, + .data = {0x00, STATE_STARTING, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame SE_327 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x327, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame SE_328 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x328, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame SE_330 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x330, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame SE_331 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x331, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame SE_332 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x332, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame SE_333 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x333, + .data = {0x53, 0x45, 0x42, 0x4D, 0x53, 0x00, 0x00, 0x00}}; //SEBMS + +static int16_t temperature_average = 0; +static uint16_t remaining_capacity_ah = 0; +static uint16_t fully_charged_capacity_ah = 0; +static uint16_t commands = 0; +static uint16_t warnings = 0; +static uint16_t faults = 0; +static uint16_t state = 0; + +void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages + + /* Calculate temperature */ + temperature_average = + ((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2); + + /* Calculate capacity, Amp hours(Ah) = Watt hours (Wh) / Voltage (V)*/ + if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0 + remaining_capacity_ah = + ((datalayer.battery.status.reported_remaining_capacity_Wh / datalayer.battery.status.voltage_dV) * 100); + fully_charged_capacity_ah = + ((datalayer.battery.info.total_capacity_Wh / datalayer.battery.status.voltage_dV) * 100); + } + /* Set active commands/warnings/faults/state*/ + if (datalayer.battery.status.bms_status == FAULT) { + state = STATE_FAULTED; + //TODO: Map warnings and faults incase an event is set. Low prio, but nice to have + commands = COMMAND_STOP; + } else { //Battery-Emulator running + state = STATE_ONLINE; + warnings = 0; + faults = 0; + if (datalayer.battery.status.reported_soc == 10000) { + //Battery full. Only allow discharge + commands = COMMAND_ONLY_DISCHARGE_ALLOWED; + } else if (datalayer.battery.status.reported_soc == 0) { + //Battery empty. Only allow charge + commands = COMMAND_ONLY_CHARGE_ALLOWED; + } else { //SOC is somewhere between 0.1% and 99.9%. Allow both charge and discharge + commands = COMMAND_CHARGE_AND_DISCHARGE_ALLOWED; + } + } + + //Map values to CAN messages + //Max charge voltage+2 (eg 10000.00V = 1000000 , 32bits long) + SE_321.data.u8[0] = ((datalayer.battery.info.max_design_voltage_dV * 10) >> 24); + SE_321.data.u8[1] = (((datalayer.battery.info.max_design_voltage_dV * 10) & 0x00FF0000) >> 16); + SE_321.data.u8[2] = (((datalayer.battery.info.max_design_voltage_dV * 10) & 0x0000FF00) >> 8); + SE_321.data.u8[3] = ((datalayer.battery.info.max_design_voltage_dV * 10) & 0x000000FF); + //Minimum discharge voltage+2 (eg 10000.00V = 1000000 , 32bits long) + SE_321.data.u8[4] = ((datalayer.battery.info.min_design_voltage_dV * 10) >> 24); + SE_321.data.u8[5] = (((datalayer.battery.info.min_design_voltage_dV * 10) & 0x00FF0000) >> 16); + SE_321.data.u8[6] = (((datalayer.battery.info.min_design_voltage_dV * 10) & 0x0000FF00) >> 8); + SE_321.data.u8[7] = ((datalayer.battery.info.min_design_voltage_dV * 10) & 0x000000FF); + + //Maximum charge current+2 (eg 10000.00A = 1000000) TODO: Note s32 bit, which direction? + SE_322.data.u8[0] = ((datalayer.battery.status.max_charge_current_dA * 10) >> 24); + SE_322.data.u8[1] = (((datalayer.battery.status.max_charge_current_dA * 10) & 0x00FF0000) >> 16); + SE_322.data.u8[2] = (((datalayer.battery.status.max_charge_current_dA * 10) & 0x0000FF00) >> 8); + SE_322.data.u8[3] = ((datalayer.battery.status.max_charge_current_dA * 10) & 0x000000FF); + //Maximum discharge current+2 (eg 10000.00A = 1000000) TODO: Note s32 bit, which direction? + SE_322.data.u8[4] = ((datalayer.battery.status.max_discharge_current_dA * 10) >> 24); + SE_322.data.u8[5] = (((datalayer.battery.status.max_discharge_current_dA * 10) & 0x00FF0000) >> 16); + SE_322.data.u8[6] = (((datalayer.battery.status.max_discharge_current_dA * 10) & 0x0000FF00) >> 8); + SE_322.data.u8[7] = ((datalayer.battery.status.max_discharge_current_dA * 10) & 0x000000FF); + + //Voltage (ex 370.00 = 37000, 32bits long) + SE_323.data.u8[0] = ((datalayer.battery.status.voltage_dV * 10) >> 24); + SE_323.data.u8[1] = (((datalayer.battery.status.voltage_dV * 10) & 0x00FF0000) >> 16); + SE_323.data.u8[2] = (((datalayer.battery.status.voltage_dV * 10) & 0x0000FF00) >> 8); + SE_323.data.u8[3] = ((datalayer.battery.status.voltage_dV * 10) & 0x000000FF); + //Current (ex 81.00A = 8100) TODO: Note s32 bit, which direction? + SE_323.data.u8[4] = ((datalayer.battery.status.current_dA * 10) >> 24); + SE_323.data.u8[5] = (((datalayer.battery.status.current_dA * 10) & 0x00FF0000) >> 16); + SE_323.data.u8[6] = (((datalayer.battery.status.current_dA * 10) & 0x0000FF00) >> 8); + SE_323.data.u8[7] = ((datalayer.battery.status.current_dA * 10) & 0x000000FF); + + //Temperature average + SE_324.data.u8[0] = (temperature_average >> 8); + SE_324.data.u8[1] = (temperature_average & 0x00FF); + //SOC (100.0%) + SE_324.data.u8[2] = ((datalayer.battery.status.reported_soc / 10) >> 8); + SE_324.data.u8[3] = ((datalayer.battery.status.reported_soc / 10) & 0x00FF); + //Commands (enum) + SE_325.data.u8[0] = (commands >> 8); + SE_325.data.u8[1] = (commands & 0x00FF); + //Warnings (enum) + SE_325.data.u8[2] = (warnings >> 8); + SE_325.data.u8[3] = (warnings & 0x00FF); + //Faults (enum) + SE_325.data.u8[4] = (faults >> 8); + SE_325.data.u8[5] = (faults & 0x00FF); + + //State (enum) + SE_326.data.u8[0] = (state >> 8); + SE_326.data.u8[1] = (state & 0x00FF); + //Cycle count (OPTIONAL UINT16) + //SE_326.data.u8[2] = Cycle count not tracked by emulator + //SE_326.data.u8[3] = Cycle count not tracked by emulator + //StateOfHealth (OPTIONAL 0-100%) + SE_326.data.u8[4] = (datalayer.battery.status.soh_pptt / 100 >> 8); + SE_326.data.u8[5] = (datalayer.battery.status.soh_pptt / 100 & 0x00FF); + //Capacity (OPTIONAL, full charge) AH+1 + SE_326.data.u8[6] = (fully_charged_capacity_ah >> 8); + SE_326.data.u8[7] = (fully_charged_capacity_ah & 0x00FF); + + //Cell temp max (OPTIONAL dC) + SE_327.data.u8[0] = (datalayer.battery.status.temperature_max_dC >> 8); + SE_327.data.u8[1] = (datalayer.battery.status.temperature_max_dC & 0x00FF); + //Cell temp min (OPTIONAL dC) + SE_327.data.u8[2] = (datalayer.battery.status.temperature_min_dC >> 8); + SE_327.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF); + //Cell max volt (OPTIONAL 4.000V) + SE_327.data.u8[4] = (datalayer.battery.status.cell_max_voltage_mV >> 8); + SE_327.data.u8[5] = (datalayer.battery.status.cell_max_voltage_mV & 0x00FF); + //Cell min volt (OPTIONAL 4.000V) + SE_327.data.u8[6] = (datalayer.battery.status.cell_min_voltage_mV >> 8); + SE_327.data.u8[7] = (datalayer.battery.status.cell_min_voltage_mV & 0x00FF); + + //Lifetime Charge Energy (OPTIONAL, WH, UINT32) + //SE_328.data.u8[0] = Lifetime energy not tracked by emulator + //SE_328.data.u8[1] = Lifetime energy not tracked by emulator + //SE_328.data.u8[2] = Lifetime energy not tracked by emulator + //SE_328.data.u8[3] = Lifetime energy not tracked by emulator + //Lifetime Discharge Energy (OPTIONAL, WH, UINT32) + //SE_328.data.u8[4] = Lifetime energy not tracked by emulator + //SE_328.data.u8[5] = Lifetime energy not tracked by emulator + //SE_328.data.u8[6] = Lifetime energy not tracked by emulator + //SE_328.data.u8[7] = Lifetime energy not tracked by emulator + + //Battery Manufacturer ID (UINT16) + //Unique identifier for each battery manufacturer implementing this protocol. IDs must be requested through Schneider Electric Solar. + SE_330.data.u8[0] = 0; //TODO, set Battery Manufacturer ID + SE_330.data.u8[1] = 0; //TODO, set Battery Manufacturer ID + //Battery Model ID (UINT16) + //Unique identifier for each battery model that a manufacturer has implemented this protocol on. IDs must be requested through Schneider Electric Solar. + SE_330.data.u8[2] = 0; //TODO, set Battery Model ID + SE_330.data.u8[3] = 0; //TODO, set Battery Model ID + //Serial numbers + //(For instance ABC123 would be represented as: + //0x41[char5], 0x42[char4], 0x43[char3], 0x31[char2], 0x32 [char1], 0x33 [char0]) + SE_330.data.u8[4] = 0x42; //Char 19 - B + SE_330.data.u8[5] = 0x41; //Char 18 - A + SE_330.data.u8[6] = 0x54; //Char 17 - T + SE_330.data.u8[7] = 0x54; //Char 16 - T + + SE_331.data.u8[0] = 0x45; //Char 15 - E + SE_331.data.u8[1] = 0x52; //Char 14 - R + SE_331.data.u8[2] = 0x59; //Char 13 - Y + SE_331.data.u8[3] = 0x45; //Char 12 - E + SE_331.data.u8[4] = 0x4D; //Char 11 - M + SE_331.data.u8[5] = 0x55; //Char 10 - U + SE_331.data.u8[6] = 0x4C; //Char 9 - L + SE_331.data.u8[7] = 0x41; //Char 8 - A + + SE_332.data.u8[0] = 0x54; //Char 7 - T + SE_332.data.u8[1] = 0x4F; //Char 6 - O + SE_332.data.u8[2] = 0x52; //Char 5 - R + SE_332.data.u8[3] = 0x30; //Char 4 - 0 + SE_332.data.u8[4] = 0x31; //Char 3 - 1 + SE_332.data.u8[5] = 0x32; //Char 2 - 2 + SE_332.data.u8[6] = 0x33; //Char 1 - 3 + SE_332.data.u8[7] = 0x34; //Char 0 - 4 + + //UNIQUE ID + //Schneider Electric Unique string identifier. The value should be an unique string "SEBMS" + SE_333.data.u8[0] = 0x53; //Char 5 - S + SE_333.data.u8[1] = 0x45; //Char 4 - E + SE_333.data.u8[2] = 0x42; //Char 3 - B + SE_333.data.u8[3] = 0x4D; //Char 2 - M + SE_333.data.u8[4] = 0x53; //Char 1 - S + SE_333.data.u8[5] = 0x00; //Char 0 - NULL + + //Protocol version, TODO: How do we reply with protocol version 0x0002 ? + SE_320.data.u8[0] = 0x00; + SE_320.data.u8[1] = 0x02; +} + +void receive_can_inverter(CAN_frame rx_frame) { + switch (rx_frame.ID) { + case 0x310: // Still alive message from inverter, every 1s + datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; + break; + default: + break; + } +} + +void send_can_inverter() { + unsigned long currentMillis = millis(); + + // Send 500ms CAN Message + if (currentMillis - previousMillis500ms >= INTERVAL_500_MS) { + previousMillis500ms = currentMillis; + + transmit_can(&SE_321, can_config.inverter); + transmit_can(&SE_322, can_config.inverter); + transmit_can(&SE_323, can_config.inverter); + transmit_can(&SE_324, can_config.inverter); + transmit_can(&SE_325, can_config.inverter); + } + // Send 2s CAN Message + if (currentMillis - previousMillis2s >= INTERVAL_2_S) { + previousMillis2s = currentMillis; + + transmit_can(&SE_320, can_config.inverter); + transmit_can(&SE_326, can_config.inverter); + transmit_can(&SE_327, can_config.inverter); + } + // Send 10s CAN Message + if (currentMillis - previousMillis10s >= INTERVAL_10_S) { + previousMillis10s = currentMillis; + transmit_can(&SE_328, can_config.inverter); + transmit_can(&SE_330, can_config.inverter); + transmit_can(&SE_331, can_config.inverter); + transmit_can(&SE_332, can_config.inverter); + transmit_can(&SE_333, can_config.inverter); + } +} + +#endif diff --git a/Software/src/inverter/SCHNEIDER-CAN.h b/Software/src/inverter/SCHNEIDER-CAN.h new file mode 100644 index 000000000..7178a533f --- /dev/null +++ b/Software/src/inverter/SCHNEIDER-CAN.h @@ -0,0 +1,32 @@ +#ifndef SCHNEIDER_CAN_H +#define SCHNEIDER_CAN_H +#include "../include.h" + +#define CAN_INVERTER_SELECTED + +#define STATE_OFFLINE 0 +#define STATE_STANDBY 1 +#define STATE_STARTING 2 +#define STATE_ONLINE 3 +#define STATE_FAULTED 4 + +// Same enumerations used for Fault and Warning +#define FAULTS_CHARGE_OVERCURRENT 0 +#define FAULTS_DISCHARGE_OVERCURRENT 1 +#define FAULTS_OVER_TEMPERATURE 2 +#define FAULTS_UNDER_TEMPERATURE 3 +#define FAULTS_OVER_VOLTAGE 4 +#define FAULTS_UNDER_VOLTAGE 5 +#define FAULTS_CELL_IMBALANCE 6 +#define FAULTS_INTERNAL_COM_ERROR 7 +#define FAULTS_SYSTEM_ERROR 8 + +// Commands. Bit0 forced charge request. Bit1 charge permitted. Bit2 discharge permitted. Bit3 Stop +#define COMMAND_ONLY_CHARGE_ALLOWED 0x02 +#define COMMAND_ONLY_DISCHARGE_ALLOWED 0x04 +#define COMMAND_CHARGE_AND_DISCHARGE_ALLOWED 0x06 +#define COMMAND_STOP 0x08 + +void transmit_can(CAN_frame* tx_frame, int interface); + +#endif From d5cd25bdf3c19b07b1b83d8262ca0a91fae3ac75 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:35:42 +0000 Subject: [PATCH 58/81] Ensure both stale checks run --- Software/src/battery/BMW-IX-BATTERY.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index ebc7e2ab6..dba9ddca6 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -463,8 +463,12 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.temperature_max_dC = max_battery_temperature; //Check stale values. As values dont change much during idle only consider stale if both parts of this message freeze. - if (isStale(min_cell_voltage, datalayer.battery.status.cell_min_voltage_mV, min_cell_voltage_lastchanged) && - isStale(max_cell_voltage, datalayer.battery.status.cell_max_voltage_mV, max_cell_voltage_lastchanged)) { + bool isMinCellVoltageStale = + isStale(min_cell_voltage, datalayer.battery.status.cell_min_voltage_mV, min_cell_voltage_lastchanged); + bool isMaxCellVoltageStale = + isStale(max_cell_voltage, datalayer.battery.status.cell_max_voltage_mV, max_cell_voltage_lastchanged); + + if (isMinCellVoltageStale && isMaxCellVoltageStale) { datalayer.battery.status.cell_min_voltage_mV = 9999; //Stale values force stop datalayer.battery.status.cell_max_voltage_mV = 9999; //Stale values force stop set_event(EVENT_CAN_RX_FAILURE, 0); From edb6dcd39b66e0930df57717148815169c3897f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 14 Nov 2024 18:38:23 +0200 Subject: [PATCH 59/81] Add battery text to datalayer --- Software/src/battery/BMW-I3-BATTERY.cpp | 9 +-- Software/src/battery/BMW-IX-BATTERY.cpp | 7 +- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 6 +- Software/src/battery/CELLPOWER-BMS.cpp | 6 +- Software/src/battery/CHADEMO-BATTERY.cpp | 8 +- .../src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 7 +- Software/src/battery/JAGUAR-IPACE-BATTERY.cpp | 6 +- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 8 +- .../src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 7 +- .../battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp | 8 +- Software/src/battery/MG-5-BATTERY.cpp | 6 +- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 7 +- Software/src/battery/PYLON-BATTERY.cpp | 7 +- .../src/battery/RANGE-ROVER-PHEV-BATTERY.cpp | 7 +- .../src/battery/RENAULT-KANGOO-BATTERY.cpp | 7 +- Software/src/battery/RENAULT-TWIZY.cpp | 7 +- .../src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp | 7 +- .../src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp | 7 +- Software/src/battery/RJXZS-BMS.cpp | 8 +- .../src/battery/SANTA-FE-PHEV-BATTERY.cpp | 6 +- .../SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp | 5 +- Software/src/battery/TESLA-BATTERY.cpp | 12 ++- Software/src/battery/TEST-FAKE-BATTERY.cpp | 7 +- Software/src/battery/VOLVO-SPA-BATTERY.cpp | 8 +- Software/src/datalayer/datalayer.h | 5 +- Software/src/devboard/webserver/webserver.cpp | 78 +------------------ 26 files changed, 101 insertions(+), 155 deletions(-) diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp index a1b576d41..15bac9f4b 100644 --- a/Software/src/battery/BMW-I3-BATTERY.cpp +++ b/Software/src/battery/BMW-I3-BATTERY.cpp @@ -1118,9 +1118,9 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("BMW i3 battery selected"); -#endif //DEBUG_VIA_USB + strncpy(datalayer.system.info.battery_protocol, "BMW i3", sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination //Before we have started up and detected which battery is in use, use 60AH values datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH; @@ -1129,9 +1129,6 @@ void setup_battery(void) { // Performs one time setup at startup datalayer.system.status.battery_allows_contactor_closing = true; #ifdef DOUBLE_BATTERY -#ifdef DEBUG_VIA_USB - Serial.println("Another BMW i3 battery also selected!"); -#endif //DEBUG_VIA_USB datalayer.battery2.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV; datalayer.battery2.info.min_design_voltage_dV = datalayer.battery.info.min_design_voltage_dV; datalayer.battery2.info.max_cell_voltage_deviation_mV = datalayer.battery.info.max_cell_voltage_deviation_mV; diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index a994e27de..5712d591c 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -778,9 +778,10 @@ void send_can_battery() { //} //We can always send CAN as the iX BMS will wake up on vehicle comms void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("BMW iX battery selected"); -#endif //DEBUG_VIA_USB + strncpy(datalayer.system.info.battery_protocol, "BMW iX and i4-7 platform", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination //Before we have started up and detected which battery is in use, use 108S values datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 9a1e1f9bc..4e7ad1fd6 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -399,9 +399,9 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("BYD Atto 3 battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "BYD Atto 3", sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.number_of_cells = 126; datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/CELLPOWER-BMS.cpp b/Software/src/battery/CELLPOWER-BMS.cpp index ec4483122..604f2e656 100644 --- a/Software/src/battery/CELLPOWER-BMS.cpp +++ b/Software/src/battery/CELLPOWER-BMS.cpp @@ -333,9 +333,9 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Cellpower BMS selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "Cellpower BMS", sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.system.status.battery_allows_contactor_closing = true; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index 90ecd9506..d582f6903 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -1031,9 +1031,11 @@ void handle_chademo_sequence() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Chademo battery selected"); -#endif + + strncpy(datalayer.system.info.battery_protocol, "Chademo V2X mode", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination CHADEMO_Status = CHADEMO_IDLE; diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp index 43ef0b07f..f24490cc3 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp @@ -224,9 +224,10 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "I-Miev / C-Zero / Ion Triplet", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp index a083a8ce7..3733810ef 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp @@ -254,9 +254,9 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Jaguar iPace 90kWh battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "Jaguar I-PACE", sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.number_of_cells = 108; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index 19db79921..0a810e9fe 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -1037,14 +1037,14 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Hyundai E-GMP (Electric Global Modular Platform) battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai EGMP platform", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination startMillis = millis(); // Record the starting time datalayer.system.status.battery_allows_contactor_closing = true; - datalayer.battery.info.number_of_cells = 192; // TODO: will vary depending on battery datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp index b3f935a63..b25268d66 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp @@ -534,9 +534,10 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Kia Niro / Hyundai Kona 64kWh battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai 64/40kWh battery", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV; //Start with 98S value. Precised later datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV; //Start with 90S value. Precised later datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp index 49a8dae6e..f6f88c52c 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp @@ -257,9 +257,11 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Kia/Hyundai Hybrid battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai Hybrid", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination + datalayer.battery.info.number_of_cells = 56; // HEV , TODO: Make dynamic according to HEV/PHEV datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/MG-5-BATTERY.cpp b/Software/src/battery/MG-5-BATTERY.cpp index 8da1b9a23..94b8ec388 100644 --- a/Software/src/battery/MG-5-BATTERY.cpp +++ b/Software/src/battery/MG-5-BATTERY.cpp @@ -135,9 +135,9 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("MG 5 battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "MG 5 battery", sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index f32a9ba6d..bb0d6f917 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -1224,9 +1224,10 @@ uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrib } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Nissan LEAF battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "Nissan LEAF battery", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/PYLON-BATTERY.cpp b/Software/src/battery/PYLON-BATTERY.cpp index 169067226..dfa0dfe03 100644 --- a/Software/src/battery/PYLON-BATTERY.cpp +++ b/Software/src/battery/PYLON-BATTERY.cpp @@ -175,9 +175,10 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Pylon battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp index 4f6eb16c1..ce6ea714f 100644 --- a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp @@ -313,9 +313,10 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Range Rover PHEV battery (L494 / L405) selected"); -#endif //DEBUG_VIA_USB + strncpy(datalayer.system.info.battery_protocol, "Range Rover 13kWh PHEV battery (L494/L405)", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp index 00b4e12b4..f3d51887b 100644 --- a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp +++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp @@ -234,9 +234,10 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Renault Kangoo battery selected"); -#endif + + strncpy(datalayer.system.info.battery_protocol, "Renault Kangoo", sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-TWIZY.cpp b/Software/src/battery/RENAULT-TWIZY.cpp index 69a83316e..74c9ea916 100644 --- a/Software/src/battery/RENAULT-TWIZY.cpp +++ b/Software/src/battery/RENAULT-TWIZY.cpp @@ -132,10 +132,9 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Renault Twizy battery selected"); -#endif - + strncpy(datalayer.system.info.battery_protocol, "Renault Twizy", sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.number_of_cells = 14; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp index 1044729fb..270509191 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp @@ -518,9 +518,10 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Renault Zoe 22/40kWh battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen1 22/40kWh", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.system.status.battery_allows_contactor_closing = true; datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp index 50b09d2e2..bafbdaf3b 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp @@ -385,9 +385,10 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Renault Zoe 50kWh battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen2 50kWh", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.system.status.battery_allows_contactor_closing = true; datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RJXZS-BMS.cpp b/Software/src/battery/RJXZS-BMS.cpp index b75934c8f..86b3d9835 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -570,10 +570,10 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("RJXZS BMS selected"); -#endif - + strncpy(datalayer.system.info.battery_protocol, "RJXZS BMS, DIY battery", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp index 36b1a649d..2180628ea 100644 --- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp +++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp @@ -402,9 +402,9 @@ uint8_t CalculateCRC8(CAN_frame rx_frame) { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Hyundai Santa Fe PHEV battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "Santa Fe PHEV", sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp index 0f4ac1351..25a97ddd3 100644 --- a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp +++ b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp @@ -219,7 +219,10 @@ void update_values_serial_link() { } void setup_battery(void) { - Serial.println("SERIAL_DATA_LINK_RECEIVER selected"); + strncpy(datalayer.system.info.battery_protocol, "Serial link to another LilyGo board", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination } // Needed to make the compiler happy void update_values_battery() {} diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index eaa01e1b4..357864be6 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -1249,13 +1249,13 @@ void printDebugIfActive(uint8_t symbol, const char* message) { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Tesla Model S/3/X/Y battery selected"); -#endif - datalayer.system.status.battery_allows_contactor_closing = true; #ifdef TESLA_MODEL_SX_BATTERY // Always use NCM/A mode on S/X packs + strncpy(datalayer.system.info.battery_protocol, "Tesla Model S/X", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM; @@ -1271,6 +1271,10 @@ void setup_battery(void) { // Performs one time setup at startup #endif // TESLA_MODEL_SX_BATTERY #ifdef TESLA_MODEL_3Y_BATTERY // Model 3/Y can be either LFP or NCM/A + strncpy(datalayer.system.info.battery_protocol, "Tesla Model 3/Y", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination #ifdef LFP_CHEMISTRY datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP; diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index fdc35294e..c964eb54c 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -145,9 +145,10 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup randomSeed(analogRead(0)); -#ifdef DEBUG_VIA_USB - Serial.println("Test mode with fake battery selected"); -#endif + strncpy(datalayer.system.info.battery_protocol, "Fake battery for testing purposes", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge) diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index efdbc0a00..fc71de424 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -332,10 +332,10 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup -#ifdef DEBUG_VIA_USB - Serial.println("Volvo SPA XC40 Recharge / Polestar2 78kWh battery selected"); -#endif - + strncpy(datalayer.system.info.battery_protocol, "Volvo / Polestar 78kWh battery", + sizeof(datalayer.system.info.battery_protocol) - 1); + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = + '\0'; // Ensure null termination datalayer.battery.info.number_of_cells = 108; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 5b1981585..f925d30c4 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -127,7 +127,10 @@ typedef struct { } DATALAYER_SHUNT_TYPE; typedef struct { - // TODO + /** array with type of battery used, for displaying on webserver */ + char battery_protocol[64] = {0}; + /** array with type of inverter used, for displaying on webserver */ + char inverter_protocol[64] = {0}; } DATALAYER_SYSTEM_INFO_TYPE; typedef struct { diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 8eab7631e..b6de87a05 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -486,6 +486,7 @@ String processor(const String& var) { // Display which components are used content += "

Inverter protocol: "; + content += datalayer.system.info.inverter_protocol; #ifdef BYD_CAN content += "BYD Battery-Box Premium HVS over CAN Bus"; #endif // BYD_CAN @@ -514,83 +515,8 @@ String processor(const String& var) { content += "SolaX Triple Power LFP over CAN bus"; #endif // SOLAX_CAN content += "

"; - content += "

Battery protocol: "; -#ifdef BMW_I3_BATTERY - content += "BMW i3"; -#endif // BMW_I3_BATTERY -#ifdef BMW_IX_BATTERY - content += "BMW iX and i4-7 platform"; -#endif // BMW_IX_BATTERY -#ifdef BYD_ATTO_3_BATTERY - content += "BYD Atto 3"; -#endif // BYD_ATTO_3_BATTERY -#ifdef CELLPOWER_BMS - content += "Cellpower BMS"; -#endif // CELLPOWER_BMS -#ifdef CHADEMO_BATTERY - content += "Chademo V2X mode"; -#endif // CHADEMO_BATTERY -#ifdef IMIEV_CZERO_ION_BATTERY - content += "I-Miev / C-Zero / Ion Triplet"; -#endif // IMIEV_CZERO_ION_BATTERY -#ifdef JAGUAR_IPACE_BATTERY - content += "Jaguar I-PACE"; -#endif // JAGUAR_IPACE_BATTERY -#ifdef KIA_HYUNDAI_64_BATTERY - content += "Kia/Hyundai 64kWh"; -#endif // KIA_HYUNDAI_64_BATTERY -#ifdef KIA_E_GMP_BATTERY - content += "Kia/Hyundai EGMP platform"; -#endif // KIA_E_GMP_BATTERY -#ifdef KIA_HYUNDAI_HYBRID_BATTERY - content += "Kia/Hyundai Hybrid"; -#endif // KIA_HYUNDAI_HYBRID_BATTERY -#ifdef MG_5_BATTERY - content += "MG 5"; -#endif // MG_5_BATTERY -#ifdef NISSAN_LEAF_BATTERY - content += "Nissan LEAF"; -#endif // NISSAN_LEAF_BATTERY -#ifdef PYLON_BATTERY - content += "Pylon compatible battery"; -#endif // PYLON_BATTERY -#ifdef RJXZS_BMS - content += "RJXZS BMS, DIY battery"; -#endif // RJXZS_BMS -#ifdef RANGE_ROVER_PHEV_BATTERY - content += "Range Rover 13kWh PHEV battery (L494/L405)"; -#endif //RANGE_ROVER_PHEV_BATTERY -#ifdef RENAULT_KANGOO_BATTERY - content += "Renault Kangoo"; -#endif // RENAULT_KANGOO_BATTERY -#ifdef RENAULT_TWIZY_BATTERY - content += "Renault Twizy"; -#endif // RENAULT_TWIZY_BATTERY -#ifdef RENAULT_ZOE_GEN1_BATTERY - content += "Renault Zoe Gen1 22/40"; -#endif // RENAULT_ZOE_GEN1_BATTERY -#ifdef RENAULT_ZOE_GEN2_BATTERY - content += "Renault Zoe Gen2 50"; -#endif // RENAULT_ZOE_GEN2_BATTERY -#ifdef SANTA_FE_PHEV_BATTERY - content += "Santa Fe PHEV"; -#endif // SANTA_FE_PHEV_BATTERY -#ifdef SERIAL_LINK_RECEIVER - content += "Serial link to another LilyGo board"; -#endif // SERIAL_LINK_RECEIVER -#ifdef TESLA_MODEL_SX_BATTERY - content += "Tesla Model S/X"; -#endif // TESLA_MODEL_SX_BATTERY -#ifdef TESLA_MODEL_3Y_BATTERY - content += "Tesla Model 3/Y"; -#endif // TESLA_MODEL_3Y_BATTERY -#ifdef VOLVO_SPA_BATTERY - content += "Volvo / Polestar 78kWh battery"; -#endif // VOLVO_SPA_BATTERY -#ifdef TEST_FAKE_BATTERY - content += "Fake battery for testing purposes"; -#endif // TEST_FAKE_BATTERY + content += datalayer.system.info.battery_protocol; #ifdef DOUBLE_BATTERY content += " (Double battery)"; if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { From 950b0bbb269d8b4f2218037ad3a2adb42ec99cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 14 Nov 2024 18:40:09 +0200 Subject: [PATCH 60/81] Remove comment --- Software/src/battery/BMW-I3-BATTERY.cpp | 3 +-- Software/src/battery/BMW-IX-BATTERY.cpp | 3 +-- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 3 +-- Software/src/battery/CELLPOWER-BMS.cpp | 3 +-- Software/src/battery/CHADEMO-BATTERY.cpp | 3 +-- Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 3 +-- Software/src/battery/JAGUAR-IPACE-BATTERY.cpp | 3 +-- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 3 +-- Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 3 +-- Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp | 3 +-- Software/src/battery/MG-5-BATTERY.cpp | 3 +-- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 3 +-- Software/src/battery/PYLON-BATTERY.cpp | 3 +-- Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp | 3 +-- Software/src/battery/RENAULT-KANGOO-BATTERY.cpp | 3 +-- Software/src/battery/RENAULT-TWIZY.cpp | 3 +-- Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp | 3 +-- Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp | 3 +-- Software/src/battery/RJXZS-BMS.cpp | 3 +-- Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp | 3 +-- Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp | 3 +-- Software/src/battery/TESLA-BATTERY.cpp | 6 ++---- Software/src/battery/TEST-FAKE-BATTERY.cpp | 3 +-- Software/src/battery/VOLVO-SPA-BATTERY.cpp | 3 +-- 24 files changed, 25 insertions(+), 50 deletions(-) diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp index 15bac9f4b..2000558e1 100644 --- a/Software/src/battery/BMW-I3-BATTERY.cpp +++ b/Software/src/battery/BMW-I3-BATTERY.cpp @@ -1119,8 +1119,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "BMW i3", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; //Before we have started up and detected which battery is in use, use 60AH values datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH; diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 5712d591c..4a0846a59 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -780,8 +780,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "BMW iX and i4-7 platform", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; //Before we have started up and detected which battery is in use, use 108S values datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 4e7ad1fd6..18040f9a4 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -400,8 +400,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "BYD Atto 3", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.number_of_cells = 126; datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/CELLPOWER-BMS.cpp b/Software/src/battery/CELLPOWER-BMS.cpp index 604f2e656..d0b949432 100644 --- a/Software/src/battery/CELLPOWER-BMS.cpp +++ b/Software/src/battery/CELLPOWER-BMS.cpp @@ -334,8 +334,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Cellpower BMS", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.system.status.battery_allows_contactor_closing = true; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index d582f6903..afcc31a5c 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -1034,8 +1034,7 @@ void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Chademo V2X mode", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; CHADEMO_Status = CHADEMO_IDLE; diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp index f24490cc3..c1ced536c 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp @@ -226,8 +226,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "I-Miev / C-Zero / Ion Triplet", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp index 3733810ef..a8c2234d9 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp @@ -255,8 +255,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Jaguar I-PACE", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.number_of_cells = 108; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index 0a810e9fe..4ec47bbfd 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -1039,8 +1039,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai EGMP platform", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; startMillis = millis(); // Record the starting time diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp index b25268d66..989ed9117 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp @@ -536,8 +536,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai 64/40kWh battery", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV; //Start with 98S value. Precised later datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV; //Start with 90S value. Precised later datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp index f6f88c52c..030ed4545 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp @@ -259,8 +259,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai Hybrid", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.number_of_cells = 56; // HEV , TODO: Make dynamic according to HEV/PHEV datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/MG-5-BATTERY.cpp b/Software/src/battery/MG-5-BATTERY.cpp index 94b8ec388..bb50dd50e 100644 --- a/Software/src/battery/MG-5-BATTERY.cpp +++ b/Software/src/battery/MG-5-BATTERY.cpp @@ -136,8 +136,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "MG 5 battery", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index bb0d6f917..fde3b41ce 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -1226,8 +1226,7 @@ uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrib void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Nissan LEAF battery", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/PYLON-BATTERY.cpp b/Software/src/battery/PYLON-BATTERY.cpp index dfa0dfe03..a805e3b88 100644 --- a/Software/src/battery/PYLON-BATTERY.cpp +++ b/Software/src/battery/PYLON-BATTERY.cpp @@ -177,8 +177,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp index ce6ea714f..101d41a19 100644 --- a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp @@ -315,8 +315,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Range Rover 13kWh PHEV battery (L494/L405)", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp index f3d51887b..1f7b5cb07 100644 --- a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp +++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp @@ -236,8 +236,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Renault Kangoo", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-TWIZY.cpp b/Software/src/battery/RENAULT-TWIZY.cpp index 74c9ea916..cce08880d 100644 --- a/Software/src/battery/RENAULT-TWIZY.cpp +++ b/Software/src/battery/RENAULT-TWIZY.cpp @@ -133,8 +133,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Renault Twizy", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.number_of_cells = 14; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp index 270509191..ed4510644 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp @@ -520,8 +520,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen1 22/40kWh", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.system.status.battery_allows_contactor_closing = true; datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp index bafbdaf3b..fe7525bdd 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp @@ -387,8 +387,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen2 50kWh", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.system.status.battery_allows_contactor_closing = true; datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RJXZS-BMS.cpp b/Software/src/battery/RJXZS-BMS.cpp index 86b3d9835..e9f4c0702 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -572,8 +572,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "RJXZS BMS, DIY battery", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp index 2180628ea..39add8d95 100644 --- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp +++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp @@ -403,8 +403,7 @@ uint8_t CalculateCRC8(CAN_frame rx_frame) { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Santa Fe PHEV", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp index 25a97ddd3..141edead9 100644 --- a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp +++ b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp @@ -221,8 +221,7 @@ void update_values_serial_link() { void setup_battery(void) { strncpy(datalayer.system.info.battery_protocol, "Serial link to another LilyGo board", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; } // Needed to make the compiler happy void update_values_battery() {} diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 357864be6..839c03356 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -1254,8 +1254,7 @@ void setup_battery(void) { // Performs one time setup at startup #ifdef TESLA_MODEL_SX_BATTERY // Always use NCM/A mode on S/X packs strncpy(datalayer.system.info.battery_protocol, "Tesla Model S/X", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM; @@ -1273,8 +1272,7 @@ void setup_battery(void) { // Performs one time setup at startup #ifdef TESLA_MODEL_3Y_BATTERY // Model 3/Y can be either LFP or NCM/A strncpy(datalayer.system.info.battery_protocol, "Tesla Model 3/Y", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; #ifdef LFP_CHEMISTRY datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP; diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index c964eb54c..bb49e0a24 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -147,8 +147,7 @@ void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Fake battery for testing purposes", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge) diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index fc71de424..e94e80695 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -334,8 +334,7 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Volvo / Polestar 78kWh battery", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = - '\0'; // Ensure null termination + datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; datalayer.battery.info.number_of_cells = 108; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; From 8bba61405378f6db806485bb7cefcdfadc0e3f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 14 Nov 2024 19:36:37 +0200 Subject: [PATCH 61/81] Add init function for inverters --- Software/Software.ino | 17 ++---------- Software/src/battery/BMW-I3-BATTERY.cpp | 4 +-- Software/src/battery/BMW-IX-BATTERY.cpp | 5 ++-- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 4 +-- Software/src/battery/CELLPOWER-BMS.cpp | 4 +-- Software/src/battery/CHADEMO-BATTERY.cpp | 5 ++-- .../src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 5 ++-- Software/src/battery/JAGUAR-IPACE-BATTERY.cpp | 5 ++-- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 5 ++-- .../src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 5 ++-- .../battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp | 5 ++-- Software/src/battery/MG-5-BATTERY.cpp | 4 +-- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 5 ++-- Software/src/battery/PYLON-BATTERY.cpp | 6 ++--- .../src/battery/RANGE-ROVER-PHEV-BATTERY.cpp | 6 ++--- .../src/battery/RENAULT-KANGOO-BATTERY.cpp | 4 +-- Software/src/battery/RENAULT-TWIZY.cpp | 4 +-- .../src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp | 5 ++-- .../src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp | 5 ++-- Software/src/battery/RJXZS-BMS.cpp | 5 ++-- .../src/battery/SANTA-FE-PHEV-BATTERY.cpp | 4 +-- .../SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp | 5 ++-- Software/src/battery/TESLA-BATTERY.cpp | 10 +++---- Software/src/battery/TEST-FAKE-BATTERY.cpp | 5 ++-- Software/src/battery/VOLVO-SPA-BATTERY.cpp | 5 ++-- Software/src/devboard/webserver/webserver.cpp | 27 ------------------- Software/src/inverter/AFORE-CAN.cpp | 4 +++ Software/src/inverter/AFORE-CAN.h | 3 +-- Software/src/inverter/BYD-CAN.cpp | 4 +++ Software/src/inverter/BYD-CAN.h | 1 + Software/src/inverter/BYD-MODBUS.cpp | 4 +++ Software/src/inverter/BYD-MODBUS.h | 1 + Software/src/inverter/BYD-SMA.cpp | 6 +++++ Software/src/inverter/BYD-SMA.h | 1 + Software/src/inverter/FOXESS-CAN.cpp | 4 +++ Software/src/inverter/FOXESS-CAN.h | 1 + Software/src/inverter/PYLON-CAN.cpp | 4 +++ Software/src/inverter/PYLON-CAN.h | 1 + Software/src/inverter/PYLON-LV-CAN.cpp | 4 +++ Software/src/inverter/PYLON-LV-CAN.h | 1 + .../SERIAL-LINK-TRANSMITTER-INVERTER.cpp | 8 ++++++ .../SERIAL-LINK-TRANSMITTER-INVERTER.h | 1 + Software/src/inverter/SMA-CAN.cpp | 5 ++++ Software/src/inverter/SMA-CAN.h | 1 + Software/src/inverter/SMA-TRIPOWER-CAN.cpp | 5 ++++ Software/src/inverter/SMA-TRIPOWER-CAN.h | 1 + Software/src/inverter/SOFAR-CAN.cpp | 5 ++++ Software/src/inverter/SOFAR-CAN.h | 1 + Software/src/inverter/SOLAX-CAN.cpp | 5 ++++ Software/src/inverter/SOLAX-CAN.h | 1 + 50 files changed, 122 insertions(+), 114 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index babef4dd7..303442a8e 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -555,26 +555,13 @@ void init_rs485() { } void init_inverter() { -#ifdef SOLAX_CAN - datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first - intervalUpdateValues = 800; // This protocol also requires the values to be updated faster -#endif -#ifdef FOXESS_CAN - intervalUpdateValues = 950; // This protocol also requires the values to be updated faster -#endif -#ifdef BYD_SMA - datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first - pinMode(INVERTER_CONTACTOR_ENABLE_PIN, INPUT); -#endif + // Inform user what inverter is used and perform setup + setup_inverter(); } void init_battery() { // Inform user what battery is used and perform setup setup_battery(); - -#ifdef CHADEMO_BATTERY - intervalUpdateValues = 800; // This mode requires the values to be updated faster -#endif } #ifdef EQUIPMENT_STOP_BUTTON diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp index 2000558e1..b15cb31ea 100644 --- a/Software/src/battery/BMW-I3-BATTERY.cpp +++ b/Software/src/battery/BMW-I3-BATTERY.cpp @@ -1118,8 +1118,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "BMW i3", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "BMW i3", 63); + datalayer.system.info.battery_protocol[63] = '\0'; //Before we have started up and detected which battery is in use, use 60AH values datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH; diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 4a0846a59..8d9f358d2 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -778,9 +778,8 @@ void send_can_battery() { //} //We can always send CAN as the iX BMS will wake up on vehicle comms void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "BMW iX and i4-7 platform", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "BMW iX and i4-7 platform", 63); + datalayer.system.info.battery_protocol[63] = '\0'; //Before we have started up and detected which battery is in use, use 108S values datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 18040f9a4..bfbc92f34 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -399,8 +399,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "BYD Atto 3", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "BYD Atto 3", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 126; datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/CELLPOWER-BMS.cpp b/Software/src/battery/CELLPOWER-BMS.cpp index d0b949432..80c981c64 100644 --- a/Software/src/battery/CELLPOWER-BMS.cpp +++ b/Software/src/battery/CELLPOWER-BMS.cpp @@ -333,8 +333,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Cellpower BMS", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Cellpower BMS", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.status.battery_allows_contactor_closing = true; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index afcc31a5c..9e8d9e8c6 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -1032,9 +1032,8 @@ void handle_chademo_sequence() { void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Chademo V2X mode", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Chademo V2X mode", 63); + datalayer.system.info.battery_protocol[63] = '\0'; CHADEMO_Status = CHADEMO_IDLE; diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp index c1ced536c..05e6e7c69 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp @@ -224,9 +224,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "I-Miev / C-Zero / Ion Triplet", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "I-Miev / C-Zero / Ion Triplet", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp index a8c2234d9..eb13017c1 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp @@ -254,9 +254,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Jaguar I-PACE", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; - + strncpy(datalayer.system.info.battery_protocol, "Jaguar I-PACE", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 108; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index 4ec47bbfd..a0715a26a 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -1037,9 +1037,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai EGMP platform", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai EGMP platform", 63); + datalayer.system.info.battery_protocol[63] = '\0'; startMillis = millis(); // Record the starting time diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp index 989ed9117..3a6663c65 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp @@ -534,9 +534,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai 64/40kWh battery", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai 64/40kWh battery", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV; //Start with 98S value. Precised later datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV; //Start with 90S value. Precised later datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp index 030ed4545..c37fcb15b 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp @@ -257,9 +257,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai Hybrid", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai Hybrid", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 56; // HEV , TODO: Make dynamic according to HEV/PHEV datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/MG-5-BATTERY.cpp b/Software/src/battery/MG-5-BATTERY.cpp index bb50dd50e..35c8ee965 100644 --- a/Software/src/battery/MG-5-BATTERY.cpp +++ b/Software/src/battery/MG-5-BATTERY.cpp @@ -135,8 +135,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "MG 5 battery", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "MG 5 battery", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index fde3b41ce..79538070c 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -1224,9 +1224,8 @@ uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrib } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Nissan LEAF battery", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Nissan LEAF battery", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/PYLON-BATTERY.cpp b/Software/src/battery/PYLON-BATTERY.cpp index a805e3b88..ec61901d0 100644 --- a/Software/src/battery/PYLON-BATTERY.cpp +++ b/Software/src/battery/PYLON-BATTERY.cpp @@ -175,10 +175,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; - + strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp index 101d41a19..4ca3a3303 100644 --- a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp @@ -313,10 +313,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Range Rover 13kWh PHEV battery (L494/L405)", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; - + strncpy(datalayer.system.info.battery_protocol, "Range Rover 13kWh PHEV battery (L494/L405)", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp index 1f7b5cb07..096cb6292 100644 --- a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp +++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp @@ -235,8 +235,8 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Renault Kangoo", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Renault Kangoo", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-TWIZY.cpp b/Software/src/battery/RENAULT-TWIZY.cpp index cce08880d..b0140eff3 100644 --- a/Software/src/battery/RENAULT-TWIZY.cpp +++ b/Software/src/battery/RENAULT-TWIZY.cpp @@ -132,8 +132,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Renault Twizy", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Renault Twizy", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 14; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp index ed4510644..ef9f54f82 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp @@ -518,9 +518,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen1 22/40kWh", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen1 22/40kWh", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.status.battery_allows_contactor_closing = true; datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp index fe7525bdd..08ac57bfe 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp @@ -385,9 +385,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen2 50kWh", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen2 50kWh", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.status.battery_allows_contactor_closing = true; datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RJXZS-BMS.cpp b/Software/src/battery/RJXZS-BMS.cpp index e9f4c0702..85b96434c 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -570,9 +570,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "RJXZS BMS, DIY battery", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "RJXZS BMS, DIY battery", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp index 39add8d95..e6f27accd 100644 --- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp +++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp @@ -402,8 +402,8 @@ uint8_t CalculateCRC8(CAN_frame rx_frame) { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Santa Fe PHEV", sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Santa Fe PHEV", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp index 141edead9..41697c33b 100644 --- a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp +++ b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp @@ -219,9 +219,8 @@ void update_values_serial_link() { } void setup_battery(void) { - strncpy(datalayer.system.info.battery_protocol, "Serial link to another LilyGo board", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Serial link to another LilyGo board", 63); + datalayer.system.info.battery_protocol[63] = '\0'; } // Needed to make the compiler happy void update_values_battery() {} diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 839c03356..6e4ea6b1d 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -1252,9 +1252,8 @@ void setup_battery(void) { // Performs one time setup at startup datalayer.system.status.battery_allows_contactor_closing = true; #ifdef TESLA_MODEL_SX_BATTERY // Always use NCM/A mode on S/X packs - strncpy(datalayer.system.info.battery_protocol, "Tesla Model S/X", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Tesla Model S/X", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM; @@ -1270,9 +1269,8 @@ void setup_battery(void) { // Performs one time setup at startup #endif // TESLA_MODEL_SX_BATTERY #ifdef TESLA_MODEL_3Y_BATTERY // Model 3/Y can be either LFP or NCM/A - strncpy(datalayer.system.info.battery_protocol, "Tesla Model 3/Y", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Tesla Model 3/Y", 63); + datalayer.system.info.battery_protocol[63] = '\0'; #ifdef LFP_CHEMISTRY datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP; diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index bb49e0a24..e69ddfb24 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -145,9 +145,8 @@ void send_can_battery() { void setup_battery(void) { // Performs one time setup at startup randomSeed(analogRead(0)); - strncpy(datalayer.system.info.battery_protocol, "Fake battery for testing purposes", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Fake battery for testing purposes", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge) diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index e94e80695..839665c88 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -332,9 +332,8 @@ void send_can_battery() { } void setup_battery(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Volvo / Polestar 78kWh battery", - sizeof(datalayer.system.info.battery_protocol) - 1); - datalayer.system.info.battery_protocol[sizeof(datalayer.system.info.battery_protocol) - 1] = '\0'; + strncpy(datalayer.system.info.battery_protocol, "Volvo / Polestar 78kWh battery", 63); + datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 108; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index b6de87a05..5c2cb2c12 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -487,33 +487,6 @@ String processor(const String& var) { // Display which components are used content += "

Inverter protocol: "; content += datalayer.system.info.inverter_protocol; -#ifdef BYD_CAN - content += "BYD Battery-Box Premium HVS over CAN Bus"; -#endif // BYD_CAN -#ifdef BYD_MODBUS - content += "BYD 11kWh HVM battery over Modbus RTU"; -#endif // BYD_MODBUS -#ifdef FOXESS_CAN - content += "FoxESS compatible HV2600/ECS4100 battery"; -#endif // FOXESS_CAN -#ifdef PYLON_CAN - content += "Pylontech battery over CAN bus"; -#endif // PYLON_CAN -#ifdef PYLON_LV_CAN - content += "Pylontech LV battery over CAN bus"; -#endif // PYLON_LV_CAN -#ifdef SERIAL_LINK_TRANSMITTER - content += "Serial link to another LilyGo board"; -#endif // SERIAL_LINK_TRANSMITTER -#ifdef SMA_CAN - content += "BYD Battery-Box H 8.9kWh, 7 mod over CAN bus"; -#endif // SMA_CAN -#ifdef SOFAR_CAN - content += "Sofar Energy Storage Inverter High Voltage BMS General Protocol (Extended Frame) over CAN bus"; -#endif // SOFAR_CAN -#ifdef SOLAX_CAN - content += "SolaX Triple Power LFP over CAN bus"; -#endif // SOLAX_CAN content += "

"; content += "

Battery protocol: "; content += datalayer.system.info.battery_protocol; diff --git a/Software/src/inverter/AFORE-CAN.cpp b/Software/src/inverter/AFORE-CAN.cpp index 744351166..a8d551d77 100644 --- a/Software/src/inverter/AFORE-CAN.cpp +++ b/Software/src/inverter/AFORE-CAN.cpp @@ -233,4 +233,8 @@ void send_can_inverter() { time_to_send_info = false; } } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "Afore battery over CAN", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/AFORE-CAN.h b/Software/src/inverter/AFORE-CAN.h index cc9f8548b..9829befca 100644 --- a/Software/src/inverter/AFORE-CAN.h +++ b/Software/src/inverter/AFORE-CAN.h @@ -4,8 +4,7 @@ #define CAN_INVERTER_SELECTED -void send_system_data(); -void send_setup_info(); void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/BYD-CAN.cpp b/Software/src/inverter/BYD-CAN.cpp index ae0bb560a..e11de744f 100644 --- a/Software/src/inverter/BYD-CAN.cpp +++ b/Software/src/inverter/BYD-CAN.cpp @@ -219,4 +219,8 @@ void send_intial_data() { transmit_can(&BYD_3D0_2, can_config.inverter); transmit_can(&BYD_3D0_3, can_config.inverter); } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "BYD Battery-Box Premium HVS over CAN Bus", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/BYD-CAN.h b/Software/src/inverter/BYD-CAN.h index 6a45317b9..5a90d6bab 100644 --- a/Software/src/inverter/BYD-CAN.h +++ b/Software/src/inverter/BYD-CAN.h @@ -8,5 +8,6 @@ void send_intial_data(); void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/BYD-MODBUS.cpp b/Software/src/inverter/BYD-MODBUS.cpp index 7cee6b8d3..814e24e46 100644 --- a/Software/src/inverter/BYD-MODBUS.cpp +++ b/Software/src/inverter/BYD-MODBUS.cpp @@ -143,4 +143,8 @@ void verify_inverter_modbus() { history_index = (history_index + 1) % HISTORY_LENGTH; } } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "BYD 11kWh HVM battery over Modbus RTU", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/BYD-MODBUS.h b/Software/src/inverter/BYD-MODBUS.h index 3d014daa4..487c97847 100644 --- a/Software/src/inverter/BYD-MODBUS.h +++ b/Software/src/inverter/BYD-MODBUS.h @@ -14,4 +14,5 @@ void verify_temperature_modbus(); void verify_inverter_modbus(); void handle_update_data_modbusp201_byd(); void handle_update_data_modbusp301_byd(); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/BYD-SMA.cpp b/Software/src/inverter/BYD-SMA.cpp index 230b61e79..34e33063a 100644 --- a/Software/src/inverter/BYD-SMA.cpp +++ b/Software/src/inverter/BYD-SMA.cpp @@ -251,4 +251,10 @@ void send_can_inverter() { } } } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "BYD Battery-Box HVS over SMA CAN", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; + datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first + pinMode(INVERTER_CONTACTOR_ENABLE_PIN, INPUT); +} #endif diff --git a/Software/src/inverter/BYD-SMA.h b/Software/src/inverter/BYD-SMA.h index b71a60fa7..e787191d9 100644 --- a/Software/src/inverter/BYD-SMA.h +++ b/Software/src/inverter/BYD-SMA.h @@ -8,5 +8,6 @@ #define STOP_STATE 0x02 void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/FOXESS-CAN.cpp b/Software/src/inverter/FOXESS-CAN.cpp index e4b3b34ca..e23f90062 100644 --- a/Software/src/inverter/FOXESS-CAN.cpp +++ b/Software/src/inverter/FOXESS-CAN.cpp @@ -737,4 +737,8 @@ void receive_can_inverter(CAN_frame rx_frame) { } } } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "FoxESS compatible HV2600/ECS4100 battery", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/FOXESS-CAN.h b/Software/src/inverter/FOXESS-CAN.h index d9a3da2d5..365a559d0 100644 --- a/Software/src/inverter/FOXESS-CAN.h +++ b/Software/src/inverter/FOXESS-CAN.h @@ -5,5 +5,6 @@ #define CAN_INVERTER_SELECTED void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/PYLON-CAN.cpp b/Software/src/inverter/PYLON-CAN.cpp index 2b8e036c0..2145b3e7f 100644 --- a/Software/src/inverter/PYLON-CAN.cpp +++ b/Software/src/inverter/PYLON-CAN.cpp @@ -477,4 +477,8 @@ void send_system_data() { //System equipment information transmit_can(&PYLON_4291, can_config.inverter); #endif } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "Pylontech battery over CAN bus", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/PYLON-CAN.h b/Software/src/inverter/PYLON-CAN.h index 86bb4afa4..6b39afcde 100644 --- a/Software/src/inverter/PYLON-CAN.h +++ b/Software/src/inverter/PYLON-CAN.h @@ -7,5 +7,6 @@ void send_system_data(); void send_setup_info(); void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/PYLON-LV-CAN.cpp b/Software/src/inverter/PYLON-LV-CAN.cpp index 458ac7a04..e5eb2e874 100644 --- a/Software/src/inverter/PYLON-LV-CAN.cpp +++ b/Software/src/inverter/PYLON-LV-CAN.cpp @@ -133,4 +133,8 @@ void send_can_inverter() { transmit_can(&PYLON_35E, can_config.inverter); } } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "Pylontech LV battery over CAN bus", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/PYLON-LV-CAN.h b/Software/src/inverter/PYLON-LV-CAN.h index 45376d2b8..ca6922eb2 100644 --- a/Software/src/inverter/PYLON-LV-CAN.h +++ b/Software/src/inverter/PYLON-LV-CAN.h @@ -12,5 +12,6 @@ void send_system_data(); void send_setup_info(); void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp index 6d3b50713..aa0a7ec4d 100644 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp @@ -159,6 +159,10 @@ void printSendingValues() { Serial.print(datalayer.battery.status.soh_pptt); Serial.print(" Voltage: "); Serial.print(datalayer.battery.status.voltage_dV); + void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "Serial link to another LilyGo board", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; + } Serial.print(" Current: "); Serial.print(datalayer.battery.status.current_dA); Serial.print(" Capacity: "); @@ -190,4 +194,8 @@ void printSendingValues() { Serial.println(""); } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "Serial link to another LilyGo board", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h index 14ae08d75..1637e6bd0 100644 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h @@ -6,5 +6,6 @@ #include "../lib/mackelec-SerialDataLink/SerialDataLink.h" void manageSerialLinkTransmitter(); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/SMA-CAN.cpp b/Software/src/inverter/SMA-CAN.cpp index cecae60ce..5831406d2 100644 --- a/Software/src/inverter/SMA-CAN.cpp +++ b/Software/src/inverter/SMA-CAN.cpp @@ -249,4 +249,9 @@ void send_can_inverter() { } } } + +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "SMA CAN", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/SMA-CAN.h b/Software/src/inverter/SMA-CAN.h index 73b9f1602..111044a46 100644 --- a/Software/src/inverter/SMA-CAN.h +++ b/Software/src/inverter/SMA-CAN.h @@ -8,5 +8,6 @@ #define STOP_STATE 0x02 void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp index 9c0cc2794..7a242daee 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp @@ -320,4 +320,9 @@ void send_tripower_init() { transmit_can(&SMA_017, can_config.inverter); // Battery Manufacturer transmit_can(&SMA_018, can_config.inverter); // Battery Name } + +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "SMA Tripower CAN", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.h b/Software/src/inverter/SMA-TRIPOWER-CAN.h index e12692da4..90967001f 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.h +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.h @@ -6,5 +6,6 @@ void send_tripower_init(); void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/SOFAR-CAN.cpp b/Software/src/inverter/SOFAR-CAN.cpp index eb13b7c57..838ebf932 100644 --- a/Software/src/inverter/SOFAR-CAN.cpp +++ b/Software/src/inverter/SOFAR-CAN.cpp @@ -263,4 +263,9 @@ void send_can_inverter() { transmit_can(&SOFAR_35A, can_config.inverter); } } + +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "Sofar BMS (Extended Frame) over CAN bus", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/SOFAR-CAN.h b/Software/src/inverter/SOFAR-CAN.h index 6d43222fe..7a80bf62a 100644 --- a/Software/src/inverter/SOFAR-CAN.h +++ b/Software/src/inverter/SOFAR-CAN.h @@ -5,5 +5,6 @@ #define CAN_INVERTER_SELECTED void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/SOLAX-CAN.cpp b/Software/src/inverter/SOLAX-CAN.cpp index 790c85249..9bef56ec8 100644 --- a/Software/src/inverter/SOLAX-CAN.cpp +++ b/Software/src/inverter/SOLAX-CAN.cpp @@ -252,4 +252,9 @@ void receive_can_inverter(CAN_frame rx_frame) { #endif } } +void setup_inverter(void) { // Performs one time setup at startup + strncpy(datalayer.system.info.inverter_protocol, "SolaX Triple Power LFP over CAN bus", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; + datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first +} #endif diff --git a/Software/src/inverter/SOLAX-CAN.h b/Software/src/inverter/SOLAX-CAN.h index 6ad461a79..68373ec2a 100644 --- a/Software/src/inverter/SOLAX-CAN.h +++ b/Software/src/inverter/SOLAX-CAN.h @@ -15,5 +15,6 @@ #define UPDATING_FW 4 void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif From 3b546d1466bf623e157a99b3845840eb033876da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 14 Nov 2024 21:24:49 +0200 Subject: [PATCH 62/81] Add all inv/bat to Github workflow --- .github/workflows/compile-all-batteries.yml | 13 ++++++------- .github/workflows/compile-all-inverters.yml | 2 ++ Software/src/devboard/webserver/webserver.cpp | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/compile-all-batteries.yml b/.github/workflows/compile-all-batteries.yml index 1cd8955ea..287b75f24 100644 --- a/.github/workflows/compile-all-batteries.yml +++ b/.github/workflows/compile-all-batteries.yml @@ -34,19 +34,25 @@ jobs: # These are the batteries for which the code will be compiled. battery: - BMW_I3_BATTERY + - BMW_IX_BATTERY - BYD_ATTO_3_BATTERY - CELLPOWER_BMS - CHADEMO_BATTERY - IMIEV_CZERO_ION_BATTERY - JAGUAR_IPACE_BATTERY - KIA_HYUNDAI_64_BATTERY + - KIA_E_GMP_BATTERY - KIA_HYUNDAI_HYBRID_BATTERY + - MG_5_BATTERY - NISSAN_LEAF_BATTERY - PYLON_BATTERY - RJXZS_BMS + - RANGE_ROVER_PHEV_BATTERY - RENAULT_KANGOO_BATTERY + - RENAULT_TWIZY_BATTERY - RENAULT_ZOE_GEN1_BATTERY - RENAULT_ZOE_GEN2_BATTERY + - SANTA_FE_PHEV_BATTERY - TESLA_MODEL_3Y_BATTERY - VOLVO_SPA_BATTERY - TEST_FAKE_BATTERY @@ -54,13 +60,6 @@ jobs: # These are the emulated inverter communication protocols for which the code will be compiled. inverter: - BYD_CAN -# - BYD_MODBUS -# - PYLON_CAN -# - SMA_CAN -# - SMA_TRIPOWER_CAN -# - SOFAR_CAN -# - SOLAX_CAN - # This is the platform GitHub will use to run our workflow. runs-on: ubuntu-latest diff --git a/.github/workflows/compile-all-inverters.yml b/.github/workflows/compile-all-inverters.yml index 85beb3847..deb0ccc92 100644 --- a/.github/workflows/compile-all-inverters.yml +++ b/.github/workflows/compile-all-inverters.yml @@ -42,10 +42,12 @@ jobs: # - TESLA_MODEL_3Y_BATTERY # These are the emulated inverter communication protocols for which the code will be compiled. inverter: + - AFORE_CAN - BYD_CAN - BYD_SMA - BYD_MODBUS - FOXESS_CAN + - PYLON_LV_CAN - PYLON_CAN - SMA_CAN - SMA_TRIPOWER_CAN diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 5c2cb2c12..b7e362732 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -492,10 +492,10 @@ String processor(const String& var) { content += datalayer.system.info.battery_protocol; #ifdef DOUBLE_BATTERY content += " (Double battery)"; +#endif // DOUBLE_BATTERY if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { content += " (LFP)"; } -#endif // DOUBLE_BATTERY content += "

"; #if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER From 0b278034d419f3e73a3b788ee027c79925621882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 14 Nov 2024 22:25:10 +0200 Subject: [PATCH 63/81] Remove compilation error, runtime event catches egmp CAN-FD --- Software/Software.ino | 25 +++++-------------- Software/src/include.h | 6 ----- .../SERIAL-LINK-TRANSMITTER-INVERTER.h | 2 ++ 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index 303442a8e..957d4537d 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -70,13 +70,11 @@ volatile bool send_ok = 0; static const uint32_t QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL; //MHZ configured in USER_SETTINGS.h ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT); static ACAN2515_Buffer16 gBuffer; -#endif +#endif //DUAL_CAN #ifdef CAN_FD #include "src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h" ACAN2517FD canfd(MCP2517_CS, SPI, MCP2517_INT); -#else -typedef char CANFDMessage; -#endif +#endif //CAN_FD // ModbusRTU parameters #ifdef MODBUS_INVERTER_SELECTED @@ -172,11 +170,10 @@ void setup() { init_rs485(); init_serialDataLink(); - - init_inverter(); - - init_battery(); - +#if defined(CAN_INVERTER_SELECTED) || defined(MODBUS_INVERTER_SELECTED) + setup_inverter(); +#endif + setup_battery(); #ifdef EQUIPMENT_STOP_BUTTON init_equipment_stop_button(); #endif @@ -554,16 +551,6 @@ void init_rs485() { #endif } -void init_inverter() { - // Inform user what inverter is used and perform setup - setup_inverter(); -} - -void init_battery() { - // Inform user what battery is used and perform setup - setup_battery(); -} - #ifdef EQUIPMENT_STOP_BUTTON void monitor_equipment_stop_button() { diff --git a/Software/src/include.h b/Software/src/include.h index 83f2290d8..b29345e64 100644 --- a/Software/src/include.h +++ b/Software/src/include.h @@ -45,10 +45,4 @@ #error No battery selected! Choose one from the USER_SETTINGS.h file #endif -#ifdef KIA_E_GMP_BATTERY -#ifndef CAN_FD -#error KIA HYUNDAI EGMP BATTERIES CANNOT BE USED WITHOUT CAN FD -#endif -#endif - #endif diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h index 1637e6bd0..487ca1038 100644 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h @@ -1,6 +1,8 @@ #ifndef SERIAL_LINK_TRANSMITTER_INVERTER_H #define SERIAL_LINK_TRANSMITTER_INVERTER_H +#define MODBUS_INVERTER_SELECTED + #include #include "../include.h" #include "../lib/mackelec-SerialDataLink/SerialDataLink.h" From a537b7ff1b9060b69a95169381265788c03392d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 14 Nov 2024 22:33:35 +0200 Subject: [PATCH 64/81] Fix compilation issue for seriallink --- .../src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp | 10 ++-------- .../src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h | 2 -- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp index aa0a7ec4d..a61ea7dc0 100644 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp @@ -128,6 +128,8 @@ void manageSerialLinkTransmitter() { static unsigned long updateDataTime = 0; if (currentTime - updateDataTime > INTERVAL_1_S) { + strncpy(datalayer.system.info.inverter_protocol, "Serial link to another LilyGo board", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; updateDataTime = currentTime; dataLinkTransmit.updateData(0, datalayer.battery.status.real_soc); dataLinkTransmit.updateData(1, datalayer.battery.status.soh_pptt); @@ -159,10 +161,6 @@ void printSendingValues() { Serial.print(datalayer.battery.status.soh_pptt); Serial.print(" Voltage: "); Serial.print(datalayer.battery.status.voltage_dV); - void setup_inverter(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "Serial link to another LilyGo board", 63); - datalayer.system.info.inverter_protocol[63] = '\0'; - } Serial.print(" Current: "); Serial.print(datalayer.battery.status.current_dA); Serial.print(" Capacity: "); @@ -194,8 +192,4 @@ void printSendingValues() { Serial.println(""); } -void setup_inverter(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "Serial link to another LilyGo board", 63); - datalayer.system.info.inverter_protocol[63] = '\0'; -} #endif diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h index 487ca1038..1637e6bd0 100644 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h @@ -1,8 +1,6 @@ #ifndef SERIAL_LINK_TRANSMITTER_INVERTER_H #define SERIAL_LINK_TRANSMITTER_INVERTER_H -#define MODBUS_INVERTER_SELECTED - #include #include "../include.h" #include "../lib/mackelec-SerialDataLink/SerialDataLink.h" From 5d9105d9a85dd6c36f874b770b2c7e725fc21bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 14 Nov 2024 22:54:19 +0200 Subject: [PATCH 65/81] Add more BYD inverter mappings --- Software/src/inverter/BYD-CAN.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Software/src/inverter/BYD-CAN.cpp b/Software/src/inverter/BYD-CAN.cpp index ae0bb560a..58dc00e46 100644 --- a/Software/src/inverter/BYD-CAN.cpp +++ b/Software/src/inverter/BYD-CAN.cpp @@ -76,6 +76,8 @@ static uint8_t inverter_name[7] = {0}; static int16_t temperature_average = 0; static uint16_t inverter_voltage = 0; static uint16_t inverter_SOC = 0; +static int16_t inverter_current = 0; +static int16_t inverter_temperature = 0; static uint16_t remaining_capacity_ah = 0; static uint16_t fully_charged_capacity_ah = 0; static long inverter_timestamp = 0; @@ -165,6 +167,8 @@ void receive_can_inverter(CAN_frame rx_frame) { case 0x091: datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; inverter_voltage = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]) * 0.1; + inverter_current = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]) * 0.1; + inverter_temperature = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) * 0.1; break; case 0x0D1: datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; From 115e7db33fe69fa4c1400dad3fbee592681e764b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 15 Nov 2024 22:58:36 +0200 Subject: [PATCH 66/81] Revise commands from testing round --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 74 ++++++------------- Software/src/battery/NISSAN-LEAF-BATTERY.h | 10 +-- .../webserver/advanced_battery_html.cpp | 2 +- 3 files changed, 30 insertions(+), 56 deletions(-) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index d5e788091..06f976a0b 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -884,20 +884,7 @@ void receive_can_battery(CAN_frame rx_frame) { //Error checking if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F)) { challengeFailed = true; - Serial.print("Challenge solving failed"); } - // All CAN messages recieved from BMS will be logged via serial during development of this function - Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D - Serial.print(" "); - Serial.print(rx_frame.ID, HEX); - Serial.print(" "); - Serial.print(rx_frame.DLC); - Serial.print(" "); - for (int i = 0; i < rx_frame.DLC; ++i) { - Serial.print(rx_frame.data.u8[i], HEX); - Serial.print(" "); - } - Serial.println(""); break; } @@ -1353,23 +1340,11 @@ void clearSOH(void) { default: break; } - // All CAN messages semt will be logged via serial during development of this function - Serial.print(millis()); // Example printout, time, ID, length, data: 7553 7B9 8 FF C0 B9 EA 0 0 2 5D - Serial.print(" "); - Serial.print(LEAF_CLEAR_SOH.ID, HEX); - Serial.print(" "); - Serial.print(LEAF_CLEAR_SOH.DLC); - Serial.print(" "); - for (int i = 0; i < LEAF_CLEAR_SOH.DLC; ++i) { - Serial.print(LEAF_CLEAR_SOH.data.u8[i], HEX); - Serial.print(" "); - } - Serial.println(""); } -uint32_t CyclicXorHash16Bit(uint32_t param_1, uint32_t param_2) { +unsigned int CyclicXorHash16Bit(unsigned int param_1, unsigned int param_2) { bool bVar1; - uint32_t uVar2, uVar3, uVar4, uVar5, uVar6, uVar7, uVar8, uVar9, uVar10, uVar11, iVar12; + unsigned int uVar2, uVar3, uVar4, uVar5, uVar6, uVar7, uVar8, uVar9, uVar10, uVar11, iVar12; param_1 = param_1 & 0xffff; param_2 = param_2 & 0xffff; @@ -1430,54 +1405,53 @@ uint32_t CyclicXorHash16Bit(uint32_t param_1, uint32_t param_2) { } while (bVar1); return uVar10; } - -uint32_t ComputeMaskedXorProduct(uint32_t param_1, uint32_t param_2, uint32_t param_3) { - return (param_3 ^ 0x780 | param_2 ^ 0x116) * ((param_1 & 0xffff) >> 8 ^ param_1 & 0xff) & 0xffff; +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; } short ShortMaskedSumAndProduct(short param_1, short param_2) { unsigned short uVar1; - uVar1 = param_2 + param_1 * 0x5ba & 0xff; + uVar1 = param_2 + param_1 * 0x0006 & 0xff; return (uVar1 + param_1) * (uVar1 + param_2); } -uint32_t MaskedBitwiseRotateMultiply(uint32_t param_1, uint32_t param_2) { - uint32_t uVar1; +unsigned int MaskedBitwiseRotateMultiply(unsigned int param_1, unsigned int param_2) { + unsigned int uVar1; param_1 = param_1 & 0xffff; param_2 = param_2 & 0xffff; - uVar1 = param_2 & (param_1 | 0x5ba) & 0xf; - return ((uint32_t)param_1 >> uVar1 | param_1 << (0x10 - uVar1 & 0x1f)) * - (param_2 << uVar1 | (uint32_t)param_2 >> (0x10 - uVar1 & 0x1f)) & + 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)) & 0xffff; } -uint32_t CryptAlgo(uint32_t param_1, uint32_t param_2, uint32_t param_3) { - uint32_t uVar1, uVar2, iVar3, iVar4; +unsigned int CryptAlgo(unsigned int param_1, unsigned int param_2, unsigned int param_3) { + unsigned int uVar1, uVar2, iVar3, iVar4; uVar1 = MaskedBitwiseRotateMultiply(param_2, param_3); uVar2 = ShortMaskedSumAndProduct(param_2, param_3); uVar1 = ComputeMaskedXorProduct(param_1, uVar1, uVar2); uVar2 = ComputeMaskedXorProduct(param_1, uVar2, uVar1); - iVar3 = CyclicXorHash16Bit(uVar1, 0xffc4); - iVar4 = CyclicXorHash16Bit(uVar2, 0xffc4); + iVar3 = CyclicXorHash16Bit(uVar1, 0x8421); + iVar4 = CyclicXorHash16Bit(uVar2, 0x8421); return iVar4 + iVar3 * 0x10000; } -void decodeChallengeData(uint32_t incomingChallenge, unsigned char* solvedChallenge) { - uint32_t uVar1, uVar2; +void decodeChallengeData(unsigned int incomingChallenge, unsigned char* solvedChallenge) { + unsigned int uVar1, uVar2; - uVar1 = CryptAlgo(0x609, 0xDD2, incomingChallenge >> 0x10); - uVar2 = CryptAlgo(incomingChallenge & 0xffff, incomingChallenge >> 0x10, 0x609); + uVar1 = CryptAlgo(0x54e9, 0x3afd, incomingChallenge >> 0x10); + uVar2 = CryptAlgo(incomingChallenge & 0xffff, incomingChallenge >> 0x10, 0x54e9); *solvedChallenge = (unsigned char)uVar1; solvedChallenge[1] = (unsigned char)uVar2; - solvedChallenge[2] = (unsigned char)((uint32_t)uVar2 >> 8); - solvedChallenge[3] = (unsigned char)((uint32_t)uVar1 >> 8); - solvedChallenge[4] = (unsigned char)((uint32_t)uVar2 >> 16); - solvedChallenge[5] = (unsigned char)((uint32_t)uVar1 >> 16); - solvedChallenge[6] = (unsigned char)((uint32_t)uVar2 >> 24); - solvedChallenge[7] = (unsigned char)((uint32_t)uVar1 >> 24); + solvedChallenge[2] = (unsigned char)((unsigned int)uVar2 >> 8); + solvedChallenge[3] = (unsigned char)((unsigned int)uVar1 >> 8); + solvedChallenge[4] = (unsigned char)((unsigned int)uVar2 >> 0x10); + solvedChallenge[5] = (unsigned char)((unsigned int)uVar1 >> 0x10); + solvedChallenge[6] = (unsigned char)((unsigned int)uVar2 >> 0x18); + solvedChallenge[7] = (unsigned char)((unsigned int)uVar1 >> 0x18); return; } diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.h b/Software/src/battery/NISSAN-LEAF-BATTERY.h index 2d29c0d7f..245f0f397 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.h +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.h @@ -16,11 +16,11 @@ void setup_battery(void); void transmit_can(CAN_frame* tx_frame, int interface); void clearSOH(void); //Cryptographic functions -void decodeChallengeData(uint32_t incomingChallenge, unsigned char* solvedChallenge); -uint32_t CyclicXorHash16Bit(uint32_t param_1, uint32_t param_2); -uint32_t ComputeMaskedXorProduct(uint32_t param_1, uint32_t param_2, uint32_t param_3); +void decodeChallengeData(unsigned int SeedInput, unsigned char* Crypt_Output_Buffer); +unsigned int CyclicXorHash16Bit(unsigned int param_1, unsigned int param_2); +unsigned int ComputeMaskedXorProduct(unsigned int param_1, unsigned int param_2, unsigned int param_3); short ShortMaskedSumAndProduct(short param_1, short param_2); -uint32_t MaskedBitwiseRotateMultiply(uint32_t param_1, uint32_t param_2); -uint32_t CryptAlgo(uint32_t param_1, uint32_t param_2, uint32_t param_3); +unsigned int MaskedBitwiseRotateMultiply(unsigned int param_1, unsigned int param_2); +unsigned int CryptAlgo(unsigned int param_1, unsigned int param_2, unsigned int param_3); #endif diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index 00202d3e1..f734230ac 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -356,7 +356,7 @@ String advanced_battery_processor(const String& var) { content += ""; return content; diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 8eab7631e..ce4ec7d4e 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1,6 +1,7 @@ #include "webserver.h" #include #include "../../datalayer/datalayer.h" +#include "../../datalayer/datalayer_extended.h" #include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h" #include "../utils/events.h" #include "../utils/led_handler.h" @@ -231,6 +232,15 @@ void init_webserver() { } }); + // Route for resetting SOH on Nissan LEAF batteries + server.on("/resetSOH", HTTP_GET, [](AsyncWebServerRequest* request) { + if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) { + return request->requestAuthentication(); + } + datalayer_extended.nissanleaf.UserRequestSOHreset = true; + request->send(200, "text/plain", "Updated successfully"); + }); + #ifdef TEST_FAKE_BATTERY // Route for editing FakeBatteryVoltage server.on("/updateFakeBatteryVoltage", HTTP_GET, [](AsyncWebServerRequest* request) { @@ -486,117 +496,16 @@ String processor(const String& var) { // Display which components are used content += "

Inverter protocol: "; -#ifdef BYD_CAN - content += "BYD Battery-Box Premium HVS over CAN Bus"; -#endif // BYD_CAN -#ifdef BYD_MODBUS - content += "BYD 11kWh HVM battery over Modbus RTU"; -#endif // BYD_MODBUS -#ifdef FOXESS_CAN - content += "FoxESS compatible HV2600/ECS4100 battery"; -#endif // FOXESS_CAN -#ifdef PYLON_CAN - content += "Pylontech battery over CAN bus"; -#endif // PYLON_CAN -#ifdef PYLON_LV_CAN - content += "Pylontech LV battery over CAN bus"; -#endif // PYLON_LV_CAN -#ifdef SERIAL_LINK_TRANSMITTER - content += "Serial link to another LilyGo board"; -#endif // SERIAL_LINK_TRANSMITTER -#ifdef SMA_CAN - content += "BYD Battery-Box H 8.9kWh, 7 mod over CAN bus"; -#endif // SMA_CAN -#ifdef SOFAR_CAN - content += "Sofar Energy Storage Inverter High Voltage BMS General Protocol (Extended Frame) over CAN bus"; -#endif // SOFAR_CAN -#ifdef SOLAX_CAN - content += "SolaX Triple Power LFP over CAN bus"; -#endif // SOLAX_CAN + content += datalayer.system.info.inverter_protocol; content += "

"; - content += "

Battery protocol: "; -#ifdef BMW_I3_BATTERY - content += "BMW i3"; -#endif // BMW_I3_BATTERY -#ifdef BMW_IX_BATTERY - content += "BMW iX and i4-7 platform"; -#endif // BMW_IX_BATTERY -#ifdef BYD_ATTO_3_BATTERY - content += "BYD Atto 3"; -#endif // BYD_ATTO_3_BATTERY -#ifdef CELLPOWER_BMS - content += "Cellpower BMS"; -#endif // CELLPOWER_BMS -#ifdef CHADEMO_BATTERY - content += "Chademo V2X mode"; -#endif // CHADEMO_BATTERY -#ifdef IMIEV_CZERO_ION_BATTERY - content += "I-Miev / C-Zero / Ion Triplet"; -#endif // IMIEV_CZERO_ION_BATTERY -#ifdef JAGUAR_IPACE_BATTERY - content += "Jaguar I-PACE"; -#endif // JAGUAR_IPACE_BATTERY -#ifdef KIA_HYUNDAI_64_BATTERY - content += "Kia/Hyundai 64kWh"; -#endif // KIA_HYUNDAI_64_BATTERY -#ifdef KIA_E_GMP_BATTERY - content += "Kia/Hyundai EGMP platform"; -#endif // KIA_E_GMP_BATTERY -#ifdef KIA_HYUNDAI_HYBRID_BATTERY - content += "Kia/Hyundai Hybrid"; -#endif // KIA_HYUNDAI_HYBRID_BATTERY -#ifdef MG_5_BATTERY - content += "MG 5"; -#endif // MG_5_BATTERY -#ifdef NISSAN_LEAF_BATTERY - content += "Nissan LEAF"; -#endif // NISSAN_LEAF_BATTERY -#ifdef PYLON_BATTERY - content += "Pylon compatible battery"; -#endif // PYLON_BATTERY -#ifdef RJXZS_BMS - content += "RJXZS BMS, DIY battery"; -#endif // RJXZS_BMS -#ifdef RANGE_ROVER_PHEV_BATTERY - content += "Range Rover 13kWh PHEV battery (L494/L405)"; -#endif //RANGE_ROVER_PHEV_BATTERY -#ifdef RENAULT_KANGOO_BATTERY - content += "Renault Kangoo"; -#endif // RENAULT_KANGOO_BATTERY -#ifdef RENAULT_TWIZY_BATTERY - content += "Renault Twizy"; -#endif // RENAULT_TWIZY_BATTERY -#ifdef RENAULT_ZOE_GEN1_BATTERY - content += "Renault Zoe Gen1 22/40"; -#endif // RENAULT_ZOE_GEN1_BATTERY -#ifdef RENAULT_ZOE_GEN2_BATTERY - content += "Renault Zoe Gen2 50"; -#endif // RENAULT_ZOE_GEN2_BATTERY -#ifdef SANTA_FE_PHEV_BATTERY - content += "Santa Fe PHEV"; -#endif // SANTA_FE_PHEV_BATTERY -#ifdef SERIAL_LINK_RECEIVER - content += "Serial link to another LilyGo board"; -#endif // SERIAL_LINK_RECEIVER -#ifdef TESLA_MODEL_SX_BATTERY - content += "Tesla Model S/X"; -#endif // TESLA_MODEL_SX_BATTERY -#ifdef TESLA_MODEL_3Y_BATTERY - content += "Tesla Model 3/Y"; -#endif // TESLA_MODEL_3Y_BATTERY -#ifdef VOLVO_SPA_BATTERY - content += "Volvo / Polestar 78kWh battery"; -#endif // VOLVO_SPA_BATTERY -#ifdef TEST_FAKE_BATTERY - content += "Fake battery for testing purposes"; -#endif // TEST_FAKE_BATTERY + content += datalayer.system.info.battery_protocol; #ifdef DOUBLE_BATTERY content += " (Double battery)"; +#endif // DOUBLE_BATTERY if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { content += " (LFP)"; } -#endif // DOUBLE_BATTERY content += "

"; #if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER diff --git a/Software/src/include.h b/Software/src/include.h index 83f2290d8..b29345e64 100644 --- a/Software/src/include.h +++ b/Software/src/include.h @@ -45,10 +45,4 @@ #error No battery selected! Choose one from the USER_SETTINGS.h file #endif -#ifdef KIA_E_GMP_BATTERY -#ifndef CAN_FD -#error KIA HYUNDAI EGMP BATTERIES CANNOT BE USED WITHOUT CAN FD -#endif -#endif - #endif diff --git a/Software/src/inverter/AFORE-CAN.cpp b/Software/src/inverter/AFORE-CAN.cpp index 744351166..a8d551d77 100644 --- a/Software/src/inverter/AFORE-CAN.cpp +++ b/Software/src/inverter/AFORE-CAN.cpp @@ -233,4 +233,8 @@ void send_can_inverter() { time_to_send_info = false; } } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "Afore battery over CAN", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/AFORE-CAN.h b/Software/src/inverter/AFORE-CAN.h index cc9f8548b..9829befca 100644 --- a/Software/src/inverter/AFORE-CAN.h +++ b/Software/src/inverter/AFORE-CAN.h @@ -4,8 +4,7 @@ #define CAN_INVERTER_SELECTED -void send_system_data(); -void send_setup_info(); void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/BYD-CAN.cpp b/Software/src/inverter/BYD-CAN.cpp index 5cf999a34..4ffa7b204 100644 --- a/Software/src/inverter/BYD-CAN.cpp +++ b/Software/src/inverter/BYD-CAN.cpp @@ -7,13 +7,6 @@ static unsigned long previousMillis2s = 0; // will store last time a 2s CAN Message was send static unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send static unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send -static uint8_t char1_151 = 0; -static uint8_t char2_151 = 0; -static uint8_t char3_151 = 0; -static uint8_t char4_151 = 0; -static uint8_t char5_151 = 0; -static uint8_t char6_151 = 0; -static uint8_t char7_151 = 0; CAN_frame BYD_250 = {.FD = false, .ext_ID = false, @@ -79,9 +72,12 @@ CAN_frame BYD_210 = {.FD = false, .ID = 0x210, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +static uint8_t inverter_name[7] = {0}; static int16_t temperature_average = 0; static uint16_t inverter_voltage = 0; static uint16_t inverter_SOC = 0; +static int16_t inverter_current = 0; +static int16_t inverter_temperature = 0; static uint16_t remaining_capacity_ah = 0; static uint16_t fully_charged_capacity_ah = 0; static long inverter_timestamp = 0; @@ -146,15 +142,12 @@ void update_values_can_inverter() { //This function maps all the values fetched BYD_210.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF); #ifdef DEBUG_VIA_USB - if (char1_151 != 0) { + if (inverter_name[0] != 0) { Serial.print("Detected inverter: "); - Serial.print((char)char1_151); - Serial.print((char)char2_151); - Serial.print((char)char3_151); - Serial.print((char)char4_151); - Serial.print((char)char5_151); - Serial.print((char)char6_151); - Serial.println((char)char7_151); + for (uint8_t i = 0; i < 7; i++) { + Serial.print((char)inverter_name[i]); + } + Serial.println(); } #endif } @@ -166,27 +159,25 @@ void receive_can_inverter(CAN_frame rx_frame) { if (rx_frame.data.u8[0] & 0x01) { //Battery requests identification send_intial_data(); } else { // We can identify what inverter type we are connected to - char1_151 = rx_frame.data.u8[1]; - char2_151 = rx_frame.data.u8[2]; - char3_151 = rx_frame.data.u8[3]; - char4_151 = rx_frame.data.u8[4]; - char5_151 = rx_frame.data.u8[5]; - char6_151 = rx_frame.data.u8[6]; - char7_151 = rx_frame.data.u8[7]; + for (uint8_t i = 0; i < 7; i++) { + inverter_name[i] = rx_frame.data.u8[i + 1]; + } } break; case 0x091: datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; - inverter_voltage = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.1; + inverter_voltage = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]) * 0.1; + inverter_current = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]) * 0.1; + inverter_temperature = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) * 0.1; break; case 0x0D1: datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; - inverter_SOC = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.1; + inverter_SOC = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]) * 0.1; break; case 0x111: datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; - inverter_timestamp = ((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[2] << 16) | (rx_frame.data.u8[1] << 8) | - rx_frame.data.u8[0]); + inverter_timestamp = ((rx_frame.data.u8[0] << 24) | (rx_frame.data.u8[1] << 16) | (rx_frame.data.u8[2] << 8) | + rx_frame.data.u8[3]); break; default: break; @@ -232,4 +223,8 @@ void send_intial_data() { transmit_can(&BYD_3D0_2, can_config.inverter); transmit_can(&BYD_3D0_3, can_config.inverter); } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "BYD Battery-Box Premium HVS over CAN Bus", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/BYD-CAN.h b/Software/src/inverter/BYD-CAN.h index 6a45317b9..5a90d6bab 100644 --- a/Software/src/inverter/BYD-CAN.h +++ b/Software/src/inverter/BYD-CAN.h @@ -8,5 +8,6 @@ void send_intial_data(); void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/BYD-MODBUS.cpp b/Software/src/inverter/BYD-MODBUS.cpp index 7cee6b8d3..814e24e46 100644 --- a/Software/src/inverter/BYD-MODBUS.cpp +++ b/Software/src/inverter/BYD-MODBUS.cpp @@ -143,4 +143,8 @@ void verify_inverter_modbus() { history_index = (history_index + 1) % HISTORY_LENGTH; } } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "BYD 11kWh HVM battery over Modbus RTU", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/BYD-MODBUS.h b/Software/src/inverter/BYD-MODBUS.h index 3d014daa4..487c97847 100644 --- a/Software/src/inverter/BYD-MODBUS.h +++ b/Software/src/inverter/BYD-MODBUS.h @@ -14,4 +14,5 @@ void verify_temperature_modbus(); void verify_inverter_modbus(); void handle_update_data_modbusp201_byd(); void handle_update_data_modbusp301_byd(); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/BYD-SMA.cpp b/Software/src/inverter/BYD-SMA.cpp index 230b61e79..34e33063a 100644 --- a/Software/src/inverter/BYD-SMA.cpp +++ b/Software/src/inverter/BYD-SMA.cpp @@ -251,4 +251,10 @@ void send_can_inverter() { } } } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "BYD Battery-Box HVS over SMA CAN", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; + datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first + pinMode(INVERTER_CONTACTOR_ENABLE_PIN, INPUT); +} #endif diff --git a/Software/src/inverter/BYD-SMA.h b/Software/src/inverter/BYD-SMA.h index b71a60fa7..e787191d9 100644 --- a/Software/src/inverter/BYD-SMA.h +++ b/Software/src/inverter/BYD-SMA.h @@ -8,5 +8,6 @@ #define STOP_STATE 0x02 void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/FOXESS-CAN.cpp b/Software/src/inverter/FOXESS-CAN.cpp index e4b3b34ca..e23f90062 100644 --- a/Software/src/inverter/FOXESS-CAN.cpp +++ b/Software/src/inverter/FOXESS-CAN.cpp @@ -737,4 +737,8 @@ void receive_can_inverter(CAN_frame rx_frame) { } } } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "FoxESS compatible HV2600/ECS4100 battery", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/FOXESS-CAN.h b/Software/src/inverter/FOXESS-CAN.h index d9a3da2d5..365a559d0 100644 --- a/Software/src/inverter/FOXESS-CAN.h +++ b/Software/src/inverter/FOXESS-CAN.h @@ -5,5 +5,6 @@ #define CAN_INVERTER_SELECTED void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/PYLON-CAN.cpp b/Software/src/inverter/PYLON-CAN.cpp index 2b8e036c0..2145b3e7f 100644 --- a/Software/src/inverter/PYLON-CAN.cpp +++ b/Software/src/inverter/PYLON-CAN.cpp @@ -477,4 +477,8 @@ void send_system_data() { //System equipment information transmit_can(&PYLON_4291, can_config.inverter); #endif } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "Pylontech battery over CAN bus", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/PYLON-CAN.h b/Software/src/inverter/PYLON-CAN.h index 86bb4afa4..6b39afcde 100644 --- a/Software/src/inverter/PYLON-CAN.h +++ b/Software/src/inverter/PYLON-CAN.h @@ -7,5 +7,6 @@ void send_system_data(); void send_setup_info(); void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/PYLON-LV-CAN.cpp b/Software/src/inverter/PYLON-LV-CAN.cpp index 458ac7a04..e5eb2e874 100644 --- a/Software/src/inverter/PYLON-LV-CAN.cpp +++ b/Software/src/inverter/PYLON-LV-CAN.cpp @@ -133,4 +133,8 @@ void send_can_inverter() { transmit_can(&PYLON_35E, can_config.inverter); } } +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "Pylontech LV battery over CAN bus", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/PYLON-LV-CAN.h b/Software/src/inverter/PYLON-LV-CAN.h index 45376d2b8..ca6922eb2 100644 --- a/Software/src/inverter/PYLON-LV-CAN.h +++ b/Software/src/inverter/PYLON-LV-CAN.h @@ -12,5 +12,6 @@ void send_system_data(); void send_setup_info(); void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp index 6d3b50713..a61ea7dc0 100644 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp @@ -128,6 +128,8 @@ void manageSerialLinkTransmitter() { static unsigned long updateDataTime = 0; if (currentTime - updateDataTime > INTERVAL_1_S) { + strncpy(datalayer.system.info.inverter_protocol, "Serial link to another LilyGo board", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; updateDataTime = currentTime; dataLinkTransmit.updateData(0, datalayer.battery.status.real_soc); dataLinkTransmit.updateData(1, datalayer.battery.status.soh_pptt); diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h index 14ae08d75..1637e6bd0 100644 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h @@ -6,5 +6,6 @@ #include "../lib/mackelec-SerialDataLink/SerialDataLink.h" void manageSerialLinkTransmitter(); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/SMA-CAN.cpp b/Software/src/inverter/SMA-CAN.cpp index cecae60ce..5831406d2 100644 --- a/Software/src/inverter/SMA-CAN.cpp +++ b/Software/src/inverter/SMA-CAN.cpp @@ -249,4 +249,9 @@ void send_can_inverter() { } } } + +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "SMA CAN", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/SMA-CAN.h b/Software/src/inverter/SMA-CAN.h index 73b9f1602..111044a46 100644 --- a/Software/src/inverter/SMA-CAN.h +++ b/Software/src/inverter/SMA-CAN.h @@ -8,5 +8,6 @@ #define STOP_STATE 0x02 void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp index 9c0cc2794..7a242daee 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp @@ -320,4 +320,9 @@ void send_tripower_init() { transmit_can(&SMA_017, can_config.inverter); // Battery Manufacturer transmit_can(&SMA_018, can_config.inverter); // Battery Name } + +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "SMA Tripower CAN", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.h b/Software/src/inverter/SMA-TRIPOWER-CAN.h index e12692da4..90967001f 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.h +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.h @@ -6,5 +6,6 @@ void send_tripower_init(); void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/SOFAR-CAN.cpp b/Software/src/inverter/SOFAR-CAN.cpp index eb13b7c57..838ebf932 100644 --- a/Software/src/inverter/SOFAR-CAN.cpp +++ b/Software/src/inverter/SOFAR-CAN.cpp @@ -263,4 +263,9 @@ void send_can_inverter() { transmit_can(&SOFAR_35A, can_config.inverter); } } + +void setup_inverter(void) { // Performs one time setup at startup over CAN bus + strncpy(datalayer.system.info.inverter_protocol, "Sofar BMS (Extended Frame) over CAN bus", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; +} #endif diff --git a/Software/src/inverter/SOFAR-CAN.h b/Software/src/inverter/SOFAR-CAN.h index 6d43222fe..7a80bf62a 100644 --- a/Software/src/inverter/SOFAR-CAN.h +++ b/Software/src/inverter/SOFAR-CAN.h @@ -5,5 +5,6 @@ #define CAN_INVERTER_SELECTED void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif diff --git a/Software/src/inverter/SOLAX-CAN.cpp b/Software/src/inverter/SOLAX-CAN.cpp index 790c85249..9bef56ec8 100644 --- a/Software/src/inverter/SOLAX-CAN.cpp +++ b/Software/src/inverter/SOLAX-CAN.cpp @@ -252,4 +252,9 @@ void receive_can_inverter(CAN_frame rx_frame) { #endif } } +void setup_inverter(void) { // Performs one time setup at startup + strncpy(datalayer.system.info.inverter_protocol, "SolaX Triple Power LFP over CAN bus", 63); + datalayer.system.info.inverter_protocol[63] = '\0'; + datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first +} #endif diff --git a/Software/src/inverter/SOLAX-CAN.h b/Software/src/inverter/SOLAX-CAN.h index 6ad461a79..68373ec2a 100644 --- a/Software/src/inverter/SOLAX-CAN.h +++ b/Software/src/inverter/SOLAX-CAN.h @@ -15,5 +15,6 @@ #define UPDATING_FW 4 void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif From f864f2ae36488023ad392aac8059e9cb20dd9c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Nov 2024 19:28:43 +0200 Subject: [PATCH 73/81] Add string to show what SOC method used --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 6 ++++++ Software/src/datalayer/datalayer_extended.h | 3 +++ Software/src/devboard/webserver/advanced_battery_html.cpp | 2 ++ 3 files changed, 11 insertions(+) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 763ba32d9..8d995291b 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -15,6 +15,7 @@ static unsigned long previousMillis50 = 0; // will store last time a 50ms CAN Message was send static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send static unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send +static bool SOC_method = false; static uint8_t counter_50ms = 0; static uint8_t counter_100ms = 0; static uint8_t frame6_counter = 0xB; @@ -61,6 +62,8 @@ static uint16_t BMS2_highest_cell_voltage_mV = 3300; #define POLL_FOR_BATTERY_CELL_MV_MAX 0x2D #define POLL_FOR_BATTERY_CELL_MV_MIN 0x2B #define UNKNOWN_POLL_1 0xFC +#define ESTIMATED 0 +#define MEASURED 1 static uint16_t poll_state = POLL_FOR_BATTERY_SOC; CAN_frame ATTO_3_12D = {.FD = false, @@ -113,8 +116,10 @@ void update_values_battery() { //This function maps all the values fetched via // We instead estimate the SOC% based on the battery voltage. // This is a bad solution, you wont be able to use 100% of the battery datalayer.battery.status.real_soc = estimateSOC(datalayer.battery.status.voltage_dV); + SOC_method = ESTIMATED; #else // Pack is not crashed, we can use periodically transmitted SOC datalayer.battery.status.real_soc = highprecision_SOC * 100; + SOC_method = MEASURED; #endif datalayer.battery.status.current_dA = -BMS_current; @@ -151,6 +156,7 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.temperature_max_dC = battery_calc_max_temperature * 10; // Update webserver datalayer + datalayer_extended.bydAtto3.SOC_method = SOC_method; datalayer_extended.bydAtto3.SOC_estimated = datalayer.battery.status.real_soc; //Once we implement switching logic, remember to change from where the estimated is taken datalayer_extended.bydAtto3.SOC_highprec = battery_highprecision_SOC; diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 2009d60b3..c4c8f33c4 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -71,6 +71,9 @@ typedef struct { } DATALAYER_INFO_BMWI3; typedef struct { + /** bool */ + /** Which SOC method currently used. 0 = Estimated, 1 = Measured */ + bool SOC_method = 0; /** uint16_t */ /** SOC% estimate. Estimated from total pack voltage */ uint16_t SOC_estimated = 0; diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index 5db011272..4d2e5a778 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -275,6 +275,8 @@ String advanced_battery_processor(const String& var) { #endif //CELLPOWER_BMS #ifdef BYD_ATTO_3_BATTERY + static const char* SOCmethod[2] = {"Estimated from voltage", "Measured by BMS"}; + content += "

SOC method used: " + String(SOCmethod[datalayer_extended.bydAtto3.SOC_method]) + "

"; content += "

SOC estimated: " + String(datalayer_extended.bydAtto3.SOC_estimated) + "

"; content += "

SOC highprec: " + String(datalayer_extended.bydAtto3.SOC_highprec) + "

"; content += "

SOC OBD2: " + String(datalayer_extended.bydAtto3.SOC_polled) + "

"; From bfebe18876dc7a8baf10898d8d3753a709f0e692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Nov 2024 19:46:24 +0200 Subject: [PATCH 74/81] Update Software.ino --- Software/Software.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.ino b/Software/Software.ino index 957d4537d..8798af478 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -53,7 +53,7 @@ Preferences settings; // Store user settings // The current software version, shown on webserver -const char* version_number = "7.7.dev"; +const char* version_number = "7.7.0"; // Interval settings uint16_t intervalUpdateValues = INTERVAL_1_S; // Interval at which to update inverter values / Modbus registers From 478d0014ade5d88e325c1769c5a25bf7166c4f30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Nov 2024 20:09:17 +0200 Subject: [PATCH 75/81] Update Software.ino --- Software/Software.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.ino b/Software/Software.ino index 8798af478..08f7a76fd 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -53,7 +53,7 @@ Preferences settings; // Store user settings // The current software version, shown on webserver -const char* version_number = "7.7.0"; +const char* version_number = "7.8.dev"; // Interval settings uint16_t intervalUpdateValues = INTERVAL_1_S; // Interval at which to update inverter values / Modbus registers From 18e09918207aee37fcc76380885d9485607dc2e0 Mon Sep 17 00:00:00 2001 From: NJbubo Date: Sun, 17 Nov 2024 21:24:04 +0100 Subject: [PATCH 76/81] =?UTF-8?q?Removed=20the=20intervalUpdateValues=20?= =?UTF-8?q?=E2=80=8B=E2=80=8B=3D=20800=20line=20(commented).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Software/src/battery/CHADEMO-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index f445724d0..5207806ce 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -1032,7 +1032,7 @@ void handle_chademo_sequence() { void setup_battery(void) { // Performs one time setup at startup - intervalUpdateValues = 800; // This mode requires the values to be updated faster +// intervalUpdateValues = 800; // This mode requires the values to be updated faster pinMode(CHADEMO_PIN_2, OUTPUT); digitalWrite(CHADEMO_PIN_2, LOW); pinMode(CHADEMO_PIN_10, OUTPUT); From d58299e936374b61ebed9e0af7f77ac45a9bcd7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 18 Nov 2024 13:40:23 +0200 Subject: [PATCH 77/81] Fix compilation error --- Software/src/inverter/SCHNEIDER-CAN.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Software/src/inverter/SCHNEIDER-CAN.h b/Software/src/inverter/SCHNEIDER-CAN.h index 7178a533f..83e7c87ae 100644 --- a/Software/src/inverter/SCHNEIDER-CAN.h +++ b/Software/src/inverter/SCHNEIDER-CAN.h @@ -28,5 +28,6 @@ #define COMMAND_STOP 0x08 void transmit_can(CAN_frame* tx_frame, int interface); +void setup_inverter(void); #endif From 6412a9e64dcbba6e38c47163cdd509e8420ab72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 19 Nov 2024 17:50:43 +0200 Subject: [PATCH 78/81] Fix compilation error Fixed compilation error when using SOC sent from battery --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 9b63cdd74..5ad4220fb 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -118,7 +118,7 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.real_soc = estimateSOC(datalayer.battery.status.voltage_dV); SOC_method = ESTIMATED; #else // Pack is not crashed, we can use periodically transmitted SOC - datalayer.battery.status.real_soc = highprecision_SOC * 100; + datalayer.battery.status.real_soc = battery_highprecision_SOC * 100; SOC_method = MEASURED; #endif From 7e15571a4108dcc3da5594e774039d093ea71041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 20 Nov 2024 22:20:35 +0200 Subject: [PATCH 79/81] Add currents to battery 2 in webserver --- Software/src/devboard/webserver/webserver.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index ce4ec7d4e..2792a0455 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -714,8 +714,19 @@ String processor(const String& var) { content += formatPowerValue("Real Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1); content += formatPowerValue("Scaled Remaining capacity", datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1); - content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1); - content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1); + + if (emulator_pause_status == NORMAL) { + content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1); + content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1); + content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; + content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; + } else { + 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"); + content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; + content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; + } + content += "

Cell max: " + String(datalayer.battery2.status.cell_max_voltage_mV) + " mV

"; content += "

Cell min: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV

"; if (cell_delta_mv > datalayer.battery2.info.max_cell_voltage_deviation_mV) { From f915184edd1568d37079777b69ede0a2a459c5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 20 Nov 2024 22:24:38 +0200 Subject: [PATCH 80/81] Make paused text stay red incase user reboots --- Software/src/devboard/webserver/webserver.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 2792a0455..640429dc0 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -585,16 +585,16 @@ String processor(const String& var) { content += formatPowerValue("Scaled Remaining capacity", datalayer.battery.status.reported_remaining_capacity_Wh, "h", 1); - if (emulator_pause_status == NORMAL) { - content += formatPowerValue("Max discharge power", datalayer.battery.status.max_discharge_power_W, "", 1); - content += formatPowerValue("Max charge power", datalayer.battery.status.max_charge_power_W, "", 1); - content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; - content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; - } else { + if (datalayer.system.settings.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"); content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; + } else { + content += formatPowerValue("Max discharge power", datalayer.battery.status.max_discharge_power_W, "", 1); + content += formatPowerValue("Max charge power", datalayer.battery.status.max_charge_power_W, "", 1); + content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; + content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; } content += "

Cell max: " + String(datalayer.battery.status.cell_max_voltage_mV) + " mV

"; @@ -715,16 +715,16 @@ String processor(const String& var) { content += formatPowerValue("Scaled Remaining capacity", datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1); - if (emulator_pause_status == NORMAL) { - content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1); - content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1); - content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; - content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; - } else { + if (datalayer.system.settings.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"); content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; + } else { + content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1); + content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1); + content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; + content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; } content += "

Cell max: " + String(datalayer.battery2.status.cell_max_voltage_mV) + " mV

"; From eb850e616c3e4715ad6f3a97ce2d73432954caa1 Mon Sep 17 00:00:00 2001 From: NJbubo Date: Thu, 21 Nov 2024 01:22:11 +0100 Subject: [PATCH 81/81] =?UTF-8?q?I=20use=20pre-commit.=20Removed=20=20inte?= =?UTF-8?q?rvalUpdateValues=20=E2=80=8B=E2=80=8B=3D=20800=20row.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Software/src/battery/CHADEMO-BATTERY.cpp | 7 +- Software/src/battery/CHADEMO-SHUNTS.cpp | 88 ++++++++++++------------ 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index 5207806ce..14720065c 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -1032,7 +1032,6 @@ void handle_chademo_sequence() { void setup_battery(void) { // Performs one time setup at startup -// intervalUpdateValues = 800; // This mode requires the values to be updated faster pinMode(CHADEMO_PIN_2, OUTPUT); digitalWrite(CHADEMO_PIN_2, LOW); pinMode(CHADEMO_PIN_10, OUTPUT); @@ -1085,9 +1084,9 @@ void setup_battery(void) { // Performs one time setup at startup x109_evse_state.s.status.ChgDischStopControl = 1; handle_chademo_sequence(); -// ISA_deFAULT(); // ISA Setup - it is sufficient to set it once, because it is saved in SUNT -// ISA_initialize(); // ISA Setup - it is sufficient to set it once, because it is saved in SUNT -// ISA_RESTART(); + // ISA_deFAULT(); // ISA Setup - it is sufficient to set it once, because it is saved in SUNT + // ISA_initialize(); // ISA Setup - it is sufficient to set it once, because it is saved in SUNT + // ISA_RESTART(); setupMillis = millis(); } diff --git a/Software/src/battery/CHADEMO-SHUNTS.cpp b/Software/src/battery/CHADEMO-SHUNTS.cpp index 34dfb734b..6ca55ca73 100644 --- a/Software/src/battery/CHADEMO-SHUNTS.cpp +++ b/Software/src/battery/CHADEMO-SHUNTS.cpp @@ -241,21 +241,21 @@ inline void ISA_handle528(CAN_frame* frame) { } void ISA_initialize() { - firstframe=false; + firstframe = false; ISA_STOP(); delay(500); - for(int i=0;i<8;i++) { + for (int i = 0; i < 8; i++) { Serial.print("ISA Initialization "); Serial.println(i); - outframe.data.u8[0]=(0x20+i); - outframe.data.u8[1]=0x02; - outframe.data.u8[2]=0x02; - outframe.data.u8[3]=(0x60+(i*18)); - outframe.data.u8[4]=0x00; - outframe.data.u8[5]=0x00; - outframe.data.u8[6]=0x00; - outframe.data.u8[7]=0x00; + outframe.data.u8[0] = (0x20 + i); + outframe.data.u8[1] = 0x02; + outframe.data.u8[2] = 0x02; + outframe.data.u8[3] = (0x60 + (i * 18)); + outframe.data.u8[4] = 0x00; + outframe.data.u8[5] = 0x00; + outframe.data.u8[6] = 0x00; + outframe.data.u8[7] = 0x00; transmit_can(&outframe, can_config.battery); delay(500); @@ -266,8 +266,8 @@ void ISA_initialize() { ISA_START(); delay(500); - lastAs=As; - lastWh=wh; + lastAs = As; + lastWh = wh; } void ISA_STOP() { @@ -316,7 +316,7 @@ void ISA_START() { } void ISA_RESTART() { - //Has the effect of zeroing AH and KWH + //Has the effect of zeroing AH and KWH Serial.println("ISA RESTART"); outframe.data.u8[0] = 0x3F; @@ -332,20 +332,20 @@ void ISA_RESTART() { } void ISA_deFAULT() { - //Returns module to original defaults + //Returns module to original defaults ISA_STOP(); delay(500); Serial.println("ISA RESTART to default"); - outframe.data.u8[0]=0x3D; - outframe.data.u8[1]=0x00; - outframe.data.u8[2]=0x00; - outframe.data.u8[3]=0x00; - outframe.data.u8[4]=0x00; - outframe.data.u8[5]=0x00; - outframe.data.u8[6]=0x00; - outframe.data.u8[7]=0x00; + outframe.data.u8[0] = 0x3D; + outframe.data.u8[1] = 0x00; + outframe.data.u8[2] = 0x00; + outframe.data.u8[3] = 0x00; + outframe.data.u8[4] = 0x00; + outframe.data.u8[5] = 0x00; + outframe.data.u8[6] = 0x00; + outframe.data.u8[7] = 0x00; transmit_can(&outframe, can_config.battery); delay(500); @@ -357,17 +357,17 @@ void ISA_deFAULT() { void ISA_initCurrent() { ISA_STOP(); delay(500); - + Serial.println("ISA Initialization Current"); - outframe.data.u8[0]=0x21; - outframe.data.u8[1]=0x02; - outframe.data.u8[2]=0x01; - outframe.data.u8[3]=0x61; - outframe.data.u8[4]=0x00; - outframe.data.u8[5]=0x00; - outframe.data.u8[6]=0x00; - outframe.data.u8[7]=0x00; + outframe.data.u8[0] = 0x21; + outframe.data.u8[1] = 0x02; + outframe.data.u8[2] = 0x01; + outframe.data.u8[3] = 0x61; + outframe.data.u8[4] = 0x00; + outframe.data.u8[5] = 0x00; + outframe.data.u8[6] = 0x00; + outframe.data.u8[7] = 0x00; transmit_can(&outframe, can_config.battery); delay(500); @@ -377,15 +377,15 @@ void ISA_initCurrent() { ISA_START(); delay(500); - lastAs=As; - lastWh=wh; + lastAs = As; + lastWh = wh; } void ISA_getCONFIG(uint8_t i) { Serial.print("ISA Get Config "); Serial.println(i); - - outframe.data.u8[0] = (0x60+i); + + outframe.data.u8[0] = (0x60 + i); outframe.data.u8[1] = 0x00; outframe.data.u8[2] = 0x00; outframe.data.u8[3] = 0x00; @@ -400,10 +400,12 @@ void ISA_getCONFIG(uint8_t i) { void ISA_getCAN_ID(uint8_t i) { Serial.print("ISA Get CAN ID "); Serial.println(i); - - outframe.data.u8[0] = (0x50+i); - if (i == 8) outframe.data.u8[0] = 0x5D; - if (i == 9) outframe.data.u8[0] = 0x5F; + + outframe.data.u8[0] = (0x50 + i); + if (i == 8) + outframe.data.u8[0] = 0x5D; + if (i == 9) + outframe.data.u8[0] = 0x5F; outframe.data.u8[1] = 0x00; outframe.data.u8[2] = 0x00; outframe.data.u8[3] = 0x00; @@ -417,9 +419,9 @@ void ISA_getCAN_ID(uint8_t i) { void ISA_getINFO(uint8_t i) { Serial.print("ISA Get INFO "); - Serial.println(i,HEX); - - outframe.data.u8[0] = (0x70+i); + Serial.println(i, HEX); + + outframe.data.u8[0] = (0x70 + i); outframe.data.u8[1] = 0x00; outframe.data.u8[2] = 0x00; outframe.data.u8[3] = 0x00; @@ -430,4 +432,4 @@ void ISA_getINFO(uint8_t i) { transmit_can(&outframe, can_config.battery); } -#endif \ No newline at end of file +#endif