Merge branch 'main' into improvement/simplify-zoe-cellvoltages

This commit is contained in:
Daniel Öster 2025-05-24 00:12:06 +03:00
commit d3f0b46923
125 changed files with 8685 additions and 7573 deletions

View file

@ -49,7 +49,7 @@
volatile unsigned long long bmsResetTimeOffset = 0;
// The current software version, shown on webserver
const char* version_number = "8.13.dev";
const char* version_number = "8.14.dev";
// Interval timers
volatile unsigned long currentMillis = 0;
@ -102,10 +102,7 @@ void setup() {
#endif // PRECHARGE_CONTROL
setup_charger();
#if defined(CAN_INVERTER_SELECTED) || defined(MODBUS_INVERTER_SELECTED) || defined(RS485_INVERTER_SELECTED)
setup_inverter();
#endif
setup_battery();
init_rs485();
@ -244,7 +241,7 @@ void core_loop(void*) {
led_exe();
handle_contactors(); // Take care of startup precharge/contactor closing
#ifdef PRECHARGE_CONTROL
handle_precharge_control();
handle_precharge_control(currentMillis);
#endif // PRECHARGE_CONTROL
#ifdef FUNCTION_TIME_MEASUREMENT
END_TIME_MEASUREMENT_MAX(time_10ms, datalayer.system.status.time_10ms_us);
@ -276,7 +273,7 @@ void core_loop(void*) {
transmit_can(currentMillis); // Send CAN messages to all components
#ifdef RS485_BATTERY_SELECTED
transmit_rs485();
transmit_rs485(currentMillis);
#endif // RS485_BATTERY_SELECTED
#ifdef FUNCTION_TIME_MEASUREMENT
END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us);
@ -334,9 +331,9 @@ void check_interconnect_available() {
clear_event(EVENT_VOLTAGE_DIFFERENCE);
if (datalayer.battery.status.bms_status == FAULT) {
// 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
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
set_event(EVENT_VOLTAGE_DIFFERENCE, (uint8_t)(voltage_diff / 10));
@ -512,15 +509,9 @@ void update_calculated_values() {
}
void update_values_inverter() {
#ifdef SELECTED_INVERTER_CLASS
if (inverter) {
inverter->update_values();
}
#else
#ifdef CAN_INVERTER_SELECTED
update_values_can_inverter();
#endif // CAN_INVERTER_SELECTED
#endif
}
void check_reset_reason() {

View file

@ -17,6 +17,7 @@
//#define FOXESS_BATTERY
//#define CELLPOWER_BMS
//#define CHADEMO_BATTERY //NOTE: inherently enables CONTACTOR_CONTROL below
//#define GEELY_GEOMETRY_C_BATTERY
//#define CMFA_EV_BATTERY
//#define IMIEV_CZERO_ION_BATTERY
//#define JAGUAR_IPACE_BATTERY
@ -92,7 +93,8 @@
//#define NISSANLEAF_CHARGER //Enable this line to control a Nissan LEAF PDM connected to battery - for example, when generator charging
/* Automatic Precharge settings (Optional) If you have a battery that expects an external voltage applied before opening contactors (within the battery), configure this section */
//#define PRECHARGE_CONTROL //Enable this line to control a modified HIA4V1 (see wiki) by PWM on the PRECHARGE_PIN.
//#define PRECHARGE_CONTROL //Enable this line to control a modified HIA4V1 via PWM on the HIA4V1_PIN (see Wiki and HAL for pin definition)
//#define INVERTER_DISCONNECT_CONTACTOR_IS_NORMALLY_OPEN //Enable this line if you use a normally open contactor instead of normally closed
/* Other options */
//#define EQUIPMENT_STOP_BUTTON // Enable this to allow an equipment stop button connected to the Battery-Emulator to disengage the battery
@ -179,6 +181,7 @@ typedef struct {
CAN_Interface charger;
CAN_Interface shunt;
} CAN_Configuration;
extern const char* getCANInterfaceName(CAN_Interface interface);
extern volatile CAN_Configuration can_config;
extern volatile uint8_t AccessPointEnabled;
extern const uint8_t wifi_channel;

View file

@ -1,5 +1,6 @@
#include "../include.h"
#include "../datalayer/datalayer_extended.h"
#include "CanBattery.h"
#include "RS485Battery.h"
@ -25,14 +26,18 @@ void setup_battery() {
#ifdef DOUBLE_BATTERY
if (battery2 == nullptr) {
#ifdef BMW_I3_BATTERY
#if defined(BMW_I3_BATTERY)
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);
#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
battery2 =
new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer.system.status.battery2_allows_contactor_closing,
nullptr, can_config.battery_double);
battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, nullptr, can_config.battery_double);
#endif
}
battery2->setup();

View file

@ -46,6 +46,10 @@ void setup_can_shunt();
#include "FOXESS-BATTERY.h"
#endif
#ifdef GEELY_GEOMETRY_C_BATTERY
#include "GEELY-GEOMETRY-C-BATTERY.h"
#endif
#ifdef ORION_BMS
#include "ORION-BMS.h"
#endif
@ -151,7 +155,7 @@ void setup_battery(void);
void update_values_battery();
#ifdef RS485_BATTERY_SELECTED
void transmit_rs485();
void transmit_rs485(unsigned long currentMillis);
void receive_RS485();
#else
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;
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 == true) {
} else if (allows_contactor_closing) {
//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);
}
}
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
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.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
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;
pinMode(wakeup_pin, OUTPUT);

View file

@ -12,9 +12,10 @@
class BmwI3Battery : public CanBattery {
public:
// 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;
allows_contactor_closing = allows_contactor_closing_ptr;
contactor_closing_allowed = contactor_closing_allowed_ptr;
allows_contactor_closing = nullptr;
can_interface = targetCan;
wakeup_pin = wakeup;
*allows_contactor_closing = true;
@ -27,6 +28,7 @@ class BmwI3Battery : public CanBattery {
BmwI3Battery() {
datalayer_battery = &datalayer.battery;
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
contactor_closing_allowed = nullptr;
can_interface = can_config.battery;
wakeup_pin = WUP_PIN1;
}
@ -53,9 +55,14 @@ class BmwI3Battery : public CanBattery {
const int NUMBER_OF_CELLS = 96;
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;
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send

View file

@ -1,535 +1,13 @@
#include "../include.h"
#ifdef BMW_IX_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "BMW-IX-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 20ms CAN Message was send
static unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was send
static unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send
static unsigned long previousMillis640 = 0; // will store last time a 600ms CAN Message was send
static unsigned long previousMillis1000 = 0; // will store last time a 600ms CAN Message was send
static unsigned long previousMillis10000 = 0; // will store last time a 10000ms CAN Message was send
#define ALIVE_MAX_VALUE 14 // BMW CAN messages contain alive counter, goes from 0...14
enum CmdState { SOH, CELL_VOLTAGE_MINMAX, SOC, CELL_VOLTAGE_CELLNO, CELL_VOLTAGE_CELLNO_LAST };
static CmdState cmdState = SOC;
static bool battery_awake = false;
bool contactorCloseReq = false;
struct ContactorCloseRequestStruct {
bool previous;
bool present;
} ContactorCloseRequest = {false, false};
struct ContactorStateStruct {
bool closed;
bool open;
};
ContactorStateStruct ContactorState = {false, true};
struct InverterContactorCloseRequestStruct {
bool previous;
bool present;
};
InverterContactorCloseRequestStruct InverterContactorCloseRequest = {false, false};
/*
SME output:
0x12B8D087 5000ms - Extended ID
0x1D2 DLC8 1000ms
0x20B DLC8 1000ms
0x2E2 DLC16 1000ms
0x31F DLC16 100ms - 2 downward counters?
0x3EA DLC8
0x453 DLC20 200ms
0x486 DLC48 1000ms
0x49C DLC8 1000ms
0x4A1 DLC8 1000ms
0x4BB DLC64 200ms - seems multplexed on [0]
0x4D0 DLC64 1000ms - some slow/flickering values - possible change during fault
0x507 DLC8
0x587 DLC8 - appears at startup 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF , 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF, 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF, 0x06 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x82 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF
0x607 - UDS response
0x7AB DLC64 - seen at startup
0x8F DLC48 10ms - appears to have analog readings like volt/temp/current
0xD0D087 DLC4
BDC output:
0x276 DLC8 - vehicle condition
0x2F1 DLC8 1000ms - during run: 0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0x00, 0xF3, 0xFF - at startup 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xF3, 0xFF. Suspect byte [4] is a counter
0x439 DLC4 1000ms - STATIC: byte0, byte1. byte3 changes, byte4 = 0xF2 OR 0xF3
0x4EB DLC8 - RSU condition
0x510 DLC8 100ms - FIXME:(update as this content is not seen in car logs:) STATIC 40 10 40 00 6F DF 19 00 during run - Startup sends this once: 0x40 0x10 0x02 0x00 0x00 0x00 0x00 0x00
0x6D DLC8 1000ms - counters? counter on byte3
0xC0 DLC2 200ms - needs counter
SME asks for:
0x125 (CCU)
0x16E (CCU)
0x188 (CCU)
0x1A1 (DSC) - vehicle speed (not seen in car logs)
0x1EA (KOMBI)
0x1FC (FIXME:(add transmitter node.))
0x21D (FIXME:(add transmitter node.))
0x276 (BDC)
0x2ED (FIXME:(add transmitter node.))
0x340 (CCU)
0x380 (FIXME:(add transmitter node.))
0x442 (FIXME:(add transmitter node.))
0x4EB (BDC)
0x4F8 (CCU)
0x91 (EME1)
0xAA (EME2) - all wheel drive only
0x?? Suspect there is a drive mode flag somewhere - balancing might only be active in some modes
TODO:
- Request batt serial number on F1 8C (already parsing RX)
*/
//Vehicle CAN START
CAN_frame BMWiX_125 = {
.FD = true,
.ext_ID = false,
.DLC = 20,
.ID = 0x125,
//.data = {TODO:, TODO:, TODO:, TODO:, 0xFE, 0x7F, 0xFE, 0x7F, TODO:, TODO:, TODO:, TODO:, TODO:, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF}
}; // CCU output
/* SME output
CAN_frame BMWiX_12B8D087 = {.FD = true,
.ext_ID = true,
.DLC = 2,
.ID = 0x12B8D087,
.data = {0xFC, 0xFF}}; // 5000ms SME output - Static values
*/
CAN_frame BMWiX_16E = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x16E,
.data = {0x00, // Almost any possible number in 0x00 and 0xFF
0xA0, // Almost any possible number in 0xA0 and 0xAF
0xC9, 0xFF,
0x60, // FIXME: find out what this value represents
0xC9,
0x3A, // 0x3A to close contactors, 0x33 to open contactors
0xF7}}; // 0xF7 to close contactors, 0xF0 to open contactors // CCU output.
CAN_frame BMWiX_188 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x188,
.data = {0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF}}; // CCU output - values while driving
CAN_frame BMWiX_1EA = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x1EA,
//.data = {TODO:km_least_significant, TODO:, TODO:, TODO:, TODO:km_most_significant, 0xFF, TODO:, TODO:}
}; // KOMBI output - kilometerstand
CAN_frame BMWiX_1FC = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x1FC,
.data = {0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0xC0,
0x00}}; // FIXME:(add transmitter node) output - heat management engine control - static values
CAN_frame BMWiX_21D = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x21D,
// .data = {TODO:, TODO:, TODO:, 0xFF, 0xFF, 0xFF, 0xFF, TODO:}
}; // FIXME:(add transmitter node) output - request heating and air conditioning system 1
CAN_frame BMWiX_276 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x276,
.data = {0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFD}}; // BDC output - vehicle condition. Used for contactor closing
CAN_frame BMWiX_2ED = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x2ED,
.data = {
0x75,
0x75}}; // FIXME:(add transmitter node) output - ambient temperature (values seen in logs vary between 0x72 and 0x79)
CAN_frame BMWiX_2F1 = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x2F1,
.data = {0xFF, 0xFF, 0xD0, 0x39, 0x94, 0x00, 0xF3, 0xFF}}; // 1000ms BDC output - Static values - varies at startup
CAN_frame BMWiX_340 = {
.FD = true,
.ext_ID = false,
.DLC = 12,
.ID = 0x340,
// .data = {TODO:, TODO:, TODO:, 0xFF, TODO:, TODO:, 0x00, 0x00, TODO:, TODO:, TODO:, 0xFF, 0xFF, }
}; // CCU output
CAN_frame BMWiX_380 = {
.FD = true,
.ext_ID = false,
.DLC = 7,
.ID = 0x380,
// .data = {FIXME:(VIN_char1), FIXME:(VIN_char2), FIXME:(VIN_char3), FIXME:(VIN_char4), FIXME:(VIN_char5), FIXME:(VIN_char6), FIXME:(VIN_char7)}
}; // FIXME:(add transmitter node) output - VIN: ASCII2HEX
/* Not requested by SME
CAN_frame BMWiX_439 = {.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x439,
.data = {0xFF, 0x3F, 0xFF, 0xF3}}; // 1000ms BDC output
*/
CAN_frame BMWiX_442 = {
.FD = true,
.ext_ID = false,
.DLC = 6,
.ID = 0x442,
// .data = {TODO: relative time byte 0, TODO: relative time byte1, 0xA9, 0x00, 0xE0, 0x23}
}; // FIXME:(add transmitter node) output - relative time BN2020
/* SME output
CAN_frame
BMWiX_486 =
{
.FD = true,
.ext_ID = false,
.DLC = 48,
.ID = 0x486,
.data =
{
0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF,
0xFE, 0xFF, 0xFF, 0x7F, 0x33, 0xFD, 0xFD, 0xFD, 0xFD, 0xC0, 0x41, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; // 1000ms SME output - Suspected keep alive Static CONFIRM NEEDED
*/
/* SME output
CAN_frame BMWiX_49C = {.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x49C,
.data = {0xD2, 0xF2, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF}}; // 1000ms SME output - Suspected keep alive Static CONFIRM NEEDED
*/
CAN_frame BMWiX_4EB = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x4EB,
// .data = {TODO:, TODO:, TODO: 0xE0 or 0xE5 (while driving), 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
}; // BDC output - RSU condition
CAN_frame BMWiX_4F8 = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x4F8,
// .data = {0xFF, 0xFD, 0xFF, 0xFF, 0xFF, TODO:, TODO:, 0xC8, 0x00, 0x00, 0xF0, 0x40, 0xFE, 0xFF, 0xFD, 0xFF, TODO:, TODO:, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
}; // CCU output
CAN_frame BMWiX_510 = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x510,
.data = {
0x40, 0x10,
0x04, // 0x02 at contactor closing, afterwards 0x04 and 0x10, 0x00 to open contactors
0x00, 0x00,
0x80, // 0x00 at start of contactor closing, changing to 0x80, afterwards 0x80
0x01,
0x00}}; // 100ms BDC output - Values change in car logs, these bytes are the most common. Used for contactor closing
CAN_frame BMWiX_6D = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x6D,
.data = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0xFF}}; // 1000ms BDC output - [0] static [1,2][3,4] counter x2. 3,4 is 9 higher than 1,2 is needed? [5-7] static
CAN_frame BMWiX_C0 = {
.FD = true,
.ext_ID = false,
.DLC = 2,
.ID = 0xC0,
.data = {
0xF0,
0x00}}; // BDC output - Keep Alive 2 BDC>SME 200ms First byte cycles F0 > FE second byte 00 static - MINIMUM ID TO KEEP SME AWAKE
//Vehicle CAN END
//Request Data CAN START
CAN_frame BMWiX_6F4 = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0xC7}}; // Generic UDS Request data from SME. byte 4 selects requested value
CAN_frame BMWiX_6F4_REQUEST_SLEEPMODE = {
.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x6F4,
.data = {0x07, 0x02, 0x11, 0x04}}; // UDS Request Request BMS/SME goes to Sleep Mode
CAN_frame BMWiX_6F4_REQUEST_HARD_RESET = {.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x6F4,
.data = {0x07, 0x02, 0x11, 0x01}}; // UDS Request Hard reset of BMS/SME
CAN_frame BMWiX_6F4_REQUEST_CELL_TEMP = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xDD, 0xC0}}; // UDS Request Cell Temperatures
CAN_frame BMWiX_6F4_REQUEST_SOC = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0xCE}}; // Min/Avg/Max SOC%
CAN_frame BMWiX_6F4_REQUEST_CAPACITY = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0xC7}}; //Current and max capacity kWh. Stored in kWh as 0.01 scale with -50 bias
CAN_frame BMWiX_6F4_REQUEST_MINMAXCELLV = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x53}}; //Min and max cell voltage 10V = Qualifier Invalid
CAN_frame BMWiX_6F4_REQUEST_MAINVOLTAGE_POSTCONTACTOR = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x4A}}; //Main Battery Voltage (After Contactor)
CAN_frame BMWiX_6F4_REQUEST_MAINVOLTAGE_PRECONTACTOR = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x4D}}; //Main Battery Voltage (Pre Contactor)
CAN_frame BMWiX_6F4_REQUEST_BATTERYCURRENT = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x61}}; //Current amps 32bit signed MSB. dA . negative is discharge
CAN_frame BMWiX_6F4_REQUEST_CELL_VOLTAGE = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x54}}; //MultiFrameIndividual Cell Voltages
CAN_frame BMWiX_6F4_REQUEST_T30VOLTAGE = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0xA7}}; //Terminal 30 Voltage (12V SME supply)
CAN_frame BMWiX_6F4_REQUEST_EOL_ISO = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xA8, 0x60}}; //Request EOL Reading including ISO
CAN_frame BMWiX_6F4_REQUEST_SOH = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x45}}; //SOH Max Min Mean Request
CAN_frame BMWiX_6F4_REQUEST_DATASUMMARY = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {
0x07, 0x03, 0x22, 0xE5,
0x45}}; //MultiFrame Summary Request, includes SOC/SOH/MinMax/MaxCapac/RemainCapac/max v and t at last charge. slow refreshrate
CAN_frame BMWiX_6F4_REQUEST_PYRO = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xAC, 0x93}}; //Pyro Status
CAN_frame BMWiX_6F4_REQUEST_UPTIME = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE4, 0xC0}}; // Uptime and Vehicle Time Status
CAN_frame BMWiX_6F4_REQUEST_HVIL = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x69}}; // Request HVIL State
CAN_frame BMWiX_6F4_REQUEST_BALANCINGSTATUS = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE4, 0xCA}}; // Request Balancing Data
CAN_frame BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x62}}; // Request allowable charge discharge amps
CAN_frame BMWiX_6F4_REQUEST_VOLTAGE_QUALIFIER_CHECK = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x4B}}; // Request HV Voltage Qualifier
CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_CLOSE = {
.FD = true,
.ext_ID = false,
.DLC = 6,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Close - Unconfirmed
CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_OPEN = {
.FD = true,
.ext_ID = false,
.DLC = 6,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Open - Unconfirmed
CAN_frame BMWiX_6F4_REQUEST_BALANCING_START = {
.FD = true,
.ext_ID = false,
.DLC = 6,
.ID = 0x6F4,
.data = {0xF4, 0x04, 0x71, 0x01, 0xAE, 0x77}}; // Request Balancing command?
CAN_frame BMWiX_6F4_REQUEST_BALANCING_START2 = {
.FD = true,
.ext_ID = false,
.DLC = 6,
.ID = 0x6F4,
.data = {0xF4, 0x04, 0x31, 0x01, 0xAE, 0x77}}; // Request Balancing command?
CAN_frame BMWiX_6F4_REQUEST_PACK_VOLTAGE_LIMITS = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x4C}}; // Request pack voltage limits
CAN_frame BMWiX_6F4_CONTINUE_DATA = {.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x6F4,
.data = {0x07, 0x30, 0x00, 0x02}};
//Action Requests:
CAN_frame BMWiX_6F4_CELL_SOC = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x9A}};
CAN_frame BMWiX_6F4_CELL_TEMP = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0xCA}};
//Request Data CAN End
//Setup UDS values to poll for
CAN_frame* UDS_REQUESTS100MS[] = {&BMWiX_6F4_REQUEST_CELL_TEMP,
&BMWiX_6F4_REQUEST_SOC,
&BMWiX_6F4_REQUEST_CAPACITY,
&BMWiX_6F4_REQUEST_MINMAXCELLV,
&BMWiX_6F4_REQUEST_MAINVOLTAGE_POSTCONTACTOR,
&BMWiX_6F4_REQUEST_MAINVOLTAGE_PRECONTACTOR,
&BMWiX_6F4_REQUEST_BATTERYCURRENT,
&BMWiX_6F4_REQUEST_CELL_VOLTAGE,
&BMWiX_6F4_REQUEST_T30VOLTAGE,
&BMWiX_6F4_REQUEST_SOH,
&BMWiX_6F4_REQUEST_UPTIME,
&BMWiX_6F4_REQUEST_PYRO,
&BMWiX_6F4_REQUEST_EOL_ISO,
&BMWiX_6F4_REQUEST_HVIL,
&BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS,
&BMWiX_6F4_REQUEST_BALANCINGSTATUS,
&BMWiX_6F4_REQUEST_PACK_VOLTAGE_LIMITS};
int numUDSreqs = sizeof(UDS_REQUESTS100MS) / sizeof(UDS_REQUESTS100MS[0]); // Number of elements in the array
//iX Intermediate vars
static bool battery_info_available = false;
static uint32_t battery_serial_number = 0;
static int32_t battery_current = 0;
static int16_t battery_voltage = 370; //Startup with valid values - needs fixing in future
static int16_t terminal30_12v_voltage = 0;
static int16_t battery_voltage_after_contactor = 0;
static int16_t min_soc_state = 5000;
static int16_t avg_soc_state = 5000;
static int16_t max_soc_state = 5000;
static int16_t min_soh_state = 9900; // Uses E5 45, also available in 78 73
static int16_t avg_soh_state = 9900; // Uses E5 45, also available in 78 73
static int16_t max_soh_state = 9900; // Uses E5 45, also available in 78 73
static uint16_t max_design_voltage = 0;
static uint16_t min_design_voltage = 0;
static int32_t remaining_capacity = 0;
static int32_t max_capacity = 0;
static int16_t min_battery_temperature = 0;
static int16_t avg_battery_temperature = 0;
static int16_t max_battery_temperature = 0;
static int16_t main_contactor_temperature = 0;
static int16_t min_cell_voltage = 3700; //Startup with valid values - needs fixing in future
static int16_t max_cell_voltage = 3700; //Startup with valid values - needs fixing in future
static unsigned long min_cell_voltage_lastchanged = 0;
static unsigned long max_cell_voltage_lastchanged = 0;
static unsigned min_cell_voltage_lastreceived = 0;
static unsigned max_cell_voltage_lastreceived = 0;
static uint32_t sme_uptime = 0; //Uses E4 C0
static int16_t allowable_charge_amps = 0; //E5 62
static int16_t allowable_discharge_amps = 0; //E5 62
static int32_t iso_safety_positive = 0; //Uses A8 60
static int32_t iso_safety_negative = 0; //Uses A8 60
static int32_t iso_safety_parallel = 0; //Uses A8 60
static int16_t count_full_charges = 0; //TODO 42
static int16_t count_charges = 0; //TODO 42
static int16_t hvil_status = 0;
static int16_t voltage_qualifier_status = 0; //0 = Valid, 1 = Invalid
static int16_t balancing_status = 0; //4 = not active
static uint8_t contactors_closed = 0; //TODO E5 BF or E5 51
static uint8_t contactor_status_precharge = 0; //TODO E5 BF
static uint8_t contactor_status_negative = 0; //TODO E5 BF
static uint8_t contactor_status_positive = 0; //TODO E5 BF
static uint8_t pyro_status_pss1 = 0; //Using AC 93
static uint8_t pyro_status_pss4 = 0; //Using AC 93
static uint8_t pyro_status_pss6 = 0; //Using AC 93
static uint8_t uds_req_id_counter = 0;
static uint8_t detected_number_of_cells = 108;
const unsigned long STALE_PERIOD =
STALE_PERIOD_CONFIG; // Time in milliseconds to check for staleness (e.g., 5000 ms = 5 seconds)
//End iX Intermediate vars
static uint8_t current_cell_polled = 0;
static uint16_t counter_10ms = 0; // max 65535 --> 655.35 seconds
static uint8_t counter_100ms = 0; // max 255 --> 25.5 seconds
// Function to check if a value has gone stale over a specified time period
bool isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChangeTime) {
bool BmwIXBattery::isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChangeTime) {
unsigned long currentTime = millis();
// Check if the value has changed
@ -544,7 +22,7 @@ bool isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChang
return (currentTime - lastChangeTime >= STALE_PERIOD);
}
static uint8_t increment_uds_req_id_counter(uint8_t index) {
uint8_t BmwIXBattery::increment_uds_req_id_counter(uint8_t index) {
index++;
if (index >= numUDSreqs) {
index = 0;
@ -552,7 +30,7 @@ static uint8_t increment_uds_req_id_counter(uint8_t index) {
return index;
}
static uint8_t increment_alive_counter(uint8_t counter) {
uint8_t BmwIXBattery::increment_alive_counter(uint8_t counter) {
counter++;
if (counter > ALIVE_MAX_VALUE) {
counter = 0;
@ -569,7 +47,7 @@ static byte increment_C0_counter(byte counter) {
return counter;
}
void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
void BmwIXBattery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer
datalayer.battery.status.real_soc = avg_soc_state;
@ -663,7 +141,7 @@ void update_values_battery() { //This function maps all the values fetched via
}
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
battery_awake = true;
switch (rx_frame.ID) {
case 0x12B8D087:
@ -919,7 +397,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void BmwIXBattery::transmit_can(unsigned long currentMillis) {
// We can always send CAN as the iX BMS will wake up on vehicle comms
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
previousMillis10 = currentMillis;
@ -996,7 +474,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void BmwIXBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "BMW iX and i4-7 platform", 63);
datalayer.system.info.battery_protocol[63] = '\0';
@ -1012,7 +490,7 @@ void setup_battery(void) { // Performs one time setup at startup
datalayer.system.status.battery_allows_contactor_closing = true;
}
void HandleIncomingUserRequest(void) {
void BmwIXBattery::HandleIncomingUserRequest(void) {
// Debug user request to open or close the contactors
#ifdef DEBUG_LOG
logging.print("User request: contactor close: ");
@ -1048,7 +526,7 @@ void HandleIncomingUserRequest(void) {
}
}
void HandleIncomingInverterRequest(void) {
void BmwIXBattery::HandleIncomingInverterRequest(void) {
InverterContactorCloseRequest.present = datalayer.system.status.inverter_allows_contactor_closing;
// Detect edge
if (InverterContactorCloseRequest.previous == false && InverterContactorCloseRequest.present == true) {
@ -1069,14 +547,14 @@ void HandleIncomingInverterRequest(void) {
InverterContactorCloseRequest.previous = InverterContactorCloseRequest.present;
}
void BmwIxCloseContactors(void) {
void BmwIXBattery::BmwIxCloseContactors(void) {
#ifdef DEBUG_LOG
logging.println("Closing contactors");
#endif // DEBUG_LOG
contactorCloseReq = true;
}
void BmwIxOpenContactors(void) {
void BmwIXBattery::BmwIxOpenContactors(void) {
#ifdef DEBUG_LOG
logging.println("Opening contactors");
#endif // DEBUG_LOG
@ -1084,7 +562,7 @@ void BmwIxOpenContactors(void) {
counter_100ms = 0; // reset counter, such that keep contactors closed message sequence starts from the beginning
}
void HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms) {
void BmwIXBattery::HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms) {
if (contactorCloseReq == true) { // Only when contactor close request is set to true
if (ContactorState.closed == false &&
ContactorState.open ==
@ -1152,7 +630,7 @@ void HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms) {
}
}
void BmwIxKeepContactorsClosed(uint8_t counter_100ms) {
void BmwIXBattery::BmwIxKeepContactorsClosed(uint8_t counter_100ms) {
if ((ContactorState.closed == true) && (ContactorState.open == false)) {
BMWiX_510.data = {0x40, 0x10,
0x04, // 0x02 at contactor closing, afterwards 0x04 and 0x10, 0x00 to open contactors
@ -1194,7 +672,7 @@ void BmwIxKeepContactorsClosed(uint8_t counter_100ms) {
}
}
void HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms) {
void BmwIXBattery::HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms) {
if (contactorCloseReq == false) { // if contactors are not requested to be closed, they are requested to be opened
if (ContactorState.open == false) { // only if contactors are not open yet
// message content to quickly open contactors

View file

@ -2,33 +2,570 @@
#define BMW_IX_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS BmwIXBattery
#define MAX_PACK_VOLTAGE_DV 4650 //4650 = 465.0V
#define MIN_PACK_VOLTAGE_DV 3000
#define MAX_CELL_DEVIATION_MV 250
#define MAX_CELL_VOLTAGE_MV 4300 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2800 //Battery is put into emergency stop if one cell goes below this value
#define MAX_DISCHARGE_POWER_ALLOWED_W 10000
#define MAX_CHARGE_POWER_ALLOWED_W 10000
#define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500
#define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
#define STALE_PERIOD_CONFIG \
900000; //Number of milliseconds before critical values are classed as stale/stuck 900000 = 900 seconds
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
class BmwIXBattery : 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:
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V
static const int MIN_PACK_VOLTAGE_DV = 3000;
static const int MAX_CELL_DEVIATION_MV = 250;
static const int MAX_CELL_VOLTAGE_MV = 4300; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2800; //Battery is put into emergency stop if one cell goes below this value
static const int MAX_DISCHARGE_POWER_ALLOWED_W = 10000;
static const int MAX_CHARGE_POWER_ALLOWED_W = 10000;
static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500;
static const int RAMPDOWN_SOC =
9000; // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
static const int STALE_PERIOD_CONFIG =
900000; //Number of milliseconds before critical values are classed as stale/stuck 900000 = 900 seconds
unsigned long previousMillis10 = 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
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was send
unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send
unsigned long previousMillis640 = 0; // will store last time a 600ms CAN Message was send
unsigned long previousMillis1000 = 0; // will store last time a 600ms CAN Message was send
unsigned long previousMillis10000 = 0; // will store last time a 10000ms CAN Message was send
static const int ALIVE_MAX_VALUE = 14; // BMW CAN messages contain alive counter, goes from 0...14
enum CmdState { SOH, CELL_VOLTAGE_MINMAX, SOC, CELL_VOLTAGE_CELLNO, CELL_VOLTAGE_CELLNO_LAST };
CmdState cmdState = SOC;
bool battery_awake = false;
bool contactorCloseReq = false;
struct ContactorCloseRequestStruct {
bool previous;
bool present;
} ContactorCloseRequest = {false, false};
struct ContactorStateStruct {
bool closed;
bool open;
};
ContactorStateStruct ContactorState = {false, true};
struct InverterContactorCloseRequestStruct {
bool previous;
bool present;
};
InverterContactorCloseRequestStruct InverterContactorCloseRequest = {false, false};
/*
SME output:
0x12B8D087 5000ms - Extended ID
0x1D2 DLC8 1000ms
0x20B DLC8 1000ms
0x2E2 DLC16 1000ms
0x31F DLC16 100ms - 2 downward counters?
0x3EA DLC8
0x453 DLC20 200ms
0x486 DLC48 1000ms
0x49C DLC8 1000ms
0x4A1 DLC8 1000ms
0x4BB DLC64 200ms - seems multplexed on [0]
0x4D0 DLC64 1000ms - some slow/flickering values - possible change during fault
0x507 DLC8
0x587 DLC8 - appears at startup 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF , 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF, 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF, 0x06 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x82 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF
0x607 - UDS response
0x7AB DLC64 - seen at startup
0x8F DLC48 10ms - appears to have analog readings like volt/temp/current
0xD0D087 DLC4
BDC output:
0x276 DLC8 - vehicle condition
0x2F1 DLC8 1000ms - during run: 0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0x00, 0xF3, 0xFF - at startup 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xF3, 0xFF. Suspect byte [4] is a counter
0x439 DLC4 1000ms - STATIC: byte0, byte1. byte3 changes, byte4 = 0xF2 OR 0xF3
0x4EB DLC8 - RSU condition
0x510 DLC8 100ms - FIXME:(update as this content is not seen in car logs:) 40 10 40 00 6F DF 19 00 during run - Startup sends this once: 0x40 0x10 0x02 0x00 0x00 0x00 0x00 0x00
0x6D DLC8 1000ms - counters? counter on byte3
0xC0 DLC2 200ms - needs counter
SME asks for:
0x125 (CCU)
0x16E (CCU)
0x188 (CCU)
0x1A1 (DSC) - vehicle speed (not seen in car logs)
0x1EA (KOMBI)
0x1FC (FIXME:(add transmitter node.))
0x21D (FIXME:(add transmitter node.))
0x276 (BDC)
0x2ED (FIXME:(add transmitter node.))
0x340 (CCU)
0x380 (FIXME:(add transmitter node.))
0x442 (FIXME:(add transmitter node.))
0x4EB (BDC)
0x4F8 (CCU)
0x91 (EME1)
0xAA (EME2) - all wheel drive only
0x?? Suspect there is a drive mode flag somewhere - balancing might only be active in some modes
TODO:
- Request batt serial number on F1 8C (already parsing RX)
*/
//Vehicle CAN START
CAN_frame BMWiX_125 = {
.FD = true,
.ext_ID = false,
.DLC = 20,
.ID = 0x125,
//.data = {TODO:, TODO:, TODO:, TODO:, 0xFE, 0x7F, 0xFE, 0x7F, TODO:, TODO:, TODO:, TODO:, TODO:, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF}
}; // CCU output
/* SME output
CAN_frame BMWiX_12B8D087 = {.FD = true,
.ext_ID = true,
.DLC = 2,
.ID = 0x12B8D087,
.data = {0xFC, 0xFF}}; // 5000ms SME output - values
*/
CAN_frame BMWiX_16E = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x16E,
.data = {0x00, // Almost any possible number in 0x00 and 0xFF
0xA0, // Almost any possible number in 0xA0 and 0xAF
0xC9, 0xFF,
0x60, // FIXME: find out what this value represents
0xC9,
0x3A, // 0x3A to close contactors, 0x33 to open contactors
0xF7}}; // 0xF7 to close contactors, 0xF0 to open contactors // CCU output.
CAN_frame BMWiX_188 = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x188,
.data = {0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF}}; // CCU output - values while driving
CAN_frame BMWiX_1EA = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x1EA,
//.data = {TODO:km_least_significant, TODO:, TODO:, TODO:, TODO:km_most_significant, 0xFF, TODO:, TODO:}
}; // KOMBI output - kilometerstand
CAN_frame BMWiX_1FC = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x1FC,
.data = {0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0xC0,
0x00}}; // FIXME:(add transmitter node) output - heat management engine control - values
CAN_frame BMWiX_21D = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x21D,
// .data = {TODO:, TODO:, TODO:, 0xFF, 0xFF, 0xFF, 0xFF, TODO:}
}; // FIXME:(add transmitter node) output - request heating and air conditioning system 1
CAN_frame BMWiX_276 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x276,
.data = {0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFD}}; // BDC output - vehicle condition. Used for contactor closing
CAN_frame BMWiX_2ED = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x2ED,
.data = {
0x75,
0x75}}; // FIXME:(add transmitter node) output - ambient temperature (values seen in logs vary between 0x72 and 0x79)
CAN_frame BMWiX_2F1 = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x2F1,
.data = {0xFF, 0xFF, 0xD0, 0x39, 0x94, 0x00, 0xF3, 0xFF}}; // 1000ms BDC output - values - varies at startup
CAN_frame BMWiX_340 = {
.FD = true,
.ext_ID = false,
.DLC = 12,
.ID = 0x340,
// .data = {TODO:, TODO:, TODO:, 0xFF, TODO:, TODO:, 0x00, 0x00, TODO:, TODO:, TODO:, 0xFF, 0xFF, }
}; // CCU output
CAN_frame BMWiX_380 = {
.FD = true,
.ext_ID = false,
.DLC = 7,
.ID = 0x380,
// .data = {FIXME:(VIN_char1), FIXME:(VIN_char2), FIXME:(VIN_char3), FIXME:(VIN_char4), FIXME:(VIN_char5), FIXME:(VIN_char6), FIXME:(VIN_char7)}
}; // FIXME:(add transmitter node) output - VIN: ASCII2HEX
/* Not requested by SME
CAN_frame BMWiX_439 = {.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x439,
.data = {0xFF, 0x3F, 0xFF, 0xF3}}; // 1000ms BDC output
*/
CAN_frame BMWiX_442 = {
.FD = true,
.ext_ID = false,
.DLC = 6,
.ID = 0x442,
// .data = {TODO: relative time byte 0, TODO: relative time byte1, 0xA9, 0x00, 0xE0, 0x23}
}; // FIXME:(add transmitter node) output - relative time BN2020
/* SME output
CAN_frame
BMWiX_486 =
{
.FD = true,
.ext_ID = false,
.DLC = 48,
.ID = 0x486,
.data =
{
0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF,
0xFE, 0xFF, 0xFF, 0x7F, 0x33, 0xFD, 0xFD, 0xFD, 0xFD, 0xC0, 0x41, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; // 1000ms SME output - Suspected keep alive CONFIRM NEEDED
*/
/* SME output
CAN_frame BMWiX_49C = {.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x49C,
.data = {0xD2, 0xF2, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF}}; // 1000ms SME output - Suspected keep alive CONFIRM NEEDED
*/
CAN_frame BMWiX_4EB = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x4EB,
// .data = {TODO:, TODO:, TODO: 0xE0 or 0xE5 (while driving), 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
}; // BDC output - RSU condition
CAN_frame BMWiX_4F8 = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x4F8,
// .data = {0xFF, 0xFD, 0xFF, 0xFF, 0xFF, TODO:, TODO:, 0xC8, 0x00, 0x00, 0xF0, 0x40, 0xFE, 0xFF, 0xFD, 0xFF, TODO:, TODO:, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
}; // CCU output
CAN_frame BMWiX_510 = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x510,
.data = {
0x40, 0x10,
0x04, // 0x02 at contactor closing, afterwards 0x04 and 0x10, 0x00 to open contactors
0x00, 0x00,
0x80, // 0x00 at start of contactor closing, changing to 0x80, afterwards 0x80
0x01,
0x00}}; // 100ms BDC output - Values change in car logs, these bytes are the most common. Used for contactor closing
CAN_frame BMWiX_6D = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x6D,
.data = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0xFF}}; // 1000ms BDC output - [0] [1,2][3,4] counter x2. 3,4 is 9 higher than 1,2 is needed? [5-7] static
CAN_frame BMWiX_C0 = {
.FD = true,
.ext_ID = false,
.DLC = 2,
.ID = 0xC0,
.data = {
0xF0,
0x00}}; // BDC output - Keep Alive 2 BDC>SME 200ms First byte cycles F0 > FE second byte 00 - MINIMUM ID TO KEEP SME AWAKE
//Vehicle CAN END
//Request Data CAN START
CAN_frame BMWiX_6F4 = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0xC7}}; // Generic UDS Request data from SME. byte 4 selects requested value
CAN_frame BMWiX_6F4_REQUEST_SLEEPMODE = {
.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x6F4,
.data = {0x07, 0x02, 0x11, 0x04}}; // UDS Request Request BMS/SME goes to Sleep Mode
CAN_frame BMWiX_6F4_REQUEST_HARD_RESET = {.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x6F4,
.data = {0x07, 0x02, 0x11, 0x01}}; // UDS Request Hard reset of BMS/SME
CAN_frame BMWiX_6F4_REQUEST_CELL_TEMP = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xDD, 0xC0}}; // UDS Request Cell Temperatures
CAN_frame BMWiX_6F4_REQUEST_SOC = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0xCE}}; // Min/Avg/Max SOC%
CAN_frame BMWiX_6F4_REQUEST_CAPACITY = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5,
0xC7}}; //Current and max capacity kWh. Stored in kWh as 0.01 scale with -50 bias
CAN_frame BMWiX_6F4_REQUEST_MINMAXCELLV = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x53}}; //Min and max cell voltage 10V = Qualifier Invalid
CAN_frame BMWiX_6F4_REQUEST_MAINVOLTAGE_POSTCONTACTOR = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x4A}}; //Main Battery Voltage (After Contactor)
CAN_frame BMWiX_6F4_REQUEST_MAINVOLTAGE_PRECONTACTOR = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x4D}}; //Main Battery Voltage (Pre Contactor)
CAN_frame BMWiX_6F4_REQUEST_BATTERYCURRENT = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x61}}; //Current amps 32bit signed MSB. dA . negative is discharge
CAN_frame BMWiX_6F4_REQUEST_CELL_VOLTAGE = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x54}}; //MultiFrameIndividual Cell Voltages
CAN_frame BMWiX_6F4_REQUEST_T30VOLTAGE = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0xA7}}; //Terminal 30 Voltage (12V SME supply)
CAN_frame BMWiX_6F4_REQUEST_EOL_ISO = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xA8, 0x60}}; //Request EOL Reading including ISO
CAN_frame BMWiX_6F4_REQUEST_SOH = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x45}}; //SOH Max Min Mean Request
CAN_frame BMWiX_6F4_REQUEST_DATASUMMARY = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {
0x07, 0x03, 0x22, 0xE5,
0x45}}; //MultiFrame Summary Request, includes SOC/SOH/MinMax/MaxCapac/RemainCapac/max v and t at last charge. slow refreshrate
CAN_frame BMWiX_6F4_REQUEST_PYRO = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xAC, 0x93}}; //Pyro Status
CAN_frame BMWiX_6F4_REQUEST_UPTIME = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE4, 0xC0}}; // Uptime and Vehicle Time Status
CAN_frame BMWiX_6F4_REQUEST_HVIL = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x69}}; // Request HVIL State
CAN_frame BMWiX_6F4_REQUEST_BALANCINGSTATUS = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE4, 0xCA}}; // Request Balancing Data
CAN_frame BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x62}}; // Request allowable charge discharge amps
CAN_frame BMWiX_6F4_REQUEST_VOLTAGE_QUALIFIER_CHECK = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x4B}}; // Request HV Voltage Qualifier
CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_CLOSE = {
.FD = true,
.ext_ID = false,
.DLC = 6,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Close - Unconfirmed
CAN_frame BMWiX_6F4_REQUEST_CONTACTORS_OPEN = {
.FD = true,
.ext_ID = false,
.DLC = 6,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x51, 0x01}}; // Request Contactors Open - Unconfirmed
CAN_frame BMWiX_6F4_REQUEST_BALANCING_START = {
.FD = true,
.ext_ID = false,
.DLC = 6,
.ID = 0x6F4,
.data = {0xF4, 0x04, 0x71, 0x01, 0xAE, 0x77}}; // Request Balancing command?
CAN_frame BMWiX_6F4_REQUEST_BALANCING_START2 = {
.FD = true,
.ext_ID = false,
.DLC = 6,
.ID = 0x6F4,
.data = {0xF4, 0x04, 0x31, 0x01, 0xAE, 0x77}}; // Request Balancing command?
CAN_frame BMWiX_6F4_REQUEST_PACK_VOLTAGE_LIMITS = {
.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x4C}}; // Request pack voltage limits
CAN_frame BMWiX_6F4_CONTINUE_DATA = {.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x6F4,
.data = {0x07, 0x30, 0x00, 0x02}};
//Action Requests:
CAN_frame BMWiX_6F4_CELL_SOC = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0x9A}};
CAN_frame BMWiX_6F4_CELL_TEMP = {.FD = true,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F4,
.data = {0x07, 0x03, 0x22, 0xE5, 0xCA}};
//Request Data CAN End
//Setup UDS values to poll for
CAN_frame* UDS_REQUESTS100MS[17] = {&BMWiX_6F4_REQUEST_CELL_TEMP,
&BMWiX_6F4_REQUEST_SOC,
&BMWiX_6F4_REQUEST_CAPACITY,
&BMWiX_6F4_REQUEST_MINMAXCELLV,
&BMWiX_6F4_REQUEST_MAINVOLTAGE_POSTCONTACTOR,
&BMWiX_6F4_REQUEST_MAINVOLTAGE_PRECONTACTOR,
&BMWiX_6F4_REQUEST_BATTERYCURRENT,
&BMWiX_6F4_REQUEST_CELL_VOLTAGE,
&BMWiX_6F4_REQUEST_T30VOLTAGE,
&BMWiX_6F4_REQUEST_SOH,
&BMWiX_6F4_REQUEST_UPTIME,
&BMWiX_6F4_REQUEST_PYRO,
&BMWiX_6F4_REQUEST_EOL_ISO,
&BMWiX_6F4_REQUEST_HVIL,
&BMWiX_6F4_REQUEST_MAX_CHARGE_DISCHARGE_AMPS,
&BMWiX_6F4_REQUEST_BALANCINGSTATUS,
&BMWiX_6F4_REQUEST_PACK_VOLTAGE_LIMITS};
int numUDSreqs = sizeof(UDS_REQUESTS100MS) / sizeof(UDS_REQUESTS100MS[0]); // Number of elements in the array
//iX Intermediate vars
bool battery_info_available = false;
uint32_t battery_serial_number = 0;
int32_t battery_current = 0;
int16_t battery_voltage = 370; //Startup with valid values - needs fixing in future
int16_t terminal30_12v_voltage = 0;
int16_t battery_voltage_after_contactor = 0;
int16_t min_soc_state = 5000;
int16_t avg_soc_state = 5000;
int16_t max_soc_state = 5000;
int16_t min_soh_state = 9900; // Uses E5 45, also available in 78 73
int16_t avg_soh_state = 9900; // Uses E5 45, also available in 78 73
int16_t max_soh_state = 9900; // Uses E5 45, also available in 78 73
uint16_t max_design_voltage = 0;
uint16_t min_design_voltage = 0;
int32_t remaining_capacity = 0;
int32_t max_capacity = 0;
int16_t min_battery_temperature = 0;
int16_t avg_battery_temperature = 0;
int16_t max_battery_temperature = 0;
int16_t main_contactor_temperature = 0;
int16_t min_cell_voltage = 3700; //Startup with valid values - needs fixing in future
int16_t max_cell_voltage = 3700; //Startup with valid values - needs fixing in future
unsigned long min_cell_voltage_lastchanged = 0;
unsigned long max_cell_voltage_lastchanged = 0;
unsigned min_cell_voltage_lastreceived = 0;
unsigned max_cell_voltage_lastreceived = 0;
uint32_t sme_uptime = 0; //Uses E4 C0
int16_t allowable_charge_amps = 0; //E5 62
int16_t allowable_discharge_amps = 0; //E5 62
int32_t iso_safety_positive = 0; //Uses A8 60
int32_t iso_safety_negative = 0; //Uses A8 60
int32_t iso_safety_parallel = 0; //Uses A8 60
int16_t count_full_charges = 0; //TODO 42
int16_t count_charges = 0; //TODO 42
int16_t hvil_status = 0;
int16_t voltage_qualifier_status = 0; //0 = Valid, 1 = Invalid
int16_t balancing_status = 0; //4 = not active
uint8_t contactors_closed = 0; //TODO E5 BF or E5 51
uint8_t contactor_status_precharge = 0; //TODO E5 BF
uint8_t contactor_status_negative = 0; //TODO E5 BF
uint8_t contactor_status_positive = 0; //TODO E5 BF
uint8_t pyro_status_pss1 = 0; //Using AC 93
uint8_t pyro_status_pss4 = 0; //Using AC 93
uint8_t pyro_status_pss6 = 0; //Using AC 93
uint8_t uds_req_id_counter = 0;
uint8_t detected_number_of_cells = 108;
const unsigned long STALE_PERIOD =
STALE_PERIOD_CONFIG; // Time in milliseconds to check for staleness (e.g., 5000 ms = 5 seconds)
//End iX Intermediate vars
uint8_t current_cell_polled = 0;
uint16_t counter_10ms = 0; // max 65535 --> 655.35 seconds
uint8_t counter_100ms = 0; // max 255 --> 25.5 seconds
bool isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChangeTime);
uint8_t increment_uds_req_id_counter(uint8_t index);
uint8_t increment_alive_counter(uint8_t counter);
/**
* @brief Handle incoming user request to close or open contactors
*
* @param[in] void
*
* @return void
*/
void HandleIncomingUserRequest(void);
void HandleIncomingUserRequest(void);
/**
/**
* @brief Handle incoming inverter request to close or open contactors.alignas
*
* This function uses the "inverter_allows_contactor_closing" flag from the datalayer, to determine if CAN messages shall be sent to the battery to close or open the contactors.
@ -37,51 +574,52 @@ void HandleIncomingUserRequest(void);
*
* @return void
*/
void HandleIncomingInverterRequest(void);
void HandleIncomingInverterRequest(void);
/**
/**
* @brief Close contactors of the BMW iX battery
*
* @param[in] void
*
* @return void
*/
void BmwIxCloseContactors(void);
void BmwIxCloseContactors(void);
/**
/**
* @brief Handle close contactors requests for the BMW iX battery
*
* @param[in] counter_10ms Counter that increments by 1, every 10ms
*
* @return void
*/
void HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms);
void HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms);
/**
/**
* @brief Keep contactors of the BMW iX battery closed
*
* @param[in] counter_100ms Counter that increments by 1, every 100 ms
*
* @return void
*/
void BmwIxKeepContactorsClosed(uint8_t counter_100ms);
void BmwIxKeepContactorsClosed(uint8_t counter_100ms);
/**
/**
* @brief Open contactors of the BMW iX battery
*
* @param[in] void
*
* @return void
*/
void BmwIxOpenContactors(void);
void BmwIxOpenContactors(void);
/**
/**
* @brief Handle open contactors requests for the BMW iX battery
*
* @param[in] counter_10ms Counter that increments by 1, every 10ms
*
* @return void
*/
void HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms);
void HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms);
};
#endif

View file

@ -1,40 +1,11 @@
#include "../include.h"
#ifdef BMW_PHEV_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "BMW-PHEV-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was send
static unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send
static unsigned long previousMillis640 = 0; // will store last time a 600ms CAN Message was send
static unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was send
static unsigned long previousMillis5000 = 0; // will store last time a 5000ms CAN Message was send
static unsigned long previousMillis10000 = 0; // will store last time a 10000ms CAN Message was send
#define ALIVE_MAX_VALUE 14 // BMW CAN messages contain alive counter, goes from 0...14
enum CmdState { SOH, CELL_VOLTAGE_MINMAX, SOC, CELL_VOLTAGE_CELLNO, CELL_VOLTAGE_CELLNO_LAST };
static CmdState cmdState = SOC;
// A structure to keep track of the ongoing multi-frame UDS response
typedef struct {
bool UDS_inProgress; // Are we currently receiving a multi-frame message?
uint16_t UDS_expectedLength; // Expected total payload length
uint16_t UDS_bytesReceived; // How many bytes have been stored so far
uint8_t UDS_moduleID; // The "module" indicated by the first frame
uint8_t receivedInBatch; // Number of CFs received in the current batch
uint8_t UDS_buffer[256]; // Buffer for the reassembled data
unsigned long UDS_lastFrameMillis; // Timestamp of last frame (for timeouts, if desired)
} UDS_RxContext;
// A single global UDS context, since only one module can respond at a time
static UDS_RxContext gUDSContext;
const unsigned char crc8_table[256] =
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0,
@ -102,349 +73,8 @@ TODO:
*/
//Vehicle CAN START
CAN_frame BMWiX_0C0 = {
.FD = false,
.ext_ID = false,
.DLC = 2,
.ID = 0x0C0,
.data = {
0xF0,
0x08}}; // Keep Alive 2 BDC>SME 200ms First byte cycles F0 > FE second byte 08 static - MINIMUM ID TO KEEP SME AWAKE
CAN_frame BMW_13E = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x13E,
.data = {0xFF, 0x31, 0xFA, 0xFA, 0xFA, 0xFA, 0x0C, 0x00}};
//Vehicle CAN END
//Request Data CAN START
CAN_frame BMW_PHEV_BUS_WAKEUP_REQUEST = {
.FD = false,
.ext_ID = false,
.DLC = 4,
.ID = 0x554,
.data = {
0x5A, 0xA5, 0x5A,
0xA5}}; // Won't work at 500kbps! Ideally sent at 50kbps - but can also achieve wakeup at 100kbps (helps with library support but might not be as reliable). Might need to be sent twice + clear buffer
CAN_frame BMWPHEV_6F1_REQUEST_SOC = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0xC4}}; // SOC%
CAN_frame BMWPHEV_6F1_REQUEST_SOH = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0x7B}}; // SOH%
CAN_frame BMWPHEV_6F1_REQUEST_CURRENT = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0x69}}; // SOH%
CAN_frame BMWPHEV_6F1_REQUEST_VOLTAGE_LIMITS = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0x7E}}; // Pack Voltage Limits Multi Frame
CAN_frame BMWPHEV_6F1_REQUEST_PAIRED_VIN = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xF1, 0x90}}; // SME Paired VIN
CAN_frame BMWPHEV_6F1_REQUEST_ISO_READING1 = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {
0x07, 0x03, 0x22, 0xDD,
0x6A}}; // MULTI FRAME ISOLATIONSWIDERSTAND 62 DD 6A [07 D0] [07 D0] [07 D0] [01] [01] [01] 00 00 00 00 00 [EXT Reading] [INT reading] [ EXT - 0 not plausible, 1 plausible]
CAN_frame BMWPHEV_6F1_REQUEST_ISO_READING2 = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xD6,
0xD9}}; // R_ISO_ROH 62 D6 D9 [07 FF] [13] (2047kohm) quality of reading 0-21 (19)
CAN_frame BMWPHEV_6F1_REQUEST_PACK_INFO = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDF, 0x71}}; // 62 DF 71 00 60 1C 25 1C? Cell Count, Module Count
CAN_frame BMWPHEV_6F1_REQUEST_CURRENT_LIMITS = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0x7D}}; // Pack Current Limits Multi Frame
CAN_frame BMWPHEV_6F1_REQUEST_MAINVOLTAGE_PRECONTACTOR = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0xB4}}; //Main Battery Voltage (Pre Contactor)
CAN_frame BMWPHEV_6F1_REQUEST_MAINVOLTAGE_POSTCONTACTOR = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0x66}}; //Main Battery Voltage (After Contactor)
CAN_frame BMWPHEV_6F1_REQUEST_CELLSUMMARY = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDF, 0xA0}}; //Min and max cell voltage + temps 6.55V = Qualifier Invalid?
CAN_frame BMWPHEV_6F1_REQUEST_CELLS_INDIVIDUAL_VOLTS = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDF, 0xA5}}; //All individual cell voltages
CAN_frame BMWPHEV_6F1_REQUEST_CELL_TEMP = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {
0x07, 0x03, 0x22, 0xDD,
0xC0}}; // UDS Request Cell Temperatures min max avg. Has continue frame min in first, then max + avg in second frame
CAN_frame BMW_6F1_REQUEST_CONTINUE_MULTIFRAME = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {
0x07, 0x30, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00}}; //Request continued frames from UDS Multiframe request byte[2] is the request messages to return per continue. default 0x03, all is 0x00
CAN_frame BMW_6F1_REQUEST_HARD_RESET = {.FD = false,
.ext_ID = false,
.DLC = 4,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x11, 0x01}}; // Reset BMS - TBC
CAN_frame BMWPHEV_6F1_REQUEST_CONTACTORS_CLOSE = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x2E, 0xDD, 0x61, 0x01, 0x00, 0x00}}; // Request Contactors Close - Unconfirmed
CAN_frame BMWPHEV_6F1_REQUEST_CONTACTORS_OPEN = {
.FD = false,
.ext_ID = false,
.DLC = 6,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x2E, 0xDD, 0x61, 0x00, 0x00, 0x00}}; // Request Contactors Open - Unconfirmed
CAN_frame BMWPHEV_6F1_REQUEST_BALANCING_STATUS = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x31, 0x03, 0xAD, 0x6B, 0x00,
0x00}}; // Balancing status. Response 7DLC F1 05 71 03 AD 6B 01 (01 = active) (03 not active)
CAN_frame BMWPHEV_6F1_REQUEST_ISOLATION_TEST = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x31, 0x01, 0xAD, 0x61, 0x00, 0x00}}; // Start Isolation Test
CAN_frame BMWPHEV_6F1_REQUEST_BALANCING_START = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x31, 0x01, 0xAD, 0x6B, 0x00, 0x00}}; // Balancing start request
CAN_frame BMWPHEV_6F1_REQUEST_BALANCING_STOP = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x31, 0x02, 0xAD, 0x6B, 0x00, 0x00}}; // Balancing stop request
//Action Requests:
CAN_frame BMW_10B = {.FD = false,
.ext_ID = false,
.DLC = 3,
.ID = 0x10B,
.data = {0xCD, 0x00, 0xFC}}; // Contactor closing command?
CAN_frame BMWPHEV_6F1_CELL_SOC = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xE5, 0x9A}};
CAN_frame BMWPHEV_6F1_CELL_TEMP = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xE5, 0xCA}};
//Request Data CAN End
static bool battery_awake = false;
//Setup Fast UDS values to poll for
CAN_frame* UDS_REQUESTS_FAST[] = {&BMWPHEV_6F1_REQUEST_CELLSUMMARY,
&BMWPHEV_6F1_REQUEST_SOC,
&BMWPHEV_6F1_REQUEST_CURRENT,
&BMWPHEV_6F1_REQUEST_VOLTAGE_LIMITS,
&BMWPHEV_6F1_REQUEST_MAINVOLTAGE_PRECONTACTOR,
&BMWPHEV_6F1_REQUEST_MAINVOLTAGE_POSTCONTACTOR};
int numFastUDSreqs = sizeof(UDS_REQUESTS_FAST) / sizeof(UDS_REQUESTS_FAST[0]); //Store Number of elements in the array
//Setup Slow UDS values to poll for
CAN_frame* UDS_REQUESTS_SLOW[] = {&BMWPHEV_6F1_REQUEST_ISO_READING1, &BMWPHEV_6F1_REQUEST_ISO_READING2,
&BMWPHEV_6F1_REQUEST_CURRENT_LIMITS, &BMWPHEV_6F1_REQUEST_SOH,
&BMWPHEV_6F1_REQUEST_CELLS_INDIVIDUAL_VOLTS, &BMWPHEV_6F1_REQUEST_CELL_TEMP,
&BMWPHEV_6F1_REQUEST_BALANCING_STATUS, &BMWPHEV_6F1_REQUEST_PAIRED_VIN};
int numSlowUDSreqs = sizeof(UDS_REQUESTS_SLOW) / sizeof(UDS_REQUESTS_SLOW[0]); // Store Number of elements in the array
//PHEV intermediate vars
//#define UDS_LOG //Useful for logging multiframe handling
static uint16_t battery_max_charge_voltage = 0;
static int16_t battery_max_charge_amperage = 0;
static uint16_t battery_min_discharge_voltage = 0;
static int16_t battery_max_discharge_amperage = 0;
static uint8_t startup_counter_contactor = 0;
static uint8_t alive_counter_20ms = 0;
static uint8_t BMW_13E_counter = 0;
static uint32_t battery_BEV_available_power_shortterm_charge = 0;
static uint32_t battery_BEV_available_power_shortterm_discharge = 0;
static uint32_t battery_BEV_available_power_longterm_charge = 0;
static uint32_t battery_BEV_available_power_longterm_discharge = 0;
static uint16_t battery_predicted_energy_charge_condition = 0;
static uint16_t battery_predicted_energy_charging_target = 0;
static uint16_t battery_prediction_voltage_shortterm_charge = 0;
static uint16_t battery_prediction_voltage_shortterm_discharge = 0;
static uint16_t battery_prediction_voltage_longterm_charge = 0;
static uint16_t battery_prediction_voltage_longterm_discharge = 0;
static uint8_t battery_status_service_disconnection_plug = 0;
static uint8_t battery_status_measurement_isolation = 0;
static uint8_t battery_request_abort_charging = 0;
static uint16_t battery_prediction_duration_charging_minutes = 0;
static uint8_t battery_prediction_time_end_of_charging_minutes = 0;
static uint16_t battery_energy_content_maximum_kWh = 0;
static uint8_t battery_request_operating_mode = 0;
static uint16_t battery_target_voltage_in_CV_mode = 0;
static uint8_t battery_request_charging_condition_minimum = 0;
static uint8_t battery_request_charging_condition_maximum = 0;
static uint16_t battery_display_SOC = 0;
static uint8_t battery_status_error_isolation_external_Bordnetz = 0;
static uint8_t battery_status_error_isolation_internal_Bordnetz = 0;
static uint8_t battery_request_cooling = 0;
static uint8_t battery_status_valve_cooling = 0;
static uint8_t battery_status_error_locking = 0;
static uint8_t battery_status_precharge_locked = 0;
static uint8_t battery_status_disconnecting_switch = 0;
static uint8_t battery_status_emergency_mode = 0;
static uint8_t battery_request_service = 0;
static uint8_t battery_error_emergency_mode = 0;
static uint8_t battery_status_error_disconnecting_switch = 0;
static uint8_t battery_status_warning_isolation = 0;
static uint8_t battery_status_cold_shutoff_valve = 0;
static int16_t battery_temperature_HV = 0;
static int16_t battery_temperature_heat_exchanger = 0;
static int16_t battery_temperature_max = 0;
static int16_t battery_temperature_min = 0;
static bool pack_limit_info_available = false;
static bool cell_limit_info_available = false;
//iX Intermediate vars
static uint32_t battery_serial_number = 0;
static int32_t battery_current = 0;
static int16_t battery_voltage = 3700; //Initialize as valid - should be fixed in future
static int16_t terminal30_12v_voltage = 0;
static int16_t battery_voltage_after_contactor = 0;
static int16_t min_soc_state = 5000;
static int16_t avg_soc_state = 5000;
static int16_t max_soc_state = 5000;
static int16_t min_soh_state = 9999; // Uses E5 45, also available in 78 73
static int16_t avg_soh_state = 9999; // Uses E5 45, also available in 78 73
static int16_t max_soh_state = 9999; // Uses E5 45, also available in 78 73
static uint16_t max_design_voltage = 0;
static uint16_t min_design_voltage = 0;
static int32_t remaining_capacity = 0;
static int32_t max_capacity = 0;
static int16_t main_contactor_temperature = 0;
static int16_t min_cell_voltage = 3700; //Initialize as valid - should be fixed in future
static int16_t max_cell_voltage = 3700; //Initialize as valid - should be fixed in future
static unsigned long min_cell_voltage_lastchanged = 0;
static unsigned long max_cell_voltage_lastchanged = 0;
static unsigned min_cell_voltage_lastreceived = 0;
static unsigned max_cell_voltage_lastreceived = 0;
static int16_t allowable_charge_amps = 0; //E5 62
static int16_t allowable_discharge_amps = 0; //E5 62
static int32_t iso_safety_int_kohm = 0; //STAT_ISOWIDERSTAND_INT_WERT
static int32_t iso_safety_ext_kohm = 0; //STAT_ISOWIDERSTAND_EXT_STD_WERT
static int32_t iso_safety_trg_kohm = 0;
static int32_t iso_safety_ext_plausible = 0; //STAT_ISOWIDERSTAND_EXT_TRG_PLAUS
static int32_t iso_safety_int_plausible = 0; //STAT_ISOWIDERSTAND_EXT_TRG_WERT
static int32_t iso_safety_trg_plausible = 0;
static int32_t iso_safety_kohm = 0; //STAT_R_ISO_ROH_01_WERT
static int32_t iso_safety_kohm_quality = 0; //STAT_R_ISO_ROH_QAL_01_INFO Quality of measurement 0-21 (higher better)
static uint8_t paired_vin[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //17 Byte array for paired VIN
static int16_t count_full_charges = 0; //TODO 42
static int16_t count_charges = 0; //TODO 42
static int16_t hvil_status = 0;
static int16_t voltage_qualifier_status = 0; //0 = Valid, 1 = Invalid
static int16_t balancing_status = 0; //4 = not active
static uint8_t contactors_closed = 0; //TODO E5 BF or E5 51
static uint8_t contactor_status_precharge = 0; //TODO E5 BF
static uint8_t contactor_status_negative = 0; //TODO E5 BF
static uint8_t contactor_status_positive = 0; //TODO E5 BF
static uint8_t uds_fast_req_id_counter = 0;
static uint8_t uds_slow_req_id_counter = 0;
static uint8_t detected_number_of_cells = 96;
const unsigned long STALE_PERIOD =
STALE_PERIOD_CONFIG; // Time in milliseconds to check for staleness (e.g., 5000 ms = 5 seconds)
static byte iX_0C0_counter = 0xF0; // Initialize to 0xF0
//End iX Intermediate vars
static uint8_t current_cell_polled = 0;
// Function to check if a value has gone stale over a specified time period
bool isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChangeTime) {
bool BmwPhevBattery::isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChangeTime) {
unsigned long currentTime = millis();
// Check if the value has changed
@ -479,7 +109,7 @@ static uint8_t increment_uds_req_id_counter(uint8_t index, int numReqs) {
UDS Multi-Frame Helpers
-------------------------------------------------------------------------- */
void startUDSMultiFrameReception(uint16_t totalLength, uint8_t moduleID) {
void BmwPhevBattery::startUDSMultiFrameReception(uint16_t totalLength, uint8_t moduleID) {
gUDSContext.UDS_inProgress = true;
gUDSContext.UDS_expectedLength = totalLength;
gUDSContext.UDS_bytesReceived = 0;
@ -488,7 +118,7 @@ void startUDSMultiFrameReception(uint16_t totalLength, uint8_t moduleID) {
gUDSContext.UDS_lastFrameMillis = millis(); // if you want to track timeouts
}
bool storeUDSPayload(const uint8_t* payload, uint8_t length) {
bool BmwPhevBattery::storeUDSPayload(const uint8_t* payload, uint8_t length) {
if (gUDSContext.UDS_bytesReceived + length > sizeof(gUDSContext.UDS_buffer)) {
// Overflow => abort
gUDSContext.UDS_inProgress = false;
@ -511,11 +141,11 @@ bool storeUDSPayload(const uint8_t* payload, uint8_t length) {
return true;
}
bool isUDSMessageComplete() {
bool BmwPhevBattery::isUDSMessageComplete() {
return (!gUDSContext.UDS_inProgress && gUDSContext.UDS_bytesReceived > 0);
}
static uint8_t increment_alive_counter(uint8_t counter) {
uint8_t BmwPhevBattery::increment_alive_counter(uint8_t counter) {
counter++;
if (counter > ALIVE_MAX_VALUE) {
counter = 0;
@ -532,7 +162,7 @@ static byte increment_0C0_counter(byte counter) {
return counter;
}
void processCellVoltages() {
void BmwPhevBattery::processCellVoltages() {
const int startByte = 3; // Start reading at byte 3
const int numVoltages = 96; // Number of cell voltage values to process
int voltage_index = 0; // Starting index for the destination array
@ -553,7 +183,7 @@ void processCellVoltages() {
}
}
void wake_battery_via_canbus() {
void BmwPhevBattery::wake_battery_via_canbus() {
//TJA1055 transceiver remote wake requires pulses on the bus of
// Dominant for at least ~7µs (min) and at most ~38µs (max)
// Followed by a Recessive interval of at least ~3µs (min) and at most ~10µs (max)
@ -570,7 +200,8 @@ void wake_battery_via_canbus() {
logging.println("Sent magic wakeup packet to SME at 100kbps...");
#endif
}
void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
void BmwPhevBattery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer
datalayer.battery.status.real_soc = avg_soc_state;
@ -677,7 +308,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
}
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
battery_awake = true;
switch (rx_frame.ID) {
@ -1005,7 +636,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void BmwPhevBattery::transmit_can(unsigned long currentMillis) {
//if (battery_awake) { //We can always send CAN as the PHEV BMS will wake up on vehicle comms
@ -1067,7 +698,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void BmwPhevBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "BMW PHEV Battery", 63);
datalayer.system.info.battery_protocol[63] = '\0';
//Wakeup the SME

View file

@ -2,21 +2,411 @@
#define BMW_PHEV_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS BmwPhevBattery
#define MAX_PACK_VOLTAGE_DV 4650 //4650 = 465.0V
#define MIN_PACK_VOLTAGE_DV 3000
#define MAX_CELL_DEVIATION_MV 250
#define MAX_CELL_VOLTAGE_MV 4300 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2800 //Battery is put into emergency stop if one cell goes below this value
#define MAX_DISCHARGE_POWER_ALLOWED_W 10000
#define MAX_CHARGE_POWER_ALLOWED_W 10000
#define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500
#define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
#define STALE_PERIOD_CONFIG \
3600000; //Number of milliseconds before critical values are classed as stale/stuck 1800000 = 3600 seconds / 60mins
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
class BmwPhevBattery : 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:
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V
static const int MIN_PACK_VOLTAGE_DV = 3000;
static const int MAX_CELL_DEVIATION_MV = 250;
static const int MAX_CELL_VOLTAGE_MV = 4300; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2800; //Battery is put into emergency stop if one cell goes below this value
static const int MAX_DISCHARGE_POWER_ALLOWED_W = 10000;
static const int MAX_CHARGE_POWER_ALLOWED_W = 10000;
static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500;
static const int RAMPDOWN_SOC =
9000; // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
static const int STALE_PERIOD_CONFIG =
3600000; //Number of milliseconds before critical values are classed as stale/stuck 1800000 = 3600 seconds / 60mins
bool isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChangeTime);
void startUDSMultiFrameReception(uint16_t totalLength, uint8_t moduleID);
bool storeUDSPayload(const uint8_t* payload, uint8_t length);
bool isUDSMessageComplete();
void processCellVoltages();
void wake_battery_via_canbus();
uint8_t increment_alive_counter(uint8_t counter);
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was send
unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send
unsigned long previousMillis640 = 0; // will store last time a 600ms CAN Message was send
unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was send
unsigned long previousMillis5000 = 0; // will store last time a 5000ms CAN Message was send
unsigned long previousMillis10000 = 0; // will store last time a 10000ms CAN Message was send
static const int ALIVE_MAX_VALUE = 14; // BMW CAN messages contain alive counter, goes from 0...14
enum CmdState { SOH, CELL_VOLTAGE_MINMAX, SOC, CELL_VOLTAGE_CELLNO, CELL_VOLTAGE_CELLNO_LAST };
CmdState cmdState = SOC;
// A structure to keep track of the ongoing multi-frame UDS response
typedef struct {
bool UDS_inProgress; // Are we currently receiving a multi-frame message?
uint16_t UDS_expectedLength; // Expected total payload length
uint16_t UDS_bytesReceived; // How many bytes have been stored so far
uint8_t UDS_moduleID; // The "module" indicated by the first frame
uint8_t receivedInBatch; // Number of CFs received in the current batch
uint8_t UDS_buffer[256]; // Buffer for the reassembled data
unsigned long UDS_lastFrameMillis; // Timestamp of last frame (for timeouts, if desired)
} UDS_RxContext;
// A single global UDS context, since only one module can respond at a time
UDS_RxContext gUDSContext;
//Vehicle CAN START
CAN_frame BMWiX_0C0 = {
.FD = false,
.ext_ID = false,
.DLC = 2,
.ID = 0x0C0,
.data = {
0xF0,
0x08}}; // Keep Alive 2 BDC>SME 200ms First byte cycles F0 > FE second byte 08 - MINIMUM ID TO KEEP SME AWAKE
CAN_frame BMW_13E = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x13E,
.data = {0xFF, 0x31, 0xFA, 0xFA, 0xFA, 0xFA, 0x0C, 0x00}};
//Vehicle CAN END
//Request Data CAN START
CAN_frame BMW_PHEV_BUS_WAKEUP_REQUEST = {
.FD = false,
.ext_ID = false,
.DLC = 4,
.ID = 0x554,
.data = {
0x5A, 0xA5, 0x5A,
0xA5}}; // Won't work at 500kbps! Ideally sent at 50kbps - but can also achieve wakeup at 100kbps (helps with library support but might not be as reliable). Might need to be sent twice + clear buffer
CAN_frame BMWPHEV_6F1_REQUEST_SOC = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0xC4}}; // SOC%
CAN_frame BMWPHEV_6F1_REQUEST_SOH = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0x7B}}; // SOH%
CAN_frame BMWPHEV_6F1_REQUEST_CURRENT = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0x69}}; // SOH%
CAN_frame BMWPHEV_6F1_REQUEST_VOLTAGE_LIMITS = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0x7E}}; // Pack Voltage Limits Multi Frame
CAN_frame BMWPHEV_6F1_REQUEST_PAIRED_VIN = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xF1, 0x90}}; // SME Paired VIN
CAN_frame BMWPHEV_6F1_REQUEST_ISO_READING1 = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {
0x07, 0x03, 0x22, 0xDD,
0x6A}}; // MULTI FRAME ISOLATIONSWIDERSTAND 62 DD 6A [07 D0] [07 D0] [07 D0] [01] [01] [01] 00 00 00 00 00 [EXT Reading] [INT reading] [ EXT - 0 not plausible, 1 plausible]
CAN_frame BMWPHEV_6F1_REQUEST_ISO_READING2 = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xD6,
0xD9}}; // R_ISO_ROH 62 D6 D9 [07 FF] [13] (2047kohm) quality of reading 0-21 (19)
CAN_frame BMWPHEV_6F1_REQUEST_PACK_INFO = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDF, 0x71}}; // 62 DF 71 00 60 1C 25 1C? Cell Count, Module Count
CAN_frame BMWPHEV_6F1_REQUEST_CURRENT_LIMITS = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0x7D}}; // Pack Current Limits Multi Frame
CAN_frame BMWPHEV_6F1_REQUEST_MAINVOLTAGE_PRECONTACTOR = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0xB4}}; //Main Battery Voltage (Pre Contactor)
CAN_frame BMWPHEV_6F1_REQUEST_MAINVOLTAGE_POSTCONTACTOR = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDD, 0x66}}; //Main Battery Voltage (After Contactor)
CAN_frame BMWPHEV_6F1_REQUEST_CELLSUMMARY = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDF, 0xA0}}; //Min and max cell voltage + temps 6.55V = Qualifier Invalid?
CAN_frame BMWPHEV_6F1_REQUEST_CELLS_INDIVIDUAL_VOLTS = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xDF, 0xA5}}; //All individual cell voltages
CAN_frame BMWPHEV_6F1_REQUEST_CELL_TEMP = {
.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {
0x07, 0x03, 0x22, 0xDD,
0xC0}}; // UDS Request Cell Temperatures min max avg. Has continue frame min in first, then max + avg in second frame
CAN_frame BMW_6F1_REQUEST_CONTINUE_MULTIFRAME = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {
0x07, 0x30, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00}}; //Request continued frames from UDS Multiframe request byte[2] is the request messages to return per continue. default 0x03, all is 0x00
CAN_frame BMW_6F1_REQUEST_HARD_RESET = {.FD = false,
.ext_ID = false,
.DLC = 4,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x11, 0x01}}; // Reset BMS - TBC
CAN_frame BMWPHEV_6F1_REQUEST_CONTACTORS_CLOSE = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x2E, 0xDD, 0x61, 0x01, 0x00, 0x00}}; // Request Contactors Close - Unconfirmed
CAN_frame BMWPHEV_6F1_REQUEST_CONTACTORS_OPEN = {
.FD = false,
.ext_ID = false,
.DLC = 6,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x2E, 0xDD, 0x61, 0x00, 0x00, 0x00}}; // Request Contactors Open - Unconfirmed
CAN_frame BMWPHEV_6F1_REQUEST_BALANCING_STATUS = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x31, 0x03, 0xAD, 0x6B, 0x00,
0x00}}; // Balancing status. Response 7DLC F1 05 71 03 AD 6B 01 (01 = active) (03 not active)
CAN_frame BMWPHEV_6F1_REQUEST_ISOLATION_TEST = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x31, 0x01, 0xAD, 0x61, 0x00, 0x00}}; // Start Isolation Test
CAN_frame BMWPHEV_6F1_REQUEST_BALANCING_START = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x31, 0x01, 0xAD, 0x6B, 0x00, 0x00}}; // Balancing start request
CAN_frame BMWPHEV_6F1_REQUEST_BALANCING_STOP = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x6F1,
.data = {0x07, 0x04, 0x31, 0x02, 0xAD, 0x6B, 0x00, 0x00}}; // Balancing stop request
//Action Requests:
CAN_frame BMW_10B = {.FD = false,
.ext_ID = false,
.DLC = 3,
.ID = 0x10B,
.data = {0xCD, 0x00, 0xFC}}; // Contactor closing command?
CAN_frame BMWPHEV_6F1_CELL_SOC = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xE5, 0x9A}};
CAN_frame BMWPHEV_6F1_CELL_TEMP = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6F1,
.data = {0x07, 0x03, 0x22, 0xE5, 0xCA}};
//Request Data CAN End
bool battery_awake = false;
//Setup Fast UDS values to poll for
CAN_frame* UDS_REQUESTS_FAST[6] = {&BMWPHEV_6F1_REQUEST_CELLSUMMARY,
&BMWPHEV_6F1_REQUEST_SOC,
&BMWPHEV_6F1_REQUEST_CURRENT,
&BMWPHEV_6F1_REQUEST_VOLTAGE_LIMITS,
&BMWPHEV_6F1_REQUEST_MAINVOLTAGE_PRECONTACTOR,
&BMWPHEV_6F1_REQUEST_MAINVOLTAGE_POSTCONTACTOR};
int numFastUDSreqs =
sizeof(UDS_REQUESTS_FAST) / sizeof(UDS_REQUESTS_FAST[0]); //Store Number of elements in the array
//Setup Slow UDS values to poll for
CAN_frame* UDS_REQUESTS_SLOW[8] = {&BMWPHEV_6F1_REQUEST_ISO_READING1, &BMWPHEV_6F1_REQUEST_ISO_READING2,
&BMWPHEV_6F1_REQUEST_CURRENT_LIMITS, &BMWPHEV_6F1_REQUEST_SOH,
&BMWPHEV_6F1_REQUEST_CELLS_INDIVIDUAL_VOLTS, &BMWPHEV_6F1_REQUEST_CELL_TEMP,
&BMWPHEV_6F1_REQUEST_BALANCING_STATUS, &BMWPHEV_6F1_REQUEST_PAIRED_VIN};
int numSlowUDSreqs =
sizeof(UDS_REQUESTS_SLOW) / sizeof(UDS_REQUESTS_SLOW[0]); // Store Number of elements in the array
//PHEV intermediate vars
//#define UDS_LOG //Useful for logging multiframe handling
uint16_t battery_max_charge_voltage = 0;
int16_t battery_max_charge_amperage = 0;
uint16_t battery_min_discharge_voltage = 0;
int16_t battery_max_discharge_amperage = 0;
uint8_t startup_counter_contactor = 0;
uint8_t alive_counter_20ms = 0;
uint8_t BMW_13E_counter = 0;
uint32_t battery_BEV_available_power_shortterm_charge = 0;
uint32_t battery_BEV_available_power_shortterm_discharge = 0;
uint32_t battery_BEV_available_power_longterm_charge = 0;
uint32_t battery_BEV_available_power_longterm_discharge = 0;
uint16_t battery_predicted_energy_charge_condition = 0;
uint16_t battery_predicted_energy_charging_target = 0;
uint16_t battery_prediction_voltage_shortterm_charge = 0;
uint16_t battery_prediction_voltage_shortterm_discharge = 0;
uint16_t battery_prediction_voltage_longterm_charge = 0;
uint16_t battery_prediction_voltage_longterm_discharge = 0;
uint8_t battery_status_service_disconnection_plug = 0;
uint8_t battery_status_measurement_isolation = 0;
uint8_t battery_request_abort_charging = 0;
uint16_t battery_prediction_duration_charging_minutes = 0;
uint8_t battery_prediction_time_end_of_charging_minutes = 0;
uint16_t battery_energy_content_maximum_kWh = 0;
uint8_t battery_request_operating_mode = 0;
uint16_t battery_target_voltage_in_CV_mode = 0;
uint8_t battery_request_charging_condition_minimum = 0;
uint8_t battery_request_charging_condition_maximum = 0;
uint16_t battery_display_SOC = 0;
uint8_t battery_status_error_isolation_external_Bordnetz = 0;
uint8_t battery_status_error_isolation_internal_Bordnetz = 0;
uint8_t battery_request_cooling = 0;
uint8_t battery_status_valve_cooling = 0;
uint8_t battery_status_error_locking = 0;
uint8_t battery_status_precharge_locked = 0;
uint8_t battery_status_disconnecting_switch = 0;
uint8_t battery_status_emergency_mode = 0;
uint8_t battery_request_service = 0;
uint8_t battery_error_emergency_mode = 0;
uint8_t battery_status_error_disconnecting_switch = 0;
uint8_t battery_status_warning_isolation = 0;
uint8_t battery_status_cold_shutoff_valve = 0;
int16_t battery_temperature_HV = 0;
int16_t battery_temperature_heat_exchanger = 0;
int16_t battery_temperature_max = 0;
int16_t battery_temperature_min = 0;
bool pack_limit_info_available = false;
bool cell_limit_info_available = false;
//iX Intermediate vars
uint32_t battery_serial_number = 0;
int32_t battery_current = 0;
int16_t battery_voltage = 3700; //Initialize as valid - should be fixed in future
int16_t terminal30_12v_voltage = 0;
int16_t battery_voltage_after_contactor = 0;
int16_t min_soc_state = 5000;
int16_t avg_soc_state = 5000;
int16_t max_soc_state = 5000;
int16_t min_soh_state = 9999; // Uses E5 45, also available in 78 73
int16_t avg_soh_state = 9999; // Uses E5 45, also available in 78 73
int16_t max_soh_state = 9999; // Uses E5 45, also available in 78 73
uint16_t max_design_voltage = 0;
uint16_t min_design_voltage = 0;
int32_t remaining_capacity = 0;
int32_t max_capacity = 0;
int16_t main_contactor_temperature = 0;
int16_t min_cell_voltage = 3700; //Initialize as valid - should be fixed in future
int16_t max_cell_voltage = 3700; //Initialize as valid - should be fixed in future
unsigned long min_cell_voltage_lastchanged = 0;
unsigned long max_cell_voltage_lastchanged = 0;
unsigned min_cell_voltage_lastreceived = 0;
unsigned max_cell_voltage_lastreceived = 0;
int16_t allowable_charge_amps = 0; //E5 62
int16_t allowable_discharge_amps = 0; //E5 62
int32_t iso_safety_int_kohm = 0; //STAT_ISOWIDERSTAND_INT_WERT
int32_t iso_safety_ext_kohm = 0; //STAT_ISOWIDERSTAND_EXT_STD_WERT
int32_t iso_safety_trg_kohm = 0;
int32_t iso_safety_ext_plausible = 0; //STAT_ISOWIDERSTAND_EXT_TRG_PLAUS
int32_t iso_safety_int_plausible = 0; //STAT_ISOWIDERSTAND_EXT_TRG_WERT
int32_t iso_safety_trg_plausible = 0;
int32_t iso_safety_kohm = 0; //STAT_R_ISO_ROH_01_WERT
int32_t iso_safety_kohm_quality = 0; //STAT_R_ISO_ROH_QAL_01_INFO Quality of measurement 0-21 (higher better)
uint8_t paired_vin[17] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //17 Byte array for paired VIN
int16_t count_full_charges = 0; //TODO 42
int16_t count_charges = 0; //TODO 42
int16_t hvil_status = 0;
int16_t voltage_qualifier_status = 0; //0 = Valid, 1 = Invalid
int16_t balancing_status = 0; //4 = not active
uint8_t contactors_closed = 0; //TODO E5 BF or E5 51
uint8_t contactor_status_precharge = 0; //TODO E5 BF
uint8_t contactor_status_negative = 0; //TODO E5 BF
uint8_t contactor_status_positive = 0; //TODO E5 BF
uint8_t uds_fast_req_id_counter = 0;
uint8_t uds_slow_req_id_counter = 0;
uint8_t detected_number_of_cells = 96;
const unsigned long STALE_PERIOD =
STALE_PERIOD_CONFIG; // Time in milliseconds to check for staleness (e.g., 5000 ms = 5 seconds)
byte iX_0C0_counter = 0xF0; // Initialize to 0xF0
//End iX Intermediate vars
uint8_t current_cell_polled = 0;
};
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef BOLT_AMPERA_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
@ -39,170 +40,7 @@ TODOs left for this implementation
0x460 Energy Storage System Temp HV (Who sends this? Battery?)
*/
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis20ms = 0; // will store last time a 20ms CAN Message was send
static unsigned long previousMillis100ms = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis120ms = 0; // will store last time a 120ms CAN Message was send
CAN_frame BOLT_778 = {.FD = false, // Unsure of what this message is, added only as example
.ext_ID = false,
.DLC = 7,
.ID = 0x778,
.data = {0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame BOLT_POLL_7E4 = {.FD = false, // VICM_HV poll
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame BOLT_ACK_7E4 = {.FD = false, //VICM_HV ack
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame BOLT_POLL_7E7 = {.FD = false, //VITM_HV poll
.ext_ID = false,
.DLC = 8,
.ID = 0x7E7,
.data = {0x03, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame BOLT_ACK_7E7 = {.FD = false, //VITM_HV ack
.ext_ID = false,
.DLC = 8,
.ID = 0x7E7,
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
// Other PID requests in the vehicle
// All HV ECUs - 0x101
// HPCC HV - 0x243 replies on 0x643
// OBCM HV - 0x244 replies on 0x644
// VICM_HV - 0x7E4 replies 0x7EC (This is battery)
// VICM2_HV - 0x7E6 replies 0x7EF (Tis is battery also)
// VITM_HV - 0x7E7 replies on 7EF (This is battery)
static uint16_t battery_cell_voltages[96]; //array with all the cellvoltages
static uint16_t battery_capacity_my17_18 = 0;
static uint16_t battery_capacity_my19plus = 0;
static uint16_t battery_SOC_display = 0;
static uint16_t battery_SOC_raw_highprec = 0;
static uint16_t battery_max_temperature = 0;
static uint16_t battery_min_temperature = 0;
static uint16_t battery_min_cell_voltage = 0;
static uint16_t battery_max_cell_voltage = 0;
static uint16_t battery_internal_resistance = 0;
static uint16_t battery_lowest_cell = 0;
static uint16_t battery_highest_cell = 0;
static uint16_t battery_voltage_polled = 0;
static uint16_t battery_voltage_periodic = 0;
static uint16_t battery_vehicle_isolation = 0;
static uint16_t battery_isolation_kohm = 0;
static uint16_t battery_HV_locked = 0;
static uint16_t battery_crash_event = 0;
static uint16_t battery_HVIL = 0;
static uint16_t battery_HVIL_status = 0;
static uint16_t battery_5V_ref = 0;
static int16_t battery_current_7E4 = 0;
static int16_t battery_module_temp_1 = 0;
static int16_t battery_module_temp_2 = 0;
static int16_t battery_module_temp_3 = 0;
static int16_t battery_module_temp_4 = 0;
static int16_t battery_module_temp_5 = 0;
static int16_t battery_module_temp_6 = 0;
static uint16_t battery_cell_average_voltage = 0;
static uint16_t battery_cell_average_voltage_2 = 0;
static uint16_t battery_terminal_voltage = 0;
static uint16_t battery_ignition_power_mode = 0;
static int16_t battery_current_7E7 = 0;
static int16_t temperature_1 = 0;
static int16_t temperature_2 = 0;
static int16_t temperature_3 = 0;
static int16_t temperature_4 = 0;
static int16_t temperature_5 = 0;
static int16_t temperature_6 = 0;
static int16_t temperature_highest = 0;
static int16_t temperature_lowest = 0;
static uint8_t mux = 0;
static uint8_t poll_index_7E4 = 0;
static uint16_t currentpoll_7E4 = POLL_7E4_CAPACITY_EST_GEN1;
static uint16_t reply_poll_7E4 = 0;
static uint8_t poll_index_7E7 = 0;
static uint16_t currentpoll_7E7 = POLL_7E7_CURRENT;
static uint16_t reply_poll_7E7 = 0;
const uint16_t poll_commands_7E4[19] = {POLL_7E4_CAPACITY_EST_GEN1,
POLL_7E4_CAPACITY_EST_GEN2,
POLL_7E4_SOC_DISPLAY,
POLL_7E4_SOC_RAW_HIGHPREC,
POLL_7E4_MAX_TEMPERATURE,
POLL_7E4_MIN_TEMPERATURE,
POLL_7E4_MIN_CELL_V,
POLL_7E4_MAX_CELL_V,
POLL_7E4_INTERNAL_RES,
POLL_7E4_LOWEST_CELL_NUMBER,
POLL_7E4_HIGHEST_CELL_NUMBER,
POLL_7E4_VOLTAGE,
POLL_7E4_VEHICLE_ISOLATION,
POLL_7E4_ISOLATION_TEST_KOHM,
POLL_7E4_HV_LOCKED_OUT,
POLL_7E4_CRASH_EVENT,
POLL_7E4_HVIL,
POLL_7E4_HVIL_STATUS,
POLL_7E4_CURRENT};
const uint16_t poll_commands_7E7[108] = {POLL_7E7_CURRENT, POLL_7E7_5V_REF,
POLL_7E7_MODULE_TEMP_1, POLL_7E7_MODULE_TEMP_2,
POLL_7E7_MODULE_TEMP_3, POLL_7E7_MODULE_TEMP_4,
POLL_7E7_MODULE_TEMP_5, POLL_7E7_MODULE_TEMP_6,
POLL_7E7_CELL_AVG_VOLTAGE, POLL_7E7_CELL_AVG_VOLTAGE_2,
POLL_7E7_TERMINAL_VOLTAGE, POLL_7E7_IGNITION_POWER_MODE,
POLL_7E7_CELL_01, POLL_7E7_CELL_02,
POLL_7E7_CELL_03, POLL_7E7_CELL_04,
POLL_7E7_CELL_05, POLL_7E7_CELL_06,
POLL_7E7_CELL_07, POLL_7E7_CELL_08,
POLL_7E7_CELL_09, POLL_7E7_CELL_10,
POLL_7E7_CELL_11, POLL_7E7_CELL_12,
POLL_7E7_CELL_13, POLL_7E7_CELL_14,
POLL_7E7_CELL_15, POLL_7E7_CELL_16,
POLL_7E7_CELL_17, POLL_7E7_CELL_18,
POLL_7E7_CELL_19, POLL_7E7_CELL_20,
POLL_7E7_CELL_21, POLL_7E7_CELL_22,
POLL_7E7_CELL_23, POLL_7E7_CELL_24,
POLL_7E7_CELL_25, POLL_7E7_CELL_26,
POLL_7E7_CELL_27, POLL_7E7_CELL_28,
POLL_7E7_CELL_29, POLL_7E7_CELL_30,
POLL_7E7_CELL_31, POLL_7E7_CELL_32,
POLL_7E7_CELL_33, POLL_7E7_CELL_34,
POLL_7E7_CELL_35, POLL_7E7_CELL_36,
POLL_7E7_CELL_37, POLL_7E7_CELL_38,
POLL_7E7_CELL_39, POLL_7E7_CELL_40,
POLL_7E7_CELL_41, POLL_7E7_CELL_42,
POLL_7E7_CELL_43, POLL_7E7_CELL_44,
POLL_7E7_CELL_45, POLL_7E7_CELL_46,
POLL_7E7_CELL_47, POLL_7E7_CELL_48,
POLL_7E7_CELL_49, POLL_7E7_CELL_50,
POLL_7E7_CELL_51, POLL_7E7_CELL_52,
POLL_7E7_CELL_53, POLL_7E7_CELL_54,
POLL_7E7_CELL_55, POLL_7E7_CELL_56,
POLL_7E7_CELL_57, POLL_7E7_CELL_58,
POLL_7E7_CELL_59, POLL_7E7_CELL_60,
POLL_7E7_CELL_61, POLL_7E7_CELL_62,
POLL_7E7_CELL_63, POLL_7E7_CELL_64,
POLL_7E7_CELL_65, POLL_7E7_CELL_66,
POLL_7E7_CELL_67, POLL_7E7_CELL_68,
POLL_7E7_CELL_69, POLL_7E7_CELL_70,
POLL_7E7_CELL_71, POLL_7E7_CELL_72,
POLL_7E7_CELL_73, POLL_7E7_CELL_74,
POLL_7E7_CELL_75, POLL_7E7_CELL_76,
POLL_7E7_CELL_77, POLL_7E7_CELL_78,
POLL_7E7_CELL_79, POLL_7E7_CELL_80,
POLL_7E7_CELL_81, POLL_7E7_CELL_82,
POLL_7E7_CELL_83, POLL_7E7_CELL_84,
POLL_7E7_CELL_85, POLL_7E7_CELL_86,
POLL_7E7_CELL_87, POLL_7E7_CELL_88,
POLL_7E7_CELL_89, POLL_7E7_CELL_90,
POLL_7E7_CELL_91, POLL_7E7_CELL_92,
POLL_7E7_CELL_93, POLL_7E7_CELL_94,
POLL_7E7_CELL_95, POLL_7E7_CELL_96};
void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
void BoltAmperaBattery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer
datalayer.battery.status.real_soc = battery_SOC_display;
@ -279,7 +117,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer_extended.boltampera.battery_current_7E4 = battery_current_7E4;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void BoltAmperaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) {
case 0x200: //High voltage Battery Cell Voltage Matrix 1
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
@ -769,7 +607,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void BoltAmperaBattery::transmit_can(unsigned long currentMillis) {
//Send 20ms message
if (currentMillis - previousMillis20ms >= INTERVAL_20_MS) {
@ -806,7 +644,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void BoltAmperaBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Chevrolet Bolt EV/Opel Ampera-e", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 96;

View file

@ -3,180 +3,349 @@
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS BoltAmperaBattery
#define MAX_PACK_VOLTAGE_DV 4150 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 2500
#define MAX_CELL_DEVIATION_MV 150
#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
class BoltAmperaBattery : 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);
#define POLL_7E4_CAPACITY_EST_GEN1 0x41A3
#define POLL_7E4_CAPACITY_EST_GEN2 0x45F9
#define POLL_7E4_SOC_DISPLAY 0x8334
#define POLL_7E4_SOC_RAW_HIGHPREC 0x43AF
#define POLL_7E4_MAX_TEMPERATURE 0x4349
#define POLL_7E4_MIN_TEMPERATURE 0x434A
#define POLL_7E4_MIN_CELL_V 0x4329
#define POLL_7E4_MAX_CELL_V 0x432B
#define POLL_7E4_INTERNAL_RES 0x40E9
#define POLL_7E4_LOWEST_CELL_NUMBER 0x433B
#define POLL_7E4_HIGHEST_CELL_NUMBER 0x433C
#define POLL_7E4_VOLTAGE 0x432D
#define POLL_7E4_VEHICLE_ISOLATION 0x41EC
#define POLL_7E4_ISOLATION_TEST_KOHM 0x43A6
#define POLL_7E4_HV_LOCKED_OUT 0x44F8
#define POLL_7E4_CRASH_EVENT 0x4522
#define POLL_7E4_HVIL 0x4310
#define POLL_7E4_HVIL_STATUS 0x4311
#define POLL_7E4_CURRENT 0x4356
private:
static const int MAX_PACK_VOLTAGE_DV = 4150; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 2500;
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 = 2700; //Battery is put into emergency stop if one cell goes below this value
static const int POLL_7E4_CAPACITY_EST_GEN1 = 0x41A3;
static const int POLL_7E4_CAPACITY_EST_GEN2 = 0x45F9;
static const int POLL_7E4_SOC_DISPLAY = 0x8334;
static const int POLL_7E4_SOC_RAW_HIGHPREC = 0x43AF;
static const int POLL_7E4_MAX_TEMPERATURE = 0x4349;
static const int POLL_7E4_MIN_TEMPERATURE = 0x434A;
static const int POLL_7E4_MIN_CELL_V = 0x4329;
static const int POLL_7E4_MAX_CELL_V = 0x432B;
static const int POLL_7E4_INTERNAL_RES = 0x40E9;
static const int POLL_7E4_LOWEST_CELL_NUMBER = 0x433B;
static const int POLL_7E4_HIGHEST_CELL_NUMBER = 0x433C;
static const int POLL_7E4_VOLTAGE = 0x432D;
static const int POLL_7E4_VEHICLE_ISOLATION = 0x41EC;
static const int POLL_7E4_ISOLATION_TEST_KOHM = 0x43A6;
static const int POLL_7E4_HV_LOCKED_OUT = 0x44F8;
static const int POLL_7E4_CRASH_EVENT = 0x4522;
static const int POLL_7E4_HVIL = 0x4310;
static const int POLL_7E4_HVIL_STATUS = 0x4311;
static const int POLL_7E4_CURRENT = 0x4356;
static const int POLL_7E7_CURRENT = 0x40D4;
static const int POLL_7E7_5V_REF = 0x40D3;
static const int POLL_7E7_MODULE_TEMP_1 = 0x40D7;
static const int POLL_7E7_MODULE_TEMP_2 = 0x40D9;
static const int POLL_7E7_MODULE_TEMP_3 = 0x40DB;
static const int POLL_7E7_MODULE_TEMP_4 = 0x40DD;
static const int POLL_7E7_MODULE_TEMP_5 = 0x40DF;
static const int POLL_7E7_MODULE_TEMP_6 = 0x40E1;
static const int POLL_7E7_CELL_AVG_VOLTAGE = 0xC218;
static const int POLL_7E7_CELL_AVG_VOLTAGE_2 = 0x44B9;
static const int POLL_7E7_TERMINAL_VOLTAGE = 0x82A3;
static const int POLL_7E7_IGNITION_POWER_MODE = 0x8002;
static const int POLL_7E7_GMLAN_HIGH_SPEED_STATUS = 0x8043;
static const int POLL_7E7_HV_ISOLATION_RESISTANCE = 0x43A6;
static const int POLL_7E7_HV_BUS_VOLTAGE = 0x438B;
static const int POLL_7E7_HYBRID_CELL_BALANCING_ID_1 = 0x4323;
static const int POLL_7E7_HYBRID_CELL_BALANCING_ID_2 = 0x4324;
static const int POLL_7E7_HYBRID_CELL_BALANCING_ID_3 = 0x4325;
static const int POLL_7E7_HYBRID_CELL_BALANCING_ID_4 = 0x4326;
static const int POLL_7E7_HYBRID_CELL_BALANCING_ID_5 = 0x4327;
static const int POLL_7E7_HYBRID_CELL_BALANCING_ID_6 = 0x4340;
static const int POLL_7E7_HYBRID_BATTERY_CELL_BALANCE_STATUS = 0x431C;
static const int POLL_7E7_5V_REF_VOLTAGE_1 = 0x428F;
static const int POLL_7E7_5V_REF_VOLTAGE_2 = 0x4290;
static const int POLL_7E7_CELL_01 = 0x4181;
static const int POLL_7E7_CELL_02 = 0x4182;
static const int POLL_7E7_CELL_03 = 0x4183;
static const int POLL_7E7_CELL_04 = 0x4184;
static const int POLL_7E7_CELL_05 = 0x4185;
static const int POLL_7E7_CELL_06 = 0x4186;
static const int POLL_7E7_CELL_07 = 0x4187;
static const int POLL_7E7_CELL_08 = 0x4188;
static const int POLL_7E7_CELL_09 = 0x4189;
static const int POLL_7E7_CELL_10 = 0x418A;
static const int POLL_7E7_CELL_11 = 0x418B;
static const int POLL_7E7_CELL_12 = 0x418C;
static const int POLL_7E7_CELL_13 = 0x418D;
static const int POLL_7E7_CELL_14 = 0x418E;
static const int POLL_7E7_CELL_15 = 0x418F;
static const int POLL_7E7_CELL_16 = 0x4190;
static const int POLL_7E7_CELL_17 = 0x4191;
static const int POLL_7E7_CELL_18 = 0x4192;
static const int POLL_7E7_CELL_19 = 0x4193;
static const int POLL_7E7_CELL_20 = 0x4194;
static const int POLL_7E7_CELL_21 = 0x4195;
static const int POLL_7E7_CELL_22 = 0x4196;
static const int POLL_7E7_CELL_23 = 0x4197;
static const int POLL_7E7_CELL_24 = 0x4198;
static const int POLL_7E7_CELL_25 = 0x4199;
static const int POLL_7E7_CELL_26 = 0x419A;
static const int POLL_7E7_CELL_27 = 0x419B;
static const int POLL_7E7_CELL_28 = 0x419C;
static const int POLL_7E7_CELL_29 = 0x419D;
static const int POLL_7E7_CELL_30 = 0x419E;
static const int POLL_7E7_CELL_31 = 0x419F;
static const int POLL_7E7_CELL_32 = 0x4200;
static const int POLL_7E7_CELL_33 = 0x4201;
static const int POLL_7E7_CELL_34 = 0x4202;
static const int POLL_7E7_CELL_35 = 0x4203;
static const int POLL_7E7_CELL_36 = 0x4204;
static const int POLL_7E7_CELL_37 = 0x4205;
static const int POLL_7E7_CELL_38 = 0x4206;
static const int POLL_7E7_CELL_39 = 0x4207;
static const int POLL_7E7_CELL_40 = 0x4208;
static const int POLL_7E7_CELL_41 = 0x4209;
static const int POLL_7E7_CELL_42 = 0x420A;
static const int POLL_7E7_CELL_43 = 0x420B;
static const int POLL_7E7_CELL_44 = 0x420C;
static const int POLL_7E7_CELL_45 = 0x420D;
static const int POLL_7E7_CELL_46 = 0x420E;
static const int POLL_7E7_CELL_47 = 0x420F;
static const int POLL_7E7_CELL_48 = 0x4210;
static const int POLL_7E7_CELL_49 = 0x4211;
static const int POLL_7E7_CELL_50 = 0x4212;
static const int POLL_7E7_CELL_51 = 0x4213;
static const int POLL_7E7_CELL_52 = 0x4214;
static const int POLL_7E7_CELL_53 = 0x4215;
static const int POLL_7E7_CELL_54 = 0x4216;
static const int POLL_7E7_CELL_55 = 0x4217;
static const int POLL_7E7_CELL_56 = 0x4218;
static const int POLL_7E7_CELL_57 = 0x4219;
static const int POLL_7E7_CELL_58 = 0x421A;
static const int POLL_7E7_CELL_59 = 0x421B;
static const int POLL_7E7_CELL_60 = 0x421C;
static const int POLL_7E7_CELL_61 = 0x421D;
static const int POLL_7E7_CELL_62 = 0x421E;
static const int POLL_7E7_CELL_63 = 0x421F;
static const int POLL_7E7_CELL_64 = 0x4220;
static const int POLL_7E7_CELL_65 = 0x4221;
static const int POLL_7E7_CELL_66 = 0x4222;
static const int POLL_7E7_CELL_67 = 0x4223;
static const int POLL_7E7_CELL_68 = 0x4224;
static const int POLL_7E7_CELL_69 = 0x4225;
static const int POLL_7E7_CELL_70 = 0x4226;
static const int POLL_7E7_CELL_71 = 0x4227;
static const int POLL_7E7_CELL_72 = 0x4228;
static const int POLL_7E7_CELL_73 = 0x4229;
static const int POLL_7E7_CELL_74 = 0x422A;
static const int POLL_7E7_CELL_75 = 0x422B;
static const int POLL_7E7_CELL_76 = 0x422C;
static const int POLL_7E7_CELL_77 = 0x422D;
static const int POLL_7E7_CELL_78 = 0x422E;
static const int POLL_7E7_CELL_79 = 0x422F;
static const int POLL_7E7_CELL_80 = 0x4230;
static const int POLL_7E7_CELL_81 = 0x4231;
static const int POLL_7E7_CELL_82 = 0x4232;
static const int POLL_7E7_CELL_83 = 0x4233;
static const int POLL_7E7_CELL_84 = 0x4234;
static const int POLL_7E7_CELL_85 = 0x4235;
static const int POLL_7E7_CELL_86 = 0x4236;
static const int POLL_7E7_CELL_87 = 0x4237;
static const int POLL_7E7_CELL_88 = 0x4238;
static const int POLL_7E7_CELL_89 = 0x4239;
static const int POLL_7E7_CELL_90 = 0x423A;
static const int POLL_7E7_CELL_91 = 0x423B;
static const int POLL_7E7_CELL_92 = 0x423C;
static const int POLL_7E7_CELL_93 = 0x423D;
static const int POLL_7E7_CELL_94 = 0x423E;
static const int POLL_7E7_CELL_95 = 0x423F;
static const int POLL_7E7_CELL_96 = 0x4240;
static const int POLL_7E7_CELL_97 = 0x4241; // Normal pack ends at 96, these cells might be unpopulated
static const int POLL_7E7_CELL_98 = 0x4242;
static const int POLL_7E7_CELL_99 = 0x4243;
static const int POLL_7E7_CELL_100 = 0x4244;
static const int POLL_7E7_CELL_101 = 0x4245;
static const int POLL_7E7_CELL_102 = 0x4246;
static const int POLL_7E7_CELL_103 = 0x4247;
static const int POLL_7E7_CELL_104 = 0x4248;
static const int POLL_7E7_CELL_105 = 0x4249;
static const int POLL_7E7_CELL_106 = 0x424A;
static const int POLL_7E7_CELL_107 = 0x424B;
static const int POLL_7E7_CELL_108 = 0x424C;
static const int POLL_7E7_CELL_109 = 0x424D;
static const int POLL_7E7_CELL_110 = 0x424E;
static const int POLL_7E7_CELL_111 = 0x424F;
static const int POLL_7E7_CELL_112 = 0x4250;
static const int POLL_7E7_CELL_113 = 0x4251;
static const int POLL_7E7_CELL_114 = 0x4252;
static const int POLL_7E7_CELL_115 = 0x4253;
static const int POLL_7E7_CELL_116 = 0x4254;
static const int POLL_7E7_CELL_117 = 0x4255;
static const int POLL_7E7_CELL_118 = 0x4256;
static const int POLL_7E7_CELL_119 = 0x4257;
static const int POLL_7E7_CELL_120 = 0x4258;
#define POLL_7E7_CURRENT 0x40D4
#define POLL_7E7_5V_REF 0x40D3
#define POLL_7E7_MODULE_TEMP_1 0x40D7
#define POLL_7E7_MODULE_TEMP_2 0x40D9
#define POLL_7E7_MODULE_TEMP_3 0x40DB
#define POLL_7E7_MODULE_TEMP_4 0x40DD
#define POLL_7E7_MODULE_TEMP_5 0x40DF
#define POLL_7E7_MODULE_TEMP_6 0x40E1
#define POLL_7E7_CELL_AVG_VOLTAGE 0xC218
#define POLL_7E7_CELL_AVG_VOLTAGE_2 0x44B9
#define POLL_7E7_TERMINAL_VOLTAGE 0x82A3
#define POLL_7E7_IGNITION_POWER_MODE 0x8002
#define POLL_7E7_GMLAN_HIGH_SPEED_STATUS 0x8043
#define POLL_7E7_HV_ISOLATION_RESISTANCE 0x43A6
#define POLL_7E7_HV_BUS_VOLTAGE 0x438B
#define POLL_7E7_HYBRID_CELL_BALANCING_ID_1 0x4323
#define POLL_7E7_HYBRID_CELL_BALANCING_ID_2 0x4324
#define POLL_7E7_HYBRID_CELL_BALANCING_ID_3 0x4325
#define POLL_7E7_HYBRID_CELL_BALANCING_ID_4 0x4326
#define POLL_7E7_HYBRID_CELL_BALANCING_ID_5 0x4327
#define POLL_7E7_HYBRID_CELL_BALANCING_ID_6 0x4340
#define POLL_7E7_HYBRID_BATTERY_CELL_BALANCE_STATUS 0x431C
#define POLL_7E7_5V_REF_VOLTAGE_1 0x428F
#define POLL_7E7_5V_REF_VOLTAGE_2 0x4290
#define POLL_7E7_CELL_01 0x4181
#define POLL_7E7_CELL_02 0x4182
#define POLL_7E7_CELL_03 0x4183
#define POLL_7E7_CELL_04 0x4184
#define POLL_7E7_CELL_05 0x4185
#define POLL_7E7_CELL_06 0x4186
#define POLL_7E7_CELL_07 0x4187
#define POLL_7E7_CELL_08 0x4188
#define POLL_7E7_CELL_09 0x4189
#define POLL_7E7_CELL_10 0x418A
#define POLL_7E7_CELL_11 0x418B
#define POLL_7E7_CELL_12 0x418C
#define POLL_7E7_CELL_13 0x418D
#define POLL_7E7_CELL_14 0x418E
#define POLL_7E7_CELL_15 0x418F
#define POLL_7E7_CELL_16 0x4190
#define POLL_7E7_CELL_17 0x4191
#define POLL_7E7_CELL_18 0x4192
#define POLL_7E7_CELL_19 0x4193
#define POLL_7E7_CELL_20 0x4194
#define POLL_7E7_CELL_21 0x4195
#define POLL_7E7_CELL_22 0x4196
#define POLL_7E7_CELL_23 0x4197
#define POLL_7E7_CELL_24 0x4198
#define POLL_7E7_CELL_25 0x4199
#define POLL_7E7_CELL_26 0x419A
#define POLL_7E7_CELL_27 0x419B
#define POLL_7E7_CELL_28 0x419C
#define POLL_7E7_CELL_29 0x419D
#define POLL_7E7_CELL_30 0x419E
#define POLL_7E7_CELL_31 0x419F
#define POLL_7E7_CELL_32 0x4200
#define POLL_7E7_CELL_33 0x4201
#define POLL_7E7_CELL_34 0x4202
#define POLL_7E7_CELL_35 0x4203
#define POLL_7E7_CELL_36 0x4204
#define POLL_7E7_CELL_37 0x4205
#define POLL_7E7_CELL_38 0x4206
#define POLL_7E7_CELL_39 0x4207
#define POLL_7E7_CELL_40 0x4208
#define POLL_7E7_CELL_41 0x4209
#define POLL_7E7_CELL_42 0x420A
#define POLL_7E7_CELL_43 0x420B
#define POLL_7E7_CELL_44 0x420C
#define POLL_7E7_CELL_45 0x420D
#define POLL_7E7_CELL_46 0x420E
#define POLL_7E7_CELL_47 0x420F
#define POLL_7E7_CELL_48 0x4210
#define POLL_7E7_CELL_49 0x4211
#define POLL_7E7_CELL_50 0x4212
#define POLL_7E7_CELL_51 0x4213
#define POLL_7E7_CELL_52 0x4214
#define POLL_7E7_CELL_53 0x4215
#define POLL_7E7_CELL_54 0x4216
#define POLL_7E7_CELL_55 0x4217
#define POLL_7E7_CELL_56 0x4218
#define POLL_7E7_CELL_57 0x4219
#define POLL_7E7_CELL_58 0x421A
#define POLL_7E7_CELL_59 0x421B
#define POLL_7E7_CELL_60 0x421C
#define POLL_7E7_CELL_61 0x421D
#define POLL_7E7_CELL_62 0x421E
#define POLL_7E7_CELL_63 0x421F
#define POLL_7E7_CELL_64 0x4220
#define POLL_7E7_CELL_65 0x4221
#define POLL_7E7_CELL_66 0x4222
#define POLL_7E7_CELL_67 0x4223
#define POLL_7E7_CELL_68 0x4224
#define POLL_7E7_CELL_69 0x4225
#define POLL_7E7_CELL_70 0x4226
#define POLL_7E7_CELL_71 0x4227
#define POLL_7E7_CELL_72 0x4228
#define POLL_7E7_CELL_73 0x4229
#define POLL_7E7_CELL_74 0x422A
#define POLL_7E7_CELL_75 0x422B
#define POLL_7E7_CELL_76 0x422C
#define POLL_7E7_CELL_77 0x422D
#define POLL_7E7_CELL_78 0x422E
#define POLL_7E7_CELL_79 0x422F
#define POLL_7E7_CELL_80 0x4230
#define POLL_7E7_CELL_81 0x4231
#define POLL_7E7_CELL_82 0x4232
#define POLL_7E7_CELL_83 0x4233
#define POLL_7E7_CELL_84 0x4234
#define POLL_7E7_CELL_85 0x4235
#define POLL_7E7_CELL_86 0x4236
#define POLL_7E7_CELL_87 0x4237
#define POLL_7E7_CELL_88 0x4238
#define POLL_7E7_CELL_89 0x4239
#define POLL_7E7_CELL_90 0x423A
#define POLL_7E7_CELL_91 0x423B
#define POLL_7E7_CELL_92 0x423C
#define POLL_7E7_CELL_93 0x423D
#define POLL_7E7_CELL_94 0x423E
#define POLL_7E7_CELL_95 0x423F
#define POLL_7E7_CELL_96 0x4240
#define POLL_7E7_CELL_97 0x4241 // Normal pack ends at 96, these cells might be unpopulated
#define POLL_7E7_CELL_98 0x4242
#define POLL_7E7_CELL_99 0x4243
#define POLL_7E7_CELL_100 0x4244
#define POLL_7E7_CELL_101 0x4245
#define POLL_7E7_CELL_102 0x4246
#define POLL_7E7_CELL_103 0x4247
#define POLL_7E7_CELL_104 0x4248
#define POLL_7E7_CELL_105 0x4249
#define POLL_7E7_CELL_106 0x424A
#define POLL_7E7_CELL_107 0x424B
#define POLL_7E7_CELL_108 0x424C
#define POLL_7E7_CELL_109 0x424D
#define POLL_7E7_CELL_110 0x424E
#define POLL_7E7_CELL_111 0x424F
#define POLL_7E7_CELL_112 0x4250
#define POLL_7E7_CELL_113 0x4251
#define POLL_7E7_CELL_114 0x4252
#define POLL_7E7_CELL_115 0x4253
#define POLL_7E7_CELL_116 0x4254
#define POLL_7E7_CELL_117 0x4255
#define POLL_7E7_CELL_118 0x4256
#define POLL_7E7_CELL_119 0x4257
#define POLL_7E7_CELL_120 0x4258
unsigned long previousMillis20ms = 0; // will store last time a 20ms CAN Message was send
unsigned long previousMillis100ms = 0; // will store last time a 100ms CAN Message was send
unsigned long previousMillis120ms = 0; // will store last time a 120ms CAN Message was send
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
CAN_frame BOLT_778 = {.FD = false, // Unsure of what this message is, added only as example
.ext_ID = false,
.DLC = 7,
.ID = 0x778,
.data = {0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame BOLT_POLL_7E4 = {.FD = false, // VICM_HV poll
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x03, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame BOLT_ACK_7E4 = {.FD = false, //VICM_HV ack
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame BOLT_POLL_7E7 = {.FD = false, //VITM_HV poll
.ext_ID = false,
.DLC = 8,
.ID = 0x7E7,
.data = {0x03, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame BOLT_ACK_7E7 = {.FD = false, //VITM_HV ack
.ext_ID = false,
.DLC = 8,
.ID = 0x7E7,
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
// Other PID requests in the vehicle
// All HV ECUs - 0x101
// HPCC HV - 0x243 replies on 0x643
// OBCM HV - 0x244 replies on 0x644
// VICM_HV - 0x7E4 replies 0x7EC (This is battery)
// VICM2_HV - 0x7E6 replies 0x7EF (Tis is battery also)
// VITM_HV - 0x7E7 replies on 7EF (This is battery)
uint16_t battery_cell_voltages[96]; //array with all the cellvoltages
uint16_t battery_capacity_my17_18 = 0;
uint16_t battery_capacity_my19plus = 0;
uint16_t battery_SOC_display = 0;
uint16_t battery_SOC_raw_highprec = 0;
uint16_t battery_max_temperature = 0;
uint16_t battery_min_temperature = 0;
uint16_t battery_min_cell_voltage = 0;
uint16_t battery_max_cell_voltage = 0;
uint16_t battery_internal_resistance = 0;
uint16_t battery_lowest_cell = 0;
uint16_t battery_highest_cell = 0;
uint16_t battery_voltage_polled = 0;
uint16_t battery_voltage_periodic = 0;
uint16_t battery_vehicle_isolation = 0;
uint16_t battery_isolation_kohm = 0;
uint16_t battery_HV_locked = 0;
uint16_t battery_crash_event = 0;
uint16_t battery_HVIL = 0;
uint16_t battery_HVIL_status = 0;
uint16_t battery_5V_ref = 0;
int16_t battery_current_7E4 = 0;
int16_t battery_module_temp_1 = 0;
int16_t battery_module_temp_2 = 0;
int16_t battery_module_temp_3 = 0;
int16_t battery_module_temp_4 = 0;
int16_t battery_module_temp_5 = 0;
int16_t battery_module_temp_6 = 0;
uint16_t battery_cell_average_voltage = 0;
uint16_t battery_cell_average_voltage_2 = 0;
uint16_t battery_terminal_voltage = 0;
uint16_t battery_ignition_power_mode = 0;
int16_t battery_current_7E7 = 0;
int16_t temperature_1 = 0;
int16_t temperature_2 = 0;
int16_t temperature_3 = 0;
int16_t temperature_4 = 0;
int16_t temperature_5 = 0;
int16_t temperature_6 = 0;
int16_t temperature_highest = 0;
int16_t temperature_lowest = 0;
uint8_t mux = 0;
uint8_t poll_index_7E4 = 0;
uint16_t currentpoll_7E4 = POLL_7E4_CAPACITY_EST_GEN1;
uint16_t reply_poll_7E4 = 0;
uint8_t poll_index_7E7 = 0;
uint16_t currentpoll_7E7 = POLL_7E7_CURRENT;
uint16_t reply_poll_7E7 = 0;
const uint16_t poll_commands_7E4[19] = {POLL_7E4_CAPACITY_EST_GEN1,
POLL_7E4_CAPACITY_EST_GEN2,
POLL_7E4_SOC_DISPLAY,
POLL_7E4_SOC_RAW_HIGHPREC,
POLL_7E4_MAX_TEMPERATURE,
POLL_7E4_MIN_TEMPERATURE,
POLL_7E4_MIN_CELL_V,
POLL_7E4_MAX_CELL_V,
POLL_7E4_INTERNAL_RES,
POLL_7E4_LOWEST_CELL_NUMBER,
POLL_7E4_HIGHEST_CELL_NUMBER,
POLL_7E4_VOLTAGE,
POLL_7E4_VEHICLE_ISOLATION,
POLL_7E4_ISOLATION_TEST_KOHM,
POLL_7E4_HV_LOCKED_OUT,
POLL_7E4_CRASH_EVENT,
POLL_7E4_HVIL,
POLL_7E4_HVIL_STATUS,
POLL_7E4_CURRENT};
const uint16_t poll_commands_7E7[108] = {POLL_7E7_CURRENT, POLL_7E7_5V_REF,
POLL_7E7_MODULE_TEMP_1, POLL_7E7_MODULE_TEMP_2,
POLL_7E7_MODULE_TEMP_3, POLL_7E7_MODULE_TEMP_4,
POLL_7E7_MODULE_TEMP_5, POLL_7E7_MODULE_TEMP_6,
POLL_7E7_CELL_AVG_VOLTAGE, POLL_7E7_CELL_AVG_VOLTAGE_2,
POLL_7E7_TERMINAL_VOLTAGE, POLL_7E7_IGNITION_POWER_MODE,
POLL_7E7_CELL_01, POLL_7E7_CELL_02,
POLL_7E7_CELL_03, POLL_7E7_CELL_04,
POLL_7E7_CELL_05, POLL_7E7_CELL_06,
POLL_7E7_CELL_07, POLL_7E7_CELL_08,
POLL_7E7_CELL_09, POLL_7E7_CELL_10,
POLL_7E7_CELL_11, POLL_7E7_CELL_12,
POLL_7E7_CELL_13, POLL_7E7_CELL_14,
POLL_7E7_CELL_15, POLL_7E7_CELL_16,
POLL_7E7_CELL_17, POLL_7E7_CELL_18,
POLL_7E7_CELL_19, POLL_7E7_CELL_20,
POLL_7E7_CELL_21, POLL_7E7_CELL_22,
POLL_7E7_CELL_23, POLL_7E7_CELL_24,
POLL_7E7_CELL_25, POLL_7E7_CELL_26,
POLL_7E7_CELL_27, POLL_7E7_CELL_28,
POLL_7E7_CELL_29, POLL_7E7_CELL_30,
POLL_7E7_CELL_31, POLL_7E7_CELL_32,
POLL_7E7_CELL_33, POLL_7E7_CELL_34,
POLL_7E7_CELL_35, POLL_7E7_CELL_36,
POLL_7E7_CELL_37, POLL_7E7_CELL_38,
POLL_7E7_CELL_39, POLL_7E7_CELL_40,
POLL_7E7_CELL_41, POLL_7E7_CELL_42,
POLL_7E7_CELL_43, POLL_7E7_CELL_44,
POLL_7E7_CELL_45, POLL_7E7_CELL_46,
POLL_7E7_CELL_47, POLL_7E7_CELL_48,
POLL_7E7_CELL_49, POLL_7E7_CELL_50,
POLL_7E7_CELL_51, POLL_7E7_CELL_52,
POLL_7E7_CELL_53, POLL_7E7_CELL_54,
POLL_7E7_CELL_55, POLL_7E7_CELL_56,
POLL_7E7_CELL_57, POLL_7E7_CELL_58,
POLL_7E7_CELL_59, POLL_7E7_CELL_60,
POLL_7E7_CELL_61, POLL_7E7_CELL_62,
POLL_7E7_CELL_63, POLL_7E7_CELL_64,
POLL_7E7_CELL_65, POLL_7E7_CELL_66,
POLL_7E7_CELL_67, POLL_7E7_CELL_68,
POLL_7E7_CELL_69, POLL_7E7_CELL_70,
POLL_7E7_CELL_71, POLL_7E7_CELL_72,
POLL_7E7_CELL_73, POLL_7E7_CELL_74,
POLL_7E7_CELL_75, POLL_7E7_CELL_76,
POLL_7E7_CELL_77, POLL_7E7_CELL_78,
POLL_7E7_CELL_79, POLL_7E7_CELL_80,
POLL_7E7_CELL_81, POLL_7E7_CELL_82,
POLL_7E7_CELL_83, POLL_7E7_CELL_84,
POLL_7E7_CELL_85, POLL_7E7_CELL_86,
POLL_7E7_CELL_87, POLL_7E7_CELL_88,
POLL_7E7_CELL_89, POLL_7E7_CELL_90,
POLL_7E7_CELL_91, POLL_7E7_CELL_92,
POLL_7E7_CELL_93, POLL_7E7_CELL_94,
POLL_7E7_CELL_95, POLL_7E7_CELL_96};
};
#endif

View file

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

View file

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

View file

@ -1,115 +1,12 @@
#include "../include.h"
#ifdef CELLPOWER_BMS
#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 "CELLPOWER-BMS.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was sent
//Actual content messages
// Optional add-on charger module. Might not be needed to send these towards the BMS to keep it happy.
CAN_frame CELLPOWER_18FF50E9 = {.FD = false,
.ext_ID = true,
.DLC = 5,
.ID = 0x18FF50E9,
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame CELLPOWER_18FF50E8 = {.FD = false,
.ext_ID = true,
.DLC = 5,
.ID = 0x18FF50E8,
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame CELLPOWER_18FF50E7 = {.FD = false,
.ext_ID = true,
.DLC = 5,
.ID = 0x18FF50E7,
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame CELLPOWER_18FF50E5 = {.FD = false,
.ext_ID = true,
.DLC = 5,
.ID = 0x18FF50E5,
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
static bool system_state_discharge = false;
static bool system_state_charge = false;
static bool system_state_cellbalancing = false;
static bool system_state_tricklecharge = false;
static bool system_state_idle = false;
static bool system_state_chargecompleted = false;
static bool system_state_maintenancecharge = false;
static bool IO_state_main_positive_relay = false;
static bool IO_state_main_negative_relay = false;
static bool IO_state_charge_enable = false;
static bool IO_state_precharge_relay = false;
static bool IO_state_discharge_enable = false;
static bool IO_state_IO_6 = false;
static bool IO_state_IO_7 = false;
static bool IO_state_IO_8 = false;
static bool error_Cell_overvoltage = false;
static bool error_Cell_undervoltage = false;
static bool error_Cell_end_of_life_voltage = false;
static bool error_Cell_voltage_misread = false;
static bool error_Cell_over_temperature = false;
static bool error_Cell_under_temperature = false;
static bool error_Cell_unmanaged = false;
static bool error_LMU_over_temperature = false;
static bool error_LMU_under_temperature = false;
static bool error_Temp_sensor_open_circuit = false;
static bool error_Temp_sensor_short_circuit = false;
static bool error_SUB_communication = false;
static bool error_LMU_communication = false;
static bool error_Over_current_IN = false;
static bool error_Over_current_OUT = false;
static bool error_Short_circuit = false;
static bool error_Leak_detected = false;
static bool error_Leak_detection_failed = false;
static bool error_Voltage_difference = false;
static bool error_BMCU_supply_over_voltage = false;
static bool error_BMCU_supply_under_voltage = false;
static bool error_Main_positive_contactor = false;
static bool error_Main_negative_contactor = false;
static bool error_Precharge_contactor = false;
static bool error_Midpack_contactor = false;
static bool error_Precharge_timeout = false;
static bool error_Emergency_connector_override = false;
static bool warning_High_cell_voltage = false;
static bool warning_Low_cell_voltage = false;
static bool warning_High_cell_temperature = false;
static bool warning_Low_cell_temperature = false;
static bool warning_High_LMU_temperature = false;
static bool warning_Low_LMU_temperature = false;
static bool warning_SUB_communication_interfered = false;
static bool warning_LMU_communication_interfered = false;
static bool warning_High_current_IN = false;
static bool warning_High_current_OUT = false;
static bool warning_Pack_resistance_difference = false;
static bool warning_High_pack_resistance = false;
static bool warning_Cell_resistance_difference = false;
static bool warning_High_cell_resistance = false;
static bool warning_High_BMCU_supply_voltage = false;
static bool warning_Low_BMCU_supply_voltage = false;
static bool warning_Low_SOC = false;
static bool warning_Balancing_required_OCV_model = false;
static bool warning_Charger_not_responding = false;
static uint16_t cell_voltage_max_mV = 3700;
static uint16_t cell_voltage_min_mV = 3700;
static int8_t pack_temperature_high_C = 0;
static int8_t pack_temperature_low_C = 0;
static uint16_t battery_pack_voltage_dV = 3700;
static int16_t battery_pack_current_dA = 0;
static uint8_t battery_SOH_percentage = 99;
static uint8_t battery_SOC_percentage = 50;
static uint16_t battery_remaining_dAh = 0;
static uint8_t cell_with_highest_voltage = 0;
static uint8_t cell_with_lowest_voltage = 0;
static uint16_t requested_charge_current_dA = 0;
static uint16_t average_charge_current_dA = 0;
static uint16_t actual_charge_current_dA = 0;
static bool requested_exceeding_average_current = 0;
static bool error_state = false;
void update_values_battery() {
void CellPowerBms::update_values() {
/* Update values from CAN */
@ -213,7 +110,8 @@ void update_values_battery() {
//TODO, shall we react on this?
}
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void CellPowerBms::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) {
case 0x1A4: //PDO1_TX - 200ms
@ -316,7 +214,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void CellPowerBms::transmit_can(unsigned long currentMillis) {
// Send 1s CAN Message
if (currentMillis - previousMillis1s >= INTERVAL_1_S) {
@ -331,7 +229,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void CellPowerBms::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Cellpower BMS", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;

View file

@ -2,18 +2,130 @@
#define CELLPOWER_BMS_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
/* Tweak these according to your battery build */
#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 1500
#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
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS CellPowerBms
class CellPowerBms : 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:
/* Tweak these according to your battery build */
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 1500;
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 previousMillis1s = 0; // will store last time a 1s CAN Message was sent
//Actual content messages
// Optional add-on charger module. Might not be needed to send these towards the BMS to keep it happy.
CAN_frame CELLPOWER_18FF50E9 = {.FD = false,
.ext_ID = true,
.DLC = 5,
.ID = 0x18FF50E9,
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame CELLPOWER_18FF50E8 = {.FD = false,
.ext_ID = true,
.DLC = 5,
.ID = 0x18FF50E8,
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame CELLPOWER_18FF50E7 = {.FD = false,
.ext_ID = true,
.DLC = 5,
.ID = 0x18FF50E7,
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame CELLPOWER_18FF50E5 = {.FD = false,
.ext_ID = true,
.DLC = 5,
.ID = 0x18FF50E5,
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
bool system_state_discharge = false;
bool system_state_charge = false;
bool system_state_cellbalancing = false;
bool system_state_tricklecharge = false;
bool system_state_idle = false;
bool system_state_chargecompleted = false;
bool system_state_maintenancecharge = false;
bool IO_state_main_positive_relay = false;
bool IO_state_main_negative_relay = false;
bool IO_state_charge_enable = false;
bool IO_state_precharge_relay = false;
bool IO_state_discharge_enable = false;
bool IO_state_IO_6 = false;
bool IO_state_IO_7 = false;
bool IO_state_IO_8 = false;
bool error_Cell_overvoltage = false;
bool error_Cell_undervoltage = false;
bool error_Cell_end_of_life_voltage = false;
bool error_Cell_voltage_misread = false;
bool error_Cell_over_temperature = false;
bool error_Cell_under_temperature = false;
bool error_Cell_unmanaged = false;
bool error_LMU_over_temperature = false;
bool error_LMU_under_temperature = false;
bool error_Temp_sensor_open_circuit = false;
bool error_Temp_sensor_short_circuit = false;
bool error_SUB_communication = false;
bool error_LMU_communication = false;
bool error_Over_current_IN = false;
bool error_Over_current_OUT = false;
bool error_Short_circuit = false;
bool error_Leak_detected = false;
bool error_Leak_detection_failed = false;
bool error_Voltage_difference = false;
bool error_BMCU_supply_over_voltage = false;
bool error_BMCU_supply_under_voltage = false;
bool error_Main_positive_contactor = false;
bool error_Main_negative_contactor = false;
bool error_Precharge_contactor = false;
bool error_Midpack_contactor = false;
bool error_Precharge_timeout = false;
bool error_Emergency_connector_override = false;
bool warning_High_cell_voltage = false;
bool warning_Low_cell_voltage = false;
bool warning_High_cell_temperature = false;
bool warning_Low_cell_temperature = false;
bool warning_High_LMU_temperature = false;
bool warning_Low_LMU_temperature = false;
bool warning_SUB_communication_interfered = false;
bool warning_LMU_communication_interfered = false;
bool warning_High_current_IN = false;
bool warning_High_current_OUT = false;
bool warning_Pack_resistance_difference = false;
bool warning_High_pack_resistance = false;
bool warning_Cell_resistance_difference = false;
bool warning_High_cell_resistance = false;
bool warning_High_BMCU_supply_voltage = false;
bool warning_Low_BMCU_supply_voltage = false;
bool warning_Low_SOC = false;
bool warning_Balancing_required_OCV_model = false;
bool warning_Charger_not_responding = false;
uint16_t cell_voltage_max_mV = 3700;
uint16_t cell_voltage_min_mV = 3700;
int8_t pack_temperature_high_C = 0;
int8_t pack_temperature_low_C = 0;
uint16_t battery_pack_voltage_dV = 3700;
int16_t battery_pack_current_dA = 0;
uint8_t battery_SOH_percentage = 99;
uint8_t battery_SOC_percentage = 50;
uint16_t battery_remaining_dAh = 0;
uint8_t cell_with_highest_voltage = 0;
uint8_t cell_with_lowest_voltage = 0;
uint16_t requested_charge_current_dA = 0;
uint16_t average_charge_current_dA = 0;
uint16_t actual_charge_current_dA = 0;
bool requested_exceeding_average_current = 0;
bool error_state = false;
};
/* Do not modify any rows below*/
#define BATTERY_SELECTED
#define NATIVECAN_250KBPS
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
#endif

View file

@ -1,233 +0,0 @@
#ifndef CHADEMO_BATTERY_TYPES_H
#define CHADEMO_BATTERY_TYPES_H
#define MAX_EVSE_POWER_CHARGING 3300
#define MAX_EVSE_OUTPUT_VOLTAGE 410
#define MAX_EVSE_OUTPUT_CURRENT 11
enum CHADEMO_STATE {
CHADEMO_FAULT,
CHADEMO_STOP,
CHADEMO_IDLE,
CHADEMO_CONNECTED,
CHADEMO_INIT, // intermediate state indicating CAN from Vehicle not yet received after connection
CHADEMO_NEGOTIATE,
CHADEMO_EV_ALLOWED,
CHADEMO_EVSE_PREPARE,
CHADEMO_EVSE_START,
CHADEMO_EVSE_CONTACTORS_ENABLED,
CHADEMO_POWERFLOW,
};
enum Mode { CHADEMO_CHARGE, CHADEMO_DISCHARGE, CHADEMO_BIDIRECTIONAL };
/* Charge/discharge sequence, indicating applicable V2H guideline
* If sequence number is not agreed upon via H201/H209 between EVSE and Vehicle,
* V2H 1.1 is assumed, which..is somehow between 0x0 and 0x1 ? TODO: better understanding here
* Use CHADEMO_seq to decide whether emitting 209 is necessary
* 0x0 1.0 and earlier
* 0x1 2.0 appendix A
* 0x2 2.0 appendix B
* TODO: is this influenced by x109->CHADEMO_protocol_number, or x102->ControlProtocolNumberEV ??
* Unused for now.
uint8_t CHADEMO_seq = 0x0;
*/
/*----------- CHARGING SUPPORT V2X --------------------------------------------------------------*/
/* ---------- VEHICLE Data structures */
//H100 - Vehicle - Minimum charging expectations
//TODO decide whether default values for vehicle-origin frames is even appropriate
struct x100_Vehicle_Charging_Limits {
uint8_t MinimumChargeCurrent = 0;
uint16_t MinimumBatteryVoltage = 300;
uint16_t MaximumBatteryVoltage = 402;
uint8_t ConstantOfChargingRateIndication = 0;
};
//H101 - Vehicle - Maximum charging expectations
struct x101_Vehicle_Charging_Estimate {
uint8_t MaxChargingTime10sBit = 0;
uint8_t MaxChargingTime1minBit = 0;
uint8_t EstimatedChargingTime = 0;
uint16_t RatedBatteryCapacity = 0;
};
//H102 - Vehicle - Charging targets and Status
// peer to x109 from EVSE
// termination triggers in both
// TODO see also Table A.26—Charge control termination command patterns
struct x102_Vehicle_Charging_Session { //Frame byte
uint8_t ControlProtocolNumberEV = 0; // 0
uint16_t TargetBatteryVoltage = 0; // 1-2
uint8_t ChargingCurrentRequest = 0; // 3 Note: per spec, units for this changed from kWh --> %
union {
uint8_t faults;
struct {
bool unused_3 : 1;
bool unused_2 : 1;
bool unused_1 : 1;
bool FaultBatteryVoltageDeviation : 1; // 4
bool FaultHighBatteryTemperature : 1; // 3
bool FaultBatteryCurrentDeviation : 1; // 2
bool FaultBatteryUnderVoltage : 1; // 1
bool FaultBatteryOverVoltage : 1; // 0
} fault;
} f;
union {
uint8_t packed;
struct {
bool StatusVehicleDischargeCompatible : 1; //5.7
bool unused_2 : 1; //5.6
bool unused_1 : 1; //5.5
bool StatusNormalStopRequest : 1; //5.4
bool StatusVehicle : 1; //5.3
bool StatusChargingError : 1; //5.2
bool StatusVehicleShifterPosition : 1; //5.1
bool StatusVehicleChargingEnabled : 1; //5.0 - bit zero is TODO. Vehicle charging enabled ==1 *AND* charge
// permission signal k needs to be active for charging to be
// permitted -- TODO document bits per byte for these flags
// and update variables to be more appropriate
} status;
} s;
uint8_t StateOfCharge = 0; //6 state of charge?
};
/* ---------- CHARGING: EVSE Data structures */
struct x108_EVSE_Capabilities { // Frame byte
bool contactor_weld_detection = 1; // 0
uint16_t available_output_voltage = MAX_EVSE_OUTPUT_VOLTAGE; // 1,2
uint8_t available_output_current = MAX_EVSE_OUTPUT_CURRENT; // 3
uint16_t threshold_voltage = 297; // 4,5 voltage that EVSE will stop if car fails to
// perhaps vehicle minus 3%, hardcoded initially to 96*2.95
// 6,7 = unused
};
/* Does double duty for charging and discharging */
struct x109_EVSE_Status { // Frame byte
uint8_t CHADEMO_protocol_number = 0x02; // 0
uint16_t setpoint_HV_VDC =
0; // 1,2 NOTE: charger_setpoint_HV_VDC elsewhere is a float. THIS is protocol-defined as an int. cast float->int and lose some precision for protocol adherence
uint8_t setpoint_HV_IDC = 0; // 3
//
bool discharge_compatible = true; // 4, bit 0. bits
// 4, bit 7-6 (?) unused. spec typo? maybe 1-7 unused
union {
uint8_t packed;
struct {
bool EVSE_status : 1; // 5, bit 0
bool EVSE_error : 1; // 5, bit 1
bool connector_locked : 1; // 5, bit 2 //NOTE: treated as connector_lock during discharge, but
// seen as 'energizing' during charging mode
bool battery_incompatible : 1; // 5, bit 3
bool ChgDischError : 1; // 5, bit 4
bool ChgDischStopControl : 1; // 5, bit 5 - set to false for initialization to indicate 'preparing to charge'
// set to false when ready to charge/discharge
} status;
} s;
// Either, or; not both.
// seconds field set to 0xFF by default
// indicating only the minutes field is used instead
// BOTH observed initially set to 0xFF in logs, so use
// that as the initialzed value
uint8_t remaining_time_10s = 0xFF; // 6
uint8_t remaining_time_1m = 0xFF; // 7
};
/*----------- DISCHARGING SUPPORT V2X --------------------------------------------------------------*/
/* ---------- VEHICLE Data structures */
//H200 - Vehicle - Discharge limits
struct x200_Vehicle_Discharge_Limits {
uint8_t MaximumDischargeCurrent = 0xFF;
uint16_t MinimumDischargeVoltage = 0;
uint16_t MinimumBatteryDischargeLevel = 0;
uint16_t MaxRemainingCapacityForCharging = 0;
};
/* TODO When charge/discharge sequence control number (ID201/209) is not received, the vehicle or the EVSE
should determine that the other is the EVSE or the vehicle of the model before the V2H guideline 1.1. */
//H201 - Vehicle - Estimated capacity available
// Intended primarily for display purposes.
// Peer to H209
// NOTE: in available CAN logs from a Leaf, 209 is sent with no 201 reply, so < 1.1 must be the inferred version
struct x201_Vehicle_Discharge_Estimate {
uint8_t V2HchargeDischargeSequenceNum = 0;
uint16_t ApproxDischargeCompletionTime = 0;
uint16_t AvailableVehicleEnergy = 0;
};
/* ---------- EVSE Data structures */
struct x208_EVSE_Discharge_Capability { // Frame byte
uint8_t present_discharge_current = 0xFF; // 0
uint16_t available_input_voltage = 500; // 1,2 -- poorly named as both 'available' and minimum input voltage
uint16_t available_input_current = 250; // 3 -- poorly named as both 'available' and maximum input current
// spec idiosyncracy in naming/description
// 4,5 = unused
uint16_t lower_threshold_voltage = 0; // 6,7
};
// H209 - EVSE - Estimated Discharge Duration
// peer to Vehicle's 201 event (Note: 209 seen
// in CAN logs even when 201 is not)
struct x209_EVSE_Discharge_Estimate { // Frame byte
uint8_t sequence_control_number = 0x2; // 0
uint16_t remaining_discharge_time = 0x0000; // 0x0000 == unused
};
/*----------- DYNAMIC CONTROL SUPPORT --------------------------------------------------------------*/
/* ---------- VEHICLE Data structures */
struct x110_Vehicle_Dynamic_Control { //Frame byte
union {
uint8_t packed;
struct {
bool PermissionResetMaxChgTime : 1; // bit 5 or 6? is this only x118 not x110?
bool unused_3 : 1;
bool unused_2 : 1;
bool unused_1 : 1;
bool HighVoltageControlStatus : 1; // bit 2 = High voltage control support
bool HighCurrentControlStatus : 1; // bit 1 = High current control support
// rate of change is -20A/s to 20A/s relative to 102.3
bool DynamicControlStatus : 1; // bit 0 = Dynamic Control support
} status;
} u;
};
/* ---------- EVSE Data structures */
// TODO 118
//H118
//see also table a.59 page 104 IEEE
struct x118_EVSE_Dynamic_Control { // Frame byte
union {
uint8_t packed;
struct {
bool PermissionResetMaxChgTime : 1; // bit 5 or 6?
bool unused_3 : 1;
bool unused_2 : 1;
bool unused_1 : 1;
bool HighVoltageControlStatus : 1; // bit 2 = High voltage control support
bool HighCurrentControlStatus : 1; // bit 1 = High current control support
// rate of change is -20A/s to 20A/s relative to 102.3
bool DynamicControlStatus : 1; // bit 0 = Dynamic Control support
} status;
} u;
};
/*----------- MANUFACTURER ID SUPPORT --------------------------------------------------------------*/
/* ---------- VEHICLE Data structures */
//H700 - Vehicle - Manufacturer identification
//Peer to H708
//Used to adapt to manufacturer-prescribed optional specification
struct x700_Vehicle_Vendor_ID {
uint8_t AutomakerCode = 0; // 0 = set to 0x0 to indicate incompatibility. Best as a starting place
uint8_t OptionalContent = 0; // 1-7, variable per vendor spec
};
void handle_chademo_sequence();
#endif

View file

@ -2,7 +2,6 @@
#ifdef CHADEMO_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "CHADEMO-BATTERY-INTERNAL.h"
#include "CHADEMO-BATTERY.h"
#include "CHADEMO-SHUNTS.h"
@ -14,101 +13,8 @@
#define CAN_STILL_ALIVE 75
//#define CH_CAN_DEBUG
static unsigned long setupMillis = 0;
static unsigned long handlerBeforeMillis = 0;
static unsigned long handlerAfterMillis = 0;
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis5000 =
0; // will store last time a 5s threshold was reached for display during debug
bool plug_inserted = false;
bool vehicle_can_initialized = false;
bool vehicle_can_received = false;
bool vehicle_permission = false;
bool evse_permission = false;
bool precharge_low = false;
bool positive_high = false;
bool contactors_ready = false;
uint8_t framecount = 0;
uint8_t max_discharge_current = 0; //TODO not sure on this one, but really influenced by inverter capability
bool high_current_control_enabled = false; // set to true when high current control is operating
// if true, values from 110.1 and 110.2 should be used instead of 102.3
// and 118 should be used for evse responses
// permissible rate of change is -20A/s to 20A/s relative to 102.3
Mode EVSE_mode = CHADEMO_DISCHARGE;
CHADEMO_STATE CHADEMO_Status = CHADEMO_IDLE;
/* Charge/discharge sequence, indicating applicable V2H guideline
* If sequence number is not agreed upon via H201/H209 between EVSE and Vehicle,
* V2H 1.1 is assumed
* Use CHADEMO_seq to decide whether emitting 209 is necessary
* 0x0 1.0 and earlier
* 0x1 2.0 appendix A
* 0x2 2.0 appendix B
* Unused for now.
uint8_t CHADEMO_seq = 0x0;
*/
bool x201_received = false;
bool x209_sent = false;
struct x100_Vehicle_Charging_Limits x100_chg_lim = {};
struct x101_Vehicle_Charging_Estimate x101_chg_est = {};
struct x102_Vehicle_Charging_Session x102_chg_session = {};
struct x110_Vehicle_Dynamic_Control x110_vehicle_dyn = {};
struct x200_Vehicle_Discharge_Limits x200_discharge_limits = {};
struct x201_Vehicle_Discharge_Estimate x201_discharge_estimate = {};
struct x700_Vehicle_Vendor_ID x700_vendor_id = {};
struct x209_EVSE_Discharge_Estimate x209_evse_dischg_est;
struct x108_EVSE_Capabilities x108_evse_cap;
struct x109_EVSE_Status x109_evse_state;
struct x118_EVSE_Dynamic_Control x118_evse_dyn;
struct x208_EVSE_Discharge_Capability x208_evse_dischg_cap;
CAN_frame CHADEMO_108 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x108,
.data = {0x01, 0xF4, 0x01, 0x0F, 0xB3, 0x01, 0x00, 0x00}};
CAN_frame CHADEMO_109 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x109,
.data = {0x02, 0x00, 0x00, 0x00, 0x01, 0x20, 0xFF, 0xFF}};
//For chademo v2.0 only
CAN_frame CHADEMO_118 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x118,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
// OLD value from skeleton implementation, indicates dynamic control is possible.
// Hardcode above as being incompatible for simplicity in current incarnation.
// .data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}};
// 0x200 : From vehicle-side. A V2X-ready vehicle will send this message to broadcast its “Maximum discharger current”. (It is a similar logic to the limits set in 0x100 or 0x102 during a DC charging session)
// 0x208 : From EVSE-side. A V2X EVSE will use this to send the “present discharger current” during the session, and the “available input current”. (uses similar logic to 0x108 and 0x109 during a DC charging session)
CAN_frame CHADEMO_208 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x208,
.data = {0xFF, 0xF4, 0x01, 0xF0, 0x00, 0x00, 0xFA, 0x00}};
CAN_frame CHADEMO_209 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x209,
.data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
//This function maps all the values fetched via CAN to the correct parameters used for the inverter
void update_values_battery() {
void ChademoBattery::update_values() {
datalayer.battery.status.real_soc = x102_chg_session.StateOfCharge;
@ -147,21 +53,21 @@ void update_values_battery() {
//see IEEE Table A.26—Charge control termination command pattern on pg58
//for stop conditions
inline void process_vehicle_charging_minimums(CAN_frame rx_frame) {
void ChademoBattery::process_vehicle_charging_minimums(CAN_frame rx_frame) {
x100_chg_lim.MinimumChargeCurrent = rx_frame.data.u8[0];
x100_chg_lim.MinimumBatteryVoltage = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
x100_chg_lim.MaximumBatteryVoltage = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]);
x100_chg_lim.ConstantOfChargingRateIndication = rx_frame.data.u8[6];
}
inline void process_vehicle_charging_maximums(CAN_frame rx_frame) {
void ChademoBattery::process_vehicle_charging_maximums(CAN_frame rx_frame) {
x101_chg_est.MaxChargingTime10sBit = rx_frame.data.u8[1];
x101_chg_est.MaxChargingTime1minBit = rx_frame.data.u8[2];
x101_chg_est.EstimatedChargingTime = rx_frame.data.u8[3];
x101_chg_est.RatedBatteryCapacity = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[5]);
}
inline void process_vehicle_charging_session(CAN_frame rx_frame) {
void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
uint16_t newTargetBatteryVoltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[1]);
uint16_t priorTargetBatteryVoltage = x102_chg_session.TargetBatteryVoltage;
uint8_t newChargingCurrentRequest = rx_frame.data.u8[3];
@ -303,7 +209,7 @@ inline void process_vehicle_charging_session(CAN_frame rx_frame) {
}
/* x200 Vehicle, peer to x208 EVSE */
inline void process_vehicle_charging_limits(CAN_frame rx_frame) {
void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) {
x200_discharge_limits.MaximumDischargeCurrent = rx_frame.data.u8[0];
x200_discharge_limits.MinimumDischargeVoltage = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]);
@ -332,7 +238,7 @@ inline void process_vehicle_charging_limits(CAN_frame rx_frame) {
/* Vehicle 0x201, peer to EVSE 0x209
* HOWEVER, 201 isn't even emitted in any of the v2x canlogs available
*/
inline void process_vehicle_discharge_estimate(CAN_frame rx_frame) {
void ChademoBattery::process_vehicle_discharge_estimate(CAN_frame rx_frame) {
unsigned long currentMillis = millis();
x201_discharge_estimate.V2HchargeDischargeSequenceNum = rx_frame.data.u8[0];
@ -350,7 +256,7 @@ inline void process_vehicle_discharge_estimate(CAN_frame rx_frame) {
#endif
}
inline void process_vehicle_dynamic_control(CAN_frame rx_frame) {
void ChademoBattery::process_vehicle_dynamic_control(CAN_frame rx_frame) {
//SM Dynamic Control = Charging station can increase of decrease "available output current" during charging.
//If you set 0x110 byte 0, bit 0 to 1 you say you can do dynamic control.
//Charging station communicates this in 0x118 byte 0, bit 0
@ -359,13 +265,13 @@ inline void process_vehicle_dynamic_control(CAN_frame rx_frame) {
x110_vehicle_dyn.u.status.DynamicControlStatus = bitRead(rx_frame.data.u8[0], 0);
}
inline void process_vehicle_vendor_ID(CAN_frame rx_frame) {
void ChademoBattery::process_vehicle_vendor_ID(CAN_frame rx_frame) {
x700_vendor_id.AutomakerCode = rx_frame.data.u8[0];
x700_vendor_id.OptionalContent =
((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[1]); //Actually more bytes, but not needed for our purpose
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void ChademoBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
#ifdef CH_CAN_DEBUG
logging.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D
logging.print(" ");
@ -420,6 +326,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
break;
case 0x110: //Only present on Chademo v2.0
process_vehicle_dynamic_control(rx_frame);
break;
case 0x700:
process_vehicle_vendor_ID(rx_frame);
break;
@ -438,7 +345,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
/* (re)initialize evse structures to pre-charge/discharge states */
void evse_init() {
void ChademoBattery::evse_init() {
// Held at 1 until start of charge when set to 0
// returns to 1 when ceasing power flow
// mutually exclusive values
@ -457,7 +364,7 @@ void evse_init() {
}
/* updates for x108 */
void update_evse_capabilities(CAN_frame& f) {
void ChademoBattery::update_evse_capabilities(CAN_frame& f) {
/* TODO use charger defines/runtime config?
* for now..leave as a future tweak.
@ -495,7 +402,7 @@ void update_evse_capabilities(CAN_frame& f) {
}
/* updates for x109 */
void update_evse_status(CAN_frame& f) {
void ChademoBattery::update_evse_status(CAN_frame& f) {
x109_evse_state.s.status.EVSE_status = 1;
x109_evse_state.s.status.EVSE_error = 0;
@ -586,7 +493,7 @@ void update_evse_status(CAN_frame& f) {
* NOTE: x209 is emitted in CAN logs when x201 isn't even present
* it may not be understood by leaf (or ignored unless >= a certain protocol version or v2h sequence number
*/
void update_evse_discharge_estimate(CAN_frame& f) {
void ChademoBattery::update_evse_discharge_estimate(CAN_frame& f) {
//x209_evse_dischg_est.remaining_discharge_time_1m = x201_discharge_estimate.ApproxDischargeCompletionTime;
@ -605,7 +512,7 @@ void update_evse_discharge_estimate(CAN_frame& f) {
}
/* x208 EVSE, peer to 0x200 Vehicle */
void update_evse_discharge_capabilities(CAN_frame& f) {
void ChademoBattery::update_evse_discharge_capabilities(CAN_frame& f) {
//present discharge current is a measured value
x208_evse_dischg_cap.present_discharge_current = 0xFF - get_measured_current();
@ -655,7 +562,7 @@ void update_evse_discharge_capabilities(CAN_frame& f) {
CHADEMO_208.data.u8[7] = highByte(x208_evse_dischg_cap.lower_threshold_voltage);
}
void transmit_can_battery(unsigned long currentMillis) {
void ChademoBattery::transmit_can(unsigned long currentMillis) {
handlerBeforeMillis = currentMillis;
handle_chademo_sequence();
@ -733,7 +640,7 @@ void transmit_can_battery(unsigned long currentMillis) {
* 5) Emergency stop stage
* CHADEMO_FAULT
*/
void handle_chademo_sequence() {
void ChademoBattery::handle_chademo_sequence() {
precharge_low = digitalRead(PRECHARGE_PIN) == LOW;
positive_high = digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH;
@ -1020,7 +927,7 @@ void handle_chademo_sequence() {
return;
}
void setup_battery(void) { // Performs one time setup at startup
void ChademoBattery::setup(void) { // Performs one time setup at startup
pinMode(CHADEMO_PIN_2, OUTPUT);
digitalWrite(CHADEMO_PIN_2, LOW);

View file

@ -2,8 +2,7 @@
#define CHADEMO_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#define BATTERY_SELECTED
#include "CanBattery.h"
//Contactor control is required for CHADEMO support
#define CONTACTOR_CONTROL
@ -12,7 +11,349 @@
// other measurement sources may be added in the future
#define ISA_SHUNT
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS ChademoBattery
class ChademoBattery : 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:
void process_vehicle_charging_minimums(CAN_frame rx_frame);
void process_vehicle_charging_maximums(CAN_frame rx_frame);
void process_vehicle_charging_session(CAN_frame rx_frame);
void process_vehicle_charging_limits(CAN_frame rx_frame);
void process_vehicle_discharge_estimate(CAN_frame rx_frame);
void process_vehicle_dynamic_control(CAN_frame rx_frame);
void process_vehicle_vendor_ID(CAN_frame rx_frame);
void evse_init();
void update_evse_capabilities(CAN_frame& f);
void update_evse_status(CAN_frame& f);
void update_evse_discharge_estimate(CAN_frame& f);
void update_evse_discharge_capabilities(CAN_frame& f);
void handle_chademo_sequence();
static const int MAX_EVSE_POWER_CHARGING = 3300;
static const int MAX_EVSE_OUTPUT_VOLTAGE = 410;
static const int MAX_EVSE_OUTPUT_CURRENT = 11;
enum CHADEMO_STATE {
CHADEMO_FAULT,
CHADEMO_STOP,
CHADEMO_IDLE,
CHADEMO_CONNECTED,
CHADEMO_INIT, // intermediate state indicating CAN from Vehicle not yet received after connection
CHADEMO_NEGOTIATE,
CHADEMO_EV_ALLOWED,
CHADEMO_EVSE_PREPARE,
CHADEMO_EVSE_START,
CHADEMO_EVSE_CONTACTORS_ENABLED,
CHADEMO_POWERFLOW,
};
enum Mode { CHADEMO_CHARGE, CHADEMO_DISCHARGE, CHADEMO_BIDIRECTIONAL };
/* Charge/discharge sequence, indicating applicable V2H guideline
* If sequence number is not agreed upon via H201/H209 between EVSE and Vehicle,
* V2H 1.1 is assumed, which..is somehow between 0x0 and 0x1 ? TODO: better understanding here
* Use CHADEMO_seq to decide whether emitting 209 is necessary
* 0x0 1.0 and earlier
* 0x1 2.0 appendix A
* 0x2 2.0 appendix B
* TODO: is this influenced by x109->CHADEMO_protocol_number, or x102->ControlProtocolNumberEV ??
* Unused for now.
uint8_t CHADEMO_seq = 0x0;
*/
/*----------- CHARGING SUPPORT V2X --------------------------------------------------------------*/
/* ---------- VEHICLE Data structures */
//H100 - Vehicle - Minimum charging expectations
//TODO decide whether default values for vehicle-origin frames is even appropriate
struct x100_Vehicle_Charging_Limits {
uint8_t MinimumChargeCurrent = 0;
uint16_t MinimumBatteryVoltage = 300;
uint16_t MaximumBatteryVoltage = 402;
uint8_t ConstantOfChargingRateIndication = 0;
};
//H101 - Vehicle - Maximum charging expectations
struct x101_Vehicle_Charging_Estimate {
uint8_t MaxChargingTime10sBit = 0;
uint8_t MaxChargingTime1minBit = 0;
uint8_t EstimatedChargingTime = 0;
uint16_t RatedBatteryCapacity = 0;
};
//H102 - Vehicle - Charging targets and Status
// peer to x109 from EVSE
// termination triggers in both
// TODO see also Table A.26—Charge control termination command patterns
struct x102_Vehicle_Charging_Session { //Frame byte
uint8_t ControlProtocolNumberEV = 0; // 0
uint16_t TargetBatteryVoltage = 0; // 1-2
uint8_t ChargingCurrentRequest = 0; // 3 Note: per spec, units for this changed from kWh --> %
union {
uint8_t faults;
struct {
bool unused_3 : 1;
bool unused_2 : 1;
bool unused_1 : 1;
bool FaultBatteryVoltageDeviation : 1; // 4
bool FaultHighBatteryTemperature : 1; // 3
bool FaultBatteryCurrentDeviation : 1; // 2
bool FaultBatteryUnderVoltage : 1; // 1
bool FaultBatteryOverVoltage : 1; // 0
} fault;
} f;
union {
uint8_t packed;
struct {
bool StatusVehicleDischargeCompatible : 1; //5.7
bool unused_2 : 1; //5.6
bool unused_1 : 1; //5.5
bool StatusNormalStopRequest : 1; //5.4
bool StatusVehicle : 1; //5.3
bool StatusChargingError : 1; //5.2
bool StatusVehicleShifterPosition : 1; //5.1
bool StatusVehicleChargingEnabled : 1; //5.0 - bit zero is TODO. Vehicle charging enabled ==1 *AND* charge
// permission signal k needs to be active for charging to be
// permitted -- TODO document bits per byte for these flags
// and update variables to be more appropriate
} status;
} s;
uint8_t StateOfCharge = 0; //6 state of charge?
};
/* ---------- CHARGING: EVSE Data structures */
struct x108_EVSE_Capabilities { // Frame byte
bool contactor_weld_detection = 1; // 0
uint16_t available_output_voltage = MAX_EVSE_OUTPUT_VOLTAGE; // 1,2
uint8_t available_output_current = MAX_EVSE_OUTPUT_CURRENT; // 3
uint16_t threshold_voltage = 297; // 4,5 voltage that EVSE will stop if car fails to
// perhaps vehicle minus 3%, hardcoded initially to 96*2.95
// 6,7 = unused
};
/* Does double duty for charging and discharging */
struct x109_EVSE_Status { // Frame byte
uint8_t CHADEMO_protocol_number = 0x02; // 0
uint16_t setpoint_HV_VDC =
0; // 1,2 NOTE: charger_setpoint_HV_VDC elsewhere is a float. THIS is protocol-defined as an int. cast float->int and lose some precision for protocol adherence
uint8_t setpoint_HV_IDC = 0; // 3
//
bool discharge_compatible = true; // 4, bit 0. bits
// 4, bit 7-6 (?) unused. spec typo? maybe 1-7 unused
union {
uint8_t packed;
struct {
bool EVSE_status : 1; // 5, bit 0
bool EVSE_error : 1; // 5, bit 1
bool connector_locked : 1; // 5, bit 2 //NOTE: treated as connector_lock during discharge, but
// seen as 'energizing' during charging mode
bool battery_incompatible : 1; // 5, bit 3
bool ChgDischError : 1; // 5, bit 4
bool ChgDischStopControl : 1; // 5, bit 5 - set to false for initialization to indicate 'preparing to charge'
// set to false when ready to charge/discharge
} status;
} s;
// Either, or; not both.
// seconds field set to 0xFF by default
// indicating only the minutes field is used instead
// BOTH observed initially set to 0xFF in logs, so use
// that as the initialzed value
uint8_t remaining_time_10s = 0xFF; // 6
uint8_t remaining_time_1m = 0xFF; // 7
};
/*----------- DISCHARGING SUPPORT V2X --------------------------------------------------------------*/
/* ---------- VEHICLE Data structures */
//H200 - Vehicle - Discharge limits
struct x200_Vehicle_Discharge_Limits {
uint8_t MaximumDischargeCurrent = 0xFF;
uint16_t MinimumDischargeVoltage = 0;
uint16_t MinimumBatteryDischargeLevel = 0;
uint16_t MaxRemainingCapacityForCharging = 0;
};
/* TODO When charge/discharge sequence control number (ID201/209) is not received, the vehicle or the EVSE
should determine that the other is the EVSE or the vehicle of the model before the V2H guideline 1.1. */
//H201 - Vehicle - Estimated capacity available
// Intended primarily for display purposes.
// Peer to H209
// NOTE: in available CAN logs from a Leaf, 209 is sent with no 201 reply, so < 1.1 must be the inferred version
struct x201_Vehicle_Discharge_Estimate {
uint8_t V2HchargeDischargeSequenceNum = 0;
uint16_t ApproxDischargeCompletionTime = 0;
uint16_t AvailableVehicleEnergy = 0;
};
/* ---------- EVSE Data structures */
struct x208_EVSE_Discharge_Capability { // Frame byte
uint8_t present_discharge_current = 0xFF; // 0
uint16_t available_input_voltage = 500; // 1,2 -- poorly named as both 'available' and minimum input voltage
uint16_t available_input_current = 250; // 3 -- poorly named as both 'available' and maximum input current
// spec idiosyncracy in naming/description
// 4,5 = unused
uint16_t lower_threshold_voltage = 0; // 6,7
};
// H209 - EVSE - Estimated Discharge Duration
// peer to Vehicle's 201 event (Note: 209 seen
// in CAN logs even when 201 is not)
struct x209_EVSE_Discharge_Estimate { // Frame byte
uint8_t sequence_control_number = 0x2; // 0
uint16_t remaining_discharge_time = 0x0000; // 0x0000 == unused
};
/*----------- DYNAMIC CONTROL SUPPORT --------------------------------------------------------------*/
/* ---------- VEHICLE Data structures */
struct x110_Vehicle_Dynamic_Control { //Frame byte
union {
uint8_t packed;
struct {
bool PermissionResetMaxChgTime : 1; // bit 5 or 6? is this only x118 not x110?
bool unused_3 : 1;
bool unused_2 : 1;
bool unused_1 : 1;
bool HighVoltageControlStatus : 1; // bit 2 = High voltage control support
bool HighCurrentControlStatus : 1; // bit 1 = High current control support
// rate of change is -20A/s to 20A/s relative to 102.3
bool DynamicControlStatus : 1; // bit 0 = Dynamic Control support
} status;
} u;
};
/* ---------- EVSE Data structures */
// TODO 118
//H118
//see also table a.59 page 104 IEEE
struct x118_EVSE_Dynamic_Control { // Frame byte
union {
uint8_t packed;
struct {
bool PermissionResetMaxChgTime : 1; // bit 5 or 6?
bool unused_3 : 1;
bool unused_2 : 1;
bool unused_1 : 1;
bool HighVoltageControlStatus : 1; // bit 2 = High voltage control support
bool HighCurrentControlStatus : 1; // bit 1 = High current control support
// rate of change is -20A/s to 20A/s relative to 102.3
bool DynamicControlStatus : 1; // bit 0 = Dynamic Control support
} status;
} u;
};
/*----------- MANUFACTURER ID SUPPORT --------------------------------------------------------------*/
/* ---------- VEHICLE Data structures */
//H700 - Vehicle - Manufacturer identification
//Peer to H708
//Used to adapt to manufacturer-prescribed optional specification
struct x700_Vehicle_Vendor_ID {
uint8_t AutomakerCode = 0; // 0 = set to 0x0 to indicate incompatibility. Best as a starting place
uint8_t OptionalContent = 0; // 1-7, variable per vendor spec
};
unsigned long setupMillis = 0;
unsigned long handlerBeforeMillis = 0;
unsigned long handlerAfterMillis = 0;
/* Do not change code below unless you are sure what you are doing */
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
unsigned long previousMillis5000 = 0; // will store last time a 5s threshold was reached for display during debug
bool plug_inserted = false;
bool vehicle_can_initialized = false;
bool vehicle_can_received = false;
bool vehicle_permission = false;
bool evse_permission = false;
bool precharge_low = false;
bool positive_high = false;
bool contactors_ready = false;
uint8_t framecount = 0;
uint8_t max_discharge_current = 0; //TODO not sure on this one, but really influenced by inverter capability
bool high_current_control_enabled = false; // set to true when high current control is operating
// if true, values from 110.1 and 110.2 should be used instead of 102.3
// and 118 should be used for evse responses
// permissible rate of change is -20A/s to 20A/s relative to 102.3
Mode EVSE_mode = CHADEMO_DISCHARGE;
CHADEMO_STATE CHADEMO_Status = CHADEMO_IDLE;
/* Charge/discharge sequence, indicating applicable V2H guideline
* If sequence number is not agreed upon via H201/H209 between EVSE and Vehicle,
* V2H 1.1 is assumed
* Use CHADEMO_seq to decide whether emitting 209 is necessary
* 0x0 1.0 and earlier
* 0x1 2.0 appendix A
* 0x2 2.0 appendix B
* Unused for now.
uint8_t CHADEMO_seq = 0x0;
*/
bool x201_received = false;
bool x209_sent = false;
struct x100_Vehicle_Charging_Limits x100_chg_lim = {};
struct x101_Vehicle_Charging_Estimate x101_chg_est = {};
struct x102_Vehicle_Charging_Session x102_chg_session = {};
struct x110_Vehicle_Dynamic_Control x110_vehicle_dyn = {};
struct x200_Vehicle_Discharge_Limits x200_discharge_limits = {};
struct x201_Vehicle_Discharge_Estimate x201_discharge_estimate = {};
struct x700_Vehicle_Vendor_ID x700_vendor_id = {};
struct x209_EVSE_Discharge_Estimate x209_evse_dischg_est;
struct x108_EVSE_Capabilities x108_evse_cap;
struct x109_EVSE_Status x109_evse_state;
struct x118_EVSE_Dynamic_Control x118_evse_dyn;
struct x208_EVSE_Discharge_Capability x208_evse_dischg_cap;
CAN_frame CHADEMO_108 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x108,
.data = {0x01, 0xF4, 0x01, 0x0F, 0xB3, 0x01, 0x00, 0x00}};
CAN_frame CHADEMO_109 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x109,
.data = {0x02, 0x00, 0x00, 0x00, 0x01, 0x20, 0xFF, 0xFF}};
//For chademo v2.0 only
CAN_frame CHADEMO_118 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x118,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
// OLD value from skeleton implementation, indicates dynamic control is possible.
// Hardcode above as being incompatible for simplicity in current incarnation.
// .data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}};
// 0x200 : From vehicle-side. A V2X-ready vehicle will send this message to broadcast its “Maximum discharger current”. (It is a similar logic to the limits set in 0x100 or 0x102 during a DC charging session)
// 0x208 : From EVSE-side. A V2X EVSE will use this to send the “present discharger current” during the session, and the “available input current”. (uses similar logic to 0x108 and 0x109 during a DC charging session)
CAN_frame CHADEMO_208 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x208,
.data = {0xFF, 0xF4, 0x01, 0xF0, 0x00, 0x00, 0xFA, 0x00}};
CAN_frame CHADEMO_209 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x209,
.data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
};
#endif

View file

@ -22,7 +22,6 @@
#ifdef CHADEMO_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "CHADEMO-BATTERY-INTERNAL.h"
#include "CHADEMO-BATTERY.h"
#include "CHADEMO-SHUNTS.h"

View file

@ -1,92 +1,15 @@
#include "../include.h"
#ifdef CMFA_EV_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "CMFA-EV-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */
CAN_frame CMFA_1EA = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x1EA, .data = {0x00}};
CAN_frame CMFA_125 = {.FD = false,
.ext_ID = false,
.DLC = 7,
.ID = 0x125,
.data = {0x7D, 0x7D, 0x7D, 0x07, 0x82, 0x6A, 0x8A}};
CAN_frame CMFA_134 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x134,
.data = {0x90, 0x8A, 0x7E, 0x3E, 0xB2, 0x4C, 0x80, 0x00}};
CAN_frame CMFA_135 = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x135, .data = {0xD5, 0x85, 0x38, 0x80, 0x01}};
CAN_frame CMFA_3D3 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x3D3,
.data = {0x47, 0x30, 0x00, 0x02, 0x5D, 0x80, 0x5D, 0xE7}};
CAN_frame CMFA_59B = {.FD = false, .ext_ID = false, .DLC = 3, .ID = 0x59B, .data = {0x00, 0x02, 0x00}};
CAN_frame CMFA_ACK = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x79B,
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame CMFA_POLLING_FRAME = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x79B,
.data = {0x03, 0x22, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00}};
static bool end_of_charge = false;
static bool interlock_flag = false;
static uint16_t soc_z = 0;
static uint16_t soc_u = 0;
static uint16_t max_regen_power = 0;
static uint16_t max_discharge_power = 0;
static int16_t average_temperature = 0;
static int16_t minimum_temperature = 0;
static int16_t maximum_temperature = 0;
static uint16_t maximum_charge_power = 0;
static uint16_t SOH_available_power = 0;
static uint16_t SOH_generated_power = 0;
static uint32_t average_voltage_of_cells = 270000;
static uint16_t highest_cell_voltage_mv = 3700;
static uint16_t lowest_cell_voltage_mv = 3700;
static uint16_t lead_acid_voltage = 12000;
static uint8_t highest_cell_voltage_number = 0;
static uint8_t lowest_cell_voltage_number = 0;
static uint64_t cumulative_energy_when_discharging = 0;
static uint64_t cumulative_energy_when_charging = 0;
static uint64_t cumulative_energy_in_regen = 0;
static uint16_t soh_average = 10000;
static uint16_t cellvoltages_mv[72];
static uint32_t poll_pid = PID_POLL_SOH_AVERAGE;
static uint16_t pid_reply = 0;
static uint8_t counter_10ms = 0;
static uint8_t content_125[16] = {0x07, 0x0C, 0x01, 0x06, 0x0B, 0x00, 0x05, 0x0A,
0x0F, 0x04, 0x09, 0x0E, 0x03, 0x08, 0x0D, 0x02};
static uint8_t content_135[16] = {0x85, 0xD5, 0x25, 0x75, 0xC5, 0x15, 0x65, 0xB5,
0x05, 0x55, 0xA5, 0xF5, 0x45, 0x95, 0xE5, 0x35};
static unsigned long previousMillis200ms = 0;
static unsigned long previousMillis100ms = 0;
static unsigned long previousMillis10ms = 0;
#define MAXSOC 9000 //90.00 Raw SOC displays this value when battery is at 100%
#define MINSOC 500 //5.00 Raw SOC displays this value when battery is at 0%
static uint8_t heartbeat = 0; //Alternates between 0x55 and 0xAA every 5th frame
static uint8_t heartbeat2 = 0; //Alternates between 0x55 and 0xAA every 5th frame
static uint32_t SOC_raw = 0;
static uint16_t SOH = 99;
static int16_t current = 0;
static uint16_t pack_voltage = 2700;
static int16_t highest_cell_temperature = 0;
static int16_t lowest_cell_temperature = 0;
static uint32_t discharge_power_w = 0;
static uint32_t charge_power_w = 0;
/* The raw SOC value sits at 90% when the battery is full, so we should report back 100% once this value is reached
Same goes for low point, when 10% is reached we report 0% */
uint16_t rescale_raw_SOC(uint32_t raw_SOC) {
uint16_t CmfaEvBattery::rescale_raw_SOC(uint32_t raw_SOC) {
uint32_t calc_soc;
calc_soc = (raw_SOC * 0.25);
@ -103,7 +26,8 @@ uint16_t rescale_raw_SOC(uint32_t raw_SOC) {
return (uint16_t)calc_soc;
}
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
void CmfaEvBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
datalayer.battery.status.soh_pptt = (SOH * 100);
datalayer.battery.status.real_soc = rescale_raw_SOC(SOC_raw);
@ -157,7 +81,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer_extended.CMFAEV.soh_average = soh_average;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void CmfaEvBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) { //These frames are transmitted by the battery
case 0x127: //10ms , Same structure as old Zoe 0x155 message!
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
@ -513,7 +437,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void CmfaEvBattery::transmit_can(unsigned long currentMillis) {
// Send 10ms CAN Message
if (currentMillis - previousMillis10ms >= INTERVAL_10_MS) {
previousMillis10ms = currentMillis;
@ -1016,7 +940,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void CmfaEvBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "CMFA platform, 27 kWh battery", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;

View file

@ -2,157 +2,211 @@
#define CMFA_EV_BATTERY_H
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 3040 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 2185
#define MAX_CELL_DEVIATION_MV 100
#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
#define SELECTED_BATTERY_CLASS CmfaEvBattery
// OBD2 PID polls. Some of these have been reverse engineered, but there are many unknown values still
#define PID_POLL_SOCZ 0x9001 //122 in log
#define PID_POLL_USOC 0x9002 //5531 (Possible SOC candidate)
#define PID_POLL_SOH_AVERAGE 0x9003
#define PID_POLL_AVERAGE_VOLTAGE_OF_CELLS 0x9006
#define PID_POLL_HIGHEST_CELL_VOLTAGE 0x9007
#define PID_POLL_CELL_NUMBER_HIGHEST_VOLTAGE 0x9008
#define PID_POLL_LOWEST_CELL_VOLTAGE 0x9009
#define PID_POLL_CELL_NUMBER_LOWEST_VOLTAGE 0x900A
#define PID_POLL_CURRENT_OFFSET 0x900C
#define PID_POLL_INSTANT_CURRENT 0x900D
#define PID_POLL_MAX_REGEN 0x900E
#define PID_POLL_MAX_DISCHARGE_POWER 0x900F
#define PID_POLL_12V_BATTERY 0x9011
#define PID_POLL_AVERAGE_TEMPERATURE 0x9012 //749 in log
#define PID_POLL_MIN_TEMPERATURE 0x9013 //736 in log
#define PID_POLL_MAX_TEMPERATURE 0x9014 //752 in log
#define PID_POLL_MAX_CHARGE_POWER 0x9018
#define PID_POLL_END_OF_CHARGE_FLAG 0x9019
#define PID_POLL_INTERLOCK_FLAG 0x901A
#define PID_POLL_BATTERY_IDENTIFICATION 0x901B // Multi frame message
#define PID_POLL_CELL_1 0x9021
#define PID_POLL_CELL_2 0x9022
#define PID_POLL_CELL_3 0x9023
#define PID_POLL_CELL_4 0x9024
#define PID_POLL_CELL_5 0x9025
#define PID_POLL_CELL_6 0x9026
#define PID_POLL_CELL_7 0x9027
#define PID_POLL_CELL_8 0x9028
#define PID_POLL_CELL_9 0x9029
#define PID_POLL_CELL_10 0x902A
#define PID_POLL_CELL_11 0x902B
#define PID_POLL_CELL_12 0x902C
#define PID_POLL_CELL_13 0x902D
#define PID_POLL_CELL_14 0x902E
#define PID_POLL_CELL_15 0x902F
#define PID_POLL_CELL_16 0x9030
#define PID_POLL_CELL_17 0x9031
#define PID_POLL_CELL_18 0x9032
#define PID_POLL_CELL_19 0x9033
#define PID_POLL_CELL_20 0x9034
#define PID_POLL_CELL_21 0x9035
#define PID_POLL_CELL_22 0x9036
#define PID_POLL_CELL_23 0x9037
#define PID_POLL_CELL_24 0x9038
#define PID_POLL_CELL_25 0x9039
#define PID_POLL_CELL_26 0x903A
#define PID_POLL_CELL_27 0x903B
#define PID_POLL_CELL_28 0x903C
#define PID_POLL_CELL_29 0x903D
#define PID_POLL_CELL_30 0x903E
#define PID_POLL_CELL_31 0x903F
#define PID_POLL_DIDS_SUPPORTED_IN_RANGE_9041_9060 0x9040
#define PID_POLL_CELL_32 0x9041
#define PID_POLL_CELL_33 0x9042
#define PID_POLL_CELL_34 0x9043
#define PID_POLL_CELL_35 0x9044
#define PID_POLL_CELL_36 0x9045
#define PID_POLL_CELL_37 0x9046
#define PID_POLL_CELL_38 0x9047
#define PID_POLL_CELL_39 0x9048
#define PID_POLL_CELL_40 0x9049
#define PID_POLL_CELL_41 0x904A
#define PID_POLL_CELL_42 0x904B
#define PID_POLL_CELL_43 0x904C
#define PID_POLL_CELL_44 0x904D
#define PID_POLL_CELL_45 0x904E
#define PID_POLL_CELL_46 0x904F
#define PID_POLL_CELL_47 0x9050
#define PID_POLL_CELL_48 0x9051
#define PID_POLL_CELL_49 0x9052
#define PID_POLL_CELL_50 0x9053
#define PID_POLL_CELL_51 0x9054
#define PID_POLL_CELL_52 0x9055
#define PID_POLL_CELL_53 0x9056
#define PID_POLL_CELL_54 0x9057
#define PID_POLL_CELL_55 0x9058
#define PID_POLL_CELL_56 0x9059
#define PID_POLL_CELL_57 0x905A
#define PID_POLL_CELL_58 0x905B
#define PID_POLL_CELL_59 0x905C
#define PID_POLL_CELL_60 0x905D
#define PID_POLL_CELL_61 0x905E
#define PID_POLL_CELL_62 0x905F
#define PID_POLL_DIDS_SUPPORTED_IN_RANGE_9061_9080 0x9060
#define PID_POLL_CELL_63 0x9061
#define PID_POLL_CELL_64 0x9062
#define PID_POLL_CELL_65 0x9063
#define PID_POLL_CELL_66 0x9064
#define PID_POLL_CELL_67 0x9065
#define PID_POLL_CELL_68 0x9066
#define PID_POLL_CELL_69 0x9067
#define PID_POLL_CELL_70 0x9068
#define PID_POLL_CELL_71 0x9069
#define PID_POLL_CELL_72 0x906A
/*
#define PID_POLL_UNKNOWNX 0x912F // Multi frame message, empty
#define PID_POLL_UNKNOWNX 0x9129
#define PID_POLL_UNKNOWNX 0x9131
#define PID_POLL_UNKNOWNX 0x9132
#define PID_POLL_UNKNOWNX 0x9133
#define PID_POLL_UNKNOWNX 0x9134
#define PID_POLL_UNKNOWNX 0x9135
#define PID_POLL_UNKNOWNX 0x9136
#define PID_POLL_UNKNOWNX 0x9137
#define PID_POLL_UNKNOWNX 0x9138
#define PID_POLL_UNKNOWNX 0x9139
#define PID_POLL_UNKNOWNX 0x913A
#define PID_POLL_UNKNOWNX 0x913B
#define PID_POLL_UNKNOWNX 0x913C
#define PID_POLL_UNKNOWN5 0x912F
#define PID_POLL_UNKNOWNX 0x91B7
*/
#define PID_POLL_SOH_AVAILABLE_POWER_CALCULATION 0x91BC // 0-100%
#define PID_POLL_SOH_GENERATED_POWER_CALCULATION 0x91BD // 0-100%
/*
#define PID_POLL_UNKNOWNX 0x91C1
#define PID_POLL_UNKNOWNX 0x91CD
#define PID_POLL_UNKNOWNX 0x91CF
#define PID_POLL_UNKNOWNX 0x91F6
#define PID_POLL_UNKNOWNX 0x91F7
#define PID_POLL_UNKNOWNX 0x920F
#define PID_POLL_UNKNOWNx 0x9242
*/
#define PID_POLL_CUMULATIVE_ENERGY_WHEN_CHARGING 0x9243
#define PID_POLL_CUMULATIVE_ENERGY_WHEN_DISCHARGING 0x9245
#define PID_POLL_CUMULATIVE_ENERGY_IN_REGEN 0x9247
/*
#define PID_POLL_UNKNOWNx 0x9256
#define PID_POLL_UNKNOWNx 0x9261
#define PID_POLL_UNKNOWN7 0x9284
#define PID_POLL_UNKNOWNx 0xF012
#define PID_POLL_UNKNOWNx 0xF1A0
#define PID_POLL_UNKNOWNx 0xF182
#define PID_POLL_UNKNOWNx 0xF187
#define PID_POLL_UNKNOWNx 0xF188
#define PID_POLL_UNKNOWNx 0xF18A
#define PID_POLL_UNKNOWNx 0xF18C
#define PID_POLL_UNKNOWNx 0xF191
#define PID_POLL_UNKNOWNx 0xF194
#define PID_POLL_UNKNOWNx 0xF195
*/
class CmfaEvBattery : 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);
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
private:
uint16_t rescale_raw_SOC(uint32_t raw_SOC);
static const int MAX_PACK_VOLTAGE_DV = 3040; // 5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 2185;
static const int MAX_CELL_DEVIATION_MV = 100;
static const int MAX_CELL_VOLTAGE_MV = 4250; // Emergency stop if above
static const int MIN_CELL_VOLTAGE_MV = 2700; // Emergency stop if below
// OBD2 PID polls
static const int PID_POLL_SOCZ = 0x9001;
static const int PID_POLL_USOC = 0x9002;
static const int PID_POLL_SOH_AVERAGE = 0x9003;
static const int PID_POLL_AVERAGE_VOLTAGE_OF_CELLS = 0x9006;
static const int PID_POLL_HIGHEST_CELL_VOLTAGE = 0x9007;
static const int PID_POLL_CELL_NUMBER_HIGHEST_VOLTAGE = 0x9008;
static const int PID_POLL_LOWEST_CELL_VOLTAGE = 0x9009;
static const int PID_POLL_CELL_NUMBER_LOWEST_VOLTAGE = 0x900A;
static const int PID_POLL_CURRENT_OFFSET = 0x900C;
static const int PID_POLL_INSTANT_CURRENT = 0x900D;
static const int PID_POLL_MAX_REGEN = 0x900E;
static const int PID_POLL_MAX_DISCHARGE_POWER = 0x900F;
static const int PID_POLL_12V_BATTERY = 0x9011;
static const int PID_POLL_AVERAGE_TEMPERATURE = 0x9012;
static const int PID_POLL_MIN_TEMPERATURE = 0x9013;
static const int PID_POLL_MAX_TEMPERATURE = 0x9014;
static const int PID_POLL_MAX_CHARGE_POWER = 0x9018;
static const int PID_POLL_END_OF_CHARGE_FLAG = 0x9019;
static const int PID_POLL_INTERLOCK_FLAG = 0x901A;
static const int PID_POLL_BATTERY_IDENTIFICATION = 0x901B;
static const int PID_POLL_CELL_1 = 0x9021;
static const int PID_POLL_CELL_2 = 0x9022;
static const int PID_POLL_CELL_3 = 0x9023;
static const int PID_POLL_CELL_4 = 0x9024;
static const int PID_POLL_CELL_5 = 0x9025;
static const int PID_POLL_CELL_6 = 0x9026;
static const int PID_POLL_CELL_7 = 0x9027;
static const int PID_POLL_CELL_8 = 0x9028;
static const int PID_POLL_CELL_9 = 0x9029;
static const int PID_POLL_CELL_10 = 0x902A;
static const int PID_POLL_CELL_11 = 0x902B;
static const int PID_POLL_CELL_12 = 0x902C;
static const int PID_POLL_CELL_13 = 0x902D;
static const int PID_POLL_CELL_14 = 0x902E;
static const int PID_POLL_CELL_15 = 0x902F;
static const int PID_POLL_CELL_16 = 0x9030;
static const int PID_POLL_CELL_17 = 0x9031;
static const int PID_POLL_CELL_18 = 0x9032;
static const int PID_POLL_CELL_19 = 0x9033;
static const int PID_POLL_CELL_20 = 0x9034;
static const int PID_POLL_CELL_21 = 0x9035;
static const int PID_POLL_CELL_22 = 0x9036;
static const int PID_POLL_CELL_23 = 0x9037;
static const int PID_POLL_CELL_24 = 0x9038;
static const int PID_POLL_CELL_25 = 0x9039;
static const int PID_POLL_CELL_26 = 0x903A;
static const int PID_POLL_CELL_27 = 0x903B;
static const int PID_POLL_CELL_28 = 0x903C;
static const int PID_POLL_CELL_29 = 0x903D;
static const int PID_POLL_CELL_30 = 0x903E;
static const int PID_POLL_CELL_31 = 0x903F;
static const int PID_POLL_DIDS_SUPPORTED_IN_RANGE_9041_9060 = 0x9040;
static const int PID_POLL_CELL_32 = 0x9041;
static const int PID_POLL_CELL_33 = 0x9042;
static const int PID_POLL_CELL_34 = 0x9043;
static const int PID_POLL_CELL_35 = 0x9044;
static const int PID_POLL_CELL_36 = 0x9045;
static const int PID_POLL_CELL_37 = 0x9046;
static const int PID_POLL_CELL_38 = 0x9047;
static const int PID_POLL_CELL_39 = 0x9048;
static const int PID_POLL_CELL_40 = 0x9049;
static const int PID_POLL_CELL_41 = 0x904A;
static const int PID_POLL_CELL_42 = 0x904B;
static const int PID_POLL_CELL_43 = 0x904C;
static const int PID_POLL_CELL_44 = 0x904D;
static const int PID_POLL_CELL_45 = 0x904E;
static const int PID_POLL_CELL_46 = 0x904F;
static const int PID_POLL_CELL_47 = 0x9050;
static const int PID_POLL_CELL_48 = 0x9051;
static const int PID_POLL_CELL_49 = 0x9052;
static const int PID_POLL_CELL_50 = 0x9053;
static const int PID_POLL_CELL_51 = 0x9054;
static const int PID_POLL_CELL_52 = 0x9055;
static const int PID_POLL_CELL_53 = 0x9056;
static const int PID_POLL_CELL_54 = 0x9057;
static const int PID_POLL_CELL_55 = 0x9058;
static const int PID_POLL_CELL_56 = 0x9059;
static const int PID_POLL_CELL_57 = 0x905A;
static const int PID_POLL_CELL_58 = 0x905B;
static const int PID_POLL_CELL_59 = 0x905C;
static const int PID_POLL_CELL_60 = 0x905D;
static const int PID_POLL_CELL_61 = 0x905E;
static const int PID_POLL_CELL_62 = 0x905F;
static const int PID_POLL_DIDS_SUPPORTED_IN_RANGE_9061_9080 = 0x9060;
static const int PID_POLL_CELL_63 = 0x9061;
static const int PID_POLL_CELL_64 = 0x9062;
static const int PID_POLL_CELL_65 = 0x9063;
static const int PID_POLL_CELL_66 = 0x9064;
static const int PID_POLL_CELL_67 = 0x9065;
static const int PID_POLL_CELL_68 = 0x9066;
static const int PID_POLL_CELL_69 = 0x9067;
static const int PID_POLL_CELL_70 = 0x9068;
static const int PID_POLL_CELL_71 = 0x9069;
static const int PID_POLL_CELL_72 = 0x906A;
static const int PID_POLL_SOH_AVAILABLE_POWER_CALCULATION = 0x91BC;
static const int PID_POLL_SOH_GENERATED_POWER_CALCULATION = 0x91BD;
static const int PID_POLL_CUMULATIVE_ENERGY_WHEN_CHARGING = 0x9243;
static const int PID_POLL_CUMULATIVE_ENERGY_WHEN_DISCHARGING = 0x9245;
static const int PID_POLL_CUMULATIVE_ENERGY_IN_REGEN = 0x9247;
CAN_frame CMFA_1EA = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x1EA, .data = {0x00}};
CAN_frame CMFA_125 = {.FD = false,
.ext_ID = false,
.DLC = 7,
.ID = 0x125,
.data = {0x7D, 0x7D, 0x7D, 0x07, 0x82, 0x6A, 0x8A}};
CAN_frame CMFA_134 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x134,
.data = {0x90, 0x8A, 0x7E, 0x3E, 0xB2, 0x4C, 0x80, 0x00}};
CAN_frame CMFA_135 = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x135, .data = {0xD5, 0x85, 0x38, 0x80, 0x01}};
CAN_frame CMFA_3D3 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x3D3,
.data = {0x47, 0x30, 0x00, 0x02, 0x5D, 0x80, 0x5D, 0xE7}};
CAN_frame CMFA_59B = {.FD = false, .ext_ID = false, .DLC = 3, .ID = 0x59B, .data = {0x00, 0x02, 0x00}};
CAN_frame CMFA_ACK = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x79B,
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame CMFA_POLLING_FRAME = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x79B,
.data = {0x03, 0x22, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00}};
bool end_of_charge = false;
bool interlock_flag = false;
uint16_t soc_z = 0;
uint16_t soc_u = 0;
uint16_t max_regen_power = 0;
uint16_t max_discharge_power = 0;
int16_t average_temperature = 0;
int16_t minimum_temperature = 0;
int16_t maximum_temperature = 0;
uint16_t maximum_charge_power = 0;
uint16_t SOH_available_power = 0;
uint16_t SOH_generated_power = 0;
uint32_t average_voltage_of_cells = 270000;
uint16_t highest_cell_voltage_mv = 3700;
uint16_t lowest_cell_voltage_mv = 3700;
uint16_t lead_acid_voltage = 12000;
uint8_t highest_cell_voltage_number = 0;
uint8_t lowest_cell_voltage_number = 0;
uint64_t cumulative_energy_when_discharging = 0;
uint64_t cumulative_energy_when_charging = 0;
uint64_t cumulative_energy_in_regen = 0;
uint16_t soh_average = 10000;
uint16_t cellvoltages_mv[72];
uint32_t poll_pid = PID_POLL_SOH_AVERAGE;
uint16_t pid_reply = 0;
uint8_t counter_10ms = 0;
uint8_t content_125[16] = {0x07, 0x0C, 0x01, 0x06, 0x0B, 0x00, 0x05, 0x0A,
0x0F, 0x04, 0x09, 0x0E, 0x03, 0x08, 0x0D, 0x02};
uint8_t content_135[16] = {0x85, 0xD5, 0x25, 0x75, 0xC5, 0x15, 0x65, 0xB5,
0x05, 0x55, 0xA5, 0xF5, 0x45, 0x95, 0xE5, 0x35};
unsigned long previousMillis200ms = 0;
unsigned long previousMillis100ms = 0;
unsigned long previousMillis10ms = 0;
static const int MAXSOC = 9000; //90.00 Raw SOC displays this value when battery is at 100%
static const int MINSOC = 500; //5.00 Raw SOC displays this value when battery is at 0%
uint8_t heartbeat = 0; //Alternates between 0x55 and 0xAA every 5th frame
uint8_t heartbeat2 = 0; //Alternates between 0x55 and 0xAA every 5th frame
uint32_t SOC_raw = 0;
uint16_t SOH = 99;
int16_t current = 0;
uint16_t pack_voltage = 2700;
int16_t highest_cell_temperature = 0;
int16_t lowest_cell_temperature = 0;
uint32_t discharge_power_w = 0;
uint32_t charge_power_w = 0;
};
#endif

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;
if (millis() - lastPacket > 60) {
if (currentMillis - lastPacket > 60) {
lastPacket = currentMillis;
uint8_t tx_buff[13] = {0};
tx_buff[0] = 0xA5;
tx_buff[1] = 0x40;
tx_buff[2] = nextCommand;
tx_buff[3] = 8;
tx_buff[12] = calculate_checksum(tx_buff);
#ifdef DEBUG_VIA_USB
dump_buff("transmitting: ", tx_buff, 13);
#endif
Serial2.write(tx_buff, 13);
lastPacket = millis();
nextCommand++;
if (nextCommand > 0x98)
nextCommand = 0x90;

View file

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

View file

@ -1,55 +1,39 @@
#include "../include.h"
#ifdef STELLANTIS_ECMP_BATTERY
#include <algorithm> // For std::min and std::max
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For More Battery Info page
#include "../devboard/utils/events.h"
#include "ECMP-BATTERY.h"
/* TODO:
This integration is still ongoing. Here is what still needs to be done in order to use this battery type
- Find SOC%
- Find battery voltage
- Find current value
- Find/estimate charge/discharge limits
- Find temperature
- Figure out contactor closing
- Which CAN messages need to be sent towards the battery?
- Handle 54/70kWh cellcounting properly
*/
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
void EcmpBattery::update_values() {
//Actual content messages
CAN_frame ECMP_XXX = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x301,
.data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
static uint16_t battery_voltage = 37000;
static uint16_t battery_soc = 0;
static uint16_t cellvoltages[108];
void update_values_battery() {
datalayer.battery.status.real_soc = battery_soc * 100;
datalayer.battery.status.real_soc = battery_soc * 10;
datalayer.battery.status.soh_pptt;
datalayer.battery.status.voltage_dV = (battery_voltage / 10);
datalayer.battery.status.voltage_dV = battery_voltage * 10;
datalayer.battery.status.current_dA;
datalayer.battery.status.current_dA = battery_current * 10;
datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
datalayer.battery.status.max_charge_power_W;
datalayer.battery.status.max_charge_power_W = battery_AllowedMaxChargeCurrent * battery_voltage;
datalayer.battery.status.max_discharge_power_W;
datalayer.battery.status.max_discharge_power_W = battery_AllowedMaxDischargeCurrent * battery_voltage;
datalayer.battery.status.temperature_min_dC;
datalayer.battery.status.temperature_min_dC = battery_lowestTemperature * 10;
datalayer.battery.status.temperature_max_dC;
datalayer.battery.status.temperature_max_dC = battery_highestTemperature * 10;
// Initialize min and max, lets find which cells are min and max!
uint16_t min_cell_mv_value = std::numeric_limits<uint16_t>::max();
@ -70,25 +54,46 @@ void update_values_battery() {
datalayer.battery.status.cell_min_voltage_mV = min_cell_mv_value;
datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value;
// Update extended datalayer (More Battery Info page)
datalayer_extended.stellantisECMP.MainConnectorState = battery_MainConnectorState;
datalayer_extended.stellantisECMP.InsulationResistance = battery_insulationResistanceKOhm;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) {
case 0x125:
case 0x125: //Common
battery_soc = (rx_frame.data.u8[0] << 2) |
(rx_frame.data.u8[1] >> 6); // Byte1, bit 7 length 10 (0x3FE when abnormal) (0-1000 ppt)
battery_MainConnectorState = ((rx_frame.data.u8[2] & 0x30) >>
4); //Byte2 , bit 4, length 2 ((00 contactors open, 01 precharged, 11 invalid))
battery_voltage =
(rx_frame.data.u8[3] << 1) | (rx_frame.data.u8[4] >> 7); //Byte 4, bit 7, length 9 (0x1FE if invalid)
battery_current = (((rx_frame.data.u8[4] & 0x7F) << 5) | (rx_frame.data.u8[5] >> 3)) -
600; // Byte5, Bit 3 length 12 (0xFFE when abnormal) (-600 to 600 , offset -600)
//battery_RelayOpenRequest = // Byte 5, bit 6, length 1 (0 no request, 1 battery requests contactor opening)
//Stellantis doc seems wrong, could Byte5 be misspelled as Byte2? Bit will otherwise collide with battery_current
break;
case 0x127:
case 0x127: //DFM specific
battery_AllowedMaxChargeCurrent =
(rx_frame.data.u8[0] << 2) |
((rx_frame.data.u8[1] & 0xC0) >> 6); //Byte 1, bit 7, length 10 (0-600A) [0x3FF if invalid]
battery_AllowedMaxDischargeCurrent =
((rx_frame.data.u8[2] & 0x3F) << 4) |
(rx_frame.data.u8[3] >> 4); //Byte 2, bit 5, length 10 (0-600A) [0x3FF if invalid]
break;
case 0x129:
case 0x129: //PSA specific
break;
case 0x31B:
break;
case 0x358:
case 0x358: //Common
battery_highestTemperature = rx_frame.data.u8[6] - 40;
battery_lowestTemperature = rx_frame.data.u8[7] - 40;
break;
case 0x359:
break;
case 0x361:
battery_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case 0x362:
break;
@ -98,8 +103,9 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
break;
case 0x594:
break;
case 0x6D0:
battery_soc = (100 - rx_frame.data.u8[0]);
case 0x6D0: //Common
battery_insulationResistanceKOhm =
(rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; //Byte 2, bit 7, length 16 (0-60000 kOhm)
break;
case 0x6D1:
break;
@ -288,20 +294,41 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
// Send 1s CAN Message
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
previousMillis1000 = currentMillis;
void EcmpBattery::transmit_can(unsigned long currentMillis) {
// Send 20ms CAN Message
if (currentMillis - previousMillis20 >= INTERVAL_20_MS) {
previousMillis20 = currentMillis;
counter_20ms = (counter_20ms + 1) % 16;
if (datalayer.battery.status.bms_status == FAULT) {
//Open contactors!
ECMP_0F0.data.u8[1] = 0x00;
ECMP_0F0.data.u8[7] = data_0F0_00[counter_20ms];
} else { // Not in faulted mode, Close contactors!
ECMP_0F0.data.u8[1] = 0x20;
ECMP_0F0.data.u8[7] = data_0F0_20[counter_20ms];
}
transmit_can_frame(&ECMP_0F0, can_config.battery); //Common!
}
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
transmit_can_frame(&ECMP_382, can_config.battery); //PSA Specific!
}
}
void setup_battery(void) { // Performs one time setup at startup
#ifdef DEBUG_VIA_USB
Serial.println("ECMP battery selected");
#endif
void EcmpBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Stellantis ECMP battery", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 108;
datalayer.battery.info.max_design_voltage_dV = 4546; // 454.6V, charging over this is not possible
datalayer.battery.info.min_design_voltage_dV = 3210; // 321.0V, under this, discharging further is disabled
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;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
datalayer.system.status.battery_allows_contactor_closing = true;
}

View file

@ -3,10 +3,56 @@
#include <Arduino.h>
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_CELL_DEVIATION_MV 250
#include "CanBattery.h"
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS EcmpBattery
class EcmpBattery : 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:
static const int MAX_PACK_VOLTAGE_DV = 4546;
static const int MIN_PACK_VOLTAGE_DV = 3210;
static const int MAX_CELL_DEVIATION_MV = 100;
static const int MAX_CELL_VOLTAGE_MV = 4250;
static const int MIN_CELL_VOLTAGE_MV = 2700;
bool battery_RelayOpenRequest = false;
uint8_t counter_20ms = 0;
uint8_t battery_MainConnectorState = 0;
int16_t battery_current = 0;
uint16_t battery_voltage = 370;
uint16_t battery_soc = 0;
uint16_t cellvoltages[108];
uint16_t battery_AllowedMaxChargeCurrent = 0;
uint16_t battery_AllowedMaxDischargeCurrent = 0;
uint16_t battery_insulationResistanceKOhm = 0;
int16_t battery_highestTemperature = 0;
int16_t battery_lowestTemperature = 0;
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was sent
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
CAN_frame ECMP_382 = {
.FD = false, //BSI_Info (VCU) PSA specific
.ext_ID = false,
.DLC = 8,
.ID = 0x382,
.data = {0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //09 20 on AC charge. 0A 20 on DC charge
CAN_frame ECMP_0F0 = {.FD = false, //VCU (Common)
.ext_ID = false,
.DLC = 8,
.ID = 0x0F0,
.data = {0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}};
uint8_t data_0F0_20[16] = {0xFF, 0x0E, 0x1D, 0x2C, 0x3B, 0x4A, 0x59, 0x68,
0x77, 0x86, 0x95, 0xA4, 0xB3, 0xC2, 0xD1, 0xE0};
uint8_t data_0F0_00[16] = {0xF1, 0x00, 0x1F, 0x2E, 0x3D, 0x4C, 0x5B, 0x6A,
0x79, 0x88, 0x97, 0xA6, 0xB5, 0xC4, 0xD3, 0xE2};
};
#endif

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

@ -1,14 +1,11 @@
#include "../include.h"
#ifdef KIA_E_GMP_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "KIA-E-GMP-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis200ms = 0; // will store last time a 200ms CAN Message was send
static unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send
const unsigned char crc8_table[256] =
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0,
@ -27,42 +24,6 @@ const unsigned char crc8_table[256] =
0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C, 0x97, 0x8A, 0xAD, 0xB0,
0xE3, 0xFE, 0xD9, 0xC4};
static uint16_t inverterVoltageFrameHigh = 0;
static uint16_t inverterVoltage = 0;
static uint16_t soc_calculated = 0;
static uint16_t SOC_BMS = 0;
static uint16_t SOC_Display = 0;
static uint16_t SOC_estimated_lowest = 0;
static uint16_t SOC_estimated_highest = 0;
static uint16_t batterySOH = 1000;
static uint16_t CellVoltMax_mV = 3700;
static uint16_t CellVoltMin_mV = 3700;
static uint16_t batteryVoltage = 6700;
static int16_t leadAcidBatteryVoltage = 120;
static int16_t batteryAmps = 0;
static int16_t temperatureMax = 0;
static int16_t temperatureMin = 0;
static int16_t allowedDischargePower = 0;
static int16_t allowedChargePower = 0;
static int16_t poll_data_pid = 0;
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 bool startedUp = false;
static bool ok_start_polling_battery = false;
static uint8_t counter_200 = 0;
static uint8_t KIA_7E4_COUNTER = 0x01;
static int8_t temperature_water_inlet = 0;
static int8_t powerRelayTemperature = 0;
static int8_t heatertemp = 0;
static bool set_voltage_limits = false;
static uint8_t ticks_200ms_counter = 0;
static uint8_t EGMP_1CF_counter = 0;
static uint8_t EGMP_3XF_counter = 0;
// Define the data points for %SOC depending on cell voltage
const uint8_t numPoints = 100;
@ -107,7 +68,7 @@ uint16_t estimateSOCFromCell(uint16_t cellVoltage) {
}
// Simplified version of the pack-based SOC estimation with compensation
uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps) {
uint16_t KiaEGmpBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps) {
// If cell count is still the default 192 but we haven't confirmed it yet
if (!set_voltage_limits && cellCount == 192) {
// Fall back to BMS-reported SOC while cell count is uncertain
@ -161,7 +122,7 @@ uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high) {
}
/* These messages are needed for contactor closing */
unsigned long startMillis;
unsigned long startMillis = 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,
45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70,
@ -695,7 +656,7 @@ void set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell)
}
}
void set_voltage_minmax_limits() {
void KiaEGmpBattery::set_voltage_minmax_limits() {
uint8_t valid_cell_count = 0;
for (int i = 0; i < MAX_AMOUNT_CELLS; ++i) {
@ -729,7 +690,8 @@ static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_
return crc;
}
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
void KiaEGmpBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
#ifdef ESTIMATE_SOC_FROM_CELLVOLTAGE
// Use the simplified pack-based SOC estimation with proper compensation
@ -850,7 +812,7 @@ void update_values_battery() { //This function maps all the values fetched via
#endif
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void KiaEGmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
startedUp = true;
switch (rx_frame.ID) {
case 0x055:
@ -1082,7 +1044,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void KiaEGmpBattery::transmit_can(unsigned long currentMillis) {
if (startedUp) {
//Send Contactor closing message loop
// Check if we still have messages to send
@ -1100,7 +1062,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
if (messageIndex >= 63) {
startMillis = millis(); // Start over!
startMillis = currentMillis; // Start over!
messageIndex = 0;
}
@ -1128,12 +1090,9 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void KiaEGmpBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai EGMP platform", 63);
datalayer.system.info.battery_protocol[63] = '\0';
startMillis = millis(); // Record the starting time
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.number_of_cells = 192; // TODO: will vary depending on battery
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;

View file

@ -3,27 +3,78 @@
#include <Arduino.h>
#include "../include.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "CanBattery.h"
extern ACAN2517FD canfd;
#define ESTIMATE_SOC_FROM_CELLVOLTAGE
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 8064 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 4320
#define MAX_CELL_DEVIATION_MV 150
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2950 //Battery is put into emergency stop if one cell goes below this value
#define MAXCHARGEPOWERALLOWED 10000
#define MAXDISCHARGEPOWERALLOWED 10000
#define RAMPDOWN_SOC 9000 // 90.00 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 SELECTED_BATTERY_CLASS KiaEGmpBattery
// Used for SoC compensation - Define internal resistance value in milliohms for the entire pack
// How to calculate: voltage_drop_under_known_load [Volts] / load [Amps] = Resistance
#define PACK_INTERNAL_RESISTANCE_MOHM 200 // 200 milliohms for the whole pack
class KiaEGmpBattery : 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);
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
private:
uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps);
void set_voltage_minmax_limits();
static const int MAX_PACK_VOLTAGE_DV = 8064; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 4320;
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
static const int MAXCHARGEPOWERALLOWED = 10000;
static const int MAXDISCHARGEPOWERALLOWED = 10000;
static const int RAMPDOWN_SOC = 9000; // 90.00 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
// Used for SoC compensation - Define internal resistance value in milliohms for the entire pack
// How to calculate: voltage_drop_under_known_load [Volts] / load [Amps] = Resistance
static const int PACK_INTERNAL_RESISTANCE_MOHM = 200; // 200 milliohms for the whole pack
unsigned long previousMillis200ms = 0; // will store last time a 200ms CAN Message was send
unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send
uint16_t inverterVoltageFrameHigh = 0;
uint16_t inverterVoltage = 0;
uint16_t soc_calculated = 0;
uint16_t SOC_BMS = 0;
uint16_t SOC_Display = 0;
uint16_t SOC_estimated_lowest = 0;
uint16_t SOC_estimated_highest = 0;
uint16_t batterySOH = 1000;
uint16_t CellVoltMax_mV = 3700;
uint16_t CellVoltMin_mV = 3700;
uint16_t batteryVoltage = 6700;
int16_t leadAcidBatteryVoltage = 120;
int16_t batteryAmps = 0;
int16_t temperatureMax = 0;
int16_t temperatureMin = 0;
int16_t allowedDischargePower = 0;
int16_t allowedChargePower = 0;
int16_t poll_data_pid = 0;
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;
bool startedUp = false;
bool ok_start_polling_battery = false;
uint8_t counter_200 = 0;
uint8_t KIA_7E4_COUNTER = 0x01;
int8_t temperature_water_inlet = 0;
int8_t powerRelayTemperature = 0;
int8_t heatertemp = 0;
bool set_voltage_limits = false;
uint8_t ticks_200ms_counter = 0;
uint8_t EGMP_1CF_counter = 0;
uint8_t EGMP_3XF_counter = 0;
};
#endif

View file

@ -1,176 +1,36 @@
#include "../include.h"
#ifdef KIA_HYUNDAI_64_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "KIA-HYUNDAI-64-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis10 = 0; // will store last time a 10s CAN Message was send
void KiaHyundai64Battery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
static uint16_t soc_calculated = 0;
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;
datalayer_battery->status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
#ifdef DOUBLE_BATTERY
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
datalayer_battery->status.soh_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
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
datalayer_battery->status.voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0)
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>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
datalayer_battery->status.temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C
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.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;
datalayer_battery->status.cell_min_voltage_mV = CellVoltMin_mV;
if (waterleakageSensor == 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
datalayer_extended.KiaHyundai64.total_cell_count = datalayer.battery.info.number_of_cells;
datalayer_extended.KiaHyundai64.battery_12V = leadAcidBatteryVoltage;
datalayer_extended.KiaHyundai64.waterleakageSensor = waterleakageSensor;
datalayer_extended.KiaHyundai64.temperature_water_inlet = temperature_water_inlet;
datalayer_extended.KiaHyundai64.powerRelayTemperature = powerRelayTemperature * 2;
datalayer_extended.KiaHyundai64.batteryManagementMode = batteryManagementMode;
datalayer_extended.KiaHyundai64.BMS_ign = BMS_ign;
datalayer_extended.KiaHyundai64.batteryRelay = batteryRelay;
datalayer_battery_extended->total_cell_count = datalayer_battery->info.number_of_cells;
datalayer_battery_extended->battery_12V = leadAcidBatteryVoltage;
datalayer_battery_extended->waterleakageSensor = waterleakageSensor;
datalayer_battery_extended->temperature_water_inlet = temperature_water_inlet;
datalayer_battery_extended->powerRelayTemperature = powerRelayTemperature * 2;
datalayer_battery_extended->batteryManagementMode = batteryManagementMode;
datalayer_battery_extended->BMS_ign = BMS_ign;
datalayer_battery_extended->batteryRelay = batteryRelay;
//Perform logging if configured to do so
#ifdef DEBUG_LOG
@ -205,7 +65,7 @@ void update_values_battery() { //This function maps all the values fetched via
logging.print(" Amps | ");
logging.print((uint16_t)batteryVoltage / 10.0, 1);
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.print("Allowed Charge ");
logging.print((uint16_t)allowedChargePower * 10);
@ -251,32 +111,32 @@ void update_values_battery() { //This function maps all the values fetched via
#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 (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
if (datalayer.battery.status.cell_voltages_mV[97] > 0) {
datalayer.battery.info.number_of_cells = 98;
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.total_capacity_Wh = 64000;
if (datalayer_battery->status.cell_voltages_mV[97] > 0) {
datalayer_battery->info.number_of_cells = 98;
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.total_capacity_Wh = 64000;
} else {
datalayer.battery.info.number_of_cells = 90;
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.total_capacity_Wh = 40000;
datalayer_battery->info.number_of_cells = 90;
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.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) {
case 0x4DE:
startedUp = true;
break;
case 0x542: //BMS SOC
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 )
break;
case 0x594:
@ -321,17 +181,17 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
poll_data_pid++;
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) {
transmit_can_frame(&KIA64_7E4_id2, can_config.battery);
transmit_can_frame(&KIA64_7E4_id2, can_interface);
} 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) {
transmit_can_frame(&KIA64_7E4_id4, can_config.battery);
transmit_can_frame(&KIA64_7E4_id4, can_interface);
} 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) {
transmit_can_frame(&KIA64_7E4_id6, can_config.battery);
transmit_can_frame(&KIA64_7E4_id6, can_interface);
}
}
break;
@ -340,7 +200,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
case 0x10: //"PID Header"
if (rx_frame.data.u8[4] == poll_data_pid) {
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;
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
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();
break;
@ -533,398 +393,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
#ifdef DOUBLE_BATTERY
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) {
void KiaHyundai64Battery::transmit_can(unsigned long currentMillis) {
if (!startedUp) {
return; // Don't send any CAN messages towards battery until it has started up
@ -934,136 +403,77 @@ void transmit_can_battery(unsigned long currentMillis) {
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
transmit_can_frame(&KIA64_553, can_config.battery);
transmit_can_frame(&KIA64_57F, can_config.battery);
transmit_can_frame(&KIA64_2A1, can_config.battery);
#ifdef DOUBLE_BATTERY
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);
if (contactor_closing_allowed == nullptr || *contactor_closing_allowed) {
transmit_can_frame(&KIA64_553, can_interface);
transmit_can_frame(&KIA64_57F, can_interface);
transmit_can_frame(&KIA64_2A1, can_interface);
}
#endif // DOUBLE_BATTERY
}
// Send 10ms CAN Message
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
previousMillis10 = currentMillis;
switch (counter_200) {
case 0:
KIA_HYUNDAI_200.data.u8[5] = 0x17;
++counter_200;
break;
case 1:
KIA_HYUNDAI_200.data.u8[5] = 0x57;
++counter_200;
break;
case 2:
KIA_HYUNDAI_200.data.u8[5] = 0x97;
++counter_200;
break;
case 3:
KIA_HYUNDAI_200.data.u8[5] = 0xD7;
++counter_200;
break;
case 4:
KIA_HYUNDAI_200.data.u8[3] = 0x10;
KIA_HYUNDAI_200.data.u8[5] = 0xFF;
++counter_200;
break;
case 5:
KIA_HYUNDAI_200.data.u8[5] = 0x3B;
++counter_200;
break;
case 6:
KIA_HYUNDAI_200.data.u8[5] = 0x7B;
++counter_200;
break;
case 7:
KIA_HYUNDAI_200.data.u8[5] = 0xBB;
++counter_200;
break;
case 8:
KIA_HYUNDAI_200.data.u8[5] = 0xFB;
counter_200 = 5;
break;
}
if (contactor_closing_allowed == nullptr || *contactor_closing_allowed) {
transmit_can_frame(&KIA_HYUNDAI_200, can_config.battery);
transmit_can_frame(&KIA_HYUNDAI_523, can_config.battery);
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) {
switch (counter_200) {
case 0:
KIA_HYUNDAI_200_2.data.u8[5] = 0x17;
++counter_200_2;
KIA_HYUNDAI_200.data.u8[5] = 0x17;
++counter_200;
break;
case 1:
KIA_HYUNDAI_200_2.data.u8[5] = 0x57;
++counter_200_2;
KIA_HYUNDAI_200.data.u8[5] = 0x57;
++counter_200;
break;
case 2:
KIA_HYUNDAI_200_2.data.u8[5] = 0x97;
++counter_200_2;
KIA_HYUNDAI_200.data.u8[5] = 0x97;
++counter_200;
break;
case 3:
KIA_HYUNDAI_200_2.data.u8[5] = 0xD7;
++counter_200_2;
KIA_HYUNDAI_200.data.u8[5] = 0xD7;
++counter_200;
break;
case 4:
KIA_HYUNDAI_200_2.data.u8[3] = 0x10;
KIA_HYUNDAI_200_2.data.u8[5] = 0xFF;
++counter_200_2;
KIA_HYUNDAI_200.data.u8[3] = 0x10;
KIA_HYUNDAI_200.data.u8[5] = 0xFF;
++counter_200;
break;
case 5:
KIA_HYUNDAI_200_2.data.u8[5] = 0x3B;
++counter_200_2;
KIA_HYUNDAI_200.data.u8[5] = 0x3B;
++counter_200;
break;
case 6:
KIA_HYUNDAI_200_2.data.u8[5] = 0x7B;
++counter_200_2;
KIA_HYUNDAI_200.data.u8[5] = 0x7B;
++counter_200;
break;
case 7:
KIA_HYUNDAI_200_2.data.u8[5] = 0xBB;
++counter_200_2;
KIA_HYUNDAI_200.data.u8[5] = 0xBB;
++counter_200;
break;
case 8:
KIA_HYUNDAI_200_2.data.u8[5] = 0xFB;
counter_200_2 = 5;
KIA_HYUNDAI_200.data.u8[5] = 0xFB;
counter_200 = 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);
transmit_can_frame(&KIA_HYUNDAI_200, can_interface);
transmit_can_frame(&KIA_HYUNDAI_523, can_interface);
transmit_can_frame(&KIA_HYUNDAI_524, can_interface);
}
#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);
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.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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
#ifdef DOUBLE_BATTERY
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
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.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;
if (allows_contactor_closing) {
*allows_contactor_closing = true;
}
}
#endif

View file

@ -1,19 +1,164 @@
#ifndef KIA_HYUNDAI_64_BATTERY_H
#define KIA_HYUNDAI_64_BATTERY_H
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_98S_DV 4110 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_98S_DV 2800
#define MAX_PACK_VOLTAGE_90S_DV 3870
#define MIN_PACK_VOLTAGE_90S_DV 2250
#define MAX_CELL_DEVIATION_MV 150
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2950 //Battery is put into emergency stop if one cell goes below this value
#define SELECTED_BATTERY_CLASS KiaHyundai64Battery
void setup_battery(void);
void update_number_of_cells();
void transmit_can_frame(CAN_frame* tx_frame, int interface);
class KiaHyundai64Battery : public CanBattery {
public:
// Use this constructor for the second battery.
KiaHyundai64Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_KIAHYUNDAI64* extended_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;
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 update_number_of_cells();
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

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef KIA_HYUNDAI_HYBRID_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
@ -9,51 +10,8 @@
- We need to figure out how to keep the BMS alive. Most likely we need to send a specific CAN message
*/
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis1000 = 0; // will store last time a 100ms CAN Message was send
static uint16_t SOC = 0;
static uint16_t SOC_display = 0;
static bool interlock_missing = false;
static int16_t battery_current = 0;
static uint8_t battery_current_high_byte = 0;
static uint16_t battery_voltage = 0;
static uint32_t available_charge_power = 0;
static uint32_t available_discharge_power = 0;
static int8_t battery_module_max_temperature = 0;
static int8_t battery_module_min_temperature = 0;
static uint8_t poll_data_pid = 0;
static uint16_t cellvoltages_mv[98];
static uint16_t min_cell_voltage_mv = 3700;
static uint16_t max_cell_voltage_mv = 3700;
CAN_frame KIA_7E4_id1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame KIA_7E4_id2 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x02, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame KIA_7E4_id3 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x02, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame KIA_7E4_id5 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x02, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00}};
CAN_frame KIA_7E4_ack = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4, //Ack frame, correct PID is returned. Flow control message
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
void KiaHyundaiHybridBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
datalayer.battery.status.real_soc = SOC * 50;
@ -86,7 +44,7 @@ void update_values_battery() { //This function maps all the values fetched via
}
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void KiaHyundaiHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) {
case 0x5F1:
@ -230,7 +188,8 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
break;
}
}
void transmit_can_battery(unsigned long currentMillis) {
void KiaHyundaiHybridBattery::transmit_can(unsigned long currentMillis) {
// Send 1000ms CAN Message
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
@ -255,7 +214,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void KiaHyundaiHybridBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai Hybrid", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;

View file

@ -3,14 +3,67 @@
#include <Arduino.h>
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 2550 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 1700
#define MAX_CELL_DEVIATION_MV 100
#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
#include "CanBattery.h"
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS KiaHyundaiHybridBattery
class KiaHyundaiHybridBattery : 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:
static const int MAX_PACK_VOLTAGE_DV = 2550; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 1700;
static const int MAX_CELL_DEVIATION_MV = 100;
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 previousMillis1000 = 0; // will store last time a 100ms CAN Message was send
uint16_t SOC = 0;
uint16_t SOC_display = 0;
bool interlock_missing = false;
int16_t battery_current = 0;
uint8_t battery_current_high_byte = 0;
uint16_t battery_voltage = 0;
uint32_t available_charge_power = 0;
uint32_t available_discharge_power = 0;
int8_t battery_module_max_temperature = 0;
int8_t battery_module_min_temperature = 0;
uint8_t poll_data_pid = 0;
uint16_t cellvoltages_mv[98];
uint16_t min_cell_voltage_mv = 3700;
uint16_t max_cell_voltage_mv = 3700;
CAN_frame KIA_7E4_id1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame KIA_7E4_id2 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x02, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame KIA_7E4_id3 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x02, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame KIA_7E4_id5 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4,
.data = {0x02, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00}};
CAN_frame KIA_7E4_ack = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7E4, //Ack frame, correct PID is returned. Flow control message
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
};
#endif

View file

@ -15,359 +15,20 @@ TODO list
- remaining_capacity_Wh is based on a lower limit of 5% soc. This means that at 5% soc, remaining_capacity_Wh returns 0.
*/
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis10ms = 0; // will store last time a 10ms CAN Message was send
static unsigned long previousMillis20ms = 0; // will store last time a 20ms CAN Message was send
static unsigned long previousMillis40ms = 0; // will store last time a 40ms CAN Message was send
static unsigned long previousMillis50ms = 0; // will store last time a 50ms CAN Message was send
static unsigned long previousMillis100ms = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis200ms = 0; // will store last time a 200ms CAN Message was send
static unsigned long previousMillis500ms = 0; // will store last time a 200ms CAN Message was send
static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send
static bool toggle = false;
static uint8_t counter_1000ms = 0;
static uint8_t counter_200ms = 0;
static uint8_t counter_100ms = 0;
static uint8_t counter_50ms = 0;
static uint8_t counter_40ms = 0;
static uint8_t counter_20ms = 0;
static uint8_t counter_10ms = 0;
static uint8_t counter_040 = 0;
static uint8_t counter_0F7 = 0;
static uint8_t counter_3b5 = 0;
static uint32_t poll_pid = PID_CELLVOLTAGE_CELL_85; // We start here to quickly determine the cell size of the pack.
static bool nof_cells_determined = false;
static uint32_t pid_reply = 0;
static uint16_t battery_soc_polled = 0;
static uint16_t battery_voltage_polled = 1480;
static int16_t battery_current_polled = 0;
static int16_t battery_max_temp = 600;
static int16_t battery_min_temp = 600;
static uint16_t battery_max_charge_voltage = 0;
static uint16_t battery_min_discharge_voltage = 0;
static uint16_t battery_allowed_charge_power = 0;
static uint16_t battery_allowed_discharge_power = 0;
static uint16_t cellvoltages_polled[108];
static uint16_t tempval = 0;
static uint8_t BMS_16A954A6_CRC = 0;
static uint8_t BMS_5A2_counter = 0;
static uint8_t BMS_5CA_counter = 0;
static uint8_t BMS_0CF_counter = 0;
static uint8_t BMS_0C0_counter = 0;
static uint8_t BMS_578_counter = 0;
static uint8_t BMS_16A954A6_counter = 0;
static bool BMS_ext_limits_active =
false; //The current current limits of the HV battery are expanded to start the combustion engine / confirmation of the request
static uint8_t BMS_mode =
0x07; //0: standby; Gates open; Communication active 1: Main contactor closed / HV network activated / normal driving operation
//2: assigned depending on the project (e.g. balancing, extended DC fast charging) //3: external charging
static uint8_t BMS_HVIL_status = 0; //0 init, 1 seated, 2 open, 3 fault
static bool BMS_fault_performance = false; //Error: Battery performance is limited (e.g. due to sensor or fan failure)
static uint16_t BMS_current = 16300;
static bool BMS_fault_emergency_shutdown_crash =
false; //Error: Safety-critical error (crash detection) Battery contactors are already opened / will be opened immediately Signal is read directly by the EMS and initiates an AKS of the PWR and an active discharge of the DC link
static uint32_t BMS_voltage_intermediate = 2000;
static uint32_t BMS_voltage = 1480;
static uint8_t BMS_status_voltage_free =
0; //0=Init, 1=BMS intermediate circuit voltage-free (U_Zwkr < 20V), 2=BMS intermediate circuit not voltage-free (U_Zwkr >/= 25V, hysteresis), 3=Error
static bool BMS_OBD_MIL = false;
static uint8_t BMS_error_status =
0x7; //0 Component_IO, 1 Restricted_CompFkt_Isoerror_I, 2 Restricted_CompFkt_Isoerror_II, 3 Restricted_CompFkt_Interlock, 4 Restricted_CompFkt_SD, 5 Restricted_CompFkt_Performance red, 6 = No component function, 7 = Init
static uint16_t BMS_capacity_ah = 0;
static bool BMS_error_lamp_req = false;
static bool BMS_warning_lamp_req = false;
static uint8_t BMS_Kl30c_Status = 0; // 0 init, 1 closed, 2 open, 3 fault
static bool service_disconnect_switch_missing = false;
static bool pilotline_open = false;
static bool balancing_request = false;
static uint8_t battery_diagnostic = 0;
static uint16_t battery_Wh_left = 0;
static uint16_t battery_Wh_max = 1000;
static uint8_t battery_potential_status = 0;
static uint8_t battery_temperature_warning = 0;
static uint16_t max_discharge_power_watt = 0;
static uint16_t max_discharge_current_amp = 0;
static uint16_t max_charge_power_watt = 0;
static uint16_t max_charge_current_amp = 0;
static uint16_t battery_SOC = 1;
static uint16_t usable_energy_amount_Wh = 0;
static uint8_t status_HV_line = 0; //0 init, 1 No open HV line, 2 open HV line detected, 3 fault
static uint8_t warning_support = 0;
static bool battery_heating_active = false;
static uint16_t power_discharge_percentage = 0;
static uint16_t power_charge_percentage = 0;
static uint16_t actual_battery_voltage = 0;
static uint16_t regen_battery = 0;
static uint16_t energy_extracted_from_battery = 0;
static uint16_t max_fastcharging_current_amp = 0; //maximum DC charging current allowed
static uint8_t BMS_Status_DCLS =
0; //Status of the voltage monitoring on the DC charging interface. 0 inactive, 1 I_O , 2 N_I_O , 3 active
static uint16_t DC_voltage_DCLS =
0; //Factor 1, DC voltage of the charging station. Measurement between the DC HV lines.
static uint16_t DC_voltage_chargeport =
0; //Factor 0.5, Current voltage at the HV battery DC charging terminals; Outlet to the DC charging plug.
static uint8_t BMS_welded_contactors_status =
0; //0: Init no diagnostic result, 1: no contactor welded, 2: at least 1 contactor welded, 3: Protection status detection error
static bool BMS_error_shutdown_request =
false; // Fault: Fault condition, requires battery contactors to be opened internal battery error; Advance notification of an impending opening of the battery contactors by the BMS
static bool BMS_error_shutdown =
false; // Fault: Fault condition, requires battery contactors to be opened Internal battery error, battery contactors opened without notice by the BMS
static uint16_t power_battery_heating_watt = 0;
static uint16_t power_battery_heating_req_watt = 0;
static uint8_t cooling_request =
0; //0 = No cooling, 1 = Light cooling, cabin prio, 2= higher cooling, 3 = immediate cooling, 4 = emergency cooling
static uint8_t heating_request = 0; //0 = init, 1= maintain temp, 2=higher demand, 3 = immediate heat demand
static uint8_t balancing_active = false; //0 = init, 1 active, 2 not active
static bool charging_active = false;
static uint16_t max_energy_Wh = 0;
static uint16_t max_charge_percent = 0;
static uint16_t min_charge_percent = 0;
static uint16_t isolation_resistance_kOhm = 0;
static bool battery_heating_installed = false;
static bool error_NT_circuit = false;
static uint8_t pump_1_control = 0; //0x0D not installed, 0x0E init, 0x0F fault
static uint8_t pump_2_control = 0; //0x0D not installed, 0x0E init, 0x0F fault
static uint8_t target_flow_temperature_C = 0; //*0,5 -40
static uint8_t return_temperature_C = 0; //*0,5 -40
static uint8_t status_valve_1 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
static uint8_t status_valve_2 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
static uint8_t temperature_request =
0; //0 high cooling, 1 medium cooling, 2 low cooling, 3 no temp requirement init, 4 low heating , 5 medium heating, 6 high heating, 7 circulation
static uint16_t performance_index_discharge_peak_temperature_percentage = 0;
static uint16_t performance_index_charge_peak_temperature_percentage = 0;
static uint8_t temperature_status_discharge =
0; //0 init, 1 temp under optimal, 2 temp optimal, 3 temp over optimal, 7 fault
static uint8_t temperature_status_charge =
0; //0 init, 1 temp under optimal, 2 temp optimal, 3 temp over optimal, 7 fault
static uint8_t isolation_fault =
0; //0 init, 1 no iso fault, 2 iso fault threshold1, 3 iso fault threshold2, 4 IWU defective
static uint8_t isolation_status =
0; // 0 init, 1 = larger threshold1, 2 = smaller threshold1 total, 3 = smaller threshold1 intern, 4 = smaller threshold2 total, 5 = smaller threshold2 intern, 6 = no measurement, 7 = measurement active
static uint8_t actual_temperature_highest_C = 0; //*0,5 -40
static uint8_t actual_temperature_lowest_C = 0; //*0,5 -40
static uint16_t actual_cellvoltage_highest_mV = 0; //bias 1000
static uint16_t actual_cellvoltage_lowest_mV = 0; //bias 1000
static uint16_t predicted_power_dyn_standard_watt = 0;
static uint8_t predicted_time_dyn_standard_minutes = 0;
static uint8_t mux = 0;
static uint16_t cellvoltages[160] = {0};
static uint16_t duration_discharge_power_watt = 0;
static uint16_t duration_charge_power_watt = 0;
static uint16_t maximum_voltage = 0;
static uint16_t minimum_voltage = 0;
static uint8_t battery_serialnumber[26];
static uint8_t realtime_overcurrent_monitor = 0;
static uint8_t realtime_CAN_communication_fault = 0;
static uint8_t realtime_overcharge_warning = 0;
static uint8_t realtime_SOC_too_high = 0;
static uint8_t realtime_SOC_too_low = 0;
static uint8_t realtime_SOC_jumping_warning = 0;
static uint8_t realtime_temperature_difference_warning = 0;
static uint8_t realtime_cell_overtemperature_warning = 0;
static uint8_t realtime_cell_undertemperature_warning = 0;
static uint8_t realtime_battery_overvoltage_warning = 0;
static uint8_t realtime_battery_undervoltage_warning = 0;
static uint8_t realtime_cell_overvoltage_warning = 0;
static uint8_t realtime_cell_undervoltage_warning = 0;
static uint8_t realtime_cell_imbalance_warning = 0;
static uint8_t realtime_warning_battery_unathorized = 0;
static bool component_protection_active = false;
static bool shutdown_active = false;
static bool transportation_mode_active = false;
static uint8_t KL15_mode = 0;
static uint8_t bus_knockout_timer = 0;
static uint8_t hybrid_wakeup_reason = 0;
static uint8_t wakeup_type = 0;
static bool instrumentation_cluster_request = false;
static uint8_t seconds = 0;
static uint32_t first_can_msg = 0;
static uint32_t last_can_msg_timestamp = 0;
static bool hv_requested = false;
static int32_t kwh_charge = 0;
static int32_t kwh_discharge = 0;
#define TIME_YEAR 2024
#define TIME_MONTH 8
#define TIME_DAY 20
#define TIME_HOUR 10
#define TIME_MINUTE 5
#define TIME_SECOND 0
#define BMS_TARGET_HV_OFF 0
#define BMS_TARGET_HV_ON 1
#define BMS_TARGET_AC_CHARGING_EXT 3 //(HS + AC_2 contactors closed)
#define BMS_TARGET_AC_CHARGING 4 //(HS + AC contactors closed)
#define BMS_TARGET_DC_CHARGING 6 //(HS + DC contactors closed)
#define BMS_TARGET_INIT 7
#define DC_FASTCHARGE_NO_START_REQUEST 0x00
#define DC_FASTCHARGE_VEHICLE 0x40
#define DC_FASTCHARGE_LS1 0x80
#define DC_FASTCHARGE_LS2 0xC0
CAN_frame MEB_POLLING_FRAME = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1C40007B, // SOC 02 8C
.data = {0x03, 0x22, 0x02, 0x8C, 0x55, 0x55, 0x55, 0x55}};
CAN_frame MEB_ACK_FRAME = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1C40007B, // Ack
.data = {0x30, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55}};
//Messages needed for contactor closing
CAN_frame MEB_040 = {.FD = true, // Airbag
.ext_ID = false,
.DLC = 8,
.ID = 0x040, //Frame5 has HV deactivate request. Needs to be 0x00
.data = {0x7E, 0x83, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00}};
CAN_frame MEB_0C0 = {
.FD = true, // EM1 message
.ext_ID = false,
.DLC = 32,
.ID = 0x0C0, //Frame 5-6 and maybe 7-8 important (external voltage at inverter)
.data = {0x77, 0x0A, 0xFE, 0xE7, 0x7F, 0x10, 0x27, 0x00, 0xE0, 0x7F, 0xFF, 0xF3, 0x3F, 0xFF, 0xF3, 0x3F,
0xFC, 0x0F, 0x00, 0x00, 0xC0, 0xFF, 0xFE, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_0FC = {
.FD = true, //
.ext_ID = false,
.DLC = 48,
.ID = 0x0FC, //This message contains emergency regen request?(byte19), battery needs to see this message
.data = {0x07, 0x08, 0x00, 0x00, 0x7E, 0x00, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFE, 0xFE, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xF4, 0x01, 0x40, 0xFF, 0xEB, 0x7F, 0x0A, 0x88, 0xE3, 0x81, 0xAF, 0x42}};
CAN_frame MEB_6B2 = {.FD = true, // Diagnostics
.ext_ID = false,
.DLC = 8,
.ID = 0x6B2,
.data = {0x6A, 0xA7, 0x37, 0x80, 0xC9, 0xBD, 0xF6, 0xC2}};
CAN_frame MEB_17FC007B_poll = {.FD = true, // Non period request
.ext_ID = true,
.DLC = 8,
.ID = 0x17FC007B,
.data = {0x03, 0x22, 0x1E, 0x3D, 0x55, 0x55, 0x55, 0x55}};
CAN_frame MEB_1A5555A6 = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1A5555A6,
.data = {0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_585 = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x585,
.data = {0xCF, 0x38, 0xAF, 0x5B, 0x25, 0x00, 0x00, 0x00}}; // CF 38 AF 5B 25 00 00 00 in start4.log
// .data = {0xCF, 0x38, 0x20, 0x02, 0x25, 0xF7, 0x30, 0x00}}; // CF 38 AF 5B 25 00 00 00 in start4.log
CAN_frame MEB_5F5 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x5F5,
.data = {0x23, 0x02, 0x39, 0xC0, 0x1B, 0x8B, 0xC8, 0x1B}};
CAN_frame MEB_641 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x641,
.data = {0x37, 0x18, 0x00, 0x00, 0xF0, 0x00, 0xAA, 0x70}};
CAN_frame MEB_3C0 = {.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x3C0,
.data = {0x66, 0x00, 0x00, 0x00}}; // Klemmen_status_01
CAN_frame MEB_0FD = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x0FD, //CRC and counter, otherwise static
.data = {0x5F, 0xD0, 0x1F, 0x81, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_16A954FB = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x16A954FB,
.data = {0x00, 0xC0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_1A555548 = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1A555548,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_1A55552B = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1A55552B,
.data = {0x00, 0x00, 0x00, 0xA0, 0x02, 0x04, 0x00, 0x30}};
CAN_frame MEB_569 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x569, //HVEM
.data = {0x00, 0x00, 0x01, 0x3A, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_16A954B4 = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x16A954B4, //eTM
.data = {0xFE, 0xB6, 0x0D, 0x00, 0x00, 0xD5, 0x48, 0xFD}};
CAN_frame MEB_1B000046 = {.FD = false, // Not FD
.ext_ID = true,
.DLC = 8,
.ID = 0x1B000046, // Klima
.data = {0x00, 0x40, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_1B000010 = {.FD = false, // Not FD
.ext_ID = true,
.DLC = 8,
.ID = 0x1B000010, // Gateway
.data = {0x00, 0x50, 0x08, 0x50, 0x01, 0xFF, 0x30, 0x00}};
CAN_frame MEB_1B0000B9 = {.FD = false, // Not FD
.ext_ID = true,
.DLC = 8,
.ID = 0x1B0000B9, //DC/DC converter
.data = {0x00, 0x40, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_153 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x153, // Static content
.data = {0x00, 0x00, 0x00, 0xFF, 0xEF, 0xFE, 0xFF, 0xFF}};
CAN_frame MEB_5E1 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x5E1, // Static content
.data = {0x7F, 0x2A, 0x00, 0x60, 0xFE, 0x00, 0x00, 0x00}};
CAN_frame MEB_3BE = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x3BE, // CRC, otherwise Static content
.data = {0x57, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x04, 0x40}};
CAN_frame MEB_272 = {.FD = true, //HVLM_14
.ext_ID = false,
.DLC = 8,
.ID = 0x272, // Static content
.data = {0x00, 0x00, 0x00, 0x00, 0x48, 0x08, 0x00, 0x94}};
CAN_frame MEB_503 = {.FD = true, //HVK_01
.ext_ID = false,
.DLC = 8,
.ID = 0x503, // Content varies. Frame1 & 3 has HV req
.data = {0x5D, 0x61, 0x00, 0xFF, 0x7F, 0x80, 0xE3, 0x03}};
CAN_frame MEB_14C = {
.FD = true, //Motor message
.ext_ID = false,
.DLC = 32,
.ID = 0x14C, //CRC needed, static content otherwise
.data = {0x38, 0x0A, 0xFF, 0x01, 0x01, 0xFF, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE,
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x25, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE}};
uint32_t can_msg_received = 0;
#define RX_0x17F0007B 0x0001
#define RX_0x12DD54D0 0x0002
#define RX_0x12DD54D1 0x0004
#define RX_0x12DD54D2 0x0008
#define RX_0x1A555550 0x0010
#define RX_0x1A555551 0x0020
#define RX_0x1A5555B2 0x0040
#define RX_0x16A954A6 0x0080
#define RX_0x1A5555B0 0x0100
#define RX_0x1A5555B1 0x0200
#define RX_0x5A2 0x0400
#define RX_0x5CA 0x0800
#define RX_0x0CF 0x1000
#define RX_DEFAULT 0xE000
const int RX_0x17F0007B = 0x0001;
const int RX_0x12DD54D0 = 0x0002;
const int RX_0x12DD54D1 = 0x0004;
const int RX_0x12DD54D2 = 0x0008;
const int RX_0x1A555550 = 0x0010;
const int RX_0x1A555551 = 0x0020;
const int RX_0x1A5555B2 = 0x0040;
const int RX_0x16A954A6 = 0x0080;
const int RX_0x1A5555B0 = 0x0100;
const int RX_0x1A5555B1 = 0x0200;
const int RX_0x5A2 = 0x0400;
const int RX_0x5CA = 0x0800;
const int RX_0x0CF = 0x1000;
const int RX_DEFAULT = 0xE000;
/** Calculate the CRC checksum for VAG CAN Messages
*
@ -538,7 +199,8 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint32_t address) {
return crc;
}
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
void MebBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
datalayer.battery.status.real_soc = battery_SOC * 5; //*0.05*100
@ -645,7 +307,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer_extended.meb.charging_active = charging_active;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
last_can_msg_timestamp = millis();
if (first_can_msg == 0) {
#ifdef DEBUG_LOG
@ -1627,7 +1289,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void MebBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis > last_can_msg_timestamp + 500) {
#ifdef DEBUG_LOG
@ -2373,7 +2035,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void MebBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Volkswagen Group MEB platform via CAN-FD", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 108; //Startup in 108S mode. We figure out the actual count later.

View file

@ -2,156 +2,501 @@
#define MEB_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_84S_DV 3528 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_84S_DV 2520
#define MAX_PACK_VOLTAGE_96S_DV 4032
#define MIN_PACK_VOLTAGE_96S_DV 2880
#define MAX_PACK_VOLTAGE_108S_DV 4536
#define MIN_PACK_VOLTAGE_108S_DV 3240
#define MAX_CELL_DEVIATION_MV 150
#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
#define SELECTED_BATTERY_CLASS MebBattery
#define PID_SOC 0x028C
#define PID_VOLTAGE 0x1E3B
#define PID_CURRENT 0x1E3D
#define PID_MAX_TEMP 0x1E0E
#define PID_MIN_TEMP 0x1E0F
#define PID_MAX_CHARGE_VOLTAGE 0x5171
#define PID_MIN_DISCHARGE_VOLTAGE 0x5170
#define PID_ENERGY_COUNTERS 0x1E32
#define PID_ALLOWED_CHARGE_POWER 0x1E1B
#define PID_ALLOWED_DISCHARGE_POWER 0x1E1C
#define PID_CELLVOLTAGE_CELL_1 0x1E40
#define PID_CELLVOLTAGE_CELL_2 0x1E41
#define PID_CELLVOLTAGE_CELL_3 0x1E42
#define PID_CELLVOLTAGE_CELL_4 0x1E43
#define PID_CELLVOLTAGE_CELL_5 0x1E44
#define PID_CELLVOLTAGE_CELL_6 0x1E45
#define PID_CELLVOLTAGE_CELL_7 0x1E46
#define PID_CELLVOLTAGE_CELL_8 0x1E47
#define PID_CELLVOLTAGE_CELL_9 0x1E48
#define PID_CELLVOLTAGE_CELL_10 0x1E49
#define PID_CELLVOLTAGE_CELL_11 0x1E4A
#define PID_CELLVOLTAGE_CELL_12 0x1E4B
#define PID_CELLVOLTAGE_CELL_13 0x1E4C
#define PID_CELLVOLTAGE_CELL_14 0x1E4D
#define PID_CELLVOLTAGE_CELL_15 0x1E4E
#define PID_CELLVOLTAGE_CELL_16 0x1E4F
#define PID_CELLVOLTAGE_CELL_17 0x1E50
#define PID_CELLVOLTAGE_CELL_18 0x1E51
#define PID_CELLVOLTAGE_CELL_19 0x1E52
#define PID_CELLVOLTAGE_CELL_20 0x1E53
#define PID_CELLVOLTAGE_CELL_21 0x1E54
#define PID_CELLVOLTAGE_CELL_22 0x1E55
#define PID_CELLVOLTAGE_CELL_23 0x1E56
#define PID_CELLVOLTAGE_CELL_24 0x1E57
#define PID_CELLVOLTAGE_CELL_25 0x1E58
#define PID_CELLVOLTAGE_CELL_26 0x1E59
#define PID_CELLVOLTAGE_CELL_27 0x1E5A
#define PID_CELLVOLTAGE_CELL_28 0x1E5B
#define PID_CELLVOLTAGE_CELL_29 0x1E5C
#define PID_CELLVOLTAGE_CELL_30 0x1E5D
#define PID_CELLVOLTAGE_CELL_31 0x1E5E
#define PID_CELLVOLTAGE_CELL_32 0x1E5F
#define PID_CELLVOLTAGE_CELL_33 0x1E60
#define PID_CELLVOLTAGE_CELL_34 0x1E61
#define PID_CELLVOLTAGE_CELL_35 0x1E62
#define PID_CELLVOLTAGE_CELL_36 0x1E63
#define PID_CELLVOLTAGE_CELL_37 0x1E64
#define PID_CELLVOLTAGE_CELL_38 0x1E65
#define PID_CELLVOLTAGE_CELL_39 0x1E66
#define PID_CELLVOLTAGE_CELL_40 0x1E67
#define PID_CELLVOLTAGE_CELL_41 0x1E68
#define PID_CELLVOLTAGE_CELL_42 0x1E69
#define PID_CELLVOLTAGE_CELL_43 0x1E6A
#define PID_CELLVOLTAGE_CELL_44 0x1E6B
#define PID_CELLVOLTAGE_CELL_45 0x1E6C
#define PID_CELLVOLTAGE_CELL_46 0x1E6D
#define PID_CELLVOLTAGE_CELL_47 0x1E6E
#define PID_CELLVOLTAGE_CELL_48 0x1E6F
#define PID_CELLVOLTAGE_CELL_49 0x1E70
#define PID_CELLVOLTAGE_CELL_50 0x1E71
#define PID_CELLVOLTAGE_CELL_51 0x1E72
#define PID_CELLVOLTAGE_CELL_52 0x1E73
#define PID_CELLVOLTAGE_CELL_53 0x1E74
#define PID_CELLVOLTAGE_CELL_54 0x1E75
#define PID_CELLVOLTAGE_CELL_55 0x1E76
#define PID_CELLVOLTAGE_CELL_56 0x1E77
#define PID_CELLVOLTAGE_CELL_57 0x1E78
#define PID_CELLVOLTAGE_CELL_58 0x1E79
#define PID_CELLVOLTAGE_CELL_59 0x1E7A
#define PID_CELLVOLTAGE_CELL_60 0x1E7B
#define PID_CELLVOLTAGE_CELL_61 0x1E7C
#define PID_CELLVOLTAGE_CELL_62 0x1E7D
#define PID_CELLVOLTAGE_CELL_63 0x1E7E
#define PID_CELLVOLTAGE_CELL_64 0x1E7F
#define PID_CELLVOLTAGE_CELL_65 0x1E80
#define PID_CELLVOLTAGE_CELL_66 0x1E81
#define PID_CELLVOLTAGE_CELL_67 0x1E82
#define PID_CELLVOLTAGE_CELL_68 0x1E83
#define PID_CELLVOLTAGE_CELL_69 0x1E84
#define PID_CELLVOLTAGE_CELL_70 0x1E85
#define PID_CELLVOLTAGE_CELL_71 0x1E86
#define PID_CELLVOLTAGE_CELL_72 0x1E87
#define PID_CELLVOLTAGE_CELL_73 0x1E88
#define PID_CELLVOLTAGE_CELL_74 0x1E89
#define PID_CELLVOLTAGE_CELL_75 0x1E8A
#define PID_CELLVOLTAGE_CELL_76 0x1E8B
#define PID_CELLVOLTAGE_CELL_77 0x1E8C
#define PID_CELLVOLTAGE_CELL_78 0x1E8D
#define PID_CELLVOLTAGE_CELL_79 0x1E8E
#define PID_CELLVOLTAGE_CELL_80 0x1E8F
#define PID_CELLVOLTAGE_CELL_81 0x1E90
#define PID_CELLVOLTAGE_CELL_82 0x1E91
#define PID_CELLVOLTAGE_CELL_83 0x1E92
#define PID_CELLVOLTAGE_CELL_84 0x1E93
#define PID_CELLVOLTAGE_CELL_85 0x1E94
#define PID_CELLVOLTAGE_CELL_86 0x1E95
#define PID_CELLVOLTAGE_CELL_87 0x1E96
#define PID_CELLVOLTAGE_CELL_88 0x1E97
#define PID_CELLVOLTAGE_CELL_89 0x1E98
#define PID_CELLVOLTAGE_CELL_90 0x1E99
#define PID_CELLVOLTAGE_CELL_91 0x1E9A
#define PID_CELLVOLTAGE_CELL_92 0x1E9B
#define PID_CELLVOLTAGE_CELL_93 0x1E9C
#define PID_CELLVOLTAGE_CELL_94 0x1E9D
#define PID_CELLVOLTAGE_CELL_95 0x1E9E
#define PID_CELLVOLTAGE_CELL_96 0x1E9F
#define PID_CELLVOLTAGE_CELL_97 0x1EA0
#define PID_CELLVOLTAGE_CELL_98 0x1EA1
#define PID_CELLVOLTAGE_CELL_99 0x1EA2
#define PID_CELLVOLTAGE_CELL_100 0x1EA3
#define PID_CELLVOLTAGE_CELL_101 0x1EA4
#define PID_CELLVOLTAGE_CELL_102 0x1EA5
#define PID_CELLVOLTAGE_CELL_103 0x1EA6
#define PID_CELLVOLTAGE_CELL_104 0x1EA7
#define PID_CELLVOLTAGE_CELL_105 0x1EA8
#define PID_CELLVOLTAGE_CELL_106 0x1EA9
#define PID_CELLVOLTAGE_CELL_107 0x1EAA
#define PID_CELLVOLTAGE_CELL_108 0x1EAB
#define PID_TEMP_POINT_1 0x1EAE
#define PID_TEMP_POINT_2 0x1EAF
#define PID_TEMP_POINT_3 0x1EB0
#define PID_TEMP_POINT_4 0x1EB1
#define PID_TEMP_POINT_5 0x1EB2
#define PID_TEMP_POINT_6 0x1EB3
#define PID_TEMP_POINT_7 0x1EB4
#define PID_TEMP_POINT_8 0x1EB5
#define PID_TEMP_POINT_9 0x1EB6
#define PID_TEMP_POINT_10 0x1EB7
#define PID_TEMP_POINT_11 0x1EB8
#define PID_TEMP_POINT_12 0x1EB9
#define PID_TEMP_POINT_13 0x1EBA
#define PID_TEMP_POINT_14 0x1EBB
#define PID_TEMP_POINT_15 0x1EBC
#define PID_TEMP_POINT_16 0x1EBD
#define PID_TEMP_POINT_17 0x1EBE
#define PID_TEMP_POINT_18 0x1EBF
class MebBattery : 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);
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
private:
static const int MAX_PACK_VOLTAGE_84S_DV = 3528; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_84S_DV = 2520;
static const int MAX_PACK_VOLTAGE_96S_DV = 4032;
static const int MIN_PACK_VOLTAGE_96S_DV = 2880;
static const int MAX_PACK_VOLTAGE_108S_DV = 4536;
static const int MIN_PACK_VOLTAGE_108S_DV = 3240;
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 = 2700; //Battery is put into emergency stop if one cell goes below this value
static const int PID_SOC = 0x028C;
static const int PID_VOLTAGE = 0x1E3B;
static const int PID_CURRENT = 0x1E3D;
static const int PID_MAX_TEMP = 0x1E0E;
static const int PID_MIN_TEMP = 0x1E0F;
static const int PID_MAX_CHARGE_VOLTAGE = 0x5171;
static const int PID_MIN_DISCHARGE_VOLTAGE = 0x5170;
static const int PID_ENERGY_COUNTERS = 0x1E32;
static const int PID_ALLOWED_CHARGE_POWER = 0x1E1B;
static const int PID_ALLOWED_DISCHARGE_POWER = 0x1E1C;
static const int PID_CELLVOLTAGE_CELL_1 = 0x1E40;
static const int PID_CELLVOLTAGE_CELL_2 = 0x1E41;
static const int PID_CELLVOLTAGE_CELL_3 = 0x1E42;
static const int PID_CELLVOLTAGE_CELL_4 = 0x1E43;
static const int PID_CELLVOLTAGE_CELL_5 = 0x1E44;
static const int PID_CELLVOLTAGE_CELL_6 = 0x1E45;
static const int PID_CELLVOLTAGE_CELL_7 = 0x1E46;
static const int PID_CELLVOLTAGE_CELL_8 = 0x1E47;
static const int PID_CELLVOLTAGE_CELL_9 = 0x1E48;
static const int PID_CELLVOLTAGE_CELL_10 = 0x1E49;
static const int PID_CELLVOLTAGE_CELL_11 = 0x1E4A;
static const int PID_CELLVOLTAGE_CELL_12 = 0x1E4B;
static const int PID_CELLVOLTAGE_CELL_13 = 0x1E4C;
static const int PID_CELLVOLTAGE_CELL_14 = 0x1E4D;
static const int PID_CELLVOLTAGE_CELL_15 = 0x1E4E;
static const int PID_CELLVOLTAGE_CELL_16 = 0x1E4F;
static const int PID_CELLVOLTAGE_CELL_17 = 0x1E50;
static const int PID_CELLVOLTAGE_CELL_18 = 0x1E51;
static const int PID_CELLVOLTAGE_CELL_19 = 0x1E52;
static const int PID_CELLVOLTAGE_CELL_20 = 0x1E53;
static const int PID_CELLVOLTAGE_CELL_21 = 0x1E54;
static const int PID_CELLVOLTAGE_CELL_22 = 0x1E55;
static const int PID_CELLVOLTAGE_CELL_23 = 0x1E56;
static const int PID_CELLVOLTAGE_CELL_24 = 0x1E57;
static const int PID_CELLVOLTAGE_CELL_25 = 0x1E58;
static const int PID_CELLVOLTAGE_CELL_26 = 0x1E59;
static const int PID_CELLVOLTAGE_CELL_27 = 0x1E5A;
static const int PID_CELLVOLTAGE_CELL_28 = 0x1E5B;
static const int PID_CELLVOLTAGE_CELL_29 = 0x1E5C;
static const int PID_CELLVOLTAGE_CELL_30 = 0x1E5D;
static const int PID_CELLVOLTAGE_CELL_31 = 0x1E5E;
static const int PID_CELLVOLTAGE_CELL_32 = 0x1E5F;
static const int PID_CELLVOLTAGE_CELL_33 = 0x1E60;
static const int PID_CELLVOLTAGE_CELL_34 = 0x1E61;
static const int PID_CELLVOLTAGE_CELL_35 = 0x1E62;
static const int PID_CELLVOLTAGE_CELL_36 = 0x1E63;
static const int PID_CELLVOLTAGE_CELL_37 = 0x1E64;
static const int PID_CELLVOLTAGE_CELL_38 = 0x1E65;
static const int PID_CELLVOLTAGE_CELL_39 = 0x1E66;
static const int PID_CELLVOLTAGE_CELL_40 = 0x1E67;
static const int PID_CELLVOLTAGE_CELL_41 = 0x1E68;
static const int PID_CELLVOLTAGE_CELL_42 = 0x1E69;
static const int PID_CELLVOLTAGE_CELL_43 = 0x1E6A;
static const int PID_CELLVOLTAGE_CELL_44 = 0x1E6B;
static const int PID_CELLVOLTAGE_CELL_45 = 0x1E6C;
static const int PID_CELLVOLTAGE_CELL_46 = 0x1E6D;
static const int PID_CELLVOLTAGE_CELL_47 = 0x1E6E;
static const int PID_CELLVOLTAGE_CELL_48 = 0x1E6F;
static const int PID_CELLVOLTAGE_CELL_49 = 0x1E70;
static const int PID_CELLVOLTAGE_CELL_50 = 0x1E71;
static const int PID_CELLVOLTAGE_CELL_51 = 0x1E72;
static const int PID_CELLVOLTAGE_CELL_52 = 0x1E73;
static const int PID_CELLVOLTAGE_CELL_53 = 0x1E74;
static const int PID_CELLVOLTAGE_CELL_54 = 0x1E75;
static const int PID_CELLVOLTAGE_CELL_55 = 0x1E76;
static const int PID_CELLVOLTAGE_CELL_56 = 0x1E77;
static const int PID_CELLVOLTAGE_CELL_57 = 0x1E78;
static const int PID_CELLVOLTAGE_CELL_58 = 0x1E79;
static const int PID_CELLVOLTAGE_CELL_59 = 0x1E7A;
static const int PID_CELLVOLTAGE_CELL_60 = 0x1E7B;
static const int PID_CELLVOLTAGE_CELL_61 = 0x1E7C;
static const int PID_CELLVOLTAGE_CELL_62 = 0x1E7D;
static const int PID_CELLVOLTAGE_CELL_63 = 0x1E7E;
static const int PID_CELLVOLTAGE_CELL_64 = 0x1E7F;
static const int PID_CELLVOLTAGE_CELL_65 = 0x1E80;
static const int PID_CELLVOLTAGE_CELL_66 = 0x1E81;
static const int PID_CELLVOLTAGE_CELL_67 = 0x1E82;
static const int PID_CELLVOLTAGE_CELL_68 = 0x1E83;
static const int PID_CELLVOLTAGE_CELL_69 = 0x1E84;
static const int PID_CELLVOLTAGE_CELL_70 = 0x1E85;
static const int PID_CELLVOLTAGE_CELL_71 = 0x1E86;
static const int PID_CELLVOLTAGE_CELL_72 = 0x1E87;
static const int PID_CELLVOLTAGE_CELL_73 = 0x1E88;
static const int PID_CELLVOLTAGE_CELL_74 = 0x1E89;
static const int PID_CELLVOLTAGE_CELL_75 = 0x1E8A;
static const int PID_CELLVOLTAGE_CELL_76 = 0x1E8B;
static const int PID_CELLVOLTAGE_CELL_77 = 0x1E8C;
static const int PID_CELLVOLTAGE_CELL_78 = 0x1E8D;
static const int PID_CELLVOLTAGE_CELL_79 = 0x1E8E;
static const int PID_CELLVOLTAGE_CELL_80 = 0x1E8F;
static const int PID_CELLVOLTAGE_CELL_81 = 0x1E90;
static const int PID_CELLVOLTAGE_CELL_82 = 0x1E91;
static const int PID_CELLVOLTAGE_CELL_83 = 0x1E92;
static const int PID_CELLVOLTAGE_CELL_84 = 0x1E93;
static const int PID_CELLVOLTAGE_CELL_85 = 0x1E94;
static const int PID_CELLVOLTAGE_CELL_86 = 0x1E95;
static const int PID_CELLVOLTAGE_CELL_87 = 0x1E96;
static const int PID_CELLVOLTAGE_CELL_88 = 0x1E97;
static const int PID_CELLVOLTAGE_CELL_89 = 0x1E98;
static const int PID_CELLVOLTAGE_CELL_90 = 0x1E99;
static const int PID_CELLVOLTAGE_CELL_91 = 0x1E9A;
static const int PID_CELLVOLTAGE_CELL_92 = 0x1E9B;
static const int PID_CELLVOLTAGE_CELL_93 = 0x1E9C;
static const int PID_CELLVOLTAGE_CELL_94 = 0x1E9D;
static const int PID_CELLVOLTAGE_CELL_95 = 0x1E9E;
static const int PID_CELLVOLTAGE_CELL_96 = 0x1E9F;
static const int PID_CELLVOLTAGE_CELL_97 = 0x1EA0;
static const int PID_CELLVOLTAGE_CELL_98 = 0x1EA1;
static const int PID_CELLVOLTAGE_CELL_99 = 0x1EA2;
static const int PID_CELLVOLTAGE_CELL_100 = 0x1EA3;
static const int PID_CELLVOLTAGE_CELL_101 = 0x1EA4;
static const int PID_CELLVOLTAGE_CELL_102 = 0x1EA5;
static const int PID_CELLVOLTAGE_CELL_103 = 0x1EA6;
static const int PID_CELLVOLTAGE_CELL_104 = 0x1EA7;
static const int PID_CELLVOLTAGE_CELL_105 = 0x1EA8;
static const int PID_CELLVOLTAGE_CELL_106 = 0x1EA9;
static const int PID_CELLVOLTAGE_CELL_107 = 0x1EAA;
static const int PID_CELLVOLTAGE_CELL_108 = 0x1EAB;
static const int PID_TEMP_POINT_1 = 0x1EAE;
static const int PID_TEMP_POINT_2 = 0x1EAF;
static const int PID_TEMP_POINT_3 = 0x1EB0;
static const int PID_TEMP_POINT_4 = 0x1EB1;
static const int PID_TEMP_POINT_5 = 0x1EB2;
static const int PID_TEMP_POINT_6 = 0x1EB3;
static const int PID_TEMP_POINT_7 = 0x1EB4;
static const int PID_TEMP_POINT_8 = 0x1EB5;
static const int PID_TEMP_POINT_9 = 0x1EB6;
static const int PID_TEMP_POINT_10 = 0x1EB7;
static const int PID_TEMP_POINT_11 = 0x1EB8;
static const int PID_TEMP_POINT_12 = 0x1EB9;
static const int PID_TEMP_POINT_13 = 0x1EBA;
static const int PID_TEMP_POINT_14 = 0x1EBB;
static const int PID_TEMP_POINT_15 = 0x1EBC;
static const int PID_TEMP_POINT_16 = 0x1EBD;
static const int PID_TEMP_POINT_17 = 0x1EBE;
static const int PID_TEMP_POINT_18 = 0x1EBF;
unsigned long previousMillis10ms = 0; // will store last time a 10ms CAN Message was send
unsigned long previousMillis20ms = 0; // will store last time a 20ms CAN Message was send
unsigned long previousMillis40ms = 0; // will store last time a 40ms CAN Message was send
unsigned long previousMillis50ms = 0; // will store last time a 50ms CAN Message was send
unsigned long previousMillis100ms = 0; // will store last time a 100ms CAN Message was send
unsigned long previousMillis200ms = 0; // will store last time a 200ms CAN Message was send
unsigned long previousMillis500ms = 0; // will store last time a 200ms CAN Message was send
unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send
bool toggle = false;
uint8_t counter_1000ms = 0;
uint8_t counter_200ms = 0;
uint8_t counter_100ms = 0;
uint8_t counter_50ms = 0;
uint8_t counter_40ms = 0;
uint8_t counter_20ms = 0;
uint8_t counter_10ms = 0;
uint8_t counter_040 = 0;
uint8_t counter_0F7 = 0;
uint8_t counter_3b5 = 0;
uint32_t poll_pid = PID_CELLVOLTAGE_CELL_85; // We start here to quickly determine the cell size of the pack.
bool nof_cells_determined = false;
uint32_t pid_reply = 0;
uint16_t battery_soc_polled = 0;
uint16_t battery_voltage_polled = 1480;
int16_t battery_current_polled = 0;
int16_t battery_max_temp = 600;
int16_t battery_min_temp = 600;
uint16_t battery_max_charge_voltage = 0;
uint16_t battery_min_discharge_voltage = 0;
uint16_t battery_allowed_charge_power = 0;
uint16_t battery_allowed_discharge_power = 0;
uint16_t cellvoltages_polled[108];
uint16_t tempval = 0;
uint8_t BMS_16A954A6_CRC = 0;
uint8_t BMS_5A2_counter = 0;
uint8_t BMS_5CA_counter = 0;
uint8_t BMS_0CF_counter = 0;
uint8_t BMS_0C0_counter = 0;
uint8_t BMS_578_counter = 0;
uint8_t BMS_16A954A6_counter = 0;
bool BMS_ext_limits_active =
false; //The current current limits of the HV battery are expanded to start the combustion engine / confirmation of the request
uint8_t BMS_mode =
0x07; //0: standby; Gates open; Communication active 1: Main contactor closed / HV network activated / normal driving operation
//2: assigned depending on the project (e.g. balancing, extended DC fast charging) //3: external charging
uint8_t BMS_HVIL_status = 0; //0 init, 1 seated, 2 open, 3 fault
bool BMS_fault_performance = false; //Error: Battery performance is limited (e.g. due to sensor or fan failure)
uint16_t BMS_current = 16300;
bool BMS_fault_emergency_shutdown_crash =
false; //Error: Safety-critical error (crash detection) Battery contactors are already opened / will be opened immediately Signal is read directly by the EMS and initiates an AKS of the PWR and an active discharge of the DC link
uint32_t BMS_voltage_intermediate = 2000;
uint32_t BMS_voltage = 1480;
uint8_t BMS_status_voltage_free =
0; //0=Init, 1=BMS intermediate circuit voltage-free (U_Zwkr < 20V), 2=BMS intermediate circuit not voltage-free (U_Zwkr >/= 25V, hysteresis), 3=Error
bool BMS_OBD_MIL = false;
uint8_t BMS_error_status =
0x7; //0 Component_IO, 1 Restricted_CompFkt_Isoerror_I, 2 Restricted_CompFkt_Isoerror_II, 3 Restricted_CompFkt_Interlock, 4 Restricted_CompFkt_SD, 5 Restricted_CompFkt_Performance red, 6 = No component function, 7 = Init
uint16_t BMS_capacity_ah = 0;
bool BMS_error_lamp_req = false;
bool BMS_warning_lamp_req = false;
uint8_t BMS_Kl30c_Status = 0; // 0 init, 1 closed, 2 open, 3 fault
bool service_disconnect_switch_missing = false;
bool pilotline_open = false;
bool balancing_request = false;
uint8_t battery_diagnostic = 0;
uint16_t battery_Wh_left = 0;
uint16_t battery_Wh_max = 1000;
uint8_t battery_potential_status = 0;
uint8_t battery_temperature_warning = 0;
uint16_t max_discharge_power_watt = 0;
uint16_t max_discharge_current_amp = 0;
uint16_t max_charge_power_watt = 0;
uint16_t max_charge_current_amp = 0;
uint16_t battery_SOC = 1;
uint16_t usable_energy_amount_Wh = 0;
uint8_t status_HV_line = 0; //0 init, 1 No open HV line, 2 open HV line detected, 3 fault
uint8_t warning_support = 0;
bool battery_heating_active = false;
uint16_t power_discharge_percentage = 0;
uint16_t power_charge_percentage = 0;
uint16_t actual_battery_voltage = 0;
uint16_t regen_battery = 0;
uint16_t energy_extracted_from_battery = 0;
uint16_t max_fastcharging_current_amp = 0; //maximum DC charging current allowed
uint8_t BMS_Status_DCLS =
0; //Status of the voltage monitoring on the DC charging interface. 0 inactive, 1 I_O , 2 N_I_O , 3 active
uint16_t DC_voltage_DCLS = 0; //Factor 1, DC voltage of the charging station. Measurement between the DC HV lines.
uint16_t DC_voltage_chargeport =
0; //Factor 0.5, Current voltage at the HV battery DC charging terminals; Outlet to the DC charging plug.
uint8_t BMS_welded_contactors_status =
0; //0: Init no diagnostic result, 1: no contactor welded, 2: at least 1 contactor welded, 3: Protection status detection error
bool BMS_error_shutdown_request =
false; // Fault: Fault condition, requires battery contactors to be opened internal battery error; Advance notification of an impending opening of the battery contactors by the BMS
bool BMS_error_shutdown =
false; // Fault: Fault condition, requires battery contactors to be opened Internal battery error, battery contactors opened without notice by the BMS
uint16_t power_battery_heating_watt = 0;
uint16_t power_battery_heating_req_watt = 0;
uint8_t cooling_request =
0; //0 = No cooling, 1 = Light cooling, cabin prio, 2= higher cooling, 3 = immediate cooling, 4 = emergency cooling
uint8_t heating_request = 0; //0 = init, 1= maintain temp, 2=higher demand, 3 = immediate heat demand
uint8_t balancing_active = false; //0 = init, 1 active, 2 not active
bool charging_active = false;
uint16_t max_energy_Wh = 0;
uint16_t max_charge_percent = 0;
uint16_t min_charge_percent = 0;
uint16_t isolation_resistance_kOhm = 0;
bool battery_heating_installed = false;
bool error_NT_circuit = false;
uint8_t pump_1_control = 0; //0x0D not installed, 0x0E init, 0x0F fault
uint8_t pump_2_control = 0; //0x0D not installed, 0x0E init, 0x0F fault
uint8_t target_flow_temperature_C = 0; //*0,5 -40
uint8_t return_temperature_C = 0; //*0,5 -40
uint8_t status_valve_1 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
uint8_t status_valve_2 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
uint8_t temperature_request =
0; //0 high cooling, 1 medium cooling, 2 low cooling, 3 no temp requirement init, 4 low heating , 5 medium heating, 6 high heating, 7 circulation
uint16_t performance_index_discharge_peak_temperature_percentage = 0;
uint16_t performance_index_charge_peak_temperature_percentage = 0;
uint8_t temperature_status_discharge =
0; //0 init, 1 temp under optimal, 2 temp optimal, 3 temp over optimal, 7 fault
uint8_t temperature_status_charge = 0; //0 init, 1 temp under optimal, 2 temp optimal, 3 temp over optimal, 7 fault
uint8_t isolation_fault =
0; //0 init, 1 no iso fault, 2 iso fault threshold1, 3 iso fault threshold2, 4 IWU defective
uint8_t isolation_status =
0; // 0 init, 1 = larger threshold1, 2 = smaller threshold1 total, 3 = smaller threshold1 intern, 4 = smaller threshold2 total, 5 = smaller threshold2 intern, 6 = no measurement, 7 = measurement active
uint8_t actual_temperature_highest_C = 0; //*0,5 -40
uint8_t actual_temperature_lowest_C = 0; //*0,5 -40
uint16_t actual_cellvoltage_highest_mV = 0; //bias 1000
uint16_t actual_cellvoltage_lowest_mV = 0; //bias 1000
uint16_t predicted_power_dyn_standard_watt = 0;
uint8_t predicted_time_dyn_standard_minutes = 0;
uint8_t mux = 0;
uint16_t cellvoltages[160] = {0};
uint16_t duration_discharge_power_watt = 0;
uint16_t duration_charge_power_watt = 0;
uint16_t maximum_voltage = 0;
uint16_t minimum_voltage = 0;
uint8_t battery_serialnumber[26];
uint8_t realtime_overcurrent_monitor = 0;
uint8_t realtime_CAN_communication_fault = 0;
uint8_t realtime_overcharge_warning = 0;
uint8_t realtime_SOC_too_high = 0;
uint8_t realtime_SOC_too_low = 0;
uint8_t realtime_SOC_jumping_warning = 0;
uint8_t realtime_temperature_difference_warning = 0;
uint8_t realtime_cell_overtemperature_warning = 0;
uint8_t realtime_cell_undertemperature_warning = 0;
uint8_t realtime_battery_overvoltage_warning = 0;
uint8_t realtime_battery_undervoltage_warning = 0;
uint8_t realtime_cell_overvoltage_warning = 0;
uint8_t realtime_cell_undervoltage_warning = 0;
uint8_t realtime_cell_imbalance_warning = 0;
uint8_t realtime_warning_battery_unathorized = 0;
bool component_protection_active = false;
bool shutdown_active = false;
bool transportation_mode_active = false;
uint8_t KL15_mode = 0;
uint8_t bus_knockout_timer = 0;
uint8_t hybrid_wakeup_reason = 0;
uint8_t wakeup_type = 0;
bool instrumentation_cluster_request = false;
uint8_t seconds = 0;
uint32_t first_can_msg = 0;
uint32_t last_can_msg_timestamp = 0;
bool hv_requested = false;
int32_t kwh_charge = 0;
int32_t kwh_discharge = 0;
#define TIME_YEAR 2024
#define TIME_MONTH 8
#define TIME_DAY 20
#define TIME_HOUR 10
#define TIME_MINUTE 5
#define TIME_SECOND 0
#define BMS_TARGET_HV_OFF 0
#define BMS_TARGET_HV_ON 1
#define BMS_TARGET_AC_CHARGING_EXT 3 //(HS + AC_2 contactors closed)
#define BMS_TARGET_AC_CHARGING 4 //(HS + AC contactors closed)
#define BMS_TARGET_DC_CHARGING 6 //(HS + DC contactors closed)
#define BMS_TARGET_INIT 7
#define DC_FASTCHARGE_NO_START_REQUEST 0x00
#define DC_FASTCHARGE_VEHICLE 0x40
#define DC_FASTCHARGE_LS1 0x80
#define DC_FASTCHARGE_LS2 0xC0
CAN_frame MEB_POLLING_FRAME = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1C40007B, // SOC 02 8C
.data = {0x03, 0x22, 0x02, 0x8C, 0x55, 0x55, 0x55, 0x55}};
CAN_frame MEB_ACK_FRAME = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1C40007B, // Ack
.data = {0x30, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55}};
//Messages needed for contactor closing
CAN_frame MEB_040 = {.FD = true, // Airbag
.ext_ID = false,
.DLC = 8,
.ID = 0x040, //Frame5 has HV deactivate request. Needs to be 0x00
.data = {0x7E, 0x83, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00}};
CAN_frame MEB_0C0 = {
.FD = true, // EM1 message
.ext_ID = false,
.DLC = 32,
.ID = 0x0C0, //Frame 5-6 and maybe 7-8 important (external voltage at inverter)
.data = {0x77, 0x0A, 0xFE, 0xE7, 0x7F, 0x10, 0x27, 0x00, 0xE0, 0x7F, 0xFF, 0xF3, 0x3F, 0xFF, 0xF3, 0x3F,
0xFC, 0x0F, 0x00, 0x00, 0xC0, 0xFF, 0xFE, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_0FC = {
.FD = true, //
.ext_ID = false,
.DLC = 48,
.ID = 0x0FC, //This message contains emergency regen request?(byte19), battery needs to see this message
.data = {0x07, 0x08, 0x00, 0x00, 0x7E, 0x00, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFE, 0xFE, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xF4, 0x01, 0x40, 0xFF, 0xEB, 0x7F, 0x0A, 0x88, 0xE3, 0x81, 0xAF, 0x42}};
CAN_frame MEB_6B2 = {.FD = true, // Diagnostics
.ext_ID = false,
.DLC = 8,
.ID = 0x6B2,
.data = {0x6A, 0xA7, 0x37, 0x80, 0xC9, 0xBD, 0xF6, 0xC2}};
CAN_frame MEB_17FC007B_poll = {.FD = true, // Non period request
.ext_ID = true,
.DLC = 8,
.ID = 0x17FC007B,
.data = {0x03, 0x22, 0x1E, 0x3D, 0x55, 0x55, 0x55, 0x55}};
CAN_frame MEB_1A5555A6 = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1A5555A6,
.data = {0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_585 = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x585,
.data = {0xCF, 0x38, 0xAF, 0x5B, 0x25, 0x00, 0x00, 0x00}}; // CF 38 AF 5B 25 00 00 00 in start4.log
// .data = {0xCF, 0x38, 0x20, 0x02, 0x25, 0xF7, 0x30, 0x00}}; // CF 38 AF 5B 25 00 00 00 in start4.log
CAN_frame MEB_5F5 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x5F5,
.data = {0x23, 0x02, 0x39, 0xC0, 0x1B, 0x8B, 0xC8, 0x1B}};
CAN_frame MEB_641 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x641,
.data = {0x37, 0x18, 0x00, 0x00, 0xF0, 0x00, 0xAA, 0x70}};
CAN_frame MEB_3C0 = {.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x3C0,
.data = {0x66, 0x00, 0x00, 0x00}}; // Klemmen_status_01
CAN_frame MEB_0FD = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x0FD, //CRC and counter, otherwise static
.data = {0x5F, 0xD0, 0x1F, 0x81, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_16A954FB = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x16A954FB,
.data = {0x00, 0xC0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_1A555548 = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1A555548,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_1A55552B = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1A55552B,
.data = {0x00, 0x00, 0x00, 0xA0, 0x02, 0x04, 0x00, 0x30}};
CAN_frame MEB_569 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x569, //HVEM
.data = {0x00, 0x00, 0x01, 0x3A, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_16A954B4 = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x16A954B4, //eTM
.data = {0xFE, 0xB6, 0x0D, 0x00, 0x00, 0xD5, 0x48, 0xFD}};
CAN_frame MEB_1B000046 = {.FD = false, // Not FD
.ext_ID = true,
.DLC = 8,
.ID = 0x1B000046, // Klima
.data = {0x00, 0x40, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_1B000010 = {.FD = false, // Not FD
.ext_ID = true,
.DLC = 8,
.ID = 0x1B000010, // Gateway
.data = {0x00, 0x50, 0x08, 0x50, 0x01, 0xFF, 0x30, 0x00}};
CAN_frame MEB_1B0000B9 = {.FD = false, // Not FD
.ext_ID = true,
.DLC = 8,
.ID = 0x1B0000B9, //DC/DC converter
.data = {0x00, 0x40, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_153 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x153, // content
.data = {0x00, 0x00, 0x00, 0xFF, 0xEF, 0xFE, 0xFF, 0xFF}};
CAN_frame MEB_5E1 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x5E1, // content
.data = {0x7F, 0x2A, 0x00, 0x60, 0xFE, 0x00, 0x00, 0x00}};
CAN_frame MEB_3BE = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x3BE, // CRC, otherwise content
.data = {0x57, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x04, 0x40}};
CAN_frame MEB_272 = {.FD = true, //HVLM_14
.ext_ID = false,
.DLC = 8,
.ID = 0x272, // content
.data = {0x00, 0x00, 0x00, 0x00, 0x48, 0x08, 0x00, 0x94}};
CAN_frame MEB_503 = {.FD = true, //HVK_01
.ext_ID = false,
.DLC = 8,
.ID = 0x503, // Content varies. Frame1 & 3 has HV req
.data = {0x5D, 0x61, 0x00, 0xFF, 0x7F, 0x80, 0xE3, 0x03}};
CAN_frame MEB_14C = {
.FD = true, //Motor message
.ext_ID = false,
.DLC = 32,
.ID = 0x14C, //CRC needed, content otherwise
.data = {0x38, 0x0A, 0xFF, 0x01, 0x01, 0xFF, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE,
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x25, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE}};
uint32_t can_msg_received = 0;
};
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef MG_5_BATTERY_H
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "MG-5-BATTERY.h"
@ -11,19 +12,8 @@
- Most important ones
*/
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
static int BMS_SOC = 0;
CAN_frame MG_5_100 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x100,
.data = {0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00}};
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
void Mg5Battery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
datalayer.battery.status.real_soc;
@ -44,7 +34,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.temperature_max_dC;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void Mg5Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) {
case 0x171: //Following messages were detected on a MG5 battery BMS
@ -108,7 +98,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
break;
}
}
void transmit_can_battery(unsigned long currentMillis) {
void Mg5Battery::transmit_can(unsigned long currentMillis) {
//Send 10ms message
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
previousMillis10 = currentMillis;
@ -123,7 +113,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void Mg5Battery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "MG 5 battery", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;

View file

@ -3,14 +3,35 @@
#include <Arduino.h>
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4040 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3100
#define MAX_CELL_DEVIATION_MV 150
#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
#include "CanBattery.h"
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS Mg5Battery
class Mg5Battery : 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:
static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 3100;
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 = 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
int BMS_SOC = 0;
CAN_frame MG_5_100 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x100,
.data = {0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00}};
};
#endif

View file

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

View file

@ -19,10 +19,9 @@
class NissanLeafBattery : public CanBattery {
public:
// Use this constructor for the second battery.
NissanLeafBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* allows_contactor_closing_ptr,
DATALAYER_INFO_NISSAN_LEAF* extended, int targetCan) {
NissanLeafBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_NISSAN_LEAF* extended, int targetCan) {
datalayer_battery = datalayer_ptr;
allows_contactor_closing = allows_contactor_closing_ptr;
allows_contactor_closing = nullptr;
datalayer_nissan = extended;
can_interface = targetCan;
@ -48,6 +47,8 @@ class NissanLeafBattery : public CanBattery {
DATALAYER_BATTERY_TYPE* datalayer_battery;
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;
int can_interface;

View file

@ -1,32 +1,10 @@
#include "../include.h"
#ifdef ORION_BMS
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "ORION-BMS.h"
/* Do not change code below unless you are sure what you are doing */
static uint16_t cellvoltages[MAX_AMOUNT_CELLS]; //array with all the cellvoltages
static uint16_t Maximum_Cell_Voltage = 3700;
static uint16_t Minimum_Cell_Voltage = 3700;
static uint16_t Pack_Health = 99;
static int16_t Pack_Current = 0;
static int16_t Average_Temperature = 0;
static uint16_t Pack_Summed_Voltage = 0;
static int16_t Average_Current = 0;
static uint16_t High_Temperature = 0;
static uint16_t Pack_SOC_ppt = 0;
static uint16_t Pack_CCL = 0; //Charge current limit (A)
static uint16_t Pack_DCL = 0; //Discharge current limit (A)
static uint16_t Maximum_Pack_Voltage = 0;
static uint16_t Minimum_Pack_Voltage = 0;
static uint16_t CellID = 0;
static uint16_t CellVoltage = 0;
static uint16_t CellResistance = 0;
static uint16_t CellOpenVoltage = 0;
static uint16_t Checksum = 0;
static uint16_t CellBalancing = 0;
static uint8_t amount_of_detected_cells = 0;
void findMinMaxCellvoltages(const uint16_t arr[], size_t size, uint16_t& Minimum_Cell_Voltage,
uint16_t& Maximum_Cell_Voltage) {
Minimum_Cell_Voltage = std::numeric_limits<uint16_t>::max();
@ -50,7 +28,7 @@ void findMinMaxCellvoltages(const uint16_t arr[], size_t size, uint16_t& Minimum
}
}
void update_values_battery() {
void OrionBms::update_values() {
datalayer.battery.status.real_soc = Pack_SOC_ppt * 10;
@ -87,7 +65,7 @@ void update_values_battery() {
}
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void OrionBms::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) {
case 0x356:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
@ -132,11 +110,11 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void OrionBms::transmit_can(unsigned long currentMillis) {
// No transmission needed for this integration
}
void setup_battery(void) { // Performs one time setup at startup
void OrionBms::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "DIY battery with Orion BMS (Victron setting)", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = NUMBER_OF_CELLS;

View file

@ -3,17 +3,48 @@
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS OrionBms
/* Change the following to suit your battery */
#define NUMBER_OF_CELLS 96
#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 1500
#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
#define MAX_CELL_DEVIATION_MV 150
class OrionBms : 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);
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
private:
/* Change the following to suit your battery */
static const int NUMBER_OF_CELLS = 96;
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 1500;
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
static const int MAX_CELL_DEVIATION_MV = 150;
uint16_t cellvoltages[MAX_AMOUNT_CELLS]; //array with all the cellvoltages
uint16_t Maximum_Cell_Voltage = 3700;
uint16_t Minimum_Cell_Voltage = 3700;
uint16_t Pack_Health = 99;
int16_t Pack_Current = 0;
int16_t Average_Temperature = 0;
uint16_t Pack_Summed_Voltage = 0;
int16_t Average_Current = 0;
uint16_t High_Temperature = 0;
uint16_t Pack_SOC_ppt = 0;
uint16_t Pack_CCL = 0; //Charge current limit (A)
uint16_t Pack_DCL = 0; //Discharge current limit (A)
uint16_t Maximum_Pack_Voltage = 0;
uint16_t Minimum_Pack_Voltage = 0;
uint16_t CellID = 0;
uint16_t CellVoltage = 0;
uint16_t CellResistance = 0;
uint16_t CellOpenVoltage = 0;
uint16_t Checksum = 0;
uint16_t CellBalancing = 0;
uint8_t amount_of_detected_cells = 0;
};
#endif

View file

@ -1,89 +1,44 @@
#include "../include.h"
#ifdef PYLON_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "PYLON-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
void PylonBattery::update_values() {
//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}};
datalayer_battery->status.real_soc = (SOC * 100); //increase SOC range from 0-100 -> 100.00
static int16_t celltemperature_max_dC = 0;
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 = 0;
static uint8_t SOH = 0;
static uint8_t charge_forbidden = 0;
static uint8_t discharge_forbidden = 0;
datalayer_battery->status.soh_pptt = (SOH * 100); //Increase decimals from 100% -> 100.00%
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>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
datalayer_battery->status.temperature_min_dC = celltemperature_min_dC;
datalayer.battery.status.cell_max_voltage_mV = cellvoltage_max_mV;
datalayer.battery.status.cell_voltages_mV[0] = cellvoltage_max_mV;
datalayer_battery->status.temperature_max_dC = celltemperature_max_dC;
datalayer.battery.status.cell_min_voltage_mV = cellvoltage_min_mV;
datalayer.battery.status.cell_voltages_mV[1] = cellvoltage_min_mV;
datalayer_battery->info.max_design_voltage_dV = charge_cutoff_voltage;
datalayer.battery.status.temperature_min_dC = celltemperature_min_dC;
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;
datalayer_battery->info.min_design_voltage_dV = discharge_cutoff_voltage;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
void PylonBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) {
case 0x7310:
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
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
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_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) {
PYLON_4200.data.u8[0] = 0x00; //Request system equipment info
}
}
}
#ifdef DOUBLE_BATTERY
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
void PylonBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 2;
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.max_cell_voltage_mV = MAX_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.number_of_cells = datalayer.battery.info.number_of_cells;
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
datalayer_battery->info.number_of_cells = 2;
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.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
if (allows_contactor_closing) {
*allows_contactor_closing = true;
}
}
#endif

View file

@ -1,18 +1,99 @@
#ifndef PYLON_BATTERY_H
#define PYLON_BATTERY_H
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS PylonBattery
/* Change the following to suit your battery */
#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 1500
#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
#define MAX_CELL_DEVIATION_MV 150
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;
}
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
// 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 */
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 1500;
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
static const int MAX_CELL_DEVIATION_MV = 150;
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;
// 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

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef RANGE_ROVER_PHEV_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RANGE-ROVER-PHEV-BATTERY.h"
@ -46,111 +47,7 @@
- Figure out contactor closing requirements
*/
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis50ms = 0; // will store last time a 50ms CAN Message was sent
//CAN content from battery
static bool StatusCAT5BPOChg = false;
static bool StatusCAT4Derate = false;
static uint8_t OCMonitorStatus = 0;
static bool StatusCAT3 = false;
static bool IsolationStatus = false;
static bool HVILStatus = false;
static bool ContactorStatus = false;
static uint8_t StatusGpCounter = 0;
static bool WeldCheckStatus = false;
static bool StatusCAT7NowBPO = false;
static bool StatusCAT6DlyBPO = false;
static uint8_t StatusGpCS = 0;
static uint8_t CAT6Count = 0;
static bool EndOfCharge = false;
static bool DerateWarning = false;
static bool PrechargeAllowed = false;
static uint8_t DischargeExtGpCounter = 0; // Counter 0-15
static uint8_t DischargeExtGpCS = 0; // CRC
static uint16_t DischargeVoltageLimit = 0; //Min voltage battery allows discharging to
static uint16_t DischargePowerLimitExt = 0; //Momentary Discharge power limit kW*0.01 (0-655)
static uint16_t DischargeContPwrLmt = 0; //Longterm Discharge power limit kW*0.01 (0-655)
static uint8_t PwrGpCS = 0; // CRC
static uint8_t PwrGpCounter = 0; // Counter 0-15
static uint16_t VoltageExt = 370; // Voltage of the HV Battery
static uint16_t VoltageBus = 0; // Voltage on the high-voltage DC bus
static int32_t CurrentExt =
209715; //Positive - discharge, Negative Charge (0 - 16777215) Scaling: 0.025 Offset: -209715.175 Units: Amps
static bool HVIsolationTestRunning = false;
static uint16_t VoltageOC =
0; //The instantaneous equivalent open-circuit voltage of the high voltage battery. This is used by the high-voltage inverter in power prediction and derating calculations.
static uint16_t DchCurrentLimit =
0; // A, 'Maximum current that can be delivered by the HV Battery during motoring mode i.e during discharging.
static uint16_t ChgCurrentLimit =
0; // - 1023 A, Maximum current that can be transferred into the HV Battery during generating mode i.e during charging. Charging is neagtive and discharging is positive.
static uint16_t ChargeContPwrLmt = 0; //Longterm charge power limit kW*0.01 (0-655)
static uint16_t ChargePowerLimitExt = 0; //Momentary Charge power limit kW*0.01 (0-655)
static uint8_t ChgExtGpCS = 0; // CRC
static uint8_t ChgExtGpCounter = 0; //counter 0-15
static uint16_t ChargeVoltageLimit = 500; //Max voltage limit during charging of the HV Battery.
static uint8_t CurrentWarning = 0; // 0 normal, 1 cell overcurrent, 2 cell undercurrent
static uint8_t TempWarning = 0; // 0 normal, 1 cell overtemp, 2 cell undertemp
static int8_t TempUpLimit = 0; //Upper temperature limit.
static uint8_t CellVoltWarning = 0; // 0 normal, 1 cell overvoltage, 2 cell undervoltage
static bool CCCVChargeMode = false; //0 CC, 1 = CV
static uint16_t CellVoltUpLimit = 0; //mV, Upper cell voltage limit
static uint16_t SOCHighestCell = 0; //0.01, %
static uint16_t SOCLowestCell = 0; //0.01, %
static uint16_t SOCAverage = 0; //0.01, %
static bool WakeUpTopUpReq =
false; //The HV Battery can trigger a vehicle wake-up to request its State of Charge to be increased.
static bool WakeUpThermalReq =
false; //The HV Battery can trigger a vehicle wake-up in order to be thermally managed (ie. cooled down OR warmed up).
static bool WakeUpDchReq =
false; //The HV Battery can trigger a vehicle wake-up to request its State of Charge to be reduced.
static uint16_t StateofHealth = 0;
static uint16_t EstimatedLossChg =
0; //fact0.001, kWh Expected energy which will be lost during charging (at the rate given by VSCEstChargePower) due to resistance within the HV Battery.
static bool CoolingRequest =
false; //HV Battery cooling request to be cooled by the eAC/chiller as its cooling needs exceed the LTR cooling loop capability.
static uint16_t EstimatedLossDch =
0; //fact0.001, kWh Expected energy which will be lost during discharging (at the rate given by VSCEstDischargePower) due to resistance within the HV Battery.
static uint8_t FanDutyRequest =
0; //Request from the HV Battery cooling system to demand a change of duty for the electrical engine cooling fan speed (whilst using its LTR cooling loop).
static bool ValveCtrlStat = false; //0 Chiller/Heater cooling loop requested , 1 LTR cooling loop requested
static uint16_t EstLossDchTgtSoC =
0; //fact0.001, kWh Expected energy which will be lost during discharging (at the rate given by VSCEstimatedDchPower) from the target charging SoC (PHEV: HVBattEnergyUsableMax, BEV: HVBattEnergyUsableBulk) down to HVBattEnergyUsableMin, due to resistance within the Traction Battery.
static uint8_t HeatPowerGenChg =
0; //fact0.1, kW, Estimated average heat generated by battery if charged at the rate given by VSCEstimatedChgPower.
static uint8_t HeatPowerGenDch =
0; //fact0.1, kW, Estimated average heat generated by battery if discharged at the rate given by VSCEstimatedDchPower.
static uint8_t WarmupRateChg =
0; //fact0.1, C/min , Expected average rate at which the battery will self-heat if charged at the rate given by VSCEstimatedChgPower.
static uint8_t WarmupRateDch =
0; //fact0.1, C/min , Expected average rate at which the battery will self-heat if discharged at the rate given by VSCEstimatedDchPower.
static uint16_t CellVoltageMax = 3700;
static uint16_t CellVoltageMin = 3700;
static int8_t CellTempAverage = 0; //factor0.5, -40 offset
static int8_t CellTempColdest = 0; //factor0.5, -40 offset
static int8_t CellTempHottest = 0; //factor0.5, -40 offset
static uint8_t HeaterCtrlStat = 0; //factor1, 0 offset
static bool ThermalOvercheck = false; // 0 OK, 1 NOT OK
static int8_t InletCoolantTemp = 0; //factor0.5, -40 offset
static bool ClntPumpDiagStat_UB = false;
static bool InletCoolantTemp_UB = false;
static bool CoolantLevel = false; // Coolant level OK , 1 NOT OK
static bool ClntPumpDiagStat = false; // 0 Pump OK, 1 NOT OK
static uint8_t MILRequest = 0; //No req, 1 ON, 2 FLASHING, 3 unused
static uint16_t EnergyAvailable = 0; //fac0.05 , The total energy available from the HV Battery
static uint16_t EnergyUsableMax = 0; //fac0.05 , The total energy available from the HV Battery at its maximum SOC
static uint16_t EnergyUsableMin = 0; //fac0.05 , The total energy available from the HV Battery at its minimum SOC
static uint16_t TotalCapacity =
0; //fac0.1 , Total Battery capacity in Kwh. This will reduce over the lifetime of the HV Battery.
//CAN messages needed by battery (LOG needed!)
CAN_frame RANGE_ROVER_18B = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x18B, //CONTENT??? TODO
.data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
void update_values_battery() {
void RangeRoverPhevBattery::update_values() {
datalayer.battery.status.real_soc = SOCAverage;
@ -180,7 +77,7 @@ void update_values_battery() {
datalayer.battery.info.min_design_voltage_dV = DischargeVoltageLimit * 10;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void RangeRoverPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) {
case 0x080: // 15ms
@ -301,7 +198,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void RangeRoverPhevBattery::transmit_can(unsigned long currentMillis) {
// Send 50ms CAN Message
if (currentMillis - previousMillis50ms >= INTERVAL_50_MS) {
previousMillis50ms = currentMillis;
@ -310,7 +207,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void RangeRoverPhevBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Range Rover 13kWh PHEV battery (L494/L405)", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;

View file

@ -3,16 +3,128 @@
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS RangeRoverPhevBattery
/* Change the following to suit your battery */
#define MAX_PACK_VOLTAGE_DV 5000 //TODO: Configure
#define MIN_PACK_VOLTAGE_DV 0 //TODO: Configure
#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
#define MAX_CELL_DEVIATION_MV 150
class RangeRoverPhevBattery : 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);
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
private:
/* Change the following to suit your battery */
static const int MAX_PACK_VOLTAGE_DV = 5000; //TODO: Configure
static const int MIN_PACK_VOLTAGE_DV = 0; //TODO: Configure
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
static const int MAX_CELL_DEVIATION_MV = 150;
;
unsigned long previousMillis50ms = 0; // will store last time a 50ms CAN Message was sent
//CAN content from battery
bool StatusCAT5BPOChg = false;
bool StatusCAT4Derate = false;
uint8_t OCMonitorStatus = 0;
bool StatusCAT3 = false;
bool IsolationStatus = false;
bool HVILStatus = false;
bool ContactorStatus = false;
uint8_t StatusGpCounter = 0;
bool WeldCheckStatus = false;
bool StatusCAT7NowBPO = false;
bool StatusCAT6DlyBPO = false;
uint8_t StatusGpCS = 0;
uint8_t CAT6Count = 0;
bool EndOfCharge = false;
bool DerateWarning = false;
bool PrechargeAllowed = false;
uint8_t DischargeExtGpCounter = 0; // Counter 0-15
uint8_t DischargeExtGpCS = 0; // CRC
uint16_t DischargeVoltageLimit = 0; //Min voltage battery allows discharging to
uint16_t DischargePowerLimitExt = 0; //Momentary Discharge power limit kW*0.01 (0-655)
uint16_t DischargeContPwrLmt = 0; //Longterm Discharge power limit kW*0.01 (0-655)
uint8_t PwrGpCS = 0; // CRC
uint8_t PwrGpCounter = 0; // Counter 0-15
uint16_t VoltageExt = 370; // Voltage of the HV Battery
uint16_t VoltageBus = 0; // Voltage on the high-voltage DC bus
int32_t CurrentExt =
209715; //Positive - discharge, Negative Charge (0 - 16777215) Scaling: 0.025 Offset: -209715.175 Units: Amps
bool HVIsolationTestRunning = false;
uint16_t VoltageOC =
0; //The instantaneous equivalent open-circuit voltage of the high voltage battery. This is used by the high-voltage inverter in power prediction and derating calculations.
uint16_t DchCurrentLimit =
0; // A, 'Maximum current that can be delivered by the HV Battery during motoring mode i.e during discharging.
uint16_t ChgCurrentLimit =
0; // - 1023 A, Maximum current that can be transferred into the HV Battery during generating mode i.e during charging. Charging is neagtive and discharging is positive.
uint16_t ChargeContPwrLmt = 0; //Longterm charge power limit kW*0.01 (0-655)
uint16_t ChargePowerLimitExt = 0; //Momentary Charge power limit kW*0.01 (0-655)
uint8_t ChgExtGpCS = 0; // CRC
uint8_t ChgExtGpCounter = 0; //counter 0-15
uint16_t ChargeVoltageLimit = 500; //Max voltage limit during charging of the HV Battery.
uint8_t CurrentWarning = 0; // 0 normal, 1 cell overcurrent, 2 cell undercurrent
uint8_t TempWarning = 0; // 0 normal, 1 cell overtemp, 2 cell undertemp
int8_t TempUpLimit = 0; //Upper temperature limit.
uint8_t CellVoltWarning = 0; // 0 normal, 1 cell overvoltage, 2 cell undervoltage
bool CCCVChargeMode = false; //0 CC, 1 = CV
uint16_t CellVoltUpLimit = 0; //mV, Upper cell voltage limit
uint16_t SOCHighestCell = 0; //0.01, %
uint16_t SOCLowestCell = 0; //0.01, %
uint16_t SOCAverage = 0; //0.01, %
bool WakeUpTopUpReq =
false; //The HV Battery can trigger a vehicle wake-up to request its State of Charge to be increased.
bool WakeUpThermalReq =
false; //The HV Battery can trigger a vehicle wake-up in order to be thermally managed (ie. cooled down OR warmed up).
bool WakeUpDchReq =
false; //The HV Battery can trigger a vehicle wake-up to request its State of Charge to be reduced.
uint16_t StateofHealth = 0;
uint16_t EstimatedLossChg =
0; //fact0.001, kWh Expected energy which will be lost during charging (at the rate given by VSCEstChargePower) due to resistance within the HV Battery.
bool CoolingRequest =
false; //HV Battery cooling request to be cooled by the eAC/chiller as its cooling needs exceed the LTR cooling loop capability.
uint16_t EstimatedLossDch =
0; //fact0.001, kWh Expected energy which will be lost during discharging (at the rate given by VSCEstDischargePower) due to resistance within the HV Battery.
uint8_t FanDutyRequest =
0; //Request from the HV Battery cooling system to demand a change of duty for the electrical engine cooling fan speed (whilst using its LTR cooling loop).
bool ValveCtrlStat = false; //0 Chiller/Heater cooling loop requested , 1 LTR cooling loop requested
uint16_t EstLossDchTgtSoC =
0; //fact0.001, kWh Expected energy which will be lost during discharging (at the rate given by VSCEstimatedDchPower) from the target charging SoC (PHEV: HVBattEnergyUsableMax, BEV: HVBattEnergyUsableBulk) down to HVBattEnergyUsableMin, due to resistance within the Traction Battery.
uint8_t HeatPowerGenChg =
0; //fact0.1, kW, Estimated average heat generated by battery if charged at the rate given by VSCEstimatedChgPower.
uint8_t HeatPowerGenDch =
0; //fact0.1, kW, Estimated average heat generated by battery if discharged at the rate given by VSCEstimatedDchPower.
uint8_t WarmupRateChg =
0; //fact0.1, C/min , Expected average rate at which the battery will self-heat if charged at the rate given by VSCEstimatedChgPower.
uint8_t WarmupRateDch =
0; //fact0.1, C/min , Expected average rate at which the battery will self-heat if discharged at the rate given by VSCEstimatedDchPower.
uint16_t CellVoltageMax = 3700;
uint16_t CellVoltageMin = 3700;
int8_t CellTempAverage = 0; //factor0.5, -40 offset
int8_t CellTempColdest = 0; //factor0.5, -40 offset
int8_t CellTempHottest = 0; //factor0.5, -40 offset
uint8_t HeaterCtrlStat = 0; //factor1, 0 offset
bool ThermalOvercheck = false; // 0 OK, 1 NOT OK
int8_t InletCoolantTemp = 0; //factor0.5, -40 offset
bool ClntPumpDiagStat_UB = false;
bool InletCoolantTemp_UB = false;
bool CoolantLevel = false; // Coolant level OK , 1 NOT OK
bool ClntPumpDiagStat = false; // 0 Pump OK, 1 NOT OK
uint8_t MILRequest = 0; //No req, 1 ON, 2 FLASHING, 3 unused
uint16_t EnergyAvailable = 0; //fac0.05 , The total energy available from the HV Battery
uint16_t EnergyUsableMax = 0; //fac0.05 , The total energy available from the HV Battery at its maximum SOC
uint16_t EnergyUsableMin = 0; //fac0.05 , The total energy available from the HV Battery at its minimum SOC
uint16_t TotalCapacity =
0; //fac0.1 , Total Battery capacity in Kwh. This will reduce over the lifetime of the HV Battery.
//CAN messages needed by battery (LOG needed!)
CAN_frame RANGE_ROVER_18B = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x18B, //CONTENT??? TODO
.data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
};
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef RENAULT_KANGOO_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RENAULT-KANGOO-BATTERY.h"
@ -18,60 +19,8 @@ There seems to be some values on the Kangoo that differ between the 22/33 kWh ve
This page has info on the larger 33kWh pack: https://openinverter.org/wiki/Renault_Kangoo_36
*/
/* Do not change code below unless you are sure what you are doing */
static uint32_t LB_Battery_Voltage = 3700;
static uint32_t LB_Charge_Power_Limit_Watts = 0;
static int32_t LB_Current = 0;
static int16_t LB_MAX_TEMPERATURE = 0;
static int16_t LB_MIN_TEMPERATURE = 0;
static uint16_t LB_SOC = 0;
static uint16_t LB_SOH = 0;
static uint16_t LB_Discharge_Power_Limit = 0;
static uint16_t LB_Charge_Power_Limit = 0;
static uint16_t LB_kWh_Remaining = 0;
static uint16_t LB_Cell_Max_Voltage = 3700;
static uint16_t LB_Cell_Min_Voltage = 3700;
static uint16_t LB_MaxChargeAllowed_W = 0;
static uint8_t LB_Discharge_Power_Limit_Byte1 = 0;
static uint8_t GVI_Pollcounter = 0;
static uint8_t LB_EOCR = 0;
static uint8_t LB_HVBUV = 0;
static uint8_t LB_HVBIR = 0;
static uint8_t LB_CUV = 0;
static uint8_t LB_COV = 0;
static uint8_t LB_HVBOV = 0;
static uint8_t LB_HVBOT = 0;
static uint8_t LB_HVBOC = 0;
static uint8_t LB_MaxInput_kW = 0;
static uint8_t LB_MaxOutput_kW = 0;
static bool GVB_79B_Continue = false;
CAN_frame KANGOO_423 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x423,
.data = {0x0B, 0x1D, 0x00, 0x02, 0xB2, 0x20, 0xB2, 0xD9}}; // Charging
// Driving: 0x07 0x1D 0x00 0x02 0x5D 0x80 0x5D 0xD8
// Charging: 0x0B 0x1D 0x00 0x02 0xB2 0x20 0xB2 0xD9
// Fastcharging: 0x07 0x1E 0x00 0x01 0x5D 0x20 0xB2 0xC7
// Old hardcoded message: .data = {0x33, 0x00, 0xFF, 0xFF, 0x00, 0xE0, 0x00, 0x00}};
CAN_frame KANGOO_79B = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x79B,
.data = {0x02, 0x21, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00}};
CAN_frame KANGOO_79B_Continue = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x79B,
.data = {0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
static unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was sent
static unsigned long GVL_pause = 0;
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters
void RenaultKangooBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters
datalayer.battery.status.real_soc = (LB_SOC * 100); //increase LB_SOC range from 0-100 -> 100.00
@ -137,7 +86,7 @@ void update_values_battery() { //This function maps all the values fetched via
#endif
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void RenaultKangooBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) {
case 0x155: //BMS1
@ -210,7 +159,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void RenaultKangooBattery::transmit_can(unsigned long currentMillis) {
// Send 100ms CAN Message (for 2.4s, then pause 10s)
if ((currentMillis - previousMillis100) >= (INTERVAL_100_MS + GVL_pause)) {
previousMillis100 = currentMillis;
@ -232,8 +181,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void RenaultKangooBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Renault Kangoo", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;

View file

@ -3,15 +3,77 @@
#include <Arduino.h>
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4150 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 2500
#define MAX_CELL_DEVIATION_MV 150
#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
#define MAX_CHARGE_POWER_W 5000 // Battery can be charged with this amount of power
#include "CanBattery.h"
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS RenaultKangooBattery
class RenaultKangooBattery : 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:
static const int MAX_PACK_VOLTAGE_DV = 4150; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 2500;
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 = 2700; //Battery is put into emergency stop if one cell goes below this value
static const int MAX_CHARGE_POWER_W = 5000; // Battery can be charged with this amount of power
uint32_t LB_Battery_Voltage = 3700;
uint32_t LB_Charge_Power_Limit_Watts = 0;
int32_t LB_Current = 0;
int16_t LB_MAX_TEMPERATURE = 0;
int16_t LB_MIN_TEMPERATURE = 0;
uint16_t LB_SOC = 0;
uint16_t LB_SOH = 0;
uint16_t LB_Discharge_Power_Limit = 0;
uint16_t LB_Charge_Power_Limit = 0;
uint16_t LB_kWh_Remaining = 0;
uint16_t LB_Cell_Max_Voltage = 3700;
uint16_t LB_Cell_Min_Voltage = 3700;
uint16_t LB_MaxChargeAllowed_W = 0;
uint8_t LB_Discharge_Power_Limit_Byte1 = 0;
uint8_t GVI_Pollcounter = 0;
uint8_t LB_EOCR = 0;
uint8_t LB_HVBUV = 0;
uint8_t LB_HVBIR = 0;
uint8_t LB_CUV = 0;
uint8_t LB_COV = 0;
uint8_t LB_HVBOV = 0;
uint8_t LB_HVBOT = 0;
uint8_t LB_HVBOC = 0;
uint8_t LB_MaxInput_kW = 0;
uint8_t LB_MaxOutput_kW = 0;
bool GVB_79B_Continue = false;
CAN_frame KANGOO_423 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x423,
.data = {0x0B, 0x1D, 0x00, 0x02, 0xB2, 0x20, 0xB2, 0xD9}}; // Charging
// Driving: 0x07 0x1D 0x00 0x02 0x5D 0x80 0x5D 0xD8
// Charging: 0x0B 0x1D 0x00 0x02 0xB2 0x20 0xB2 0xD9
// Fastcharging: 0x07 0x1E 0x00 0x01 0x5D 0x20 0xB2 0xC7
// Old hardcoded message: .data = {0x33, 0x00, 0xFF, 0xFF, 0x00, 0xE0, 0x00, 0x00}};
CAN_frame KANGOO_79B = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x79B,
.data = {0x02, 0x21, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00}};
CAN_frame KANGOO_79B_Continue = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x79B,
.data = {0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was sent
unsigned long GVL_pause = 0;
};
#endif

View file

@ -5,19 +5,6 @@
#include "../devboard/utils/events.h"
#include "RENAULT-TWIZY.h"
/* Do not change code below unless you are sure what you are doing */
static int16_t cell_temperatures_dC[7] = {0};
static int16_t current_dA = 0;
static uint16_t voltage_dV = 0;
static int16_t cellvoltages_mV[14] = {0};
static int16_t max_discharge_power = 0;
static int16_t max_recup_power = 0;
static int16_t max_charge_power = 0;
static uint16_t SOC = 0;
static uint16_t SOH = 0;
static uint16_t remaining_capacity_Wh = 0;
int16_t max_value(int16_t* entries, size_t len) {
int result = INT16_MIN;
for (int i = 0; i < len; i++) {
@ -38,7 +25,7 @@ int16_t min_value(int16_t* entries, size_t len) {
return result;
}
void update_values_battery() {
void RenaultTwizyBattery::update_values() {
datalayer.battery.status.real_soc = SOC;
datalayer.battery.status.soh_pptt = SOH;
@ -65,7 +52,7 @@ void update_values_battery() {
max_value(cell_temperatures_dC, sizeof(cell_temperatures_dC) / sizeof(*cell_temperatures_dC));
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void RenaultTwizyBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) {
case 0x155:
@ -127,11 +114,11 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void RenaultTwizyBattery::transmit_can(unsigned long currentMillis) {
// we do not need to send anything to the battery for now
}
void setup_battery(void) { // Performs one time setup at startup
void RenaultTwizyBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Renault Twizy", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 14;

View file

@ -1,15 +1,35 @@
#ifndef RENAULT_TWIZY_BATTERY_H
#define RENAULT_TWIZY_BATTERY_H
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 579 // 57.9V at 100% SOC (with 70% SOH, new one might be higher)
#define MIN_PACK_VOLTAGE_DV 480 // 48.4V at 13.76% SOC
#define MAX_CELL_DEVIATION_MV 150
#define MAX_CELL_VOLTAGE_MV 4200 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 3400 //Battery is put into emergency stop if one cell goes below this value
#define SELECTED_BATTERY_CLASS RenaultTwizyBattery
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
class RenaultTwizyBattery : 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:
static const int MAX_PACK_VOLTAGE_DV = 579; // 57.9V at 100% SOC (with 70% SOH, new one might be higher)
static const int MIN_PACK_VOLTAGE_DV = 480; // 48.4V at 13.76% SOC
static const int MAX_CELL_DEVIATION_MV = 150;
static const int MAX_CELL_VOLTAGE_MV = 4200; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 3400; //Battery is put into emergency stop if one cell goes below this value
int16_t cell_temperatures_dC[7] = {0};
int16_t current_dA = 0;
uint16_t voltage_dV = 0;
int16_t cellvoltages_mV[14] = {0};
int16_t max_discharge_power = 0;
int16_t max_recup_power = 0;
int16_t max_charge_power = 0;
uint16_t SOC = 0;
uint16_t SOH = 0;
uint16_t remaining_capacity_Wh = 0;
};
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef RENAULT_ZOE_GEN2_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"
@ -20,154 +21,10 @@ every time the power is reset which can be dangerous. In this state, the voltage
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/blob/master/vehicle/OVMS.V3/components/vehicle_renaultzoe_ph2_obd/src/vehicle_renaultzoe_ph2_obd.cpp
https://github.com/ljames28/Renault-Zoe-PH2-ZE50-Canbus-LBC-Information?tab=readme-ov-file
https://github.com/fesch/CanZE/tree/master/app/src/main/assets/ZOE_Ph2
/*
*/
/* Do not change code below unless you are sure what you are doing */
static uint16_t battery_soc = 0;
static uint16_t battery_usable_soc = 5000;
static uint16_t battery_soh = 10000;
static uint16_t battery_pack_voltage = 370;
static uint16_t battery_max_cell_voltage = 3700;
static uint16_t battery_min_cell_voltage = 3700;
static uint16_t battery_12v = 12000;
static uint16_t battery_avg_temp = 920;
static uint16_t battery_min_temp = 920;
static uint16_t battery_max_temp = 920;
static uint16_t battery_max_power = 0;
static uint16_t battery_interlock = 0;
static uint16_t battery_kwh = 0;
static int32_t battery_current = 32640;
static uint16_t battery_current_offset = 0;
static uint16_t battery_max_generated = 0;
static uint16_t battery_max_available = 0;
static uint16_t battery_current_voltage = 0;
static uint16_t battery_charging_status = 0;
static uint16_t battery_remaining_charge = 0;
static uint16_t battery_balance_capacity_total = 0;
static uint16_t battery_balance_time_total = 0;
static uint16_t battery_balance_capacity_sleep = 0;
static uint16_t battery_balance_time_sleep = 0;
static uint16_t battery_balance_capacity_wake = 0;
static uint16_t battery_balance_time_wake = 0;
static uint16_t battery_bms_state = 0;
static uint16_t battery_balance_switches = 0;
static uint16_t battery_energy_complete = 0;
static uint16_t battery_energy_partial = 0;
static uint16_t battery_slave_failures = 0;
static uint16_t battery_mileage = 0;
static uint16_t battery_fan_speed = 0;
static uint16_t battery_fan_period = 0;
static uint16_t battery_fan_control = 0;
static uint16_t battery_fan_duty = 0;
static uint16_t battery_temporisation = 0;
static uint16_t battery_time = 0;
static uint16_t battery_pack_time = 0;
static uint16_t battery_soc_min = 0;
static uint16_t battery_soc_max = 0;
static uint32_t ZOE_376_time_now_s = 1745452800; // Initialized to make the battery think it is April 24, 2025
unsigned long kProductionTimestamp_s =
1614454107; // Production timestamp in seconds since January 1, 1970. Production timestamp used: February 25, 2021 at 8:08:27 AM GMT
void RenaultZoeGen2Battery::update_values() {
CAN_frame ZOE_373 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x373,
.data = {0xC1, 0x40, 0x5D, 0xB2, 0x00, 0x01, 0xff,
0xe3}}; // FIXME: remove if not needed: {0xC1, 0x80, 0x5D, 0x5D, 0x00, 0x00, 0xff, 0xcb}};
CAN_frame ZOE_376 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x373,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
0x00}}; // fill first 6 bytes with 0's. The first 6 bytes are calculated based on the current time.
CAN_frame ZOE_POLL_18DADBF1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x18DADBF1,
.data = {0x03, 0x22, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00}};
//NVROL Reset
CAN_frame ZOE_NVROL_1_18DADBF1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x18DADBF1,
.data = {0x02, 0x10, 0x03, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}};
CAN_frame ZOE_NVROL_2_18DADBF1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x18DADBF1,
.data = {0x04, 0x31, 0x01, 0xB0, 0x09, 0x00, 0xAA, 0xAA}};
//Enable temporisation before sleep
CAN_frame ZOE_SLEEP_1_18DADBF1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x18DADBF1,
.data = {0x02, 0x10, 0x03, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}};
CAN_frame ZOE_SLEEP_2_18DADBF1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x18DADBF1,
.data = {0x04, 0x2E, 0x92, 0x81, 0x01, 0xAA, 0xAA, 0xAA}};
const uint16_t poll_commands[48] = {POLL_SOC,
POLL_USABLE_SOC,
POLL_SOH,
POLL_PACK_VOLTAGE,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_MAX_CELL_VOLTAGE,
POLL_MIN_CELL_VOLTAGE,
POLL_12V,
POLL_AVG_TEMP,
POLL_MIN_TEMP,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_MAX_TEMP,
POLL_MAX_POWER,
POLL_INTERLOCK,
POLL_KWH,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CURRENT_OFFSET,
POLL_MAX_GENERATED,
POLL_MAX_AVAILABLE,
POLL_CURRENT_VOLTAGE,
POLL_CHARGING_STATUS,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_REMAINING_CHARGE,
POLL_BALANCE_CAPACITY_TOTAL,
POLL_BALANCE_TIME_TOTAL,
POLL_BALANCE_CAPACITY_SLEEP,
POLL_BALANCE_TIME_SLEEP,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_BALANCE_CAPACITY_WAKE,
POLL_BALANCE_TIME_WAKE,
POLL_BMS_STATE,
POLL_BALANCE_SWITCHES,
POLL_ENERGY_COMPLETE,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_ENERGY_PARTIAL,
POLL_SLAVE_FAILURES,
POLL_MILEAGE,
POLL_FAN_SPEED,
POLL_FAN_PERIOD,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_FAN_CONTROL,
POLL_FAN_DUTY,
POLL_TEMPORISATION,
POLL_TIME,
POLL_PACK_TIME,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_SOC_MIN,
POLL_SOC_MAX};
static uint8_t counter_373 = 0;
static uint8_t poll_index = 0;
static uint16_t currentpoll = POLL_SOC;
static uint16_t reply_poll = 0;
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
static unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was sent
static unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was sent
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
datalayer.battery.status.soh_pptt = battery_soh;
if (battery_soc >= 300) {
@ -188,13 +45,16 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.max_charge_power_W = battery_max_generated * 10;
datalayer.battery.status.temperature_min_dC = ((battery_min_temp - 640) * 0.625);
//Temperatures and voltages update at slow rate. Only publish new values once both have been sampled to avoid events
if ((battery_min_temp != 920) && (battery_max_temp != 920)) {
datalayer.battery.status.temperature_min_dC = ((battery_min_temp - 640) * 0.625);
datalayer.battery.status.temperature_max_dC = ((battery_max_temp - 640) * 0.625);
}
datalayer.battery.status.temperature_max_dC = ((battery_max_temp - 640) * 0.625);
datalayer.battery.status.cell_min_voltage_mV = (battery_min_cell_voltage * 0.976563);
datalayer.battery.status.cell_max_voltage_mV = (battery_max_cell_voltage * 0.976563);
if ((battery_min_cell_voltage != 3700) && (battery_max_cell_voltage != 3700)) {
datalayer.battery.status.cell_min_voltage_mV = (battery_min_cell_voltage * 0.976563);
datalayer.battery.status.cell_max_voltage_mV = (battery_max_cell_voltage * 0.976563);
}
if (battery_12v < 11000) { //11.000V
set_event(EVENT_12V_LOW, battery_12v);
@ -244,7 +104,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer_extended.zoePH2.battery_soc_max = battery_soc_max;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void RenaultZoeGen2Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) {
case 0x18DAF1DB: // LBC Reply from active polling
@ -265,10 +125,16 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
battery_pack_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_MAX_CELL_VOLTAGE:
battery_max_cell_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
temporary_variable = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
if (temporary_variable > 500) { //Disregard messages with value unavailable
battery_max_cell_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
}
break;
case POLL_MIN_CELL_VOLTAGE:
battery_min_cell_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
temporary_variable = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
if (temporary_variable > 500) { //Disregard messages with value unavailable
battery_min_cell_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
}
break;
case POLL_12V:
battery_12v = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
@ -375,6 +241,294 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
case POLL_SOC_MAX:
battery_soc_max = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_0:
datalayer.battery.status.cell_voltages_mV[0] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_1:
datalayer.battery.status.cell_voltages_mV[1] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_2:
datalayer.battery.status.cell_voltages_mV[2] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_3:
datalayer.battery.status.cell_voltages_mV[3] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_4:
datalayer.battery.status.cell_voltages_mV[4] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_5:
datalayer.battery.status.cell_voltages_mV[5] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_6:
datalayer.battery.status.cell_voltages_mV[6] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_7:
datalayer.battery.status.cell_voltages_mV[7] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_8:
datalayer.battery.status.cell_voltages_mV[8] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_9:
datalayer.battery.status.cell_voltages_mV[9] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_10:
datalayer.battery.status.cell_voltages_mV[10] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_11:
datalayer.battery.status.cell_voltages_mV[11] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_12:
datalayer.battery.status.cell_voltages_mV[12] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_13:
datalayer.battery.status.cell_voltages_mV[13] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_14:
datalayer.battery.status.cell_voltages_mV[14] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_15:
datalayer.battery.status.cell_voltages_mV[15] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_16:
datalayer.battery.status.cell_voltages_mV[16] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_17:
datalayer.battery.status.cell_voltages_mV[17] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_18:
datalayer.battery.status.cell_voltages_mV[18] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_19:
datalayer.battery.status.cell_voltages_mV[19] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_20:
datalayer.battery.status.cell_voltages_mV[20] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_21:
datalayer.battery.status.cell_voltages_mV[21] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_22:
datalayer.battery.status.cell_voltages_mV[22] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_23:
datalayer.battery.status.cell_voltages_mV[23] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_24:
datalayer.battery.status.cell_voltages_mV[24] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_25:
datalayer.battery.status.cell_voltages_mV[25] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_26:
datalayer.battery.status.cell_voltages_mV[26] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_27:
datalayer.battery.status.cell_voltages_mV[27] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_28:
datalayer.battery.status.cell_voltages_mV[28] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_29:
datalayer.battery.status.cell_voltages_mV[29] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_30:
datalayer.battery.status.cell_voltages_mV[30] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_31:
datalayer.battery.status.cell_voltages_mV[31] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_32:
datalayer.battery.status.cell_voltages_mV[32] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_33:
datalayer.battery.status.cell_voltages_mV[33] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_34:
datalayer.battery.status.cell_voltages_mV[34] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_35:
datalayer.battery.status.cell_voltages_mV[35] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_36:
datalayer.battery.status.cell_voltages_mV[36] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_37:
datalayer.battery.status.cell_voltages_mV[37] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_38:
datalayer.battery.status.cell_voltages_mV[38] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_39:
datalayer.battery.status.cell_voltages_mV[39] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_40:
datalayer.battery.status.cell_voltages_mV[40] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_41:
datalayer.battery.status.cell_voltages_mV[41] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_42:
datalayer.battery.status.cell_voltages_mV[42] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_43:
datalayer.battery.status.cell_voltages_mV[43] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_44:
datalayer.battery.status.cell_voltages_mV[44] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_45:
datalayer.battery.status.cell_voltages_mV[45] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_46:
datalayer.battery.status.cell_voltages_mV[46] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_47:
datalayer.battery.status.cell_voltages_mV[47] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_48:
datalayer.battery.status.cell_voltages_mV[48] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_49:
datalayer.battery.status.cell_voltages_mV[49] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_50:
datalayer.battery.status.cell_voltages_mV[50] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_51:
datalayer.battery.status.cell_voltages_mV[51] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_52:
datalayer.battery.status.cell_voltages_mV[52] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_53:
datalayer.battery.status.cell_voltages_mV[53] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_54:
datalayer.battery.status.cell_voltages_mV[54] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_55:
datalayer.battery.status.cell_voltages_mV[55] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_56:
datalayer.battery.status.cell_voltages_mV[56] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_57:
datalayer.battery.status.cell_voltages_mV[57] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_58:
datalayer.battery.status.cell_voltages_mV[58] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_59:
datalayer.battery.status.cell_voltages_mV[59] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_60:
datalayer.battery.status.cell_voltages_mV[60] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_61:
datalayer.battery.status.cell_voltages_mV[61] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_62:
datalayer.battery.status.cell_voltages_mV[62] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_63:
datalayer.battery.status.cell_voltages_mV[63] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_64:
datalayer.battery.status.cell_voltages_mV[64] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_65:
datalayer.battery.status.cell_voltages_mV[65] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_66:
datalayer.battery.status.cell_voltages_mV[66] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_67:
datalayer.battery.status.cell_voltages_mV[67] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_68:
datalayer.battery.status.cell_voltages_mV[68] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_69:
datalayer.battery.status.cell_voltages_mV[69] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_70:
datalayer.battery.status.cell_voltages_mV[70] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_71:
datalayer.battery.status.cell_voltages_mV[71] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_72:
datalayer.battery.status.cell_voltages_mV[72] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_73:
datalayer.battery.status.cell_voltages_mV[73] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_74:
datalayer.battery.status.cell_voltages_mV[74] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_75:
datalayer.battery.status.cell_voltages_mV[75] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_76:
datalayer.battery.status.cell_voltages_mV[76] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_77:
datalayer.battery.status.cell_voltages_mV[77] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_78:
datalayer.battery.status.cell_voltages_mV[78] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_79:
datalayer.battery.status.cell_voltages_mV[79] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_80:
datalayer.battery.status.cell_voltages_mV[80] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_81:
datalayer.battery.status.cell_voltages_mV[81] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_82:
datalayer.battery.status.cell_voltages_mV[82] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_83:
datalayer.battery.status.cell_voltages_mV[83] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_84:
datalayer.battery.status.cell_voltages_mV[84] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_85:
datalayer.battery.status.cell_voltages_mV[85] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_86:
datalayer.battery.status.cell_voltages_mV[86] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_87:
datalayer.battery.status.cell_voltages_mV[87] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_88:
datalayer.battery.status.cell_voltages_mV[88] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_89:
datalayer.battery.status.cell_voltages_mV[89] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_90:
datalayer.battery.status.cell_voltages_mV[90] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_91:
datalayer.battery.status.cell_voltages_mV[91] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_92:
datalayer.battery.status.cell_voltages_mV[92] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_93:
datalayer.battery.status.cell_voltages_mV[93] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_94:
datalayer.battery.status.cell_voltages_mV[94] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case POLL_CELL_95:
datalayer.battery.status.cell_voltages_mV[95] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
default: // Unknown reply
break;
}
@ -384,7 +538,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void RenaultZoeGen2Battery::transmit_can(unsigned long currentMillis) {
if (datalayer_extended.zoePH2.UserRequestNVROLReset) {
// Send NVROL reset frames
transmit_reset_nvrol_frames();
@ -416,7 +570,7 @@ void transmit_can_battery(unsigned long currentMillis) {
// Update current poll from the array
currentpoll = poll_commands[poll_index];
poll_index = (poll_index + 1) % 48;
poll_index = (poll_index + 1) % 163;
ZOE_POLL_18DADBF1.data.u8[2] = (uint8_t)((currentpoll & 0xFF00) >> 8);
ZOE_POLL_18DADBF1.data.u8[3] = (uint8_t)(currentpoll & 0x00FF);
@ -434,7 +588,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void RenaultZoeGen2Battery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen2 50kWh", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
@ -446,7 +600,7 @@ void setup_battery(void) { // Performs one time setup at startup
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
}
void transmit_can_frame_376(void) {
void RenaultZoeGen2Battery::transmit_can_frame_376(void) {
unsigned int secondsSinceProduction = ZOE_376_time_now_s - kProductionTimestamp_s;
float minutesSinceProduction = (float)secondsSinceProduction / 60.0;
float yearUnfloored = minutesSinceProduction / 255.0 / 255.0;
@ -467,7 +621,7 @@ void transmit_can_frame_376(void) {
transmit_can_frame(&ZOE_376, can_config.battery);
}
void transmit_reset_nvrol_frames(void) {
void RenaultZoeGen2Battery::transmit_reset_nvrol_frames(void) {
// NVROL reset, part 1: send 0x021003AAAAAAAAAA
ZOE_POLL_18DADBF1.data = {0x02, 0x10, 0x03, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
transmit_can_frame(&ZOE_POLL_18DADBF1, can_config.battery);
@ -497,7 +651,7 @@ void transmit_reset_nvrol_frames(void) {
wait_ms(30000);
}
void wait_ms(int duration_ms) {
void RenaultZoeGen2Battery::wait_ms(int duration_ms) {
unsigned long freezeMillis = millis();
while (millis() - freezeMillis < duration_ms) {
// Do nothing - just wait

View file

@ -2,17 +2,424 @@
#define RENAULT_ZOE_GEN2_BATTERY_H
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4100 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3000
#define MAX_CELL_DEVIATION_MV 150
#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
#define SELECTED_BATTERY_CLASS RenaultZoeGen2Battery
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
class RenaultZoeGen2Battery : 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:
static const int MAX_PACK_VOLTAGE_DV = 4100; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 3000;
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 = 2700; //Battery is put into emergency stop if one cell goes below this value
static const int POLL_SOC = 0x9001;
static const int POLL_USABLE_SOC = 0x9002;
static const int POLL_SOH = 0x9003;
static const int POLL_PACK_VOLTAGE = 0x9005;
static const int POLL_MAX_CELL_VOLTAGE = 0x9007;
static const int POLL_MIN_CELL_VOLTAGE = 0x9009;
static const int POLL_12V = 0x9011;
static const int POLL_AVG_TEMP = 0x9012;
static const int POLL_MIN_TEMP = 0x9013;
static const int POLL_MAX_TEMP = 0x9014;
static const int POLL_MAX_POWER = 0x9018;
static const int POLL_INTERLOCK = 0x901A;
static const int POLL_KWH = 0x91C8;
static const int POLL_CURRENT = 0x925D;
static const int POLL_CURRENT_OFFSET = 0x900C;
static const int POLL_MAX_GENERATED = 0x900E;
static const int POLL_MAX_AVAILABLE = 0x900F;
static const int POLL_CURRENT_VOLTAGE = 0x9130;
static const int POLL_CHARGING_STATUS = 0x9019;
static const int POLL_REMAINING_CHARGE = 0xF45B;
static const int POLL_BALANCE_CAPACITY_TOTAL = 0x924F;
static const int POLL_BALANCE_TIME_TOTAL = 0x9250;
static const int POLL_BALANCE_CAPACITY_SLEEP = 0x9251;
static const int POLL_BALANCE_TIME_SLEEP = 0x9252;
static const int POLL_BALANCE_CAPACITY_WAKE = 0x9262;
static const int POLL_BALANCE_TIME_WAKE = 0x9263;
static const int POLL_BMS_STATE = 0x9259;
static const int POLL_BALANCE_SWITCHES = 0x912B;
static const int POLL_ENERGY_COMPLETE = 0x9210;
static const int POLL_ENERGY_PARTIAL = 0x9215;
static const int POLL_SLAVE_FAILURES = 0x9129;
static const int POLL_MILEAGE = 0x91CF;
static const int POLL_FAN_SPEED = 0x912E;
static const int POLL_FAN_PERIOD = 0x91F4;
static const int POLL_FAN_CONTROL = 0x91C9;
static const int POLL_FAN_DUTY = 0x91F5;
static const int POLL_TEMPORISATION = 0x9281;
static const int POLL_TIME = 0x9261;
static const int POLL_PACK_TIME = 0x91C1;
static const int POLL_SOC_MIN = 0x91B9;
static const int POLL_SOC_MAX = 0x91BA;
static const int POLL_CELL_0 = 0x9021;
static const int POLL_CELL_1 = 0x9022;
static const int POLL_CELL_2 = 0x9023;
static const int POLL_CELL_3 = 0x9024;
static const int POLL_CELL_4 = 0x9025;
static const int POLL_CELL_5 = 0x9026;
static const int POLL_CELL_6 = 0x9027;
static const int POLL_CELL_7 = 0x9028;
static const int POLL_CELL_8 = 0x9029;
static const int POLL_CELL_9 = 0x902A;
static const int POLL_CELL_10 = 0x902B;
static const int POLL_CELL_11 = 0x902C;
static const int POLL_CELL_12 = 0x902D;
static const int POLL_CELL_13 = 0x902E;
static const int POLL_CELL_14 = 0x902F;
static const int POLL_CELL_15 = 0x9030;
static const int POLL_CELL_16 = 0x9031;
static const int POLL_CELL_17 = 0x9032;
static const int POLL_CELL_18 = 0x9033;
static const int POLL_CELL_19 = 0x9034;
static const int POLL_CELL_20 = 0x9035;
static const int POLL_CELL_21 = 0x9036;
static const int POLL_CELL_22 = 0x9037;
static const int POLL_CELL_23 = 0x9038;
static const int POLL_CELL_24 = 0x9039;
static const int POLL_CELL_25 = 0x903A;
static const int POLL_CELL_26 = 0x903B;
static const int POLL_CELL_27 = 0x903C;
static const int POLL_CELL_28 = 0x903D;
static const int POLL_CELL_29 = 0x903E;
static const int POLL_CELL_30 = 0x903F;
static const int POLL_CELL_31 = 0x9041;
static const int POLL_CELL_32 = 0x9042;
static const int POLL_CELL_33 = 0x9043;
static const int POLL_CELL_34 = 0x9044;
static const int POLL_CELL_35 = 0x9045;
static const int POLL_CELL_36 = 0x9046;
static const int POLL_CELL_37 = 0x9047;
static const int POLL_CELL_38 = 0x9048;
static const int POLL_CELL_39 = 0x9049;
static const int POLL_CELL_40 = 0x904A;
static const int POLL_CELL_41 = 0x904B;
static const int POLL_CELL_42 = 0x904C;
static const int POLL_CELL_43 = 0x904D;
static const int POLL_CELL_44 = 0x904E;
static const int POLL_CELL_45 = 0x904F;
static const int POLL_CELL_46 = 0x9050;
static const int POLL_CELL_47 = 0x9051;
static const int POLL_CELL_48 = 0x9052;
static const int POLL_CELL_49 = 0x9053;
static const int POLL_CELL_50 = 0x9054;
static const int POLL_CELL_51 = 0x9055;
static const int POLL_CELL_52 = 0x9056;
static const int POLL_CELL_53 = 0x9057;
static const int POLL_CELL_54 = 0x9058;
static const int POLL_CELL_55 = 0x9059;
static const int POLL_CELL_56 = 0x905A;
static const int POLL_CELL_57 = 0x905B;
static const int POLL_CELL_58 = 0x905C;
static const int POLL_CELL_59 = 0x905D;
static const int POLL_CELL_60 = 0x905E;
static const int POLL_CELL_61 = 0x905F;
static const int POLL_CELL_62 = 0x9061;
static const int POLL_CELL_63 = 0x9062;
static const int POLL_CELL_64 = 0x9063;
static const int POLL_CELL_65 = 0x9064;
static const int POLL_CELL_66 = 0x9065;
static const int POLL_CELL_67 = 0x9066;
static const int POLL_CELL_68 = 0x9067;
static const int POLL_CELL_69 = 0x9068;
static const int POLL_CELL_70 = 0x9069;
static const int POLL_CELL_71 = 0x906A;
static const int POLL_CELL_72 = 0x906B;
static const int POLL_CELL_73 = 0x906C;
static const int POLL_CELL_74 = 0x906D;
static const int POLL_CELL_75 = 0x906E;
static const int POLL_CELL_76 = 0x906F;
static const int POLL_CELL_77 = 0x9070;
static const int POLL_CELL_78 = 0x9071;
static const int POLL_CELL_79 = 0x9072;
static const int POLL_CELL_80 = 0x9073;
static const int POLL_CELL_81 = 0x9074;
static const int POLL_CELL_82 = 0x9075;
static const int POLL_CELL_83 = 0x9076;
static const int POLL_CELL_84 = 0x9077;
static const int POLL_CELL_85 = 0x9078;
static const int POLL_CELL_86 = 0x9079;
static const int POLL_CELL_87 = 0x907A;
static const int POLL_CELL_88 = 0x907B;
static const int POLL_CELL_89 = 0x907C;
static const int POLL_CELL_90 = 0x907D;
static const int POLL_CELL_91 = 0x907E;
static const int POLL_CELL_92 = 0x907F;
static const int POLL_CELL_93 = 0x9081;
static const int POLL_CELL_94 = 0x9082;
static const int POLL_CELL_95 = 0x9083;
uint16_t battery_soc = 0;
uint16_t battery_usable_soc = 5000;
uint16_t battery_soh = 10000;
uint16_t battery_pack_voltage = 370;
uint16_t battery_max_cell_voltage = 3700;
uint16_t battery_min_cell_voltage = 3700;
uint16_t battery_12v = 12000;
uint16_t battery_avg_temp = 920;
uint16_t battery_min_temp = 920;
uint16_t battery_max_temp = 920;
uint16_t battery_max_power = 0;
uint16_t battery_interlock = 0;
uint16_t battery_kwh = 0;
int32_t battery_current = 32640;
uint16_t battery_current_offset = 0;
uint16_t battery_max_generated = 0;
uint16_t battery_max_available = 0;
uint16_t battery_current_voltage = 0;
uint16_t battery_charging_status = 0;
uint16_t battery_remaining_charge = 0;
uint16_t battery_balance_capacity_total = 0;
uint16_t battery_balance_time_total = 0;
uint16_t battery_balance_capacity_sleep = 0;
uint16_t battery_balance_time_sleep = 0;
uint16_t battery_balance_capacity_wake = 0;
uint16_t battery_balance_time_wake = 0;
uint16_t battery_bms_state = 0;
uint16_t battery_balance_switches = 0;
uint16_t battery_energy_complete = 0;
uint16_t battery_energy_partial = 0;
uint16_t battery_slave_failures = 0;
uint16_t battery_mileage = 0;
uint16_t battery_fan_speed = 0;
uint16_t battery_fan_period = 0;
uint16_t battery_fan_control = 0;
uint16_t battery_fan_duty = 0;
uint16_t battery_temporisation = 0;
uint16_t battery_time = 0;
uint16_t battery_pack_time = 0;
uint16_t battery_soc_min = 0;
uint16_t battery_soc_max = 0;
uint16_t temporary_variable = 0;
uint32_t ZOE_376_time_now_s = 1745452800; // Initialized to make the battery think it is April 24, 2025
unsigned long kProductionTimestamp_s =
1614454107; // Production timestamp in seconds since January 1, 1970. Production timestamp used: February 25, 2021 at 8:08:27 AM GMT
CAN_frame ZOE_373 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x373,
.data = {0xC1, 0x40, 0x5D, 0xB2, 0x00, 0x01, 0xff,
0xe3}}; // FIXME: remove if not needed: {0xC1, 0x80, 0x5D, 0x5D, 0x00, 0x00, 0xff, 0xcb}};
CAN_frame ZOE_376 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x376,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
0x00}}; // fill first 6 bytes with 0's. The first 6 bytes are calculated based on the current time.
CAN_frame ZOE_POLL_18DADBF1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x18DADBF1,
.data = {0x03, 0x22, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00}};
//NVROL Reset
CAN_frame ZOE_NVROL_1_18DADBF1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x18DADBF1,
.data = {0x02, 0x10, 0x03, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}};
CAN_frame ZOE_NVROL_2_18DADBF1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x18DADBF1,
.data = {0x04, 0x31, 0x01, 0xB0, 0x09, 0x00, 0xAA, 0xAA}};
//Enable temporisation before sleep
CAN_frame ZOE_SLEEP_1_18DADBF1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x18DADBF1,
.data = {0x02, 0x10, 0x03, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}};
CAN_frame ZOE_SLEEP_2_18DADBF1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x18DADBF1,
.data = {0x04, 0x2E, 0x92, 0x81, 0x01, 0xAA, 0xAA, 0xAA}};
const uint16_t poll_commands[163] = {POLL_SOC,
POLL_USABLE_SOC,
POLL_SOH,
POLL_PACK_VOLTAGE,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_MAX_CELL_VOLTAGE,
POLL_MIN_CELL_VOLTAGE,
POLL_12V,
POLL_AVG_TEMP,
POLL_MIN_TEMP,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_MAX_TEMP,
POLL_MAX_POWER,
POLL_INTERLOCK,
POLL_KWH,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CURRENT_OFFSET,
POLL_MAX_GENERATED,
POLL_MAX_AVAILABLE,
POLL_CURRENT_VOLTAGE,
POLL_CHARGING_STATUS,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_REMAINING_CHARGE,
POLL_BALANCE_CAPACITY_TOTAL,
POLL_BALANCE_TIME_TOTAL,
POLL_BALANCE_CAPACITY_SLEEP,
POLL_BALANCE_TIME_SLEEP,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_BALANCE_CAPACITY_WAKE,
POLL_BALANCE_TIME_WAKE,
POLL_BMS_STATE,
POLL_BALANCE_SWITCHES,
POLL_ENERGY_COMPLETE,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_ENERGY_PARTIAL,
POLL_SLAVE_FAILURES,
POLL_MILEAGE,
POLL_FAN_SPEED,
POLL_FAN_PERIOD,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_FAN_CONTROL,
POLL_FAN_DUTY,
POLL_TEMPORISATION,
POLL_TIME,
POLL_PACK_TIME,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_SOC_MIN,
POLL_SOC_MAX,
POLL_CELL_0,
POLL_CELL_1,
POLL_CELL_2,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_3,
POLL_CELL_4,
POLL_CELL_5,
POLL_CELL_6,
POLL_CELL_7,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_8,
POLL_CELL_9,
POLL_CELL_10,
POLL_CELL_11,
POLL_CELL_12,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_13,
POLL_CELL_14,
POLL_CELL_15,
POLL_CELL_16,
POLL_CELL_17,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_18,
POLL_CELL_19,
POLL_CELL_20,
POLL_CELL_21,
POLL_CELL_22,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_23,
POLL_CELL_24,
POLL_CELL_25,
POLL_CELL_26,
POLL_CELL_27,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_28,
POLL_CELL_29,
POLL_CELL_30,
POLL_CELL_31,
POLL_CELL_32,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_33,
POLL_CELL_34,
POLL_CELL_35,
POLL_CELL_36,
POLL_CELL_37,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_38,
POLL_CELL_39,
POLL_CELL_40,
POLL_CELL_41,
POLL_CELL_42,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_43,
POLL_CELL_44,
POLL_CELL_45,
POLL_CELL_46,
POLL_CELL_47,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_48,
POLL_CELL_49,
POLL_CELL_50,
POLL_CELL_51,
POLL_CELL_52,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_53,
POLL_CELL_54,
POLL_CELL_55,
POLL_CELL_56,
POLL_CELL_57,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_58,
POLL_CELL_59,
POLL_CELL_60,
POLL_CELL_61,
POLL_CELL_62,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_63,
POLL_CELL_64,
POLL_CELL_65,
POLL_CELL_66,
POLL_CELL_67,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_68,
POLL_CELL_69,
POLL_CELL_70,
POLL_CELL_71,
POLL_CELL_72,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_73,
POLL_CELL_74,
POLL_CELL_75,
POLL_CELL_76,
POLL_CELL_77,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_78,
POLL_CELL_79,
POLL_CELL_80,
POLL_CELL_81,
POLL_CELL_82,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_83,
POLL_CELL_84,
POLL_CELL_85,
POLL_CELL_86,
POLL_CELL_87,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_88,
POLL_CELL_89,
POLL_CELL_90,
POLL_CELL_91,
POLL_CELL_92,
POLL_CURRENT, //Repeated to speed up update rate on this critical measurement
POLL_CELL_93,
POLL_CELL_94,
POLL_CELL_95};
uint8_t counter_373 = 0;
uint8_t poll_index = 0;
uint16_t currentpoll = POLL_SOC;
uint16_t reply_poll = 0;
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
unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was sent
/**
* @brief Transmit CAN frame 0x376
*
* @param[in] void
@ -20,66 +427,25 @@ void transmit_can_frame(CAN_frame* tx_frame, int interface);
* @return void
*
*/
void transmit_can_frame_376(void);
void transmit_can_frame_376(void);
/**
/**
* @brief Reset NVROL, by sending specific frames
*
* @param[in] void
*
* @return void
*/
void transmit_reset_nvrol_frames(void);
void transmit_reset_nvrol_frames(void);
/**
/**
* @brief Wait function
*
* @param[in] duration_ms wait duration in ms
*
* @return void
*/
void wait_ms(int duration_ms);
#define POLL_SOC 0x9001
#define POLL_USABLE_SOC 0x9002
#define POLL_SOH 0x9003
#define POLL_PACK_VOLTAGE 0x9005
#define POLL_MAX_CELL_VOLTAGE 0x9007
#define POLL_MIN_CELL_VOLTAGE 0x9009
#define POLL_12V 0x9011
#define POLL_AVG_TEMP 0x9012
#define POLL_MIN_TEMP 0x9013
#define POLL_MAX_TEMP 0x9014
#define POLL_MAX_POWER 0x9018
#define POLL_INTERLOCK 0x901A
#define POLL_KWH 0x91C8
#define POLL_CURRENT 0x925D
#define POLL_CURRENT_OFFSET 0x900C
#define POLL_MAX_GENERATED 0x900E
#define POLL_MAX_AVAILABLE 0x900F
#define POLL_CURRENT_VOLTAGE 0x9130
#define POLL_CHARGING_STATUS 0x9019
#define POLL_REMAINING_CHARGE 0xF45B
#define POLL_BALANCE_CAPACITY_TOTAL 0x924F
#define POLL_BALANCE_TIME_TOTAL 0x9250
#define POLL_BALANCE_CAPACITY_SLEEP 0x9251
#define POLL_BALANCE_TIME_SLEEP 0x9252
#define POLL_BALANCE_CAPACITY_WAKE 0x9262
#define POLL_BALANCE_TIME_WAKE 0x9263
#define POLL_BMS_STATE 0x9259
#define POLL_BALANCE_SWITCHES 0x912B
#define POLL_ENERGY_COMPLETE 0x9210
#define POLL_ENERGY_PARTIAL 0x9215
#define POLL_SLAVE_FAILURES 0x9129
#define POLL_MILEAGE 0x91CF
#define POLL_FAN_SPEED 0x912E
#define POLL_FAN_PERIOD 0x91F4
#define POLL_FAN_CONTROL 0x91C9
#define POLL_FAN_DUTY 0x91F5
#define POLL_TEMPORISATION 0x9281
#define POLL_TIME 0x9261
#define POLL_PACK_TIME 0x91C1
#define POLL_SOC_MIN 0x91B9
#define POLL_SOC_MAX 0x91BA
void wait_ms(int duration_ms);
};
#endif

View file

@ -1,83 +1,11 @@
#include "../include.h"
#ifdef RJXZS_BMS
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RJXZS-BMS.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was sent
//Actual content messages
CAN_frame RJXZS_1C = {.FD = false, .ext_ID = true, .DLC = 3, .ID = 0xF4, .data = {0x1C, 0x00, 0x02}};
CAN_frame RJXZS_10 = {.FD = false, .ext_ID = true, .DLC = 3, .ID = 0xF4, .data = {0x10, 0x00, 0x02}};
#define FIVE_MINUTES 60
static uint8_t mux = 0;
static bool setup_completed = false;
static uint16_t total_voltage = 0;
static int16_t total_current = 0;
static uint16_t total_power = 0;
static uint16_t battery_usage_capacity = 0;
static uint16_t battery_capacity_percentage = 0;
static uint16_t charging_capacity = 0;
static uint16_t charging_recovery_voltage = 0;
static uint16_t discharging_recovery_voltage = 0;
static uint16_t remaining_capacity = 0;
static int16_t host_temperature = 0;
static uint16_t status_accounting = 0;
static uint16_t equalization_starting_voltage = 0;
static uint16_t discharge_protection_voltage = 0;
static uint16_t protective_current = 0;
static uint16_t battery_pack_capacity = 0;
static uint16_t number_of_battery_strings = 0;
static uint16_t charging_protection_voltage = 0;
static int16_t protection_temperature = 0;
static bool temperature_below_zero_mod1_4 = false;
static bool temperature_below_zero_mod5_8 = false;
static bool temperature_below_zero_mod9_12 = false;
static bool temperature_below_zero_mod13_16 = false;
static uint16_t module_1_temperature = 0;
static uint16_t module_2_temperature = 0;
static uint16_t module_3_temperature = 0;
static uint16_t module_4_temperature = 0;
static uint16_t module_5_temperature = 0;
static uint16_t module_6_temperature = 0;
static uint16_t module_7_temperature = 0;
static uint16_t module_8_temperature = 0;
static uint16_t module_9_temperature = 0;
static uint16_t module_10_temperature = 0;
static uint16_t module_11_temperature = 0;
static uint16_t module_12_temperature = 0;
static uint16_t module_13_temperature = 0;
static uint16_t module_14_temperature = 0;
static uint16_t module_15_temperature = 0;
static uint16_t module_16_temperature = 0;
static uint16_t low_voltage_power_outage_protection = 0;
static uint16_t low_voltage_power_outage_delayed = 0;
static uint16_t num_of_triggering_protection_cells = 0;
static uint16_t balanced_reference_voltage = 0;
static uint16_t minimum_cell_voltage = 0;
static uint16_t maximum_cell_voltage = 0;
static uint16_t cellvoltages[MAX_AMOUNT_CELLS];
static uint8_t populated_cellvoltages = 0;
static uint16_t accumulated_total_capacity_high = 0;
static uint16_t accumulated_total_capacity_low = 0;
static uint16_t pre_charge_delay_time = 0;
static uint16_t LCD_status = 0;
static uint16_t differential_pressure_setting_value = 0;
static uint16_t use_capacity_to_automatically_reset = 0;
static uint16_t low_temperature_protection_setting_value = 0;
static uint16_t protecting_historical_logs = 0;
static uint16_t hall_sensor_type = 0;
static uint16_t fan_start_setting_value = 0;
static uint16_t ptc_heating_start_setting_value = 0;
static uint16_t default_channel_state = 0;
static uint8_t timespent_without_soc = 0;
static bool charging_active = false;
static bool discharging_active = false;
void update_values_battery() {
void RjxzsBms::update_values() {
datalayer.battery.status.real_soc = battery_capacity_percentage * 100;
if (battery_capacity_percentage == 0) {
@ -166,7 +94,7 @@ void update_values_battery() {
datalayer.battery.status.cell_min_voltage_mV = minimum_cell_voltage;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void RjxzsBms::handle_incoming_can_frame(CAN_frame rx_frame) {
/*
// All CAN messages recieved will be logged via serial
@ -571,7 +499,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void RjxzsBms::transmit_can(unsigned long currentMillis) {
// Send 10s CAN Message
if (currentMillis - previousMillis10s >= INTERVAL_10_S) {
previousMillis10s = currentMillis;
@ -588,7 +516,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void RjxzsBms::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "RJXZS BMS, DIY battery", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;

View file

@ -3,22 +3,105 @@
#include <Arduino.h>
#include "../include.h"
/* Tweak these according to your battery build */
#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 1500
#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
#define MAX_CELL_DEVIATION_MV 250
#define MAX_DISCHARGE_POWER_ALLOWED_W 5000
#define MAX_CHARGE_POWER_ALLOWED_W 5000
#define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500
#define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
#include "CanBattery.h"
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS RjxzsBms
class RjxzsBms : 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:
/* Tweak these according to your battery build */
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 1500;
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
static const int MAX_CELL_DEVIATION_MV = 250;
static const int MAX_DISCHARGE_POWER_ALLOWED_W = 5000;
static const int MAX_CHARGE_POWER_ALLOWED_W = 5000;
static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500;
static const int RAMPDOWN_SOC =
9000; // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was sent
//Actual content messages
CAN_frame RJXZS_1C = {.FD = false, .ext_ID = true, .DLC = 3, .ID = 0xF4, .data = {0x1C, 0x00, 0x02}};
CAN_frame RJXZS_10 = {.FD = false, .ext_ID = true, .DLC = 3, .ID = 0xF4, .data = {0x10, 0x00, 0x02}};
static const int FIVE_MINUTES = 60;
uint8_t mux = 0;
bool setup_completed = false;
uint16_t total_voltage = 0;
int16_t total_current = 0;
uint16_t total_power = 0;
uint16_t battery_usage_capacity = 0;
uint16_t battery_capacity_percentage = 0;
uint16_t charging_capacity = 0;
uint16_t charging_recovery_voltage = 0;
uint16_t discharging_recovery_voltage = 0;
uint16_t remaining_capacity = 0;
int16_t host_temperature = 0;
uint16_t status_accounting = 0;
uint16_t equalization_starting_voltage = 0;
uint16_t discharge_protection_voltage = 0;
uint16_t protective_current = 0;
uint16_t battery_pack_capacity = 0;
uint16_t number_of_battery_strings = 0;
uint16_t charging_protection_voltage = 0;
int16_t protection_temperature = 0;
bool temperature_below_zero_mod1_4 = false;
bool temperature_below_zero_mod5_8 = false;
bool temperature_below_zero_mod9_12 = false;
bool temperature_below_zero_mod13_16 = false;
uint16_t module_1_temperature = 0;
uint16_t module_2_temperature = 0;
uint16_t module_3_temperature = 0;
uint16_t module_4_temperature = 0;
uint16_t module_5_temperature = 0;
uint16_t module_6_temperature = 0;
uint16_t module_7_temperature = 0;
uint16_t module_8_temperature = 0;
uint16_t module_9_temperature = 0;
uint16_t module_10_temperature = 0;
uint16_t module_11_temperature = 0;
uint16_t module_12_temperature = 0;
uint16_t module_13_temperature = 0;
uint16_t module_14_temperature = 0;
uint16_t module_15_temperature = 0;
uint16_t module_16_temperature = 0;
uint16_t low_voltage_power_outage_protection = 0;
uint16_t low_voltage_power_outage_delayed = 0;
uint16_t num_of_triggering_protection_cells = 0;
uint16_t balanced_reference_voltage = 0;
uint16_t minimum_cell_voltage = 0;
uint16_t maximum_cell_voltage = 0;
uint16_t cellvoltages[MAX_AMOUNT_CELLS];
uint8_t populated_cellvoltages = 0;
uint16_t accumulated_total_capacity_high = 0;
uint16_t accumulated_total_capacity_low = 0;
uint16_t pre_charge_delay_time = 0;
uint16_t LCD_status = 0;
uint16_t differential_pressure_setting_value = 0;
uint16_t use_capacity_to_automatically_reset = 0;
uint16_t low_temperature_protection_setting_value = 0;
uint16_t protecting_historical_logs = 0;
uint16_t hall_sensor_type = 0;
uint16_t fan_start_setting_value = 0;
uint16_t ptc_heating_start_setting_value = 0;
uint16_t default_channel_state = 0;
uint8_t timespent_without_soc = 0;
bool charging_active = false;
bool discharging_active = false;
};
/* Do not modify any rows below*/
#define BATTERY_SELECTED
#define NATIVECAN_250KBPS
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef SANTA_FE_PHEV_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.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
*/
/* 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 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 uint8_t CalculateCRC8(CAN_frame rx_frame) {
int crc = 0;
static uint16_t SOC_Display = 0;
static uint16_t batterySOH = 100;
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];
for (uint8_t framepos = 0; framepos < 8; framepos++) {
crc ^= rx_frame.data.u8[framepos];
#ifdef DOUBLE_BATTERY
static uint16_t battery2_SOC_Display = 0;
static uint16_t battery2_SOH = 100;
static uint16_t battery2_CellVoltMax_mV = 3700;
static uint16_t battery2_CellVoltMin_mV = 3700;
static uint8_t battery2_CellVmaxNo = 0;
static uint8_t battery2_CellVminNo = 0;
static uint16_t battery2_allowedDischargePower = 0;
static uint16_t battery2_allowedChargePower = 0;
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
for (uint8_t j = 0; j < 8; j++) {
if ((crc & 0x80) != 0) {
crc = (crc << 1) ^ 0x1;
} else {
crc <<= 1;
}
}
}
return (uint8_t)crc;
}
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}};
void SantaFePhevBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
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>(
(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_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) {
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) {
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);
break;
case 0x4D5:
@ -127,16 +73,16 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
case 0x4DD:
break;
case 0x4DE:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x4E0:
break;
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;
break;
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]);
break;
case 0x597:
@ -146,7 +92,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
case 0x5A7:
break;
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];
break;
case 0x5AE:
@ -154,25 +100,25 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
case 0x5F1:
break;
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];
temperatureMin = rx_frame.data.u8[6]; //Lowest temp in battery
temperatureMax = rx_frame.data.u8[7]; //Highest temp in battery
break;
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]);
allowedDischargePower = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
break;
case 0x671:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->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); //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;
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);
//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) {
}
break;
@ -333,7 +279,8 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
break;
}
}
void transmit_can_battery(unsigned long currentMillis) {
void SantaFePhevBattery::transmit_can(unsigned long currentMillis) {
//Send 10ms message
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
previousMillis10 = currentMillis;
@ -344,14 +291,9 @@ void transmit_can_battery(unsigned long currentMillis) {
SANTAFE_200.data.u8[7] = checksum_200;
transmit_can_frame(&SANTAFE_200, can_config.battery);
transmit_can_frame(&SANTAFE_2A1, can_config.battery);
transmit_can_frame(&SANTAFE_2F0, can_config.battery);
#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
transmit_can_frame(&SANTAFE_200, can_interface);
transmit_can_frame(&SANTAFE_2A1, can_interface);
transmit_can_frame(&SANTAFE_2F0, can_interface);
counter_200++;
if (counter_200 > 0xF) {
@ -363,10 +305,7 @@ void transmit_can_battery(unsigned long currentMillis) {
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
transmit_can_frame(&SANTAFE_523, can_config.battery);
#ifdef DOUBLE_BATTERY
transmit_can_frame(&SANTAFE_523, can_config.battery_double);
#endif //DOUBLE_BATTERY
transmit_can_frame(&SANTAFE_523, can_interface);
}
// 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:
poll_data_pid = (poll_data_pid % 5) + 1;
SANTAFE_7E4_poll.data.u8[3] = (uint8_t)poll_data_pid;
transmit_can_frame(&SANTAFE_7E4_poll, can_config.battery);
#ifdef DOUBLE_BATTERY
transmit_can_frame(&SANTAFE_7E4_poll, can_config.battery_double);
#endif //DOUBLE_BATTERY
transmit_can_frame(&SANTAFE_7E4_poll, can_interface);
}
}
#ifdef DOUBLE_BATTERY
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
void SantaFePhevBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Santa Fe PHEV", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 96;
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.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;
datalayer.system.status.battery_allows_contactor_closing = true;
#ifdef DOUBLE_BATTERY
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;
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
datalayer_battery->info.number_of_cells = 96;
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.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;
if (allows_contactor_closing) {
*allows_contactor_closing = true;
}
}
#endif

View file

@ -1,17 +1,101 @@
#ifndef SANTA_FE_PHEV_BATTERY_H
#define SANTA_FE_PHEV_BATTERY_H
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4040 //5000 = 500.0V
#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
#define SELECTED_BATTERY_CLASS SantaFePhevBattery
uint8_t CalculateCRC8(CAN_frame rx_frame);
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
class SantaFePhevBattery : public CanBattery {
public:
// 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

View file

@ -4,34 +4,7 @@
#include "../devboard/utils/events.h"
#include "SIMPBMS-BATTERY.h"
#define SIMPBMS_MAX_CELLS 128
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
//Actual content messages
static int16_t celltemperature_max_dC = 0;
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 cells_in_series = 0;
static uint8_t voltage_level = 0;
static uint8_t ah_total = 0;
static uint8_t SOC = 0;
static uint8_t SOH = 99;
static uint8_t charge_forbidden = 0;
static uint8_t discharge_forbidden = 0;
static uint16_t cellvoltages_mV[SIMPBMS_MAX_CELLS] = {0};
void update_values_battery() {
void SimpBmsBattery::update_values() {
datalayer.battery.status.real_soc = (SOC * 100); //increase SOC range from 0-100 -> 100.00
@ -67,7 +40,7 @@ void update_values_battery() {
datalayer.battery.info.number_of_cells = cells_in_series;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void SimpBmsBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) {
case 0x355:
@ -115,11 +88,11 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void transmit_can_battery(unsigned long currentMillis) {
void SimpBmsBattery::transmit_can(unsigned long currentMillis) {
// No periodic transmitting for this battery type
}
void setup_battery(void) { // Performs one time setup at startup
void SimpBmsBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "SIMPBMS battery", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = CELL_COUNT;

View file

@ -3,17 +3,52 @@
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS SimpBmsBattery
/* DEFAULT VALUES BMS will send configured */
#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 1500
#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
#define MAX_CELL_DEVIATION_MV 500
#define CELL_COUNT 96
class SimpBmsBattery : 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);
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
private:
/* DEFAULT VALUES BMS will send configured */
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 1500;
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
static const int MAX_CELL_DEVIATION_MV = 500;
static const int CELL_COUNT = 96;
static const int SIMPBMS_MAX_CELLS = 128;
unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
//Actual content messages
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 cells_in_series = 0;
uint8_t voltage_level = 0;
uint8_t ah_total = 0;
uint8_t SOC = 0;
uint8_t SOH = 99;
uint8_t charge_forbidden = 0;
uint8_t discharge_forbidden = 0;
uint16_t cellvoltages_mV[SIMPBMS_MAX_CELLS] = {0};
};
#endif

View file

@ -1,38 +1,12 @@
#include "../include.h"
#ifdef SONO_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SONO-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was send
static uint8_t seconds = 0;
static uint8_t functionalsafetybitmask = 0;
static uint16_t batteryVoltage = 3700;
static uint16_t allowedDischargePower = 0;
static uint16_t allowedChargePower = 0;
static uint16_t CellVoltMax_mV = 0;
static uint16_t CellVoltMin_mV = 0;
static int16_t batteryAmps = 0;
static int16_t temperatureMin = 0;
static int16_t temperatureMax = 0;
static uint8_t batterySOH = 99;
static uint8_t realSOC = 99;
CAN_frame SONO_400 = {.FD = false, //Message of Vehicle Command, 100ms
.ext_ID = false,
.DLC = 8,
.ID = 0x400,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame SONO_401 = {.FD = false, //Message of Vehicle Date, 1000ms
.ext_ID = false,
.DLC = 8,
.ID = 0x400,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
void SonoBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
datalayer.battery.status.real_soc = (realSOC * 100); //increase SOC range from 0-100 -> 100.00
@ -60,7 +34,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.temperature_max_dC = temperatureMax;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void SonoBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) {
case 0x100:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
@ -137,7 +111,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
break;
}
}
void transmit_can_battery(unsigned long currentMillis) {
void SonoBattery::transmit_can(unsigned long currentMillis) {
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
@ -166,7 +140,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void SonoBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Sono Motors Sion 64kWh LFP ", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 96;

View file

@ -3,15 +3,51 @@
#include <Arduino.h>
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 2500
#define MAX_CELL_DEVIATION_MV 250
#define MAX_CELL_VOLTAGE_MV 3800 //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
#include "CanBattery.h"
uint8_t CalculateCRC8(CAN_frame rx_frame);
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS SonoBattery
class SonoBattery : 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:
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 2500;
static const int MAX_CELL_DEVIATION_MV = 250;
static const int MAX_CELL_VOLTAGE_MV = 3800; //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 previousMillis100 = 0; // will store last time a 100ms CAN Message was send
unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was send
uint8_t seconds = 0;
uint8_t functionalsafetybitmask = 0;
uint16_t batteryVoltage = 3700;
uint16_t allowedDischargePower = 0;
uint16_t allowedChargePower = 0;
uint16_t CellVoltMax_mV = 0;
uint16_t CellVoltMin_mV = 0;
int16_t batteryAmps = 0;
int16_t temperatureMin = 0;
int16_t temperatureMax = 0;
uint8_t batterySOH = 99;
uint8_t realSOC = 99;
CAN_frame SONO_400 = {.FD = false, //Message of Vehicle Command, 100ms
.ext_ID = false,
.DLC = 8,
.ID = 0x400,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame SONO_401 = {.FD = false, //Message of Vehicle Date, 1000ms
.ext_ID = false,
.DLC = 8,
.ID = 0x400,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
};
#endif

View file

@ -1,453 +1,13 @@
#include "../include.h"
#ifdef TESLA_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For Advanced Battery Insights webpage
#include "../devboard/utils/events.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/) */
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) {
switch (index) {
case 0:
@ -767,7 +327,8 @@ inline const char* getFault(bool value) {
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
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
}
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 uint16_t temp = 0;
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[] = {
{.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}},
@ -1862,9 +1422,8 @@ unsigned long lastSend118 = 0;
int index_1CF = 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,
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
@ -1874,26 +1433,26 @@ 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
}
#if defined(TESLA_MODEL_SX_BATTERY) || defined(EXP_TESLA_BMS_DIGITAL_HVIL)
if ((datalayer.system.status.inverter_allows_contactor_closing) && (datalayer.battery.status.bms_status != FAULT)) {
if (currentMillis - lastSend1CF >= 10) {
transmit_can_frame(&can_msg_1CF[index_1CF], can_config.battery);
if (operate_contactors) {
if ((datalayer.system.status.inverter_allows_contactor_closing) && (datalayer.battery.status.bms_status != FAULT)) {
if (currentMillis - lastSend1CF >= 10) {
transmit_can_frame(&can_msg_1CF[index_1CF], can_config.battery);
index_1CF = (index_1CF + 1) % 8;
lastSend1CF = currentMillis;
index_1CF = (index_1CF + 1) % 8;
lastSend1CF = currentMillis;
}
if (currentMillis - lastSend118 >= 10) {
transmit_can_frame(&can_msg_118[index_118], can_config.battery);
index_118 = (index_118 + 1) % 16;
lastSend118 = currentMillis;
}
} else {
index_1CF = 0;
index_118 = 0;
}
if (currentMillis - lastSend118 >= 10) {
transmit_can_frame(&can_msg_118[index_118], can_config.battery);
index_118 = (index_118 + 1) % 16;
lastSend118 = currentMillis;
}
} else {
index_1CF = 0;
index_118 = 0;
}
#endif //defined(TESLA_MODEL_SX_BATTERY) || defined(EXP_TESLA_BMS_DIGITAL_HVIL)
//Send 10ms message
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) {
logging.print(header);
logging.print(value);
@ -2049,7 +1614,7 @@ void print_SOC(char* header, int SOC) {
logging.println("%");
}
void printFaultCodesIfActive() {
void TeslaBattery::printFaultCodesIfActive() {
if (battery_packCtrsClosingAllowed == 0) {
logging.println(
"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");
}
void printDebugIfActive(uint8_t symbol, const char* message) {
if (symbol == 1) {
logging.println(message);
void TeslaModel3YBattery::setup(void) { // Performs one time setup at startup
if (allows_contactor_closing) {
*allows_contactor_closing = true;
}
}
void setup_battery(void) { // Performs one time setup at startup
datalayer.system.status.battery_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
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
}
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

View file

@ -1,43 +1,512 @@
#ifndef TESLA_BATTERY_H
#define TESLA_BATTERY_H
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
/* Modify these if needed */
#define MAXCHARGEPOWERALLOWED 15000 // 15000W we use a define since the value supplied by Tesla is always 0
#define MAXDISCHARGEPOWERALLOWED 60000 // 60000W we use a define since the value supplied by Tesla is always 0
/* Do not change the defines below */
#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
#ifdef TESLA_MODEL_3Y_BATTERY
#define SELECTED_BATTERY_CLASS TeslaModel3YBattery
#endif
#ifdef TESLA_MODEL_SX_BATTERY
#define SELECTED_BATTERY_CLASS TeslaModelSXBattery
#endif
//#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();
void printDebugIfActive(uint8_t symbol, const char* message);
void print_int_with_units(char* header, int value, char* units);
void print_SOC(char* header, int SOC);
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
class TeslaBattery : public CanBattery {
public:
// Use the default constructor to create the first or single battery.
TeslaBattery() { allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing; }
#ifdef DOUBLE_BATTERY
void printFaultCodesIfActive_battery2();
#endif //DOUBLE_BATTERY
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
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

View file

@ -3,153 +3,89 @@
#include "../datalayer/datalayer.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) {
logging.print(header);
logging.print(value);
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) {
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
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*/
#ifdef DEBUG_LOG
logging.println("FAKE Values going to inverter");
print_units("SOH%: ", (datalayer.battery.status.soh_pptt * 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(", 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 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(", 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("SOH%: ", (datalayer_battery->status.soh_pptt * 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(", 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 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(", Max cell voltage: ", datalayer_battery->status.cell_max_voltage_mV, "mV ");
print_units(", Min cell voltage: ", datalayer_battery->status.cell_min_voltage_mV, "mV ");
logging.println("");
#endif
}
#ifdef DOUBLE_BATTERY
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
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 TestFakeBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
}
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) {
void TestFakeBattery::transmit_can(unsigned long currentMillis) {
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
// 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));
strncpy(datalayer.system.info.battery_protocol, "Fake battery for testing purposes", 63);
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)
datalayer.battery.info.min_design_voltage_dV = 2450; // 245.0V under this, discharging further is disabled
datalayer.battery.info.number_of_cells = 96;
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer_battery->info.min_design_voltage_dV = 2450; // 245.0V under this, discharging further is disabled
datalayer_battery->info.number_of_cells = 96;
if (allows_contactor_closing) {
*allows_contactor_closing = true;
}
}
#endif

View file

@ -1,11 +1,50 @@
#ifndef TEST_FAKE_BATTERY_H
#define TEST_FAKE_BATTERY_H
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#define MAX_CELL_DEVIATION_MV 9999
#define SELECTED_BATTERY_CLASS TestFakeBattery
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
class TestFakeBattery : public CanBattery {
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

View file

@ -1,106 +1,13 @@
#include "../include.h"
#ifdef VOLVO_SPA_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 "VOLVO-SPA-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send
static unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send
static float BATT_U = 0; //0x3A
static float MAX_U = 0; //0x3A
static float MIN_U = 0; //0x3A
static float BATT_I = 0; //0x3A
static int32_t CHARGE_ENERGY = 0; //0x1A1
static uint8_t BATT_ERR_INDICATION = 0; //0x413
static float BATT_T_MAX = 0; //0x413
static float BATT_T_MIN = 0; //0x413
static float BATT_T_AVG = 0; //0x413
static uint16_t SOC_BMS = 0; //0X37D
static uint16_t SOC_CALC = 0;
static uint16_t CELL_U_MAX = 3700; //0x37D
static uint16_t CELL_U_MIN = 3700; //0x37D
static uint8_t CELL_ID_U_MAX = 0; //0x37D
static uint16_t HvBattPwrLimDchaSoft = 0; //0x369
static uint16_t HvBattPwrLimDcha1 = 0; //0x175
static uint16_t HvBattPwrLimDchaSlowAgi = 0; //0x177
static uint16_t HvBattPwrLimChrgSlowAgi = 0; //0x177
static uint8_t batteryModuleNumber = 0x10; // First battery module
static uint8_t battery_request_idx = 0;
static uint8_t rxConsecutiveFrames = 0;
static uint16_t min_max_voltage[2]; //contains cell min[0] and max[1] values in mV
static uint8_t cellcounter = 0;
static uint16_t cell_voltages[108]; //array with all the cellvoltages
static bool startedUp = false;
static uint8_t DTC_reset_counter = 0;
CAN_frame VOLVO_536 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x536,
//.data = {0x00, 0x40, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame
.data = {0x00, 0x40, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame
CAN_frame VOLVO_140_CLOSE = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x140,
.data = {0x00, 0x02, 0x00, 0xB7, 0xFF, 0x03, 0xFF, 0x82}}; //Close contactors message
CAN_frame VOLVO_140_OPEN = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x140,
.data = {0x00, 0x02, 0x00, 0x9E, 0xFF, 0x03, 0xFF, 0x82}}; //Open contactor message
CAN_frame VOLVO_372 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x372,
.data = {0x00, 0xA6, 0x07, 0x14, 0x04, 0x00, 0x80, 0x00}}; //Ambient Temp -->>VERIFY this data content!!!<<--
CAN_frame VOLVO_CELL_U_Req = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Cell voltage request frame
CAN_frame VOLVO_FlowControl = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x30, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Flowcontrol
CAN_frame VOLVO_SOH_Req = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0x49, 0x6D, 0x00, 0x00, 0x00, 0x00}}; //Battery SOH request frame
CAN_frame VOLVO_BECMsupplyVoltage_Req = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0xF4, 0x42, 0x00, 0x00, 0x00, 0x00}}; //BECM supply voltage request frame
CAN_frame VOLVO_DTC_Erase = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x04, 0x14, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}}; //Global DTC erase
CAN_frame VOLVO_BECM_ECUreset = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x02, 0x11, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00}}; //BECM ECU reset command (reboot/powercycle BECM)
CAN_frame VOLVO_DTCreadout = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x02, 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Global DTC readout
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
void VolvoSpaBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
uint8_t cnt = 0;
// Update webserver datalayer
@ -227,7 +134,7 @@ void update_values_battery() { //This function maps all the values fetched via
#endif
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) {
case 0x3A:
@ -427,7 +334,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void readCellVoltages() {
void VolvoSpaBattery::readCellVoltages() {
battery_request_idx = 0;
batteryModuleNumber = 0x10;
rxConsecutiveFrames = 0;
@ -435,7 +342,7 @@ void readCellVoltages() {
transmit_can_frame(&VOLVO_CELL_U_Req, can_config.battery); //Send cell voltage read request for first module
}
void transmit_can_battery(unsigned long currentMillis) {
void VolvoSpaBattery::transmit_can(unsigned long currentMillis) {
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
@ -470,7 +377,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void VolvoSpaBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Volvo / Polestar 69/78kWh SPA battery", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 0; // Initializes when all cells have been read

View file

@ -3,16 +3,122 @@
#include <Arduino.h>
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_108S_DV 4540
#define MIN_PACK_VOLTAGE_108S_DV 2938
#define MAX_PACK_VOLTAGE_96S_DV 4080
#define MIN_PACK_VOLTAGE_96S_DV 2620
#define MAX_CELL_DEVIATION_MV 250
#define MAX_CELL_VOLTAGE_MV 4260 // Charging is halted if one cell goes above this
#define MIN_CELL_VOLTAGE_MV 2700 // Charging is halted if one cell goes below this
#include "CanBattery.h"
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS VolvoSpaBattery
class VolvoSpaBattery : 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:
void readCellVoltages();
static const int MAX_PACK_VOLTAGE_108S_DV = 4540;
static const int MIN_PACK_VOLTAGE_108S_DV = 2938;
static const int MAX_PACK_VOLTAGE_96S_DV = 4080;
static const int MIN_PACK_VOLTAGE_96S_DV = 2620;
static const int MAX_CELL_DEVIATION_MV = 250;
static const int MAX_CELL_VOLTAGE_MV = 4260; // Charging is halted if one cell goes above this
static const int MIN_CELL_VOLTAGE_MV = 2700; // Charging is halted if one cell goes below this
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send
unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send
float BATT_U = 0; //0x3A
float MAX_U = 0; //0x3A
float MIN_U = 0; //0x3A
float BATT_I = 0; //0x3A
int32_t CHARGE_ENERGY = 0; //0x1A1
uint8_t BATT_ERR_INDICATION = 0; //0x413
float BATT_T_MAX = 0; //0x413
float BATT_T_MIN = 0; //0x413
float BATT_T_AVG = 0; //0x413
uint16_t SOC_BMS = 0; //0X37D
uint16_t SOC_CALC = 0;
uint16_t CELL_U_MAX = 3700; //0x37D
uint16_t CELL_U_MIN = 3700; //0x37D
uint8_t CELL_ID_U_MAX = 0; //0x37D
uint16_t HvBattPwrLimDchaSoft = 0; //0x369
uint16_t HvBattPwrLimDcha1 = 0; //0x175
uint16_t HvBattPwrLimDchaSlowAgi = 0; //0x177
uint16_t HvBattPwrLimChrgSlowAgi = 0; //0x177
uint8_t batteryModuleNumber = 0x10; // First battery module
uint8_t battery_request_idx = 0;
uint8_t rxConsecutiveFrames = 0;
uint16_t min_max_voltage[2]; //contains cell min[0] and max[1] values in mV
uint8_t cellcounter = 0;
uint16_t cell_voltages[108]; //array with all the cellvoltages
bool startedUp = false;
uint8_t DTC_reset_counter = 0;
CAN_frame VOLVO_536 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x536,
//.data = {0x00, 0x40, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame
.data = {0x00, 0x40, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame
CAN_frame VOLVO_140_CLOSE = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x140,
.data = {0x00, 0x02, 0x00, 0xB7, 0xFF, 0x03, 0xFF, 0x82}}; //Close contactors message
CAN_frame VOLVO_140_OPEN = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x140,
.data = {0x00, 0x02, 0x00, 0x9E, 0xFF, 0x03, 0xFF, 0x82}}; //Open contactor message
CAN_frame VOLVO_372 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x372,
.data = {0x00, 0xA6, 0x07, 0x14, 0x04, 0x00, 0x80, 0x00}}; //Ambient Temp -->>VERIFY this data content!!!<<--
CAN_frame VOLVO_CELL_U_Req = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Cell voltage request frame
CAN_frame VOLVO_FlowControl = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x30, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Flowcontrol
CAN_frame VOLVO_SOH_Req = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0x49, 0x6D, 0x00, 0x00, 0x00, 0x00}}; //Battery SOH request frame
CAN_frame VOLVO_BECMsupplyVoltage_Req = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0xF4, 0x42, 0x00, 0x00, 0x00, 0x00}}; //BECM supply voltage request frame
CAN_frame VOLVO_DTC_Erase = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x04, 0x14, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}}; //Global DTC erase
CAN_frame VOLVO_BECM_ECUreset = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x02, 0x11, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00}}; //BECM ECU reset command (reboot/powercycle BECM)
CAN_frame VOLVO_DTCreadout = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x02, 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Global DTC readout
};
#endif

View file

@ -1,108 +1,13 @@
#include "../include.h"
#ifdef VOLVO_SPA_HYBRID_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 "VOLVO-SPA-HYBRID-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send
static unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send
static float BATT_U = 0; //0x3A
static float MAX_U = 0; //0x3A
static float MIN_U = 0; //0x3A
static float BATT_I = 0; //0x3A
static int32_t CHARGE_ENERGY = 0; //0x1A1
static uint8_t BATT_ERR_INDICATION = 0; //0x413
static float BATT_T_MAX = 0; //0x413
static float BATT_T_MIN = 0; //0x413
static float BATT_T_AVG = 0; //0x413
static uint16_t SOC_BMS = 0; //0X37D
static uint16_t SOC_CALC = 0;
static uint16_t CELL_U_MAX = 3700; //0x37D
static uint16_t CELL_U_MIN = 3700; //0x37D
static uint8_t CELL_ID_U_MAX = 0; //0x37D
static uint16_t HvBattPwrLimDchaSoft = 0; //0x369
static uint16_t HvBattPwrLimDcha1 = 0; //0x175
//static uint16_t HvBattPwrLimDchaSlowAgi = 0; //0x177
//static uint16_t HvBattPwrLimChrgSlowAgi = 0; //0x177
//static uint8_t batteryModuleNumber = 0x10; // First battery module
static uint8_t battery_request_idx = 0;
static uint8_t rxConsecutiveFrames = 0;
static uint16_t min_max_voltage[2]; //contains cell min[0] and max[1] values in mV
static uint8_t cellcounter = 0;
static uint32_t remaining_capacity = 0;
static uint16_t cell_voltages[102]; //array with all the cellvoltages
static bool startedUp = false;
static uint8_t DTC_reset_counter = 0;
CAN_frame VOLVO_536 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x536,
//.data = {0x00, 0x40, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame
.data = {0x00, 0x40, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame
CAN_frame VOLVO_140_CLOSE = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x140,
.data = {0x00, 0x02, 0x00, 0xB7, 0xFF, 0x03, 0xFF, 0x82}}; //Close contactors message
CAN_frame VOLVO_140_OPEN = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x140,
.data = {0x00, 0x02, 0x00, 0x9E, 0xFF, 0x03, 0xFF, 0x82}}; //Open contactor message
CAN_frame VOLVO_372 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x372,
.data = {0x00, 0xA6, 0x07, 0x14, 0x04, 0x00, 0x80, 0x00}}; //Ambient Temp -->>VERIFY this data content!!!<<--
CAN_frame VOLVO_CELL_U_Req = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0x48, 0x06, 0x00, 0x00, 0x00, 0x00}}; //Cell voltage request frame // changed
CAN_frame VOLVO_FlowControl = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x30, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Flowcontrol
CAN_frame VOLVO_SOH_Req = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0x49, 0x6D, 0x00, 0x00, 0x00, 0x00}}; //Battery SOH request frame
CAN_frame VOLVO_BECMsupplyVoltage_Req = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0xF4, 0x42, 0x00, 0x00, 0x00, 0x00}}; //BECM supply voltage request frame
CAN_frame VOLVO_DTC_Erase = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x04, 0x14, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}}; //Global DTC erase
CAN_frame VOLVO_BECM_ECUreset = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x02, 0x11, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00}}; //BECM ECU reset command (reboot/powercycle BECM)
CAN_frame VOLVO_DTCreadout = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x02, 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Global DTC readout
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
void VolvoSpaHybridBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
uint8_t cnt = 0;
// Update webserver datalayer
@ -213,7 +118,7 @@ void update_values_battery() { //This function maps all the values fetched via
#endif
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) {
case 0x3A:
@ -602,7 +507,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
}
}
void readCellVoltages() {
void VolvoSpaHybridBattery::readCellVoltages() {
battery_request_idx = 0;
//batteryModuleNumber = 0x10;
rxConsecutiveFrames = 0;
@ -610,7 +515,7 @@ void readCellVoltages() {
transmit_can_frame(&VOLVO_CELL_U_Req, can_config.battery); //Send cell voltage read request for first module
}
void transmit_can_battery(unsigned long currentMillis) {
void VolvoSpaHybridBattery::transmit_can(unsigned long currentMillis) {
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
@ -648,7 +553,7 @@ void transmit_can_battery(unsigned long currentMillis) {
}
}
void setup_battery(void) { // Performs one time setup at startup
void VolvoSpaHybridBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Volvo PHEV battery", 63); //changed
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 102; //was 108, changed

View file

@ -3,14 +3,122 @@
#include <Arduino.h>
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4294 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 2754
#define MAX_CELL_DEVIATION_MV 250
#define MAX_CELL_VOLTAGE_MV 4210 //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
#include "CanBattery.h"
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
#define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS VolvoSpaHybridBattery
class VolvoSpaHybridBattery : 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:
void readCellVoltages();
static const int MAX_PACK_VOLTAGE_DV = 4294; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 2754;
static const int MAX_CELL_DEVIATION_MV = 250;
static const int MAX_CELL_VOLTAGE_MV = 4210; //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 previousMillis100 = 0; // will store last time a 100ms CAN Message was send
unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send
unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send
float BATT_U = 0; //0x3A
float MAX_U = 0; //0x3A
float MIN_U = 0; //0x3A
float BATT_I = 0; //0x3A
int32_t CHARGE_ENERGY = 0; //0x1A1
uint8_t BATT_ERR_INDICATION = 0; //0x413
float BATT_T_MAX = 0; //0x413
float BATT_T_MIN = 0; //0x413
float BATT_T_AVG = 0; //0x413
uint16_t SOC_BMS = 0; //0X37D
uint16_t SOC_CALC = 0;
uint16_t CELL_U_MAX = 3700; //0x37D
uint16_t CELL_U_MIN = 3700; //0x37D
uint8_t CELL_ID_U_MAX = 0; //0x37D
uint16_t HvBattPwrLimDchaSoft = 0; //0x369
uint16_t HvBattPwrLimDcha1 = 0; //0x175
//uint16_t HvBattPwrLimDchaSlowAgi = 0; //0x177
//uint16_t HvBattPwrLimChrgSlowAgi = 0; //0x177
//uint8_t batteryModuleNumber = 0x10; // First battery module
uint8_t battery_request_idx = 0;
uint8_t rxConsecutiveFrames = 0;
uint16_t min_max_voltage[2]; //contains cell min[0] and max[1] values in mV
uint8_t cellcounter = 0;
uint32_t remaining_capacity = 0;
uint16_t cell_voltages[102]; //array with all the cellvoltages
bool startedUp = false;
uint8_t DTC_reset_counter = 0;
CAN_frame VOLVO_536 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x536,
//.data = {0x00, 0x40, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame
.data = {0x00, 0x40, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame
CAN_frame VOLVO_140_CLOSE = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x140,
.data = {0x00, 0x02, 0x00, 0xB7, 0xFF, 0x03, 0xFF, 0x82}}; //Close contactors message
CAN_frame VOLVO_140_OPEN = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x140,
.data = {0x00, 0x02, 0x00, 0x9E, 0xFF, 0x03, 0xFF, 0x82}}; //Open contactor message
CAN_frame VOLVO_372 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x372,
.data = {0x00, 0xA6, 0x07, 0x14, 0x04, 0x00, 0x80, 0x00}}; //Ambient Temp -->>VERIFY this data content!!!<<--
CAN_frame VOLVO_CELL_U_Req = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0x48, 0x06, 0x00, 0x00, 0x00, 0x00}}; //Cell voltage request frame // changed
CAN_frame VOLVO_FlowControl = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x30, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Flowcontrol
CAN_frame VOLVO_SOH_Req = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0x49, 0x6D, 0x00, 0x00, 0x00, 0x00}}; //Battery SOH request frame
CAN_frame VOLVO_BECMsupplyVoltage_Req = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x03, 0x22, 0xF4, 0x42, 0x00, 0x00, 0x00, 0x00}}; //BECM supply voltage request frame
CAN_frame VOLVO_DTC_Erase = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x04, 0x14, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}}; //Global DTC erase
CAN_frame VOLVO_BECM_ECUreset = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x735,
.data = {0x02, 0x11, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00}}; //BECM ECU reset command (reboot/powercycle BECM)
CAN_frame VOLVO_DTCreadout = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x02, 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Global DTC readout
};
#endif

View file

@ -225,7 +225,7 @@ void handle_contactors() {
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
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_POSITIVE_CONTACTOR_PIN, ON);
datalayer.system.status.contactors_battery2_engaged = true;

View file

@ -2,20 +2,22 @@
#include "../../datalayer/datalayer.h"
#include "../../datalayer/datalayer_extended.h"
#include "../../include.h"
// Parameters
#ifdef PRECHARGE_CONTROL
// Parameters
#define MAX_PRECHARGE_TIME_MS 15000 // Maximum time precharge may be enabled
#define Precharge_default_PWM_Freq 11000
#define Precharge_min_PWM_Freq 5000
#define Precharge_max_PWM_Freq 34000
#define PWM_Res 8
#define PWM_OFF_DUTY 0
#define Precharge_PWM_Res 8
#define PWM_Freq 20000 // 20 kHz frequency, beyond audible range
#define PWM_Precharge_Channel 0
#ifndef INVERTER_DISCONNECT_CONTACTOR_IS_NORMALLY_OPEN
#define ON 0 //Normally closed contactors use inverted logic
#define OFF 1 //Normally closed contactors use inverted logic
#else
#define ON 1
#define OFF 0
#endif
unsigned long prechargeStartTime = 0;
static uint32_t freq = Precharge_default_PWM_Freq;
uint16_t delta_freq = 1;
@ -28,48 +30,33 @@ void init_precharge_control() {
#ifdef DEBUG_LOG
logging.printf("Precharge control initialised\n");
#endif
pinMode(PRECHARGE_PIN, OUTPUT);
digitalWrite(PRECHARGE_PIN, LOW);
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
pinMode(HIA4V1_PIN, OUTPUT);
digitalWrite(HIA4V1_PIN, LOW);
pinMode(INVERTER_DISCONNECT_CONTACTOR_PIN, OUTPUT);
digitalWrite(INVERTER_DISCONNECT_CONTACTOR_PIN, LOW);
}
// Main functions
void handle_precharge_control() {
unsigned long currentTime = millis();
#ifdef MEB_BATTERY
void handle_precharge_control(unsigned long currentMillis) {
int32_t target_voltage = datalayer.battery.status.voltage_dV;
int32_t external_voltage = datalayer_extended.meb.BMS_voltage_intermediate_dV;
#endif
// Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge
switch (datalayer.system.status.precharge_status) {
case AUTO_PRECHARGE_IDLE:
#if 0
if (datalayer.battery.status.bms_status != FAULT && datalayer.battery.status.real_bms_status == BMS_STANDBY &&
/*datalayer.system.status.inverter_allows_contactor_closing &&*/
!datalayer.system.settings.equipment_stop_active) {
datalayer.system.status.precharge_status = AUTO_PRECHARGE_START;
}
#else
if (datalayer.system.settings.start_precharging) {
datalayer.system.status.precharge_status = AUTO_PRECHARGE_START;
}
#endif
break;
case AUTO_PRECHARGE_START:
freq = Precharge_default_PWM_Freq;
ledcAttachChannel(PRECHARGE_PIN, freq, PWM_Res, PWM_Precharge_Channel);
ledcWriteTone(PRECHARGE_PIN, freq); // Set frequency and set dutycycle to 50%
prechargeStartTime = currentTime;
ledcAttachChannel(HIA4V1_PIN, freq, Precharge_PWM_Res, PWM_Precharge_Channel);
ledcWriteTone(HIA4V1_PIN, freq); // Set frequency and set dutycycle to 50%
prechargeStartTime = currentMillis;
datalayer.system.status.precharge_status = AUTO_PRECHARGE_PRECHARGING;
#ifdef DEBUG_LOG
logging.printf("Precharge: Starting sequence\n");
#endif
digitalWrite(POSITIVE_CONTACTOR_PIN, HIGH);
digitalWrite(INVERTER_DISCONNECT_CONTACTOR_PIN, OFF);
break;
case AUTO_PRECHARGE_PRECHARGING:
@ -97,24 +84,24 @@ void handle_precharge_control() {
logging.printf("Precharge: Target: %d V Extern: %d V Frequency: %u\n", target_voltage / 10,
external_voltage / 10, freq);
#endif
ledcWriteTone(PRECHARGE_PIN, freq);
ledcWriteTone(HIA4V1_PIN, freq);
}
if ((datalayer.battery.status.real_bms_status != BMS_STANDBY &&
datalayer.battery.status.real_bms_status != BMS_ACTIVE) ||
datalayer.battery.status.bms_status != ACTIVE || datalayer.system.settings.equipment_stop_active) {
pinMode(PRECHARGE_PIN, OUTPUT);
digitalWrite(PRECHARGE_PIN, LOW);
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
pinMode(HIA4V1_PIN, OUTPUT);
digitalWrite(HIA4V1_PIN, LOW);
digitalWrite(INVERTER_DISCONNECT_CONTACTOR_PIN, ON);
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
#ifdef DEBUG_LOG
logging.printf("Precharge: Disabling Precharge bms not standby/active or equipment stop\n");
#endif
} else if (currentTime - prechargeStartTime >= MAX_PRECHARGE_TIME_MS ||
} else if (currentMillis - prechargeStartTime >= MAX_PRECHARGE_TIME_MS ||
datalayer.battery.status.real_bms_status == BMS_FAULT) {
pinMode(PRECHARGE_PIN, OUTPUT);
digitalWrite(PRECHARGE_PIN, LOW);
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
pinMode(HIA4V1_PIN, OUTPUT);
digitalWrite(HIA4V1_PIN, LOW);
digitalWrite(INVERTER_DISCONNECT_CONTACTOR_PIN, ON);
datalayer.system.status.precharge_status = AUTO_PRECHARGE_OFF;
#ifdef DEBUG_LOG
logging.printf("Precharge: Disabled (timeout reached / BMS fault) -> AUTO_PRECHARGE_OFF\n");
@ -123,9 +110,9 @@ void handle_precharge_control() {
// Add event
} else if (datalayer.system.status.battery_allows_contactor_closing) {
pinMode(PRECHARGE_PIN, OUTPUT);
digitalWrite(PRECHARGE_PIN, LOW);
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
pinMode(HIA4V1_PIN, OUTPUT);
digitalWrite(HIA4V1_PIN, LOW);
digitalWrite(INVERTER_DISCONNECT_CONTACTOR_PIN, ON);
datalayer.system.status.precharge_status = AUTO_PRECHARGE_COMPLETED;
#ifdef DEBUG_LOG
logging.printf("Precharge: Disabled (contacts closed) -> COMPLETED\n");
@ -147,8 +134,8 @@ void handle_precharge_control() {
!datalayer.system.status.inverter_allows_contactor_closing ||
datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != FAULT) {
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
pinMode(PRECHARGE_PIN, OUTPUT);
digitalWrite(PRECHARGE_PIN, LOW);
pinMode(HIA4V1_PIN, OUTPUT);
digitalWrite(HIA4V1_PIN, LOW);
#ifdef DEBUG_LOG
logging.printf("Precharge: equipment stop activated -> IDLE\n");
#endif
@ -159,4 +146,4 @@ void handle_precharge_control() {
break;
}
}
#endif // AUTO_PRECHARGE_CONTROL
#endif // PRECHARGE_CONTROL

View file

@ -17,19 +17,10 @@ void init_precharge_control();
/**
* @brief Handle contactors
*
* @param[in] void
* @param[in] unsigned long currentMillis
*
* @return void
*/
void handle_precharge_control();
/**
* @brief Handle contactors of battery 2
*
* @param[in] void
*
* @return void
*/
void handle_contactors_battery2();
void handle_precharge_control(unsigned long currentMillis);
#endif // _PRECHARGE_CONTROL_H_

View file

@ -297,10 +297,12 @@ typedef struct {
* we report the inverter as missing entirely on the CAN bus.
*/
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;
/** 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 */
bool inverter_allows_contactor_closing = true;
#ifdef CONTACTOR_CONTROL

View file

@ -294,6 +294,38 @@ typedef struct {
uint64_t cumulative_energy_in_regen = 0;
} DATALAYER_INFO_CMFAEV;
typedef struct {
uint8_t MainConnectorState = 0;
uint16_t InsulationResistance = 0;
} DATALAYER_INFO_ECMP;
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 {
uint8_t total_cell_count = 0;
int16_t battery_12V = 0;
@ -303,16 +335,6 @@ typedef struct {
uint8_t batteryManagementMode = 0;
uint8_t BMS_ign = 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;
typedef struct {
@ -791,7 +813,10 @@ class DataLayerExtended {
DATALAYER_INFO_BYDATTO3 bydAtto3;
DATALAYER_INFO_CELLPOWER cellpower;
DATALAYER_INFO_CMFAEV CMFAEV;
DATALAYER_INFO_ECMP stellantisECMP;
DATALAYER_INFO_GEELY_GEOMETRY_C geometryC;
DATALAYER_INFO_KIAHYUNDAI64 KiaHyundai64;
DATALAYER_INFO_KIAHYUNDAI64 KiaHyundai64_2;
DATALAYER_INFO_TESLA tesla;
DATALAYER_INFO_NISSAN_LEAF nissanleaf;
DATALAYER_INFO_MEB meb;

View file

@ -61,6 +61,10 @@
// SMA CAN contactor pins
#define INVERTER_CONTACTOR_ENABLE_PIN 36
// Automatic precharging
#define HIA4V1_PIN 25
#define INVERTER_DISCONNECT_CONTACTOR_PIN 32
// SD card
//#define SD_MISO_PIN 2
//#define SD_MOSI_PIN 15

View file

@ -62,6 +62,10 @@ The pin layout below supports the following:
// Equipment stop pin
#define EQUIPMENT_STOP_PIN GPIO_NUM_12
// Automatic precharging
#define HIA4V1_PIN GPIO_NUM_17
#define INVERTER_DISCONNECT_CONTACTOR_PIN GPIO_NUM_5
// BMW_I3_BATTERY wake up pin
#ifdef BMW_I3_BATTERY
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1

View file

@ -53,6 +53,10 @@
#define PRECHARGE_PIN 25
#define BMS_POWER 18 // Note, this pin collides with CAN add-ons and Chademo
// Automatic precharging
#define HIA4V1_PIN 25
#define INVERTER_DISCONNECT_CONTACTOR_PIN 32
// SMA CAN contactor pins
#define INVERTER_CONTACTOR_ENABLE_PIN 5

View file

@ -51,6 +51,10 @@ GPIOs on extra header
#define PRECHARGE_PIN 25
#define BMS_POWER 23
// Automatic precharging
#define HIA4V1_PIN 19 //Available as extra GPIO via pin header
#define INVERTER_DISCONNECT_CONTACTOR_PIN 25
// SMA CAN contactor pins
#define INVERTER_CONTACTOR_ENABLE_PIN 2

View file

@ -70,6 +70,7 @@ SensorConfig sensorConfigTemplate[] = {
{"state_of_health", "State Of Health", "", "%", "battery"},
{"temperature_min", "Temperature Min", "", "°C", "temperature"},
{"temperature_max", "Temperature Max", "", "°C", "temperature"},
{"cpu_temp", "CPU Temperature", "", "°C", "temperature"},
{"stat_batt_power", "Stat Batt Power", "", "W", "power"},
{"battery_current", "Battery Current", "", "A", "current"},
{"cell_max_voltage", "Cell Max Voltage", "", "V", "voltage"},
@ -169,6 +170,7 @@ void set_battery_attributes(JsonDocument& doc, const DATALAYER_BATTERY_TYPE& bat
doc["state_of_health" + suffix] = ((float)battery.status.soh_pptt) / 100.0;
doc["temperature_min" + suffix] = ((float)((int16_t)battery.status.temperature_min_dC)) / 10.0;
doc["temperature_max" + suffix] = ((float)((int16_t)battery.status.temperature_max_dC)) / 10.0;
doc["cpu_temp" + suffix] = datalayer.system.info.CPU_temperature;
doc["stat_batt_power" + suffix] = ((float)((int32_t)battery.status.active_power_W));
doc["battery_current" + suffix] = ((float)((int16_t)battery.status.current_dA)) / 10.0;
doc["battery_voltage" + suffix] = ((float)battery.status.voltage_dV) / 10.0;

View file

@ -458,32 +458,80 @@ String advanced_battery_processor(const String& var) {
"<h4>Cumulative energy regen: " + String(datalayer_extended.CMFAEV.cumulative_energy_in_regen) + "Wh</h4>";
#endif //CMFA_EV_BATTERY
#ifdef STELLANTIS_ECMP_BATTERY
content += "<h4>Main Connector State: ";
if (datalayer_extended.stellantisECMP.MainConnectorState == 0) {
content += "Contactors open</h4>";
} else if (datalayer_extended.stellantisECMP.MainConnectorState == 0x01) {
content += "Precharged</h4>";
} else {
content += "Invalid</h4>";
}
content +=
"<h4>Insulation Resistance: " + String(datalayer_extended.stellantisECMP.InsulationResistance) + "kOhm</h4>";
#endif //STELLANTIS_ECMP_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
content += "<h4>Cells: " + String(datalayer_extended.KiaHyundai64.total_cell_count) + "S</h4>";
content += "<h4>12V voltage: " + String(datalayer_extended.KiaHyundai64.battery_12V / 10.0, 1) + "</h4>";
content += "<h4>Waterleakage: " + String(datalayer_extended.KiaHyundai64.waterleakageSensor) + "</h4>";
content +=
"<h4>Temperature, water inlet: " + String(datalayer_extended.KiaHyundai64.temperature_water_inlet) + "</h4>";
content +=
"<h4>Temperature, power relay: " + String(datalayer_extended.KiaHyundai64.powerRelayTemperature) + "</h4>";
content += "<h4>Batterymanagement mode: " + String(datalayer_extended.KiaHyundai64.batteryManagementMode) + "</h4>";
content += "<h4>BMS ignition: " + String(datalayer_extended.KiaHyundai64.BMS_ign) + "</h4>";
content += "<h4>Battery relay: " + String(datalayer_extended.KiaHyundai64.batteryRelay) + "</h4>";
auto print_hyundai = [&content](DATALAYER_INFO_KIAHYUNDAI64& data) {
content += "<h4>Cells: " + String(data.total_cell_count) + "S</h4>";
content += "<h4>12V voltage: " + String(data.battery_12V / 10.0, 1) + "</h4>";
content += "<h4>Waterleakage: " + String(data.waterleakageSensor) + "</h4>";
content += "<h4>Temperature, water inlet: " + String(data.temperature_water_inlet) + "</h4>";
content += "<h4>Temperature, power relay: " + String(data.powerRelayTemperature) + "</h4>";
content += "<h4>Batterymanagement mode: " + String(data.batteryManagementMode) + "</h4>";
content += "<h4>BMS ignition: " + String(data.BMS_ign) + "</h4>";
content += "<h4>Battery relay: " + String(data.batteryRelay) + "</h4>";
};
print_hyundai(datalayer_extended.KiaHyundai64);
#ifdef DOUBLE_BATTERY
content += "<h4>Values from battery 2</h4>";
content += "<h4>Cells: " + String(datalayer_extended.KiaHyundai64.battery2_total_cell_count) + "S</h4>";
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>";
print_hyundai(datalayer_extended.KiaHyundai64_2);
#endif //DOUBLE_BATTERY
#endif //KIA_HYUNDAI_64_BATTERY
@ -1496,11 +1544,12 @@ String advanced_battery_processor(const String& var) {
content += "<button onclick='Volvo_BECMecuReset()'>Restart BECM module</button>";
#endif // VOLVO_SPA_HYBRID_BATTERY
#if !defined(BMW_PHEV_BATTERY) && !defined(BMW_IX_BATTERY) && !defined(BOLT_AMPERA_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(MEB_BATTERY) && !defined(VOLVO_SPA_BATTERY) && !defined(VOLVO_SPA_HYBRID_BATTERY) && \
!defined(KIA_HYUNDAI_64_BATTERY) && !defined(CMFA_EV_BATTERY) && \
#if !defined(BMW_PHEV_BATTERY) && !defined(BMW_IX_BATTERY) && !defined(BOLT_AMPERA_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(MEB_BATTERY) && !defined(VOLVO_SPA_BATTERY) && !defined(VOLVO_SPA_HYBRID_BATTERY) && \
!defined(KIA_HYUNDAI_64_BATTERY) && !defined(CMFA_EV_BATTERY) && !defined(STELLANTIS_ECMP_BATTERY) && \
!defined(KIA_HYUNDAI_64_BATTERY) && !defined(GEELY_GEOMETRY_C_BATTERY) && !defined(CMFA_EV_BATTERY) && \
!defined(RENAULT_ZOE_GEN1_BATTERY) //Only the listed types have extra info
content += "No extra information available for this battery type";
#endif

View file

@ -39,13 +39,10 @@ String settings_processor(const String& var) {
String(getCANInterfaceName(can_config.battery_double)) + "</span></h4>";
#endif // DOUBLE_BATTERY
#ifdef CAN_INVERTER_SELECTED
content += "<h4 style='color: white;'>Inverter interface: <span id='Inverter'>" +
String(getCANInterfaceName(can_config.inverter)) + "</span></h4>";
#endif //CAN_INVERTER_SELECTED
#ifdef MODBUS_INVERTER_SELECTED
content += "<h4 style='color: white;'>Inverter interface: RS485<span id='Inverter'></span></h4>";
#endif
if (inverter) {
content += "<h4 style='color: white;'>Inverter interface: <span id='Inverter'>" +
String(inverter->interface_name()) + "</span></h4>";
}
#ifdef CAN_SHUNT_SELECTED
content += "<h4 style='color: white;'>Shunt Interface: <span id='Shunt'>" +

View file

@ -1379,7 +1379,7 @@ String processor(const String& var) {
content += "<h4>Automatic contactor closing allowed:</h4>";
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>";
} else {
content += "<span style='color: red;'>&#10005;</span>";

View file

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

View file

@ -1,8 +1,7 @@
#include "../include.h"
#ifdef AFORE_CAN
#include "AFORE-CAN.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "AFORE-CAN.h"
#include "../include.h"
#define SOCMAX 100
#define SOCMIN 1
@ -175,4 +174,3 @@ void AforeCanInverter::setup(void) { // Performs one time setup at startup over
strncpy(datalayer.system.info.inverter_protocol, "Afore battery over CAN", 63);
datalayer.system.info.inverter_protocol[63] = '\0';
}
#endif

View file

@ -2,8 +2,10 @@
#define AFORE_CAN_H
#include "../include.h"
#ifdef AFORE_CAN
#define CAN_INVERTER_SELECTED
#define SELECTED_INVERTER_CLASS AforeCanInverter
#endif
#include "CanInverterProtocol.h"

View file

@ -1,8 +1,7 @@
#include "../include.h"
#ifdef BYD_CAN
#include "BYD-CAN.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "BYD-CAN.h"
#include "../include.h"
/* Do not change code below unless you are sure what you are doing */
@ -175,4 +174,3 @@ void BydCanInverter::setup(void) { // Performs one time setup at startup over C
strncpy(datalayer.system.info.inverter_protocol, "BYD Battery-Box Premium HVS over CAN Bus", 63);
datalayer.system.info.inverter_protocol[63] = '\0';
}
#endif

View file

@ -2,11 +2,10 @@
#define BYD_CAN_H
#include "../include.h"
#ifdef BYD_CAN
#define CAN_INVERTER_SELECTED
#define SELECTED_INVERTER_CLASS BydCanInverter
#define FW_MAJOR_VERSION 0x03
#define FW_MINOR_VERSION 0x29
#endif
#include "CanInverterProtocol.h"
@ -23,7 +22,9 @@ class BydCanInverter : public CanInverterProtocol {
unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send
unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send
#define VOLTAGE_OFFSET_DV 20
static const int FW_MAJOR_VERSION = 0x03;
static const int FW_MINOR_VERSION = 0x29;
static const int VOLTAGE_OFFSET_DV = 20;
CAN_frame BYD_250 = {.FD = false,
.ext_ID = false,

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef BYD_MODBUS
#include "BYD-MODBUS.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../lib/eModbus-eModbus/scripts/mbServerFCs.h"
#include "BYD-MODBUS.h"
// For modbus register definitions, see https://gitlab.com/pelle8/inverter_resources/-/blob/main/byd_registers_modbus_rtu.md
@ -166,5 +165,3 @@ void BydModbusInverter::setup(void) { // Performs one time setup at startup ove
// Start ModbusRTU background task
MBserver.begin(Serial2, MODBUS_CORE);
}
#endif

View file

@ -2,8 +2,10 @@
#define BYD_MODBUS_H
#include "../include.h"
#ifdef BYD_MODBUS
#define MODBUS_INVERTER_SELECTED
#define SELECTED_INVERTER_CLASS BydModbusInverter
#endif
#include "ModbusInverterProtocol.h"
@ -20,7 +22,7 @@ class BydModbusInverter : public ModbusInverterProtocol {
void handle_update_data_modbusp301_byd();
//BYD Modbus specific value
const int MAX_POWER = 40960;
const uint16_t MAX_POWER = 40960;
};
#endif

View file

@ -7,6 +7,7 @@
class CanInverterProtocol : public InverterProtocol {
public:
virtual const char* interface_name() { return getCANInterfaceName(can_config.inverter); }
virtual void transmit_can(unsigned long currentMillis) = 0;
virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0;
};

View file

@ -1,8 +1,7 @@
#include "../include.h"
#ifdef FERROAMP_CAN
#include "FERROAMP-CAN.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "FERROAMP-CAN.h"
#include "../include.h"
void FerroampCanInverter::
update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages
@ -365,4 +364,3 @@ void FerroampCanInverter::setup(void) { // Performs one time setup at startup o
strncpy(datalayer.system.info.inverter_protocol, "Ferroamp Pylon battery over CAN bus", 63);
datalayer.system.info.inverter_protocol[63] = '\0';
}
#endif

View file

@ -4,17 +4,16 @@
#include "CanInverterProtocol.h"
#ifdef FERROAMP_CAN
#define CAN_INVERTER_SELECTED
#define SELECTED_INVERTER_CLASS FerroampCanInverter
#endif
class FerroampCanInverter : public CanInverterProtocol {
public:
void setup();
void update_values();
void transmit_can(unsigned long currentMillis);
void map_can_frame_to_variable(CAN_frame rx_frame);
private:
@ -27,13 +26,13 @@ class FerroampCanInverter : public CanInverterProtocol {
//useful for some inverters like Sofar that report the voltages incorrect otherwise
#define SET_30K_OFFSET //If defined, current values are sent with a 30k offest (useful for ferroamp)
/* Some inverters need to see a specific amount of cells/modules to emulate a specific Pylon battery.
/* Some inverters need to see a specific amount of cells/modules to emulate a specific Pylon battery.
Change the following only if your inverter is generating fault codes about voltage range */
#define TOTAL_CELL_AMOUNT 120 //Adjust this parameter in steps of 120 to add another 14,2kWh of capacity
#define MODULES_IN_SERIES 4
#define CELLS_PER_MODULE 30
#define VOLTAGE_LEVEL 384
#define AH_CAPACITY 37
static const int TOTAL_CELL_AMOUNT = 120; //Adjust this parameter in steps of 120 to add another 14,2kWh of capacity
static const int MODULES_IN_SERIES = 4;
static const int CELLS_PER_MODULE = 30;
static const int VOLTAGE_LEVEL = 384;
static const int AH_CAPACITY = 37;
//Actual content messages
CAN_frame PYLON_7310 = {.FD = false,
@ -50,14 +49,14 @@ Change the following only if your inverter is generating fault codes about volta
.ext_ID = true,
.DLC = 8,
.ID = 0x7320,
.data = {TOTAL_CELL_AMOUNT, (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES,
.data = {(TOTAL_CELL_AMOUNT & 0xFF), (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES,
CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8),
AH_CAPACITY, (uint8_t)(AH_CAPACITY >> 8)}};
CAN_frame PYLON_7321 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x7321,
.data = {TOTAL_CELL_AMOUNT, (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES,
.data = {(TOTAL_CELL_AMOUNT & 0xFF), (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES,
CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8),
AH_CAPACITY, (uint8_t)(AH_CAPACITY >> 8)}};
CAN_frame PYLON_4210 = {.FD = false,

View file

@ -1,8 +1,8 @@
#include "../include.h"
#ifdef FOXESS_CAN
#include "FOXESS-CAN.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "FOXESS-CAN.h"
#include "../include.h"
/* Based on info from this excellent repo: https://github.com/FozzieUK/FoxESS-Canbus-Protocol */
/* The FoxESS protocol emulates the stackable (1-8) 48V towers found in the HV2600 / ECS4100 batteries
@ -22,352 +22,9 @@ below that you can customize, incase you use a lower voltage battery with this p
#define TOTAL_LIFETIME_WH_ACCUMULATED 0 //We dont have this value in the emulator
/* Do not change code below unless you are sure what you are doing */
static int16_t temperature_average = 0;
static uint16_t voltage_per_pack = 0;
static int16_t current_per_pack = 0;
static uint8_t temperature_max_per_pack = 0;
static uint8_t temperature_min_per_pack = 0;
static uint8_t current_pack_info = 0;
// Batch send of CAN message variables
const uint8_t delay_between_batches_ms = 10; //TODO, tweak to as low as possible before performance issues appear
static bool send_bms_info = false;
static bool send_individual_pack_status = false;
static bool send_serial_numbers = false;
static bool send_cellvoltages = false;
static unsigned long currentMillis = 0;
static unsigned long previousMillisCellvoltage = 0;
static unsigned long previousMillisSerialNumber = 0;
static unsigned long previousMillisBMSinfo = 0;
static unsigned long previousMillisIndividualPacks = 0;
static uint8_t can_message_cellvolt_index = 0;
static uint8_t can_message_serial_index = 0;
static uint8_t can_message_individualpack_index = 0;
static uint8_t can_message_bms_index = 0;
//CAN message translations from this amazing repository: https://github.com/rand12345/FOXESS_can_bus
CAN_frame FOXESS_1872 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1872,
.data = {0x40, 0x12, 0x80, 0x0C, 0xCD, 0x00, 0xF4, 0x01}}; //BMS_Limits
CAN_frame FOXESS_1873 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1873,
.data = {0xA3, 0x10, 0x0D, 0x00, 0x5D, 0x00, 0x77, 0x07}}; //BMS_PackData
CAN_frame FOXESS_1874 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1874,
.data = {0xA3, 0x10, 0x0D, 0x00, 0x5D, 0x00, 0x77, 0x07}}; //BMS_CellData
CAN_frame FOXESS_1875 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1875,
.data = {0xF9, 0x00, 0xFF, 0x08, 0x01, 0x00, 0x8E, 0x00}}; //BMS_Status
CAN_frame FOXESS_1876 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1876,
.data = {0x01, 0x00, 0x07, 0x0D, 0x0, 0x0, 0xFE, 0x0C}}; //BMS_PackTemps
CAN_frame FOXESS_1877 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1877,
.data = {0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x20, 0x50}}; //BMS_Unk1
CAN_frame FOXESS_1878 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1878,
.data = {0x07, 0x0A, 0x00, 0x00, 0xD0, 0xFF, 0x4E, 0x00}}; //BMS_PackStats
CAN_frame FOXESS_1879 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1879,
.data = {0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //BMS_Unk2
CAN_frame FOXESS_1881 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1881,
.data = {0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
CAN_frame FOXESS_1882 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1882,
.data = {0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
CAN_frame FOXESS_1883 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1883,
.data = {0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
CAN_frame FOXESS_0C05 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C05,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C06 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C06,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C07 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C07,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C08 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C08,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C09 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C09,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C0A = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C0A,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C0B = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C0B,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C0C = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C0C,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
// Cellvoltages
CAN_frame FOXESS_0C1D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C1D, //Cell 1-4
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C21 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C21, //Cell 5-8
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C25 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C25, //Cell 9-12
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C29 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C29, //Cell 13-16
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C2D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C2D, //Cell 17-20
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C31 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C31, //Cell 21-24
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C35 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C35, //Cell 25-28
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C39 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C39, //Cell 29-32
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C3D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C3D, //Cell 33-36
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C41 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C41, //Cell 37-40
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C45 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C45, //Cell 41-44
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C49 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C49, //Cell 45-48
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C4D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C4D, //Cell 49-52
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C51 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C51, //Cell 53-56
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C55 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C55, //Cell 57-60
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C59 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C59, //Cell 61-64
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C5D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C5D, //Cell 65-68
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C61 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C61, //Cell 69-72
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C65 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C65, //Cell 73-76
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C69 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C69, //Cell 77-80
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C6D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C6D, //Cell 81-84
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C71 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C71, //Cell 85-88
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C75 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C75, //Cell 89-92
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C79 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C79, //Cell 93-96
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C7D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C7D, //Cell 97-100
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C81 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C81, //Cell 101-104
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C85 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C85, //Cell 105-108
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C89 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C89, //Cell 109-112
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C8D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C8D, //Cell 113-116
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C91 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C91, //Cell 117-120
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C95 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C95, //Cell 121-124
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C99 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C99, //Cell 125-128
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C9D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C9D, //Cell 129-132
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0CA1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0CA1, //Cell 133-136
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0CA5 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0CA5, //Cell 137-140
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0CA9 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0CA9, //Cell 141-144
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
// Temperatures
CAN_frame FOXESS_0D21 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D21, //Celltemperatures Pack 1
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D29 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D29, //Celltemperatures Pack 2
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D31 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D31, //Celltemperatures Pack 3
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D39 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D39, //Celltemperatures Pack 4
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D41 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D41, //Celltemperatures Pack 5
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D49 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D49, //Celltemperatures Pack 6
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D51 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D51, //Celltemperatures Pack 7
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D59 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D59, //Celltemperatures Pack 8
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
void update_values_can_inverter() { //This function maps all the CAN values fetched from battery. It also checks some safeties.
void FoxessCanInverter::
update_values() { //This function maps all the CAN values fetched from battery. It also checks some safeties.
//Calculate the required values
temperature_average =
@ -595,7 +252,7 @@ void update_values_can_inverter() { //This function maps all the CAN values fet
// So do we really need to map the same two values over and over to 32 places?
}
void transmit_can_inverter(unsigned long currentMillis) {
void FoxessCanInverter::transmit_can(unsigned long currentMillis) {
if (send_bms_info) {
@ -861,7 +518,7 @@ void transmit_can_inverter(unsigned long currentMillis) {
}
}
void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {
void FoxessCanInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
if (rx_frame.ID == 0x1871) {
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE;
@ -904,8 +561,7 @@ void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {
}
}
}
void setup_inverter(void) { // Performs one time setup at startup over CAN bus
void FoxessCanInverter::setup(void) { // Performs one time setup at startup over CAN bus
strncpy(datalayer.system.info.inverter_protocol, "FoxESS compatible HV2600/ECS4100 battery", 63);
datalayer.system.info.inverter_protocol[63] = '\0';
}
#endif

View file

@ -2,9 +2,365 @@
#define FOXESS_CAN_H
#include "../include.h"
#define CAN_INVERTER_SELECTED
#include "CanInverterProtocol.h"
void transmit_can_frame(CAN_frame* tx_frame, int interface);
void setup_inverter(void);
#ifdef FOXESS_CAN
#define CAN_INVERTER_SELECTED
#define SELECTED_INVERTER_CLASS FoxessCanInverter
#endif
class FoxessCanInverter : public CanInverterProtocol {
public:
void setup();
void update_values();
void transmit_can(unsigned long currentMillis);
void map_can_frame_to_variable(CAN_frame rx_frame);
private:
int16_t temperature_average = 0;
uint16_t voltage_per_pack = 0;
int16_t current_per_pack = 0;
uint8_t temperature_max_per_pack = 0;
uint8_t temperature_min_per_pack = 0;
uint8_t current_pack_info = 0;
// Batch send of CAN message variables
const uint8_t delay_between_batches_ms = 10; //TODO, tweak to as low as possible before performance issues appear
bool send_bms_info = false;
bool send_individual_pack_status = false;
bool send_serial_numbers = false;
bool send_cellvoltages = false;
unsigned long currentMillis = 0;
unsigned long previousMillisCellvoltage = 0;
unsigned long previousMillisSerialNumber = 0;
unsigned long previousMillisBMSinfo = 0;
unsigned long previousMillisIndividualPacks = 0;
uint8_t can_message_cellvolt_index = 0;
uint8_t can_message_serial_index = 0;
uint8_t can_message_individualpack_index = 0;
uint8_t can_message_bms_index = 0;
//CAN message translations from this amazing repository: https://github.com/rand12345/FOXESS_can_bus
CAN_frame FOXESS_1872 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1872,
.data = {0x40, 0x12, 0x80, 0x0C, 0xCD, 0x00, 0xF4, 0x01}}; //BMS_Limits
CAN_frame FOXESS_1873 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1873,
.data = {0xA3, 0x10, 0x0D, 0x00, 0x5D, 0x00, 0x77, 0x07}}; //BMS_PackData
CAN_frame FOXESS_1874 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1874,
.data = {0xA3, 0x10, 0x0D, 0x00, 0x5D, 0x00, 0x77, 0x07}}; //BMS_CellData
CAN_frame FOXESS_1875 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1875,
.data = {0xF9, 0x00, 0xFF, 0x08, 0x01, 0x00, 0x8E, 0x00}}; //BMS_Status
CAN_frame FOXESS_1876 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1876,
.data = {0x01, 0x00, 0x07, 0x0D, 0x0, 0x0, 0xFE, 0x0C}}; //BMS_PackTemps
CAN_frame FOXESS_1877 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1877,
.data = {0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x20, 0x50}}; //BMS_Unk1
CAN_frame FOXESS_1878 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1878,
.data = {0x07, 0x0A, 0x00, 0x00, 0xD0, 0xFF, 0x4E, 0x00}}; //BMS_PackStats
CAN_frame FOXESS_1879 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1879,
.data = {0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //BMS_Unk2
CAN_frame FOXESS_1881 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1881,
.data = {0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
CAN_frame FOXESS_1882 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1882,
.data = {0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
CAN_frame FOXESS_1883 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1883,
.data = {0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
CAN_frame FOXESS_0C05 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C05,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C06 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C06,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C07 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C07,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C08 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C08,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C09 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C09,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C0A = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C0A,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C0B = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C0B,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
CAN_frame FOXESS_0C0C = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C0C,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD0, 0xD0}};
// Cellvoltages
CAN_frame FOXESS_0C1D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C1D, //Cell 1-4
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C21 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C21, //Cell 5-8
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C25 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C25, //Cell 9-12
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C29 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C29, //Cell 13-16
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C2D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C2D, //Cell 17-20
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C31 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C31, //Cell 21-24
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C35 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C35, //Cell 25-28
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C39 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C39, //Cell 29-32
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C3D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C3D, //Cell 33-36
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C41 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C41, //Cell 37-40
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C45 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C45, //Cell 41-44
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C49 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C49, //Cell 45-48
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C4D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C4D, //Cell 49-52
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C51 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C51, //Cell 53-56
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C55 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C55, //Cell 57-60
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C59 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C59, //Cell 61-64
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C5D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C5D, //Cell 65-68
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C61 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C61, //Cell 69-72
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C65 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C65, //Cell 73-76
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C69 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C69, //Cell 77-80
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C6D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C6D, //Cell 81-84
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C71 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C71, //Cell 85-88
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C75 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C75, //Cell 89-92
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C79 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C79, //Cell 93-96
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C7D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C7D, //Cell 97-100
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C81 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C81, //Cell 101-104
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C85 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C85, //Cell 105-108
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C89 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C89, //Cell 109-112
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C8D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C8D, //Cell 113-116
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C91 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C91, //Cell 117-120
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C95 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C95, //Cell 121-124
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C99 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C99, //Cell 125-128
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0C9D = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0C9D, //Cell 129-132
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0CA1 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0CA1, //Cell 133-136
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0CA5 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0CA5, //Cell 137-140
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
CAN_frame FOXESS_0CA9 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0CA9, //Cell 141-144
.data = {0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C, 0xE4, 0x0C}}; //All cells init to 3300mV
// Temperatures
CAN_frame FOXESS_0D21 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D21, //Celltemperatures Pack 1
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D29 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D29, //Celltemperatures Pack 2
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D31 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D31, //Celltemperatures Pack 3
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D39 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D39, //Celltemperatures Pack 4
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D41 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D41, //Celltemperatures Pack 5
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D49 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D49, //Celltemperatures Pack 6
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D51 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D51, //Celltemperatures Pack 7
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
CAN_frame FOXESS_0D59 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x0D59, //Celltemperatures Pack 8
.data = {0x49, 0x48, 0x47, 0x47, 0x48, 0x49, 0x46, 0x47}};
};
#endif

View file

@ -1,7 +1,7 @@
#include "../include.h"
#ifdef GROWATT_HV_CAN
#include "../datalayer/datalayer.h"
#include "GROWATT-HV-CAN.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../include.h"
/* TODO:
This protocol has not been tested with any inverter. Proceed with extreme caution.
@ -21,143 +21,8 @@ FCC - Full charge capacity
RM - Remaining capacity
BMS - Battery Information Collector*/
//Total number of Cells (1-512)
//(Total number of Cells = number of Packs in parallel * number of Modules in series * number of Cells in the module)
#define TOTAL_NUMBER_OF_CELLS 300
// Number of Modules in series (1-32)
#define NUMBER_OF_MODULES_IN_SERIES 20
// Number of packs in parallel (1-65536)
#define NUMBER_OF_PACKS_IN_PARALLEL 1
//Manufacturer abbreviation, part 1
#define MANUFACTURER_ASCII_0 0x47 //G
#define MANUFACTURER_ASCII_1 0x54 //T
/* Do not change code below unless you are sure what you are doing */
//Actual content messages
CAN_frame GROWATT_3110 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3110,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3120 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3120,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3130 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3130,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3140 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3140,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3150 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3150,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3160 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3160,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3170 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3170,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3180 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3180,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3190 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3190,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3200 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3200,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3210 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3210,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3220 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3220,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3230 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3230,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3240 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3240,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3250 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3250,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3260 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3260,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3270 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3270,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3280 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3280,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3290 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3290,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3F00 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3F00,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send
static unsigned long previousMillisBatchSend = 0;
static uint32_t unix_time = 0;
static uint16_t ampere_hours_remaining = 0;
static uint16_t ampere_hours_full = 0;
static uint16_t send_times = 0; // Overflows every 18hours. Cumulative number, plus 1 for each transmission
static uint8_t safety_specification = 0;
static uint8_t charging_command = 0;
static uint8_t discharging_command = 0;
static uint8_t shielding_external_communication_failure = 0;
static uint8_t clearing_battery_fault =
0; //When PCS receives the forced charge Mark 1 and Cell under- voltage protection fault, it will send 0XAA
static uint8_t ISO_detection_command = 0;
static uint8_t sleep_wakeup_control = 0;
static uint8_t PCS_working_status = 0; //00 standby, 01 operating
static uint8_t serial_number_counter = 0; //0-1-2-0-1-2...
static uint8_t can_message_batch_index = 0;
static const uint8_t delay_between_batches_ms = 10;
static bool inverter_alive = false;
static bool time_to_send_1s_data = false;
void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages
void GrowattHvInverter::
update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages
if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0
ampere_hours_remaining =
@ -493,7 +358,7 @@ void update_values_can_inverter() { //This function maps all the values fetched
GROWATT_3F00.data.u8[7] = 0; // RESERVED
}
void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {
void GrowattHvInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
switch (rx_frame.ID) {
case 0x3010: // Heartbeat command, 1000ms
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE;
@ -523,7 +388,7 @@ void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {
}
}
void transmit_can_inverter(unsigned long currentMillis) {
void GrowattHvInverter::transmit_can(unsigned long currentMillis) {
if (!inverter_alive) {
return; //Dont send messages towards inverter until it has started
@ -585,8 +450,7 @@ void transmit_can_inverter(unsigned long currentMillis) {
}
}
void setup_inverter(void) { // Performs one time setup at startup over CAN bus
void GrowattHvInverter::setup(void) { // Performs one time setup at startup over CAN bus
strncpy(datalayer.system.info.inverter_protocol, "Growatt High Voltage protocol via CAN", 63);
datalayer.system.info.inverter_protocol[63] = '\0';
}
#endif

View file

@ -2,9 +2,156 @@
#define GROWATT_HV_CAN_H
#include "../include.h"
#define CAN_INVERTER_SELECTED
#include "CanInverterProtocol.h"
void transmit_can_frame(CAN_frame* tx_frame, int interface);
void setup_inverter(void);
#ifdef GROWATT_HV_CAN
#define CAN_INVERTER_SELECTED
#define SELECTED_INVERTER_CLASS GrowattHvInverter
#endif
class GrowattHvInverter : public CanInverterProtocol {
public:
void setup();
void update_values();
void transmit_can(unsigned long currentMillis);
void map_can_frame_to_variable(CAN_frame rx_frame);
private:
//Total number of Cells (1-512)
//(Total number of Cells = number of Packs in parallel * number of Modules in series * number of Cells in the module)
static const int TOTAL_NUMBER_OF_CELLS = 300;
// Number of Modules in series (1-32)
static const int NUMBER_OF_MODULES_IN_SERIES = 20;
// Number of packs in parallel (1-65536)
static const int NUMBER_OF_PACKS_IN_PARALLEL = 1;
//Manufacturer abbreviation, part 1
static const int MANUFACTURER_ASCII_0 = 0x47; //G
static const int MANUFACTURER_ASCII_1 = 0x54; //T
/* Do not change code below unless you are sure what you are doing */
//Actual content messages
CAN_frame GROWATT_3110 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3110,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3120 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3120,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3130 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3130,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3140 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3140,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3150 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3150,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3160 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3160,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3170 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3170,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3180 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3180,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3190 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3190,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3200 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3200,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3210 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3210,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3220 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3220,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3230 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3230,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3240 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3240,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3250 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3250,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3260 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3260,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3270 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3270,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3280 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3280,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3290 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3290,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_3F00 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x3F00,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send
unsigned long previousMillisBatchSend = 0;
uint32_t unix_time = 0;
uint16_t ampere_hours_remaining = 0;
uint16_t ampere_hours_full = 0;
uint16_t send_times = 0; // Overflows every 18hours. Cumulative number, plus 1 for each transmission
uint8_t safety_specification = 0;
uint8_t charging_command = 0;
uint8_t discharging_command = 0;
uint8_t shielding_external_communication_failure = 0;
uint8_t clearing_battery_fault =
0; //When PCS receives the forced charge Mark 1 and Cell under- voltage protection fault, it will send 0XAA
uint8_t ISO_detection_command = 0;
uint8_t sleep_wakeup_control = 0;
uint8_t PCS_working_status = 0; //00 standby, 01 operating
uint8_t serial_number_counter = 0; //0-1-2-0-1-2...
uint8_t can_message_batch_index = 0;
const uint8_t delay_between_batches_ms = 10;
bool inverter_alive = false;
bool time_to_send_1s_data = false;
};
#endif

View file

@ -1,7 +1,7 @@
#include "../include.h"
#ifdef GROWATT_LV_CAN
#include "../datalayer/datalayer.h"
#include "GROWATT-LV-CAN.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../include.h"
/* Growatt BMS CAN-Bus-protocol Low Voltage Rev_04
CAN 2.0A
@ -10,76 +10,8 @@ Big-endian
The inverter replies data every second (standard frame/decimal)0x301:*/
/* Do not change code below unless you are sure what you are doing */
//Actual content messages
CAN_frame GROWATT_311 = {.FD = false, //Voltage and charge limits and status
.ext_ID = false,
.DLC = 8,
.ID = 0x311,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_312 = {.FD = false, //status bits , pack number, total cell number
.ext_ID = false,
.DLC = 8,
.ID = 0x312,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_313 = {.FD = false, //voltage, current, temp, soc, soh
.ext_ID = false,
.DLC = 8,
.ID = 0x313,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_314 = {.FD = false, //capacity, delta V, cycle count
.ext_ID = false,
.DLC = 8,
.ID = 0x314,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_319 = {.FD = false, //max/min cell voltage, num of cell max/min, protect pack ID
.ext_ID = false,
.DLC = 8,
.ID = 0x319,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_320 = {.FD = false, //manufacturer name, hw ver, sw ver, date and time
.ext_ID = false,
.DLC = 8,
.ID = 0x320,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_321 = {.FD = false, //Update status, ID
.ext_ID = false,
.DLC = 8,
.ID = 0x321,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
//Cellvoltages
CAN_frame GROWATT_315 = {.FD = false, //Cells 1-4
.ext_ID = false,
.DLC = 8,
.ID = 0x315,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_316 = {.FD = false, //Cells 5-8
.ext_ID = false,
.DLC = 8,
.ID = 0x316,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_317 = {.FD = false, //Cells 9-12
.ext_ID = false,
.DLC = 8,
.ID = 0x317,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_318 = {.FD = false, //Cells 13-16
.ext_ID = false,
.DLC = 8,
.ID = 0x318,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
#define VOLTAGE_OFFSET_DV 40 //Offset in deciVolt from max charge voltage and min discharge voltage
#define MAX_VOLTAGE_DV 630
#define MIN_VOLTAGE_DV 410
static uint16_t cell_delta_mV = 0;
static uint16_t ampere_hours_remaining = 0;
static uint16_t ampere_hours_full = 0;
void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages
void GrowattLvInverter::
update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages
cell_delta_mV = datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV;
@ -246,7 +178,7 @@ void update_values_can_inverter() { //This function maps all the values fetched
GROWATT_318.data.u8[7] = (datalayer.battery.status.cell_voltages_mV[15] & 0x00FF);
}
void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {
void GrowattLvInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
switch (rx_frame.ID) {
case 0x301:
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE;
@ -267,12 +199,11 @@ void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {
}
}
void transmit_can_inverter(unsigned long currentMillis) {
void GrowattLvInverter::transmit_can(unsigned long currentMillis) {
// No periodic sending for this battery type. Data is sent when inverter requests it
}
void setup_inverter(void) { // Performs one time setup at startup over CAN bus
void GrowattLvInverter::setup(void) { // Performs one time setup at startup over CAN bus
strncpy(datalayer.system.info.inverter_protocol, "Growatt Low Voltage (48V) protocol via CAN", 63);
datalayer.system.info.inverter_protocol[63] = '\0';
}
#endif

View file

@ -2,9 +2,87 @@
#define GROWATT_LV_CAN_H
#include "../include.h"
#define CAN_INVERTER_SELECTED
#include "CanInverterProtocol.h"
void transmit_can_frame(CAN_frame* tx_frame, int interface);
void setup_inverter(void);
#ifdef GROWATT_LV_CAN
#define CAN_INVERTER_SELECTED
#define SELECTED_INVERTER_CLASS GrowattLvInverter
#endif
class GrowattLvInverter : public CanInverterProtocol {
public:
void setup();
void update_values();
void transmit_can(unsigned long currentMillis);
void map_can_frame_to_variable(CAN_frame rx_frame);
private:
//Actual content messages
CAN_frame GROWATT_311 = {.FD = false, //Voltage and charge limits and status
.ext_ID = false,
.DLC = 8,
.ID = 0x311,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_312 = {.FD = false, //status bits , pack number, total cell number
.ext_ID = false,
.DLC = 8,
.ID = 0x312,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_313 = {.FD = false, //voltage, current, temp, soc, soh
.ext_ID = false,
.DLC = 8,
.ID = 0x313,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_314 = {.FD = false, //capacity, delta V, cycle count
.ext_ID = false,
.DLC = 8,
.ID = 0x314,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_319 = {.FD = false, //max/min cell voltage, num of cell max/min, protect pack ID
.ext_ID = false,
.DLC = 8,
.ID = 0x319,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_320 = {.FD = false, //manufacturer name, hw ver, sw ver, date and time
.ext_ID = false,
.DLC = 8,
.ID = 0x320,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_321 = {.FD = false, //Update status, ID
.ext_ID = false,
.DLC = 8,
.ID = 0x321,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
//Cellvoltages
CAN_frame GROWATT_315 = {.FD = false, //Cells 1-4
.ext_ID = false,
.DLC = 8,
.ID = 0x315,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_316 = {.FD = false, //Cells 5-8
.ext_ID = false,
.DLC = 8,
.ID = 0x316,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_317 = {.FD = false, //Cells 9-12
.ext_ID = false,
.DLC = 8,
.ID = 0x317,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame GROWATT_318 = {.FD = false, //Cells 13-16
.ext_ID = false,
.DLC = 8,
.ID = 0x318,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
static const int VOLTAGE_OFFSET_DV = 40; //Offset in deciVolt from max charge voltage and min discharge voltage
static const int MAX_VOLTAGE_DV = 630;
static const int MIN_VOLTAGE_DV = 410;
uint16_t cell_delta_mV = 0;
uint16_t ampere_hours_remaining = 0;
uint16_t ampere_hours_full = 0;
};
#endif

View file

@ -3,9 +3,7 @@
// These functions adapt the old C-style global functions inverter-API to the
// object-oriented inverter protocol API.
#ifdef SELECTED_INVERTER_CLASS
InverterProtocol* inverter;
InverterProtocol* inverter = nullptr;
#ifdef CAN_INVERTER_SELECTED
CanInverterProtocol* can_inverter;
@ -30,7 +28,9 @@ void setup_inverter() {
inverter = new SELECTED_INVERTER_CLASS();
#endif
inverter->setup();
if (inverter) {
inverter->setup();
}
}
#ifdef CAN_INVERTER_SELECTED
@ -52,5 +52,3 @@ void receive_RS485() {
((Rs485InverterProtocol*)inverter)->receive_RS485();
}
#endif
#endif

View file

@ -6,82 +6,31 @@ extern InverterProtocol* inverter;
#include "../../USER_SETTINGS.h"
#ifdef AFORE_CAN
#include "AFORE-CAN.h"
#endif
#ifdef BYD_CAN_DEYE
#define BYD_CAN
#endif
#ifdef BYD_CAN
#include "BYD-CAN.h"
#endif
#ifdef BYD_MODBUS
#include "BYD-MODBUS.h"
#endif
#ifdef BYD_KOSTAL_RS485
#include "KOSTAL-RS485.h"
#endif
#ifdef FERROAMP_CAN
#include "FERROAMP-CAN.h"
#endif
#ifdef FOXESS_CAN
#include "FOXESS-CAN.h"
#endif
#ifdef GROWATT_HV_CAN
#include "GROWATT-HV-CAN.h"
#endif
#ifdef GROWATT_LV_CAN
#include "GROWATT-LV-CAN.h"
#endif
#ifdef PYLON_CAN
#include "KOSTAL-RS485.h"
#include "PYLON-CAN.h"
#endif
#ifdef PYLON_LV_CAN
#include "PYLON-LV-CAN.h"
#endif
#ifdef SCHNEIDER_CAN
#include "SCHNEIDER-CAN.h"
#endif
#ifdef SMA_BYD_H_CAN
#include "SMA-BYD-H-CAN.h"
#endif
#ifdef SMA_BYD_HVS_CAN
#include "SMA-BYD-HVS-CAN.h"
#endif
#ifdef SMA_LV_CAN
#include "SMA-LV-CAN.h"
#endif
#ifdef SMA_TRIPOWER_CAN
#include "SMA-TRIPOWER-CAN.h"
#endif
#ifdef SOFAR_CAN
#include "SOFAR-CAN.h"
#endif
#ifdef SOLAX_CAN
#include "SOLAX-CAN.h"
#endif
#ifdef SUNGROW_CAN
#include "SUNGROW-CAN.h"
#endif
// Call to initialize the build-time selected inverter. Safe to call even though inverter was not selected.
void setup_inverter();
#ifdef CAN_INVERTER_SELECTED

View file

@ -5,6 +5,7 @@
class InverterProtocol {
public:
virtual void setup() = 0;
virtual const char* interface_name() = 0;
// This function maps all the values fetched from battery to the correct battery emulator data structures
virtual void update_values() = 0;

Some files were not shown because too many files have changed in this diff Show more