diff --git a/.github/workflows/compile-all-batteries.yml b/.github/workflows/compile-all-batteries.yml index 1c163126..e89f3443 100644 --- a/.github/workflows/compile-all-batteries.yml +++ b/.github/workflows/compile-all-batteries.yml @@ -38,6 +38,7 @@ jobs: - CHADEMO_BATTERY - IMIEV_CZERO_ION_BATTERY - KIA_HYUNDAI_64_BATTERY + - KIA_HYUNDAI_HYBRID_BATTERY - NISSAN_LEAF_BATTERY - PYLON_BATTERY - RENAULT_KANGOO_BATTERY diff --git a/.github/workflows/compile-all-combinations.yml b/.github/workflows/compile-all-combinations.yml index 356f35cb..057813fe 100644 --- a/.github/workflows/compile-all-combinations.yml +++ b/.github/workflows/compile-all-combinations.yml @@ -41,6 +41,7 @@ jobs: - CHADEMO_BATTERY - IMIEV_CZERO_ION_BATTERY - KIA_HYUNDAI_64_BATTERY + - KIA_HYUNDAI_HYBRID_BATTERY - NISSAN_LEAF_BATTERY - PYLON_BATTERY - RENAULT_KANGOO_BATTERY diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index faea3632..c513ef96 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -14,6 +14,7 @@ //#define IMIEV_CZERO_ION_BATTERY //#define KIA_HYUNDAI_64_BATTERY //#define KIA_E_GMP_BATTERY +//#define KIA_HYUNDAI_HYBRID_BATTERY //#define MG_5_BATTERY //#define NISSAN_LEAF_BATTERY //#define PYLON_BATTERY diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index daf47608..1a008aff 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -27,6 +27,10 @@ #include "KIA-HYUNDAI-64-BATTERY.h" #endif +#ifdef KIA_HYUNDAI_HYBRID_BATTERY +#include "KIA-HYUNDAI-HYBRID-BATTERY.h" +#endif + #ifdef MG_5_BATTERY #include "MG-5-BATTERY.h" #endif diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp new file mode 100644 index 00000000..ec3cfe27 --- /dev/null +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp @@ -0,0 +1,284 @@ +#include "../include.h" +#ifdef KIA_HYUNDAI_HYBRID_BATTERY +#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 "KIA-HYUNDAI-HYBRID-BATTERY.h" + +/* TODO: +- The HEV battery seems to turn off after 1 minute of use. When this happens SOC% stops updating. +- We need to figure out how to keep the BMS alive. Most likely we need to send a specific CAN message +*/ + +/* Do not change code below unless you are sure what you are doing */ +static unsigned long previousMillis1000 = 0; // will store last time a 100ms CAN Message was send + +static uint16_t SOC = 0; +static uint16_t SOC_display = 0; +static bool interlock_missing = false; +static int16_t battery_current = 0; +static uint8_t battery_current_high_byte = 0; +static uint16_t battery_voltage = 0; +static uint32_t available_charge_power = 0; +static uint32_t available_discharge_power = 0; +static int8_t battery_module_max_temperature = 0; +static int8_t battery_module_min_temperature = 0; +static uint8_t poll_data_pid = 0; +static uint16_t cellvoltages_mv[98]; +static uint16_t min_cell_voltage_mv = 3700; +static uint16_t max_cell_voltage_mv = 3700; + +CAN_frame_t KIA_7E4_id1 = {.FIR = {.B = + { + .DLC = 8, + .FF = CAN_frame_std, + }}, + .MsgID = 0x7E4, + .data = {0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame_t KIA_7E4_id2 = {.FIR = {.B = + { + .DLC = 8, + .FF = CAN_frame_std, + }}, + .MsgID = 0x7E4, + .data = {0x02, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame_t KIA_7E4_id3 = {.FIR = {.B = + { + .DLC = 8, + .FF = CAN_frame_std, + }}, + .MsgID = 0x7E4, + .data = {0x02, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame_t KIA_7E4_id5 = {.FIR = {.B = + { + .DLC = 8, + .FF = CAN_frame_std, + }}, + .MsgID = 0x7E4, + .data = {0x02, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame_t KIA_7E4_ack = {.FIR = {.B = + { + .DLC = 8, + .FF = CAN_frame_std, + }}, + .MsgID = 0x7E4, //Ack frame, correct PID is returned. Flow control message + .data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 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 = SOC * 50; + + datalayer.battery.status.voltage_dV = battery_voltage; + + datalayer.battery.status.current_dA = battery_current; + + datalayer.battery.status.remaining_capacity_Wh = static_cast( + (static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh); + + datalayer.battery.status.max_discharge_power_W = available_discharge_power * 10; + + 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); + + datalayer.battery.status.cell_max_voltage_mV = max_cell_voltage_mv; + + datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage_mv; + + //Map all cell voltages to the global array + memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_mv, 98 * sizeof(uint16_t)); + + if (interlock_missing) { + set_event(EVENT_HVIL_FAILURE, 0); + } else { + clear_event(EVENT_HVIL_FAILURE); + } +} + +void receive_can_battery(CAN_frame_t rx_frame) { + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + switch (rx_frame.MsgID) { + case 0x5F1: + break; + case 0x51E: + break; + case 0x588: + break; + case 0x5AE: + interlock_missing = (bool)(rx_frame.data.u8[1] & 0x02) >> 1; + break; + case 0x5AF: + break; + case 0x5AD: + break; + case 0x670: + break; + case 0x7EC: //Data From polled PID group, BigEndian + switch (rx_frame.data.u8[0]) { + case 0x10: //"PID Header" + if (rx_frame.data.u8[3] == poll_data_pid) { + ESP32Can.CANWriteFrame(&KIA_7E4_ack); //Send ack to BMS if the same frame is sent as polled + } + break; + case 0x21: //First frame in PID group + if (poll_data_pid == 1) { // 21 01 + SOC = rx_frame.data.u8[1]; // 0xBC = 188 /2 = 94% + available_charge_power = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); + available_discharge_power = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + battery_current_high_byte = rx_frame.data.u8[7]; + } else if (poll_data_pid == 2) { //21 02 + cellvoltages_mv[0] = (rx_frame.data.u8[2] * 20); + cellvoltages_mv[1] = (rx_frame.data.u8[3] * 20); + cellvoltages_mv[2] = (rx_frame.data.u8[4] * 20); + cellvoltages_mv[3] = (rx_frame.data.u8[5] * 20); + cellvoltages_mv[4] = (rx_frame.data.u8[6] * 20); + cellvoltages_mv[5] = (rx_frame.data.u8[7] * 20); + } else if (poll_data_pid == 3) { //21 03 + cellvoltages_mv[31] = (rx_frame.data.u8[2] * 20); + cellvoltages_mv[32] = (rx_frame.data.u8[3] * 20); + cellvoltages_mv[33] = (rx_frame.data.u8[4] * 20); + cellvoltages_mv[34] = (rx_frame.data.u8[5] * 20); + cellvoltages_mv[35] = (rx_frame.data.u8[6] * 20); + cellvoltages_mv[36] = (rx_frame.data.u8[7] * 20); + } else if (poll_data_pid == 5) { //21 05 + } + break; + case 0x22: //Second datarow in PID group + if (poll_data_pid == 1) { + battery_current = ((battery_current_high_byte << 8) | rx_frame.data.u8[1]); + battery_voltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); + battery_module_max_temperature = rx_frame.data.u8[4]; + battery_module_min_temperature = rx_frame.data.u8[5]; + } else if (poll_data_pid == 2) { + cellvoltages_mv[6] = (rx_frame.data.u8[1] * 20); + cellvoltages_mv[7] = (rx_frame.data.u8[2] * 20); + cellvoltages_mv[8] = (rx_frame.data.u8[3] * 20); + cellvoltages_mv[9] = (rx_frame.data.u8[4] * 20); + cellvoltages_mv[10] = (rx_frame.data.u8[5] * 20); + cellvoltages_mv[11] = (rx_frame.data.u8[6] * 20); + cellvoltages_mv[12] = (rx_frame.data.u8[7] * 20); + + } else if (poll_data_pid == 3) { + cellvoltages_mv[37] = (rx_frame.data.u8[2] * 20); + cellvoltages_mv[38] = (rx_frame.data.u8[3] * 20); + cellvoltages_mv[39] = (rx_frame.data.u8[4] * 20); + cellvoltages_mv[40] = (rx_frame.data.u8[5] * 20); + cellvoltages_mv[41] = (rx_frame.data.u8[6] * 20); + cellvoltages_mv[42] = (rx_frame.data.u8[7] * 20); + } else if (poll_data_pid == 5) { + } + break; + case 0x23: //Third datarow in PID group + if (poll_data_pid == 1) { + max_cell_voltage_mv = rx_frame.data.u8[6] * 20; + } else if (poll_data_pid == 2) { + cellvoltages_mv[13] = (rx_frame.data.u8[1] * 20); + cellvoltages_mv[14] = (rx_frame.data.u8[2] * 20); + cellvoltages_mv[15] = (rx_frame.data.u8[3] * 20); + cellvoltages_mv[16] = (rx_frame.data.u8[4] * 20); + cellvoltages_mv[17] = (rx_frame.data.u8[5] * 20); + cellvoltages_mv[18] = (rx_frame.data.u8[6] * 20); + cellvoltages_mv[19] = (rx_frame.data.u8[7] * 20); + } else if (poll_data_pid == 3) { + cellvoltages_mv[43] = (rx_frame.data.u8[2] * 20); + cellvoltages_mv[44] = (rx_frame.data.u8[3] * 20); + cellvoltages_mv[45] = (rx_frame.data.u8[4] * 20); + cellvoltages_mv[46] = (rx_frame.data.u8[5] * 20); + cellvoltages_mv[47] = (rx_frame.data.u8[6] * 20); + cellvoltages_mv[48] = (rx_frame.data.u8[7] * 20); + } else if (poll_data_pid == 5) { + } + break; + case 0x24: //Fourth datarow in PID group + if (poll_data_pid == 1) { + min_cell_voltage_mv = rx_frame.data.u8[1] * 20; + } else if (poll_data_pid == 2) { + cellvoltages_mv[20] = (rx_frame.data.u8[1] * 20); + cellvoltages_mv[21] = (rx_frame.data.u8[2] * 20); + cellvoltages_mv[22] = (rx_frame.data.u8[3] * 20); + cellvoltages_mv[23] = (rx_frame.data.u8[4] * 20); + cellvoltages_mv[24] = (rx_frame.data.u8[5] * 20); + cellvoltages_mv[25] = (rx_frame.data.u8[6] * 20); + cellvoltages_mv[26] = (rx_frame.data.u8[7] * 20); + } else if (poll_data_pid == 3) { + cellvoltages_mv[49] = (rx_frame.data.u8[2] * 20); + cellvoltages_mv[50] = (rx_frame.data.u8[3] * 20); + cellvoltages_mv[51] = (rx_frame.data.u8[4] * 20); + cellvoltages_mv[52] = (rx_frame.data.u8[5] * 20); + cellvoltages_mv[53] = (rx_frame.data.u8[6] * 20); + cellvoltages_mv[54] = (rx_frame.data.u8[7] * 20); + } else if (poll_data_pid == 5) { + SOC_display = rx_frame.data.u8[7]; //0x26 = 38% + } + break; + case 0x25: //Fifth datarow in PID group + if (poll_data_pid == 1) { + + } else if (poll_data_pid == 2) { + cellvoltages_mv[27] = (rx_frame.data.u8[1] * 20); + cellvoltages_mv[28] = (rx_frame.data.u8[2] * 20); + cellvoltages_mv[29] = (rx_frame.data.u8[3] * 20); + cellvoltages_mv[30] = (rx_frame.data.u8[4] * 20); + } else if (poll_data_pid == 3) { + cellvoltages_mv[55] = (rx_frame.data.u8[4] * 20); + cellvoltages_mv[56] = (rx_frame.data.u8[5] * 20); + cellvoltages_mv[57] = (rx_frame.data.u8[6] * 20); + cellvoltages_mv[58] = (rx_frame.data.u8[7] * 20); + } else if (poll_data_pid == 5) { + } + break; + case 0x26: //Sixth datarow in PID group + break; + case 0x27: //Seventh datarow in PID group + break; + case 0x28: //Eighth datarow in PID group + break; + } + break; + default: + break; + } +} +void send_can_battery() { + unsigned long currentMillis = millis(); + + // Send 1000ms CAN Message + if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { + previousMillis1000 = currentMillis; + + //PID data is polled after last message sent from battery: + if (poll_data_pid >= 5) { //polling one of 5 PIDs at 100ms, resolution = 500ms + poll_data_pid = 0; + } + poll_data_pid++; + if (poll_data_pid == 1) { + ESP32Can.CANWriteFrame(&KIA_7E4_id1); + } else if (poll_data_pid == 2) { + ESP32Can.CANWriteFrame(&KIA_7E4_id2); + } else if (poll_data_pid == 3) { + ESP32Can.CANWriteFrame(&KIA_7E4_id3); + } else if (poll_data_pid == 4) { + + } else if (poll_data_pid == 5) { + ESP32Can.CANWriteFrame(&KIA_7E4_id5); + } + } +} + +void setup_battery(void) { // Performs one time setup at startup +#ifdef DEBUG_VIA_USB + Serial.println("Kia/Hyundai Hybrid battery selected"); +#endif + datalayer.battery.info.number_of_cells = 56; // HEV , TODO: Make dynamic according to HEV/PHEV + datalayer.battery.info.max_design_voltage_dV = 2550; //TODO: Values OK? + datalayer.battery.info.min_design_voltage_dV = 1700; +} + +#endif diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h new file mode 100644 index 00000000..bc23d89a --- /dev/null +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h @@ -0,0 +1,12 @@ +#ifndef KIA_HYUNDAI_HYBRID_BATTERY_H +#define KIA_HYUNDAI_HYBRID_BATTERY_H +#include +#include "../include.h" +#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" + +#define BATTERY_SELECTED +#define MAX_CELL_DEVIATION_MV 100 + +void setup_battery(void); + +#endif diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index a8db9987..e55c4494 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -465,6 +465,9 @@ String processor(const String& var) { #ifdef KIA_E_GMP_BATTERY content += "Kia/Hyundai EGMP platform"; #endif +#ifdef KIA_HYUNDAI_HYBRID_BATTERY + content += "Kia/Hyundai Hybrid"; +#endif #ifdef MG_5_BATTERY content += "MG 5"; #endif