diff --git a/README.md b/README.md index 45de1c16..3977368a 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,12 @@ For more examples showing wiring, see each battery types own Wiki page. For inst 1. Download the Arduino IDE: https://www.arduino.cc/en/software 2. When the Arduino IDE has been started; Click "File" in the upper left corner -> Preferences -> Additional Development >Board Manager URL -> Enter the URL in the input box https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json -3. Go to "Boards Manager", and install the ESP32 package by Espressif Systems. **The latest confirmed compatible version is 2.0.11** +3. Go to "Boards Manager", and install the ESP32 package by Espressif Systems. **NOTE: The version depends on which release of Battery-Emulator you are running!** -⚠️ Make sure to use a 2.x.x version - preferably 2.0.11 - and not a 3.x.x version, as it is not yet supported by the libraries we include with the SW! -![image](https://github.com/dalathegreat/Battery-Emulator/assets/81711263/79602ef1-1a23-4670-a638-b896b7f6cdf6) +- ⚠️ Make sure to use a 2.x.x version if you are on a release **older** than 6.0.0 (For instance ESP32 v2.0.11 when using Battery-Emulator v5.4.0) +- ⚠️ Make sure to use a 3.x.x version if you are on a release **newer** than 6.0.0 (For instance ESP32 v3.0.0 when using Battery-Emulator v6.0.0) -For future migration, see this link: https://docs.espressif.com/projects/arduino-esp32/en/latest/migration_guides/2.x_to_3.0.html +![bild](https://github.com/dalathegreat/Battery-Emulator/assets/26695010/6a2414b1-f2ca-4746-8e8d-9afd78bd9252) 4. The arduino settings should be set to "ESP32 Dev Module" with the following settings; ![alt text](https://github.com/Xinyuan-LilyGO/T-CAN485/blob/main/img/arduino_setting.png) diff --git a/Software/Software.ino b/Software/Software.ino index 376b9462..533a61f5 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -33,7 +33,7 @@ Preferences settings; // Store user settings // The current software version, shown on webserver -const char* version_number = "5.11.dev"; +const char* version_number = "6.0.RC"; // Interval settings uint16_t intervalUpdateValues = INTERVAL_5_S; // Interval at which to update inverter values / Modbus registers diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 03ca6a59..a91db051 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -13,6 +13,7 @@ //#define IMIEV_CZERO_ION_BATTERY //#define KIA_HYUNDAI_64_BATTERY //#define KIA_E_GMP_BATTERY +//#define MG_5_BATTERY //#define NISSAN_LEAF_BATTERY //#define PYLON_BATTERY //#define RENAULT_KANGOO_BATTERY diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index be9cd616..d57e7374 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -23,6 +23,10 @@ #include "KIA-HYUNDAI-64-BATTERY.h" #endif +#ifdef MG_5_BATTERY +#include "MG-5-BATTERY.h" +#endif + #ifdef NISSAN_LEAF_BATTERY #include "NISSAN-LEAF-BATTERY.h" #endif diff --git a/Software/src/battery/MG-5-BATTERY.cpp b/Software/src/battery/MG-5-BATTERY.cpp new file mode 100644 index 00000000..766f0e0d --- /dev/null +++ b/Software/src/battery/MG-5-BATTERY.cpp @@ -0,0 +1,150 @@ +#include "../include.h" +#ifdef MG_5_BATTERY_H +#include "../datalayer/datalayer.h" +#include "../devboard/utils/events.h" +#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h" +#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" +#include "MG-5-BATTERY.h" + +/* TODO: +- Get contactor closing working +- Figure out which CAN messages need to be sent towards the battery to keep it alive +- Map all values from battery CAN messages +- Most important ones +*/ + +/* Do not change code below unless you are sure what you are doing */ +static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send +static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send + +static int BMS_SOC = 0; + +CAN_frame_t MG_5_100 = {.FIR = {.B = + { + .DLC = 8, + .FF = CAN_frame_std, + }}, + .MsgID = 0x100, + .data = {0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00}}; + +void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus + + 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.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; + +#ifdef DEBUG_VIA_USB + +#endif +} + +void receive_can_battery(CAN_frame_t rx_frame) { + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + switch (rx_frame.MsgID) { + case 0x171: //Following messages were detected on a MG5 battery BMS + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; // Let system know battery is sending CAN + break; + case 0x172: + break; + case 0x173: + break; + case 0x293: + break; + case 0x295: + break; + case 0x297: + break; + case 0x29B: + break; + case 0x29C: + break; + case 0x2A0: + break; + case 0x2A2: + break; + case 0x322: + break; + case 0x334: + break; + case 0x33F: + break; + case 0x391: + break; + case 0x393: + break; + case 0x3AB: + break; + case 0x3AC: + break; + case 0x3B8: + break; + case 0x3BA: + break; + case 0x3BC: + break; + case 0x3BE: + break; + case 0x3C0: + break; + case 0x3C2: + break; + case 0x400: + break; + case 0x402: + break; + case 0x418: + break; + case 0x44C: + break; + case 0x620: + break; + default: + break; + } +} +void send_can_battery() { + unsigned long currentMillis = millis(); + //Send 10ms message + if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { + // Check if sending of CAN messages has been delayed too much. + if ((currentMillis - previousMillis10 >= INTERVAL_10_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) { + set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis10)); + } + previousMillis10 = currentMillis; + + ESP32Can.CANWriteFrame(&MG_5_100); + } + // Send 100ms CAN Message + if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { + previousMillis100 = currentMillis; + + //ESP32Can.CANWriteFrame(&MG_5_100); + } +} + +void setup_battery(void) { // Performs one time setup at startup +#ifdef DEBUG_VIA_USB + Serial.println("MG 5 battery selected"); +#endif + + datalayer.battery.info.max_design_voltage_dV = 4040; // Over this charging is not possible + datalayer.battery.info.min_design_voltage_dV = 3100; // Under this discharging is disabled +} + +#endif diff --git a/Software/src/battery/MG-5-BATTERY.h b/Software/src/battery/MG-5-BATTERY.h new file mode 100644 index 00000000..dce6dfda --- /dev/null +++ b/Software/src/battery/MG-5-BATTERY.h @@ -0,0 +1,12 @@ +#ifndef MG_5_BATTERY_H +#define MG_5_BATTERY_H +#include +#include "../include.h" +#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" + +#define BATTERY_SELECTED +#define MAX_CELL_DEVIATION_MV 150 + +void setup_battery(void); + +#endif diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 1e9ae6f6..ff19bf2c 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -16,6 +16,7 @@ static unsigned long previousMillis10s = 0; // will store last time a 1s CAN Me static uint8_t mprun10r = 0; //counter 0-20 for 0x1F2 message static uint8_t mprun10 = 0; //counter 0-3 static uint8_t mprun100 = 0; //counter 0-3 +static bool can_bus_alive = false; CAN_frame_t LEAF_1F2 = {.FIR = {.B = { @@ -202,11 +203,6 @@ void update_values_battery() { /* This function maps all the values fetched via datalayer.battery.status.max_charge_power_W = (LB_Charge_Power_Limit * 1000); //kW to W - //Map all cell voltages to the global array - for (int i = 0; i < 96; ++i) { - datalayer.battery.status.cell_voltages_mV[i] = cell_voltages[i]; - } - /*Extra safety functions below*/ if (LB_GIDS < 10) //700Wh left in battery! { //Battery is running abnormally low, some discharge logic might have failed. Zero it all out. @@ -352,6 +348,7 @@ void receive_can_battery(CAN_frame_t rx_frame) { LB_Capacity_Empty = (bool)((rx_frame.data.u8[6] & 0x80) >> 7); break; case 0x5BC: + can_bus_alive = true; datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; // Let system know battery is sending CAN LB_MAX = ((rx_frame.data.u8[5] & 0x10) >> 4); @@ -451,6 +448,13 @@ void receive_can_battery(CAN_frame_t rx_frame) { } if (rx_frame.data.u8[6] == 0xFF && rx_frame.data.u8[0] == 0x2C) { //Last frame //Last frame does not contain any cell data, calculate the result + + //Map all cell voltages to the global array + for (int i = 0; i < 96; ++i) { + datalayer.battery.status.cell_voltages_mV[i] = cell_voltages[i]; + } + + //calculate min/max voltages min_max_voltage[0] = 9999; min_max_voltage[1] = 0; for (cellcounter = 0; cellcounter < 96; cellcounter++) { @@ -548,189 +552,192 @@ void receive_can_battery(CAN_frame_t rx_frame) { } } void send_can_battery() { - unsigned long currentMillis = millis(); + if (can_bus_alive) { - //Send 10ms message - if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { - // Check if sending of CAN messages has been delayed too much. - if ((currentMillis - previousMillis10 >= INTERVAL_10_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) { - set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis10)); - } - previousMillis10 = currentMillis; + unsigned long currentMillis = millis(); - switch (mprun10) { - case 0: - LEAF_1D4.data.u8[4] = 0x07; - LEAF_1D4.data.u8[7] = 0x12; - break; - case 1: - LEAF_1D4.data.u8[4] = 0x47; - LEAF_1D4.data.u8[7] = 0xD5; - break; - case 2: - LEAF_1D4.data.u8[4] = 0x87; - LEAF_1D4.data.u8[7] = 0x19; - break; - case 3: - LEAF_1D4.data.u8[4] = 0xC7; - LEAF_1D4.data.u8[7] = 0xDE; - break; - } - ESP32Can.CANWriteFrame(&LEAF_1D4); + //Send 10ms message + if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { + // Check if sending of CAN messages has been delayed too much. + if ((currentMillis - previousMillis10 >= INTERVAL_10_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) { + set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis10)); + } + previousMillis10 = currentMillis; - switch (mprun10r) { - case (0): - LEAF_1F2.data.u8[3] = 0xB0; - LEAF_1F2.data.u8[6] = 0x00; - LEAF_1F2.data.u8[7] = 0x8F; - break; - case (1): - LEAF_1F2.data.u8[6] = 0x01; - LEAF_1F2.data.u8[7] = 0x80; - break; - case (2): - LEAF_1F2.data.u8[6] = 0x02; - LEAF_1F2.data.u8[7] = 0x81; - break; - case (3): - LEAF_1F2.data.u8[6] = 0x03; - LEAF_1F2.data.u8[7] = 0x82; - break; - case (4): - LEAF_1F2.data.u8[6] = 0x00; - LEAF_1F2.data.u8[7] = 0x8F; - break; - case (5): // Set 2 - LEAF_1F2.data.u8[3] = 0xB4; - LEAF_1F2.data.u8[6] = 0x01; - LEAF_1F2.data.u8[7] = 0x84; - break; - case (6): - LEAF_1F2.data.u8[6] = 0x02; - LEAF_1F2.data.u8[7] = 0x85; - break; - case (7): - LEAF_1F2.data.u8[6] = 0x03; - LEAF_1F2.data.u8[7] = 0x86; - break; - case (8): - LEAF_1F2.data.u8[6] = 0x00; - LEAF_1F2.data.u8[7] = 0x83; - break; - case (9): - LEAF_1F2.data.u8[6] = 0x01; - LEAF_1F2.data.u8[7] = 0x84; - break; - case (10): // Set 3 - LEAF_1F2.data.u8[3] = 0xB0; - LEAF_1F2.data.u8[6] = 0x02; - LEAF_1F2.data.u8[7] = 0x81; - break; - case (11): - LEAF_1F2.data.u8[6] = 0x03; - LEAF_1F2.data.u8[7] = 0x82; - break; - case (12): - LEAF_1F2.data.u8[6] = 0x00; - LEAF_1F2.data.u8[7] = 0x8F; - break; - case (13): - LEAF_1F2.data.u8[6] = 0x01; - LEAF_1F2.data.u8[7] = 0x80; - break; - case (14): - LEAF_1F2.data.u8[6] = 0x02; - LEAF_1F2.data.u8[7] = 0x81; - break; - case (15): // Set 4 - LEAF_1F2.data.u8[3] = 0xB4; - LEAF_1F2.data.u8[6] = 0x03; - LEAF_1F2.data.u8[7] = 0x86; - break; - case (16): - LEAF_1F2.data.u8[6] = 0x00; - LEAF_1F2.data.u8[7] = 0x83; - break; - case (17): - LEAF_1F2.data.u8[6] = 0x01; - LEAF_1F2.data.u8[7] = 0x84; - break; - case (18): - LEAF_1F2.data.u8[6] = 0x02; - LEAF_1F2.data.u8[7] = 0x85; - break; - case (19): - LEAF_1F2.data.u8[6] = 0x03; - LEAF_1F2.data.u8[7] = 0x86; - break; - default: - break; - } + switch (mprun10) { + case 0: + LEAF_1D4.data.u8[4] = 0x07; + LEAF_1D4.data.u8[7] = 0x12; + break; + case 1: + LEAF_1D4.data.u8[4] = 0x47; + LEAF_1D4.data.u8[7] = 0xD5; + break; + case 2: + LEAF_1D4.data.u8[4] = 0x87; + LEAF_1D4.data.u8[7] = 0x19; + break; + case 3: + LEAF_1D4.data.u8[4] = 0xC7; + LEAF_1D4.data.u8[7] = 0xDE; + break; + } + ESP32Can.CANWriteFrame(&LEAF_1D4); + + switch (mprun10r) { + case (0): + LEAF_1F2.data.u8[3] = 0xB0; + LEAF_1F2.data.u8[6] = 0x00; + LEAF_1F2.data.u8[7] = 0x8F; + break; + case (1): + LEAF_1F2.data.u8[6] = 0x01; + LEAF_1F2.data.u8[7] = 0x80; + break; + case (2): + LEAF_1F2.data.u8[6] = 0x02; + LEAF_1F2.data.u8[7] = 0x81; + break; + case (3): + LEAF_1F2.data.u8[6] = 0x03; + LEAF_1F2.data.u8[7] = 0x82; + break; + case (4): + LEAF_1F2.data.u8[6] = 0x00; + LEAF_1F2.data.u8[7] = 0x8F; + break; + case (5): // Set 2 + LEAF_1F2.data.u8[3] = 0xB4; + LEAF_1F2.data.u8[6] = 0x01; + LEAF_1F2.data.u8[7] = 0x84; + break; + case (6): + LEAF_1F2.data.u8[6] = 0x02; + LEAF_1F2.data.u8[7] = 0x85; + break; + case (7): + LEAF_1F2.data.u8[6] = 0x03; + LEAF_1F2.data.u8[7] = 0x86; + break; + case (8): + LEAF_1F2.data.u8[6] = 0x00; + LEAF_1F2.data.u8[7] = 0x83; + break; + case (9): + LEAF_1F2.data.u8[6] = 0x01; + LEAF_1F2.data.u8[7] = 0x84; + break; + case (10): // Set 3 + LEAF_1F2.data.u8[3] = 0xB0; + LEAF_1F2.data.u8[6] = 0x02; + LEAF_1F2.data.u8[7] = 0x81; + break; + case (11): + LEAF_1F2.data.u8[6] = 0x03; + LEAF_1F2.data.u8[7] = 0x82; + break; + case (12): + LEAF_1F2.data.u8[6] = 0x00; + LEAF_1F2.data.u8[7] = 0x8F; + break; + case (13): + LEAF_1F2.data.u8[6] = 0x01; + LEAF_1F2.data.u8[7] = 0x80; + break; + case (14): + LEAF_1F2.data.u8[6] = 0x02; + LEAF_1F2.data.u8[7] = 0x81; + break; + case (15): // Set 4 + LEAF_1F2.data.u8[3] = 0xB4; + LEAF_1F2.data.u8[6] = 0x03; + LEAF_1F2.data.u8[7] = 0x86; + break; + case (16): + LEAF_1F2.data.u8[6] = 0x00; + LEAF_1F2.data.u8[7] = 0x83; + break; + case (17): + LEAF_1F2.data.u8[6] = 0x01; + LEAF_1F2.data.u8[7] = 0x84; + break; + case (18): + LEAF_1F2.data.u8[6] = 0x02; + LEAF_1F2.data.u8[7] = 0x85; + break; + case (19): + LEAF_1F2.data.u8[6] = 0x03; + LEAF_1F2.data.u8[7] = 0x86; + break; + default: + break; + } //Only send this message when NISSANLEAF_CHARGER is not defined (otherwise it will collide!) #ifndef NISSANLEAF_CHARGER - ESP32Can.CANWriteFrame(&LEAF_1F2); //Contains (CHG_STA_RQ == 1 == Normal Charge) + ESP32Can.CANWriteFrame(&LEAF_1F2); //Contains (CHG_STA_RQ == 1 == Normal Charge) #endif - mprun10r = (mprun10r + 1) % 20; // 0x1F2 patter repeats after 20 messages. 0-1..19-0 + mprun10r = (mprun10r + 1) % 20; // 0x1F2 patter repeats after 20 messages. 0-1..19-0 - mprun10 = (mprun10 + 1) % 4; // mprun10 cycles between 0-1-2-3-0-1... - } - - // Send 100ms CAN Message - if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { - previousMillis100 = currentMillis; - - //When battery requests heating pack status change, ack this - if (Batt_Heater_Mail_Send_Request) { - LEAF_50B.data.u8[6] = 0x20; //Batt_Heater_Mail_Send_OK - } else { - LEAF_50B.data.u8[6] = 0x00; //Batt_Heater_Mail_Send_NG + mprun10 = (mprun10 + 1) % 4; // mprun10 cycles between 0-1-2-3-0-1... } - // VCM message, containing info if battery should sleep or stay awake - ESP32Can.CANWriteFrame(&LEAF_50B); // HCM_WakeUpSleepCommand == 11b == WakeUp, and CANMASK = 1 + // Send 100ms CAN Message + if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { + previousMillis100 = currentMillis; - LEAF_50C.data.u8[3] = mprun100; - switch (mprun100) { - case 0: - LEAF_50C.data.u8[4] = 0x5D; - LEAF_50C.data.u8[5] = 0xC8; - break; - case 1: - LEAF_50C.data.u8[4] = 0xB2; - LEAF_50C.data.u8[5] = 0x31; - break; - case 2: - LEAF_50C.data.u8[4] = 0x5D; - LEAF_50C.data.u8[5] = 0x63; - break; - case 3: - LEAF_50C.data.u8[4] = 0xB2; - LEAF_50C.data.u8[5] = 0x9A; - break; - } - ESP32Can.CANWriteFrame(&LEAF_50C); + //When battery requests heating pack status change, ack this + if (Batt_Heater_Mail_Send_Request) { + LEAF_50B.data.u8[6] = 0x20; //Batt_Heater_Mail_Send_OK + } else { + LEAF_50B.data.u8[6] = 0x00; //Batt_Heater_Mail_Send_NG + } - mprun100 = (mprun100 + 1) % 4; // mprun100 cycles between 0-1-2-3-0-1... - } + // VCM message, containing info if battery should sleep or stay awake + ESP32Can.CANWriteFrame(&LEAF_50B); // HCM_WakeUpSleepCommand == 11b == WakeUp, and CANMASK = 1 - //Send 10s CAN messages - if (currentMillis - previousMillis10s >= INTERVAL_10_S) { - previousMillis10s = currentMillis; + LEAF_50C.data.u8[3] = mprun100; + switch (mprun100) { + case 0: + LEAF_50C.data.u8[4] = 0x5D; + LEAF_50C.data.u8[5] = 0xC8; + break; + case 1: + LEAF_50C.data.u8[4] = 0xB2; + LEAF_50C.data.u8[5] = 0x31; + break; + case 2: + LEAF_50C.data.u8[4] = 0x5D; + LEAF_50C.data.u8[5] = 0x63; + break; + case 3: + LEAF_50C.data.u8[4] = 0xB2; + LEAF_50C.data.u8[5] = 0x9A; + break; + } + ESP32Can.CANWriteFrame(&LEAF_50C); - //Every 10s, ask diagnostic data from the battery. Don't ask if someone is already polling on the bus (Leafspy?) - if (!stop_battery_query) { - group = (group == 1) ? 2 : (group == 2) ? 4 : 1; - // Cycle between group 1, 2, and 4 using ternary operation - LEAF_GROUP_REQUEST.data.u8[2] = group; - ESP32Can.CANWriteFrame(&LEAF_GROUP_REQUEST); + mprun100 = (mprun100 + 1) % 4; // mprun100 cycles between 0-1-2-3-0-1... } - if (hold_off_with_polling_10seconds > 0) { - hold_off_with_polling_10seconds--; - } else { - stop_battery_query = false; + //Send 10s CAN messages + if (currentMillis - previousMillis10s >= INTERVAL_10_S) { + previousMillis10s = currentMillis; + + //Every 10s, ask diagnostic data from the battery. Don't ask if someone is already polling on the bus (Leafspy?) + if (!stop_battery_query) { + group = (group == 1) ? 2 : (group == 2) ? 4 : 1; + // Cycle between group 1, 2, and 4 using ternary operation + LEAF_GROUP_REQUEST.data.u8[2] = group; + ESP32Can.CANWriteFrame(&LEAF_GROUP_REQUEST); + } + + if (hold_off_with_polling_10seconds > 0) { + hold_off_with_polling_10seconds--; + } else { + stop_battery_query = false; + } } } } diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index 06721579..88b91d6b 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -75,7 +75,8 @@ static void publish_cell_voltages(void) { doc.clear(); // clear after sending autoconfig } else { // If cell voltages haven't been populated... - if (datalayer.battery.info.number_of_cells == 0u) { + if (datalayer.battery.info.number_of_cells == 0u || + datalayer.battery.status.cell_voltages_mV[datalayer.battery.info.number_of_cells - 1] == 0u) { return; } @@ -158,10 +159,13 @@ static void publish_common_info(void) { doc["temperature_max"] = ((float)((int16_t)datalayer.battery.status.temperature_max_dC)) / 10.0; doc["stat_batt_power"] = ((float)((int32_t)datalayer.battery.status.active_power_W)); doc["battery_current"] = ((float)((int16_t)datalayer.battery.status.current_dA)) / 10.0; - doc["cell_max_voltage"] = ((float)datalayer.battery.status.cell_max_voltage_mV) / 1000.0; - doc["cell_min_voltage"] = ((float)datalayer.battery.status.cell_min_voltage_mV) / 1000.0; doc["battery_voltage"] = ((float)datalayer.battery.status.voltage_dV) / 10.0; - + // publish only 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) { + doc["cell_max_voltage"] = ((float)datalayer.battery.status.cell_max_voltage_mV) / 1000.0; + doc["cell_min_voltage"] = ((float)datalayer.battery.status.cell_min_voltage_mV) / 1000.0; + } serializeJson(doc, mqtt_msg); if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) { #ifdef DEBUG_VIA_USB diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index c37bb7da..0603adf8 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -159,6 +159,7 @@ void init_events(void) { events.entries[EVENT_PRECHARGE_FAILURE].level = EVENT_LEVEL_INFO; events.entries[EVENT_INTERNAL_OPEN_FAULT].level = EVENT_LEVEL_ERROR; events.entries[EVENT_INVERTER_OPEN_CONTACTOR].level = EVENT_LEVEL_INFO; + events.entries[EVENT_MODBUS_INVERTER_MISSING].level = EVENT_LEVEL_INFO; events.entries[EVENT_ERROR_OPEN_CONTACTOR].level = EVENT_LEVEL_INFO; events.entries[EVENT_CELL_UNDER_VOLTAGE].level = EVENT_LEVEL_ERROR; events.entries[EVENT_CELL_OVER_VOLTAGE].level = EVENT_LEVEL_ERROR; @@ -265,6 +266,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) { case EVENT_ERROR_OPEN_CONTACTOR: return "Info: Too much time spent in error state. Opening contactors, not safe to continue charging. " "Check other error code for reason!"; + case EVENT_MODBUS_INVERTER_MISSING: + return "Info: Modbus inverter has not sent any data. Inspect communication wiring!"; case EVENT_CELL_UNDER_VOLTAGE: return "ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!"; case EVENT_CELL_OVER_VOLTAGE: diff --git a/Software/src/devboard/utils/events.h b/Software/src/devboard/utils/events.h index bb085a2c..110fdcb8 100644 --- a/Software/src/devboard/utils/events.h +++ b/Software/src/devboard/utils/events.h @@ -55,6 +55,7 @@ XX(EVENT_PRECHARGE_FAILURE) \ XX(EVENT_INTERNAL_OPEN_FAULT) \ XX(EVENT_INVERTER_OPEN_CONTACTOR) \ + XX(EVENT_MODBUS_INVERTER_MISSING) \ XX(EVENT_ERROR_OPEN_CONTACTOR) \ XX(EVENT_CELL_UNDER_VOLTAGE) \ XX(EVENT_CELL_OVER_VOLTAGE) \ diff --git a/Software/src/devboard/webserver/cellmonitor_html.cpp b/Software/src/devboard/webserver/cellmonitor_html.cpp index 50131c2c..cb514bb0 100644 --- a/Software/src/devboard/webserver/cellmonitor_html.cpp +++ b/Software/src/devboard/webserver/cellmonitor_html.cpp @@ -3,7 +3,7 @@ #include "../../datalayer/datalayer.h" String cellmonitor_processor(const String& var) { - if (var == "ABC") { + if (var == "X") { String content = ""; // Page format content += "