mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 09:49:32 +02:00
Merge pull request #365 from dalathegreat/feature/kia-hyundai-hybrid
Feature: New battery, Kia Niro Hybrid
This commit is contained in:
commit
145a6496e5
7 changed files with 306 additions and 0 deletions
1
.github/workflows/compile-all-batteries.yml
vendored
1
.github/workflows/compile-all-batteries.yml
vendored
|
@ -38,6 +38,7 @@ jobs:
|
||||||
- CHADEMO_BATTERY
|
- CHADEMO_BATTERY
|
||||||
- IMIEV_CZERO_ION_BATTERY
|
- IMIEV_CZERO_ION_BATTERY
|
||||||
- KIA_HYUNDAI_64_BATTERY
|
- KIA_HYUNDAI_64_BATTERY
|
||||||
|
- KIA_HYUNDAI_HYBRID_BATTERY
|
||||||
- NISSAN_LEAF_BATTERY
|
- NISSAN_LEAF_BATTERY
|
||||||
- PYLON_BATTERY
|
- PYLON_BATTERY
|
||||||
- RENAULT_KANGOO_BATTERY
|
- RENAULT_KANGOO_BATTERY
|
||||||
|
|
|
@ -41,6 +41,7 @@ jobs:
|
||||||
- CHADEMO_BATTERY
|
- CHADEMO_BATTERY
|
||||||
- IMIEV_CZERO_ION_BATTERY
|
- IMIEV_CZERO_ION_BATTERY
|
||||||
- KIA_HYUNDAI_64_BATTERY
|
- KIA_HYUNDAI_64_BATTERY
|
||||||
|
- KIA_HYUNDAI_HYBRID_BATTERY
|
||||||
- NISSAN_LEAF_BATTERY
|
- NISSAN_LEAF_BATTERY
|
||||||
- PYLON_BATTERY
|
- PYLON_BATTERY
|
||||||
- RENAULT_KANGOO_BATTERY
|
- RENAULT_KANGOO_BATTERY
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
//#define IMIEV_CZERO_ION_BATTERY
|
//#define IMIEV_CZERO_ION_BATTERY
|
||||||
//#define KIA_HYUNDAI_64_BATTERY
|
//#define KIA_HYUNDAI_64_BATTERY
|
||||||
//#define KIA_E_GMP_BATTERY
|
//#define KIA_E_GMP_BATTERY
|
||||||
|
//#define KIA_HYUNDAI_HYBRID_BATTERY
|
||||||
//#define MG_5_BATTERY
|
//#define MG_5_BATTERY
|
||||||
//#define NISSAN_LEAF_BATTERY
|
//#define NISSAN_LEAF_BATTERY
|
||||||
//#define PYLON_BATTERY
|
//#define PYLON_BATTERY
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
#include "KIA-HYUNDAI-64-BATTERY.h"
|
#include "KIA-HYUNDAI-64-BATTERY.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef KIA_HYUNDAI_HYBRID_BATTERY
|
||||||
|
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef MG_5_BATTERY
|
#ifdef MG_5_BATTERY
|
||||||
#include "MG-5-BATTERY.h"
|
#include "MG-5-BATTERY.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
284
Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
Normal file
284
Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
Normal 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
|
12
Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h
Normal file
12
Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h
Normal 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
|
|
@ -465,6 +465,9 @@ String processor(const String& var) {
|
||||||
#ifdef KIA_E_GMP_BATTERY
|
#ifdef KIA_E_GMP_BATTERY
|
||||||
content += "Kia/Hyundai EGMP platform";
|
content += "Kia/Hyundai EGMP platform";
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef KIA_HYUNDAI_HYBRID_BATTERY
|
||||||
|
content += "Kia/Hyundai Hybrid";
|
||||||
|
#endif
|
||||||
#ifdef MG_5_BATTERY
|
#ifdef MG_5_BATTERY
|
||||||
content += "MG 5";
|
content += "MG 5";
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue