Merge branch 'main' into bugfix/pylon-battery-soh

This commit is contained in:
Daniel Öster 2025-05-21 16:40:01 +03:00
commit c81c176c60
31 changed files with 2187 additions and 2016 deletions

View file

@ -273,7 +273,7 @@ void core_loop(void*) {
transmit_can(currentMillis); // Send CAN messages to all components transmit_can(currentMillis); // Send CAN messages to all components
#ifdef RS485_BATTERY_SELECTED #ifdef RS485_BATTERY_SELECTED
transmit_rs485(); transmit_rs485(currentMillis);
#endif // RS485_BATTERY_SELECTED #endif // RS485_BATTERY_SELECTED
#ifdef FUNCTION_TIME_MEASUREMENT #ifdef FUNCTION_TIME_MEASUREMENT
END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us); END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us);
@ -331,9 +331,9 @@ void check_interconnect_available() {
clear_event(EVENT_VOLTAGE_DIFFERENCE); clear_event(EVENT_VOLTAGE_DIFFERENCE);
if (datalayer.battery.status.bms_status == FAULT) { if (datalayer.battery.status.bms_status == FAULT) {
// If main battery is in fault state, disengage the second battery // If main battery is in fault state, disengage the second battery
datalayer.system.status.battery2_allows_contactor_closing = false; datalayer.system.status.battery2_allowed_contactor_closing = false;
} else { // If main battery is OK, allow second battery to join } else { // If main battery is OK, allow second battery to join
datalayer.system.status.battery2_allows_contactor_closing = true; datalayer.system.status.battery2_allowed_contactor_closing = true;
} }
} else { //Voltage between the two packs is too large } else { //Voltage between the two packs is too large
set_event(EVENT_VOLTAGE_DIFFERENCE, (uint8_t)(voltage_diff / 10)); set_event(EVENT_VOLTAGE_DIFFERENCE, (uint8_t)(voltage_diff / 10));

View file

@ -17,6 +17,7 @@
//#define FOXESS_BATTERY //#define FOXESS_BATTERY
//#define CELLPOWER_BMS //#define CELLPOWER_BMS
//#define CHADEMO_BATTERY //NOTE: inherently enables CONTACTOR_CONTROL below //#define CHADEMO_BATTERY //NOTE: inherently enables CONTACTOR_CONTROL below
//#define GEELY_GEOMETRY_C_BATTERY
//#define CMFA_EV_BATTERY //#define CMFA_EV_BATTERY
//#define IMIEV_CZERO_ION_BATTERY //#define IMIEV_CZERO_ION_BATTERY
//#define JAGUAR_IPACE_BATTERY //#define JAGUAR_IPACE_BATTERY

View file

@ -1,5 +1,6 @@
#include "../include.h" #include "../include.h"
#include "../datalayer/datalayer_extended.h"
#include "CanBattery.h" #include "CanBattery.h"
#include "RS485Battery.h" #include "RS485Battery.h"
@ -25,14 +26,18 @@ void setup_battery() {
#ifdef DOUBLE_BATTERY #ifdef DOUBLE_BATTERY
if (battery2 == nullptr) { if (battery2 == nullptr) {
#ifdef BMW_I3_BATTERY #if defined(BMW_I3_BATTERY)
battery2 = battery2 =
new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer.system.status.battery2_allows_contactor_closing, new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer.system.status.battery2_allowed_contactor_closing,
can_config.battery_double, WUP_PIN2); can_config.battery_double, WUP_PIN2);
#elif defined(KIA_HYUNDAI_64_BATTERY)
battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer_extended.KiaHyundai64_2,
&datalayer.system.status.battery2_allowed_contactor_closing,
can_config.battery_double);
#elif defined(SANTA_FE_PHEV_BATTERY) || defined(TEST_FAKE_BATTERY)
battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, can_config.battery_double);
#else #else
battery2 = battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, nullptr, can_config.battery_double);
new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer.system.status.battery2_allows_contactor_closing,
nullptr, can_config.battery_double);
#endif #endif
} }
battery2->setup(); battery2->setup();

View file

@ -46,6 +46,10 @@ void setup_can_shunt();
#include "FOXESS-BATTERY.h" #include "FOXESS-BATTERY.h"
#endif #endif
#ifdef GEELY_GEOMETRY_C_BATTERY
#include "GEELY-GEOMETRY-C-BATTERY.h"
#endif
#ifdef ORION_BMS #ifdef ORION_BMS
#include "ORION-BMS.h" #include "ORION-BMS.h"
#endif #endif
@ -151,7 +155,7 @@ void setup_battery(void);
void update_values_battery(); void update_values_battery();
#ifdef RS485_BATTERY_SELECTED #ifdef RS485_BATTERY_SELECTED
void transmit_rs485(); void transmit_rs485(unsigned long currentMillis);
void receive_RS485(); void receive_RS485();
#else #else
void handle_incoming_can_frame_battery(CAN_frame rx_frame); void handle_incoming_can_frame_battery(CAN_frame rx_frame);

View file

@ -329,11 +329,15 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
BMW_13E.data.u8[4] = BMW_13E_counter; BMW_13E.data.u8[4] = BMW_13E_counter;
if (datalayer_battery->status.bms_status == FAULT) { if (datalayer_battery->status.bms_status == FAULT) {
} //If battery is not in Fault mode, allow contactor to close by sending 10B } else if (allows_contactor_closing) {
else if (*allows_contactor_closing == true) { //If battery is not in Fault mode, and we are allowed to control contactors, we allow contactor to close by sending 10B
*allows_contactor_closing = true;
transmit_can_frame(&BMW_10B, can_interface);
} else if (contactor_closing_allowed && *contactor_closing_allowed) {
transmit_can_frame(&BMW_10B, can_interface); transmit_can_frame(&BMW_10B, can_interface);
} }
} }
// Send 100ms CAN Message // Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis; previousMillis100 = currentMillis;
@ -512,7 +516,10 @@ void BmwI3Battery::setup(void) { // Performs one time setup at startup
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH; datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH; datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
if (allows_contactor_closing) {
*allows_contactor_closing = true;
}
datalayer_battery->info.number_of_cells = NUMBER_OF_CELLS; datalayer_battery->info.number_of_cells = NUMBER_OF_CELLS;
pinMode(wakeup_pin, OUTPUT); pinMode(wakeup_pin, OUTPUT);

View file

@ -12,9 +12,10 @@
class BmwI3Battery : public CanBattery { class BmwI3Battery : public CanBattery {
public: public:
// Use this constructor for the second battery. // Use this constructor for the second battery.
BmwI3Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* allows_contactor_closing_ptr, int targetCan, int wakeup) { BmwI3Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* contactor_closing_allowed_ptr, int targetCan, int wakeup) {
datalayer_battery = datalayer_ptr; datalayer_battery = datalayer_ptr;
allows_contactor_closing = allows_contactor_closing_ptr; contactor_closing_allowed = contactor_closing_allowed_ptr;
allows_contactor_closing = nullptr;
can_interface = targetCan; can_interface = targetCan;
wakeup_pin = wakeup; wakeup_pin = wakeup;
*allows_contactor_closing = true; *allows_contactor_closing = true;
@ -27,6 +28,7 @@ class BmwI3Battery : public CanBattery {
BmwI3Battery() { BmwI3Battery() {
datalayer_battery = &datalayer.battery; datalayer_battery = &datalayer.battery;
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing; allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
contactor_closing_allowed = nullptr;
can_interface = can_config.battery; can_interface = can_config.battery;
wakeup_pin = WUP_PIN1; wakeup_pin = WUP_PIN1;
} }
@ -53,9 +55,14 @@ class BmwI3Battery : public CanBattery {
const int NUMBER_OF_CELLS = 96; const int NUMBER_OF_CELLS = 96;
DATALAYER_BATTERY_TYPE* datalayer_battery; DATALAYER_BATTERY_TYPE* datalayer_battery;
bool* allows_contactor_closing;
int wakeup_pin;
// If not null, this battery decides when the contactor can be closed and writes the value here.
bool* allows_contactor_closing;
// If not null, this battery listens to this boolean to determine whether contactor closing is allowed
bool* contactor_closing_allowed;
int wakeup_pin;
int can_interface; int can_interface;
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send

View file

@ -485,10 +485,12 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) {
previousMillis50 = currentMillis; previousMillis50 = currentMillis;
// Set close contactors to allowed (Useful for crashed packs, started via contactor control thru GPIO) // Set close contactors to allowed (Useful for crashed packs, started via contactor control thru GPIO)
if (allows_contactor_closing) {
if (datalayer_battery->status.bms_status == ACTIVE) { if (datalayer_battery->status.bms_status == ACTIVE) {
datalayer.system.status.battery_allows_contactor_closing = true; *allows_contactor_closing = true;
} else { // Fault state, open contactors! } else { // Fault state, open contactors!
datalayer.system.status.battery_allows_contactor_closing = false; *allows_contactor_closing = false;
}
} }
counter_50ms++; counter_50ms++;

View file

@ -33,11 +33,10 @@
class BydAttoBattery : public CanBattery { class BydAttoBattery : public CanBattery {
public: public:
// Use this constructor for the second battery. // Use this constructor for the second battery.
BydAttoBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* allows_contactor_closing_ptr, BydAttoBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_BYDATTO3* extended, int targetCan) {
DATALAYER_INFO_BYDATTO3* extended, int targetCan) {
datalayer_battery = datalayer_ptr; datalayer_battery = datalayer_ptr;
datalayer_bydatto = extended; datalayer_bydatto = extended;
allows_contactor_closing = allows_contactor_closing_ptr; allows_contactor_closing = nullptr;
can_interface = targetCan; can_interface = targetCan;
} }

View file

@ -156,24 +156,22 @@ void decode_packet(uint8_t command, uint8_t data[8]) {
} }
} }
void DalyBms::transmit_rs485() { void DalyBms::transmit_rs485(unsigned long currentMillis) {
static uint8_t nextCommand = 0x90; static uint8_t nextCommand = 0x90;
if (millis() - lastPacket > 60) { if (currentMillis - lastPacket > 60) {
lastPacket = currentMillis;
uint8_t tx_buff[13] = {0}; uint8_t tx_buff[13] = {0};
tx_buff[0] = 0xA5; tx_buff[0] = 0xA5;
tx_buff[1] = 0x40; tx_buff[1] = 0x40;
tx_buff[2] = nextCommand; tx_buff[2] = nextCommand;
tx_buff[3] = 8; tx_buff[3] = 8;
tx_buff[12] = calculate_checksum(tx_buff); tx_buff[12] = calculate_checksum(tx_buff);
#ifdef DEBUG_VIA_USB #ifdef DEBUG_VIA_USB
dump_buff("transmitting: ", tx_buff, 13); dump_buff("transmitting: ", tx_buff, 13);
#endif #endif
Serial2.write(tx_buff, 13); Serial2.write(tx_buff, 13);
lastPacket = millis();
nextCommand++; nextCommand++;
if (nextCommand > 0x98) if (nextCommand > 0x98)
nextCommand = 0x90; nextCommand = 0x90;

View file

@ -22,7 +22,7 @@ class DalyBms : public RS485Battery {
public: public:
void setup(); void setup();
void update_values(); void update_values();
void transmit_rs485(); void transmit_rs485(unsigned long currentMillis);
void receive_RS485(); void receive_RS485();
private: private:

View file

@ -0,0 +1,675 @@
#include "../include.h"
#ifdef GEELY_GEOMETRY_C_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "GEELY-GEOMETRY-C-BATTERY.h"
/* TODO
- Contactor closing: CAN log needed from complete H-CAN of Geely Geometry C vehicle. We are not sure what needs to be sent towards the battery yet to get contactor closing working. DTC readout complains that a "Power CAN BUS Data Missing" message is still missing
- Unsure if the current CAN sending routine is enough to keep BMS alive 24/7. Testing needed
- There are a few UNKNOWN PID polls, these need to be decoded
- Some critical values are still missing:
- Current sensor (Mandatory!)
- Max charge power (can be estimated)
- Max discharge power (can be estimated)
- Cell voltage min/max (not mandatory, but very nice to have)
- All cell voltages (not mandatory, but very nice to have)
Node descriptions, these can send CAN messages in the Geely Geometry C
DSCU (Drivers Seat Control Unit)
OBC (On Board Charger)
FRS (Front Radar System)
IPU (Integrated Power Unit Control)
EGSM (Electronic Gear Shifter)
MMI
T-BOX (Electrocar Communication Control Module)
IPK
FCS
FRS
TCM(SAS)
RPS
ESC
ACU(YRS)
DSCU
PEPS
ESCL
BCM (Body Control Module)
AC
BMSH (Battery Management System)
VCU (Vehicle Control Unit)
AVAS
IB
RSRS
RML
There are 4 CAN buses in the Geometry C, we are interested in the Hybrid CAN (HB-CAN)
- Hybrid, HB CAN: gateway, electronic shifter, VCU, T-BOX, BMS, high and low voltage charging system, integrated power controller
- Infotainent, IF CAN: gateway, diagnostic interface, combined instrument, controller, head-up display, audio host, T-BOX
- Comfort, CF CAN: gateway, diagnostic interface, low-speed alarm controller, thermal management control module, electronic
steering column lock, BCM, seat module
- Chassis, CS CAN: gateway, steering wheel angle sensor, front monocular camera, VCU, millimeter wave radar probe, EPS,
smart booster, ESC, airbag control module, automatic parking module
*/
/* Do not change code below unless you are sure what you are doing */
void GeelyGeometryCBattery::update_values() {
datalayer_battery->status.soh_pptt;
datalayer_battery->status.real_soc = poll_soc * 10;
datalayer_battery->status.voltage_dV = battery_voltage;
datalayer_battery->status.current_dA;
if (poll_amount_cells == 102) { // We have determined that we are on 70kWh pack
datalayer_battery->info.total_capacity_Wh = 70000;
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_70_DV;
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_70_DV;
}
//Calculate the remaining Wh amount from SOC% and max Wh value.
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 = discharge_power_allowed * 100;
datalayer_battery->status.max_charge_power_W;
datalayer_battery->status.cell_min_voltage_mV = maximum_cell_voltage - 10; //TODO: Fix once we have min value
datalayer_battery->status.cell_max_voltage_mV = maximum_cell_voltage;
// Initialize highest and lowest to the first element
maximum_temperature = poll_temperature[0];
minimum_temperature = poll_temperature[0];
// Iterate through the array to find the highest and lowest values
for (uint8_t i = 1; i < 6; ++i) {
if (poll_temperature[i] > maximum_temperature) {
maximum_temperature = poll_temperature[i];
}
if (poll_temperature[i] < minimum_temperature) {
minimum_temperature = poll_temperature[i];
}
}
datalayer_battery->status.temperature_min_dC = minimum_temperature * 10;
datalayer_battery->status.temperature_max_dC = maximum_temperature * 10;
if (HVIL_signal > 0) {
set_event(EVENT_HVIL_FAILURE, HVIL_signal);
} else {
clear_event(EVENT_HVIL_FAILURE);
}
//Update webserver more battery info page
memcpy(datalayer_geometryc->BatterySerialNumber, serialnumbers, sizeof(serialnumbers));
memcpy(datalayer_geometryc->ModuleTemperatures, poll_temperature, sizeof(poll_temperature));
memcpy(datalayer_geometryc->BatterySoftwareVersion, poll_software_version, sizeof(poll_software_version));
memcpy(datalayer_geometryc->BatteryHardwareVersion, poll_hardware_version, sizeof(poll_hardware_version));
datalayer_geometryc->soc = poll_soc;
datalayer_geometryc->CC2voltage = poll_cc2_voltage;
datalayer_geometryc->cellMaxVoltageNumber = poll_cell_max_voltage_number;
datalayer_geometryc->cellMinVoltageNumber = poll_cell_min_voltage_number;
datalayer_geometryc->cellTotalAmount = poll_amount_cells;
datalayer_geometryc->specificialVoltage = poll_specificial_voltage;
datalayer_geometryc->unknown1 = poll_unknown1;
datalayer_geometryc->rawSOCmax = poll_raw_soc_max;
datalayer_geometryc->rawSOCmin = poll_raw_soc_min;
datalayer_geometryc->unknown4 = poll_unknown4;
datalayer_geometryc->capModMax = poll_cap_module_max;
datalayer_geometryc->capModMin = poll_cap_module_min;
datalayer_geometryc->unknown7 = poll_unknown7;
datalayer_geometryc->unknown8 = poll_unknown8;
}
const unsigned char crctable[256] = { // CRC8_SAE_J1850_ZER0 formula,0x2F Poly,initial value 0xFF,Final XOR value 0xFF
0x00, 0x2F, 0x5E, 0x71, 0xBC, 0x93, 0xE2, 0xCD, 0x57, 0x78, 0x09, 0x26, 0xEB, 0xC4, 0xB5, 0x9A, 0xAE, 0x81, 0xF0,
0xDF, 0x12, 0x3D, 0x4C, 0x63, 0xF9, 0xD6, 0xA7, 0x88, 0x45, 0x6A, 0x1B, 0x34, 0x73, 0x5C, 0x2D, 0x02, 0xCF, 0xE0,
0x91, 0xBE, 0x24, 0x0B, 0x7A, 0x55, 0x98, 0xB7, 0xC6, 0xE9, 0xDD, 0xF2, 0x83, 0xAC, 0x61, 0x4E, 0x3F, 0x10, 0x8A,
0xA5, 0xD4, 0xFB, 0x36, 0x19, 0x68, 0x47, 0xE6, 0xC9, 0xB8, 0x97, 0x5A, 0x75, 0x04, 0x2B, 0xB1, 0x9E, 0xEF, 0xC0,
0x0D, 0x22, 0x53, 0x7C, 0x48, 0x67, 0x16, 0x39, 0xF4, 0xDB, 0xAA, 0x85, 0x1F, 0x30, 0x41, 0x6E, 0xA3, 0x8C, 0xFD,
0xD2, 0x95, 0xBA, 0xCB, 0xE4, 0x29, 0x06, 0x77, 0x58, 0xC2, 0xED, 0x9C, 0xB3, 0x7E, 0x51, 0x20, 0x0F, 0x3B, 0x14,
0x65, 0x4A, 0x87, 0xA8, 0xD9, 0xF6, 0x6C, 0x43, 0x32, 0x1D, 0xD0, 0xFF, 0x8E, 0xA1, 0xE3, 0xCC, 0xBD, 0x92, 0x5F,
0x70, 0x01, 0x2E, 0xB4, 0x9B, 0xEA, 0xC5, 0x08, 0x27, 0x56, 0x79, 0x4D, 0x62, 0x13, 0x3C, 0xF1, 0xDE, 0xAF, 0x80,
0x1A, 0x35, 0x44, 0x6B, 0xA6, 0x89, 0xF8, 0xD7, 0x90, 0xBF, 0xCE, 0xE1, 0x2C, 0x03, 0x72, 0x5D, 0xC7, 0xE8, 0x99,
0xB6, 0x7B, 0x54, 0x25, 0x0A, 0x3E, 0x11, 0x60, 0x4F, 0x82, 0xAD, 0xDC, 0xF3, 0x69, 0x46, 0x37, 0x18, 0xD5, 0xFA,
0x8B, 0xA4, 0x05, 0x2A, 0x5B, 0x74, 0xB9, 0x96, 0xE7, 0xC8, 0x52, 0x7D, 0x0C, 0x23, 0xEE, 0xC1, 0xB0, 0x9F, 0xAB,
0x84, 0xF5, 0xDA, 0x17, 0x38, 0x49, 0x66, 0xFC, 0xD3, 0xA2, 0x8D, 0x40, 0x6F, 0x1E, 0x31, 0x76, 0x59, 0x28, 0x07,
0xCA, 0xE5, 0x94, 0xBB, 0x21, 0x0E, 0x7F, 0x50, 0x9D, 0xB2, 0xC3, 0xEC, 0xD8, 0xF7, 0x86, 0xA9, 0x64, 0x4B, 0x3A,
0x15, 0x8F, 0xA0, 0xD1, 0xFE, 0x33, 0x1C, 0x6D, 0x42};
bool is_message_corrupt(CAN_frame* rx_frame) {
uint8_t crc = 0xFF; // Initial value
for (uint8_t j = 0; j < 7; j++) {
crc = crctable[crc ^ rx_frame->data.u8[j]];
}
crc = (crc ^ 0xFF); // Final XOR
return crc != rx_frame->data.u8[7];
}
void GeelyGeometryCBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) {
case 0x0B0: //10ms
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
if (is_message_corrupt(&rx_frame)) {
datalayer.battery2.status.CAN_error_counter++;
break; //Message content malformed, abort reading data from it
}
//Contains:
//HVIL Signal
// - HVIL not connected: 000000B0 00 8 10 06 00 00 00 00 8E 31
// - HVIL connected: 000000B0 00 8 10 00 00 00 00 00 82 0D
// Based on this, the two HVIL is most likely frame1
HVIL_signal = (rx_frame.data.u8[1] & 0x0F);
//BatteryDchgSysFaultLevel
//ChgFaultLevel
//frame7, CRC
//frame6, low byte counter 0-F
break;
case 0x178: //10ms (64 13 88 00 0E 30 0A 85)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
if (is_message_corrupt(&rx_frame)) {
datalayer.battery2.status.CAN_error_counter++;
break; //Message content malformed, abort reading data from it
}
battery_voltage = ((rx_frame.data.u8[4] & 0x1F) << 8) | rx_frame.data.u8[5];
//frame7, CRC
//frame6, low byte counter 0-F
break;
case 0x179: //20ms (3E 52 BA 5D A4 3F 0C D9)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
if (is_message_corrupt(&rx_frame)) {
datalayer.battery2.status.CAN_error_counter++;
break; //Message content malformed, abort reading data from it
}
//2BA = 69.8 //Potentially charge power allowed
//frame7, CRC
//frame6, low byte counter 0-F
break;
case 0x17A: //100ms (Battery 01 B4 52 28 4A 46 6E AE)
//(Car log 0A 3D EE F1 BD C6 67 F7)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
if (is_message_corrupt(&rx_frame)) {
datalayer.battery2.status.CAN_error_counter++;
break; //Message content malformed, abort reading data from it
}
//frame7, CRC
//frame6, low byte counter 0-F
break;
case 0x17B: //20ms (00 00 10 00 0F FE 03 C9) (car is the same, static)
if (is_message_corrupt(&rx_frame)) {
datalayer.battery2.status.CAN_error_counter++;
break; //Message content malformed, abort reading data from it
}
//frame7, CRC
//frame6, low byte counter 0-F
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x210: //100ms (38 04 3A 01 38 22 22 39)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
if (is_message_corrupt(&rx_frame)) {
datalayer.battery2.status.CAN_error_counter++;
break; //Message content malformed, abort reading data from it
}
discharge_power_allowed = ((rx_frame.data.u8[1] & 0x0F) << 8) | rx_frame.data.u8[2]; //TODO, not confirmed
//43A = 108.2kW potentially discharge power allowed
//frame7, CRC
//frame6, counter 0 - 0x22
break;
case 0x211: //100ms (00 D8 C6 00 00 00 0F 3A)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
if (is_message_corrupt(&rx_frame)) {
datalayer.battery2.status.CAN_error_counter++;
break; //Message content malformed, abort reading data from it
}
//frame7, CRC
//frame6, low byte counter 0-F
break;
case 0x212: //500ms (Completely empty)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x351: //100ms (4A 31 71 B8 6E F8 84 00)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x352: //500ms (76 78 00 00 82 FF FF 00)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x353: //500ms (00 00 00 00 62 00 EA 5D) (car 00 00 00 00 00 00 E6 04)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x354: //500ms (Completely empty)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x355: //500ms (89 4A 03 5C 39 06 04 00)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x356: //1s (6B 09 0C 69 0A F1 D3 86)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x357: //20ms (18 17 6F 20 00 00 00 00)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
//Frame 0 and 1 seems to count something large
break;
case 0x358: //1s (03 DF 10 3C DA 0E 20 DE)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x359: //1s (1F 40 00 00 00 00 00 36)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
//Frame7 loops 01-21-22-36-01-21...
break;
case 0x35B: //200ms (Serialnumbers)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
mux = rx_frame.data.u8[0];
switch (mux) {
case 0x01:
serialnumbers[0] = rx_frame.data.u8[1];
serialnumbers[1] = rx_frame.data.u8[2];
serialnumbers[2] = rx_frame.data.u8[3];
serialnumbers[3] = rx_frame.data.u8[4];
serialnumbers[4] = rx_frame.data.u8[5];
serialnumbers[5] = rx_frame.data.u8[6];
serialnumbers[6] = rx_frame.data.u8[7];
break;
case 0x02:
serialnumbers[7] = rx_frame.data.u8[1];
serialnumbers[8] = rx_frame.data.u8[2];
serialnumbers[9] = rx_frame.data.u8[3];
serialnumbers[10] = rx_frame.data.u8[4];
serialnumbers[11] = rx_frame.data.u8[5];
serialnumbers[12] = rx_frame.data.u8[6];
serialnumbers[13] = rx_frame.data.u8[7];
break;
case 0x03:
serialnumbers[14] = rx_frame.data.u8[1];
serialnumbers[15] = rx_frame.data.u8[2];
serialnumbers[16] = rx_frame.data.u8[3];
serialnumbers[17] = rx_frame.data.u8[4];
serialnumbers[18] = rx_frame.data.u8[5];
serialnumbers[19] = rx_frame.data.u8[6];
serialnumbers[20] = rx_frame.data.u8[7];
break;
case 0x04:
serialnumbers[21] = rx_frame.data.u8[1];
serialnumbers[22] = rx_frame.data.u8[2];
serialnumbers[23] = rx_frame.data.u8[3];
serialnumbers[24] = rx_frame.data.u8[4];
serialnumbers[25] = rx_frame.data.u8[5];
serialnumbers[26] = rx_frame.data.u8[6];
serialnumbers[27] = rx_frame.data.u8[7];
break;
default:
break;
}
break;
case 0x424: //500ms (24 10 01 01 02 00 00 00)
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x7EA:
if (rx_frame.data.u8[0] == 0x10) { //Multiframe response, send ACK
transmit_can_frame(&GEELY_ACK, can_config.battery);
//Multiframe has the poll reply slightly different location
incoming_poll = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
}
if (rx_frame.data.u8[0] < 0x10) { //One line response
incoming_poll = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
switch (incoming_poll) {
case POLL_SOC:
poll_soc = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CC2_VOLTAGE:
poll_cc2_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_MAX_VOLTAGE_NUMBER:
poll_cell_max_voltage_number = rx_frame.data.u8[4];
break;
case POLL_CELL_MIN_VOLTAGE_NUMBER:
poll_cell_min_voltage_number = rx_frame.data.u8[4];
break;
case POLL_AMOUNT_CELLS:
poll_amount_cells = rx_frame.data.u8[4];
datalayer_battery->info.number_of_cells = poll_amount_cells;
break;
case POLL_SPECIFICIAL_VOLTAGE:
poll_specificial_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_UNKNOWN_1:
poll_unknown1 = rx_frame.data.u8[4];
break;
case POLL_RAW_SOC_MAX:
poll_raw_soc_max = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_RAW_SOC_MIN:
poll_raw_soc_min = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_UNKNOWN_4:
poll_unknown4 = rx_frame.data.u8[4];
break;
case POLL_CAPACITY_MODULE_MAX:
poll_cap_module_max = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CAPACITY_MODULE_MIN:
poll_cap_module_min = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_UNKNOWN_7:
poll_unknown7 = rx_frame.data.u8[4];
break;
case POLL_UNKNOWN_8:
poll_unknown8 = rx_frame.data.u8[4];
break;
default:
break;
}
}
switch (incoming_poll) //Multiframe response
{
case POLL_MULTI_TEMPS:
switch (rx_frame.data.u8[0]) {
case 0x10:
poll_temperature[0] = (rx_frame.data.u8[5] - TEMP_OFFSET);
poll_temperature[1] = (rx_frame.data.u8[6] - TEMP_OFFSET);
poll_temperature[2] = (rx_frame.data.u8[7] - TEMP_OFFSET);
break;
case 0x21:
poll_temperature[3] = (rx_frame.data.u8[1] - TEMP_OFFSET);
poll_temperature[4] = (rx_frame.data.u8[2] - TEMP_OFFSET);
poll_temperature[5] = (rx_frame.data.u8[3] - TEMP_OFFSET);
break;
default:
break;
}
break;
case POLL_MULTI_SOFTWARE_VERSION:
switch (rx_frame.data.u8[0]) {
case 0x10:
poll_software_version[0] = rx_frame.data.u8[5];
poll_software_version[1] = rx_frame.data.u8[6];
poll_software_version[2] = rx_frame.data.u8[7];
break;
case 0x21:
poll_software_version[3] = rx_frame.data.u8[1];
poll_software_version[4] = rx_frame.data.u8[2];
poll_software_version[5] = rx_frame.data.u8[3];
poll_software_version[6] = rx_frame.data.u8[4];
poll_software_version[7] = rx_frame.data.u8[5];
poll_software_version[8] = rx_frame.data.u8[6];
poll_software_version[9] = rx_frame.data.u8[7];
break;
case 0x22:
poll_software_version[10] = rx_frame.data.u8[1];
poll_software_version[11] = rx_frame.data.u8[2];
poll_software_version[12] = rx_frame.data.u8[3];
poll_software_version[13] = rx_frame.data.u8[4];
poll_software_version[14] = rx_frame.data.u8[5];
poll_software_version[15] = rx_frame.data.u8[6];
break;
case 0x23:
break;
default:
break;
}
break;
case POLL_MULTI_HARDWARE_VERSION:
switch (rx_frame.data.u8[0]) {
case 0x10:
poll_hardware_version[0] = rx_frame.data.u8[5];
poll_hardware_version[1] = rx_frame.data.u8[6];
poll_hardware_version[2] = rx_frame.data.u8[7];
break;
case 0x21:
poll_hardware_version[3] = rx_frame.data.u8[1];
poll_hardware_version[4] = rx_frame.data.u8[2];
poll_hardware_version[5] = rx_frame.data.u8[3];
poll_hardware_version[6] = rx_frame.data.u8[4];
poll_hardware_version[7] = rx_frame.data.u8[5];
poll_hardware_version[8] = rx_frame.data.u8[6];
poll_hardware_version[9] = rx_frame.data.u8[7];
break;
case 0x22:
poll_hardware_version[10] = rx_frame.data.u8[1];
poll_hardware_version[11] = rx_frame.data.u8[2];
poll_hardware_version[12] = rx_frame.data.u8[3];
poll_hardware_version[13] = rx_frame.data.u8[4];
poll_hardware_version[14] = rx_frame.data.u8[5];
poll_hardware_version[15] = rx_frame.data.u8[6];
break;
case 0x23:
break;
default:
break;
}
break;
default:
//Not a multiframe response, do nothing
break;
}
break;
default:
break;
}
}
uint8_t calc_crc8_geely(CAN_frame* rx_frame) {
uint8_t crc = 0xFF; // Initial value
for (uint8_t j = 0; j < 7; j++) {
crc = crctable[crc ^ rx_frame->data.u8[j]];
}
return crc ^ 0xFF; // Final XOR
}
void GeelyGeometryCBattery::transmit_can(unsigned long currentMillis) {
// Send 10ms CAN Message
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
previousMillis10 = currentMillis;
GEELY_191.data.u8[6] = ((GEELY_191.data.u8[6] & 0xF0) | counter_10ms);
GEELY_191.data.u8[7] = calc_crc8_geely(&GEELY_191);
GEELY_0A6.data.u8[6] = ((GEELY_0A6.data.u8[6] & 0xF0) | counter_10ms);
GEELY_0A6.data.u8[7] = calc_crc8_geely(&GEELY_0A6);
GEELY_165.data.u8[6] = ((GEELY_165.data.u8[6] & 0xF0) | counter_10ms);
GEELY_165.data.u8[7] = calc_crc8_geely(&GEELY_165);
GEELY_1A4.data.u8[6] = ((GEELY_1A4.data.u8[6] & 0xF0) | counter_10ms);
GEELY_1A4.data.u8[7] = calc_crc8_geely(&GEELY_1A4);
GEELY_162.data.u8[6] = ((GEELY_162.data.u8[6] & 0xF0) | counter_10ms);
GEELY_162.data.u8[7] = calc_crc8_geely(&GEELY_162);
GEELY_1A5.data.u8[6] = ((GEELY_1A5.data.u8[6] & 0xF0) | counter_10ms);
GEELY_1A5.data.u8[7] = calc_crc8_geely(&GEELY_1A5);
GEELY_220.data.u8[6] = ((GEELY_220.data.u8[6] & 0xF0) | counter_10ms);
GEELY_220.data.u8[7] = calc_crc8_geely(&GEELY_220);
GEELY_0E0.data.u8[4] = ((GEELY_0E0.data.u8[4] & 0xF0) | counter_10ms); //unique
GEELY_0E0.data.u8[5] = calc_crc8_geely(&GEELY_0E0); //unique
counter_10ms = (counter_10ms + 1) % 17; // 0-1-...F-0-1 etc.
transmit_can_frame(&GEELY_191, can_config.battery);
transmit_can_frame(&GEELY_0A6, can_config.battery);
transmit_can_frame(&GEELY_160, can_config.battery);
transmit_can_frame(&GEELY_165, can_config.battery);
transmit_can_frame(&GEELY_1A4, can_config.battery);
transmit_can_frame(&GEELY_162, can_config.battery); //CONFIRMED MANDATORY! VCU message
transmit_can_frame(&GEELY_1A5, can_config.battery);
transmit_can_frame(&GEELY_220, can_config.battery); //CONFIRMED MANDATORY! OBC message
transmit_can_frame(&GEELY_0E0, can_config.battery);
}
if (currentMillis - previousMillis20 >= INTERVAL_20_MS) {
previousMillis20 = currentMillis;
GEELY_145.data.u8[6] = ((GEELY_145.data.u8[6] & 0xF0) | counter_10ms);
GEELY_145.data.u8[7] = calc_crc8_geely(&GEELY_145);
GEELY_150.data.u8[6] = ((GEELY_150.data.u8[6] & 0xF0) | counter_10ms);
GEELY_150.data.u8[7] = calc_crc8_geely(&GEELY_150);
counter_20ms = (counter_20ms + 1) % 17; // 0-1-...F-0-1 etc.
transmit_can_frame(&GEELY_145, can_config.battery); //CONFIRMED MANDATORY! shifter
transmit_can_frame(&GEELY_0F9, can_config.battery); //CONFIRMED MANDATORY! shifter
transmit_can_frame(&GEELY_0FA, can_config.battery); //Might be unnecessary, not in workshop manual
transmit_can_frame(&GEELY_197, can_config.battery); //Might be unnecessary, not in workshop manual
transmit_can_frame(&GEELY_150, can_config.battery);
}
if (currentMillis - previousMillis50 >= INTERVAL_50_MS) {
previousMillis50 = currentMillis;
GEELY_1A3.data.u8[6] = ((GEELY_1A3.data.u8[6] & 0xF0) | counter_10ms);
GEELY_1A3.data.u8[7] = calc_crc8_geely(&GEELY_1A3);
GEELY_0A8.data.u8[6] = ((GEELY_0A8.data.u8[6] & 0xF0) | counter_10ms);
GEELY_0A8.data.u8[7] = calc_crc8_geely(&GEELY_0A8);
counter_50ms = (counter_50ms + 1) % 17; // 0-1-...F-0-1 etc.
transmit_can_frame(&GEELY_1B2, can_config.battery);
transmit_can_frame(&GEELY_221, can_config.battery); //CONFIRMED MANDATORY! OBC message
//transmit_can_frame(&GEELY_1A3, can_config.battery); //Might be unnecessary, radar info
transmit_can_frame(&GEELY_1A7, can_config.battery); //Might be unnecessary
transmit_can_frame(&GEELY_0A8, can_config.battery); //CONFIRMED MANDATORY! IPU message
transmit_can_frame(&GEELY_1F2, can_config.battery); //Might be unnecessary, not in manual
transmit_can_frame(&GEELY_1A6, can_config.battery); //Might be unnecessary, not in manual
}
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
GEELY_0A8.data.u8[6] = ((GEELY_0A8.data.u8[6] & 0x0F) | (counter_10ms << 4)); //unique bitshift
GEELY_0A8.data.u8[7] = calc_crc8_geely(&GEELY_0A8);
counter_100ms = (counter_100ms + 1) % 17; // 0-1-...F-0-1 etc.
transmit_can_frame(&GEELY_222, can_config.battery); //CONFIRMED MANDATORY! OBC message
//transmit_can_frame(&GEELY_2D2, can_config.battery); //Might be unnecessary, seat info
transmit_can_frame(&GEELY_292, can_config.battery); //CONFIRMED MANDATORY! T-BOX
}
// Send 200ms CAN Message
if (currentMillis - previousMillis200 >= INTERVAL_200_MS) {
previousMillis200 = currentMillis;
switch (poll_pid) {
case POLL_SOC:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_SOC >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_SOC;
poll_pid = POLL_CC2_VOLTAGE;
break;
case POLL_CC2_VOLTAGE:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_CC2_VOLTAGE >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_CC2_VOLTAGE;
poll_pid = POLL_CELL_MAX_VOLTAGE_NUMBER;
break;
case POLL_CELL_MAX_VOLTAGE_NUMBER:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_CELL_MAX_VOLTAGE_NUMBER >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_CELL_MAX_VOLTAGE_NUMBER;
poll_pid = POLL_CELL_MIN_VOLTAGE_NUMBER;
break;
case POLL_CELL_MIN_VOLTAGE_NUMBER:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_CELL_MIN_VOLTAGE_NUMBER >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_CELL_MIN_VOLTAGE_NUMBER;
poll_pid = POLL_AMOUNT_CELLS;
break;
case POLL_AMOUNT_CELLS:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_AMOUNT_CELLS >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_AMOUNT_CELLS;
poll_pid = POLL_SPECIFICIAL_VOLTAGE;
break;
case POLL_SPECIFICIAL_VOLTAGE:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_SPECIFICIAL_VOLTAGE >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_SPECIFICIAL_VOLTAGE;
poll_pid = POLL_UNKNOWN_1;
break;
case POLL_UNKNOWN_1:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_UNKNOWN_1 >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_UNKNOWN_1;
poll_pid = POLL_RAW_SOC_MAX;
break;
case POLL_RAW_SOC_MAX:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_RAW_SOC_MAX >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_RAW_SOC_MAX;
poll_pid = POLL_RAW_SOC_MIN;
break;
case POLL_RAW_SOC_MIN:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_RAW_SOC_MIN >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_RAW_SOC_MIN;
poll_pid = POLL_UNKNOWN_4;
break;
case POLL_UNKNOWN_4:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_UNKNOWN_4 >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_UNKNOWN_4;
poll_pid = POLL_CAPACITY_MODULE_MAX;
break;
case POLL_CAPACITY_MODULE_MAX:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_CAPACITY_MODULE_MAX >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_CAPACITY_MODULE_MAX;
poll_pid = POLL_CAPACITY_MODULE_MIN;
break;
case POLL_CAPACITY_MODULE_MIN:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_CAPACITY_MODULE_MIN >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_CAPACITY_MODULE_MIN;
poll_pid = POLL_UNKNOWN_7;
break;
case POLL_UNKNOWN_7:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_UNKNOWN_7 >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_UNKNOWN_7;
poll_pid = POLL_UNKNOWN_8;
break;
case POLL_UNKNOWN_8:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_UNKNOWN_8 >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_UNKNOWN_8;
poll_pid = POLL_MULTI_TEMPS;
break;
case POLL_MULTI_TEMPS:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_TEMPS >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_TEMPS;
poll_pid = POLL_MULTI_UNKNOWN_2;
break;
case POLL_MULTI_UNKNOWN_2:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_UNKNOWN_2 >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_UNKNOWN_2;
poll_pid = POLL_MULTI_UNKNOWN_3;
break;
case POLL_MULTI_UNKNOWN_3:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_UNKNOWN_3 >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_UNKNOWN_3;
poll_pid = POLL_MULTI_UNKNOWN_4;
break;
case POLL_MULTI_UNKNOWN_4:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_UNKNOWN_4 >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_UNKNOWN_4;
poll_pid = POLL_MULTI_HARDWARE_VERSION;
break;
case POLL_MULTI_HARDWARE_VERSION:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_HARDWARE_VERSION >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_HARDWARE_VERSION;
poll_pid = POLL_MULTI_SOFTWARE_VERSION;
break;
case POLL_MULTI_SOFTWARE_VERSION:
GEELY_POLL.data.u8[2] = (uint8_t)(POLL_MULTI_SOFTWARE_VERSION >> 8);
GEELY_POLL.data.u8[3] = (uint8_t)POLL_MULTI_SOFTWARE_VERSION;
poll_pid = POLL_SOC;
break;
default:
poll_pid = POLL_SOC;
break;
}
transmit_can_frame(&GEELY_POLL, can_config.battery);
}
}
void GeelyGeometryCBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Geely Geometry C", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer_battery->info.number_of_cells = 102; //70kWh pack has 102S, startup in this mode
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_70_DV; //Startup in extreme ends
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_53_DV; //Before pack size determined
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;
}
#endif

View file

@ -0,0 +1,217 @@
#ifndef GEELY_GEOMETRY_C_BATTERY_H
#define GEELY_GEOMETRY_C_BATTERY_H
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS GeelyGeometryCBattery
#define POLL_SOC 0x4B35
#define POLL_CC2_VOLTAGE 0x4BCF
#define POLL_CELL_MAX_VOLTAGE_NUMBER 0x4B1E
#define POLL_CELL_MIN_VOLTAGE_NUMBER 0x4B20
#define POLL_AMOUNT_CELLS 0x4B07
#define POLL_SPECIFICIAL_VOLTAGE 0x4B05
#define POLL_UNKNOWN_1 0x4BDA //245 on two batteries
#define POLL_RAW_SOC_MAX 0x4BC3
#define POLL_RAW_SOC_MIN 0x4BC4
#define POLL_UNKNOWN_4 0xDF00 //144 (the other battery 143)
#define POLL_CAPACITY_MODULE_MAX 0x4B3D
#define POLL_CAPACITY_MODULE_MIN 0x4B3E
#define POLL_UNKNOWN_7 0x4B24 //1 (the other battery 23)
#define POLL_UNKNOWN_8 0x4B26 //16 (the other battery 33)
#define POLL_MULTI_TEMPS 0x4B0F
#define POLL_MULTI_UNKNOWN_2 0x4B10
#define POLL_MULTI_UNKNOWN_3 0x4B53
#define POLL_MULTI_UNKNOWN_4 0x4B54
#define POLL_MULTI_HARDWARE_VERSION 0x4B6B
#define POLL_MULTI_SOFTWARE_VERSION 0x4B6C
class GeelyGeometryCBattery : public CanBattery {
public:
virtual void setup(void);
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
private:
const int MAX_PACK_VOLTAGE_70_DV 4420 //70kWh
const int MIN_PACK_VOLTAGE_70_DV 2860 const int MAX_PACK_VOLTAGE_53_DV 4160 //53kWh
const int MIN_PACK_VOLTAGE_53_DV 2700 const int MAX_CELL_DEVIATION_MV 150 const int
MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
const int MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value
DATALAYER_BATTERY_TYPE* datalayer_battery;
DATALAYER_INFO_GEELY_GEOMETRY_C* datalayer_geometryc;
CAN_frame GEELY_191 = {.FD = false, //PAS_APA_Status , 10ms
.ext_ID = false,
.DLC = 8,
.ID = 0x191,
.data = {0x00, 0x00, 0x81, 0x20, 0x00, 0x00, 0x00, 0x01}};
CAN_frame GEELY_2D2 = {.FD = false, //DSCU 100ms
.ext_ID = false,
.DLC = 8,
.ID = 0x2D2,
.data = {0x60, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GEELY_0A6 = {.FD = false, //VCU 10ms
.ext_ID = false,
.DLC = 8,
.ID = 0x0A6,
.data = {0xFA, 0x0F, 0xA0, 0x00, 0x00, 0xFA, 0x00, 0xE4}};
CAN_frame GEELY_160 = {.FD = false, //VCU 10ms
.ext_ID = false,
.DLC = 8,
.ID = 0x160,
.data = {0x00, 0x01, 0x67, 0xF7, 0xC0, 0x19, 0x00, 0x20}};
CAN_frame GEELY_165 = {.FD = false, //VCU_ModeControl 10ms
.ext_ID = false,
.DLC = 8,
.ID = 0x165,
.data = {0x00, 0x81, 0xA1, 0x00, 0x00, 0x1E, 0x00, 0xD6}};
CAN_frame GEELY_1A4 = {.FD = false, //VCU 10ms
.ext_ID = false,
.DLC = 8,
.ID = 0x1A4,
.data = {0x17, 0x73, 0x17, 0x70, 0x02, 0x1C, 0x00, 0x56}};
CAN_frame GEELY_162 = {.FD = false, //VCU 10ms
.ext_ID = false,
.DLC = 8,
.ID = 0x162,
.data = {0x00, 0x05, 0x06, 0x81, 0x00, 0x09, 0x00, 0xC6}};
CAN_frame GEELY_1A5 = {.FD = false, //VCU 10ms
.ext_ID = false,
.DLC = 8,
.ID = 0x1A5,
.data = {0x17, 0x70, 0x24, 0x0B, 0x00, 0x00, 0x00, 0xF9}};
CAN_frame GEELY_1B2 = {.FD = false, //??? 50ms
.ext_ID = false,
.DLC = 8,
.ID = 0x1B2,
.data = {0x17, 0x70, 0x24, 0x0B, 0x00, 0x00, 0x00, 0xF9}};
CAN_frame GEELY_221 = {.FD = false, //OBC 50ms
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00}};
CAN_frame GEELY_220 = {.FD = false, //OBC 100ms
.ext_ID = false,
.DLC = 8,
.ID = 0x220,
.data = {0x0B, 0x43, 0x69, 0xF3, 0x3A, 0x10, 0x00, 0x31}};
CAN_frame GEELY_1A3 = {.FD = false, //FRS 50ms
.ext_ID = false,
.DLC = 8,
.ID = 0x1A3,
.data = {0xFF, 0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x4F}};
CAN_frame GEELY_1A7 = {.FD = false, //??? 50ms
.ext_ID = false,
.DLC = 8,
.ID = 0x1A7,
.data = {0x00, 0x7F, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00}};
CAN_frame GEELY_0A8 = {.FD = false, //IPU 100ms
.ext_ID = false,
.DLC = 8,
.ID = 0x0A8,
.data = {0x00, 0x2E, 0xDC, 0x4E, 0x20, 0x00, 0x20, 0xA2}};
CAN_frame GEELY_1F2 = {.FD = false, //??? 50ms
.ext_ID = false,
.DLC = 8,
.ID = 0x1F2,
.data = {0x9B, 0xA3, 0x99, 0xA2, 0x41, 0x42, 0x41, 0x42}};
CAN_frame GEELY_222 = {.FD = false, //OBC 100ms
.ext_ID = false,
.DLC = 8,
.ID = 0x222,
.data = {0x00, 0x00, 0x00, 0xFF, 0xF8, 0x00, 0x00, 0x00}};
CAN_frame GEELY_1A6 = {.FD = false, //OBC 100ms
.ext_ID = false,
.DLC = 8,
.ID = 0x1A6,
.data = {0x00, 0x7F, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00}};
CAN_frame GEELY_145 = {.FD = false, //EGSM 20ms
.ext_ID = false,
.DLC = 8,
.ID = 0x145,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A}};
CAN_frame GEELY_0E0 = {.FD = false, //IPU 10ms
.ext_ID = false,
.DLC = 8,
.ID = 0x0E0,
.data = {0xFF, 0x09, 0x00, 0xE0, 0x00, 0x8F, 0x00, 0x00}};
CAN_frame GEELY_0F9 = {.FD = false, //??? 20ms
.ext_ID = false,
.DLC = 8,
.ID = 0x0F9,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GEELY_292 = {.FD = false, //T-BOX 100ms
.ext_ID = false,
.DLC = 8,
.ID = 0x292,
.data = {0x00, 0x00, 0x00, 0x1F, 0xE7, 0xE7, 0x00, 0xBC}};
CAN_frame GEELY_0FA = {.FD = false, //??? 20ms
.ext_ID = false,
.DLC = 8,
.ID = 0x0FA,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GEELY_197 = {.FD = false, //??? 20ms
.ext_ID = false,
.DLC = 8,
.ID = 0x197,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A}};
CAN_frame GEELY_150 = {.FD = false, //EPS 20ms
.ext_ID = false,
.DLC = 8,
.ID = 0x150,
.data = {0x7E, 0x00, 0x24, 0x00, 0x01, 0x01, 0x00, 0xA9}};
CAN_frame GEELY_POLL = {.FD = false, //Polling frame
.ext_ID = false,
.DLC = 8,
.ID = 0x7E2,
.data = {0x03, 0x22, 0x4B, 0xDA, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GEELY_ACK = {.FD = false, //Ack frame
.ext_ID = false,
.DLC = 8,
.ID = 0x7E2,
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
uint16_t poll_pid = POLL_SOC;
uint16_t incoming_poll = 0;
uint8_t counter_10ms = 0;
uint8_t counter_20ms = 0;
uint8_t counter_50ms = 0;
uint8_t counter_100ms = 0;
unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was sent
unsigned long previousMillis50 = 0; // will store last time a 50ms CAN Message was sent
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was sent
uint8_t mux = 0;
uint16_t battery_voltage = 3700;
int16_t maximum_temperature = 0;
int16_t minimum_temperature = 0;
uint8_t HVIL_signal = 0;
uint8_t serialnumbers[28] = {0};
uint16_t maximum_cell_voltage = 3700;
uint16_t discharge_power_allowed = 0;
uint16_t poll_soc = 0;
uint16_t poll_cc2_voltage = 0;
uint16_t poll_cell_max_voltage_number = 0;
uint16_t poll_cell_min_voltage_number = 0;
uint16_t poll_amount_cells = 0;
uint16_t poll_specificial_voltage = 0;
uint16_t poll_unknown1 = 0;
uint16_t poll_raw_soc_max = 0;
uint16_t poll_raw_soc_min = 0;
uint16_t poll_unknown4 = 0;
uint16_t poll_cap_module_max = 0;
uint16_t poll_cap_module_min = 0;
uint16_t poll_unknown7 = 0;
uint16_t poll_unknown8 = 0;
int16_t poll_temperature[6] = {0};
#define TEMP_OFFSET 30 //TODO, not calibrated yet, best guess
uint8_t poll_software_version[16] = {0};
uint8_t poll_hardware_version[16] = {0};
};
#endif

View file

@ -161,7 +161,7 @@ uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high) {
} }
/* These messages are needed for contactor closing */ /* These messages are needed for contactor closing */
unsigned long startMillis; unsigned long startMillis = 0;
uint8_t messageIndex = 0; uint8_t messageIndex = 0;
uint8_t messageDelays[63] = {0, 0, 5, 10, 10, 15, 19, 19, 20, 20, 25, 30, 30, 35, 40, 40, uint8_t messageDelays[63] = {0, 0, 5, 10, 10, 15, 19, 19, 20, 20, 25, 30, 30, 35, 40, 40,
45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70, 45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70,
@ -1100,7 +1100,7 @@ void transmit_can_battery(unsigned long currentMillis) {
} }
if (messageIndex >= 63) { if (messageIndex >= 63) {
startMillis = millis(); // Start over! startMillis = currentMillis; // Start over!
messageIndex = 0; messageIndex = 0;
} }
@ -1131,9 +1131,6 @@ void transmit_can_battery(unsigned long currentMillis) {
void setup_battery(void) { // Performs one time setup at startup void setup_battery(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai EGMP platform", 63); strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai EGMP platform", 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
startMillis = millis(); // Record the starting time
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.number_of_cells = 192; // TODO: will vary depending on battery 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.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;

View file

@ -1,176 +1,36 @@
#include "../include.h" #include "../include.h"
#ifdef KIA_HYUNDAI_64_BATTERY #ifdef KIA_HYUNDAI_64_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" #include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "KIA-HYUNDAI-64-BATTERY.h" #include "KIA-HYUNDAI-64-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */ void KiaHyundai64Battery::
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
static unsigned long previousMillis10 = 0; // will store last time a 10s CAN Message was send
static uint16_t soc_calculated = 0; datalayer_battery->status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
static uint16_t SOC_BMS = 0;
static uint16_t SOC_Display = 0;
static uint16_t batterySOH = 1000;
static uint16_t CellVoltMax_mV = 3700;
static uint16_t CellVoltMin_mV = 3700;
static uint16_t allowedDischargePower = 0;
static uint16_t allowedChargePower = 0;
static uint16_t batteryVoltage = 0;
static uint16_t inverterVoltageFrameHigh = 0;
static uint16_t inverterVoltage = 0;
static uint16_t cellvoltages_mv[98];
static int16_t leadAcidBatteryVoltage = 120;
static int16_t batteryAmps = 0;
static int16_t temperatureMax = 0;
static int16_t temperatureMin = 0;
static int16_t poll_data_pid = 0;
static bool holdPidCounter = false;
static uint8_t CellVmaxNo = 0;
static uint8_t CellVminNo = 0;
static uint8_t batteryManagementMode = 0;
static uint8_t BMS_ign = 0;
static uint8_t batteryRelay = 0;
static uint8_t waterleakageSensor = 164;
static uint8_t counter_200 = 0;
static int8_t temperature_water_inlet = 0;
static int8_t heatertemp = 0;
static int8_t powerRelayTemperature = 0;
static bool startedUp = false;
#ifdef DOUBLE_BATTERY datalayer_battery->status.soh_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
static uint8_t counter_200_2 = 0;
static uint16_t battery2_soc_calculated = 0;
static uint16_t battery2_SOC_BMS = 0;
static uint16_t battery2_SOC_Display = 0;
static uint16_t battery2_batterySOH = 1000;
static uint16_t battery2_CellVoltMax_mV = 3700;
static uint16_t battery2_CellVoltMin_mV = 3700;
static uint16_t battery2_allowedDischargePower = 0;
static uint16_t battery2_allowedChargePower = 0;
static uint16_t battery2_batteryVoltage = 0;
static uint16_t battery2_inverterVoltageFrameHigh = 0;
static uint16_t battery2_inverterVoltage = 0;
static uint16_t battery2_cellvoltages_mv[98];
static int16_t battery2_leadAcidBatteryVoltage = 120;
static int16_t battery2_batteryAmps = 0;
static int16_t battery2_temperatureMax = 0;
static int16_t battery2_temperatureMin = 0;
static int16_t battery2_poll_data_pid = 0;
static bool battery2_holdPidCounter = false;
static uint8_t battery2_CellVmaxNo = 0;
static uint8_t battery2_CellVminNo = 0;
static uint8_t battery2_batteryManagementMode = 0;
static uint8_t battery2_BMS_ign = 0;
static uint8_t battery2_batteryRelay = 0;
static uint8_t battery2_waterleakageSensor = 164;
static uint8_t battery2_counter_200 = 0;
static int8_t battery2_temperature_water_inlet = 0;
static int8_t battery2_heatertemp = 0;
static int8_t battery2_powerRelayTemperature = 0;
static bool battery2_startedUp = false;
CAN_frame KIA_HYUNDAI_200_2 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x200,
.data = {0x00, 0x80, 0xD8, 0x04, 0x00, 0x17, 0xD0, 0x00}}; //2nd battery
#endif //DOUBLE_BATTERY
CAN_frame KIA_HYUNDAI_200 = {.FD = false, datalayer_battery->status.voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0)
.ext_ID = false,
.DLC = 8,
.ID = 0x200,
.data = {0x00, 0x80, 0xD8, 0x04, 0x00, 0x17, 0xD0, 0x00}};
CAN_frame KIA_HYUNDAI_523 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x523,
.data = {0x08, 0x38, 0x36, 0x36, 0x33, 0x34, 0x00, 0x01}};
CAN_frame KIA_HYUNDAI_524 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x524,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
//553 Needed frame 200ms
CAN_frame KIA64_553 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x553,
.data = {0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00}};
//57F Needed frame 100ms
CAN_frame KIA64_57F = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x57F,
.data = {0x80, 0x0A, 0x72, 0x00, 0x00, 0x00, 0x00, 0x72}};
//Needed frame 100ms
CAN_frame KIA64_2A1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2A1,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame KIA64_7E4_id1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 01
CAN_frame KIA64_7E4_id2 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 02
CAN_frame KIA64_7E4_id3 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 03
CAN_frame KIA64_7E4_id4 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 04
CAN_frame KIA64_7E4_id5 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 05
CAN_frame KIA64_7E4_id6 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 06
CAN_frame KIA64_7E4_ack = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus datalayer_battery->status.current_dA = -batteryAmps; //value is *10 (150 = 15.0) , invert the sign
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00 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.soh_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00% datalayer_battery->status.max_charge_power_W = allowedChargePower * 10;
datalayer.battery.status.voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0) datalayer_battery->status.max_discharge_power_W = allowedDischargePower * 10;
datalayer.battery.status.current_dA = -batteryAmps; //value is *10 (150 = 15.0) , invert the sign datalayer_battery->status.temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>( datalayer_battery->status.temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
datalayer.battery.status.max_charge_power_W = allowedChargePower * 10; datalayer_battery->status.cell_max_voltage_mV = CellVoltMax_mV;
datalayer.battery.status.max_discharge_power_W = allowedDischargePower * 10; datalayer_battery->status.cell_min_voltage_mV = CellVoltMin_mV;
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
datalayer.battery.status.cell_max_voltage_mV = CellVoltMax_mV;
datalayer.battery.status.cell_min_voltage_mV = CellVoltMin_mV;
if (waterleakageSensor == 0) { if (waterleakageSensor == 0) {
set_event(EVENT_WATER_INGRESS, 0); set_event(EVENT_WATER_INGRESS, 0);
@ -181,14 +41,14 @@ void update_values_battery() { //This function maps all the values fetched via
} }
// Update webserver datalayer // Update webserver datalayer
datalayer_extended.KiaHyundai64.total_cell_count = datalayer.battery.info.number_of_cells; datalayer_battery_extended->total_cell_count = datalayer_battery->info.number_of_cells;
datalayer_extended.KiaHyundai64.battery_12V = leadAcidBatteryVoltage; datalayer_battery_extended->battery_12V = leadAcidBatteryVoltage;
datalayer_extended.KiaHyundai64.waterleakageSensor = waterleakageSensor; datalayer_battery_extended->waterleakageSensor = waterleakageSensor;
datalayer_extended.KiaHyundai64.temperature_water_inlet = temperature_water_inlet; datalayer_battery_extended->temperature_water_inlet = temperature_water_inlet;
datalayer_extended.KiaHyundai64.powerRelayTemperature = powerRelayTemperature * 2; datalayer_battery_extended->powerRelayTemperature = powerRelayTemperature * 2;
datalayer_extended.KiaHyundai64.batteryManagementMode = batteryManagementMode; datalayer_battery_extended->batteryManagementMode = batteryManagementMode;
datalayer_extended.KiaHyundai64.BMS_ign = BMS_ign; datalayer_battery_extended->BMS_ign = BMS_ign;
datalayer_extended.KiaHyundai64.batteryRelay = batteryRelay; datalayer_battery_extended->batteryRelay = batteryRelay;
//Perform logging if configured to do so //Perform logging if configured to do so
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
@ -205,7 +65,7 @@ void update_values_battery() { //This function maps all the values fetched via
logging.print(" Amps | "); logging.print(" Amps | ");
logging.print((uint16_t)batteryVoltage / 10.0, 1); logging.print((uint16_t)batteryVoltage / 10.0, 1);
logging.print(" Volts | "); logging.print(" Volts | ");
logging.print((int16_t)datalayer.battery.status.active_power_W); logging.print((int16_t)datalayer_battery->status.active_power_W);
logging.println(" Watts"); logging.println(" Watts");
logging.print("Allowed Charge "); logging.print("Allowed Charge ");
logging.print((uint16_t)allowedChargePower * 10); logging.print((uint16_t)allowedChargePower * 10);
@ -251,32 +111,32 @@ void update_values_battery() { //This function maps all the values fetched via
#endif #endif
} }
void update_number_of_cells() { void KiaHyundai64Battery::update_number_of_cells() {
//If we have cell values and number_of_cells not initialized yet //If we have cell values and number_of_cells not initialized yet
if (cellvoltages_mv[0] > 0 && datalayer.battery.info.number_of_cells == 0) { if (cellvoltages_mv[0] > 0 && datalayer_battery->info.number_of_cells == 0) {
// Check if we have 98S or 90S battery // Check if we have 98S or 90S battery
if (datalayer.battery.status.cell_voltages_mV[97] > 0) { if (datalayer_battery->status.cell_voltages_mV[97] > 0) {
datalayer.battery.info.number_of_cells = 98; datalayer_battery->info.number_of_cells = 98;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV; datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_98S_DV; datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_98S_DV;
datalayer.battery.info.total_capacity_Wh = 64000; datalayer_battery->info.total_capacity_Wh = 64000;
} else { } else {
datalayer.battery.info.number_of_cells = 90; datalayer_battery->info.number_of_cells = 90;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_90S_DV; datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_90S_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV; datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV;
datalayer.battery.info.total_capacity_Wh = 40000; datalayer_battery->info.total_capacity_Wh = 40000;
} }
} }
} }
void handle_incoming_can_frame_battery(CAN_frame rx_frame) { void KiaHyundai64Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) { switch (rx_frame.ID) {
case 0x4DE: case 0x4DE:
startedUp = true; startedUp = true;
break; break;
case 0x542: //BMS SOC case 0x542: //BMS SOC
startedUp = true; startedUp = true;
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
SOC_Display = rx_frame.data.u8[0] * 5; //100% = 200 ( 200 * 5 = 1000 ) SOC_Display = rx_frame.data.u8[0] * 5; //100% = 200 ( 200 * 5 = 1000 )
break; break;
case 0x594: case 0x594:
@ -321,17 +181,17 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
} }
poll_data_pid++; poll_data_pid++;
if (poll_data_pid == 1) { if (poll_data_pid == 1) {
transmit_can_frame(&KIA64_7E4_id1, can_config.battery); transmit_can_frame(&KIA64_7E4_id1, can_interface);
} else if (poll_data_pid == 2) { } else if (poll_data_pid == 2) {
transmit_can_frame(&KIA64_7E4_id2, can_config.battery); transmit_can_frame(&KIA64_7E4_id2, can_interface);
} else if (poll_data_pid == 3) { } else if (poll_data_pid == 3) {
transmit_can_frame(&KIA64_7E4_id3, can_config.battery); transmit_can_frame(&KIA64_7E4_id3, can_interface);
} else if (poll_data_pid == 4) { } else if (poll_data_pid == 4) {
transmit_can_frame(&KIA64_7E4_id4, can_config.battery); transmit_can_frame(&KIA64_7E4_id4, can_interface);
} else if (poll_data_pid == 5) { } else if (poll_data_pid == 5) {
transmit_can_frame(&KIA64_7E4_id5, can_config.battery); transmit_can_frame(&KIA64_7E4_id5, can_interface);
} else if (poll_data_pid == 6) { } else if (poll_data_pid == 6) {
transmit_can_frame(&KIA64_7E4_id6, can_config.battery); transmit_can_frame(&KIA64_7E4_id6, can_interface);
} }
} }
break; break;
@ -340,7 +200,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
case 0x10: //"PID Header" case 0x10: //"PID Header"
if (rx_frame.data.u8[4] == poll_data_pid) { if (rx_frame.data.u8[4] == poll_data_pid) {
transmit_can_frame(&KIA64_7E4_ack, transmit_can_frame(&KIA64_7E4_ack,
can_config.battery); //Send ack to BMS if the same frame is sent as polled can_interface); //Send ack to BMS if the same frame is sent as polled
} }
break; break;
case 0x21: //First frame in PID group case 0x21: //First frame in PID group
@ -511,7 +371,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
} }
} }
//Map all cell voltages to the global array //Map all cell voltages to the global array
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_mv, 98 * sizeof(uint16_t)); memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_mv, 98 * sizeof(uint16_t));
//Update number of cells //Update number of cells
update_number_of_cells(); update_number_of_cells();
break; break;
@ -533,398 +393,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
} }
} }
#ifdef DOUBLE_BATTERY void KiaHyundai64Battery::transmit_can(unsigned long currentMillis) {
void update_values_battery2() { // Handle the values coming in from battery #2
/* Start with mapping all values */
datalayer.battery2.status.real_soc = (battery2_SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
datalayer.battery2.status.soh_pptt = (battery2_batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
datalayer.battery2.status.voltage_dV = battery2_batteryVoltage; //value is *10 (3700 = 370.0)
datalayer.battery2.status.current_dA = -battery2_batteryAmps; //value is *10 (150 = 15.0) , invert the sign
datalayer.battery2.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery2.status.real_soc) / 10000) * datalayer.battery2.info.total_capacity_Wh);
datalayer.battery2.status.max_charge_power_W = battery2_allowedChargePower * 10;
datalayer.battery2.status.max_discharge_power_W = battery2_allowedDischargePower * 10;
datalayer.battery2.status.temperature_min_dC =
(int8_t)battery2_temperatureMin * 10; //Increase decimals, 17C -> 17.0C
datalayer.battery2.status.temperature_max_dC =
(int8_t)battery2_temperatureMax * 10; //Increase decimals, 18C -> 18.0C
datalayer.battery2.status.cell_max_voltage_mV = battery2_CellVoltMax_mV;
datalayer.battery2.status.cell_min_voltage_mV = battery2_CellVoltMin_mV;
if (battery2_waterleakageSensor == 0) {
set_event(EVENT_WATER_INGRESS, 0);
}
if (battery2_leadAcidBatteryVoltage < 110) {
set_event(EVENT_12V_LOW, leadAcidBatteryVoltage);
}
// Update webserver datalayer
datalayer_extended.KiaHyundai64.battery2_total_cell_count = datalayer.battery2.info.number_of_cells;
datalayer_extended.KiaHyundai64.battery2_battery_12V = battery2_leadAcidBatteryVoltage;
datalayer_extended.KiaHyundai64.battery2_waterleakageSensor = battery2_waterleakageSensor;
datalayer_extended.KiaHyundai64.battery2_temperature_water_inlet = battery2_temperature_water_inlet;
datalayer_extended.KiaHyundai64.battery2_powerRelayTemperature = battery2_powerRelayTemperature * 2;
datalayer_extended.KiaHyundai64.battery2_batteryManagementMode = battery2_batteryManagementMode;
datalayer_extended.KiaHyundai64.battery2_BMS_ign = battery2_BMS_ign;
datalayer_extended.KiaHyundai64.battery2_batteryRelay = battery2_batteryRelay;
//Perform logging if configured to do so
#ifdef DEBUG_LOG
logging.println(); //sepatator
logging.println("Values from battery: ");
logging.print("SOC BMS: ");
logging.print((uint16_t)battery2_SOC_BMS / 10.0, 1);
logging.print("% | SOC Display: ");
logging.print((uint16_t)battery2_SOC_Display / 10.0, 1);
logging.print("% | SOH ");
logging.print((uint16_t)battery2_batterySOH / 10.0, 1);
logging.println("%");
logging.print((int16_t)battery2_batteryAmps / 10.0, 1);
logging.print(" Amps | ");
logging.print((uint16_t)battery2_batteryVoltage / 10.0, 1);
logging.print(" Volts | ");
logging.print((int16_t)datalayer.battery2.status.active_power_W);
logging.println(" Watts");
logging.print("Allowed Charge ");
logging.print((uint16_t)battery2_allowedChargePower * 10);
logging.print(" W | Allowed Discharge ");
logging.print((uint16_t)battery2_allowedDischargePower * 10);
logging.println(" W");
logging.print("MaxCellVolt ");
logging.print(battery2_CellVoltMax_mV);
logging.print(" mV No ");
logging.print(battery2_CellVmaxNo);
logging.print(" | MinCellVolt ");
logging.print(battery2_CellVoltMin_mV);
logging.print(" mV No ");
logging.println(battery2_CellVminNo);
logging.print("TempHi ");
logging.print((int16_t)battery2_temperatureMax);
logging.print("°C TempLo ");
logging.print((int16_t)battery2_temperatureMin);
logging.print("°C WaterInlet ");
logging.print((int8_t)battery2_temperature_water_inlet);
logging.print("°C PowerRelay ");
logging.print((int8_t)battery2_powerRelayTemperature * 2);
logging.println("°C");
logging.print("Aux12volt: ");
logging.print((int16_t)battery2_leadAcidBatteryVoltage / 10.0, 1);
logging.println("V | ");
logging.print("BmsManagementMode ");
logging.print((uint8_t)battery2_batteryManagementMode, BIN);
if (bitRead((uint8_t)battery2_BMS_ign, 2) == 1) {
logging.print(" | BmsIgnition ON");
} else {
logging.print(" | BmsIgnition OFF");
}
if (bitRead((uint8_t)battery2_batteryRelay, 0) == 1) {
logging.print(" | PowerRelay ON");
} else {
logging.print(" | PowerRelay OFF");
}
logging.print(" | Inverter ");
logging.print(battery2_inverterVoltage);
logging.println(" Volts");
#endif
}
void update_number_of_cells_battery2() {
//If we have cell values and number_of_cells not initialized yet
if (battery2_cellvoltages_mv[0] > 0 && datalayer.battery2.info.number_of_cells == 0) {
// Check if we have 98S or 90S battery
if (datalayer.battery2.status.cell_voltages_mV[97] > 0) {
datalayer.battery2.info.number_of_cells = 98;
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_98S_DV;
datalayer.battery2.info.total_capacity_Wh = 64000;
} else {
datalayer.battery2.info.number_of_cells = 90;
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_90S_DV;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV;
datalayer.battery2.info.total_capacity_Wh = 40000;
}
}
}
void handle_incoming_can_frame_battery2(CAN_frame rx_frame) {
switch (rx_frame.ID) {
case 0x4DE:
battery2_startedUp = true;
break;
case 0x542: //BMS SOC
battery2_startedUp = true;
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
battery2_SOC_Display = rx_frame.data.u8[0] * 5; //100% = 200 ( 200 * 5 = 1000 )
break;
case 0x594:
battery2_startedUp = true;
battery2_allowedChargePower = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
battery2_allowedDischargePower = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
battery2_SOC_BMS = rx_frame.data.u8[5] * 5; //100% = 200 ( 200 * 5 = 1000 )
break;
case 0x595:
battery2_startedUp = true;
battery2_batteryVoltage = (rx_frame.data.u8[7] << 8) + rx_frame.data.u8[6];
battery2_batteryAmps = (rx_frame.data.u8[5] << 8) + rx_frame.data.u8[4];
if (battery2_counter_200 > 3) {
//KIA_HYUNDAI_524.data.u8[0] = (uint8_t)(battery2_batteryVoltage / 10);
//KIA_HYUNDAI_524.data.u8[1] = (uint8_t)((battery2_batteryVoltage / 10) >> 8);
} //VCU measured voltage sent back to bms (Not required since battery1 writes this)
break;
case 0x596:
battery2_startedUp = true;
battery2_leadAcidBatteryVoltage = rx_frame.data.u8[1]; //12v Battery Volts
battery2_temperatureMin = rx_frame.data.u8[6]; //Lowest temp in battery
battery2_temperatureMax = rx_frame.data.u8[7]; //Highest temp in battery
break;
case 0x598:
battery2_startedUp = true;
break;
case 0x5D5:
battery2_startedUp = true;
battery2_waterleakageSensor =
rx_frame.data.u8[3]; //Water sensor inside pack, value 164 is no water --> 0 is short
battery2_powerRelayTemperature = rx_frame.data.u8[7];
break;
case 0x5D8:
battery2_startedUp = true;
//PID data is polled after last message sent from battery every other time:
if (battery2_holdPidCounter == true) {
battery2_holdPidCounter = false;
} else {
battery2_holdPidCounter = true;
if (battery2_poll_data_pid >= 6) { //polling one of six PIDs at 100ms*2, resolution = 1200ms
battery2_poll_data_pid = 0;
}
battery2_poll_data_pid++;
if (battery2_poll_data_pid == 1) {
transmit_can_frame(&KIA64_7E4_id1, can_config.battery_double);
} else if (battery2_poll_data_pid == 2) {
transmit_can_frame(&KIA64_7E4_id2, can_config.battery_double);
} else if (battery2_poll_data_pid == 3) {
transmit_can_frame(&KIA64_7E4_id3, can_config.battery_double);
} else if (battery2_poll_data_pid == 4) {
transmit_can_frame(&KIA64_7E4_id4, can_config.battery_double);
} else if (battery2_poll_data_pid == 5) {
transmit_can_frame(&KIA64_7E4_id5, can_config.battery_double);
} else if (battery2_poll_data_pid == 6) {
transmit_can_frame(&KIA64_7E4_id6, can_config.battery_double);
}
}
break;
case 0x7EC: //Data From polled PID group, BigEndian
switch (rx_frame.data.u8[0]) {
case 0x10: //"PID Header"
if (rx_frame.data.u8[4] == battery2_poll_data_pid) {
transmit_can_frame(&KIA64_7E4_ack,
can_config.battery_double); //Send ack to BMS if the same frame is sent as polled
}
break;
case 0x21: //First frame in PID group
if (battery2_poll_data_pid == 1) {
battery2_batteryRelay = rx_frame.data.u8[7];
} else if (battery2_poll_data_pid == 2) {
battery2_cellvoltages_mv[0] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[1] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[2] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[3] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[4] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[5] = (rx_frame.data.u8[7] * 20);
} else if (battery2_poll_data_pid == 3) {
battery2_cellvoltages_mv[32] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[33] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[34] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[35] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[36] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[37] = (rx_frame.data.u8[7] * 20);
} else if (battery2_poll_data_pid == 4) {
battery2_cellvoltages_mv[64] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[65] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[66] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[67] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[68] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[69] = (rx_frame.data.u8[7] * 20);
}
break;
case 0x22: //Second datarow in PID group
if (battery2_poll_data_pid == 2) {
battery2_cellvoltages_mv[6] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[7] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[8] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[9] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[10] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[11] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[12] = (rx_frame.data.u8[7] * 20);
} else if (battery2_poll_data_pid == 3) {
battery2_cellvoltages_mv[38] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[39] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[40] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[41] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[42] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[43] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[44] = (rx_frame.data.u8[7] * 20);
} else if (battery2_poll_data_pid == 4) {
battery2_cellvoltages_mv[70] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[71] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[72] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[73] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[74] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[75] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[76] = (rx_frame.data.u8[7] * 20);
} else if (battery2_poll_data_pid == 6) {
battery2_batteryManagementMode = rx_frame.data.u8[5];
}
break;
case 0x23: //Third datarow in PID group
if (battery2_poll_data_pid == 1) {
battery2_temperature_water_inlet = rx_frame.data.u8[6];
battery2_CellVoltMax_mV = (rx_frame.data.u8[7] * 20); //(volts *50) *20 =mV
} else if (battery2_poll_data_pid == 2) {
battery2_cellvoltages_mv[13] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[14] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[15] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[16] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[17] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[18] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[19] = (rx_frame.data.u8[7] * 20);
} else if (battery2_poll_data_pid == 3) {
battery2_cellvoltages_mv[45] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[46] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[47] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[48] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[49] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[50] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[51] = (rx_frame.data.u8[7] * 20);
} else if (battery2_poll_data_pid == 4) {
battery2_cellvoltages_mv[77] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[78] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[79] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[80] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[81] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[82] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[83] = (rx_frame.data.u8[7] * 20);
} else if (battery2_poll_data_pid == 5) {
battery2_heatertemp = rx_frame.data.u8[7];
}
break;
case 0x24: //Fourth datarow in PID group
if (battery2_poll_data_pid == 1) {
battery2_CellVmaxNo = rx_frame.data.u8[1];
battery2_CellVminNo = rx_frame.data.u8[3];
battery2_CellVoltMin_mV = (rx_frame.data.u8[2] * 20); //(volts *50) *20 =mV
} else if (battery2_poll_data_pid == 2) {
battery2_cellvoltages_mv[20] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[21] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[22] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[23] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[24] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[25] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[26] = (rx_frame.data.u8[7] * 20);
} else if (battery2_poll_data_pid == 3) {
battery2_cellvoltages_mv[52] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[53] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[54] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[55] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[56] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[57] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[58] = (rx_frame.data.u8[7] * 20);
} else if (battery2_poll_data_pid == 4) {
battery2_cellvoltages_mv[84] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[85] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[86] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[87] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[88] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[89] = (rx_frame.data.u8[6] * 20);
if (rx_frame.data.u8[7] > 4) { // Data only valid on 98S
battery2_cellvoltages_mv[90] = (rx_frame.data.u8[7] * 20); // Perform extra checks
}
} else if (battery2_poll_data_pid == 5) {
battery2_batterySOH = ((rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3]);
}
break;
case 0x25: //Fifth datarow in PID group
if (battery2_poll_data_pid == 2) {
battery2_cellvoltages_mv[27] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[28] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[29] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[30] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[31] = (rx_frame.data.u8[5] * 20);
} else if (battery2_poll_data_pid == 3) {
battery2_cellvoltages_mv[59] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[60] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[61] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[62] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[63] = (rx_frame.data.u8[5] * 20);
} else if (battery2_poll_data_pid == 4) { // Data only valid on 98S
if (rx_frame.data.u8[1] > 4) { // Perform extra checks
battery2_cellvoltages_mv[91] = (rx_frame.data.u8[1] * 20);
}
if (rx_frame.data.u8[2] > 4) { // Perform extra checks
battery2_cellvoltages_mv[92] = (rx_frame.data.u8[2] * 20);
}
if (rx_frame.data.u8[3] > 4) { // Perform extra checks
battery2_cellvoltages_mv[93] = (rx_frame.data.u8[3] * 20);
}
if (rx_frame.data.u8[4] > 4) { // Perform extra checks
battery2_cellvoltages_mv[94] = (rx_frame.data.u8[4] * 20);
}
if (rx_frame.data.u8[5] > 4) { // Perform extra checks
battery2_cellvoltages_mv[95] = (rx_frame.data.u8[5] * 20);
}
} else if (battery2_poll_data_pid == 5) { // Data only valid on 98S
if (rx_frame.data.u8[4] > 4) { // Perform extra checks
battery2_cellvoltages_mv[96] = (rx_frame.data.u8[4] * 20);
}
if (rx_frame.data.u8[5] > 4) { // Perform extra checks
battery2_cellvoltages_mv[97] = (rx_frame.data.u8[5] * 20);
}
}
break;
case 0x26: //Sixth datarow in PID group
//We have read all cells, check that content is valid:
for (uint8_t i = 85; i < 97; ++i) {
if (battery2_cellvoltages_mv[i] < 300) { // Zero the value if it's below 300
battery2_cellvoltages_mv[i] = 0; // Some packs incorrectly report the last unpopulated cells as 20-60mV
}
}
//Map all cell voltages to the global array
memcpy(datalayer.battery2.status.cell_voltages_mV, battery2_cellvoltages_mv, 98 * sizeof(uint16_t));
//Update number of cells
update_number_of_cells_battery2();
break;
case 0x27: //Seventh datarow in PID group
if (battery2_poll_data_pid == 1) {
battery2_BMS_ign = rx_frame.data.u8[6];
battery2_inverterVoltageFrameHigh = rx_frame.data.u8[7];
}
break;
case 0x28: //Eighth datarow in PID group
if (battery2_poll_data_pid == 1) {
battery2_inverterVoltage = (battery2_inverterVoltageFrameHigh << 8) + rx_frame.data.u8[1];
}
break;
}
break;
default:
break;
}
}
#endif //DOUBLE_BATTERY
void transmit_can_battery(unsigned long currentMillis) {
if (!startedUp) { if (!startedUp) {
return; // Don't send any CAN messages towards battery until it has started up return; // Don't send any CAN messages towards battery until it has started up
@ -934,21 +403,19 @@ void transmit_can_battery(unsigned long currentMillis) {
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis; previousMillis100 = currentMillis;
transmit_can_frame(&KIA64_553, can_config.battery); if (contactor_closing_allowed == nullptr || *contactor_closing_allowed) {
transmit_can_frame(&KIA64_57F, can_config.battery); transmit_can_frame(&KIA64_553, can_interface);
transmit_can_frame(&KIA64_2A1, can_config.battery); transmit_can_frame(&KIA64_57F, can_interface);
#ifdef DOUBLE_BATTERY transmit_can_frame(&KIA64_2A1, can_interface);
if (battery2_startedUp && datalayer.system.status.battery2_allows_contactor_closing) {
transmit_can_frame(&KIA64_553, can_config.battery_double);
transmit_can_frame(&KIA64_57F, can_config.battery_double);
transmit_can_frame(&KIA64_2A1, can_config.battery_double);
} }
#endif // DOUBLE_BATTERY
} }
// Send 10ms CAN Message // Send 10ms CAN Message
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
previousMillis10 = currentMillis; previousMillis10 = currentMillis;
if (contactor_closing_allowed == nullptr || *contactor_closing_allowed) {
switch (counter_200) { switch (counter_200) {
case 0: case 0:
KIA_HYUNDAI_200.data.u8[5] = 0x17; KIA_HYUNDAI_200.data.u8[5] = 0x17;
@ -989,81 +456,24 @@ void transmit_can_battery(unsigned long currentMillis) {
break; break;
} }
transmit_can_frame(&KIA_HYUNDAI_200, can_config.battery); transmit_can_frame(&KIA_HYUNDAI_200, can_interface);
transmit_can_frame(&KIA_HYUNDAI_523, can_interface);
transmit_can_frame(&KIA_HYUNDAI_523, can_config.battery); transmit_can_frame(&KIA_HYUNDAI_524, can_interface);
transmit_can_frame(&KIA_HYUNDAI_524, can_config.battery);
#ifdef DOUBLE_BATTERY
if (battery2_startedUp && datalayer.system.status.battery2_allows_contactor_closing) {
switch (counter_200_2) {
case 0:
KIA_HYUNDAI_200_2.data.u8[5] = 0x17;
++counter_200_2;
break;
case 1:
KIA_HYUNDAI_200_2.data.u8[5] = 0x57;
++counter_200_2;
break;
case 2:
KIA_HYUNDAI_200_2.data.u8[5] = 0x97;
++counter_200_2;
break;
case 3:
KIA_HYUNDAI_200_2.data.u8[5] = 0xD7;
++counter_200_2;
break;
case 4:
KIA_HYUNDAI_200_2.data.u8[3] = 0x10;
KIA_HYUNDAI_200_2.data.u8[5] = 0xFF;
++counter_200_2;
break;
case 5:
KIA_HYUNDAI_200_2.data.u8[5] = 0x3B;
++counter_200_2;
break;
case 6:
KIA_HYUNDAI_200_2.data.u8[5] = 0x7B;
++counter_200_2;
break;
case 7:
KIA_HYUNDAI_200_2.data.u8[5] = 0xBB;
++counter_200_2;
break;
case 8:
KIA_HYUNDAI_200_2.data.u8[5] = 0xFB;
counter_200_2 = 5;
break;
} }
transmit_can_frame(&KIA_HYUNDAI_200_2, can_config.battery_double);
transmit_can_frame(&KIA_HYUNDAI_523, can_config.battery_double);
transmit_can_frame(&KIA_HYUNDAI_524, can_config.battery_double);
}
#endif // DOUBLE_BATTERY
} }
} }
void setup_battery(void) { // Performs one time setup at startup void KiaHyundai64Battery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai 64/40kWh battery", 63); strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai 64/40kWh battery", 63);
datalayer.system.info.battery_protocol[63] = '\0'; 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.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.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; 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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true; if (allows_contactor_closing) {
#ifdef DOUBLE_BATTERY *allows_contactor_closing = true;
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_mV = datalayer.battery.info.max_cell_voltage_mV;
datalayer.battery2.info.min_cell_voltage_mV = datalayer.battery.info.min_cell_voltage_mV;
datalayer.battery2.info.max_cell_voltage_deviation_mV = datalayer.battery.info.max_cell_voltage_deviation_mV;
#endif //DOUBLE_BATTERY
} }
#endif #endif

View file

@ -1,19 +1,164 @@
#ifndef KIA_HYUNDAI_64_BATTERY_H #ifndef KIA_HYUNDAI_64_BATTERY_H
#define KIA_HYUNDAI_64_BATTERY_H #define KIA_HYUNDAI_64_BATTERY_H
#include <Arduino.h> #include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../include.h" #include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED #define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_98S_DV 4110 //5000 = 500.0V #define SELECTED_BATTERY_CLASS KiaHyundai64Battery
#define MIN_PACK_VOLTAGE_98S_DV 2800
#define MAX_PACK_VOLTAGE_90S_DV 3870 class KiaHyundai64Battery : public CanBattery {
#define MIN_PACK_VOLTAGE_90S_DV 2250 public:
#define MAX_CELL_DEVIATION_MV 150 // Use this constructor for the second battery.
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value KiaHyundai64Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_KIAHYUNDAI64* extended_ptr,
#define MIN_CELL_VOLTAGE_MV 2950 //Battery is put into emergency stop if one cell goes below this value bool* contactor_closing_allowed_ptr, int targetCan) {
datalayer_battery = datalayer_ptr;
contactor_closing_allowed = contactor_closing_allowed_ptr;
allows_contactor_closing = nullptr;
can_interface = targetCan;
datalayer_battery_extended = extended_ptr;
}
// Use the default constructor to create the first or single battery.
KiaHyundai64Battery() {
datalayer_battery = &datalayer.battery;
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
contactor_closing_allowed = nullptr;
can_interface = can_config.battery;
datalayer_battery_extended = &datalayer_extended.KiaHyundai64;
}
virtual void setup(void);
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
private:
DATALAYER_BATTERY_TYPE* datalayer_battery;
DATALAYER_INFO_KIAHYUNDAI64* datalayer_battery_extended;
// If not null, this battery decides when the contactor can be closed and writes the value here.
bool* allows_contactor_closing;
// If not null, this battery listens to this boolean to determine whether contactor closing is allowed
bool* contactor_closing_allowed;
int can_interface;
void setup_battery(void);
void update_number_of_cells(); void update_number_of_cells();
void transmit_can_frame(CAN_frame* tx_frame, int interface);
static const int MAX_PACK_VOLTAGE_98S_DV = 4110; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_98S_DV = 2800;
static const int MAX_PACK_VOLTAGE_90S_DV = 3870;
static const int MIN_PACK_VOLTAGE_90S_DV = 2250;
static const int MAX_CELL_DEVIATION_MV = 150;
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2950; //Battery is put into emergency stop if one cell goes below this value
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
unsigned long previousMillis10 = 0; // will store last time a 10s CAN Message was send
uint16_t soc_calculated = 0;
uint16_t SOC_BMS = 0;
uint16_t SOC_Display = 0;
uint16_t batterySOH = 1000;
uint16_t CellVoltMax_mV = 3700;
uint16_t CellVoltMin_mV = 3700;
uint16_t allowedDischargePower = 0;
uint16_t allowedChargePower = 0;
uint16_t batteryVoltage = 0;
uint16_t inverterVoltageFrameHigh = 0;
uint16_t inverterVoltage = 0;
uint16_t cellvoltages_mv[98];
int16_t leadAcidBatteryVoltage = 120;
int16_t batteryAmps = 0;
int16_t temperatureMax = 0;
int16_t temperatureMin = 0;
int16_t poll_data_pid = 0;
bool holdPidCounter = false;
uint8_t CellVmaxNo = 0;
uint8_t CellVminNo = 0;
uint8_t batteryManagementMode = 0;
uint8_t BMS_ign = 0;
uint8_t batteryRelay = 0;
uint8_t waterleakageSensor = 164;
uint8_t counter_200 = 0;
int8_t temperature_water_inlet = 0;
int8_t heatertemp = 0;
int8_t powerRelayTemperature = 0;
bool startedUp = false;
CAN_frame KIA_HYUNDAI_200 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x200,
.data = {0x00, 0x80, 0xD8, 0x04, 0x00, 0x17, 0xD0, 0x00}};
CAN_frame KIA_HYUNDAI_523 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x523,
.data = {0x08, 0x38, 0x36, 0x36, 0x33, 0x34, 0x00, 0x01}};
CAN_frame KIA_HYUNDAI_524 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x524,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
//553 Needed frame 200ms
CAN_frame KIA64_553 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x553,
.data = {0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00}};
//57F Needed frame 100ms
CAN_frame KIA64_57F = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x57F,
.data = {0x80, 0x0A, 0x72, 0x00, 0x00, 0x00, 0x00, 0x72}};
//Needed frame 100ms
CAN_frame KIA64_2A1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2A1,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame KIA64_7E4_id1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 01
CAN_frame KIA64_7E4_id2 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 02
CAN_frame KIA64_7E4_id3 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 03
CAN_frame KIA64_7E4_id4 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 04
CAN_frame KIA64_7E4_id5 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 05
CAN_frame KIA64_7E4_id6 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 06
CAN_frame KIA64_7E4_ack = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned
};
#endif #endif

View file

@ -74,7 +74,7 @@ void NissanLeafBattery::
datalayer_battery->status.max_charge_power_W = (battery_Charge_Power_Limit * 1000); //kW to W datalayer_battery->status.max_charge_power_W = (battery_Charge_Power_Limit * 1000); //kW to W
//Allow contactors to close //Allow contactors to close
if (battery_can_alive) { if (battery_can_alive && allows_contactor_closing) {
*allows_contactor_closing = true; *allows_contactor_closing = true;
} }

View file

@ -19,10 +19,9 @@
class NissanLeafBattery : public CanBattery { class NissanLeafBattery : public CanBattery {
public: public:
// Use this constructor for the second battery. // Use this constructor for the second battery.
NissanLeafBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* allows_contactor_closing_ptr, NissanLeafBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_NISSAN_LEAF* extended, int targetCan) {
DATALAYER_INFO_NISSAN_LEAF* extended, int targetCan) {
datalayer_battery = datalayer_ptr; datalayer_battery = datalayer_ptr;
allows_contactor_closing = allows_contactor_closing_ptr; allows_contactor_closing = nullptr;
datalayer_nissan = extended; datalayer_nissan = extended;
can_interface = targetCan; can_interface = targetCan;
@ -48,6 +47,8 @@ class NissanLeafBattery : public CanBattery {
DATALAYER_BATTERY_TYPE* datalayer_battery; DATALAYER_BATTERY_TYPE* datalayer_battery;
DATALAYER_INFO_NISSAN_LEAF* datalayer_nissan; DATALAYER_INFO_NISSAN_LEAF* datalayer_nissan;
// If not null, this battery decides when the contactor can be closed and writes the value here.
bool* allows_contactor_closing; bool* allows_contactor_closing;
int can_interface; int can_interface;

View file

@ -1,89 +1,44 @@
#include "../include.h" #include "../include.h"
#ifdef PYLON_BATTERY #ifdef PYLON_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "PYLON-BATTERY.h" #include "PYLON-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */ void PylonBattery::update_values() {
static unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
//Actual content messages datalayer_battery->status.real_soc = (SOC * 100); //increase SOC range from 0-100 -> 100.00
CAN_frame PYLON_3010 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3010,
.data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame PYLON_8200 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x8200,
.data = {0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame PYLON_8210 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x8210,
.data = {0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame PYLON_4200 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x4200,
.data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
static int16_t celltemperature_max_dC = 0; datalayer_battery->status.soh_pptt = (SOH * 100); //Increase decimals from 100% -> 100.00%
static int16_t celltemperature_min_dC = 0;
static int16_t current_dA = 0;
static uint16_t voltage_dV = 0;
static uint16_t cellvoltage_max_mV = 3700;
static uint16_t cellvoltage_min_mV = 3700;
static uint16_t charge_cutoff_voltage = 0;
static uint16_t discharge_cutoff_voltage = 0;
static int16_t max_charge_current = 0;
static int16_t max_discharge_current = 0;
static uint8_t ensemble_info_ack = 0;
static uint8_t battery_module_quantity = 0;
static uint8_t battery_modules_in_series = 0;
static uint8_t cell_quantity_in_module = 0;
static uint8_t voltage_level = 0;
static uint8_t ah_number = 0;
static uint8_t SOC = 50;
static uint8_t SOH = 100;
static uint8_t charge_forbidden = 0;
static uint8_t discharge_forbidden = 0;
void update_values_battery() { datalayer_battery->status.voltage_dV = voltage_dV; //value is *10 (3700 = 370.0)
datalayer.battery.status.real_soc = (SOC * 100); //increase SOC range from 0-100 -> 100.00 datalayer_battery->status.current_dA = current_dA; //value is *10 (150 = 15.0) , invert the sign
datalayer.battery.status.soh_pptt = (SOH * 100); //Increase decimals from 100% -> 100.00% datalayer_battery->status.max_charge_power_W = (max_charge_current * (voltage_dV / 10));
datalayer.battery.status.voltage_dV = voltage_dV; //value is *10 (3700 = 370.0) datalayer_battery->status.max_discharge_power_W = (-max_discharge_current * (voltage_dV / 10));
datalayer.battery.status.current_dA = current_dA; //value is *10 (150 = 15.0) , invert the sign 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_charge_power_W = (max_charge_current * (voltage_dV / 10)); datalayer_battery->status.cell_max_voltage_mV = cellvoltage_max_mV;
datalayer_battery->status.cell_voltages_mV[0] = cellvoltage_max_mV;
datalayer.battery.status.max_discharge_power_W = (-max_discharge_current * (voltage_dV / 10)); datalayer_battery->status.cell_min_voltage_mV = cellvoltage_min_mV;
datalayer_battery->status.cell_voltages_mV[1] = cellvoltage_min_mV;
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>( datalayer_battery->status.temperature_min_dC = celltemperature_min_dC;
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
datalayer.battery.status.cell_max_voltage_mV = cellvoltage_max_mV; datalayer_battery->status.temperature_max_dC = celltemperature_max_dC;
datalayer.battery.status.cell_voltages_mV[0] = cellvoltage_max_mV;
datalayer.battery.status.cell_min_voltage_mV = cellvoltage_min_mV; datalayer_battery->info.max_design_voltage_dV = charge_cutoff_voltage;
datalayer.battery.status.cell_voltages_mV[1] = cellvoltage_min_mV;
datalayer.battery.status.temperature_min_dC = celltemperature_min_dC; datalayer_battery->info.min_design_voltage_dV = discharge_cutoff_voltage;
datalayer.battery.status.temperature_max_dC = celltemperature_max_dC;
datalayer.battery.info.max_design_voltage_dV = charge_cutoff_voltage;
datalayer.battery.info.min_design_voltage_dV = discharge_cutoff_voltage;
} }
void handle_incoming_can_frame_battery(CAN_frame rx_frame) { void PylonBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) { switch (rx_frame.ID) {
case 0x7310: case 0x7310:
case 0x7311: case 0x7311:
@ -158,7 +113,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
} }
} }
void transmit_can_battery(unsigned long currentMillis) { void PylonBattery::transmit_can(unsigned long currentMillis) {
// Send 1s CAN Message // Send 1s CAN Message
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
previousMillis1000 = currentMillis; previousMillis1000 = currentMillis;
@ -168,170 +123,26 @@ void transmit_can_battery(unsigned long currentMillis) {
transmit_can_frame(&PYLON_8200, can_config.battery); // Control device quit sleep status transmit_can_frame(&PYLON_8200, can_config.battery); // Control device quit sleep status
transmit_can_frame(&PYLON_8210, can_config.battery); // Charge command transmit_can_frame(&PYLON_8210, can_config.battery); // Charge command
#ifdef DOUBLE_BATTERY
transmit_can_frame(&PYLON_3010, can_config.battery_double); // Heartbeat
transmit_can_frame(&PYLON_4200, can_config.battery_double); // Ensemble OR System equipment info, depends on frame0
transmit_can_frame(&PYLON_8200, can_config.battery_double); // Control device quit sleep status
transmit_can_frame(&PYLON_8210, can_config.battery_double); // Charge command
#endif //DOUBLE_BATTERY
if (ensemble_info_ack) { if (ensemble_info_ack) {
PYLON_4200.data.u8[0] = 0x00; //Request system equipment info PYLON_4200.data.u8[0] = 0x00; //Request system equipment info
} }
} }
} }
#ifdef DOUBLE_BATTERY void PylonBattery::setup(void) { // Performs one time setup at startup
static int16_t battery2_celltemperature_max_dC = 0;
static int16_t battery2_celltemperature_min_dC = 0;
static int16_t battery2_current_dA = 0;
static uint16_t battery2_voltage_dV = 0;
static uint16_t battery2_cellvoltage_max_mV = 3700;
static uint16_t battery2_cellvoltage_min_mV = 3700;
static uint16_t battery2_charge_cutoff_voltage = 0;
static uint16_t battery2_discharge_cutoff_voltage = 0;
static int16_t battery2_max_charge_current = 0;
static int16_t battery2_max_discharge_current = 0;
static uint8_t battery2_ensemble_info_ack = 0;
static uint8_t battery2_module_quantity = 0;
static uint8_t battery2_modules_in_series = 0;
static uint8_t battery2_cell_quantity_in_module = 0;
static uint8_t battery2_voltage_level = 0;
static uint8_t battery2_ah_number = 0;
static uint8_t battery2_SOC = 0;
static uint8_t battery2_SOH = 0;
static uint8_t battery2_charge_forbidden = 0;
static uint8_t battery2_discharge_forbidden = 0;
void update_values_battery2() {
datalayer.battery2.status.real_soc = (battery2_SOC * 100); //increase SOC range from 0-100 -> 100.00
datalayer.battery2.status.soh_pptt = (battery2_SOH * 100); //Increase decimals from 100% -> 100.00%
datalayer.battery2.status.voltage_dV = battery2_voltage_dV; //value is *10 (3700 = 370.0)
datalayer.battery2.status.current_dA = battery2_current_dA; //value is *10 (150 = 15.0) , invert the sign
datalayer.battery2.status.max_charge_power_W = (battery2_max_charge_current * (battery2_voltage_dV / 10));
datalayer.battery2.status.max_discharge_power_W = (-battery2_max_discharge_current * (battery2_voltage_dV / 10));
datalayer.battery2.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery2.status.real_soc) / 10000) * datalayer.battery2.info.total_capacity_Wh);
datalayer.battery2.status.cell_max_voltage_mV = battery2_cellvoltage_max_mV;
datalayer.battery2.status.cell_voltages_mV[0] = battery2_cellvoltage_max_mV;
datalayer.battery2.status.cell_min_voltage_mV = battery2_cellvoltage_min_mV;
datalayer.battery2.status.cell_voltages_mV[1] = battery2_cellvoltage_min_mV;
datalayer.battery2.status.temperature_min_dC = battery2_celltemperature_min_dC;
datalayer.battery2.status.temperature_max_dC = battery2_celltemperature_max_dC;
datalayer.battery2.info.max_design_voltage_dV = battery2_charge_cutoff_voltage;
datalayer.battery2.info.min_design_voltage_dV = battery2_discharge_cutoff_voltage;
datalayer.battery2.info.number_of_cells = datalayer.battery.info.number_of_cells;
}
void handle_incoming_can_frame_battery2(CAN_frame rx_frame) {
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) {
case 0x7310:
case 0x7311:
battery2_ensemble_info_ack = true;
// This message contains software/hardware version info. No interest to us
break;
case 0x7320:
case 0x7321:
battery2_ensemble_info_ack = true;
battery2_module_quantity = rx_frame.data.u8[0];
battery2_modules_in_series = rx_frame.data.u8[2];
battery2_cell_quantity_in_module = rx_frame.data.u8[3];
battery2_voltage_level = rx_frame.data.u8[4];
battery2_ah_number = rx_frame.data.u8[6];
break;
case 0x4210:
case 0x4211:
battery2_voltage_dV = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
battery2_current_dA = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 30000;
battery2_SOC = rx_frame.data.u8[6];
battery2_SOH = rx_frame.data.u8[7];
break;
case 0x4220:
case 0x4221:
battery2_charge_cutoff_voltage = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
battery2_discharge_cutoff_voltage = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
battery2_max_charge_current = (((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) * 0.1) - 3000;
battery2_max_discharge_current = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) * 0.1) - 3000;
break;
case 0x4230:
case 0x4231:
battery2_cellvoltage_max_mV = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
battery2_cellvoltage_min_mV = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
break;
case 0x4240:
case 0x4241:
battery2_celltemperature_max_dC = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) - 1000;
battery2_celltemperature_min_dC = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 1000;
break;
case 0x4250:
case 0x4251:
//Byte0 Basic Status
//Byte1-2 Cycle Period
//Byte3 Error
//Byte4-5 Alarm
//Byte6-7 Protection
break;
case 0x4260:
case 0x4261:
//Byte0-1 Module Max Voltage
//Byte2-3 Module Min Voltage
//Byte4-5 Module Max. Voltage Number
//Byte6-7 Module Min. Voltage Number
break;
case 0x4270:
case 0x4271:
//Byte0-1 Module Max. Temperature
//Byte2-3 Module Min. Temperature
//Byte4-5 Module Max. Temperature Number
//Byte6-7 Module Min. Temperature Number
break;
case 0x4280:
case 0x4281:
battery2_charge_forbidden = rx_frame.data.u8[0];
battery2_discharge_forbidden = rx_frame.data.u8[1];
break;
case 0x4290:
case 0x4291:
break;
default:
break;
}
}
#endif //DOUBLE_BATTERY
void setup_battery(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", 63); strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 2; datalayer_battery->info.number_of_cells = 2;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; 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.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; 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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
#ifdef DOUBLE_BATTERY datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.battery2.info.number_of_cells = datalayer.battery.info.number_of_cells;
datalayer.battery2.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV; if (allows_contactor_closing) {
datalayer.battery2.info.min_design_voltage_dV = datalayer.battery.info.min_design_voltage_dV; *allows_contactor_closing = true;
datalayer.battery2.info.max_cell_voltage_mV = datalayer.battery.info.max_cell_voltage_mV; }
datalayer.battery2.info.min_cell_voltage_mV = datalayer.battery.info.min_cell_voltage_mV;
datalayer.battery2.info.max_cell_voltage_deviation_mV = datalayer.battery.info.max_cell_voltage_deviation_mV;
#endif //DOUBLE_BATTERY
} }
#endif #endif

View file

@ -1,18 +1,99 @@
#ifndef PYLON_BATTERY_H #ifndef PYLON_BATTERY_H
#define PYLON_BATTERY_H #define PYLON_BATTERY_H
#include <Arduino.h> #include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../include.h" #include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED #define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS PylonBattery
class PylonBattery : public CanBattery {
public:
// Use this constructor for the second battery.
PylonBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* contactor_closing_allowed_ptr, int targetCan) {
datalayer_battery = datalayer_ptr;
contactor_closing_allowed = contactor_closing_allowed_ptr;
allows_contactor_closing = nullptr;
can_interface = targetCan;
}
// Use the default constructor to create the first or single battery.
PylonBattery() {
datalayer_battery = &datalayer.battery;
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
contactor_closing_allowed = nullptr;
can_interface = can_config.battery;
}
virtual void setup(void);
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
private:
/* Change the following to suit your battery */ /* Change the following to suit your battery */
#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 1500 static const int MIN_PACK_VOLTAGE_DV = 1500;
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value static const int 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 static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
#define MAX_CELL_DEVIATION_MV 150 static const int MAX_CELL_DEVIATION_MV = 150;
void setup_battery(void); DATALAYER_BATTERY_TYPE* datalayer_battery;
void transmit_can_frame(CAN_frame* tx_frame, int interface);
// If not null, this battery decides when the contactor can be closed and writes the value here.
bool* allows_contactor_closing;
// If not null, this battery listens to this boolean to determine whether contactor closing is allowed
bool* contactor_closing_allowed;
int can_interface;
unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
//Actual content messages
CAN_frame PYLON_3010 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3010,
.data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame PYLON_8200 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x8200,
.data = {0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame PYLON_8210 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x8210,
.data = {0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame PYLON_4200 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x4200,
.data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
int16_t celltemperature_max_dC = 0;
int16_t celltemperature_min_dC = 0;
int16_t current_dA = 0;
uint16_t voltage_dV = 0;
uint16_t cellvoltage_max_mV = 3700;
uint16_t cellvoltage_min_mV = 3700;
uint16_t charge_cutoff_voltage = 0;
uint16_t discharge_cutoff_voltage = 0;
int16_t max_charge_current = 0;
int16_t max_discharge_current = 0;
uint8_t ensemble_info_ack = 0;
uint8_t battery_module_quantity = 0;
uint8_t battery_modules_in_series = 0;
uint8_t cell_quantity_in_module = 0;
uint8_t voltage_level = 0;
uint8_t ah_number = 0;
uint8_t SOC = 50;
uint8_t SOH = 100;
uint8_t charge_forbidden = 0;
uint8_t discharge_forbidden = 0;
};
#endif #endif

View file

@ -1,5 +1,6 @@
#include "../include.h" #include "../include.h"
#ifdef SANTA_FE_PHEV_BATTERY #ifdef SANTA_FE_PHEV_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "SANTA-FE-PHEV-BATTERY.h" #include "SANTA-FE-PHEV-BATTERY.h"
@ -13,113 +14,58 @@ TODO: Tweak temperature values once more data is known about them
TODO: Check if CRC function works like it should. This enables checking for corrupt messages TODO: Check if CRC function works like it should. This enables checking for corrupt messages
*/ */
/* Do not change code below unless you are sure what you are doing */ static uint8_t CalculateCRC8(CAN_frame rx_frame) {
static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send int crc = 0;
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 uint8_t poll_data_pid = 0;
static uint8_t counter_200 = 0;
static uint8_t checksum_200 = 0;
static uint16_t SOC_Display = 0; for (uint8_t framepos = 0; framepos < 8; framepos++) {
static uint16_t batterySOH = 100; crc ^= rx_frame.data.u8[framepos];
static uint16_t CellVoltMax_mV = 3700;
static uint16_t CellVoltMin_mV = 3700;
static uint8_t CellVmaxNo = 0;
static uint8_t CellVminNo = 0;
static uint16_t allowedDischargePower = 0;
static uint16_t allowedChargePower = 0;
static uint16_t batteryVoltage = 0;
static int16_t leadAcidBatteryVoltage = 120;
static int8_t temperatureMax = 0;
static int8_t temperatureMin = 0;
static int16_t batteryAmps = 0;
static uint8_t StatusBattery = 0;
static uint16_t cellvoltages_mv[96];
#ifdef DOUBLE_BATTERY for (uint8_t j = 0; j < 8; j++) {
static uint16_t battery2_SOC_Display = 0; if ((crc & 0x80) != 0) {
static uint16_t battery2_SOH = 100; crc = (crc << 1) ^ 0x1;
static uint16_t battery2_CellVoltMax_mV = 3700; } else {
static uint16_t battery2_CellVoltMin_mV = 3700; crc <<= 1;
static uint8_t battery2_CellVmaxNo = 0; }
static uint8_t battery2_CellVminNo = 0; }
static uint16_t battery2_allowedDischargePower = 0; }
static uint16_t battery2_allowedChargePower = 0; return (uint8_t)crc;
static uint16_t battery2_batteryVoltage = 0; }
static int16_t battery2_leadAcidBatteryVoltage = 120;
static int8_t battery2_temperatureMax = 0;
static int8_t battery2_temperatureMin = 0;
static int16_t battery2_batteryAmps = 0;
static uint8_t battery2_StatusBattery = 0;
static uint16_t battery2_cellvoltages_mv[96];
#endif //DOUBLE_BATTERY
CAN_frame SANTAFE_200 = {.FD = false, void SantaFePhevBattery::
.ext_ID = false, update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
.DLC = 8,
.ID = 0x200,
.data = {0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00}};
CAN_frame SANTAFE_2A1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2A1,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x02}};
CAN_frame SANTAFE_2F0 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2F0,
.data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00}};
CAN_frame SANTAFE_523 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x523,
.data = {0x60, 0x00, 0x60, 0, 0, 0, 0, 0}};
CAN_frame SANTAFE_7E4_poll = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4, //Polling frame, 0x22 01 0X
.data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}};
CAN_frame SANTAFE_7E4_ack = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4, //Ack frame, correct PID is returned. Flow control message
.data = {0x30, 0x00, 0x03, 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_Display * 10); //increase SOC range from 0-100.0 -> 100.00
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00 datalayer_battery->status.soh_pptt = (batterySOH * 100); //Increase decimals from 100% -> 100.00%
datalayer.battery.status.soh_pptt = (batterySOH * 100); //Increase decimals from 100% -> 100.00% datalayer_battery->status.voltage_dV = batteryVoltage;
datalayer.battery.status.voltage_dV = batteryVoltage; datalayer_battery->status.current_dA = -batteryAmps;
datalayer.battery.status.current_dA = -batteryAmps; 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.remaining_capacity_Wh = static_cast<uint32_t>( datalayer_battery->status.max_discharge_power_W = allowedDischargePower * 10;
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
datalayer.battery.status.max_discharge_power_W = allowedDischargePower * 10; datalayer_battery->status.max_charge_power_W = allowedChargePower * 10;
datalayer.battery.status.max_charge_power_W = allowedChargePower * 10; datalayer_battery->status.cell_max_voltage_mV = CellVoltMax_mV;
datalayer.battery.status.cell_max_voltage_mV = CellVoltMax_mV; datalayer_battery->status.cell_min_voltage_mV = CellVoltMin_mV;
datalayer.battery.status.cell_min_voltage_mV = CellVoltMin_mV; datalayer_battery->status.temperature_min_dC = temperatureMin * 10; //Increase decimals, 17C -> 17.0C
datalayer.battery.status.temperature_min_dC = temperatureMin * 10; //Increase decimals, 17C -> 17.0C datalayer_battery->status.temperature_max_dC = temperatureMax * 10; //Increase decimals, 18C -> 18.0C
datalayer.battery.status.temperature_max_dC = temperatureMax * 10; //Increase decimals, 18C -> 18.0C
if (leadAcidBatteryVoltage < 110) { if (leadAcidBatteryVoltage < 110) {
set_event(EVENT_12V_LOW, leadAcidBatteryVoltage); set_event(EVENT_12V_LOW, leadAcidBatteryVoltage);
} }
} }
void handle_incoming_can_frame_battery(CAN_frame rx_frame) { void SantaFePhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) { switch (rx_frame.ID) {
case 0x1FF: case 0x1FF:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
StatusBattery = (rx_frame.data.u8[0] & 0x0F); StatusBattery = (rx_frame.data.u8[0] & 0x0F);
break; break;
case 0x4D5: case 0x4D5:
@ -127,16 +73,16 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
case 0x4DD: case 0x4DD:
break; break;
case 0x4DE: case 0x4DE:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break; break;
case 0x4E0: case 0x4E0:
break; break;
case 0x542: case 0x542:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
SOC_Display = ((rx_frame.data.u8[1] << 8) + rx_frame.data.u8[0]) / 2; SOC_Display = ((rx_frame.data.u8[1] << 8) + rx_frame.data.u8[0]) / 2;
break; break;
case 0x588: case 0x588:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
batteryVoltage = ((rx_frame.data.u8[1] << 8) + rx_frame.data.u8[0]); batteryVoltage = ((rx_frame.data.u8[1] << 8) + rx_frame.data.u8[0]);
break; break;
case 0x597: case 0x597:
@ -146,7 +92,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
case 0x5A7: case 0x5A7:
break; break;
case 0x5AD: case 0x5AD:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
batteryAmps = (rx_frame.data.u8[3] << 8) + rx_frame.data.u8[2]; batteryAmps = (rx_frame.data.u8[3] << 8) + rx_frame.data.u8[2];
break; break;
case 0x5AE: case 0x5AE:
@ -154,25 +100,25 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
case 0x5F1: case 0x5F1:
break; break;
case 0x620: case 0x620:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
leadAcidBatteryVoltage = rx_frame.data.u8[1]; leadAcidBatteryVoltage = rx_frame.data.u8[1];
temperatureMin = rx_frame.data.u8[6]; //Lowest temp in battery temperatureMin = rx_frame.data.u8[6]; //Lowest temp in battery
temperatureMax = rx_frame.data.u8[7]; //Highest temp in battery temperatureMax = rx_frame.data.u8[7]; //Highest temp in battery
break; break;
case 0x670: case 0x670:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
allowedChargePower = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); allowedChargePower = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
allowedDischargePower = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); allowedDischargePower = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
break; break;
case 0x671: case 0x671:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break; break;
case 0x7EC: //Data From polled PID group, BigEndian case 0x7EC: //Data From polled PID group, BigEndian
switch (rx_frame.data.u8[0]) { switch (rx_frame.data.u8[0]) {
case 0x10: //"PID Header" case 0x10: //"PID Header"
if (rx_frame.data.u8[4] == poll_data_pid) { if (rx_frame.data.u8[4] == poll_data_pid) {
transmit_can_frame(&SANTAFE_7E4_ack, transmit_can_frame(&SANTAFE_7E4_ack,
can_config.battery); //Send ack to BMS if the same frame is sent as polled can_interface); //Send ack to BMS if the same frame is sent as polled
} }
break; break;
case 0x21: //First frame in PID group case 0x21: //First frame in PID group
@ -317,7 +263,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
cellvoltages_mv[95] = (rx_frame.data.u8[5] * 20); cellvoltages_mv[95] = (rx_frame.data.u8[5] * 20);
//Map all cell voltages to the global array, we have sampled them all! //Map all cell voltages to the global array, we have sampled them all!
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_mv, 96 * sizeof(uint16_t)); memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_mv, 96 * sizeof(uint16_t));
} else if (poll_data_pid == 5) { } else if (poll_data_pid == 5) {
} }
break; break;
@ -333,7 +279,8 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
break; break;
} }
} }
void transmit_can_battery(unsigned long currentMillis) {
void SantaFePhevBattery::transmit_can(unsigned long currentMillis) {
//Send 10ms message //Send 10ms message
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
previousMillis10 = currentMillis; previousMillis10 = currentMillis;
@ -344,14 +291,9 @@ void transmit_can_battery(unsigned long currentMillis) {
SANTAFE_200.data.u8[7] = checksum_200; SANTAFE_200.data.u8[7] = checksum_200;
transmit_can_frame(&SANTAFE_200, can_config.battery); transmit_can_frame(&SANTAFE_200, can_interface);
transmit_can_frame(&SANTAFE_2A1, can_config.battery); transmit_can_frame(&SANTAFE_2A1, can_interface);
transmit_can_frame(&SANTAFE_2F0, can_config.battery); transmit_can_frame(&SANTAFE_2F0, can_interface);
#ifdef DOUBLE_BATTERY
transmit_can_frame(&SANTAFE_200, can_config.battery_double);
transmit_can_frame(&SANTAFE_2A1, can_config.battery_double);
transmit_can_frame(&SANTAFE_2F0, can_config.battery_double);
#endif //DOUBLE_BATTERY
counter_200++; counter_200++;
if (counter_200 > 0xF) { if (counter_200 > 0xF) {
@ -363,10 +305,7 @@ void transmit_can_battery(unsigned long currentMillis) {
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis; previousMillis100 = currentMillis;
transmit_can_frame(&SANTAFE_523, can_config.battery); transmit_can_frame(&SANTAFE_523, can_interface);
#ifdef DOUBLE_BATTERY
transmit_can_frame(&SANTAFE_523, can_config.battery_double);
#endif //DOUBLE_BATTERY
} }
// Send 500ms CAN Message // Send 500ms CAN Message
@ -376,302 +315,23 @@ void transmit_can_battery(unsigned long currentMillis) {
// PID data is polled after last message sent from battery: // PID data is polled after last message sent from battery:
poll_data_pid = (poll_data_pid % 5) + 1; poll_data_pid = (poll_data_pid % 5) + 1;
SANTAFE_7E4_poll.data.u8[3] = (uint8_t)poll_data_pid; SANTAFE_7E4_poll.data.u8[3] = (uint8_t)poll_data_pid;
transmit_can_frame(&SANTAFE_7E4_poll, can_config.battery); transmit_can_frame(&SANTAFE_7E4_poll, can_interface);
#ifdef DOUBLE_BATTERY
transmit_can_frame(&SANTAFE_7E4_poll, can_config.battery_double);
#endif //DOUBLE_BATTERY
} }
} }
#ifdef DOUBLE_BATTERY void SantaFePhevBattery::setup(void) { // Performs one time setup at startup
void update_values_battery2() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
datalayer.battery2.status.real_soc = (battery2_SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
datalayer.battery2.status.soh_pptt = (battery2_SOH * 100); //Increase decimals from 100% -> 100.00%
datalayer.battery2.status.voltage_dV = battery2_batteryVoltage;
datalayer.battery2.status.current_dA = -battery2_batteryAmps;
datalayer.battery2.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery2.status.real_soc) / 10000) * datalayer.battery2.info.total_capacity_Wh);
datalayer.battery2.status.max_discharge_power_W = battery2_allowedDischargePower * 10;
datalayer.battery2.status.max_charge_power_W = battery2_allowedChargePower * 10;
//Power in watts, Negative = charging batt
datalayer.battery2.status.active_power_W =
((datalayer.battery2.status.voltage_dV * datalayer.battery2.status.current_dA) / 100);
datalayer.battery2.status.cell_max_voltage_mV = battery2_CellVoltMax_mV;
datalayer.battery2.status.cell_min_voltage_mV = battery2_CellVoltMin_mV;
datalayer.battery2.status.temperature_min_dC = battery2_temperatureMin * 10; //Increase decimals, 17C -> 17.0C
datalayer.battery2.status.temperature_max_dC = battery2_temperatureMax * 10; //Increase decimals, 18C -> 18.0C
if (battery2_leadAcidBatteryVoltage < 110) {
set_event(EVENT_12V_LOW, battery2_leadAcidBatteryVoltage);
}
}
void handle_incoming_can_frame_battery2(CAN_frame rx_frame) {
switch (rx_frame.ID) {
case 0x1FF:
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
battery2_StatusBattery = (rx_frame.data.u8[0] & 0x0F);
break;
case 0x4D5:
break;
case 0x4DD:
break;
case 0x4DE:
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x4E0:
break;
case 0x542:
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
battery2_SOC_Display = ((rx_frame.data.u8[1] << 8) + rx_frame.data.u8[0]) / 2;
break;
case 0x588:
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
battery2_batteryVoltage = ((rx_frame.data.u8[1] << 8) + rx_frame.data.u8[0]);
break;
case 0x597:
break;
case 0x5A6:
break;
case 0x5A7:
break;
case 0x5AD:
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
battery2_batteryAmps = (rx_frame.data.u8[3] << 8) + rx_frame.data.u8[2];
break;
case 0x5AE:
break;
case 0x5F1:
break;
case 0x620:
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
battery2_leadAcidBatteryVoltage = rx_frame.data.u8[1];
battery2_temperatureMin = rx_frame.data.u8[6]; //Lowest temp in battery
battery2_temperatureMax = rx_frame.data.u8[7]; //Highest temp in battery
break;
case 0x670:
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
battery2_allowedChargePower = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
battery2_allowedDischargePower = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
break;
case 0x671:
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x7EC: //Data From polled PID group, BigEndian
switch (rx_frame.data.u8[0]) {
case 0x10: //"PID Header"
if (rx_frame.data.u8[4] == poll_data_pid) {
transmit_can_frame(&SANTAFE_7E4_ack,
can_config.battery_double); //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) {
} else if (poll_data_pid == 2) {
battery2_cellvoltages_mv[0] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[1] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[2] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[3] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[4] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[5] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 3) {
battery2_cellvoltages_mv[32] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[33] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[34] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[35] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[36] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[37] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 4) {
battery2_cellvoltages_mv[64] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[65] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[66] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[67] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[68] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[69] = (rx_frame.data.u8[7] * 20);
}
break;
case 0x22: //Second datarow in PID group
if (poll_data_pid == 2) {
battery2_cellvoltages_mv[6] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[7] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[8] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[9] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[10] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[11] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[12] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 3) {
battery2_cellvoltages_mv[38] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[39] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[40] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[41] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[42] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[43] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[44] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 4) {
battery2_cellvoltages_mv[70] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[71] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[72] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[73] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[74] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[75] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[76] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 6) {
}
break;
case 0x23: //Third datarow in PID group
if (poll_data_pid == 1) {
battery2_CellVoltMax_mV = (rx_frame.data.u8[7] * 20); //(volts *50) *20 =mV
} else if (poll_data_pid == 2) {
battery2_cellvoltages_mv[13] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[14] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[15] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[16] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[17] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[18] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[19] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 3) {
battery2_cellvoltages_mv[45] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[46] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[47] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[48] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[49] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[50] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[51] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 4) {
battery2_cellvoltages_mv[77] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[78] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[79] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[80] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[81] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[82] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[83] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 5) {
if (rx_frame.data.u8[6] > 0) {
battery2_SOH = rx_frame.data.u8[6];
}
if (battery2_SOH > 100) {
battery2_SOH = 100;
}
}
break;
case 0x24: //Fourth datarow in PID group
if (poll_data_pid == 1) {
battery2_CellVmaxNo = rx_frame.data.u8[1];
battery2_CellVminNo = rx_frame.data.u8[3];
CellVoltMin_mV = (rx_frame.data.u8[2] * 20); //(volts *50) *20 =mV
} else if (poll_data_pid == 2) {
battery2_cellvoltages_mv[20] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[21] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[22] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[23] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[24] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[25] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[26] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 3) {
battery2_cellvoltages_mv[52] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[53] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[54] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[55] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[56] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[57] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[58] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 4) {
battery2_cellvoltages_mv[84] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[85] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[86] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[87] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[88] = (rx_frame.data.u8[5] * 20);
battery2_cellvoltages_mv[89] = (rx_frame.data.u8[6] * 20);
battery2_cellvoltages_mv[90] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 5) {
}
break;
case 0x25: //Fifth datarow in PID group
if (poll_data_pid == 2) {
battery2_cellvoltages_mv[27] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[28] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[29] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[30] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[31] = (rx_frame.data.u8[5] * 20);
} else if (poll_data_pid == 3) {
battery2_cellvoltages_mv[59] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[60] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[61] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[62] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[63] = (rx_frame.data.u8[5] * 20);
} else if (poll_data_pid == 4) {
battery2_cellvoltages_mv[91] = (rx_frame.data.u8[1] * 20);
battery2_cellvoltages_mv[92] = (rx_frame.data.u8[2] * 20);
battery2_cellvoltages_mv[93] = (rx_frame.data.u8[3] * 20);
battery2_cellvoltages_mv[94] = (rx_frame.data.u8[4] * 20);
battery2_cellvoltages_mv[95] = (rx_frame.data.u8[5] * 20);
//Map all cell voltages to the global array, we have sampled them all!
memcpy(datalayer.battery2.status.cell_voltages_mV, battery2_cellvoltages_mv, 96 * sizeof(uint16_t));
} 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;
}
}
#endif //DOUBLE_BATTERY
uint8_t CalculateCRC8(CAN_frame rx_frame) {
int crc = 0;
for (uint8_t framepos = 0; framepos < 8; framepos++) {
crc ^= rx_frame.data.u8[framepos];
for (uint8_t j = 0; j < 8; j++) {
if ((crc & 0x80) != 0) {
crc = (crc << 1) ^ 0x1;
} else {
crc <<= 1;
}
}
}
return (uint8_t)crc;
}
void setup_battery(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Santa Fe PHEV", 63); strncpy(datalayer.system.info.battery_protocol, "Santa Fe PHEV", 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 96; datalayer_battery->info.number_of_cells = 96;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; 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.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; 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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
#ifdef DOUBLE_BATTERY if (allows_contactor_closing) {
datalayer.battery2.info.number_of_cells = datalayer.battery.info.number_of_cells; *allows_contactor_closing = true;
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_mV = datalayer.battery.info.max_cell_voltage_mV;
datalayer.battery2.info.min_cell_voltage_mV = datalayer.battery.info.min_cell_voltage_mV;
datalayer.battery2.info.max_cell_voltage_deviation_mV = datalayer.battery.info.max_cell_voltage_deviation_mV;
#endif //DOUBLE_BATTERY
} }
#endif #endif

View file

@ -1,17 +1,101 @@
#ifndef SANTA_FE_PHEV_BATTERY_H #ifndef SANTA_FE_PHEV_BATTERY_H
#define SANTA_FE_PHEV_BATTERY_H #define SANTA_FE_PHEV_BATTERY_H
#include <Arduino.h> #include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../include.h" #include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED #define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4040 //5000 = 500.0V #define SELECTED_BATTERY_CLASS SantaFePhevBattery
#define MIN_PACK_VOLTAGE_DV 2880
#define MAX_CELL_DEVIATION_MV 250
#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
uint8_t CalculateCRC8(CAN_frame rx_frame); class SantaFePhevBattery : public CanBattery {
void setup_battery(void); public:
void transmit_can_frame(CAN_frame* tx_frame, int interface); // Use this constructor for the second battery.
SantaFePhevBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, int targetCan) {
datalayer_battery = datalayer_ptr;
allows_contactor_closing = nullptr;
can_interface = targetCan;
}
// Use the default constructor to create the first or single battery.
SantaFePhevBattery() {
datalayer_battery = &datalayer.battery;
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
can_interface = can_config.battery;
}
virtual void setup(void);
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
private:
DATALAYER_BATTERY_TYPE* datalayer_battery;
// If not null, this battery decides when the contactor can be closed and writes the value here.
bool* allows_contactor_closing;
int can_interface;
static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 2880;
static const int MAX_CELL_DEVIATION_MV = 250;
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send
uint8_t poll_data_pid = 0;
uint8_t counter_200 = 0;
uint8_t checksum_200 = 0;
uint16_t SOC_Display = 0;
uint16_t batterySOH = 100;
uint16_t CellVoltMax_mV = 3700;
uint16_t CellVoltMin_mV = 3700;
uint8_t CellVmaxNo = 0;
uint8_t CellVminNo = 0;
uint16_t allowedDischargePower = 0;
uint16_t allowedChargePower = 0;
uint16_t batteryVoltage = 0;
int16_t leadAcidBatteryVoltage = 120;
int8_t temperatureMax = 0;
int8_t temperatureMin = 0;
int16_t batteryAmps = 0;
uint8_t StatusBattery = 0;
uint16_t cellvoltages_mv[96];
CAN_frame SANTAFE_200 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x200,
.data = {0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00}};
CAN_frame SANTAFE_2A1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2A1,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x02}};
CAN_frame SANTAFE_2F0 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2F0,
.data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00}};
CAN_frame SANTAFE_523 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x523,
.data = {0x60, 0x00, 0x60, 0, 0, 0, 0, 0}};
CAN_frame SANTAFE_7E4_poll = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4, //Polling frame, 0x22 01 0X
.data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}};
CAN_frame SANTAFE_7E4_ack = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4, //Ack frame, correct PID is returned. Flow control message
.data = {0x30, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}};
};
#endif #endif

View file

@ -1,453 +1,13 @@
#include "../include.h" #include "../include.h"
#ifdef TESLA_BATTERY #ifdef TESLA_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For Advanced Battery Insights webpage #include "../datalayer/datalayer_extended.h" //For Advanced Battery Insights webpage
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "TESLA-BATTERY.h" #include "TESLA-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */
/* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */ /* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */
static unsigned long previousMillis10 = 0; // will store last time a 50ms CAN Message was sent
static unsigned long previousMillis50 = 0; // will store last time a 50ms CAN Message was sent
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
static unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was sent
static unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was sent
static bool alternate243 = false;
//0x221 545 VCFRONT_LVPowerState: "GenMsgCycleTime" 50ms
CAN_frame TESLA_221_1 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x41, 0x11, 0x01, 0x00, 0x00, 0x00, 0x20, 0x96}}; //Contactor frame 221 - close contactors
CAN_frame TESLA_221_2 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA}}; //Contactor Frame 221 - hv_up_for_drive
//0x241 VCFRONT_coolant 100ms
CAN_frame TESLA_241 = {.FD = false,
.ext_ID = false,
.DLC = 7,
.ID = 0x241,
.data = {0x3C, 0x78, 0x2C, 0x0F, 0x1E, 0x5B, 0x00}};
//0x242 VCLEFT_LVPowerState 100ms
CAN_frame TESLA_242 = {.FD = false, .ext_ID = false, .DLC = 2, .ID = 0x242, .data = {0x10, 0x95}};
//0x243 VCRIGHT_hvacStatus 50ms
CAN_frame TESLA_243_1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x243,
.data = {0xC9, 0x00, 0xEB, 0xD4, 0x31, 0x32, 0x02, 0x00}};
CAN_frame TESLA_243_2 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x243,
.data = {0x08, 0x81, 0x42, 0x60, 0x92, 0x2C, 0x0E, 0x09}};
//0x129 SteeringAngle 10ms
CAN_frame TESLA_129 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x129,
.data = {0x21, 0x24, 0x36, 0x5F, 0x00, 0x20, 0xFF, 0x3F}};
//0x612 UDS diagnostic requests - on demand
CAN_frame TESLA_602 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x602,
.data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}};
static uint8_t stateMachineClearIsolationFault = 0xFF;
static uint8_t stateMachineBMSReset = 0xFF;
static uint16_t sendContactorClosingMessagesStill = 300;
static uint16_t battery_cell_max_v = 3300;
static uint16_t battery_cell_min_v = 3300;
static uint16_t battery_cell_deviation_mV = 0; //contains the deviation between highest and lowest cell in mV
static bool cellvoltagesRead = false;
//0x3d2: 978 BMS_kwhCounter
static uint32_t battery_total_discharge = 0;
static uint32_t battery_total_charge = 0;
//0x352: 850 BMS_energyStatus
static bool BMS352_mux = false; // variable to store when 0x352 mux is present
static uint16_t battery_energy_buffer = 0; // kWh
static uint16_t battery_energy_buffer_m1 = 0; // kWh
static uint16_t battery_energy_to_charge_complete = 0; // kWh
static uint16_t battery_energy_to_charge_complete_m1 = 0; // kWh
static uint16_t battery_expected_energy_remaining = 0; // kWh
static uint16_t battery_expected_energy_remaining_m1 = 0; // kWh
static bool battery_full_charge_complete = false; // Changed to bool
static bool battery_fully_charged = false; // Changed to bool
static uint16_t battery_ideal_energy_remaining = 0; // kWh
static uint16_t battery_ideal_energy_remaining_m0 = 0; // kWh
static uint16_t battery_nominal_energy_remaining = 0; // kWh
static uint16_t battery_nominal_energy_remaining_m0 = 0; // kWh
static uint16_t battery_nominal_full_pack_energy = 0; // Kwh
static uint16_t battery_nominal_full_pack_energy_m0 = 0; // Kwh
//0x132 306 HVBattAmpVolt
static uint16_t battery_volts = 0; // V
static int16_t battery_amps = 0; // A
static int16_t battery_raw_amps = 0; // A
static uint16_t battery_charge_time_remaining = 0; // Minutes
//0x252 594 BMS_powerAvailable
static uint16_t BMS_maxRegenPower = 0; //rename from battery_regenerative_limit
static uint16_t BMS_maxDischargePower = 0; // rename from battery_discharge_limit
static uint16_t BMS_maxStationaryHeatPower = 0; //rename from battery_max_heat_park
static uint16_t BMS_hvacPowerBudget = 0; //rename from battery_hvac_max_power
static uint8_t BMS_notEnoughPowerForHeatPump = 0;
static uint8_t BMS_powerLimitState = 0;
static uint8_t BMS_inverterTQF = 0;
//0x2d2: 722 BMSVAlimits
static uint16_t battery_max_discharge_current = 0;
static uint16_t battery_max_charge_current = 0;
static uint16_t battery_bms_max_voltage = 0;
static uint16_t battery_bms_min_voltage = 0;
//0x2b4: 692 PCS_dcdcRailStatus
static uint16_t battery_dcdcHvBusVolt = 0; // Change name from battery_high_voltage to battery_dcdcHvBusVolt
static uint16_t battery_dcdcLvBusVolt = 0; // Change name from battery_low_voltage to battery_dcdcLvBusVolt
static uint16_t battery_dcdcLvOutputCurrent =
0; // Change name from battery_output_current to battery_dcdcLvOutputCurrent
//0x292: 658 BMS_socStatus
static uint16_t battery_beginning_of_life = 0; // kWh
static uint16_t battery_soc_min = 0;
static uint16_t battery_soc_max = 0;
static uint16_t battery_soc_ui = 0; //Change name from battery_soc_vi to reflect DBC battery_soc_ui
static uint16_t battery_soc_ave = 0;
static uint8_t battery_battTempPct = 0;
//0x392: BMS_packConfig
static uint32_t battery_packMass = 0;
static uint32_t battery_platformMaxBusVoltage = 0;
static uint32_t battery_packConfigMultiplexer = 0;
static uint32_t battery_moduleType = 0;
static uint32_t battery_reservedConfig = 0;
//0x332: 818 BattBrickMinMax:BMS_bmbMinMax
static int16_t battery_max_temp = 0; // C*
static int16_t battery_min_temp = 0; // C*
static uint16_t battery_BrickVoltageMax = 0;
static uint16_t battery_BrickVoltageMin = 0;
static uint8_t battery_BrickTempMaxNum = 0;
static uint8_t battery_BrickTempMinNum = 0;
static uint8_t battery_BrickModelTMax = 0;
static uint8_t battery_BrickModelTMin = 0;
static uint8_t battery_BrickVoltageMaxNum = 0; //rename from battery_max_vno
static uint8_t battery_BrickVoltageMinNum = 0; //rename from battery_min_vno
//0x20A: 522 HVP_contactorState
static uint8_t battery_contactor = 0; //State of contactor
static uint8_t battery_hvil_status = 0;
static uint8_t battery_packContNegativeState = 0;
static uint8_t battery_packContPositiveState = 0;
static uint8_t battery_packContactorSetState = 0;
static bool battery_packCtrsClosingAllowed = false; // Change to bool
static bool battery_pyroTestInProgress = false; // Change to bool
static bool battery_packCtrsOpenNowRequested = false; // Change to bool
static bool battery_packCtrsOpenRequested = false; // Change to bool
static uint8_t battery_packCtrsRequestStatus = 0;
static bool battery_packCtrsResetRequestRequired = false; // Change to bool
static bool battery_dcLinkAllowedToEnergize = false; // Change to bool
static bool battery_fcContNegativeAuxOpen = false; // Change to bool
static uint8_t battery_fcContNegativeState = 0;
static bool battery_fcContPositiveAuxOpen = false; // Change to bool
static uint8_t battery_fcContPositiveState = 0;
static uint8_t battery_fcContactorSetState = 0;
static bool battery_fcCtrsClosingAllowed = false; // Change to bool
static bool battery_fcCtrsOpenNowRequested = false; // Change to bool
static bool battery_fcCtrsOpenRequested = false; // Change to bool
static uint8_t battery_fcCtrsRequestStatus = 0;
static bool battery_fcCtrsResetRequestRequired = false; // Change to bool
static bool battery_fcLinkAllowedToEnergize = false; // Change to bool
//0x72A: BMS_serialNumber
static uint8_t BMS_SerialNumber[14] = {0}; // Stores raw HEX values for ASCII chars
//0x212: 530 BMS_status
static bool battery_BMS_hvacPowerRequest = false; //Change to bool
static bool battery_BMS_notEnoughPowerForDrive = false; //Change to bool
static bool battery_BMS_notEnoughPowerForSupport = false; //Change to bool
static bool battery_BMS_preconditionAllowed = false; //Change to bool
static bool battery_BMS_updateAllowed = false; //Change to bool
static bool battery_BMS_activeHeatingWorthwhile = false; //Change to bool
static bool battery_BMS_cpMiaOnHvs = false; //Change to bool
static uint8_t battery_BMS_contactorState = 0;
static uint8_t battery_BMS_state = 0;
static uint8_t battery_BMS_hvState = 0;
static uint16_t battery_BMS_isolationResistance = 0;
static bool battery_BMS_chargeRequest = false; //Change to bool
static bool battery_BMS_keepWarmRequest = false; //Change to bool
static uint8_t battery_BMS_uiChargeStatus = 0;
static bool battery_BMS_diLimpRequest = false; //Change to bool
static bool battery_BMS_okToShipByAir = false; //Change to bool
static bool battery_BMS_okToShipByLand = false; //Change to bool
static uint32_t battery_BMS_chgPowerAvailable = 0;
static uint8_t battery_BMS_chargeRetryCount = 0;
static bool battery_BMS_pcsPwmEnabled = false; //Change to bool
static bool battery_BMS_ecuLogUploadRequest = false; //Change to bool
static uint8_t battery_BMS_minPackTemperature = 0;
// 0x224:548 PCS_dcdcStatus
static uint8_t battery_PCS_dcdcPrechargeStatus = 0;
static uint8_t battery_PCS_dcdc12VSupportStatus = 0;
static uint8_t battery_PCS_dcdcHvBusDischargeStatus = 0;
static uint16_t battery_PCS_dcdcMainState = 0;
static uint8_t battery_PCS_dcdcSubState = 0;
static bool battery_PCS_dcdcFaulted = false; //Change to bool
static bool battery_PCS_dcdcOutputIsLimited = false; //Change to bool
static uint32_t battery_PCS_dcdcMaxOutputCurrentAllowed = 0;
static uint8_t battery_PCS_dcdcPrechargeRtyCnt = 0;
static uint8_t battery_PCS_dcdc12VSupportRtyCnt = 0;
static uint8_t battery_PCS_dcdcDischargeRtyCnt = 0;
static uint8_t battery_PCS_dcdcPwmEnableLine = 0;
static uint8_t battery_PCS_dcdcSupportingFixedLvTarget = 0;
static uint8_t battery_PCS_ecuLogUploadRequest = 0;
static uint8_t battery_PCS_dcdcPrechargeRestartCnt = 0;
static uint8_t battery_PCS_dcdcInitialPrechargeSubState = 0;
//0x312: 786 BMS_thermalStatus
static uint16_t BMS_powerDissipation = 0;
static uint16_t BMS_flowRequest = 0;
static uint16_t BMS_inletActiveCoolTargetT = 0;
static uint16_t BMS_inletPassiveTargetT = 0;
static uint16_t BMS_inletActiveHeatTargetT = 0;
static uint16_t BMS_packTMin = 0;
static uint16_t BMS_packTMax = 0;
static bool BMS_pcsNoFlowRequest = false;
static bool BMS_noFlowRequest = false;
//0x2A4; 676 PCS_thermalStatus
static int16_t PCS_chgPhATemp = 0;
static int16_t PCS_chgPhBTemp = 0;
static int16_t PCS_chgPhCTemp = 0;
static int16_t PCS_dcdcTemp = 0;
static int16_t PCS_ambientTemp = 0;
//0x2C4; 708 PCS_logging
static uint16_t PCS_logMessageSelect = 0;
static uint16_t PCS_dcdcMaxLvOutputCurrent = 0;
static uint16_t PCS_dcdcCurrentLimit = 0;
static uint16_t PCS_dcdcLvOutputCurrentTempLimit = 0;
static uint16_t PCS_dcdcUnifiedCommand = 0;
static uint16_t PCS_dcdcCLAControllerOutput = 0;
static int16_t PCS_dcdcTankVoltage = 0;
static uint16_t PCS_dcdcTankVoltageTarget = 0;
static uint16_t PCS_dcdcClaCurrentFreq = 0;
static int16_t PCS_dcdcTCommMeasured = 0;
static uint16_t PCS_dcdcShortTimeUs = 0;
static uint16_t PCS_dcdcHalfPeriodUs = 0;
static uint16_t PCS_dcdcIntervalMaxFrequency = 0;
static uint16_t PCS_dcdcIntervalMaxHvBusVolt = 0;
static uint16_t PCS_dcdcIntervalMaxLvBusVolt = 0;
static uint16_t PCS_dcdcIntervalMaxLvOutputCurr = 0;
static uint16_t PCS_dcdcIntervalMinFrequency = 0;
static uint16_t PCS_dcdcIntervalMinHvBusVolt = 0;
static uint16_t PCS_dcdcIntervalMinLvBusVolt = 0;
static uint16_t PCS_dcdcIntervalMinLvOutputCurr = 0;
static uint32_t PCS_dcdc12vSupportLifetimekWh = 0;
//0x7AA: //1962 HVP_debugMessage:
static uint8_t HVP_debugMessageMultiplexer = 0;
static bool HVP_gpioPassivePyroDepl = false; //Change to bool
static bool HVP_gpioPyroIsoEn = false; //Change to bool
static bool HVP_gpioCpFaultIn = false; //Change to bool
static bool HVP_gpioPackContPowerEn = false; //Change to bool
static bool HVP_gpioHvCablesOk = false; //Change to bool
static bool HVP_gpioHvpSelfEnable = false; //Change to bool
static bool HVP_gpioLed = false; //Change to bool
static bool HVP_gpioCrashSignal = false; //Change to bool
static bool HVP_gpioShuntDataReady = false; //Change to bool
static bool HVP_gpioFcContPosAux = false; //Change to bool
static bool HVP_gpioFcContNegAux = false; //Change to bool
static bool HVP_gpioBmsEout = false; //Change to bool
static bool HVP_gpioCpFaultOut = false; //Change to bool
static bool HVP_gpioPyroPor = false; //Change to bool
static bool HVP_gpioShuntEn = false; //Change to bool
static bool HVP_gpioHvpVerEn = false; //Change to bool
static bool HVP_gpioPackCoontPosFlywheel = false; //Change to bool
static bool HVP_gpioCpLatchEnable = false; //Change to bool
static bool HVP_gpioPcsEnable = false; //Change to bool
static bool HVP_gpioPcsDcdcPwmEnable = false; //Change to bool
static bool HVP_gpioPcsChargePwmEnable = false; //Change to bool
static bool HVP_gpioFcContPowerEnable = false; //Change to bool
static bool HVP_gpioHvilEnable = false; //Change to bool
static bool HVP_gpioSecDrdy = false; //Change to bool
static uint16_t HVP_hvp1v5Ref = 0;
static int16_t HVP_shuntCurrentDebug = 0;
static bool HVP_packCurrentMia = false; //Change to bool
static bool HVP_auxCurrentMia = false; //Change to bool
static bool HVP_currentSenseMia = false; //Change to bool
static bool HVP_shuntRefVoltageMismatch = false; //Change to bool
static bool HVP_shuntThermistorMia = false; //Change to bool
static bool HVP_shuntHwMia = false; //Change to bool
static int16_t HVP_dcLinkVoltage = 0;
static int16_t HVP_packVoltage = 0;
static int16_t HVP_fcLinkVoltage = 0;
static uint16_t HVP_packContVoltage = 0;
static int16_t HVP_packNegativeV = 0;
static int16_t HVP_packPositiveV = 0;
static uint16_t HVP_pyroAnalog = 0;
static int16_t HVP_dcLinkNegativeV = 0;
static int16_t HVP_dcLinkPositiveV = 0;
static int16_t HVP_fcLinkNegativeV = 0;
static uint16_t HVP_fcContCoilCurrent = 0;
static uint16_t HVP_fcContVoltage = 0;
static uint16_t HVP_hvilInVoltage = 0;
static uint16_t HVP_hvilOutVoltage = 0;
static int16_t HVP_fcLinkPositiveV = 0;
static uint16_t HVP_packContCoilCurrent = 0;
static uint16_t HVP_battery12V = 0;
static int16_t HVP_shuntRefVoltageDbg = 0;
static int16_t HVP_shuntAuxCurrentDbg = 0;
static int16_t HVP_shuntBarTempDbg = 0;
static int16_t HVP_shuntAsicTempDbg = 0;
static uint8_t HVP_shuntAuxCurrentStatus = 0;
static uint8_t HVP_shuntBarTempStatus = 0;
static uint8_t HVP_shuntAsicTempStatus = 0;
//0x3aa: HVP_alertMatrix1 Fault codes // Change to bool
static bool battery_WatchdogReset = false; //Warns if the processor has experienced a reset due to watchdog reset.
static bool battery_PowerLossReset = false; //Warns if the processor has experienced a reset due to power loss.
static bool battery_SwAssertion = false; //An internal software assertion has failed.
static bool battery_CrashEvent = false; //Warns if the crash signal is detected by HVP
static bool battery_OverDchgCurrentFault = false; //Warns if the pack discharge is above max discharge current limit
static bool battery_OverChargeCurrentFault =
false; //Warns if the pack discharge current is above max charge current limit
static bool battery_OverCurrentFault =
false; //Warns if the pack current (discharge or charge) is above max current limit.
static bool battery_OverTemperatureFault = false; //A pack module temperature is above maximum temperature limit
static bool battery_OverVoltageFault = false; //A brick voltage is above maximum voltage limit
static bool battery_UnderVoltageFault = false; //A brick voltage is below minimum voltage limit
static bool battery_PrimaryBmbMiaFault =
false; //Warns if the voltage and temperature readings from primary BMB chain are mia
static bool battery_SecondaryBmbMiaFault =
false; //Warns if the voltage and temperature readings from secondary BMB chain are mia
static bool battery_BmbMismatchFault =
false; //Warns if the primary and secondary BMB chain readings don't match with each other
static bool battery_BmsHviMiaFault = false; //Warns if the BMS node is mia on HVS or HVI CAN
static bool battery_CpMiaFault = false; //Warns if the CP node is mia on HVS CAN
static bool battery_PcsMiaFault = false; //The PCS node is mia on HVS CAN
static bool battery_BmsFault = false; //Warns if the BMS ECU has faulted
static bool battery_PcsFault = false; //Warns if the PCS ECU has faulted
static bool battery_CpFault = false; //Warns if the CP ECU has faulted
static bool battery_ShuntHwMiaFault = false; //Warns if the shunt current reading is not available
static bool battery_PyroMiaFault = false; //Warns if the pyro squib is not connected
static bool battery_hvsMiaFault = false; //Warns if the pack contactor hw fault
static bool battery_hviMiaFault = false; //Warns if the FC contactor hw fault
static bool battery_Supply12vFault = false; //Warns if the low voltage (12V) battery is below minimum voltage threshold
static bool battery_VerSupplyFault =
false; //Warns if the Energy reserve voltage supply is below minimum voltage threshold
static bool battery_HvilFault = false; //Warn if a High Voltage Inter Lock fault is detected
static bool battery_BmsHvsMiaFault = false; //Warns if the BMS node is mia on HVS or HVI CAN
static bool battery_PackVoltMismatchFault =
false; //Warns if the pack voltage doesn't match approximately with sum of brick voltages
static bool battery_EnsMiaFault = false; //Warns if the ENS line is not connected to HVC
static bool battery_PackPosCtrArcFault = false; //Warns if the HVP detectes series arc at pack contactor
static bool battery_packNegCtrArcFault = false; //Warns if the HVP detectes series arc at FC contactor
static bool battery_ShuntHwAndBmsMiaFault = false;
static bool battery_fcContHwFault = false;
static bool battery_robinOverVoltageFault = false;
static bool battery_packContHwFault = false;
static bool battery_pyroFuseBlown = false;
static bool battery_pyroFuseFailedToBlow = false;
static bool battery_CpilFault = false;
static bool battery_PackContactorFellOpen = false;
static bool battery_FcContactorFellOpen = false;
static bool battery_packCtrCloseBlocked = false;
static bool battery_fcCtrCloseBlocked = false;
static bool battery_packContactorForceOpen = false;
static bool battery_fcContactorForceOpen = false;
static bool battery_dcLinkOverVoltage = false;
static bool battery_shuntOverTemperature = false;
static bool battery_passivePyroDeploy = false;
static bool battery_logUploadRequest = false;
static bool battery_packCtrCloseFailed = false;
static bool battery_fcCtrCloseFailed = false;
static bool battery_shuntThermistorMia = false;
//0x320: 800 BMS_alertMatrix
static uint8_t battery_BMS_matrixIndex = 0; // Changed to bool
static bool battery_BMS_a061_robinBrickOverVoltage = false;
static bool battery_BMS_a062_SW_BrickV_Imbalance = false;
static bool battery_BMS_a063_SW_ChargePort_Fault = false;
static bool battery_BMS_a064_SW_SOC_Imbalance = false;
static bool battery_BMS_a127_SW_shunt_SNA = false;
static bool battery_BMS_a128_SW_shunt_MIA = false;
static bool battery_BMS_a069_SW_Low_Power = false;
static bool battery_BMS_a130_IO_CAN_Error = false;
static bool battery_BMS_a071_SW_SM_TransCon_Not_Met = false;
static bool battery_BMS_a132_HW_BMB_OTP_Uncorrctbl = false;
static bool battery_BMS_a134_SW_Delayed_Ctr_Off = false;
static bool battery_BMS_a075_SW_Chg_Disable_Failure = false;
static bool battery_BMS_a076_SW_Dch_While_Charging = false;
static bool battery_BMS_a017_SW_Brick_OV = false;
static bool battery_BMS_a018_SW_Brick_UV = false;
static bool battery_BMS_a019_SW_Module_OT = false;
static bool battery_BMS_a021_SW_Dr_Limits_Regulation = false;
static bool battery_BMS_a022_SW_Over_Current = false;
static bool battery_BMS_a023_SW_Stack_OV = false;
static bool battery_BMS_a024_SW_Islanded_Brick = false;
static bool battery_BMS_a025_SW_PwrBalance_Anomaly = false;
static bool battery_BMS_a026_SW_HFCurrent_Anomaly = false;
static bool battery_BMS_a087_SW_Feim_Test_Blocked = false;
static bool battery_BMS_a088_SW_VcFront_MIA_InDrive = false;
static bool battery_BMS_a089_SW_VcFront_MIA = false;
static bool battery_BMS_a090_SW_Gateway_MIA = false;
static bool battery_BMS_a091_SW_ChargePort_MIA = false;
static bool battery_BMS_a092_SW_ChargePort_Mia_On_Hv = false;
static bool battery_BMS_a034_SW_Passive_Isolation = false;
static bool battery_BMS_a035_SW_Isolation = false;
static bool battery_BMS_a036_SW_HvpHvilFault = false;
static bool battery_BMS_a037_SW_Flood_Port_Open = false;
static bool battery_BMS_a158_SW_HVP_HVI_Comms = false;
static bool battery_BMS_a039_SW_DC_Link_Over_Voltage = false;
static bool battery_BMS_a041_SW_Power_On_Reset = false;
static bool battery_BMS_a042_SW_MPU_Error = false;
static bool battery_BMS_a043_SW_Watch_Dog_Reset = false;
static bool battery_BMS_a044_SW_Assertion = false;
static bool battery_BMS_a045_SW_Exception = false;
static bool battery_BMS_a046_SW_Task_Stack_Usage = false;
static bool battery_BMS_a047_SW_Task_Stack_Overflow = false;
static bool battery_BMS_a048_SW_Log_Upload_Request = false;
static bool battery_BMS_a169_SW_FC_Pack_Weld = false;
static bool battery_BMS_a050_SW_Brick_Voltage_MIA = false;
static bool battery_BMS_a051_SW_HVC_Vref_Bad = false;
static bool battery_BMS_a052_SW_PCS_MIA = false;
static bool battery_BMS_a053_SW_ThermalModel_Sanity = false;
static bool battery_BMS_a054_SW_Ver_Supply_Fault = false;
static bool battery_BMS_a176_SW_GracefulPowerOff = false;
static bool battery_BMS_a059_SW_Pack_Voltage_Sensing = false;
static bool battery_BMS_a060_SW_Leakage_Test_Failure = false;
static bool battery_BMS_a077_SW_Charger_Regulation = false;
static bool battery_BMS_a081_SW_Ctr_Close_Blocked = false;
static bool battery_BMS_a082_SW_Ctr_Force_Open = false;
static bool battery_BMS_a083_SW_Ctr_Close_Failure = false;
static bool battery_BMS_a084_SW_Sleep_Wake_Aborted = false;
static bool battery_BMS_a094_SW_Drive_Inverter_MIA = false;
static bool battery_BMS_a099_SW_BMB_Communication = false;
static bool battery_BMS_a105_SW_One_Module_Tsense = false;
static bool battery_BMS_a106_SW_All_Module_Tsense = false;
static bool battery_BMS_a107_SW_Stack_Voltage_MIA = false;
static bool battery_BMS_a121_SW_NVRAM_Config_Error = false;
static bool battery_BMS_a122_SW_BMS_Therm_Irrational = false;
static bool battery_BMS_a123_SW_Internal_Isolation = false;
static bool battery_BMS_a129_SW_VSH_Failure = false;
static bool battery_BMS_a131_Bleed_FET_Failure = false;
static bool battery_BMS_a136_SW_Module_OT_Warning = false;
static bool battery_BMS_a137_SW_Brick_UV_Warning = false;
static bool battery_BMS_a138_SW_Brick_OV_Warning = false;
static bool battery_BMS_a139_SW_DC_Link_V_Irrational = false;
static bool battery_BMS_a141_SW_BMB_Status_Warning = false;
static bool battery_BMS_a144_Hvp_Config_Mismatch = false;
static bool battery_BMS_a145_SW_SOC_Change = false;
static bool battery_BMS_a146_SW_Brick_Overdischarged = false;
static bool battery_BMS_a149_SW_Missing_Config_Block = false;
static bool battery_BMS_a151_SW_external_isolation = false;
static bool battery_BMS_a156_SW_BMB_Vref_bad = false;
static bool battery_BMS_a157_SW_HVP_HVS_Comms = false;
static bool battery_BMS_a159_SW_HVP_ECU_Error = false;
static bool battery_BMS_a161_SW_DI_Open_Request = false;
static bool battery_BMS_a162_SW_No_Power_For_Support = false;
static bool battery_BMS_a163_SW_Contactor_Mismatch = false;
static bool battery_BMS_a164_SW_Uncontrolled_Regen = false;
static bool battery_BMS_a165_SW_Pack_Partial_Weld = false;
static bool battery_BMS_a166_SW_Pack_Full_Weld = false;
static bool battery_BMS_a167_SW_FC_Partial_Weld = false;
static bool battery_BMS_a168_SW_FC_Full_Weld = false;
static bool battery_BMS_a170_SW_Limp_Mode = false;
static bool battery_BMS_a171_SW_Stack_Voltage_Sense = false;
static bool battery_BMS_a174_SW_Charge_Failure = false;
static bool battery_BMS_a179_SW_Hvp_12V_Fault = false;
static bool battery_BMS_a180_SW_ECU_reset_blocked = false;
// Function definitions
inline const char* getContactorText(int index) { inline const char* getContactorText(int index) {
switch (index) { switch (index) {
case 0: case 0:
@ -767,7 +327,8 @@ inline const char* getFault(bool value) {
return value ? "ACTIVE" : "NOT_ACTIVE"; return value ? "ACTIVE" : "NOT_ACTIVE";
} }
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus void TeslaBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
//After values are mapped, we perform some safety checks, and do some serial printouts //After values are mapped, we perform some safety checks, and do some serial printouts
datalayer.battery.status.soh_pptt = 9900; //Tesla batteries do not send a SOH% value on bus. Hardcode to 99% datalayer.battery.status.soh_pptt = 9900; //Tesla batteries do not send a SOH% value on bus. Hardcode to 99%
@ -1138,7 +699,7 @@ void update_values_battery() { //This function maps all the values fetched via
#endif //DEBUG_LOG #endif //DEBUG_LOG
} }
void handle_incoming_can_frame_battery(CAN_frame rx_frame) { void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
static uint8_t mux = 0; static uint8_t mux = 0;
static uint16_t temp = 0; static uint16_t temp = 0;
static bool mux0_read = false; static bool mux0_read = false;
@ -1828,7 +1389,6 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
} }
} }
#if defined(TESLA_MODEL_SX_BATTERY) || defined(EXP_TESLA_BMS_DIGITAL_HVIL)
CAN_frame can_msg_1CF[] = { CAN_frame can_msg_1CF[] = {
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0x60, 0x69}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0x60, 0x69}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0x80, 0x89}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0x80, 0x89}},
@ -1862,9 +1422,8 @@ unsigned long lastSend118 = 0;
int index_1CF = 0; int index_1CF = 0;
int index_118 = 0; int index_118 = 0;
#endif //defined(TESLA_MODEL_SX_BATTERY) || defined(EXP_TESLA_BMS_DIGITAL_HVIL)
void transmit_can_battery(unsigned long currentMillis) { void TeslaBattery::transmit_can(unsigned long currentMillis) {
/*From bielec: My fist 221 message, to close the contactors is 0x41, 0x11, 0x01, 0x00, 0x00, 0x00, 0x20, 0x96 and then, /*From bielec: My fist 221 message, to close the contactors is 0x41, 0x11, 0x01, 0x00, 0x00, 0x00, 0x20, 0x96 and then,
to cause "hv_up_for_drive" I send an additional 221 message 0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA so to cause "hv_up_for_drive" I send an additional 221 message 0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA so
two 221 messages are being continuously transmitted. When I want to shut down, I stop the second message and only send two 221 messages are being continuously transmitted. When I want to shut down, I stop the second message and only send
@ -1874,7 +1433,7 @@ the first, for a few cycles, then stop all messages which causes the contactor
return; //All cellvoltages not read yet, do not proceed with contactor closing return; //All cellvoltages not read yet, do not proceed with contactor closing
} }
#if defined(TESLA_MODEL_SX_BATTERY) || defined(EXP_TESLA_BMS_DIGITAL_HVIL) if (operate_contactors) {
if ((datalayer.system.status.inverter_allows_contactor_closing) && (datalayer.battery.status.bms_status != FAULT)) { if ((datalayer.system.status.inverter_allows_contactor_closing) && (datalayer.battery.status.bms_status != FAULT)) {
if (currentMillis - lastSend1CF >= 10) { if (currentMillis - lastSend1CF >= 10) {
transmit_can_frame(&can_msg_1CF[index_1CF], can_config.battery); transmit_can_frame(&can_msg_1CF[index_1CF], can_config.battery);
@ -1893,7 +1452,7 @@ the first, for a few cycles, then stop all messages which causes the contactor
index_1CF = 0; index_1CF = 0;
index_118 = 0; index_118 = 0;
} }
#endif //defined(TESLA_MODEL_SX_BATTERY) || defined(EXP_TESLA_BMS_DIGITAL_HVIL) }
//Send 10ms message //Send 10ms message
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
@ -2032,6 +1591,12 @@ the first, for a few cycles, then stop all messages which causes the contactor
} }
} }
void printDebugIfActive(uint8_t symbol, const char* message) {
if (symbol == 1) {
logging.println(message);
}
}
void print_int_with_units(char* header, int value, char* units) { void print_int_with_units(char* header, int value, char* units) {
logging.print(header); logging.print(header);
logging.print(value); logging.print(value);
@ -2049,7 +1614,7 @@ void print_SOC(char* header, int SOC) {
logging.println("%"); logging.println("%");
} }
void printFaultCodesIfActive() { void TeslaBattery::printFaultCodesIfActive() {
if (battery_packCtrsClosingAllowed == 0) { if (battery_packCtrsClosingAllowed == 0) {
logging.println( logging.println(
"ERROR: Check high voltage connectors and interlock circuit! Closing contactor not allowed! Values: "); "ERROR: Check high voltage connectors and interlock circuit! Closing contactor not allowed! Values: ");
@ -2217,24 +1782,11 @@ void printFaultCodesIfActive() {
printDebugIfActive(battery_BMS_a180_SW_ECU_reset_blocked, "ERROR: BMS_a180_SW_ECU_reset_blocked"); printDebugIfActive(battery_BMS_a180_SW_ECU_reset_blocked, "ERROR: BMS_a180_SW_ECU_reset_blocked");
} }
void printDebugIfActive(uint8_t symbol, const char* message) { void TeslaModel3YBattery::setup(void) { // Performs one time setup at startup
if (symbol == 1) {
logging.println(message);
}
}
void setup_battery(void) { // Performs one time setup at startup if (allows_contactor_closing) {
datalayer.system.status.battery_allows_contactor_closing = true; *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", 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;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
#endif // TESLA_MODEL_SX_BATTERY
#ifdef TESLA_MODEL_3Y_BATTERY // Model 3/Y can be either LFP or NCM/A #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", 63); strncpy(datalayer.system.info.battery_protocol, "Tesla Model 3/Y", 63);
@ -2256,4 +1808,18 @@ void setup_battery(void) { // Performs one time setup at startup
#endif // TESLA_MODEL_3Y_BATTERY #endif // TESLA_MODEL_3Y_BATTERY
} }
void TeslaModelSXBattery::setup(void) {
if (allows_contactor_closing) {
*allows_contactor_closing = true;
}
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;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
}
#endif // TESLA_BATTERY #endif // TESLA_BATTERY

View file

@ -1,43 +1,512 @@
#ifndef TESLA_BATTERY_H #ifndef TESLA_BATTERY_H
#define TESLA_BATTERY_H #define TESLA_BATTERY_H
#include "../datalayer/datalayer.h"
#include "../include.h" #include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED #define BATTERY_SELECTED
#ifdef TESLA_MODEL_3Y_BATTERY
/* Modify these if needed */ #define SELECTED_BATTERY_CLASS TeslaModel3YBattery
#define MAXCHARGEPOWERALLOWED 15000 // 15000W we use a define since the value supplied by Tesla is always 0 #endif
#define MAXDISCHARGEPOWERALLOWED 60000 // 60000W we use a define since the value supplied by Tesla is always 0 #ifdef TESLA_MODEL_SX_BATTERY
#define SELECTED_BATTERY_CLASS TeslaModelSXBattery
/* Do not change the defines below */ #endif
#define RAMPDOWN_SOC 900 // 90.0 SOC% to start ramping down from max charge power towards 0 at 100.00%
#define RAMPDOWNPOWERALLOWED 10000 // What power we ramp down from towards top balancing
#define FLOAT_MAX_POWER_W 200 // W, what power to allow for top balancing battery
#define FLOAT_START_MV 20 // mV, how many mV under overvoltage to start float charging
#define MAX_PACK_VOLTAGE_SX_NCMA 4600 // V+1, if pack voltage goes over this, charge stops
#define MIN_PACK_VOLTAGE_SX_NCMA 3100 // V+1, if pack voltage goes over this, charge stops
#define MAX_PACK_VOLTAGE_3Y_NCMA 4030 // V+1, if pack voltage goes over this, charge stops
#define MIN_PACK_VOLTAGE_3Y_NCMA 3100 // V+1, if pack voltage goes below this, discharge stops
#define MAX_PACK_VOLTAGE_3Y_LFP 3880 // V+1, if pack voltage goes over this, charge stops
#define MIN_PACK_VOLTAGE_3Y_LFP 2968 // V+1, if pack voltage goes below this, discharge stops
#define MAX_CELL_DEVIATION_NCA_NCM 500 //LED turns yellow on the board if mv delta exceeds this value
#define MAX_CELL_DEVIATION_LFP 400 //LED turns yellow on the board if mv delta exceeds this value
#define MAX_CELL_VOLTAGE_NCA_NCM 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_NCA_NCM 2950 //Battery is put into emergency stop if one cell goes below this value
#define MAX_CELL_VOLTAGE_LFP 3650 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_LFP 2800 //Battery is put into emergency stop if one cell goes below this value
//#define EXP_TESLA_BMS_DIGITAL_HVIL // Experimental parameter. Forces the transmission of additional CAN frames for experimental purposes, to test potential HVIL issues in 3/Y packs with newer firmware. //#define EXP_TESLA_BMS_DIGITAL_HVIL // Experimental parameter. Forces the transmission of additional CAN frames for experimental purposes, to test potential HVIL issues in 3/Y packs with newer firmware.
void printFaultCodesIfActive(); class TeslaBattery : public CanBattery {
void printDebugIfActive(uint8_t symbol, const char* message); public:
void print_int_with_units(char* header, int value, char* units); // Use the default constructor to create the first or single battery.
void print_SOC(char* header, int SOC); TeslaBattery() { allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing; }
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
#ifdef DOUBLE_BATTERY virtual void handle_incoming_can_frame(CAN_frame rx_frame);
void printFaultCodesIfActive_battery2(); virtual void update_values();
#endif //DOUBLE_BATTERY virtual void transmit_can(unsigned long currentMillis);
protected:
/* Modify these if needed */
static const int MAXCHARGEPOWERALLOWED =
15000; // 15000W we use a define since the value supplied by Tesla is always 0
static const int MAXDISCHARGEPOWERALLOWED =
60000; // 60000W we use a define since the value supplied by Tesla is always 0
/* Do not change the defines below */
static const int RAMPDOWN_SOC = 900; // 90.0 SOC% to start ramping down from max charge power towards 0 at 100.00%
static const int RAMPDOWNPOWERALLOWED = 10000; // What power we ramp down from towards top balancing
static const int FLOAT_MAX_POWER_W = 200; // W, what power to allow for top balancing battery
static const int FLOAT_START_MV = 20; // mV, how many mV under overvoltage to start float charging
static const int MAX_PACK_VOLTAGE_SX_NCMA = 4600; // V+1, if pack voltage goes over this, charge stops
static const int MIN_PACK_VOLTAGE_SX_NCMA = 3100; // V+1, if pack voltage goes over this, charge stops
static const int MAX_PACK_VOLTAGE_3Y_NCMA = 4030; // V+1, if pack voltage goes over this, charge stops
static const int MIN_PACK_VOLTAGE_3Y_NCMA = 3100; // V+1, if pack voltage goes below this, discharge stops
static const int MAX_PACK_VOLTAGE_3Y_LFP = 3880; // V+1, if pack voltage goes over this, charge stops
static const int MIN_PACK_VOLTAGE_3Y_LFP = 2968; // V+1, if pack voltage goes below this, discharge stops
static const int MAX_CELL_DEVIATION_NCA_NCM = 500; //LED turns yellow on the board if mv delta exceeds this value
static const int MAX_CELL_DEVIATION_LFP = 400; //LED turns yellow on the board if mv delta exceeds this value
static const int MAX_CELL_VOLTAGE_NCA_NCM =
4250; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_NCA_NCM =
2950; //Battery is put into emergency stop if one cell goes below this value
static const int MAX_CELL_VOLTAGE_LFP = 3650; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_LFP = 2800; //Battery is put into emergency stop if one cell goes below this value
bool operate_contactors = false;
// If not null, this battery decides when the contactor can be closed and writes the value here.
bool* allows_contactor_closing;
void printFaultCodesIfActive();
unsigned long previousMillis10 = 0; // will store last time a 50ms CAN Message was sent
unsigned long previousMillis50 = 0; // will store last time a 50ms CAN Message was sent
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was sent
unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was sent
bool alternate243 = false;
//0x221 545 VCFRONT_LVPowerState: "GenMsgCycleTime" 50ms
CAN_frame TESLA_221_1 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x41, 0x11, 0x01, 0x00, 0x00, 0x00, 0x20, 0x96}}; //Contactor frame 221 - close contactors
CAN_frame TESLA_221_2 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA}}; //Contactor Frame 221 - hv_up_for_drive
//0x241 VCFRONT_coolant 100ms
CAN_frame TESLA_241 = {.FD = false,
.ext_ID = false,
.DLC = 7,
.ID = 0x241,
.data = {0x3C, 0x78, 0x2C, 0x0F, 0x1E, 0x5B, 0x00}};
//0x242 VCLEFT_LVPowerState 100ms
CAN_frame TESLA_242 = {.FD = false, .ext_ID = false, .DLC = 2, .ID = 0x242, .data = {0x10, 0x95}};
//0x243 VCRIGHT_hvacStatus 50ms
CAN_frame TESLA_243_1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x243,
.data = {0xC9, 0x00, 0xEB, 0xD4, 0x31, 0x32, 0x02, 0x00}};
CAN_frame TESLA_243_2 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x243,
.data = {0x08, 0x81, 0x42, 0x60, 0x92, 0x2C, 0x0E, 0x09}};
//0x129 SteeringAngle 10ms
CAN_frame TESLA_129 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x129,
.data = {0x21, 0x24, 0x36, 0x5F, 0x00, 0x20, 0xFF, 0x3F}};
//0x612 UDS diagnostic requests - on demand
CAN_frame TESLA_602 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x602,
.data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}};
uint8_t stateMachineClearIsolationFault = 0xFF;
uint8_t stateMachineBMSReset = 0xFF;
uint16_t sendContactorClosingMessagesStill = 300;
uint16_t battery_cell_max_v = 3300;
uint16_t battery_cell_min_v = 3300;
uint16_t battery_cell_deviation_mV = 0; //contains the deviation between highest and lowest cell in mV
bool cellvoltagesRead = false;
//0x3d2: 978 BMS_kwhCounter
uint32_t battery_total_discharge = 0;
uint32_t battery_total_charge = 0;
//0x352: 850 BMS_energyStatus
bool BMS352_mux = false; // variable to store when 0x352 mux is present
uint16_t battery_energy_buffer = 0; // kWh
uint16_t battery_energy_buffer_m1 = 0; // kWh
uint16_t battery_energy_to_charge_complete = 0; // kWh
uint16_t battery_energy_to_charge_complete_m1 = 0; // kWh
uint16_t battery_expected_energy_remaining = 0; // kWh
uint16_t battery_expected_energy_remaining_m1 = 0; // kWh
bool battery_full_charge_complete = false; // Changed to bool
bool battery_fully_charged = false; // Changed to bool
uint16_t battery_ideal_energy_remaining = 0; // kWh
uint16_t battery_ideal_energy_remaining_m0 = 0; // kWh
uint16_t battery_nominal_energy_remaining = 0; // kWh
uint16_t battery_nominal_energy_remaining_m0 = 0; // kWh
uint16_t battery_nominal_full_pack_energy = 0; // Kwh
uint16_t battery_nominal_full_pack_energy_m0 = 0; // Kwh
//0x132 306 HVBattAmpVolt
uint16_t battery_volts = 0; // V
int16_t battery_amps = 0; // A
int16_t battery_raw_amps = 0; // A
uint16_t battery_charge_time_remaining = 0; // Minutes
//0x252 594 BMS_powerAvailable
uint16_t BMS_maxRegenPower = 0; //rename from battery_regenerative_limit
uint16_t BMS_maxDischargePower = 0; // rename from battery_discharge_limit
uint16_t BMS_maxStationaryHeatPower = 0; //rename from battery_max_heat_park
uint16_t BMS_hvacPowerBudget = 0; //rename from battery_hvac_max_power
uint8_t BMS_notEnoughPowerForHeatPump = 0;
uint8_t BMS_powerLimitState = 0;
uint8_t BMS_inverterTQF = 0;
//0x2d2: 722 BMSVAlimits
uint16_t battery_max_discharge_current = 0;
uint16_t battery_max_charge_current = 0;
uint16_t battery_bms_max_voltage = 0;
uint16_t battery_bms_min_voltage = 0;
//0x2b4: 692 PCS_dcdcRailStatus
uint16_t battery_dcdcHvBusVolt = 0; // Change name from battery_high_voltage to battery_dcdcHvBusVolt
uint16_t battery_dcdcLvBusVolt = 0; // Change name from battery_low_voltage to battery_dcdcLvBusVolt
uint16_t battery_dcdcLvOutputCurrent = 0; // Change name from battery_output_current to battery_dcdcLvOutputCurrent
//0x292: 658 BMS_socStatus
uint16_t battery_beginning_of_life = 0; // kWh
uint16_t battery_soc_min = 0;
uint16_t battery_soc_max = 0;
uint16_t battery_soc_ui = 0; //Change name from battery_soc_vi to reflect DBC battery_soc_ui
uint16_t battery_soc_ave = 0;
uint8_t battery_battTempPct = 0;
//0x392: BMS_packConfig
uint32_t battery_packMass = 0;
uint32_t battery_platformMaxBusVoltage = 0;
uint32_t battery_packConfigMultiplexer = 0;
uint32_t battery_moduleType = 0;
uint32_t battery_reservedConfig = 0;
//0x332: 818 BattBrickMinMax:BMS_bmbMinMax
int16_t battery_max_temp = 0; // C*
int16_t battery_min_temp = 0; // C*
uint16_t battery_BrickVoltageMax = 0;
uint16_t battery_BrickVoltageMin = 0;
uint8_t battery_BrickTempMaxNum = 0;
uint8_t battery_BrickTempMinNum = 0;
uint8_t battery_BrickModelTMax = 0;
uint8_t battery_BrickModelTMin = 0;
uint8_t battery_BrickVoltageMaxNum = 0; //rename from battery_max_vno
uint8_t battery_BrickVoltageMinNum = 0; //rename from battery_min_vno
//0x20A: 522 HVP_contactorState
uint8_t battery_contactor = 0; //State of contactor
uint8_t battery_hvil_status = 0;
uint8_t battery_packContNegativeState = 0;
uint8_t battery_packContPositiveState = 0;
uint8_t battery_packContactorSetState = 0;
bool battery_packCtrsClosingAllowed = false; // Change to bool
bool battery_pyroTestInProgress = false; // Change to bool
bool battery_packCtrsOpenNowRequested = false; // Change to bool
bool battery_packCtrsOpenRequested = false; // Change to bool
uint8_t battery_packCtrsRequestStatus = 0;
bool battery_packCtrsResetRequestRequired = false; // Change to bool
bool battery_dcLinkAllowedToEnergize = false; // Change to bool
bool battery_fcContNegativeAuxOpen = false; // Change to bool
uint8_t battery_fcContNegativeState = 0;
bool battery_fcContPositiveAuxOpen = false; // Change to bool
uint8_t battery_fcContPositiveState = 0;
uint8_t battery_fcContactorSetState = 0;
bool battery_fcCtrsClosingAllowed = false; // Change to bool
bool battery_fcCtrsOpenNowRequested = false; // Change to bool
bool battery_fcCtrsOpenRequested = false; // Change to bool
uint8_t battery_fcCtrsRequestStatus = 0;
bool battery_fcCtrsResetRequestRequired = false; // Change to bool
bool battery_fcLinkAllowedToEnergize = false; // Change to bool
//0x72A: BMS_serialNumber
uint8_t BMS_SerialNumber[14] = {0}; // Stores raw HEX values for ASCII chars
//0x212: 530 BMS_status
bool battery_BMS_hvacPowerRequest = false; //Change to bool
bool battery_BMS_notEnoughPowerForDrive = false; //Change to bool
bool battery_BMS_notEnoughPowerForSupport = false; //Change to bool
bool battery_BMS_preconditionAllowed = false; //Change to bool
bool battery_BMS_updateAllowed = false; //Change to bool
bool battery_BMS_activeHeatingWorthwhile = false; //Change to bool
bool battery_BMS_cpMiaOnHvs = false; //Change to bool
uint8_t battery_BMS_contactorState = 0;
uint8_t battery_BMS_state = 0;
uint8_t battery_BMS_hvState = 0;
uint16_t battery_BMS_isolationResistance = 0;
bool battery_BMS_chargeRequest = false; //Change to bool
bool battery_BMS_keepWarmRequest = false; //Change to bool
uint8_t battery_BMS_uiChargeStatus = 0;
bool battery_BMS_diLimpRequest = false; //Change to bool
bool battery_BMS_okToShipByAir = false; //Change to bool
bool battery_BMS_okToShipByLand = false; //Change to bool
uint32_t battery_BMS_chgPowerAvailable = 0;
uint8_t battery_BMS_chargeRetryCount = 0;
bool battery_BMS_pcsPwmEnabled = false; //Change to bool
bool battery_BMS_ecuLogUploadRequest = false; //Change to bool
uint8_t battery_BMS_minPackTemperature = 0;
// 0x224:548 PCS_dcdcStatus
uint8_t battery_PCS_dcdcPrechargeStatus = 0;
uint8_t battery_PCS_dcdc12VSupportStatus = 0;
uint8_t battery_PCS_dcdcHvBusDischargeStatus = 0;
uint16_t battery_PCS_dcdcMainState = 0;
uint8_t battery_PCS_dcdcSubState = 0;
bool battery_PCS_dcdcFaulted = false; //Change to bool
bool battery_PCS_dcdcOutputIsLimited = false; //Change to bool
uint32_t battery_PCS_dcdcMaxOutputCurrentAllowed = 0;
uint8_t battery_PCS_dcdcPrechargeRtyCnt = 0;
uint8_t battery_PCS_dcdc12VSupportRtyCnt = 0;
uint8_t battery_PCS_dcdcDischargeRtyCnt = 0;
uint8_t battery_PCS_dcdcPwmEnableLine = 0;
uint8_t battery_PCS_dcdcSupportingFixedLvTarget = 0;
uint8_t battery_PCS_ecuLogUploadRequest = 0;
uint8_t battery_PCS_dcdcPrechargeRestartCnt = 0;
uint8_t battery_PCS_dcdcInitialPrechargeSubState = 0;
//0x312: 786 BMS_thermalStatus
uint16_t BMS_powerDissipation = 0;
uint16_t BMS_flowRequest = 0;
uint16_t BMS_inletActiveCoolTargetT = 0;
uint16_t BMS_inletPassiveTargetT = 0;
uint16_t BMS_inletActiveHeatTargetT = 0;
uint16_t BMS_packTMin = 0;
uint16_t BMS_packTMax = 0;
bool BMS_pcsNoFlowRequest = false;
bool BMS_noFlowRequest = false;
//0x2A4; 676 PCS_thermalStatus
int16_t PCS_chgPhATemp = 0;
int16_t PCS_chgPhBTemp = 0;
int16_t PCS_chgPhCTemp = 0;
int16_t PCS_dcdcTemp = 0;
int16_t PCS_ambientTemp = 0;
//0x2C4; 708 PCS_logging
uint16_t PCS_logMessageSelect = 0;
uint16_t PCS_dcdcMaxLvOutputCurrent = 0;
uint16_t PCS_dcdcCurrentLimit = 0;
uint16_t PCS_dcdcLvOutputCurrentTempLimit = 0;
uint16_t PCS_dcdcUnifiedCommand = 0;
uint16_t PCS_dcdcCLAControllerOutput = 0;
int16_t PCS_dcdcTankVoltage = 0;
uint16_t PCS_dcdcTankVoltageTarget = 0;
uint16_t PCS_dcdcClaCurrentFreq = 0;
int16_t PCS_dcdcTCommMeasured = 0;
uint16_t PCS_dcdcShortTimeUs = 0;
uint16_t PCS_dcdcHalfPeriodUs = 0;
uint16_t PCS_dcdcIntervalMaxFrequency = 0;
uint16_t PCS_dcdcIntervalMaxHvBusVolt = 0;
uint16_t PCS_dcdcIntervalMaxLvBusVolt = 0;
uint16_t PCS_dcdcIntervalMaxLvOutputCurr = 0;
uint16_t PCS_dcdcIntervalMinFrequency = 0;
uint16_t PCS_dcdcIntervalMinHvBusVolt = 0;
uint16_t PCS_dcdcIntervalMinLvBusVolt = 0;
uint16_t PCS_dcdcIntervalMinLvOutputCurr = 0;
uint32_t PCS_dcdc12vSupportLifetimekWh = 0;
//0x7AA: //1962 HVP_debugMessage:
uint8_t HVP_debugMessageMultiplexer = 0;
bool HVP_gpioPassivePyroDepl = false; //Change to bool
bool HVP_gpioPyroIsoEn = false; //Change to bool
bool HVP_gpioCpFaultIn = false; //Change to bool
bool HVP_gpioPackContPowerEn = false; //Change to bool
bool HVP_gpioHvCablesOk = false; //Change to bool
bool HVP_gpioHvpSelfEnable = false; //Change to bool
bool HVP_gpioLed = false; //Change to bool
bool HVP_gpioCrashSignal = false; //Change to bool
bool HVP_gpioShuntDataReady = false; //Change to bool
bool HVP_gpioFcContPosAux = false; //Change to bool
bool HVP_gpioFcContNegAux = false; //Change to bool
bool HVP_gpioBmsEout = false; //Change to bool
bool HVP_gpioCpFaultOut = false; //Change to bool
bool HVP_gpioPyroPor = false; //Change to bool
bool HVP_gpioShuntEn = false; //Change to bool
bool HVP_gpioHvpVerEn = false; //Change to bool
bool HVP_gpioPackCoontPosFlywheel = false; //Change to bool
bool HVP_gpioCpLatchEnable = false; //Change to bool
bool HVP_gpioPcsEnable = false; //Change to bool
bool HVP_gpioPcsDcdcPwmEnable = false; //Change to bool
bool HVP_gpioPcsChargePwmEnable = false; //Change to bool
bool HVP_gpioFcContPowerEnable = false; //Change to bool
bool HVP_gpioHvilEnable = false; //Change to bool
bool HVP_gpioSecDrdy = false; //Change to bool
uint16_t HVP_hvp1v5Ref = 0;
int16_t HVP_shuntCurrentDebug = 0;
bool HVP_packCurrentMia = false; //Change to bool
bool HVP_auxCurrentMia = false; //Change to bool
bool HVP_currentSenseMia = false; //Change to bool
bool HVP_shuntRefVoltageMismatch = false; //Change to bool
bool HVP_shuntThermistorMia = false; //Change to bool
bool HVP_shuntHwMia = false; //Change to bool
int16_t HVP_dcLinkVoltage = 0;
int16_t HVP_packVoltage = 0;
int16_t HVP_fcLinkVoltage = 0;
uint16_t HVP_packContVoltage = 0;
int16_t HVP_packNegativeV = 0;
int16_t HVP_packPositiveV = 0;
uint16_t HVP_pyroAnalog = 0;
int16_t HVP_dcLinkNegativeV = 0;
int16_t HVP_dcLinkPositiveV = 0;
int16_t HVP_fcLinkNegativeV = 0;
uint16_t HVP_fcContCoilCurrent = 0;
uint16_t HVP_fcContVoltage = 0;
uint16_t HVP_hvilInVoltage = 0;
uint16_t HVP_hvilOutVoltage = 0;
int16_t HVP_fcLinkPositiveV = 0;
uint16_t HVP_packContCoilCurrent = 0;
uint16_t HVP_battery12V = 0;
int16_t HVP_shuntRefVoltageDbg = 0;
int16_t HVP_shuntAuxCurrentDbg = 0;
int16_t HVP_shuntBarTempDbg = 0;
int16_t HVP_shuntAsicTempDbg = 0;
uint8_t HVP_shuntAuxCurrentStatus = 0;
uint8_t HVP_shuntBarTempStatus = 0;
uint8_t HVP_shuntAsicTempStatus = 0;
//0x3aa: HVP_alertMatrix1 Fault codes // Change to bool
bool battery_WatchdogReset = false; //Warns if the processor has experienced a reset due to watchdog reset.
bool battery_PowerLossReset = false; //Warns if the processor has experienced a reset due to power loss.
bool battery_SwAssertion = false; //An internal software assertion has failed.
bool battery_CrashEvent = false; //Warns if the crash signal is detected by HVP
bool battery_OverDchgCurrentFault = false; //Warns if the pack discharge is above max discharge current limit
bool battery_OverChargeCurrentFault = false; //Warns if the pack discharge current is above max charge current limit
bool battery_OverCurrentFault = false; //Warns if the pack current (discharge or charge) is above max current limit.
bool battery_OverTemperatureFault = false; //A pack module temperature is above maximum temperature limit
bool battery_OverVoltageFault = false; //A brick voltage is above maximum voltage limit
bool battery_UnderVoltageFault = false; //A brick voltage is below minimum voltage limit
bool battery_PrimaryBmbMiaFault =
false; //Warns if the voltage and temperature readings from primary BMB chain are mia
bool battery_SecondaryBmbMiaFault =
false; //Warns if the voltage and temperature readings from secondary BMB chain are mia
bool battery_BmbMismatchFault =
false; //Warns if the primary and secondary BMB chain readings don't match with each other
bool battery_BmsHviMiaFault = false; //Warns if the BMS node is mia on HVS or HVI CAN
bool battery_CpMiaFault = false; //Warns if the CP node is mia on HVS CAN
bool battery_PcsMiaFault = false; //The PCS node is mia on HVS CAN
bool battery_BmsFault = false; //Warns if the BMS ECU has faulted
bool battery_PcsFault = false; //Warns if the PCS ECU has faulted
bool battery_CpFault = false; //Warns if the CP ECU has faulted
bool battery_ShuntHwMiaFault = false; //Warns if the shunt current reading is not available
bool battery_PyroMiaFault = false; //Warns if the pyro squib is not connected
bool battery_hvsMiaFault = false; //Warns if the pack contactor hw fault
bool battery_hviMiaFault = false; //Warns if the FC contactor hw fault
bool battery_Supply12vFault = false; //Warns if the low voltage (12V) battery is below minimum voltage threshold
bool battery_VerSupplyFault = false; //Warns if the Energy reserve voltage supply is below minimum voltage threshold
bool battery_HvilFault = false; //Warn if a High Voltage Inter Lock fault is detected
bool battery_BmsHvsMiaFault = false; //Warns if the BMS node is mia on HVS or HVI CAN
bool battery_PackVoltMismatchFault =
false; //Warns if the pack voltage doesn't match approximately with sum of brick voltages
bool battery_EnsMiaFault = false; //Warns if the ENS line is not connected to HVC
bool battery_PackPosCtrArcFault = false; //Warns if the HVP detectes series arc at pack contactor
bool battery_packNegCtrArcFault = false; //Warns if the HVP detectes series arc at FC contactor
bool battery_ShuntHwAndBmsMiaFault = false;
bool battery_fcContHwFault = false;
bool battery_robinOverVoltageFault = false;
bool battery_packContHwFault = false;
bool battery_pyroFuseBlown = false;
bool battery_pyroFuseFailedToBlow = false;
bool battery_CpilFault = false;
bool battery_PackContactorFellOpen = false;
bool battery_FcContactorFellOpen = false;
bool battery_packCtrCloseBlocked = false;
bool battery_fcCtrCloseBlocked = false;
bool battery_packContactorForceOpen = false;
bool battery_fcContactorForceOpen = false;
bool battery_dcLinkOverVoltage = false;
bool battery_shuntOverTemperature = false;
bool battery_passivePyroDeploy = false;
bool battery_logUploadRequest = false;
bool battery_packCtrCloseFailed = false;
bool battery_fcCtrCloseFailed = false;
bool battery_shuntThermistorMia = false;
//0x320: 800 BMS_alertMatrix
uint8_t battery_BMS_matrixIndex = 0; // Changed to bool
bool battery_BMS_a061_robinBrickOverVoltage = false;
bool battery_BMS_a062_SW_BrickV_Imbalance = false;
bool battery_BMS_a063_SW_ChargePort_Fault = false;
bool battery_BMS_a064_SW_SOC_Imbalance = false;
bool battery_BMS_a127_SW_shunt_SNA = false;
bool battery_BMS_a128_SW_shunt_MIA = false;
bool battery_BMS_a069_SW_Low_Power = false;
bool battery_BMS_a130_IO_CAN_Error = false;
bool battery_BMS_a071_SW_SM_TransCon_Not_Met = false;
bool battery_BMS_a132_HW_BMB_OTP_Uncorrctbl = false;
bool battery_BMS_a134_SW_Delayed_Ctr_Off = false;
bool battery_BMS_a075_SW_Chg_Disable_Failure = false;
bool battery_BMS_a076_SW_Dch_While_Charging = false;
bool battery_BMS_a017_SW_Brick_OV = false;
bool battery_BMS_a018_SW_Brick_UV = false;
bool battery_BMS_a019_SW_Module_OT = false;
bool battery_BMS_a021_SW_Dr_Limits_Regulation = false;
bool battery_BMS_a022_SW_Over_Current = false;
bool battery_BMS_a023_SW_Stack_OV = false;
bool battery_BMS_a024_SW_Islanded_Brick = false;
bool battery_BMS_a025_SW_PwrBalance_Anomaly = false;
bool battery_BMS_a026_SW_HFCurrent_Anomaly = false;
bool battery_BMS_a087_SW_Feim_Test_Blocked = false;
bool battery_BMS_a088_SW_VcFront_MIA_InDrive = false;
bool battery_BMS_a089_SW_VcFront_MIA = false;
bool battery_BMS_a090_SW_Gateway_MIA = false;
bool battery_BMS_a091_SW_ChargePort_MIA = false;
bool battery_BMS_a092_SW_ChargePort_Mia_On_Hv = false;
bool battery_BMS_a034_SW_Passive_Isolation = false;
bool battery_BMS_a035_SW_Isolation = false;
bool battery_BMS_a036_SW_HvpHvilFault = false;
bool battery_BMS_a037_SW_Flood_Port_Open = false;
bool battery_BMS_a158_SW_HVP_HVI_Comms = false;
bool battery_BMS_a039_SW_DC_Link_Over_Voltage = false;
bool battery_BMS_a041_SW_Power_On_Reset = false;
bool battery_BMS_a042_SW_MPU_Error = false;
bool battery_BMS_a043_SW_Watch_Dog_Reset = false;
bool battery_BMS_a044_SW_Assertion = false;
bool battery_BMS_a045_SW_Exception = false;
bool battery_BMS_a046_SW_Task_Stack_Usage = false;
bool battery_BMS_a047_SW_Task_Stack_Overflow = false;
bool battery_BMS_a048_SW_Log_Upload_Request = false;
bool battery_BMS_a169_SW_FC_Pack_Weld = false;
bool battery_BMS_a050_SW_Brick_Voltage_MIA = false;
bool battery_BMS_a051_SW_HVC_Vref_Bad = false;
bool battery_BMS_a052_SW_PCS_MIA = false;
bool battery_BMS_a053_SW_ThermalModel_Sanity = false;
bool battery_BMS_a054_SW_Ver_Supply_Fault = false;
bool battery_BMS_a176_SW_GracefulPowerOff = false;
bool battery_BMS_a059_SW_Pack_Voltage_Sensing = false;
bool battery_BMS_a060_SW_Leakage_Test_Failure = false;
bool battery_BMS_a077_SW_Charger_Regulation = false;
bool battery_BMS_a081_SW_Ctr_Close_Blocked = false;
bool battery_BMS_a082_SW_Ctr_Force_Open = false;
bool battery_BMS_a083_SW_Ctr_Close_Failure = false;
bool battery_BMS_a084_SW_Sleep_Wake_Aborted = false;
bool battery_BMS_a094_SW_Drive_Inverter_MIA = false;
bool battery_BMS_a099_SW_BMB_Communication = false;
bool battery_BMS_a105_SW_One_Module_Tsense = false;
bool battery_BMS_a106_SW_All_Module_Tsense = false;
bool battery_BMS_a107_SW_Stack_Voltage_MIA = false;
bool battery_BMS_a121_SW_NVRAM_Config_Error = false;
bool battery_BMS_a122_SW_BMS_Therm_Irrational = false;
bool battery_BMS_a123_SW_Internal_Isolation = false;
bool battery_BMS_a129_SW_VSH_Failure = false;
bool battery_BMS_a131_Bleed_FET_Failure = false;
bool battery_BMS_a136_SW_Module_OT_Warning = false;
bool battery_BMS_a137_SW_Brick_UV_Warning = false;
bool battery_BMS_a138_SW_Brick_OV_Warning = false;
bool battery_BMS_a139_SW_DC_Link_V_Irrational = false;
bool battery_BMS_a141_SW_BMB_Status_Warning = false;
bool battery_BMS_a144_Hvp_Config_Mismatch = false;
bool battery_BMS_a145_SW_SOC_Change = false;
bool battery_BMS_a146_SW_Brick_Overdischarged = false;
bool battery_BMS_a149_SW_Missing_Config_Block = false;
bool battery_BMS_a151_SW_external_isolation = false;
bool battery_BMS_a156_SW_BMB_Vref_bad = false;
bool battery_BMS_a157_SW_HVP_HVS_Comms = false;
bool battery_BMS_a159_SW_HVP_ECU_Error = false;
bool battery_BMS_a161_SW_DI_Open_Request = false;
bool battery_BMS_a162_SW_No_Power_For_Support = false;
bool battery_BMS_a163_SW_Contactor_Mismatch = false;
bool battery_BMS_a164_SW_Uncontrolled_Regen = false;
bool battery_BMS_a165_SW_Pack_Partial_Weld = false;
bool battery_BMS_a166_SW_Pack_Full_Weld = false;
bool battery_BMS_a167_SW_FC_Partial_Weld = false;
bool battery_BMS_a168_SW_FC_Full_Weld = false;
bool battery_BMS_a170_SW_Limp_Mode = false;
bool battery_BMS_a171_SW_Stack_Voltage_Sense = false;
bool battery_BMS_a174_SW_Charge_Failure = false;
bool battery_BMS_a179_SW_Hvp_12V_Fault = false;
bool battery_BMS_a180_SW_ECU_reset_blocked = false;
};
class TeslaModel3YBattery : public TeslaBattery {
public:
TeslaModel3YBattery() {
#ifdef EXP_TESLA_BMS_DIGITAL_HVIL
operate_contactors = true;
#endif
}
virtual void setup(void);
};
class TeslaModelSXBattery : public TeslaBattery {
public:
TeslaModelSXBattery() { operate_contactors = true; }
virtual void setup(void);
};
#endif #endif

View file

@ -3,153 +3,89 @@
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "TEST-FAKE-BATTERY.h" #include "TEST-FAKE-BATTERY.h"
/* 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 unsigned long previousMillis10s = 0; // will store last time a 1s CAN Message was send
CAN_frame TEST = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x123,
.data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}};
void print_units(char* header, int value, char* units) { void print_units(char* header, int value, char* units) {
logging.print(header); logging.print(header);
logging.print(value); logging.print(value);
logging.print(units); logging.print(units);
} }
void update_values_battery() { /* This function puts fake values onto the parameters sent towards the inverter */ void TestFakeBattery::
update_values() { /* This function puts fake values onto the parameters sent towards the inverter */
datalayer.battery.status.real_soc = 5000; // 50.00% datalayer_battery->status.real_soc = 5000; // 50.00%
datalayer.battery.status.soh_pptt = 9900; // 99.00% datalayer_battery->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_battery->status.voltage_dV = 3700; // 370.0V , value set in startup in .ino file, editable via webUI
datalayer.battery.status.current_dA = 0; // 0 A datalayer_battery->status.current_dA = 0; // 0 A
datalayer.battery.info.total_capacity_Wh = 30000; // 30kWh datalayer_battery->info.total_capacity_Wh = 30000; // 30kWh
datalayer.battery.status.remaining_capacity_Wh = 15000; // 15kWh datalayer_battery->status.remaining_capacity_Wh = 15000; // 15kWh
datalayer.battery.status.cell_max_voltage_mV = 3596; datalayer_battery->status.cell_max_voltage_mV = 3596;
datalayer.battery.status.cell_min_voltage_mV = 3500; datalayer_battery->status.cell_min_voltage_mV = 3500;
datalayer.battery.status.temperature_min_dC = 50; // 5.0*C datalayer_battery->status.temperature_min_dC = 50; // 5.0*C
datalayer.battery.status.temperature_max_dC = 60; // 6.0*C datalayer_battery->status.temperature_max_dC = 60; // 6.0*C
datalayer.battery.status.max_discharge_power_W = 5000; // 5kW datalayer_battery->status.max_discharge_power_W = 5000; // 5kW
datalayer.battery.status.max_charge_power_W = 5000; // 5kW datalayer_battery->status.max_charge_power_W = 5000; // 5kW
for (int i = 0; i < 97; ++i) { for (int i = 0; i < 97; ++i) {
datalayer.battery.status.cell_voltages_mV[i] = 3700 + random(-20, 21); datalayer_battery->status.cell_voltages_mV[i] = 3700 + random(-20, 21);
} }
//Fake that we get CAN messages //Fake that we get CAN messages
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
/*Finally print out values to serial if configured to do so*/ /*Finally print out values to serial if configured to do so*/
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
logging.println("FAKE Values going to inverter"); logging.println("FAKE Values going to inverter");
print_units("SOH%: ", (datalayer.battery.status.soh_pptt * 0.01), "% "); print_units("SOH%: ", (datalayer_battery->status.soh_pptt * 0.01), "% ");
print_units(", SOC%: ", (datalayer.battery.status.reported_soc * 0.01), "% "); print_units(", SOC%: ", (datalayer_battery->status.reported_soc * 0.01), "% ");
print_units(", Voltage: ", (datalayer.battery.status.voltage_dV * 0.1), "V "); print_units(", Voltage: ", (datalayer_battery->status.voltage_dV * 0.1), "V ");
print_units(", Max discharge power: ", datalayer.battery.status.max_discharge_power_W, "W "); print_units(", Max discharge power: ", datalayer_battery->status.max_discharge_power_W, "W ");
print_units(", Max charge power: ", datalayer.battery.status.max_charge_power_W, "W "); print_units(", Max charge power: ", datalayer_battery->status.max_charge_power_W, "W ");
print_units(", Max temp: ", (datalayer.battery.status.temperature_max_dC * 0.1), "°C "); print_units(", Max temp: ", (datalayer_battery->status.temperature_max_dC * 0.1), "°C ");
print_units(", Min temp: ", (datalayer.battery.status.temperature_min_dC * 0.1), "°C "); print_units(", Min temp: ", (datalayer_battery->status.temperature_min_dC * 0.1), "°C ");
print_units(", Max cell voltage: ", datalayer.battery.status.cell_max_voltage_mV, "mV "); print_units(", Max cell voltage: ", datalayer_battery->status.cell_max_voltage_mV, "mV ");
print_units(", Min cell voltage: ", datalayer.battery.status.cell_min_voltage_mV, "mV "); print_units(", Min cell voltage: ", datalayer_battery->status.cell_min_voltage_mV, "mV ");
logging.println(""); logging.println("");
#endif #endif
} }
#ifdef DOUBLE_BATTERY void TestFakeBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
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%
//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.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] = 3700 + random(-20, 21);
} }
//Fake that we get CAN messages void TestFakeBattery::transmit_can(unsigned long currentMillis) {
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
/*Finally print out values to serial if configured to do so*/
#ifdef DEBUG_LOG
logging.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 ");
logging.println("");
#endif
}
void handle_incoming_can_frame_battery2(CAN_frame rx_frame) {
datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
}
#endif // DOUBLE_BATTERY
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
}
void transmit_can_battery(unsigned long currentMillis) {
// Send 100ms CAN Message // Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis; previousMillis100 = currentMillis;
// Put fake messages here incase you want to test sending CAN // Put fake messages here incase you want to test sending CAN
//transmit_can_frame(&TEST, can_config.battery); //transmit_can_frame(&TEST, can_interface);
} }
} }
void setup_battery(void) { // Performs one time setup at startup void TestFakeBattery::setup(void) { // Performs one time setup at startup
randomSeed(analogRead(0)); randomSeed(analogRead(0));
strncpy(datalayer.system.info.battery_protocol, "Fake battery for testing purposes", 63); strncpy(datalayer.system.info.battery_protocol, "Fake battery for testing purposes", 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.max_design_voltage_dV = datalayer_battery->info.max_design_voltage_dV =
4040; // 404.4V, over this, charging is not possible (goes into forced discharge) 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 2450; // 245.0V under this, discharging further is disabled datalayer_battery->info.min_design_voltage_dV = 2450; // 245.0V under this, discharging further is disabled
datalayer.battery.info.number_of_cells = 96; datalayer_battery->info.number_of_cells = 96;
datalayer.system.status.battery_allows_contactor_closing = true;
if (allows_contactor_closing) {
*allows_contactor_closing = true;
}
} }
#endif #endif

View file

@ -1,11 +1,50 @@
#ifndef TEST_FAKE_BATTERY_H #ifndef TEST_FAKE_BATTERY_H
#define TEST_FAKE_BATTERY_H #define TEST_FAKE_BATTERY_H
#include "../datalayer/datalayer.h"
#include "../include.h" #include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED #define BATTERY_SELECTED
#define MAX_CELL_DEVIATION_MV 9999 #define SELECTED_BATTERY_CLASS TestFakeBattery
void setup_battery(void); class TestFakeBattery : public CanBattery {
void transmit_can_frame(CAN_frame* tx_frame, int interface); public:
// Use this constructor for the second battery.
TestFakeBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, int targetCan) {
datalayer_battery = datalayer_ptr;
can_interface = targetCan;
allows_contactor_closing = nullptr;
}
// Use the default constructor to create the first or single battery.
TestFakeBattery() {
datalayer_battery = &datalayer.battery;
can_interface = can_config.battery;
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
}
virtual void setup();
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
private:
DATALAYER_BATTERY_TYPE* datalayer_battery;
int can_interface;
// If not null, this battery decides when the contactor can be closed and writes the value here.
bool* allows_contactor_closing;
static const int MAX_CELL_DEVIATION_MV = 9999;
unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
unsigned long previousMillis10s = 0; // will store last time a 1s CAN Message was send
CAN_frame TEST = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x123,
.data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}};
};
#endif #endif

View file

@ -225,7 +225,7 @@ void handle_contactors() {
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
void handle_contactors_battery2() { void handle_contactors_battery2() {
if ((contactorStatus == COMPLETED) && datalayer.system.status.battery2_allows_contactor_closing) { if ((contactorStatus == COMPLETED) && datalayer.system.status.battery2_allowed_contactor_closing) {
set(SECOND_NEGATIVE_CONTACTOR_PIN, ON); set(SECOND_NEGATIVE_CONTACTOR_PIN, ON);
set(SECOND_POSITIVE_CONTACTOR_PIN, ON); set(SECOND_POSITIVE_CONTACTOR_PIN, ON);
datalayer.system.status.contactors_battery2_engaged = true; datalayer.system.status.contactors_battery2_engaged = true;

View file

@ -297,10 +297,12 @@ typedef struct {
* we report the inverter as missing entirely on the CAN bus. * we report the inverter as missing entirely on the CAN bus.
*/ */
uint8_t CAN_inverter_still_alive = CAN_STILL_ALIVE; uint8_t CAN_inverter_still_alive = CAN_STILL_ALIVE;
/** True if the battery allows for the contactors to close */ /** True if the primary battery allows for the contactors to close */
bool battery_allows_contactor_closing = false; bool battery_allows_contactor_closing = false;
/** True if the second battery allows for the contactors to close */
bool battery2_allows_contactor_closing = false; /** True if the second battery is allowed to close the contactors */
bool battery2_allowed_contactor_closing = false;
/** True if the inverter allows for the contactors to close */ /** True if the inverter allows for the contactors to close */
bool inverter_allows_contactor_closing = true; bool inverter_allows_contactor_closing = true;
#ifdef CONTACTOR_CONTROL #ifdef CONTACTOR_CONTROL

View file

@ -294,6 +294,33 @@ typedef struct {
uint64_t cumulative_energy_in_regen = 0; uint64_t cumulative_energy_in_regen = 0;
} DATALAYER_INFO_CMFAEV; } DATALAYER_INFO_CMFAEV;
typedef struct {
/** uint8_t */
/** Battery software/hardware/serial versions, stores raw HEX values for ASCII chars */
uint8_t BatterySoftwareVersion[16] = {0};
uint8_t BatteryHardwareVersion[16] = {0};
uint8_t BatterySerialNumber[28] = {0};
/** int16_t */
/** Module temperatures 1-6 */
int16_t ModuleTemperatures[6] = {0};
/** uint16_t */
/** Various values polled via OBD2 PIDs */
uint16_t soc = 0;
uint16_t CC2voltage = 0;
uint16_t cellMaxVoltageNumber = 0;
uint16_t cellMinVoltageNumber = 0;
uint16_t cellTotalAmount = 0;
uint16_t specificialVoltage = 0;
uint16_t unknown1 = 0;
uint16_t rawSOCmax = 0;
uint16_t rawSOCmin = 0;
uint16_t unknown4 = 0;
uint16_t capModMax = 0;
uint16_t capModMin = 0;
uint16_t unknown7 = 0;
uint16_t unknown8 = 0;
} DATALAYER_INFO_GEELY_GEOMETRY_C;
typedef struct { typedef struct {
uint8_t total_cell_count = 0; uint8_t total_cell_count = 0;
int16_t battery_12V = 0; int16_t battery_12V = 0;
@ -303,16 +330,6 @@ typedef struct {
uint8_t batteryManagementMode = 0; uint8_t batteryManagementMode = 0;
uint8_t BMS_ign = 0; uint8_t BMS_ign = 0;
uint8_t batteryRelay = 0; uint8_t batteryRelay = 0;
#ifdef DOUBLE_BATTERY
uint8_t battery2_total_cell_count = 0;
int16_t battery2_battery_12V = 0;
uint8_t battery2_waterleakageSensor = 0;
int8_t battery2_temperature_water_inlet = 0;
int8_t battery2_powerRelayTemperature = 0;
uint8_t battery2_batteryManagementMode = 0;
uint8_t battery2_BMS_ign = 0;
uint8_t battery2_batteryRelay = 0;
#endif //DOUBLE BATTERY
} DATALAYER_INFO_KIAHYUNDAI64; } DATALAYER_INFO_KIAHYUNDAI64;
typedef struct { typedef struct {
@ -779,7 +796,9 @@ class DataLayerExtended {
DATALAYER_INFO_BYDATTO3 bydAtto3; DATALAYER_INFO_BYDATTO3 bydAtto3;
DATALAYER_INFO_CELLPOWER cellpower; DATALAYER_INFO_CELLPOWER cellpower;
DATALAYER_INFO_CMFAEV CMFAEV; DATALAYER_INFO_CMFAEV CMFAEV;
DATALAYER_INFO_GEELY_GEOMETRY_C geometryC;
DATALAYER_INFO_KIAHYUNDAI64 KiaHyundai64; DATALAYER_INFO_KIAHYUNDAI64 KiaHyundai64;
DATALAYER_INFO_KIAHYUNDAI64 KiaHyundai64_2;
DATALAYER_INFO_TESLA tesla; DATALAYER_INFO_TESLA tesla;
DATALAYER_INFO_NISSAN_LEAF nissanleaf; DATALAYER_INFO_NISSAN_LEAF nissanleaf;
DATALAYER_INFO_MEB meb; DATALAYER_INFO_MEB meb;

View file

@ -458,32 +458,67 @@ String advanced_battery_processor(const String& var) {
"<h4>Cumulative energy regen: " + String(datalayer_extended.CMFAEV.cumulative_energy_in_regen) + "Wh</h4>"; "<h4>Cumulative energy regen: " + String(datalayer_extended.CMFAEV.cumulative_energy_in_regen) + "Wh</h4>";
#endif //CMFA_EV_BATTERY #endif //CMFA_EV_BATTERY
#ifdef GEELY_GEOMETRY_C_BATTERY
char readableSerialNumber[29]; // One extra space for null terminator
memcpy(readableSerialNumber, datalayer_extended.geometryC.BatterySerialNumber,
sizeof(datalayer_extended.geometryC.BatterySerialNumber));
readableSerialNumber[28] = '\0'; // Null terminate the string
char readableSoftwareVersion[17]; // One extra space for null terminator
memcpy(readableSoftwareVersion, datalayer_extended.geometryC.BatterySoftwareVersion,
sizeof(datalayer_extended.geometryC.BatterySoftwareVersion));
readableSoftwareVersion[16] = '\0'; // Null terminate the string
char readableHardwareVersion[17]; // One extra space for null terminator
memcpy(readableHardwareVersion, datalayer_extended.geometryC.BatteryHardwareVersion,
sizeof(datalayer_extended.geometryC.BatteryHardwareVersion));
readableHardwareVersion[16] = '\0'; // Null terminate the string
content += "<h4>Serial number: " + String(readableSoftwareVersion) + "</h4>";
content += "<h4>Software version: " + String(readableSerialNumber) + "</h4>";
content += "<h4>Hardware version: " + String(readableHardwareVersion) + "</h4>";
content += "<h4>SOC display: " + String(datalayer_extended.geometryC.soc) + "ppt</h4>";
content += "<h4>CC2 voltage: " + String(datalayer_extended.geometryC.CC2voltage) + "mV</h4>";
content += "<h4>Cell max voltage number: " + String(datalayer_extended.geometryC.cellMaxVoltageNumber) + "</h4>";
content += "<h4>Cell min voltage number: " + String(datalayer_extended.geometryC.cellMinVoltageNumber) + "</h4>";
content += "<h4>Cell total amount: " + String(datalayer_extended.geometryC.cellTotalAmount) + "S</h4>";
content += "<h4>Specificial Voltage: " + String(datalayer_extended.geometryC.specificialVoltage) + "dV</h4>";
content += "<h4>Unknown1: " + String(datalayer_extended.geometryC.unknown1) + "</h4>";
content += "<h4>Raw SOC max: " + String(datalayer_extended.geometryC.rawSOCmax) + "</h4>";
content += "<h4>Raw SOC min: " + String(datalayer_extended.geometryC.rawSOCmin) + "</h4>";
content += "<h4>Unknown4: " + String(datalayer_extended.geometryC.unknown4) + "</h4>";
content += "<h4>Capacity module max: " + String((datalayer_extended.geometryC.capModMax / 10)) + "Ah</h4>";
content += "<h4>Capacity module min: " + String((datalayer_extended.geometryC.capModMin / 10)) + "Ah</h4>";
content += "<h4>Unknown7: " + String(datalayer_extended.geometryC.unknown7) + "</h4>";
content += "<h4>Unknown8: " + String(datalayer_extended.geometryC.unknown8) + "</h4>";
content +=
"<h4>Module 1 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[0]) + " &deg;C</h4>";
content +=
"<h4>Module 2 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[1]) + " &deg;C</h4>";
content +=
"<h4>Module 3 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[2]) + " &deg;C</h4>";
content +=
"<h4>Module 4 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[3]) + " &deg;C</h4>";
content +=
"<h4>Module 5 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[4]) + " &deg;C</h4>";
content +=
"<h4>Module 6 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[5]) + " &deg;C</h4>";
#endif //GEELY_GEOMETRY_C_BATTERY
#ifdef KIA_HYUNDAI_64_BATTERY #ifdef KIA_HYUNDAI_64_BATTERY
content += "<h4>Cells: " + String(datalayer_extended.KiaHyundai64.total_cell_count) + "S</h4>"; auto print_hyundai = [&content](DATALAYER_INFO_KIAHYUNDAI64& data) {
content += "<h4>12V voltage: " + String(datalayer_extended.KiaHyundai64.battery_12V / 10.0, 1) + "</h4>"; content += "<h4>Cells: " + String(data.total_cell_count) + "S</h4>";
content += "<h4>Waterleakage: " + String(datalayer_extended.KiaHyundai64.waterleakageSensor) + "</h4>"; content += "<h4>12V voltage: " + String(data.battery_12V / 10.0, 1) + "</h4>";
content += content += "<h4>Waterleakage: " + String(data.waterleakageSensor) + "</h4>";
"<h4>Temperature, water inlet: " + String(datalayer_extended.KiaHyundai64.temperature_water_inlet) + "</h4>"; content += "<h4>Temperature, water inlet: " + String(data.temperature_water_inlet) + "</h4>";
content += content += "<h4>Temperature, power relay: " + String(data.powerRelayTemperature) + "</h4>";
"<h4>Temperature, power relay: " + String(datalayer_extended.KiaHyundai64.powerRelayTemperature) + "</h4>"; content += "<h4>Batterymanagement mode: " + String(data.batteryManagementMode) + "</h4>";
content += "<h4>Batterymanagement mode: " + String(datalayer_extended.KiaHyundai64.batteryManagementMode) + "</h4>"; content += "<h4>BMS ignition: " + String(data.BMS_ign) + "</h4>";
content += "<h4>BMS ignition: " + String(datalayer_extended.KiaHyundai64.BMS_ign) + "</h4>"; content += "<h4>Battery relay: " + String(data.batteryRelay) + "</h4>";
content += "<h4>Battery relay: " + String(datalayer_extended.KiaHyundai64.batteryRelay) + "</h4>"; };
print_hyundai(datalayer_extended.KiaHyundai64);
#ifdef DOUBLE_BATTERY #ifdef DOUBLE_BATTERY
content += "<h4>Values from battery 2</h4>"; content += "<h4>Values from battery 2</h4>";
content += "<h4>Cells: " + String(datalayer_extended.KiaHyundai64.battery2_total_cell_count) + "S</h4>"; print_hyundai(datalayer_extended.KiaHyundai64_2);
content += "<h4>12V voltage: " + String(datalayer_extended.KiaHyundai64.battery2_battery_12V / 10.0, 1) + "</h4>";
content += "<h4>Waterleakage: " + String(datalayer_extended.KiaHyundai64.battery2_waterleakageSensor) + "</h4>";
content +=
"<h4>Temperature, water inlet: " + String(datalayer_extended.KiaHyundai64.battery2_temperature_water_inlet) +
"</h4>";
content +=
"<h4>Temperature, power relay: " + String(datalayer_extended.KiaHyundai64.battery2_powerRelayTemperature) +
"</h4>";
content += "<h4>Batterymanagement mode: " + String(datalayer_extended.KiaHyundai64.battery2_batteryManagementMode) +
"</h4>";
content += "<h4>BMS ignition: " + String(datalayer_extended.KiaHyundai64.battery2_BMS_ign) + "</h4>";
content += "<h4>Battery relay: " + String(datalayer_extended.KiaHyundai64.battery2_batteryRelay) + "</h4>";
#endif //DOUBLE_BATTERY #endif //DOUBLE_BATTERY
#endif //KIA_HYUNDAI_64_BATTERY #endif //KIA_HYUNDAI_64_BATTERY
@ -1489,7 +1524,8 @@ String advanced_battery_processor(const String& var) {
!defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && \ !defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && \
!defined(BYD_ATTO_3_BATTERY) && !defined(RENAULT_ZOE_GEN2_BATTERY) && !defined(CELLPOWER_BMS) && \ !defined(BYD_ATTO_3_BATTERY) && !defined(RENAULT_ZOE_GEN2_BATTERY) && !defined(CELLPOWER_BMS) && \
!defined(MEB_BATTERY) && !defined(VOLVO_SPA_BATTERY) && !defined(VOLVO_SPA_HYBRID_BATTERY) && \ !defined(MEB_BATTERY) && !defined(VOLVO_SPA_BATTERY) && !defined(VOLVO_SPA_HYBRID_BATTERY) && \
!defined(KIA_HYUNDAI_64_BATTERY) && !defined(CMFA_EV_BATTERY) //Only the listed types have extra info !defined(KIA_HYUNDAI_64_BATTERY) && !defined(GEELY_GEOMETRY_C_BATTERY) && \
!defined(CMFA_EV_BATTERY) //Only the listed types have extra info
content += "No extra information available for this battery type"; content += "No extra information available for this battery type";
#endif #endif

View file

@ -1379,7 +1379,7 @@ String processor(const String& var) {
content += "<h4>Automatic contactor closing allowed:</h4>"; content += "<h4>Automatic contactor closing allowed:</h4>";
content += "<h4>Battery: "; content += "<h4>Battery: ";
if (datalayer.system.status.battery2_allows_contactor_closing == true) { if (datalayer.system.status.battery2_allowed_contactor_closing == true) {
content += "<span>&#10003;</span>"; content += "<span>&#10003;</span>";
} else { } else {
content += "<span style='color: red;'>&#10005;</span>"; content += "<span style='color: red;'>&#10005;</span>";

View file

@ -73,7 +73,7 @@ void wifi_monitor() {
#endif #endif
// Try WiFi.reconnect() if it was successfully connected at least once // Try WiFi.reconnect() if it was successfully connected at least once
if (hasConnectedBefore) { if (hasConnectedBefore) {
lastReconnectAttempt = millis(); // Reset reconnection attempt timer lastReconnectAttempt = currentMillis; // Reset reconnection attempt timer
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
logging.println("Wi-Fi reconnect attempt..."); logging.println("Wi-Fi reconnect attempt...");
#endif #endif