Merge pull request #365 from dalathegreat/feature/kia-hyundai-hybrid

Feature: New battery, Kia Niro Hybrid
This commit is contained in:
Daniel Öster 2024-07-07 21:28:30 +03:00 committed by GitHub
commit 145a6496e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 306 additions and 0 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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<uint32_t>(
(static_cast<double>(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

View file

@ -0,0 +1,12 @@
#ifndef KIA_HYUNDAI_HYBRID_BATTERY_H
#define KIA_HYUNDAI_HYBRID_BATTERY_H
#include <Arduino.h>
#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

View file

@ -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