Merge branch 'main' into bugfix/SMA-voltages

This commit is contained in:
Daniel Öster 2025-07-29 10:50:14 +03:00
commit 3550881b65
223 changed files with 7435 additions and 4765 deletions

View file

@ -1,17 +1,15 @@
#include "../include.h"
#include "BATTERIES.h"
#include "../datalayer/datalayer_extended.h"
#include "CanBattery.h"
#include "RS485Battery.h"
#if !defined(COMMON_IMAGE) && !defined(SELECTED_BATTERY_CLASS)
#error No battery selected! Choose one from the USER_SETTINGS.h file
#error No battery selected! Choose one from the USER_SETTINGS.h file or build COMMON_IMAGE.
#endif
Battery* battery = nullptr;
Battery* battery2 = nullptr;
#ifdef COMMON_IMAGE
std::vector<BatteryType> supported_battery_types() {
std::vector<BatteryType> types;
@ -22,7 +20,39 @@ std::vector<BatteryType> supported_battery_types() {
return types;
}
extern const char* name_for_battery_type(BatteryType type) {
const char* name_for_chemistry(battery_chemistry_enum chem) {
switch (chem) {
case battery_chemistry_enum::LFP:
return "LFP";
case battery_chemistry_enum::NCA:
return "NCA";
case battery_chemistry_enum::NMC:
return "NMC";
default:
return nullptr;
}
}
const char* name_for_comm_interface(comm_interface comm) {
switch (comm) {
case comm_interface::Modbus:
return "Modbus";
case comm_interface::RS485:
return "RS485";
case comm_interface::CanNative:
return "Native CAN";
case comm_interface::CanFdNative:
return "Native CAN FD";
case comm_interface::CanAddonMcp2515:
return "CAN MCP 2515 add-on";
case comm_interface::CanFdAddonMcp2518:
return "CAN FD MCP 2518 add-on";
default:
return nullptr;
}
}
const char* name_for_battery_type(BatteryType type) {
switch (type) {
case BatteryType::None:
return "None";
@ -36,10 +66,8 @@ extern const char* name_for_battery_type(BatteryType type) {
return BydAttoBattery::Name;
case BatteryType::CellPowerBms:
return CellPowerBms::Name;
#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms
case BatteryType::Chademo:
return ChademoBattery::Name;
#endif
case BatteryType::CmfaEv:
return CmfaEvBattery::Name;
case BatteryType::Foxess:
@ -66,6 +94,8 @@ extern const char* name_for_battery_type(BatteryType type) {
return MebBattery::Name;
case BatteryType::Mg5:
return Mg5Battery::Name;
case BatteryType::MgHsPhev:
return MgHsPHEVBattery::Name;
case BatteryType::NissanLeaf:
return NissanLeafBattery::Name;
case BatteryType::Pylon:
@ -102,8 +132,15 @@ extern const char* name_for_battery_type(BatteryType type) {
return nullptr;
}
}
#ifdef LFP_CHEMISTRY
const battery_chemistry_enum battery_chemistry_default = battery_chemistry_enum::LFP;
#else
const battery_chemistry_enum battery_chemistry_default = battery_chemistry_enum::NMC;
#endif
battery_chemistry_enum user_selected_battery_chemistry = battery_chemistry_default;
#ifdef COMMON_IMAGE
#ifdef SELECTED_BATTERY_CLASS
#error "Compile time SELECTED_BATTERY_CLASS should not be defined with COMMON_IMAGE"
@ -126,10 +163,8 @@ Battery* create_battery(BatteryType type) {
return new BydAttoBattery();
case BatteryType::CellPowerBms:
return new CellPowerBms();
#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms
case BatteryType::Chademo:
return new ChademoBattery();
#endif
case BatteryType::CmfaEv:
return new CmfaEvBattery();
case BatteryType::Foxess:
@ -156,6 +191,8 @@ Battery* create_battery(BatteryType type) {
return new MebBattery();
case BatteryType::Mg5:
return new Mg5Battery();
case BatteryType::MgHsPhev:
return new MgHsPHEVBattery();
case BatteryType::NissanLeaf:
return new NissanLeafBattery();
case BatteryType::Pylon:
@ -179,7 +216,7 @@ Battery* create_battery(BatteryType type) {
case BatteryType::SimpBms:
return new SimpBmsBattery();
case BatteryType::TeslaModel3Y:
return new TeslaModel3YBattery();
return new TeslaModel3YBattery(user_selected_battery_chemistry);
case BatteryType::TeslaModelSX:
return new TeslaModelSXBattery();
case BatteryType::TestFake:
@ -212,7 +249,7 @@ void setup_battery() {
break;
case BatteryType::BmwI3:
battery2 = new BmwI3Battery(&datalayer.battery2, &datalayer.system.status.battery2_allowed_contactor_closing,
can_config.battery_double, WUP_PIN2);
can_config.battery_double, esp32hal->WUP_PIN2());
break;
case BatteryType::KiaHyundai64:
battery2 = new KiaHyundai64Battery(&datalayer.battery2, &datalayer_extended.KiaHyundai64_2,
@ -221,6 +258,9 @@ void setup_battery() {
case BatteryType::SantaFePhev:
battery2 = new SantaFePhevBattery(&datalayer.battery2, can_config.battery_double);
break;
case BatteryType::RenaultZoe1:
battery2 = new RenaultZoeGen1Battery(&datalayer.battery2, nullptr, can_config.battery_double);
break;
case BatteryType::TestFake:
battery2 = new TestFakeBattery(&datalayer.battery2, can_config.battery_double);
break;
@ -236,7 +276,11 @@ void setup_battery() {
void setup_battery() {
// Instantiate the battery only once just in case this function gets called multiple times.
if (battery == nullptr) {
#ifdef TESLA_MODEL_3Y_BATTERY
battery = new SELECTED_BATTERY_CLASS(user_selected_battery_chemistry);
#else
battery = new SELECTED_BATTERY_CLASS();
#endif
}
battery->setup();
@ -245,7 +289,7 @@ void setup_battery() {
#if defined(BMW_I3_BATTERY)
battery2 =
new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer.system.status.battery2_allowed_contactor_closing,
can_config.battery_double, WUP_PIN2);
can_config.battery_double, esp32hal->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,

View file

@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "../include.h"
/* Do not change code below unless you are sure what you are doing */
@ -44,7 +43,7 @@ uint8_t BmwI3Battery::increment_alive_counter(uint8_t counter) {
void BmwI3Battery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer
if (datalayer.system.settings.equipment_stop_active == true) {
digitalWrite(wakeup_pin, LOW); // Turn off wakeup pin
} else {
} else if (millis() > INTERVAL_1_S) {
digitalWrite(wakeup_pin, HIGH); // Wake up the battery
}
@ -235,7 +234,7 @@ void BmwI3Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
case 0x607: //BMS - responses to message requests on 0x615
if ((cmdState == CELL_VOLTAGE_CELLNO || cmdState == CELL_VOLTAGE_CELLNO_LAST) && (rx_frame.data.u8[0] == 0xF4)) {
if (rx_frame.DLC == 6) {
transmit_can_frame(&BMW_6F4_CELL_CONTINUE, can_interface); // tell battery to send the cellvoltage
transmit_can_frame(&BMW_6F4_CELL_CONTINUE); // tell battery to send the cellvoltage
}
if (rx_frame.DLC == 8) { // We have the full value, map it
datalayer_battery->status.cell_voltages_mV[current_cell_polled - 1] =
@ -248,7 +247,7 @@ void BmwI3Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
while (count < rx_frame.DLC && next_data < 49) {
message_data[next_data++] = rx_frame.data.u8[count++];
}
transmit_can_frame(&BMW_6F1_CONTINUE, can_interface); // tell battery to send additional messages
transmit_can_frame(&BMW_6F1_CONTINUE); // tell battery to send additional messages
} else if (rx_frame.DLC > 3 && next_data > 0 && rx_frame.data.u8[0] == 0xf1 &&
((rx_frame.data.u8[1] & 0xF0) == 0x20)) {
@ -316,9 +315,9 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
} 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);
transmit_can_frame(&BMW_10B);
} else if (contactor_closing_allowed && *contactor_closing_allowed) {
transmit_can_frame(&BMW_10B, can_interface);
transmit_can_frame(&BMW_10B);
}
}
@ -331,7 +330,7 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
alive_counter_100ms = increment_alive_counter(alive_counter_100ms);
transmit_can_frame(&BMW_12F, can_interface);
transmit_can_frame(&BMW_12F);
}
// Send 200ms CAN Message
if (currentMillis - previousMillis200 >= INTERVAL_200_MS) {
@ -342,7 +341,7 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
alive_counter_200ms = increment_alive_counter(alive_counter_200ms);
transmit_can_frame(&BMW_19B, can_interface);
transmit_can_frame(&BMW_19B);
}
// Send 500ms CAN Message
if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
@ -353,14 +352,14 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
alive_counter_500ms = increment_alive_counter(alive_counter_500ms);
transmit_can_frame(&BMW_30B, can_interface);
transmit_can_frame(&BMW_30B);
}
// Send 640ms CAN Message
if (currentMillis - previousMillis640 >= INTERVAL_640_MS) {
previousMillis640 = currentMillis;
transmit_can_frame(&BMW_512, can_interface); // Keep BMS alive
transmit_can_frame(&BMW_5F8, can_interface);
transmit_can_frame(&BMW_512); // Keep BMS alive
transmit_can_frame(&BMW_5F8);
}
// Send 1000ms CAN Message
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
@ -397,22 +396,22 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
alive_counter_1000ms = increment_alive_counter(alive_counter_1000ms);
transmit_can_frame(&BMW_3E8, can_interface); //Order comes from CAN logs
transmit_can_frame(&BMW_328, can_interface);
transmit_can_frame(&BMW_3F9, can_interface);
transmit_can_frame(&BMW_2E2, can_interface);
transmit_can_frame(&BMW_41D, can_interface);
transmit_can_frame(&BMW_3D0, can_interface);
transmit_can_frame(&BMW_3CA, can_interface);
transmit_can_frame(&BMW_3A7, can_interface);
transmit_can_frame(&BMW_2CA, can_interface);
transmit_can_frame(&BMW_3FB, can_interface);
transmit_can_frame(&BMW_418, can_interface);
transmit_can_frame(&BMW_1D0, can_interface);
transmit_can_frame(&BMW_3EC, can_interface);
transmit_can_frame(&BMW_192, can_interface);
transmit_can_frame(&BMW_13E, can_interface);
transmit_can_frame(&BMW_433, can_interface);
transmit_can_frame(&BMW_3E8); //Order comes from CAN logs
transmit_can_frame(&BMW_328);
transmit_can_frame(&BMW_3F9);
transmit_can_frame(&BMW_2E2);
transmit_can_frame(&BMW_41D);
transmit_can_frame(&BMW_3D0);
transmit_can_frame(&BMW_3CA);
transmit_can_frame(&BMW_3A7);
transmit_can_frame(&BMW_2CA);
transmit_can_frame(&BMW_3FB);
transmit_can_frame(&BMW_418);
transmit_can_frame(&BMW_1D0);
transmit_can_frame(&BMW_3EC);
transmit_can_frame(&BMW_192);
transmit_can_frame(&BMW_13E);
transmit_can_frame(&BMW_433);
BMW_433.data.u8[1] = 0x01; // First 433 message byte1 we send is unique, once we sent initial value send this
BMW_3E8.data.u8[0] = 0xF1; // First 3E8 message byte0 we send is unique, once we sent initial value send this
@ -420,15 +419,15 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
next_data = 0;
switch (cmdState) {
case SOC:
transmit_can_frame(&BMW_6F1_CELL, can_interface);
transmit_can_frame(&BMW_6F1_CELL);
cmdState = CELL_VOLTAGE_MINMAX;
break;
case CELL_VOLTAGE_MINMAX:
transmit_can_frame(&BMW_6F1_SOH, can_interface);
transmit_can_frame(&BMW_6F1_SOH);
cmdState = SOH;
break;
case SOH:
transmit_can_frame(&BMW_6F1_CELL_VOLTAGE_AVG, can_interface);
transmit_can_frame(&BMW_6F1_CELL_VOLTAGE_AVG);
cmdState = CELL_VOLTAGE_CELLNO;
current_cell_polled = 0;
@ -441,11 +440,11 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
cmdState = CELL_VOLTAGE_CELLNO;
BMW_6F4_CELL_VOLTAGE_CELLNO.data.u8[6] = current_cell_polled;
transmit_can_frame(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_interface);
transmit_can_frame(&BMW_6F4_CELL_VOLTAGE_CELLNO);
}
break;
case CELL_VOLTAGE_CELLNO_LAST:
transmit_can_frame(&BMW_6F1_SOC, can_interface);
transmit_can_frame(&BMW_6F1_SOC);
cmdState = SOC;
break;
}
@ -457,16 +456,16 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
BMW_3FC.data.u8[1] = ((BMW_3FC.data.u8[1] & 0xF0) + alive_counter_5000ms);
BMW_3C5.data.u8[0] = ((BMW_3C5.data.u8[0] & 0xF0) + alive_counter_5000ms);
transmit_can_frame(&BMW_3FC, can_interface); //Order comes from CAN logs
transmit_can_frame(&BMW_3C5, can_interface);
transmit_can_frame(&BMW_3A0, can_interface);
transmit_can_frame(&BMW_592_0, can_interface);
transmit_can_frame(&BMW_592_1, can_interface);
transmit_can_frame(&BMW_3FC); //Order comes from CAN logs
transmit_can_frame(&BMW_3C5);
transmit_can_frame(&BMW_3A0);
transmit_can_frame(&BMW_592_0);
transmit_can_frame(&BMW_592_1);
alive_counter_5000ms = increment_alive_counter(alive_counter_5000ms);
if (BMW_380_counter < 3) {
transmit_can_frame(&BMW_380, can_interface); // This message stops after 3 times on startup
transmit_can_frame(&BMW_380); // This message stops after 3 times on startup
BMW_380_counter++;
}
}
@ -474,9 +473,9 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis10000 >= INTERVAL_10_S) {
previousMillis10000 = currentMillis;
transmit_can_frame(&BMW_3E5, can_interface); //Order comes from CAN logs
transmit_can_frame(&BMW_3E4, can_interface);
transmit_can_frame(&BMW_37B, can_interface);
transmit_can_frame(&BMW_3E5); //Order comes from CAN logs
transmit_can_frame(&BMW_3E4);
transmit_can_frame(&BMW_37B);
BMW_3E5.data.u8[0] = 0xFD; // First 3E5 message byte0 we send is unique, once we sent initial value send this
}
@ -493,6 +492,10 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
}
void BmwI3Battery::setup(void) { // Performs one time setup at startup
if (!esp32hal->alloc_pins(Name, wakeup_pin)) {
return;
}
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
@ -507,5 +510,5 @@ void BmwI3Battery::setup(void) { // Performs one time setup at startup
datalayer_battery->info.number_of_cells = NUMBER_OF_CELLS;
pinMode(wakeup_pin, OUTPUT);
digitalWrite(wakeup_pin, HIGH); // Wake up the battery
digitalWrite(wakeup_pin, LOW); // Set pin to low, prepare to wakeup later on!
}

View file

@ -2,9 +2,9 @@
#define BMW_I3_BATTERY_H
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "BMW-I3-HTML.h"
#include "CanBattery.h"
#include "src/devboard/hal/hal.h"
#ifdef BMW_I3_BATTERY
#define SELECTED_BATTERY_CLASS BmwI3Battery
@ -14,7 +14,7 @@ class BmwI3Battery : public CanBattery {
public:
// Use this constructor for the second battery.
BmwI3Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* contactor_closing_allowed_ptr, CAN_Interface targetCan,
int wakeup)
gpio_num_t wakeup)
: CanBattery(targetCan), renderer(*this) {
datalayer_battery = datalayer_ptr;
contactor_closing_allowed = contactor_closing_allowed_ptr;
@ -30,7 +30,7 @@ class BmwI3Battery : public CanBattery {
datalayer_battery = &datalayer.battery;
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
contactor_closing_allowed = nullptr;
wakeup_pin = WUP_PIN1;
wakeup_pin = esp32hal->WUP_PIN1();
}
virtual void setup(void);
@ -95,7 +95,7 @@ class BmwI3Battery : public CanBattery {
// If not null, this battery listens to this boolean to determine whether contactor closing is allowed
bool* contactor_closing_allowed;
int wakeup_pin;
gpio_num_t wakeup_pin;
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

View file

@ -1,5 +1,4 @@
#include "BMW-I3-HTML.h"
#include "../include.h"
#include "BMW-I3-BATTERY.h"
// Helper function for safe array access

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../devboard/utils/logging.h"
// Function to check if a value has gone stale over a specified time period
bool BmwIXBattery::isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChangeTime) {
@ -173,7 +173,7 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
}
//Frame has continued data - so request it
transmit_can_frame(&BMWiX_6F4_CONTINUE_DATA, can_config.battery);
transmit_can_frame(&BMWiX_6F4_CONTINUE_DATA);
}
if (rx_frame.DLC = 64 && rx_frame.data.u8[0] == 0xF4 &&
@ -307,7 +307,7 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
logging.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset");
#endif // DEBUG_LOG
//set_event(EVENT_BATTERY_VALUE_UNAVAILABLE, (millis())); //Eventually need new Info level event type
transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery);
transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET);
} else { //Only ingest values if they are not the 10V Error state
min_cell_voltage = (rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]);
max_cell_voltage = (rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]);
@ -405,8 +405,8 @@ void BmwIXBattery::transmit_can(unsigned long currentMillis) {
ContactorState.closed ==
true) { // Do not send unless the contactors are requested to be closed and are closed, as sending these does not allow the contactors to close
uds_req_id_counter = increment_uds_req_id_counter(uds_req_id_counter);
transmit_can_frame(UDS_REQUESTS100MS[uds_req_id_counter],
can_config.battery); // FIXME: sending these does not allow the contactors to close
transmit_can_frame(
UDS_REQUESTS100MS[uds_req_id_counter]); // FIXME: sending these does not allow the contactors to close
} else { // FIXME: hotfix: If contactors are not requested to be closed, ensure the battery is reported as alive, even if no CAN messages are received
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
}
@ -419,7 +419,7 @@ void BmwIXBattery::transmit_can(unsigned long currentMillis) {
}
//Send SME Keep alive values 100ms
//transmit_can_frame(&BMWiX_510, can_config.battery);
//transmit_can_frame(&BMWiX_510);
}
// Send 200ms CAN Message
if (currentMillis - previousMillis200 >= INTERVAL_200_MS) {
@ -427,7 +427,7 @@ void BmwIXBattery::transmit_can(unsigned long currentMillis) {
//Send SME Keep alive values 200ms
//BMWiX_C0.data.u8[0] = increment_C0_counter(BMWiX_C0.data.u8[0]); //Keep Alive 1
//transmit_can_frame(&BMWiX_C0, can_config.battery);
//transmit_can_frame(&BMWiX_C0);
}
// Send 1000ms CAN Message
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
@ -438,8 +438,8 @@ void BmwIXBattery::transmit_can(unsigned long currentMillis) {
// Send 10000ms CAN Message
if (currentMillis - previousMillis10000 >= INTERVAL_10_S) {
previousMillis10000 = currentMillis;
//transmit_can_frame(&BMWiX_6F4_REQUEST_BALANCING_START2, can_config.battery);
//transmit_can_frame(&BMWiX_6F4_REQUEST_BALANCING_START, can_config.battery);
//transmit_can_frame(&BMWiX_6F4_REQUEST_BALANCING_START2);
//transmit_can_frame(&BMWiX_6F4_REQUEST_BALANCING_START);
}
}
@ -448,7 +448,7 @@ void BmwIXBattery::setup(void) { // Performs one time setup at startup
datalayer.system.info.battery_protocol[63] = '\0';
//Reset Battery at bootup
//transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery);
//transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET);
//Before we have started up and detected which battery is in use, use 108S values
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -547,20 +547,20 @@ void BmwIXBattery::HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms) {
if (counter_10ms == 0) {
// @0 ms
transmit_can_frame(&BMWiX_510, can_config.battery);
transmit_can_frame(&BMWiX_510);
#ifdef DEBUG_LOG
logging.println("Transmitted 0x510 - 1/6");
#endif // DEBUG_LOG
} else if (counter_10ms == 5) {
// @50 ms
transmit_can_frame(&BMWiX_276, can_config.battery);
transmit_can_frame(&BMWiX_276);
#ifdef DEBUG_LOG
logging.println("Transmitted 0x276 - 2/6");
#endif // DEBUG_LOG
} else if (counter_10ms == 10) {
// @100 ms
BMWiX_510.data.u8[2] = 0x04; // TODO: check if needed
transmit_can_frame(&BMWiX_510, can_config.battery);
transmit_can_frame(&BMWiX_510);
#ifdef DEBUG_LOG
logging.println("Transmitted 0x510 - 3/6");
#endif // DEBUG_LOG
@ -568,7 +568,7 @@ void BmwIXBattery::HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms) {
// @200 ms
BMWiX_510.data.u8[2] = 0x10; // TODO: check if needed
BMWiX_510.data.u8[5] = 0x80; // needed to close contactors
transmit_can_frame(&BMWiX_510, can_config.battery);
transmit_can_frame(&BMWiX_510);
#ifdef DEBUG_LOG
logging.println("Transmitted 0x510 - 4/6");
#endif // DEBUG_LOG
@ -576,7 +576,7 @@ void BmwIXBattery::HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms) {
// @300 ms
BMWiX_16E.data.u8[0] = 0x6A;
BMWiX_16E.data.u8[1] = 0xAD;
transmit_can_frame(&BMWiX_16E, can_config.battery);
transmit_can_frame(&BMWiX_16E);
#ifdef DEBUG_LOG
logging.println("Transmitted 0x16E - 5/6");
#endif // DEBUG_LOG
@ -584,7 +584,7 @@ void BmwIXBattery::HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms) {
// @500 ms
BMWiX_16E.data.u8[0] = 0x03;
BMWiX_16E.data.u8[1] = 0xA9;
transmit_can_frame(&BMWiX_16E, can_config.battery);
transmit_can_frame(&BMWiX_16E);
#ifdef DEBUG_LOG
logging.println("Transmitted 0x16E - 6/6");
#endif // DEBUG_LOG
@ -613,20 +613,20 @@ void BmwIXBattery::BmwIxKeepContactorsClosed(uint8_t counter_100ms) {
logging.println("Sending keep contactors closed messages started");
#endif // DEBUG_LOG
// @0 ms
transmit_can_frame(&BMWiX_510, can_config.battery);
transmit_can_frame(&BMWiX_510);
} else if (counter_100ms == 7) {
// @ 730 ms
BMWiX_16E.data.u8[0] = 0x8C;
BMWiX_16E.data.u8[1] = 0xA0;
transmit_can_frame(&BMWiX_16E, can_config.battery);
transmit_can_frame(&BMWiX_16E);
} else if (counter_100ms == 24) {
// @2380 ms
transmit_can_frame(&BMWiX_510, can_config.battery);
transmit_can_frame(&BMWiX_510);
} else if (counter_100ms == 29) {
// @ 2900 ms
BMWiX_16E.data.u8[0] = 0x02;
BMWiX_16E.data.u8[1] = 0xA7;
transmit_can_frame(&BMWiX_16E, can_config.battery);
transmit_can_frame(&BMWiX_16E);
#ifdef DEBUG_LOG
logging.println("Sending keep contactors closed messages finished");
#endif // DEBUG_LOG
@ -645,14 +645,14 @@ void BmwIXBattery::HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms) {
// @0 ms (0.00) RX0 510 [8] 40 10 00 00 00 80 00 00
BMWiX_510.data = {0x40, 0x10, 0x00, 0x00,
0x00, 0x80, 0x00, 0x00}; // Explicit declaration, to prevent modification by other functions
transmit_can_frame(&BMWiX_510, can_config.battery);
transmit_can_frame(&BMWiX_510);
// set back to default values
BMWiX_510.data = {0x40, 0x10, 0x04, 0x00, 0x00, 0x80, 0x01, 0x00}; // default values
} else if (counter_10ms == 6) {
// @60 ms (0.06) RX0 16E [8] E6 A4 C8 FF 60 C9 33 F0
BMWiX_16E.data = {0xE6, 0xA4, 0xC8, 0xFF,
0x60, 0xC9, 0x33, 0xF0}; // Explicit declaration, to prevent modification by other functions
transmit_can_frame(&BMWiX_16E, can_config.battery);
transmit_can_frame(&BMWiX_16E);
// set back to default values
BMWiX_16E.data = {0x00, 0xA0, 0xC9, 0xFF, 0x60, 0xC9, 0x3A, 0xF7}; // default values
ContactorState.closed = false;

View file

@ -1,7 +1,6 @@
#ifndef BMW_IX_BATTERY_H
#define BMW_IX_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "BMW-IX-HTML.h"
#include "CanBattery.h"

View file

@ -1,5 +1,4 @@
#include "BMW-IX-HTML.h"
#include "../include.h"
#include "BMW-IX-BATTERY.h"
String BmwIXHtmlRenderer::get_status_html() {

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../devboard/utils/logging.h"
const unsigned char crc8_table[256] =
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
@ -188,12 +188,12 @@ void BmwPhevBattery::wake_battery_via_canbus() {
// Followed by a Recessive interval of at least ~3µs (min) and at most ~10µs (max)
// Then a second dominant pulse of similar timing.
CAN_cfg.speed = CAN_SPEED_100KBPS; //Slow down canbus to achieve wakeup timings
ESP32Can.CANInit(); // ReInit native CAN module at new speed
transmit_can_frame(&BMW_PHEV_BUS_WAKEUP_REQUEST, can_config.battery);
transmit_can_frame(&BMW_PHEV_BUS_WAKEUP_REQUEST, can_config.battery);
CAN_cfg.speed = CAN_SPEED_500KBPS; //Resume fullspeed
ESP32Can.CANInit(); // ReInit native CAN module at new speed
auto original_speed = change_can_speed(CAN_Speed::CAN_SPEED_100KBPS);
transmit_can_frame(&BMW_PHEV_BUS_WAKEUP_REQUEST);
transmit_can_frame(&BMW_PHEV_BUS_WAKEUP_REQUEST);
change_can_speed(original_speed);
#ifdef DEBUG_LOG
logging.println("Sent magic wakeup packet to SME at 100kbps...");
@ -432,7 +432,7 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
#if defined(DEBUG_LOG) && defined(UDS_LOG)
logging.println("Requesting continue frame...");
#endif // DEBUG_LOG && UDS_LOG
transmit_can_frame(&BMW_6F1_REQUEST_CONTINUE_MULTIFRAME, can_config.battery);
transmit_can_frame(&BMW_6F1_REQUEST_CONTINUE_MULTIFRAME);
break;
}
@ -478,7 +478,7 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
#if defined(DEBUG_LOG) && defined(UDS_LOG)
logging.println("Batch Complete - Requesting continue frame...");
#endif // DEBUG_LOG && UDS_LOG
transmit_can_frame(&BMW_6F1_REQUEST_CONTINUE_MULTIFRAME, can_config.battery);
transmit_can_frame(&BMW_6F1_REQUEST_CONTINUE_MULTIFRAME);
gUDSContext.receivedInBatch = 0; // Reset batch count
Serial.println("Sent FC for next batch of 3 frames.");
}
@ -659,7 +659,7 @@ void BmwPhevBattery::transmit_can(unsigned long currentMillis) {
//if (datalayer.battery.status.bms_status == FAULT) { //ALLOW ANY TIME - TEST ONLY
//} //If battery is not in Fault mode, allow contactor to close by sending 10B
//else {
transmit_can_frame(&BMW_10B, can_config.battery);
transmit_can_frame(&BMW_10B);
//}
}
@ -672,7 +672,7 @@ void BmwPhevBattery::transmit_can(unsigned long currentMillis) {
previousMillis200 = currentMillis;
uds_fast_req_id_counter = increment_uds_req_id_counter(
uds_fast_req_id_counter, numFastUDSreqs); //Loop through and send a different UDS request each cycle
transmit_can_frame(UDS_REQUESTS_FAST[uds_fast_req_id_counter], can_config.battery);
transmit_can_frame(UDS_REQUESTS_FAST[uds_fast_req_id_counter]);
}
// Send 1000ms CAN Message
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
@ -680,7 +680,7 @@ void BmwPhevBattery::transmit_can(unsigned long currentMillis) {
uds_slow_req_id_counter = increment_uds_req_id_counter(
uds_slow_req_id_counter, numSlowUDSreqs); //Loop through and send a different UDS request each cycle
transmit_can_frame(UDS_REQUESTS_SLOW[uds_slow_req_id_counter], can_config.battery);
transmit_can_frame(UDS_REQUESTS_SLOW[uds_slow_req_id_counter]);
}
// Send 5000ms CAN Message
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
@ -692,8 +692,7 @@ void BmwPhevBattery::transmit_can(unsigned long currentMillis) {
// Send 10000ms CAN Message
if (currentMillis - previousMillis10000 >= INTERVAL_10_S) {
previousMillis10000 = currentMillis;
transmit_can_frame(&BMWPHEV_6F1_REQUEST_BALANCING_START,
can_config.battery); // Enable Balancing
transmit_can_frame(&BMWPHEV_6F1_REQUEST_BALANCING_START); // Enable Balancing
}
}
@ -703,8 +702,7 @@ void BmwPhevBattery::setup(void) { // Performs one time setup at startup
//Wakeup the SME
wake_battery_via_canbus();
transmit_can_frame(&BMWPHEV_6F1_REQUEST_ISOLATION_TEST,
can_config.battery); // Run Internal Isolation Test at startup
transmit_can_frame(&BMWPHEV_6F1_REQUEST_ISOLATION_TEST); // Run Internal Isolation Test at startup
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;

View file

@ -1,7 +1,6 @@
#ifndef BMW_PHEV_BATTERY_H
#define BMW_PHEV_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "BMW-PHEV-HTML.h"
#include "CanBattery.h"

View file

@ -1,7 +1,6 @@
#include "BMW-SBOX.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../include.h"
uint8_t reverse_bits(uint8_t byte) {
uint8_t reversed = 0;
@ -172,8 +171,8 @@ void BmwSbox::transmit_can(unsigned long currentMillis) {
SBOX_100.data.u8[1] = CAN100_cnt << 4 | 0x01;
SBOX_100.data.u8[3] = 0x00;
SBOX_100.data.u8[3] = calculateCRC(SBOX_100);
transmit_can_frame(&SBOX_100, can_config.shunt);
transmit_can_frame(&SBOX_300, can_config.shunt);
transmit_can_frame(&SBOX_100);
transmit_can_frame(&SBOX_300);
}
}

View file

@ -1,6 +1,5 @@
#ifndef BMW_SBOX_CONTROL_H
#define BMW_SBOX_CONTROL_H
#include "../include.h"
#ifdef BMW_SBOX
#define SELECTED_SHUNT_CLASS BmwSbox

View file

@ -3,27 +3,22 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "../include.h"
/*
TODOs left for this implementation
- The battery has 3 CAN ports. One of the internal modules is responsible for the 7E4 polls, the battery for the 7E7 polls
- Current implementation only seems to get the 7E7 polls working.
- We might need to poll on 7E6 also?
- The values missing for a working implementation is:
- SOC% missing! This is absolutely mandatory to fix before starting to use this!
- Capacity (kWh) (can be estimated)
- Charge max power (can be estimated)
- Discharge max power (can be estimated)
- SOH% (low prio))
- The values missing for a fully working implementation is:
- SOC% missing! (now estimated based on voltage)
- Capacity (kWh) (now estimated)
- Charge max power (now estimated)
- Discharge max power (now estimated)
- Current is updating extremely slow, consider switching to sensed_ value
- Balancing info seems to be available via OBD/ISO-TP service $22OBD/ISO-TP service $22 , 4340 and onwards
*/
/*TODO, messages we might need to send towards the battery to keep it happy and close contactors
0x262 Battery Block Voltage Diag Status HV (Who sends this? Battery?)
0x272 Battery Cell Voltage Diag Status HV (Who sends this? Battery?)
0x274 Battery Temperature Sensor diagnostic status HV (Who sends this? Battery?)
0x270 Battery VoltageSensor BalancingSwitches diagnostic status (Who sends this? Battery?)
0x214 Charger coolant temp info HV
0x20E Hybrid balancing request HV
0x30E High Voltage Charger Command HV
@ -39,48 +34,86 @@ TODOs left for this implementation
0x460 Energy Storage System Temp HV (Who sends this? Battery?)
*/
void BoltAmperaBattery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer
// Define the data points for %SOC depending on cell voltage
// NCM Discharge Curve Lookup Table (100 points, 4.2V-3.0V)
// SOC[100] = State of Charge (0.01% units, e.g., 10000 = 100.00%)
// voltage_lookup[100] = Pack voltage (mV)
const uint8_t numEntries = 100;
const uint16_t SOC[100] = {
10000, 9985, 9970, 9955, 9940, 9925, 9910, 9895, 9880, 9865, // 4.20V - 4.15V (High plateau)
9850, 9820, 9790, 9760, 9730, 9700, 9660, 9620, 9580, 9540, // 4.14V - 4.00V
9500, 9450, 9400, 9350, 9300, 9250, 9200, 9150, 9100, 9050, // 3.99V - 3.90V
9000, 8900, 8800, 8700, 8600, 8500, 8400, 8300, 8200, 8100, // 3.89V - 3.80V
8000, 7850, 7700, 7550, 7400, 7250, 7100, 6950, 6800, 6650, // 3.79V - 3.70V
6500, 6300, 6100, 5900, 5700, 5500, 5300, 5100, 4900, 4700, // 3.69V - 3.60V
4500, 4300, 4100, 3900, 3700, 3500, 3300, 3100, 2900, 2700, // 3.59V - 3.50V
2500, 2250, 2000, 1750, 1500, 1250, 1000, 800, 600, 400, // 3.49V - 3.40V (Steep drop)
300, 200, 150, 100, 80, 60, 40, 30, 20, 10, // 3.39V - 3.30V
5, 2, 1, 0, 0, 0, 0, 0, 0, 0 // <3.30V (Cutoff)
};
datalayer.battery.status.real_soc = battery_SOC_display;
//datalayer.battery.status.voltage_dV = battery_voltage * 0.52;
datalayer.battery.status.voltage_dV = (battery_voltage_periodic / 8) * 10;
datalayer.battery.status.current_dA = battery_current_7E7;
datalayer.battery.info.total_capacity_Wh;
datalayer.battery.status.remaining_capacity_Wh;
datalayer.battery.status.soh_pptt;
datalayer.battery.status.max_discharge_power_W;
datalayer.battery.status.max_charge_power_W;
// Store temperatures in an array
int16_t temperatures[] = {temperature_1, temperature_2, temperature_3, temperature_4, temperature_5, temperature_6};
// Initialize highest and lowest to the first element
temperature_highest = temperatures[0];
temperature_lowest = temperatures[0];
// Iterate through the array to find the highest and lowest values
for (uint8_t i = 1; i < 6; ++i) {
if (temperatures[i] > temperature_highest) {
temperature_highest = temperatures[i];
}
if (temperatures[i] < temperature_lowest) {
temperature_lowest = temperatures[i];
}
const uint16_t voltage_lookup[100] = {
4200, 4195, 4190, 4185, 4180, 4175, 4170, 4165, 4160, 4155, // High plateau
4150, 4140, 4130, 4120, 4110, 4100, 4090, 4080, 4070, 4060, 4050, 4040, 4030, 4020, 4010, 4000, 3990, 3980,
3970, 3960, 3950, 3940, 3930, 3920, 3910, 3900, 3890, 3880, 3870, 3860, 3850, 3840, 3830, 3820, 3810, 3800,
3790, 3780, 3770, 3760, 3750, 3740, 3730, 3720, 3710, 3700, 3690, 3680, 3670, 3660, 3650, 3640, 3630, 3620,
3610, 3600, 3590, 3580, 3570, 3560, 3550, 3540, 3530, 3520, 3510, 3500, 3490, 3480, 3470, 3460, 3450, 3440,
3430, 3420, 3410, 3400, 3390, 3380, 3370, 3360, 3350, 3340, 3330, 3320, 3310, 3300, 3290, 3280, 3270, 3260};
static uint16_t estimateSOC(uint16_t cellVoltage) { // Linear interpolation function
if (cellVoltage >= voltage_lookup[0]) {
return SOC[0];
}
if (cellVoltage <= voltage_lookup[numEntries - 1]) {
return SOC[numEntries - 1];
}
datalayer.battery.status.temperature_min_dC = temperature_lowest * 10;
for (int i = 1; i < numEntries; ++i) {
if (cellVoltage >= voltage_lookup[i]) {
double t = (cellVoltage - voltage_lookup[i]) / (voltage_lookup[i - 1] - voltage_lookup[i]);
return SOC[i] + t * (SOC[i - 1] - SOC[i]);
}
}
return 0; // Default return for safety, should never reach here
}
datalayer.battery.status.temperature_max_dC = temperature_highest * 10;
void BoltAmperaBattery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer
datalayer.battery.status.real_soc = estimateSOC(battery_cell_voltage_max_mV); //TODO, this is bad and barely works
datalayer.battery.status.voltage_dV = battery_voltage_periodic_dV;
datalayer.battery.status.current_dA = (sensed_current_sensor_1 * 0.2); //TODO: Is sensor 1 OK?
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 = 9900;
// Charge power is set in .h file (TODO: Remove this estimation when real value has been found)
if (datalayer.battery.status.real_soc > 9900) {
datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_WHEN_TOPBALANCING_W;
} else if (datalayer.battery.status.real_soc > RAMPDOWN_SOC) {
// When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0
datalayer.battery.status.max_charge_power_W =
MAX_CHARGE_POWER_ALLOWED_W *
(1 - (datalayer.battery.status.real_soc - RAMPDOWN_SOC) / (10000.0 - RAMPDOWN_SOC));
} else { // No limits, max charging power allowed
datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_ALLOWED_W;
}
// Discharge power is also set in .h file (TODO: Remove this estimation when real value has been found)
datalayer.battery.status.max_discharge_power_W = MAX_DISCHARGE_POWER_ALLOWED_W;
datalayer.battery.status.temperature_min_dC = temperature_lowest_C * 10;
datalayer.battery.status.temperature_max_dC = temperature_highest_C * 10;
//Map all cell voltages to the global array
memcpy(datalayer.battery.status.cell_voltages_mV, battery_cell_voltages, 96 * sizeof(uint16_t));
memcpy(datalayer.battery.status.cell_voltages_mV, cellblock_voltage, 96 * sizeof(uint16_t));
datalayer.battery.status.cell_max_voltage_mV = battery_cell_voltage_max_mV;
datalayer.battery.status.cell_min_voltage_mV = battery_cell_voltage_min_mV;
// Update webserver datalayer
datalayer_extended.boltampera.battery_5V_ref = battery_5V_ref;
@ -120,38 +153,223 @@ 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;
mux = ((rx_frame.data.u8[6] & 0xE0) >> 5); //goes from 0-7
cellbank_mux = ((rx_frame.data.u8[6] & 0xE0) >> 5); //Goes from 0-7
switch (cellbank_mux) {
case 0:
cellblock_voltage[0] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[1] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[2] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 1:
cellblock_voltage[3] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[4] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[5] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 2:
cellblock_voltage[6] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[7] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[8] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 3:
cellblock_voltage[9] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[10] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[11] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 4:
cellblock_voltage[12] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[13] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[14] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 5:
cellblock_voltage[15] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[16] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[17] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 6:
cellblock_voltage[18] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[19] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[20] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 7:
cellblock_voltage[21] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[22] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[23] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
default:
break;
}
break;
case 0x202: //High voltage Battery Cell Voltage Matrix 2
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
mux = ((rx_frame.data.u8[6] & 0xE0) >> 5); //goes from 0-7
cellbank_mux = ((rx_frame.data.u8[6] & 0xE0) >> 5); //goes from 0-7
switch (cellbank_mux) {
case 0:
cellblock_voltage[24] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[25] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[26] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 1:
cellblock_voltage[27] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[28] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[29] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 2:
cellblock_voltage[30] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[31] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[32] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 3:
cellblock_voltage[33] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[34] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[35] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 4:
cellblock_voltage[36] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[37] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[38] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 5:
cellblock_voltage[39] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[40] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[41] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 6:
cellblock_voltage[42] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[43] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[44] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 7:
cellblock_voltage[45] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[46] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[47] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
default:
break;
}
break;
case 0x204: //High voltage Battery Cell Voltage Matrix 3
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
mux = ((rx_frame.data.u8[6] & 0xE0) >> 5); //goes from 0-7
cellbank_mux = ((rx_frame.data.u8[6] & 0xE0) >> 5); //goes from 0-7
switch (cellbank_mux) {
case 0:
cellblock_voltage[48] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[49] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[50] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 1:
cellblock_voltage[51] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[52] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[53] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 2:
cellblock_voltage[54] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[55] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[56] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 3:
cellblock_voltage[57] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[58] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[59] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 4:
cellblock_voltage[60] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[61] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[62] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 5:
cellblock_voltage[63] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[64] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[65] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 6:
cellblock_voltage[66] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[67] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[68] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 7:
cellblock_voltage[69] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[70] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[71] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
default:
break;
}
break;
case 0x206: //High voltage Battery Cell Voltage Matrix 4
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
mux = ((rx_frame.data.u8[6] & 0xE0) >> 5); //goes from 0-7
cellbank_mux = ((rx_frame.data.u8[6] & 0xE0) >> 5); //goes from 0-7
switch (cellbank_mux) {
case 0:
cellblock_voltage[72] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[73] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[74] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 1:
cellblock_voltage[75] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[76] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[77] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 2:
cellblock_voltage[78] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[79] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[80] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 3:
cellblock_voltage[81] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[82] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[83] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 4:
cellblock_voltage[84] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[85] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[86] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 5:
cellblock_voltage[87] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[88] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[89] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 6:
cellblock_voltage[90] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[91] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[92] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
case 7:
cellblock_voltage[93] = (((rx_frame.data.u8[0] & 0x1F) << 7) | ((rx_frame.data.u8[1] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[94] = (((rx_frame.data.u8[2] & 0x1F) << 7) | ((rx_frame.data.u8[3] & 0xFE) >> 1)) * 1.25;
cellblock_voltage[95] = (((rx_frame.data.u8[4]) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 1.25;
break;
default:
break;
}
break;
case 0x208: //High voltage Battery Cell Voltage Matrix 5
case 0x208: //High voltage Battery Cell Voltage Matrix 5 (Empty on most packs)
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
mux = ((rx_frame.data.u8[6] & 0xE0) >> 5); //goes from 0-7
cellbank_mux = ((rx_frame.data.u8[6] & 0xE0) >> 5); //goes from 0-7
break;
case 0x20A: //VICM Status HV
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x20C: //VITM Status HV
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
battery_isolation_kohm = (rx_frame.data.u8[1] * 25);
battery_cell_voltage_max_mV = (rx_frame.data.u8[4] * 20);
battery_cell_voltage_min_mV = (rx_frame.data.u8[5] * 20);
break;
case 0x216: // High voltage battery sensed Output HV
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
sensed_battery_voltage_mV = (((rx_frame.data.u8[1] & 0x0F) << 4) | rx_frame.data.u8[2]) * 125; //mV
sensed_current_sensor_1 = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
sensed_current_sensor_2 = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]);
break;
case 0x2C7:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
battery_voltage_periodic = (rx_frame.data.u8[3] << 4) | (rx_frame.data.u8[4] >> 4);
battery_voltage_periodic_dV = ((rx_frame.data.u8[3] << 4) | (rx_frame.data.u8[4] >> 4)) * 1.25;
/*355V 2C7 [6] 03 20 00 AF A0 00
360V 2C7 [6] 03 20 00 AD D0 00
396V 2C7 [6] 03 20 53 C7 30 00*/
break;
case 0x260: //VITM Diagnostic Status 1 HV
case 0x260: //VITM Diagnostic Status 1 HV (Contains which DTCs are active)
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x262: //Battery block voltage diagnostic status
@ -174,28 +392,35 @@ void BoltAmperaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
temperature_4 = ((rx_frame.data.u8[4] / 2) - 40); //Module 4 Temperature
temperature_5 = ((rx_frame.data.u8[5] / 2) - 40); //Module 5 Temperature
temperature_6 = ((rx_frame.data.u8[6] / 2) - 40); //Module 6 Temperature
//There is also a mux here to get more temps, but not required for our integration
//since we only care about min and max temps (from message 3E3)
break;
case 0x304: //High Voltage Control Energy Management HV
break;
case 0x307: //High Voltage Battery SOC HV
//TODO: Is this CAN message on all packs? If so, SOC is here
break;
case 0x3E3:
case 0x308: //24 92 49 24 90
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x3E3: //Min and maximum values
//Frame0 is cellvoltage min * 20
//Frame1 is cellvoltage max * 20
//Frame7 is cellvoltage avg * 20
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
temperature_lowest_C = ((rx_frame.data.u8[2] / 2) - 40);
temperature_highest_C = ((rx_frame.data.u8[4] / 2) - 40);
break;
case 0x460: //Energy Storage System Temp HV
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
inlet_coolant_temperature = ((((rx_frame.data.u8[0] & 0x03) << 8) | rx_frame.data.u8[1]) / 2) - 40;
outlet_coolant_temperature = ((((rx_frame.data.u8[2] & 0x03) << 8) | rx_frame.data.u8[3]) / 2) - 40;
break;
case 0x5EF: //OBD7E7 Unsolicited tester responce (ECU to tester)
case 0x5EF: //OBD7E7 Unsolicited tester responce (UUDT)
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x5EC: //OBD7E4 Unsolicited tester responce (ECU to tester)
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x7EC: //When polling 7E4 BMS replies with 7EC
case 0x7EC: //When polling 7E4 BMS replies with 7EC (This is not working for some reason)
if (rx_frame.data.u8[0] == 0x10) { //"PID Header"
transmit_can_frame(&BOLT_ACK_7E4, can_config.battery);
transmit_can_frame(&BOLT_ACK_7E4);
}
//Frame 2 & 3 contains reply
@ -267,7 +492,7 @@ void BoltAmperaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
case 0x7EF: //When polling 7E7 BMS replies with 7EF
if (rx_frame.data.u8[0] == 0x10) { //"PID Header"
transmit_can_frame(&BOLT_ACK_7E7, can_config.battery);
transmit_can_frame(&BOLT_ACK_7E7);
}
//Frame 2 & 3 contains reply
@ -611,7 +836,7 @@ void BoltAmperaBattery::transmit_can(unsigned long currentMillis) {
//Send 20ms message
if (currentMillis - previousMillis20ms >= INTERVAL_20_MS) {
previousMillis20ms = currentMillis;
transmit_can_frame(&BOLT_778, can_config.battery);
transmit_can_frame(&BOLT_778);
}
//Send 100ms message
@ -625,7 +850,7 @@ void BoltAmperaBattery::transmit_can(unsigned long currentMillis) {
BOLT_POLL_7E7.data.u8[2] = (uint8_t)((currentpoll_7E7 & 0xFF00) >> 8);
BOLT_POLL_7E7.data.u8[3] = (uint8_t)(currentpoll_7E7 & 0x00FF);
transmit_can_frame(&BOLT_POLL_7E7, can_config.battery);
transmit_can_frame(&BOLT_POLL_7E7);
}
//Send 120ms message
@ -639,7 +864,7 @@ void BoltAmperaBattery::transmit_can(unsigned long currentMillis) {
BOLT_POLL_7E4.data.u8[2] = (uint8_t)((currentpoll_7E4 & 0xFF00) >> 8);
BOLT_POLL_7E4.data.u8[3] = (uint8_t)(currentpoll_7E4 & 0x00FF);
transmit_can_frame(&BOLT_POLL_7E4, can_config.battery);
//transmit_can_frame(&BOLT_POLL_7E4); //TODO: Battery does not seem to reply on this poll
}
}
@ -647,6 +872,7 @@ void BoltAmperaBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 96;
datalayer.battery.info.total_capacity_Wh = 64000;
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;

View file

@ -1,7 +1,6 @@
#ifndef BOLT_AMPERA_BATTERY_H
#define BOLT_AMPERA_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "BOLT-AMPERA-HTML.h"
#include "CanBattery.h"
@ -23,11 +22,17 @@ class BoltAmperaBattery : public CanBattery {
private:
BoltAmperaHtmlRenderer renderer;
static const int MAX_PACK_VOLTAGE_DV = 4150; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 2500;
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 MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 2510;
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_CELL_VOLTAGE_MV = 4220; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 3000; //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;
@ -167,30 +172,6 @@ class BoltAmperaBattery : public CanBattery {
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;
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
@ -226,11 +207,16 @@ class BoltAmperaBattery : public CanBattery {
// 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)
// 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 soc_periodic = 0;
uint16_t battery_cell_voltages[96]; //array with all the cellvoltages polled via PID
uint16_t cellblock_voltage[96]; //array with all the cellvoltages, constantly broadcasted
uint32_t sensed_battery_voltage_mV = 0;
int16_t sensed_current_sensor_1 = 0;
int16_t sensed_current_sensor_2 = 0;
uint16_t battery_capacity_my17_18 = 0;
uint16_t battery_capacity_my19plus = 0;
uint16_t battery_SOC_display = 0;
@ -243,9 +229,9 @@ class BoltAmperaBattery : public CanBattery {
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_voltage_periodic_dV = 3700;
uint16_t battery_vehicle_isolation = 0;
uint16_t battery_isolation_kohm = 0;
uint16_t battery_isolation_kohm = 9999;
uint16_t battery_HV_locked = 0;
uint16_t battery_crash_event = 0;
uint16_t battery_HVIL = 0;
@ -258,20 +244,24 @@ class BoltAmperaBattery : public CanBattery {
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_voltage_max_mV = 3700;
uint16_t battery_cell_voltage_min_mV = 3700;
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 inlet_coolant_temperature = 0;
int16_t outlet_coolant_temperature = 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;
int16_t temperature_highest_C = 0;
int16_t temperature_lowest_C = 0;
uint8_t cellbank_mux = 0;
uint8_t poll_index_7E4 = 0;
uint16_t currentpoll_7E4 = POLL_7E4_CAPACITY_EST_GEN1;
uint16_t reply_poll_7E4 = 0;

View file

@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "../include.h"
/* Notes
SOC% by default is now ESTIMATED.
@ -21,17 +20,17 @@ After battery has been unlocked, you can remove the "USE_ESTIMATED_SOC" from the
#define UNKNOWN_POLL_0 0x1FFE //0x64 19 C4 3B
#define UNKNOWN_POLL_1 0x1FFC //0x72 1F C4 3B
#define POLL_MAX_CHARGE_POWER 0x000A
#define UNKNOWN_POLL_3 0x000B //0x00B1 (177 interesting!)
#define UNKNOWN_POLL_4 0x000E //0x0B27 (2855 interesting!)
#define POLL_CHARGE_TIMES 0x000B // Using Carscanner name for now. Likely a counter for BMS 100% SOC calibration
#define POLL_MAX_DISCHARGE_POWER 0x000E
#define POLL_TOTAL_CHARGED_AH 0x000F
#define POLL_TOTAL_DISCHARGED_AH 0x0010
#define POLL_TOTAL_CHARGED_KWH 0x0011
#define POLL_TOTAL_DISCHARGED_KWH 0x0012
#define UNKNOWN_POLL_9 0x0004 //0x0034 (52 interesting!)
#define UNKNOWN_POLL_10 0x002A //0x5B
#define UNKNOWN_POLL_11 0x002E //0x08 (probably module number, or cell number?)
#define UNKNOWN_POLL_12 0x002C //0x43
#define UNKNOWN_POLL_13 0x0030 //0x01 (probably module number, or cell number?)
#define POLL_TIMES_FULL_POWER 0x0004 // Using Carscanner name for now. Unknown what it means for the moment
#define UNKNOWN_POLL_10 0x002A //0x5B
#define UNKNOWN_POLL_11 0x002E //0x08 (probably module number, or cell number?)
#define UNKNOWN_POLL_12 0x002C //0x43
#define UNKNOWN_POLL_13 0x0030 //0x01 (probably module number, or cell number?)
#define POLL_MODULE_1_LOWEST_MV_NUMBER 0x016C
#define POLL_MODULE_1_LOWEST_CELL_MV 0x016D
#define POLL_MODULE_1_HIGHEST_MV_NUMBER 0x016E
@ -172,9 +171,20 @@ void BydAttoBattery::
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 = MAXPOWER_DISCHARGE_W; //TODO: Map from CAN later on
if (SOC_method == ESTIMATED && battery_estimated_SOC * 0.1 < RAMPDOWN_SOC && RAMPDOWN_SOC > 0) {
// If using estimated SOC, ramp down max discharge power as SOC decreases.
rampdown_power = RAMPDOWN_POWER_ALLOWED * ((battery_estimated_SOC * 0.1) / RAMPDOWN_SOC);
datalayer_battery->status.max_charge_power_W = BMS_allowed_charge_power * 10; //TODO: Scaling unknown, *10 best guess
if (rampdown_power < BMS_allowed_discharge_power * 100) { // Never allow more than BMS_allowed_discharge_power
datalayer_battery->status.max_discharge_power_W = rampdown_power;
} else {
datalayer_battery->status.max_discharge_power_W = BMS_allowed_discharge_power * 100;
}
} else {
datalayer_battery->status.max_discharge_power_W = BMS_allowed_discharge_power * 100;
}
datalayer_battery->status.max_charge_power_W = BMS_allowed_charge_power * 100;
datalayer_battery->status.cell_max_voltage_mV = BMS_highest_cell_voltage_mV;
@ -277,13 +287,13 @@ void BydAttoBattery::
datalayer_bydatto->unknown0 = BMS_unknown0;
datalayer_bydatto->unknown1 = BMS_unknown1;
datalayer_bydatto->chargePower = BMS_allowed_charge_power;
datalayer_bydatto->unknown3 = BMS_unknown3;
datalayer_bydatto->unknown4 = BMS_unknown4;
datalayer_bydatto->charge_times = BMS_charge_times;
datalayer_bydatto->dischargePower = BMS_allowed_discharge_power;
datalayer_bydatto->total_charged_ah = BMS_total_charged_ah;
datalayer_bydatto->total_discharged_ah = BMS_total_discharged_ah;
datalayer_bydatto->total_charged_kwh = BMS_total_charged_kwh;
datalayer_bydatto->total_discharged_kwh = BMS_total_discharged_kwh;
datalayer_bydatto->unknown9 = BMS_unknown9;
datalayer_bydatto->times_full_power = BMS_times_full_power;
datalayer_bydatto->unknown10 = BMS_unknown10;
datalayer_bydatto->unknown11 = BMS_unknown11;
datalayer_bydatto->unknown12 = BMS_unknown12;
@ -400,7 +410,7 @@ void BydAttoBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
break;
case 0x7EF: //OBD2 PID reply from battery
if (rx_frame.data.u8[0] == 0x10) {
transmit_can_frame(&ATTO_3_7E7_ACK, can_interface); //Send next line request
transmit_can_frame(&ATTO_3_7E7_ACK); //Send next line request
}
pid_reply = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
switch (pid_reply) {
@ -439,11 +449,11 @@ void BydAttoBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
case POLL_MAX_CHARGE_POWER:
BMS_allowed_charge_power = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
break;
case UNKNOWN_POLL_3:
BMS_unknown3 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
case POLL_CHARGE_TIMES:
BMS_charge_times = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
break;
case UNKNOWN_POLL_4:
BMS_unknown4 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
case POLL_MAX_DISCHARGE_POWER:
BMS_allowed_discharge_power = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
break;
case POLL_TOTAL_CHARGED_AH:
BMS_total_charged_ah = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
@ -457,8 +467,8 @@ void BydAttoBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
case POLL_TOTAL_DISCHARGED_KWH:
BMS_total_discharged_kwh = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
break;
case UNKNOWN_POLL_9:
BMS_unknown9 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
case POLL_TIMES_FULL_POWER:
BMS_times_full_power = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
break;
case UNKNOWN_POLL_10:
BMS_unknown10 = rx_frame.data.u8[4];
@ -517,7 +527,7 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) {
ATTO_3_12D.data.u8[6] = (0x0F | (frame6_counter << 4));
ATTO_3_12D.data.u8[7] = (0x09 | (frame7_counter << 4));
transmit_can_frame(&ATTO_3_12D, can_interface);
transmit_can_frame(&ATTO_3_12D);
}
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
@ -534,21 +544,21 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) {
ATTO_3_441.data.u8[7] = 0xF5;
}
transmit_can_frame(&ATTO_3_441, can_interface);
transmit_can_frame(&ATTO_3_441);
switch (stateMachineClearCrash) {
case STARTED:
ATTO_3_7E7_CLEAR_CRASH.data = {0x02, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&ATTO_3_7E7_CLEAR_CRASH, can_interface);
transmit_can_frame(&ATTO_3_7E7_CLEAR_CRASH);
stateMachineClearCrash = RUNNING_STEP_1;
break;
case RUNNING_STEP_1:
ATTO_3_7E7_CLEAR_CRASH.data = {0x04, 0x14, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00};
transmit_can_frame(&ATTO_3_7E7_CLEAR_CRASH, can_interface);
transmit_can_frame(&ATTO_3_7E7_CLEAR_CRASH);
stateMachineClearCrash = RUNNING_STEP_2;
break;
case RUNNING_STEP_2:
ATTO_3_7E7_CLEAR_CRASH.data = {0x03, 0x19, 0x02, 0x09, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&ATTO_3_7E7_CLEAR_CRASH, can_interface);
transmit_can_frame(&ATTO_3_7E7_CLEAR_CRASH);
stateMachineClearCrash = NOT_RUNNING;
break;
case NOT_RUNNING:
@ -615,16 +625,16 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) {
case POLL_MAX_CHARGE_POWER:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_MAX_CHARGE_POWER & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_MAX_CHARGE_POWER & 0x00FF);
poll_state = UNKNOWN_POLL_3;
poll_state = POLL_CHARGE_TIMES;
break;
case UNKNOWN_POLL_3:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_3 & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_3 & 0x00FF);
poll_state = UNKNOWN_POLL_4;
case POLL_CHARGE_TIMES:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_CHARGE_TIMES & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_CHARGE_TIMES & 0x00FF);
poll_state = POLL_MAX_DISCHARGE_POWER;
break;
case UNKNOWN_POLL_4:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_4 & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_4 & 0x00FF);
case POLL_MAX_DISCHARGE_POWER:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_MAX_DISCHARGE_POWER & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_MAX_DISCHARGE_POWER & 0x00FF);
poll_state = POLL_TOTAL_CHARGED_AH;
break;
case POLL_TOTAL_CHARGED_AH:
@ -645,11 +655,11 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) {
case POLL_TOTAL_DISCHARGED_KWH:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_TOTAL_DISCHARGED_KWH & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_TOTAL_DISCHARGED_KWH & 0x00FF);
poll_state = UNKNOWN_POLL_9;
poll_state = POLL_TIMES_FULL_POWER;
break;
case UNKNOWN_POLL_9:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_9 & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_9 & 0x00FF);
case POLL_TIMES_FULL_POWER:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_TIMES_FULL_POWER & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_TIMES_FULL_POWER & 0x00FF);
poll_state = UNKNOWN_POLL_10;
break;
case UNKNOWN_POLL_10:
@ -678,7 +688,7 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) {
}
if (stateMachineClearCrash == NOT_RUNNING) { //Don't poll battery for data if clear crash running
transmit_can_frame(&ATTO_3_7E7_POLL, can_interface);
transmit_can_frame(&ATTO_3_7E7_POLL);
}
}
}

View file

@ -3,20 +3,22 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../include.h"
#include "BYD-ATTO-3-HTML.h"
#include "CanBattery.h"
#define USE_ESTIMATED_SOC // If enabled, SOC is estimated from pack voltage. Useful for locked packs. \
// Comment out this only if you know your BMS is unlocked and able to send SOC%
#define MAXPOWER_CHARGE_W 10000
#define MAXPOWER_DISCHARGE_W 10000
//Uncomment and configure this line, if you want to filter out a broken temperature sensor (1-10)
//Make sure you understand risks associated with disabling. Values can be read via "More Battery info"
//#define SKIP_TEMPERATURE_SENSOR_NUMBER 1
// Ramp down settings that are used when SOC is estimated from voltage
static const int RAMPDOWN_SOC = 100; // SOC to start ramping down from. Value set here is scaled by 10 (100 = 10.0%)
static const int RAMPDOWN_POWER_ALLOWED =
10000; // Power to start ramp down from, set a lower value to limit the power even further as SOC decreases
/* Do not modify the rows below */
#ifdef BYD_ATTO_3_BATTERY
#define SELECTED_BATTERY_CLASS BydAttoBattery
@ -116,19 +118,20 @@ class BydAttoBattery : public CanBattery {
uint32_t BMS_unknown0 = 0;
uint32_t BMS_unknown1 = 0;
uint16_t BMS_allowed_charge_power = 0;
uint16_t BMS_unknown3 = 0;
uint16_t BMS_unknown4 = 0;
uint16_t BMS_charge_times = 0;
uint16_t BMS_allowed_discharge_power = 0;
uint16_t BMS_total_charged_ah = 0;
uint16_t BMS_total_discharged_ah = 0;
uint16_t BMS_total_charged_kwh = 0;
uint16_t BMS_total_discharged_kwh = 0;
uint16_t BMS_unknown9 = 0;
uint16_t BMS_times_full_power = 0;
uint8_t BMS_unknown10 = 0;
uint8_t BMS_unknown11 = 0;
uint8_t BMS_unknown12 = 0;
uint8_t BMS_unknown13 = 0;
uint8_t battery_frame_index = 0;
uint16_t battery_cellvoltages[CELLCOUNT_EXTENDED] = {0};
uint16_t rampdown_power = 0;
uint16_t poll_state = POLL_FOR_BATTERY_SOC;
uint16_t pid_reply = 0;

View file

@ -12,33 +12,38 @@ class BydAtto3HtmlRenderer : public BatteryHtmlRenderer {
String get_status_html() {
String content;
float soc_estimated = static_cast<float>(byd_datalayer->SOC_estimated) * 0.01;
float soc_measured = static_cast<float>(byd_datalayer->SOC_highprec) * 0.1;
float BMS_maxChargePower = static_cast<float>(byd_datalayer->chargePower) * 0.1;
float BMS_maxDischargePower = static_cast<float>(byd_datalayer->dischargePower) * 0.1;
static const char* SOCmethod[2] = {"Estimated from voltage", "Measured by BMS"};
content += "<h4>SOC method used: " + String(SOCmethod[byd_datalayer->SOC_method]) + "</h4>";
content += "<h4>SOC estimated: " + String(byd_datalayer->SOC_estimated) + "</h4>";
content += "<h4>SOC highprec: " + String(byd_datalayer->SOC_highprec) + "</h4>";
content += "<h4>SOC OBD2: " + String(byd_datalayer->SOC_polled) + "</h4>";
content += "<h4>Voltage periodic: " + String(byd_datalayer->voltage_periodic) + "</h4>";
content += "<h4>Voltage OBD2: " + String(byd_datalayer->voltage_polled) + "</h4>";
content += "<h4>Temperature sensor 1: " + String(byd_datalayer->battery_temperatures[0]) + "</h4>";
content += "<h4>Temperature sensor 2: " + String(byd_datalayer->battery_temperatures[1]) + "</h4>";
content += "<h4>Temperature sensor 3: " + String(byd_datalayer->battery_temperatures[2]) + "</h4>";
content += "<h4>Temperature sensor 4: " + String(byd_datalayer->battery_temperatures[3]) + "</h4>";
content += "<h4>Temperature sensor 5: " + String(byd_datalayer->battery_temperatures[4]) + "</h4>";
content += "<h4>Temperature sensor 6: " + String(byd_datalayer->battery_temperatures[5]) + "</h4>";
content += "<h4>Temperature sensor 7: " + String(byd_datalayer->battery_temperatures[6]) + "</h4>";
content += "<h4>Temperature sensor 8: " + String(byd_datalayer->battery_temperatures[7]) + "</h4>";
content += "<h4>Temperature sensor 9: " + String(byd_datalayer->battery_temperatures[8]) + "</h4>";
content += "<h4>Temperature sensor 10: " + String(byd_datalayer->battery_temperatures[9]) + "</h4>";
content += "<h4>SOC estimated: " + String(soc_estimated) + "&percnt;</h4>";
content += "<h4>SOC measured: " + String(soc_measured) + "&percnt;</h4>";
content += "<h4>SOC OBD2: " + String(byd_datalayer->SOC_polled) + "&percnt;</h4>";
content += "<h4>Voltage periodic: " + String(byd_datalayer->voltage_periodic) + " V</h4>";
content += "<h4>Voltage OBD2: " + String(byd_datalayer->voltage_polled) + " V</h4>";
content += "<h4>Temperature sensor 1: " + String(byd_datalayer->battery_temperatures[0]) + " &deg;C</h4>";
content += "<h4>Temperature sensor 2: " + String(byd_datalayer->battery_temperatures[1]) + " &deg;C</h4>";
content += "<h4>Temperature sensor 3: " + String(byd_datalayer->battery_temperatures[2]) + " &deg;C</h4>";
content += "<h4>Temperature sensor 4: " + String(byd_datalayer->battery_temperatures[3]) + " &deg;C</h4>";
content += "<h4>Temperature sensor 5: " + String(byd_datalayer->battery_temperatures[4]) + " &deg;C</h4>";
content += "<h4>Temperature sensor 6: " + String(byd_datalayer->battery_temperatures[5]) + " &deg;C</h4>";
content += "<h4>Temperature sensor 7: " + String(byd_datalayer->battery_temperatures[6]) + " &deg;C</h4>";
content += "<h4>Temperature sensor 8: " + String(byd_datalayer->battery_temperatures[7]) + " &deg;C</h4>";
content += "<h4>Temperature sensor 9: " + String(byd_datalayer->battery_temperatures[8]) + " &deg;C</h4>";
content += "<h4>Temperature sensor 10: " + String(byd_datalayer->battery_temperatures[9]) + " &deg;C</h4>";
content += "<h4>Max discharge power: " + String(BMS_maxDischargePower) + " kW</h4>";
content += "<h4>Max charge (regen) power: " + String(BMS_maxChargePower) + " kW</h4>";
content += "<h4>Total charged: " + String(byd_datalayer->total_charged_kwh) + " kWh</h4>";
content += "<h4>Total discharged: " + String(byd_datalayer->total_discharged_kwh) + " kWh</h4>";
content += "<h4>Total charged: " + String(byd_datalayer->total_charged_ah) + " Ah</h4>";
content += "<h4>Total discharged: " + String(byd_datalayer->total_discharged_ah) + " Ah</h4>";
content += "<h4>Charge times: " + String(byd_datalayer->charge_times) + "</h4>";
content += "<h4>Times of full power: " + String(byd_datalayer->times_full_power) + "</h4>";
content += "<h4>Unknown0: " + String(byd_datalayer->unknown0) + "</h4>";
content += "<h4>Unknown1: " + String(byd_datalayer->unknown1) + "</h4>";
content += "<h4>Charge power raw: " + String(byd_datalayer->chargePower) + "</h4>";
content += "<h4>Unknown3: " + String(byd_datalayer->unknown3) + "</h4>";
content += "<h4>Unknown4: " + String(byd_datalayer->unknown4) + "</h4>";
content += "<h4>Total charged Ah: " + String(byd_datalayer->total_charged_ah) + "</h4>";
content += "<h4>Total discharged Ah: " + String(byd_datalayer->total_discharged_ah) + "</h4>";
content += "<h4>Total charged kWh: " + String(byd_datalayer->total_charged_kwh) + "</h4>";
content += "<h4>Total discharged kWh: " + String(byd_datalayer->total_discharged_kwh) + "</h4>";
content += "<h4>Unknown9: " + String(byd_datalayer->unknown9) + "</h4>";
content += "<h4>Unknown10: " + String(byd_datalayer->unknown10) + "</h4>";
content += "<h4>Unknown11: " + String(byd_datalayer->unknown11) + "</h4>";
content += "<h4>Unknown12: " + String(byd_datalayer->unknown12) + "</h4>";

View file

@ -2,11 +2,11 @@
#define BATTERY_H
#include <vector>
#include "src/devboard/utils/types.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
enum class BatteryType {
None = 0,
BmwSbox = 1,
BmwI3 = 2,
BmwIx = 3,
BoltAmpera = 4,
@ -48,10 +48,14 @@ enum class BatteryType {
extern std::vector<BatteryType> supported_battery_types();
extern const char* name_for_battery_type(BatteryType type);
extern const char* name_for_chemistry(battery_chemistry_enum chem);
extern const char* name_for_comm_interface(comm_interface comm);
extern BatteryType user_selected_battery_type;
extern bool user_selected_second_battery;
extern battery_chemistry_enum user_selected_battery_chemistry;
// Abstract base class for next-generation battery implementations.
// Defines the interface to call battery specific functionality.
class Battery {
@ -60,7 +64,7 @@ class Battery {
virtual void update_values() = 0;
// The name of the comm interface the battery is using.
virtual String interface_name() = 0;
virtual const char* interface_name() = 0;
// These are commands from external I/O (UI, MQTT etc.)
// Override in battery if it supports them. Otherwise they are NOP.

View file

@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "../include.h"
void CellPowerBms::update_values() {
@ -220,10 +219,10 @@ void CellPowerBms::transmit_can(unsigned long currentMillis) {
previousMillis1s = currentMillis;
/*
transmit_can_frame(&CELLPOWER_18FF50E9, can_config.battery);
transmit_can_frame(&CELLPOWER_18FF50E8, can_config.battery);
transmit_can_frame(&CELLPOWER_18FF50E7, can_config.battery);
transmit_can_frame(&CELLPOWER_18FF50E5, can_config.battery);
transmit_can_frame(&CELLPOWER_18FF50E9);
transmit_can_frame(&CELLPOWER_18FF50E8);
transmit_can_frame(&CELLPOWER_18FF50E7);
transmit_can_frame(&CELLPOWER_18FF50E5);
*/
}
}

View file

@ -1,7 +1,6 @@
#ifndef CELLPOWER_BMS_H
#define CELLPOWER_BMS_H
#include <Arduino.h>
#include "../include.h"
#include "CELLPOWER-HTML.h"
#include "CanBattery.h"
@ -11,7 +10,7 @@
class CellPowerBms : public CanBattery {
public:
CellPowerBms() : CanBattery(true) {}
CellPowerBms() : CanBattery(CAN_Speed::CAN_SPEED_250KBPS) {}
virtual void setup(void);
virtual void handle_incoming_can_frame(CAN_frame rx_frame);

View file

@ -1,11 +1,8 @@
#include "CHADEMO-BATTERY.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "CHADEMO-SHUNTS.h"
#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms
//This function maps all the values fetched via CAN to the correct parameters used for the inverter
void ChademoBattery::update_values() {
@ -93,7 +90,7 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
vehicle_can_initialized = true;
vehicle_permission = digitalRead(CHADEMO_PIN_4);
vehicle_permission = digitalRead(pin4);
x102_chg_session.ControlProtocolNumberEV = rx_frame.data.u8[0];
@ -598,8 +595,8 @@ void ChademoBattery::transmit_can(unsigned long currentMillis) {
* that is the limiting factor. Therefore, we
* can generally send as is without tweaks here.
*/
transmit_can_frame(&CHADEMO_108, can_config.battery);
transmit_can_frame(&CHADEMO_109, can_config.battery);
transmit_can_frame(&CHADEMO_108);
transmit_can_frame(&CHADEMO_109);
/* TODO for dynamic control: can send x118 with byte 6 bit 0 set to 0 for 1s (before flipping back to 1) as a way of giving vehicle a chance to update 101.1 and 101.2
* within 6 seconds of x118 toggle.
@ -608,9 +605,9 @@ void ChademoBattery::transmit_can(unsigned long currentMillis) {
*/
if (EVSE_mode == CHADEMO_DISCHARGE || EVSE_mode == CHADEMO_BIDIRECTIONAL) {
transmit_can_frame(&CHADEMO_208, can_config.battery);
transmit_can_frame(&CHADEMO_208);
if (x201_received) {
transmit_can_frame(&CHADEMO_209, can_config.battery);
transmit_can_frame(&CHADEMO_209);
x209_sent = true;
}
}
@ -622,7 +619,7 @@ void ChademoBattery::transmit_can(unsigned long currentMillis) {
//FIXME REMOVE
logging.println("REMOVE: proto 2.0");
#endif
transmit_can_frame(&CHADEMO_118, can_config.battery);
transmit_can_frame(&CHADEMO_118);
}
}
}
@ -650,10 +647,11 @@ void ChademoBattery::transmit_can(unsigned long currentMillis) {
*/
void ChademoBattery::handle_chademo_sequence() {
precharge_low = digitalRead(PRECHARGE_PIN) == LOW;
positive_high = digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH;
precharge_low = digitalRead(precharge) == LOW;
positive_high = digitalRead(positive_contactor) == HIGH;
contactors_ready = precharge_low && positive_high;
vehicle_permission = digitalRead(CHADEMO_PIN_4);
vehicle_permission = digitalRead(pin4);
/* ------------------- State override conditions checks ------------------- */
/* ------------------------------------------------------------------------------ */
@ -676,8 +674,8 @@ void ChademoBattery::handle_chademo_sequence() {
switch (CHADEMO_Status) {
case CHADEMO_IDLE:
/* this is where we can unlock connector */
digitalWrite(CHADEMO_LOCK, LOW);
plug_inserted = digitalRead(CHADEMO_PIN_7);
digitalWrite(pin_lock, LOW);
plug_inserted = digitalRead(pin7);
if (!plug_inserted) {
#ifdef DEBUG_LOG
@ -704,7 +702,7 @@ void ChademoBattery::handle_chademo_sequence() {
/* If connection is detectable, jumpstart handshake by
* indicate that the EVSE is ready to begin
*/
digitalWrite(CHADEMO_PIN_2, HIGH);
digitalWrite(pin2, HIGH);
/* State change to initializing. We will re-enter the handler upon receipt of CAN */
CHADEMO_Status = CHADEMO_INIT;
@ -715,9 +713,7 @@ void ChademoBattery::handle_chademo_sequence() {
* with timers to have higher confidence of certain conditions hitting
* a steady state
*/
#ifdef DEBUG_LOG
logging.println("CHADEMO plug is not inserted, cannot connect d2 relay to begin initialization.");
#endif
DEBUG_PRINTLN("CHADEMO plug is not inserted, cannot connect d2 relay to begin initialization.");
CHADEMO_Status = CHADEMO_IDLE;
}
break;
@ -726,9 +722,7 @@ void ChademoBattery::handle_chademo_sequence() {
* Used for triggers/error handling elsewhere;
* State change to CHADEMO_NEGOTIATE occurs in handle_incoming_can_frame_battery(..)
*/
#ifdef DEBUG_LOG
// logging.println("Awaiting initial vehicle CAN to trigger negotiation");
#endif
DEBUG_PRINTLN("Awaiting initial vehicle CAN to trigger negotiation");
evse_init();
break;
case CHADEMO_NEGOTIATE:
@ -750,7 +744,7 @@ void ChademoBattery::handle_chademo_sequence() {
// that pin 4 (j) reads high
if (vehicle_permission) {
//lock connector here
digitalWrite(CHADEMO_LOCK, HIGH);
digitalWrite(pin_lock, HIGH);
//TODO spec requires test to validate solenoid has indeed engaged.
// example uses a comparator/current consumption check around solenoid
@ -780,7 +774,7 @@ void ChademoBattery::handle_chademo_sequence() {
if (x102_chg_session.s.status.StatusVehicleChargingEnabled) {
if (get_measured_voltage() < 20) {
digitalWrite(CHADEMO_PIN_10, HIGH);
digitalWrite(pin10, HIGH);
evse_permission = true;
} else {
logging.println("Insulation check measures > 20v ");
@ -898,8 +892,8 @@ void ChademoBattery::handle_chademo_sequence() {
*/
if (get_measured_current() <= 5 && get_measured_voltage() <= 10) {
/* welding detection ideally here */
digitalWrite(CHADEMO_PIN_10, LOW);
digitalWrite(CHADEMO_PIN_2, LOW);
digitalWrite(pin10, LOW);
digitalWrite(pin2, LOW);
CHADEMO_Status = CHADEMO_IDLE;
}
@ -916,8 +910,8 @@ void ChademoBattery::handle_chademo_sequence() {
#ifdef DEBUG_LOG
logging.println("CHADEMO fault encountered, tearing down to make safe");
#endif
digitalWrite(CHADEMO_PIN_10, LOW);
digitalWrite(CHADEMO_PIN_2, LOW);
digitalWrite(pin10, LOW);
digitalWrite(pin2, LOW);
evse_permission = false;
vehicle_permission = false;
x209_sent = false;
@ -937,14 +931,18 @@ void ChademoBattery::handle_chademo_sequence() {
void ChademoBattery::setup(void) { // Performs one time setup at startup
pinMode(CHADEMO_PIN_2, OUTPUT);
digitalWrite(CHADEMO_PIN_2, LOW);
pinMode(CHADEMO_PIN_10, OUTPUT);
digitalWrite(CHADEMO_PIN_10, LOW);
pinMode(CHADEMO_LOCK, OUTPUT);
digitalWrite(CHADEMO_LOCK, LOW);
pinMode(CHADEMO_PIN_4, INPUT);
pinMode(CHADEMO_PIN_7, INPUT);
if (!esp32hal->alloc_pins(Name, pin2, pin10, pin4, pin7, pin_lock)) {
return;
}
pinMode(pin2, OUTPUT);
digitalWrite(pin2, LOW);
pinMode(pin10, OUTPUT);
digitalWrite(pin10, LOW);
pinMode(pin_lock, OUTPUT);
digitalWrite(pin_lock, LOW);
pinMode(pin4, INPUT);
pinMode(pin7, INPUT);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
@ -995,5 +993,3 @@ void ChademoBattery::setup(void) { // Performs one time setup at startup
setupMillis = millis();
}
#endif

View file

@ -3,9 +3,9 @@
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../include.h"
#include "CHADEMO-BATTERY-HTML.h"
#include "CanBattery.h"
#include "src/devboard/hal/hal.h"
#ifdef CHADEMO_BATTERY
#define SELECTED_BATTERY_CLASS ChademoBattery
@ -16,6 +16,18 @@
class ChademoBattery : public CanBattery {
public:
ChademoBattery() {
pin2 = esp32hal->CHADEMO_PIN_2();
pin10 = esp32hal->CHADEMO_PIN_10();
pin4 = esp32hal->CHADEMO_PIN_4();
pin7 = esp32hal->CHADEMO_PIN_7();
pin_lock = esp32hal->CHADEMO_LOCK();
// Assuming these are initialized by contactor control module.
precharge = esp32hal->PRECHARGE_PIN();
positive_contactor = esp32hal->POSITIVE_CONTACTOR_PIN();
}
virtual void setup(void);
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
@ -31,6 +43,7 @@ class ChademoBattery : public CanBattery {
static constexpr const char* Name = "Chademo V2X mode";
private:
gpio_num_t pin2, pin10, pin4, pin7, pin_lock, precharge, positive_contactor;
ChademoBatteryHtmlRenderer renderer;
void process_vehicle_charging_minimums(CAN_frame rx_frame);

View file

@ -21,8 +21,8 @@
#include "CHADEMO-SHUNTS.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "CHADEMO-BATTERY.h"
#include "src/devboard/utils/logging.h"
/* Initial frames received from ISA shunts provide invalid during initialization */
static int framecount = 0;
@ -236,6 +236,10 @@ inline void ISA_handle528(CAN_frame* frame) {
lastWh = wh;
}
static void transmit_can_frame(CAN_frame* frame, CAN_Interface can_interface) {
transmit_can_frame_to_interface(frame, can_interface);
}
void ISA_initialize() {
firstframe = false;
ISA_STOP();

View file

@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "../include.h"
/* 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% */
@ -128,7 +127,7 @@ void CmfaEvBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
break;
case 0x7BB: // Reply from battery
if (rx_frame.data.u8[0] == 0x10) { //PID header
transmit_can_frame(&CMFA_ACK, can_config.battery);
transmit_can_frame(&CMFA_ACK);
}
pid_reply = (rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3];
@ -440,10 +439,10 @@ void CmfaEvBattery::transmit_can(unsigned long currentMillis) {
// Send 10ms CAN Message
if (currentMillis - previousMillis10ms >= INTERVAL_10_MS) {
previousMillis10ms = currentMillis;
transmit_can_frame(&CMFA_1EA, can_config.battery);
transmit_can_frame(&CMFA_135, can_config.battery);
transmit_can_frame(&CMFA_134, can_config.battery);
transmit_can_frame(&CMFA_125, can_config.battery);
transmit_can_frame(&CMFA_1EA);
transmit_can_frame(&CMFA_135);
transmit_can_frame(&CMFA_134);
transmit_can_frame(&CMFA_125);
CMFA_135.data.u8[1] = content_135[counter_10ms];
CMFA_125.data.u8[3] = content_125[counter_10ms];
@ -453,8 +452,8 @@ void CmfaEvBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis100ms >= INTERVAL_100_MS) {
previousMillis100ms = currentMillis;
transmit_can_frame(&CMFA_59B, can_config.battery);
transmit_can_frame(&CMFA_3D3, can_config.battery);
transmit_can_frame(&CMFA_59B);
transmit_can_frame(&CMFA_3D3);
}
//Send 200ms message
if (currentMillis - previousMillis200ms >= INTERVAL_200_MS) {
@ -935,7 +934,7 @@ void CmfaEvBattery::transmit_can(unsigned long currentMillis) {
break;
}
transmit_can_frame(&CMFA_POLLING_FRAME, can_config.battery);
transmit_can_frame(&CMFA_POLLING_FRAME);
}
}

View file

@ -1,6 +1,5 @@
#ifndef CMFA_EV_BATTERY_H
#define CMFA_EV_BATTERY_H
#include "../include.h"
#include "CMFA-EV-HTML.h"
#include "CanBattery.h"

View file

@ -1,8 +1,11 @@
#include "CanBattery.h"
#include "../../src/include.h"
CanBattery::CanBattery(bool halfSpeed) {
CanBattery::CanBattery(CAN_Speed speed) {
can_interface = can_config.battery;
register_transmitter(this);
register_can_receiver(this, can_interface, halfSpeed);
register_can_receiver(this, can_interface, speed);
}
CAN_Speed CanBattery::change_can_speed(CAN_Speed speed) {
return ::change_can_speed(can_interface, speed);
}

View file

@ -3,8 +3,10 @@
#include "Battery.h"
#include "USER_SETTINGS.h"
#include "src/communication/Transmitter.h"
#include "src/communication/can/CanReceiver.h"
#include "src/communication/can/comm_can.h"
#include "src/devboard/utils/types.h"
// Abstract base class for batteries using the CAN bus
@ -13,7 +15,7 @@ class CanBattery : public Battery, Transmitter, CanReceiver {
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
virtual void transmit_can(unsigned long currentMillis) = 0;
String interface_name() { return getCANInterfaceName(can_interface); }
const char* interface_name() { return getCANInterfaceName(can_interface); }
void transmit(unsigned long currentMillis) { transmit_can(currentMillis); }
@ -22,13 +24,17 @@ class CanBattery : public Battery, Transmitter, CanReceiver {
protected:
CAN_Interface can_interface;
CanBattery(bool halfSpeed = false);
CanBattery(CAN_Speed speed = CAN_Speed::CAN_SPEED_500KBPS);
CanBattery(CAN_Interface interface, bool halfSpeed = false) {
CanBattery(CAN_Interface interface, CAN_Speed speed = CAN_Speed::CAN_SPEED_500KBPS) {
can_interface = interface;
register_transmitter(this);
register_can_receiver(this, can_interface, halfSpeed);
register_can_receiver(this, can_interface, speed);
}
CAN_Speed change_can_speed(CAN_Speed speed);
void transmit_can_frame(CAN_frame* frame) { transmit_can_frame_to_interface(frame, can_interface); }
};
#endif

View file

@ -1,8 +1,8 @@
#include "DALY-BMS.h"
#include <cstdint>
#include "../datalayer/datalayer.h"
#include "../devboard/hal/hal.h"
#include "../devboard/utils/events.h"
#include "../include.h"
/* Do not change code below unless you are sure what you are doing */
@ -70,7 +70,14 @@ void DalyBms::setup(void) { // Performs one time setup at startup
datalayer.battery.info.total_capacity_Wh = BATTERY_WH_MAX;
datalayer.system.status.battery_allows_contactor_closing = true;
Serial2.begin(baud_rate(), SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
auto rx_pin = esp32hal->RS485_RX_PIN();
auto tx_pin = esp32hal->RS485_TX_PIN();
if (!esp32hal->alloc_pins(Name, rx_pin, tx_pin)) {
return;
}
Serial2.begin(baud_rate(), SERIAL_8N1, rx_pin, tx_pin);
}
uint8_t calculate_checksum(uint8_t buff[12]) {

View file

@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For More Battery Info page
#include "../devboard/utils/events.h"
#include "../include.h"
/* TODO:
This integration is still ongoing. Here is what still needs to be done in order to use this battery type
@ -466,7 +465,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
} else { //Normal PID polling ongoing
if (rx_frame.data.u8[0] == 0x10) { //Multiframe response, send ACK
transmit_can_frame(&ECMP_ACK, can_config.battery);
transmit_can_frame(&ECMP_ACK);
//Multiframe has the poll reply slightly different location
incoming_poll = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
}
@ -852,19 +851,19 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
//Failure to do this results in the contactors opening after 30 seconds with load
if (datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring) {
if (DisableIsoMonitoringStatemachine == 0) {
transmit_can_frame(&ECMP_DIAG_START, can_config.battery);
transmit_can_frame(&ECMP_DIAG_START);
DisableIsoMonitoringStatemachine = 1;
}
if (DisableIsoMonitoringStatemachine == 2) {
transmit_can_frame(&ECMP_ACK_MESSAGE, can_config.battery);
transmit_can_frame(&ECMP_ACK_MESSAGE);
DisableIsoMonitoringStatemachine = 3;
}
if (DisableIsoMonitoringStatemachine == 4) {
transmit_can_frame(&ECMP_FACTORY_MODE_ACTIVATION, can_config.battery);
transmit_can_frame(&ECMP_FACTORY_MODE_ACTIVATION);
DisableIsoMonitoringStatemachine = 5;
}
if (DisableIsoMonitoringStatemachine == 6) {
transmit_can_frame(&ECMP_DISABLE_ISOLATION_REQ, can_config.battery);
transmit_can_frame(&ECMP_DISABLE_ISOLATION_REQ);
DisableIsoMonitoringStatemachine = 7;
}
timeSpentDisableIsoMonitoring++;
@ -875,15 +874,15 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
}
} else if (datalayer_extended.stellantisECMP.UserRequestContactorReset) {
if (ContactorResetStatemachine == 0) {
transmit_can_frame(&ECMP_DIAG_START, can_config.battery);
transmit_can_frame(&ECMP_DIAG_START);
ContactorResetStatemachine = 1;
}
if (ContactorResetStatemachine == 2) {
transmit_can_frame(&ECMP_CONTACTOR_RESET_START, can_config.battery);
transmit_can_frame(&ECMP_CONTACTOR_RESET_START);
ContactorResetStatemachine = 3;
}
if (ContactorResetStatemachine == 4) {
transmit_can_frame(&ECMP_CONTACTOR_RESET_PROGRESS, can_config.battery);
transmit_can_frame(&ECMP_CONTACTOR_RESET_PROGRESS);
ContactorResetStatemachine = 5;
}
@ -897,15 +896,15 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
} else if (datalayer_extended.stellantisECMP.UserRequestCollisionReset) {
if (CollisionResetStatemachine == 0) {
transmit_can_frame(&ECMP_DIAG_START, can_config.battery);
transmit_can_frame(&ECMP_DIAG_START);
CollisionResetStatemachine = 1;
}
if (CollisionResetStatemachine == 2) {
transmit_can_frame(&ECMP_COLLISION_RESET_START, can_config.battery);
transmit_can_frame(&ECMP_COLLISION_RESET_START);
CollisionResetStatemachine = 3;
}
if (CollisionResetStatemachine == 4) {
transmit_can_frame(&ECMP_COLLISION_RESET_PROGRESS, can_config.battery);
transmit_can_frame(&ECMP_COLLISION_RESET_PROGRESS);
CollisionResetStatemachine = 5;
}
@ -919,15 +918,15 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
} else if (datalayer_extended.stellantisECMP.UserRequestIsolationReset) {
if (IsolationResetStatemachine == 0) {
transmit_can_frame(&ECMP_DIAG_START, can_config.battery);
transmit_can_frame(&ECMP_DIAG_START);
IsolationResetStatemachine = 1;
}
if (IsolationResetStatemachine == 2) {
transmit_can_frame(&ECMP_ISOLATION_RESET_START, can_config.battery);
transmit_can_frame(&ECMP_ISOLATION_RESET_START);
IsolationResetStatemachine = 3;
}
if (IsolationResetStatemachine == 4) {
transmit_can_frame(&ECMP_ISOLATION_RESET_PROGRESS, can_config.battery);
transmit_can_frame(&ECMP_ISOLATION_RESET_PROGRESS);
IsolationResetStatemachine = 5;
}
@ -1299,7 +1298,7 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
poll_state = PID_WELD_CHECK;
break;
}
transmit_can_frame(&ECMP_POLL, can_config.battery);
transmit_can_frame(&ECMP_POLL);
}
}
}
@ -1328,14 +1327,14 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
ECMP_17B.data.u8[7] = counter_10ms << 4 | checksum_calc(counter_10ms, ECMP_17B);
ECMP_112.data.u8[7] = counter_10ms << 4 | checksum_calc(counter_10ms, ECMP_112);
transmit_can_frame(&ECMP_112, can_config.battery); //MCU1_112
transmit_can_frame(&ECMP_0C5, can_config.battery); //DC2_0C5
transmit_can_frame(&ECMP_17B, can_config.battery); //VCU_PCANInfo_17B
transmit_can_frame(&ECMP_0F2, can_config.battery); //CtrlMCU1_0F2
transmit_can_frame(&ECMP_112); //MCU1_112
transmit_can_frame(&ECMP_0C5); //DC2_0C5
transmit_can_frame(&ECMP_17B); //VCU_PCANInfo_17B
transmit_can_frame(&ECMP_0F2); //CtrlMCU1_0F2
if (simulateEntireCar) {
transmit_can_frame(&ECMP_111, can_config.battery);
transmit_can_frame(&ECMP_110, can_config.battery);
transmit_can_frame(&ECMP_114, can_config.battery);
transmit_can_frame(&ECMP_111);
transmit_can_frame(&ECMP_110);
transmit_can_frame(&ECMP_114);
}
}
@ -1354,7 +1353,7 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
ECMP_0F0.data.u8[7] = counter_20ms << 4 | checksum_calc(counter_20ms, ECMP_0F0);
transmit_can_frame(&ECMP_0F0, can_config.battery); //VCU2_0F0
transmit_can_frame(&ECMP_0F0); //VCU2_0F0
}
// Send 50ms periodic CAN Message simulating the car still being attached
if (currentMillis - previousMillis50 >= INTERVAL_50_MS) {
@ -1367,8 +1366,8 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
//Normal operation for contactor closing
ECMP_27A.data = {0x4F, 0x58, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00};
}
transmit_can_frame(&ECMP_230, can_config.battery); //OBC3_230
transmit_can_frame(&ECMP_27A, can_config.battery); //VCU_BSI_Wakeup_27A
transmit_can_frame(&ECMP_230); //OBC3_230
transmit_can_frame(&ECMP_27A); //VCU_BSI_Wakeup_27A
}
// Send 100ms periodic CAN Message simulating the car still being attached
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
@ -1418,7 +1417,7 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
data_3A2_CRC[13] = 0xDF;
data_3A2_CRC[14] = 0xEE;
data_3A2_CRC[15] = 0xFD;
transmit_can_frame(&ECMP_3D0, can_config.battery); //Not in logs, but makes speed go to 0km/h
transmit_can_frame(&ECMP_3D0); //Not in logs, but makes speed go to 0km/h
} else {
//Normal operation for contactor closing
ECMP_31E.data.u8[0] = 0x50;
@ -1472,26 +1471,26 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
ECMP_31D.data.u8[7] = counter_100ms << 4 | checksum_calc(counter_100ms, ECMP_31D);
ECMP_3D0.data.u8[7] = counter_100ms << 4 | checksum_calc(counter_100ms, ECMP_3D0);
transmit_can_frame(&ECMP_382, can_config.battery); //PSA Specific VCU (BSIInfo_382)
transmit_can_frame(&ECMP_345, can_config.battery); //DC1_345
transmit_can_frame(&ECMP_3A2, can_config.battery); //OBC2_3A2
transmit_can_frame(&ECMP_3A3, can_config.battery); //OBC1_3A3
transmit_can_frame(&ECMP_010, can_config.battery); //VCU_BCM_Crash
transmit_can_frame(&ECMP_382); //PSA Specific VCU (BSIInfo_382)
transmit_can_frame(&ECMP_345); //DC1_345
transmit_can_frame(&ECMP_3A2); //OBC2_3A2
transmit_can_frame(&ECMP_3A3); //OBC1_3A3
transmit_can_frame(&ECMP_010); //VCU_BCM_Crash
if (simulateEntireCar) {
transmit_can_frame(&ECMP_31E, can_config.battery);
transmit_can_frame(&ECMP_383, can_config.battery);
transmit_can_frame(&ECMP_0A6, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_37F, can_config.battery); //Seems to be temperatures of some sort
transmit_can_frame(&ECMP_372, can_config.battery);
transmit_can_frame(&ECMP_351, can_config.battery);
transmit_can_frame(&ECMP_31D, can_config.battery);
transmit_can_frame(&ECMP_31E);
transmit_can_frame(&ECMP_383);
transmit_can_frame(&ECMP_0A6); //Not in all logs
transmit_can_frame(&ECMP_37F); //Seems to be temperatures of some sort
transmit_can_frame(&ECMP_372);
transmit_can_frame(&ECMP_351);
transmit_can_frame(&ECMP_31D);
}
}
// Send 500ms periodic CAN Message simulating the car still being attached
if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
previousMillis500 = currentMillis;
if (simulateEntireCar) {
transmit_can_frame(&ECMP_0AE, can_config.battery);
transmit_can_frame(&ECMP_0AE);
}
}
// Send 1s CAN Message
@ -1515,21 +1514,21 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
ECMP_552.data.u8[2] = ((ticks_552 & 0x0000FF00) >> 8);
ECMP_552.data.u8[3] = (ticks_552 & 0x000000FF);
transmit_can_frame(&ECMP_439, can_config.battery); //OBC4
transmit_can_frame(&ECMP_552, can_config.battery); //VCU_552 timetracking
transmit_can_frame(&ECMP_439); //OBC4
transmit_can_frame(&ECMP_552); //VCU_552 timetracking
if (simulateEntireCar) {
transmit_can_frame(&ECMP_486, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_041, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_786, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_591, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_794, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_486); //Not in all logs
transmit_can_frame(&ECMP_041); //Not in all logs
transmit_can_frame(&ECMP_786); //Not in all logs
transmit_can_frame(&ECMP_591); //Not in all logs
transmit_can_frame(&ECMP_794); //Not in all logs
}
}
// Send 5s periodic CAN Message simulating the car still being attached
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
previousMillis5000 = currentMillis;
if (simulateEntireCar) {
transmit_can_frame(&ECMP_55F, can_config.battery);
transmit_can_frame(&ECMP_55F);
}
}
}

View file

@ -1,7 +1,6 @@
#ifndef STELLANTIS_ECMP_BATTERY_H
#define STELLANTIS_ECMP_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
#include "ECMP-HTML.h"

View file

@ -2,7 +2,6 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
/*
Can bus @ 500k - all Extended ID, little endian
@ -417,7 +416,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_send_pack_statistics
transmit_can_frame(&FOX_1871); //bms_send_pack_statistics
break;
case 1: //1s
FOX_1871.data.u8[0] = 0x02;
@ -428,7 +427,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_stop_sending
transmit_can_frame(&FOX_1871); //bms_stop_sending
break;
case 2: //1.5s
FOX_1871.data.u8[0] = 0x05;
@ -439,7 +438,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_serial_request
transmit_can_frame(&FOX_1871); //bms_serial_request
break;
case 3: //2.0s
FOX_1871.data.u8[0] = 0x01;
@ -450,7 +449,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_send_pack_statistics
transmit_can_frame(&FOX_1871); //bms_send_pack_statistics
break;
case 4: //2.5s
FOX_1871.data.u8[0] = 0x02;
@ -461,7 +460,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_stop_sending
transmit_can_frame(&FOX_1871); //bms_stop_sending
break;
case 5: //3.0s cell temp and voltages
//0x1871 [0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00]
@ -473,7 +472,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_send_pack_cell_volts
transmit_can_frame(&FOX_1871); //bms_send_pack_cell_volts
//0x1871 [0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00]
FOX_1871.data.u8[0] = 0x01;
FOX_1871.data.u8[1] = 0x00;
@ -483,7 +482,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_send_pack_cell_temps
transmit_can_frame(&FOX_1871); //bms_send_pack_cell_temps
break;
case 6: //3.5s
FOX_1871.data.u8[0] = 0x01;
@ -494,7 +493,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_send_pack_statistics
transmit_can_frame(&FOX_1871); //bms_send_pack_statistics
break;
case 7: //4.0s
FOX_1871.data.u8[0] = 0x02;
@ -505,7 +504,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_stop_sending
transmit_can_frame(&FOX_1871); //bms_stop_sending
break;
case 8: //4.5s
FOX_1871.data.u8[0] = 0x01;
@ -516,7 +515,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_send_pack_statistics
transmit_can_frame(&FOX_1871); //bms_send_pack_statistics
break;
case 9: //5.0s
FOX_1871.data.u8[0] = 0x02;
@ -527,7 +526,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_stop_sending
transmit_can_frame(&FOX_1871); //bms_stop_sending
break;
case 10: //5.5s
//0x1871 [0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00]
@ -539,7 +538,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_send_pack_cell_volts
transmit_can_frame(&FOX_1871); //bms_send_pack_cell_volts
//0x1871 [0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00]
FOX_1871.data.u8[0] = 0x01;
FOX_1871.data.u8[1] = 0x00;
@ -549,7 +548,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x00;
FOX_1871.data.u8[6] = 0x00;
FOX_1871.data.u8[7] = 0x00;
transmit_can_frame(&FOX_1871, can_config.battery); //bms_send_pack_cell_temps
transmit_can_frame(&FOX_1871); //bms_send_pack_cell_temps
break;
case 11: //6.0s 0x1871 [0x03, 0x06, 0x17, 0x05, 0x09, 0x09, 0x28, 0x22]
FOX_1871.data.u8[0] = 0x03;
@ -560,7 +559,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
FOX_1871.data.u8[5] = 0x09;
FOX_1871.data.u8[6] = 0x28;
FOX_1871.data.u8[7] = 0x22;
transmit_can_frame(&FOX_1871, can_config.battery); //timestamp
transmit_can_frame(&FOX_1871); //timestamp
break;
default:
statemachine_polling = 0;

View file

@ -1,7 +1,6 @@
#ifndef FOXESS_BATTERY_H
#define FOXESS_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"

View file

@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "../include.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
@ -309,7 +308,7 @@ void GeelyGeometryCBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
break;
case 0x7EA:
if (rx_frame.data.u8[0] == 0x10) { //Multiframe response, send ACK
transmit_can_frame(&GEELY_ACK, can_config.battery);
transmit_can_frame(&GEELY_ACK);
//Multiframe has the poll reply slightly different location
incoming_poll = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
}
@ -488,15 +487,15 @@ void GeelyGeometryCBattery::transmit_can(unsigned long currentMillis) {
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);
transmit_can_frame(&GEELY_191);
transmit_can_frame(&GEELY_0A6);
transmit_can_frame(&GEELY_160);
transmit_can_frame(&GEELY_165);
transmit_can_frame(&GEELY_1A4);
transmit_can_frame(&GEELY_162); //CONFIRMED MANDATORY! VCU message
transmit_can_frame(&GEELY_1A5);
transmit_can_frame(&GEELY_220); //CONFIRMED MANDATORY! OBC message
transmit_can_frame(&GEELY_0E0);
}
if (currentMillis - previousMillis20 >= INTERVAL_20_MS) {
previousMillis20 = currentMillis;
@ -508,11 +507,11 @@ void GeelyGeometryCBattery::transmit_can(unsigned long currentMillis) {
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);
transmit_can_frame(&GEELY_145); //CONFIRMED MANDATORY! shifter
transmit_can_frame(&GEELY_0F9); //CONFIRMED MANDATORY! shifter
transmit_can_frame(&GEELY_0FA); //Might be unnecessary, not in workshop manual
transmit_can_frame(&GEELY_197); //Might be unnecessary, not in workshop manual
transmit_can_frame(&GEELY_150);
}
if (currentMillis - previousMillis50 >= INTERVAL_50_MS) {
previousMillis50 = currentMillis;
@ -524,13 +523,13 @@ void GeelyGeometryCBattery::transmit_can(unsigned long currentMillis) {
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
transmit_can_frame(&GEELY_1B2);
transmit_can_frame(&GEELY_221); //CONFIRMED MANDATORY! OBC message
//transmit_can_frame(&GEELY_1A3); //Might be unnecessary, radar info
transmit_can_frame(&GEELY_1A7); //Might be unnecessary
transmit_can_frame(&GEELY_0A8); //CONFIRMED MANDATORY! IPU message
transmit_can_frame(&GEELY_1F2); //Might be unnecessary, not in manual
transmit_can_frame(&GEELY_1A6); //Might be unnecessary, not in manual
}
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
@ -541,9 +540,9 @@ void GeelyGeometryCBattery::transmit_can(unsigned long currentMillis) {
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
transmit_can_frame(&GEELY_222); //CONFIRMED MANDATORY! OBC message
//transmit_can_frame(&GEELY_2D2); //Might be unnecessary, seat info
transmit_can_frame(&GEELY_292); //CONFIRMED MANDATORY! T-BOX
}
// Send 200ms CAN Message
if (currentMillis - previousMillis200 >= INTERVAL_200_MS) {
@ -655,7 +654,7 @@ void GeelyGeometryCBattery::transmit_can(unsigned long currentMillis) {
break;
}
transmit_can_frame(&GEELY_POLL, can_config.battery);
transmit_can_frame(&GEELY_POLL);
}
}

View file

@ -2,7 +2,6 @@
#define GEELY_GEOMETRY_C_BATTERY_H
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../include.h"
#include "CanBattery.h"
#include "GEELY-GEOMETRY-C-HTML.h"

View file

@ -2,7 +2,7 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../devboard/utils/logging.h"
//Code still work in progress, TODO:
//Figure out if CAN messages need to be sent to keep the system happy?

View file

@ -1,7 +1,6 @@
#ifndef IMIEV_CZERO_ION_BATTERY_H
#define IMIEV_CZERO_ION_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"

View file

@ -2,7 +2,7 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../devboard/utils/logging.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillisKeepAlive = 0;
@ -56,7 +56,7 @@ CAN_frame ipace_keep_alive = {.FD = false,
.ID = 0x59e,
.data = {0x9E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};*/
static void print_units(char* header, int value, char* units) {
static void print_units(const char* header, int value, const char* units) {
logging.print(header);
logging.print(value);
logging.print(units);
@ -221,12 +221,12 @@ void JaguarIpaceBattery::transmit_can(unsigned long currentMillis) {
/* Send keep-alive every 200ms */
if (currentMillis - previousMillisKeepAlive >= INTERVAL_200_MS) {
previousMillisKeepAlive = currentMillis;
transmit_can_frame(&ipace_keep_alive, can_config.battery);
transmit_can_frame(&ipace_keep_alive);
}
}
void JaguarIpaceBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Jaguar I-PACE", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 108;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;

View file

@ -2,8 +2,8 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "../devboard/utils/logging.h"
#include "../system_settings.h"
const unsigned char crc8_table[256] =
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
@ -811,6 +811,29 @@ void KiaEGmpBattery::
#endif
}
// Getter implementations for HTML renderer
int KiaEGmpBattery::get_battery_12V() const {
return leadAcidBatteryVoltage;
}
int KiaEGmpBattery::get_waterleakageSensor() const {
return waterleakageSensor;
}
int KiaEGmpBattery::get_temperature_water_inlet() const {
return temperature_water_inlet;
}
int KiaEGmpBattery::get_powerRelayTemperature() const {
return powerRelayTemperature;
}
int KiaEGmpBattery::get_batteryManagementMode() const {
return batteryManagementMode;
}
int KiaEGmpBattery::get_BMS_ign() const {
return BMS_ign;
}
int KiaEGmpBattery::get_batRelay() const {
return batteryRelay;
}
void KiaEGmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
startedUp = true;
switch (rx_frame.ID) {
@ -872,7 +895,7 @@ void KiaEGmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
// logging.println ("Send ack");
poll_data_pid = rx_frame.data.u8[4];
// if (rx_frame.data.u8[4] == poll_data_pid) {
transmit_can_frame(&EGMP_7E4_ack, can_config.battery); //Send ack to BMS if the same frame is sent as polled
transmit_can_frame(&EGMP_7E4_ack); //Send ack to BMS if the same frame is sent as polled
// }
break;
case 0x21: //First frame in PID group
@ -1053,7 +1076,7 @@ void KiaEGmpBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis - startMillis >= messageDelays[messageIndex]) {
// Transmit the current message
transmit_can_frame(messages[messageIndex], can_config.battery);
transmit_can_frame(messages[messageIndex]);
// Move to the next message
messageIndex++;
@ -1072,7 +1095,7 @@ void KiaEGmpBattery::transmit_can(unsigned long currentMillis) {
EGMP_7E4.data.u8[3] = KIA_7E4_COUNTER;
if (ok_start_polling_battery) {
transmit_can_frame(&EGMP_7E4, can_config.battery);
transmit_can_frame(&EGMP_7E4);
}
KIA_7E4_COUNTER++;

View file

@ -1,11 +1,8 @@
#ifndef KIA_E_GMP_BATTERY_H
#define KIA_E_GMP_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "CanBattery.h"
extern ACAN2517FD canfd;
#include "KIA-E-GMP-HTML.h"
#define ESTIMATE_SOC_FROM_CELLVOLTAGE
@ -15,13 +12,24 @@ extern ACAN2517FD canfd;
class KiaEGmpBattery : public CanBattery {
public:
KiaEGmpBattery() : renderer(*this) {}
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);
static constexpr const char* Name = "Kia/Hyundai EGMP platform";
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
// Getter implementations for HTML renderer
int get_battery_12V() const;
int get_waterleakageSensor() const;
int get_temperature_water_inlet() const;
int get_powerRelayTemperature() const;
int get_batteryManagementMode() const;
int get_BMS_ign() const;
int get_batRelay() const;
private:
KiaEGMPHtmlRenderer renderer;
uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps);
void set_voltage_minmax_limits();
@ -44,9 +52,9 @@ class KiaEGmpBattery : public CanBattery {
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_calculated = 500;
uint16_t SOC_BMS = 500;
uint16_t SOC_Display = 500;
uint16_t SOC_estimated_lowest = 0;
uint16_t SOC_estimated_highest = 0;
uint16_t batterySOH = 1000;
@ -55,24 +63,24 @@ class KiaEGmpBattery : public CanBattery {
uint16_t batteryVoltage = 6700;
int16_t leadAcidBatteryVoltage = 120;
int16_t batteryAmps = 0;
int16_t temperatureMax = 0;
int16_t temperatureMin = 0;
int16_t temperatureMax = 20;
int16_t temperatureMin = 20;
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 BMS_ign = 0xff;
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;
int8_t temperature_water_inlet = 20;
int8_t powerRelayTemperature = 10;
int8_t heatertemp = 20;
bool set_voltage_limits = false;
uint8_t ticks_200ms_counter = 0;
uint8_t EGMP_1CF_counter = 0;

View file

@ -0,0 +1,15 @@
#include "KIA-E-GMP-HTML.h"
#include "KIA-E-GMP-BATTERY.h"
String KiaEGMPHtmlRenderer::get_status_html() {
String content;
content += "<h4>Cells: " + String(datalayer.battery.info.number_of_cells) + "S</h4>";
content += "<h4>12V voltage: " + String(batt.get_battery_12V() / 10.0, 1) + "</h4>";
content += "<h4>Waterleakage: " + String(batt.get_waterleakageSensor()) + "</h4>";
content += "<h4>Temperature, water inlet: " + String(batt.get_temperature_water_inlet()) + "</h4>";
content += "<h4>Temperature, power relay: " + String(batt.get_powerRelayTemperature() * 2) + "</h4>";
content += "<h4>Batterymanagement mode: " + String(batt.get_batteryManagementMode()) + "</h4>";
content += "<h4>BMS ignition: " + String(batt.get_BMS_ign()) + "</h4>";
content += "<h4>Battery relay: " + String(batt.get_batRelay()) + "</h4>";
return content;
}

View file

@ -0,0 +1,18 @@
#ifndef _KIA_E_GMP_HTML_H
#define _KIA_E_GMP_HTML_H
#include "../datalayer/datalayer.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
class KiaEGmpBattery;
class KiaEGMPHtmlRenderer : public BatteryHtmlRenderer {
private:
KiaEGmpBattery& batt;
public:
KiaEGMPHtmlRenderer(KiaEGmpBattery& b) : batt(b) {}
String get_status_html();
};
#endif

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../devboard/utils/logging.h"
void KiaHyundai64Battery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
@ -178,17 +178,17 @@ void KiaHyundai64Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
}
poll_data_pid++;
if (poll_data_pid == 1) {
transmit_can_frame(&KIA64_7E4_id1, can_interface);
transmit_can_frame(&KIA64_7E4_id1);
} else if (poll_data_pid == 2) {
transmit_can_frame(&KIA64_7E4_id2, can_interface);
transmit_can_frame(&KIA64_7E4_id2);
} else if (poll_data_pid == 3) {
transmit_can_frame(&KIA64_7E4_id3, can_interface);
transmit_can_frame(&KIA64_7E4_id3);
} else if (poll_data_pid == 4) {
transmit_can_frame(&KIA64_7E4_id4, can_interface);
transmit_can_frame(&KIA64_7E4_id4);
} else if (poll_data_pid == 5) {
transmit_can_frame(&KIA64_7E4_id5, can_interface);
transmit_can_frame(&KIA64_7E4_id5);
} else if (poll_data_pid == 6) {
transmit_can_frame(&KIA64_7E4_id6, can_interface);
transmit_can_frame(&KIA64_7E4_id6);
}
}
break;
@ -196,8 +196,7 @@ void KiaHyundai64Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.data.u8[0]) {
case 0x10: //"PID Header"
if (rx_frame.data.u8[4] == poll_data_pid) {
transmit_can_frame(&KIA64_7E4_ack,
can_interface); //Send ack to BMS if the same frame is sent as polled
transmit_can_frame(&KIA64_7E4_ack); //Send ack to BMS if the same frame is sent as polled
}
break;
case 0x21: //First frame in PID group
@ -403,9 +402,9 @@ void KiaHyundai64Battery::transmit_can(unsigned long currentMillis) {
previousMillis100 = currentMillis;
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);
transmit_can_frame(&KIA64_553);
transmit_can_frame(&KIA64_57F);
transmit_can_frame(&KIA64_2A1);
}
}
@ -455,9 +454,9 @@ void KiaHyundai64Battery::transmit_can(unsigned long currentMillis) {
break;
}
transmit_can_frame(&KIA_HYUNDAI_200, can_interface);
transmit_can_frame(&KIA_HYUNDAI_523, can_interface);
transmit_can_frame(&KIA_HYUNDAI_524, can_interface);
transmit_can_frame(&KIA_HYUNDAI_200);
transmit_can_frame(&KIA_HYUNDAI_523);
transmit_can_frame(&KIA_HYUNDAI_524);
}
}
}

View file

@ -3,7 +3,6 @@
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../include.h"
#include "CanBattery.h"
#include "KIA-HYUNDAI-64-HTML.h"

View file

@ -2,7 +2,6 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
/* TODO:
- The HEV battery seems to turn off after 1 minute of use. When this happens SOC% stops updating.
@ -65,7 +64,7 @@ void KiaHyundaiHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.data.u8[0]) {
case 0x10: //"PID Header"
if (rx_frame.data.u8[3] == poll_data_pid) {
transmit_can_frame(&KIA_7E4_ack, can_config.battery); //Send ack to BMS if the same frame is sent as polled
transmit_can_frame(&KIA_7E4_ack); //Send ack to BMS if the same frame is sent as polled
}
break;
case 0x21: //First frame in PID group
@ -200,15 +199,15 @@ void KiaHyundaiHybridBattery::transmit_can(unsigned long currentMillis) {
}
poll_data_pid++;
if (poll_data_pid == 1) {
transmit_can_frame(&KIA_7E4_id1, can_config.battery);
transmit_can_frame(&KIA_7E4_id1);
} else if (poll_data_pid == 2) {
transmit_can_frame(&KIA_7E4_id2, can_config.battery);
transmit_can_frame(&KIA_7E4_id2);
} else if (poll_data_pid == 3) {
transmit_can_frame(&KIA_7E4_id3, can_config.battery);
transmit_can_frame(&KIA_7E4_id3);
} else if (poll_data_pid == 4) {
} else if (poll_data_pid == 5) {
transmit_can_frame(&KIA_7E4_id5, can_config.battery);
transmit_can_frame(&KIA_7E4_id5);
}
}
}

View file

@ -1,7 +1,6 @@
#ifndef KIA_HYUNDAI_HYBRID_BATTERY_H
#define KIA_HYUNDAI_HYBRID_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"

View file

@ -5,7 +5,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../devboard/utils/logging.h"
/*
TODO list
@ -750,7 +750,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
break;
case 0x1C42007B: // Reply from battery
if (rx_frame.data.u8[0] == 0x10) { //PID header
transmit_can_frame(&MEB_ACK_FRAME, can_config.battery);
transmit_can_frame(&MEB_ACK_FRAME);
}
if (rx_frame.DLC == 8) {
pid_reply = (rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3];
@ -1290,7 +1290,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
void MebBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis > last_can_msg_timestamp + 500) {
if (currentMillis - last_can_msg_timestamp > 500) {
#ifdef DEBUG_LOG
if (first_can_msg)
logging.printf("MEB: No CAN msg received for 500ms\n");
@ -1311,7 +1311,7 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
counter_10ms = (counter_10ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
transmit_can_frame(&MEB_0FC, can_config.battery); // Required for contactor closing
transmit_can_frame(&MEB_0FC); // Required for contactor closing
}
// Send 20ms CAN Message
if (currentMillis - previousMillis20ms >= INTERVAL_20_MS) {
@ -1322,7 +1322,7 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
counter_20ms = (counter_20ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
transmit_can_frame(&MEB_0FD, can_config.battery); // Required for contactor closing
transmit_can_frame(&MEB_0FD); // Required for contactor closing
}
// Send 40ms CAN Message
if (currentMillis - previousMillis40ms >= INTERVAL_40_MS) {
@ -1339,7 +1339,7 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
}
toggle = !toggle; // Flip the toggle each time the code block is executed
transmit_can_frame(&MEB_040, can_config.battery); // Airbag message - Needed for contactor closing
transmit_can_frame(&MEB_040); // Airbag message - Needed for contactor closing
}
// Send 50ms CAN Message
if (currentMillis - previousMillis50ms >= INTERVAL_50_MS) {
@ -1355,7 +1355,7 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
MEB_0C0.data.u8[0] = vw_crc_calc(MEB_0C0.data.u8, MEB_0C0.DLC, MEB_0C0.ID);
counter_50ms = (counter_50ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
transmit_can_frame(&MEB_0C0, can_config.battery); // Needed for contactor closing
transmit_can_frame(&MEB_0C0); // Needed for contactor closing
}
// Send 100ms CAN Message
if (currentMillis - previousMillis100ms >= INTERVAL_100_MS) {
@ -1443,11 +1443,11 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
MEB_14C.data.u8[0] = vw_crc_calc(MEB_14C.data.u8, MEB_14C.DLC, MEB_14C.ID);
counter_100ms = (counter_100ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
transmit_can_frame(&MEB_503, can_config.battery);
transmit_can_frame(&MEB_272, can_config.battery);
transmit_can_frame(&MEB_3C0, can_config.battery);
transmit_can_frame(&MEB_3BE, can_config.battery);
transmit_can_frame(&MEB_14C, can_config.battery);
transmit_can_frame(&MEB_503);
transmit_can_frame(&MEB_272);
transmit_can_frame(&MEB_3C0);
transmit_can_frame(&MEB_3BE);
transmit_can_frame(&MEB_14C);
}
//Send 200ms message
if (currentMillis - previousMillis200ms >= INTERVAL_200_MS) {
@ -1457,11 +1457,11 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
//TODO: MEB_1B0000B9 & MEB_1B000010 & MEB_1B000046 has CAN sleep commands. May be removed?
transmit_can_frame(&MEB_5E1, can_config.battery);
transmit_can_frame(&MEB_153, can_config.battery);
transmit_can_frame(&MEB_1B0000B9, can_config.battery);
transmit_can_frame(&MEB_1B000010, can_config.battery);
transmit_can_frame(&MEB_1B000046, can_config.battery);
transmit_can_frame(&MEB_5E1);
transmit_can_frame(&MEB_153);
transmit_can_frame(&MEB_1B0000B9);
transmit_can_frame(&MEB_1B000010);
transmit_can_frame(&MEB_1B000046);
switch (poll_pid) {
case PID_SOC:
@ -1990,7 +1990,7 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
break;
}
if (first_can_msg > 0 && currentMillis > first_can_msg + 1000) {
transmit_can_frame(&MEB_POLLING_FRAME, can_config.battery);
transmit_can_frame(&MEB_POLLING_FRAME);
}
}
@ -1998,11 +1998,11 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis500ms >= INTERVAL_500_MS) {
previousMillis500ms = currentMillis;
transmit_can_frame(&MEB_16A954B4, can_config.battery); //eTM, Cooling valves and pumps for BMS
transmit_can_frame(&MEB_569, can_config.battery); // Battery heating requests
transmit_can_frame(&MEB_1A55552B, can_config.battery); //Climate, heatpump and priorities
transmit_can_frame(&MEB_1A555548, can_config.battery); //ORU, OTA update message for reserving battery
transmit_can_frame(&MEB_16A954FB, can_config.battery); //Climate, request to BMS for starting preconditioning
transmit_can_frame(&MEB_16A954B4); //eTM, Cooling valves and pumps for BMS
transmit_can_frame(&MEB_569); // Battery heating requests
transmit_can_frame(&MEB_1A55552B); //Climate, heatpump and priorities
transmit_can_frame(&MEB_1A555548); //ORU, OTA update message for reserving battery
transmit_can_frame(&MEB_16A954FB); //Climate, request to BMS for starting preconditioning
}
//Send 1s CANFD message
@ -2023,12 +2023,12 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
MEB_6B2.data.u8[7] = (uint8_t)((seconds & 0x3E) >> 1);
seconds = (seconds + 1) % 60;
counter_1000ms = (counter_1000ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
transmit_can_frame(&MEB_6B2, can_config.battery); // Diagnostics - Needed for contactor closing
transmit_can_frame(&MEB_641, can_config.battery); // Motor - OBD
transmit_can_frame(&MEB_5F5, can_config.battery); // Loading profile
transmit_can_frame(&MEB_585, can_config.battery); // Systeminfo
transmit_can_frame(&MEB_1A5555A6, can_config.battery); // Temperature QBit
counter_1000ms = (counter_1000ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
transmit_can_frame(&MEB_6B2); // Diagnostics - Needed for contactor closing
transmit_can_frame(&MEB_641); // Motor - OBD
transmit_can_frame(&MEB_5F5); // Loading profile
transmit_can_frame(&MEB_585); // Systeminfo
transmit_can_frame(&MEB_1A5555A6); // Temperature QBit
transmit_obd_can_frame(0x18DA05F1, can_config.battery, true);
}

View file

@ -1,7 +1,6 @@
#ifndef MEB_BATTERY_H
#define MEB_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
#include "MEB-HTML.h"

View file

@ -253,8 +253,15 @@ class MebHtmlRenderer : public BatteryHtmlRenderer {
content += "<h4>Cell imbalance: " + String(rt_enum[datalayer_extended.meb.rt_cell_imbalance & 0x03]) + "</h4>";
content +=
"<h4>Battery unathorized: " + String(rt_enum[datalayer_extended.meb.rt_battery_unathorized & 0x03]) + "</h4>";
content +=
"<h4>Battery temperature: " + String(datalayer_extended.meb.battery_temperature_dC / 10.f, 1) + " &deg;C</h4>";
content += "<h4>Battery temperature: ";
if (datalayer_extended.meb.battery_temperature_dC == 875) { //Raw value 255
content += "ERROR</h4>";
} else if (datalayer_extended.meb.battery_temperature_dC == 870) { //Raw value 254
content += "INIT</h4>";
} else {
content += String(datalayer_extended.meb.battery_temperature_dC / 10.f, 1) + " &deg;C</h4>";
}
for (int i = 0; i < 3; i++) {
content += "<h4>Temperature points " + String(i * 6 + 1) + "-" + String(i * 6 + 6) + " :";
for (int j = 0; j < 6; j++)

View file

@ -2,7 +2,6 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
/* TODO:
- Get contactor closing working
@ -102,13 +101,13 @@ void Mg5Battery::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
previousMillis10 = currentMillis;
transmit_can_frame(&MG_5_100, can_config.battery);
transmit_can_frame(&MG_5_100);
}
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
//transmit_can_frame(&MG_5_100, can_config.battery);
//transmit_can_frame(&MG_5_100);
}
}

View file

@ -1,7 +1,6 @@
#ifndef MG_5_BATTERY_H
#define MG_5_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"

View file

@ -1,351 +1,371 @@
#include "../include.h"
#ifdef MG_HS_PHEV_BATTERY_H
#include "MG-HS-PHEV-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../communication/contactorcontrol/comm_contactorcontrol.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "MG-HS-PHEV-BATTERY.h"
#include "../devboard/utils/logging.h"
/* TODO:
- Get contactor closing working
- Figure out which CAN messages need to be sent towards the battery to keep it alive
- Map all values from battery CAN messages
- Note: Charge power/discharge power is estimated for now
/*
MG HS PHEV 16.6kWh battery integration
This may work on other MG batteries, but will need some hardcoded constants
changing.
OPTIONAL SETTINGS
Put these in your USER_SETTINGS.h:
// This will scale the SoC so the batteries top out at 4.2V/cell instead of
4.1V/cell. The car only seems to use up to 4.1V/cell in service.
#define MG_HS_PHEV_USE_FULL_CAPACITY true
// If you have bypassed the contactors, you can avoid them being activated //
(which also disables isolation resistance measuring).
#define MG_HS_PHEV_DISABLE_CONTACTORS true
CAN CONNECTIONS
Battery Emulator should be connected via CAN to either:
- CAN1 (pins 1+2 on the LV connector)
This provides efficient data updates including individual cell voltages, and
allows control over the contactors.
- CAN1 and CAN2 (pins 3+4) in parallel
This adds extra information (currently just SoH), and works in practice despite
the potential problems with connecting CAN buses in parallel.
NOTES
- Charge power/discharge power is estimated for now
# row3 pin2 needs strobing to 12V (via a 1k) to wake up the BMU
# but contactor won't come on until deasserted
# BMU goes to sleep after after ~18s of no CAN
*/
void MgHsPHEVBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
datalayer.battery.status.real_soc;
// Should be called every second
if (cellVoltageValidTime > 0) {
cellVoltageValidTime--;
}
}
datalayer.battery.status.voltage_dV;
void MgHsPHEVBattery::update_soc(uint16_t soc_times_ten) {
#if MG_HS_PHEV_USE_FULL_CAPACITY
// The SoC hits 100% at 4.1V/cell. To get the full 4.2V/cell we need to use
// voltage instead for the last bit.
datalayer.battery.status.current_dA;
if (cellVoltageValidTime == 0) {
// We don't have a recent cell max voltage reading, so can't do
// voltage-based SoC.
} else if (soc_times_ten > 900 && datalayer.battery.status.cell_max_voltage_mV < 4000) {
// Something is wrong with our max cell voltage reading (it is too low), so
// don't trust it - we'll just let the SoC hit 100%.
} else if (soc_times_ten == 1000 && datalayer.battery.status.cell_max_voltage_mV >= 4100) {
// We've hit 100%, so use voltage-based-SoC calculation for the last bit.
datalayer.battery.info.total_capacity_Wh;
// We usually hit 92% at ~369V, and the pack max is 378V.
datalayer.battery.status.remaining_capacity_Wh;
// Scale so that 100% becomes 92%
soc_times_ten = (uint16_t)(((uint32_t)soc_times_ten * 9200) / 10000);
datalayer.battery.status.max_discharge_power_W;
// Add on the last 100mV as the last 8% of SoC.
soc_times_ten += (uint16_t)((((uint32_t)datalayer.battery.status.cell_max_voltage_mV - 4100) * 800) / 1000);
if (soc_times_ten > 1000) {
soc_times_ten = 1000; // Don't let it go above 100%
}
} else {
// Scale so that 100% becomes 92%
soc_times_ten = (uint16_t)(((uint32_t)soc_times_ten * 9200) / 10000);
}
#endif
datalayer.battery.status.max_charge_power_W;
// Set the state of charge in the datalayer
datalayer.battery.status.real_soc = soc_times_ten * 10;
datalayer.battery.status.temperature_min_dC;
RealSoC = datalayer.battery.status.real_soc / 100;
datalayer.battery.status.temperature_max_dC;
// Calculate the remaining capacity.
tempfloat = datalayer.battery.info.total_capacity_Wh * (RealSoC - MinSoC) / 100;
if (tempfloat > 0) {
datalayer.battery.status.remaining_capacity_Wh = tempfloat;
} else {
datalayer.battery.status.remaining_capacity_Wh = 0;
}
// Calculate the maximum charge power. Taper the charge power between 90% and 100% SoC, as 100% SoC is approached
if (RealSoC < StartChargeTaper) {
datalayer.battery.status.max_charge_power_W = MaxChargePower;
} else if (RealSoC >= 100) {
datalayer.battery.status.max_charge_power_W = TricklePower;
} else {
//Taper the charge to the Trickle value. The shape and start point of the taper is set by the constants
datalayer.battery.status.max_charge_power_W =
(MaxChargePower * pow(((100 - RealSoC) / (100 - StartChargeTaper)), ChargeTaperExponent)) + TricklePower;
}
// Calculate the maximum discharge power. Taper the discharge power between 35% and Min% SoC, as Min% SoC is approached
if (RealSoC > StartDischargeTaper) {
datalayer.battery.status.max_discharge_power_W = MaxDischargePower;
} else if (RealSoC < MinSoC) {
datalayer.battery.status.max_discharge_power_W = TricklePower;
} else {
//Taper the charge to the Trickle value. The shape and start point of the taper is set by the constants
datalayer.battery.status.max_discharge_power_W =
(MaxDischargePower * pow(((RealSoC - MinSoC) / (StartDischargeTaper - MinSoC)), DischargeTaperExponent)) +
TricklePower;
}
}
void MgHsPHEVBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
uint16_t soc1, soc2, cell_id, v;
switch (rx_frame.ID) {
case 0x171: //Following messages were detected on a MG5 battery BMS
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x172:
break;
case 0x173:
break;
case 0x293:
break;
case 0x295:
// Contains cell min/max voltages
v = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
if (v > 0 && v < 0x2000) {
datalayer.battery.status.cell_max_voltage_mV = v;
v = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
if (v > 0 && v < 0x2000) {
datalayer.battery.status.cell_min_voltage_mV = v;
cellVoltageValidTime = CELL_VOLTAGE_TIMEOUT;
}
}
break;
case 0x297:
// Contains battery status in rx_frame.data.u8[1]
// Presumed mapping:
// 1 = disconnected
// 2 = precharge
// 3 = connected
// 15 = isolation fault
// 0/8 = checking
#ifdef DEBUG_LOG
if (rx_frame.data.u8[1] != previousState) {
logging.printf("MG_HS_PHEV: Battery status changed to %d (%d)\n", rx_frame.data.u8[1], rx_frame.data.u8[0]);
}
#endif
if (rx_frame.data.u8[1] == 0xf && previousState != 0xf) {
// Isolation fault, set event
set_event(EVENT_BATTERY_ISOLATION, rx_frame.data.u8[0]);
} else if (rx_frame.data.u8[1] != 0xf && previousState == 0xf) {
// Isolation fault has cleared, clear event
clear_event(EVENT_BATTERY_ISOLATION);
}
if (datalayer.battery.status.bms_status == FAULT) {
// If in fault state, don't try resetting things yet as it'll turn the
// BMS off and we'll lose CAN info
} else if ((rx_frame.data.u8[0] == 0x02 || rx_frame.data.u8[0] == 0x06) && rx_frame.data.u8[1] == 0x01) {
// A weird 'stuck' state where the battery won't reconnect
datalayer.system.status.battery_allows_contactor_closing = false;
if (!datalayer.system.status.BMS_startup_in_progress) {
#ifdef DEBUG_LOG
logging.printf("MG_HS_PHEV: Stuck, resetting.\n");
#endif
start_bms_reset();
}
} else if (rx_frame.data.u8[1] == 0xf) {
// A fault state (likely isolation failure)
datalayer.system.status.battery_allows_contactor_closing = false;
if (!datalayer.system.status.BMS_startup_in_progress) {
#ifdef DEBUG_LOG
logging.printf("MG_HS_PHEV: Fault, resetting.\n");
#endif
start_bms_reset();
}
} else {
datalayer.system.status.battery_allows_contactor_closing = true;
}
previousState = rx_frame.data.u8[1];
break;
case 0x29B: //This ID is on the MG HS RX WITHOUT ANY TX PRESENT
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
case 0x2A2:
// Contains temperatures.
if (rx_frame.data.u8[0] < 0xfe) {
// Max cell temp
datalayer.battery.status.temperature_max_dC = ((rx_frame.data.u8[0] << 8) / 50) - 400;
}
if (rx_frame.data.u8[5] < 0xfe) {
// Min cell temp
datalayer.battery.status.temperature_min_dC = ((rx_frame.data.u8[5] << 8) / 50) - 400;
}
// Coolant temp
// ((rx_frame.data.u8[1] << 8)/50) - 400;
// There is another unknown temp in [6]/[7]
break;
case 0x29C:
break;
case 0x2A0:
break;
case 0x2A2: //This ID is on the MG HS RX WITHOUT ANY TX PRESENT
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x322:
break;
case 0x334:
break;
case 0x33F:
break;
case 0x391: //This ID is on the MG HS RX WITHOUT ANY TX PRESENT
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x393:
break;
case 0x3AB: //This ID is on the MG HS RX WITHOUT ANY TX PRESENT
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x3AC: //This ID is on the MG HS RX WITHOUT ANY TX PRESENT
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x3B8:
break;
case 0x3BA:
break;
case 0x3BC:
case 0x3AC:
// Contains SoCs, voltage, current. Is emitted by both CAN1 and CAN2, but
// the CAN2 version only has one SoC (soc2), the CAN1 version has all four
// values.
// Both SoCs top out at about ~4.1V/cell, the first SoC at 92%, and the
// second at 100%.
// They are scaled differently, the relationship seems to be:
// soc2 = (1.392*soc1) - 28.064
soc1 = (rx_frame.data.u8[0] << 8 | rx_frame.data.u8[1]);
soc2 = (rx_frame.data.u8[2] << 8 | rx_frame.data.u8[3]);
// soc2 is present in both CAN1 and CAN2 messages
if (soc2 < 1022) {
update_soc(soc2);
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
}
if (((rx_frame.data.u8[4] << 8) & 0xf00 | rx_frame.data.u8[5]) != 0) {
// 3AC message contains a nonzero voltage (so must have come from CAN1)
v = (rx_frame.data.u8[4] << 8) & 0xf00 | rx_frame.data.u8[5];
if (v > 0 && v < 4000) {
datalayer.battery.status.voltage_dV = v * 2.5;
}
// Current
v = (rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]);
if (v > 0 && v < 0xf000) {
datalayer.battery.status.current_dA = -(v - 20000) * 0.5;
}
}
break;
case 0x3BE:
// Per-cell voltages and temps
cell_id = rx_frame.data.u8[5];
if (cell_id < 90) {
v = 1000 + (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
datalayer.battery.status.cell_voltages_mV[cell_id] = v < 10000 ? v : 0;
// cell temperature is rx_frame.data.u8[1]-40 but BE doesn't use it
}
break;
case 0x3C0:
break;
case 0x3C2:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x400:
break;
case 0x402:
break;
case 0x418:
break;
case 0x44C: //This ID is on the MG HS RX WITHOUT ANY TX PRESENT
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x620:
break; //This is the last on the list in the MG5 template.
case 0x3a8: //This ID is on the MG HS RX WITHOUT ANY TX PRESENT
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x508: //This ID is a new one MG HS RX WHEN TRANSMITTING 03 22 B0 41 00 00 00 00. Rx data is 00 00 00 00 00 00 00 00
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x7ED: //This ID is the battery BMS
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
//Process rx data for incoming responses to "Read data by ID" Tx
case 0x7ED:
// A response from our CAN2 OBD requests
// We mostly ignore these, apart from SoH, and also the voltage as a
// safety measure (in case CAN1 misbehaves).
if (rx_frame.data.u8[1] == 0x62) {
if (rx_frame.data.u8[2] == 0xB0) { //Battery information
if (rx_frame.data.u8[3] == 0x41 && rx_frame.data.u8[0] == 0x05) { // Battery bus voltage
// Serial.print ("Battery Bus voltage frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
// datalayer.battery.status.PARAMETER = (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]) * 2.5;
// Serial.print ("Battery Bus Voltage = ");
// Serial.println (datalayer.battery.status.PARAMETER);
}
if (rx_frame.data.u8[3] == 0x42 && rx_frame.data.u8[0] == 0x05) { // Battery voltage
// Serial.print ("Battery voltage frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
// Battery bus voltage
// (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]) * 2.5;
} else if (rx_frame.data.u8[3] == 0x42 && rx_frame.data.u8[0] == 0x05) {
// Battery voltage
datalayer.battery.status.voltage_dV = (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]) * 2.5;
// Serial.print ("Battery voltage = ");
// Serial.println (datalayer.battery.status.voltage_dV);
}
if (rx_frame.data.u8[3] == 0x43 && rx_frame.data.u8[0] == 0x05) { // Battery current
// Serial.print ("Battery current frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
datalayer.battery.status.current_dA = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]) - 40000) / -4;
// Serial.print ("Battery current = ");
// Serial.println (datalayer.battery.status.current_dA);
}
if (rx_frame.data.u8[3] == 0x45 && rx_frame.data.u8[0] == 0x05) { // Battery resistance
// Serial.print ("Battery resistance frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
// datalayer.battery.status.PARAMETER = (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]);
// Serial.print ("Battery resistance = ");
// Serial.println (datalayer.battery.status.PARAMETER);
}
if (rx_frame.data.u8[3] == 0x46 && rx_frame.data.u8[0] == 0x05) { // Battery SoC
// Serial.print ("Battery SoC frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
datalayer.battery.status.real_soc = (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]) * 10;
// Serial.print ("Battery SoC = ");
// Serial.println (datalayer.battery.status.real_soc);
RealSoC = datalayer.battery.status.real_soc / 100; // For calculation of charge and discharge rates
}
if (rx_frame.data.u8[3] == 0x47) { // && rx_frame.data.u8[0] == 0x05) { // BMS error code
// Serial.print ("BMS error code frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
// datalayer.battery.status.PARAMETER = (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]); // HOLD: What to do with this data
// Serial.print ("Battery error = ");
// Serial.println (datalayer.battery.status.PARAMETER);
}
if (rx_frame.data.u8[3] == 0x48) { // && rx_frame.data.u8[0] == 0x05) { // BMS status code
// Serial.print ("BMS status code frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
// datalayer.battery.status.PARAMETER = (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]); // HOLD: What to do with this data
// Serial.print ("Battery Status = ");
// Serial.println (datalayer.battery.status.PARAMETER);
}
if (rx_frame.data.u8[3] == 0x49) { // && rx_frame.data.u8[0] == 0x05) { // System main relay B status
// Serial.print ("System main relay B status frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
// datalayer.battery.status.PARAMETER = (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]); // HOLD: What to do with this data
// Serial.print ("Main relay B status = ");
// Serial.println (datalayer.battery.status.PARAMETER);
}
if (rx_frame.data.u8[3] == 0x4A) { // && rx_frame.data.u8[0] == 0x05) { // System main relay G status
// Serial.print ("System main relay G status frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
// datalayer.battery.status.PARAMETER = (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]); // HOLD: What to do with this data
// Serial.print ("Main relay G status = ");
// Serial.println (datalayer.battery.status.PARAMETER);
}
if (rx_frame.data.u8[3] == 0x52) { // && rx_frame.data.u8[0] == 0x05) { // System main relay P status
// Serial.print ("System main relay P status frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
// datalayer.battery.status.PARAMETER = (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]); // HOLD: What to do with this data
// Serial.print ("BMain relay P status = ");
// Serial.println (datalayer.battery.status.PARAMETER);
}
if (rx_frame.data.u8[3] == 0x56 && rx_frame.data.u8[0] == 0x05) { // Max cell temperature
// Serial.print ("Max cell temperature frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
datalayer.battery.status.temperature_max_dC =
(((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]) / 500) - 40) * 10;
// Serial.print ("Max cell temperature = ");
// Serial.println (datalayer.battery.status.temperature_max_dC);
}
if (rx_frame.data.u8[3] == 0x57 && rx_frame.data.u8[0] == 0x05) { // Min cell temperature
// Serial.print ("Min cell temperature frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
datalayer.battery.status.temperature_min_dC =
(((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]) / 500) - 40) * 10;
// Serial.print ("Min cell temperature = ");
// Serial.println (datalayer.battery.status.temperature_min_dC);
}
if (rx_frame.data.u8[3] == 0x58 && rx_frame.data.u8[0] == 0x06) { // Max cell voltage
// Serial.print ("Max cell voltage frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
datalayer.battery.status.cell_max_voltage_mV =
(rx_frame.data.u8[4] << 16 | rx_frame.data.u8[5] << 8 | rx_frame.data.u8[6]) / 250;
// Serial.print ("Max cell voltage = ");
// Serial.println (datalayer.battery.status.cell_max_voltage_mV);
}
if (rx_frame.data.u8[3] == 0x59 && rx_frame.data.u8[0] == 0x06) { // Min cell voltage
// Serial.print ("Min cell voltage frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
datalayer.battery.status.cell_min_voltage_mV =
(rx_frame.data.u8[4] << 16 | rx_frame.data.u8[5] << 8 | rx_frame.data.u8[6]) / 250;
// Serial.print ("Min cell voltage = ");
// Serial.println (datalayer.battery.status.cell_min_voltage_mV);
}
if (rx_frame.data.u8[3] == 0x61 && rx_frame.data.u8[0] == 0x05) { // Battery SoH
// Serial.print ("Battery SoH frame = ");
// print_can_frame_MG5(rx_frame, frameDirection(MSG_RX));
} else if (rx_frame.data.u8[3] == 0x43 && rx_frame.data.u8[0] == 0x05) {
// Battery current
// we won't update this as it differs in rounding from the CAN1 version
//datalayer.battery.status.current_dA = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]) - 40000) / -4;
} else if (rx_frame.data.u8[3] == 0x45 && rx_frame.data.u8[0] == 0x05) {
// Battery resistance
// rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]);
} else if (rx_frame.data.u8[3] == 0x46 && rx_frame.data.u8[0] == 0x05) {
// The battery SoC, the same as soc1 in 3AC.
soc1 = (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]);
// We won't use since we're using soc2
} else if (rx_frame.data.u8[3] == 0x47) {
// BMS error code
// (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]); // HOLD: What to do with this data
} else if (rx_frame.data.u8[3] == 0x48) {
// BMS status coded
// (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]); // HOLD: What to do with this data
// This is the same as 297[1]
} else if (rx_frame.data.u8[3] == 0x49) {
// System main relay B status
// (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]); // HOLD: What to do with this data
} else if (rx_frame.data.u8[3] == 0x4A) {
// System main relay G status
// (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]); // HOLD: What to do with this data
} else if (rx_frame.data.u8[3] ==
0x52) { // && rx_frame.data.u8[0] == 0x05) { // System main relay P status
// System main relay P status
// (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]); // HOLD: What to do with this data
} else if (rx_frame.data.u8[3] == 0x56 && rx_frame.data.u8[0] == 0x05) {
// Max cell temperature
// datalayer.battery.status.temperature_max_dC =
// (((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]) / 500) - 40) * 10;
} else if (rx_frame.data.u8[3] == 0x57 && rx_frame.data.u8[0] == 0x05) {
// Min cell temperature
// datalayer.battery.status.temperature_min_dC =
// (((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]) / 500) - 40) * 10;
} else if (rx_frame.data.u8[3] == 0x58 && rx_frame.data.u8[0] == 0x06) {
// Max cell voltage
// datalayer.battery.status.cell_max_voltage_mV = rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5];
// cellVoltageValidTime = CELL_VOLTAGE_TIMEOUT;
} else if (rx_frame.data.u8[3] == 0x59 && rx_frame.data.u8[0] == 0x06) {
// Min cell voltage
// datalayer.battery.status.cell_min_voltage_mV = rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5];
} else if (rx_frame.data.u8[3] == 0x61 && rx_frame.data.u8[0] == 0x05) {
// Battery SoH
datalayer.battery.status.soh_pptt = (rx_frame.data.u8[4] << 8 | rx_frame.data.u8[5]);
// Serial.print ("Battery SoH = ");
// Serial.println (datalayer.battery.status.soh_pptt);
}
} // data.u8[2]=0xB0
} // data.u8[1] = 0x62)
//Set calculated and derived parameters
// Calculate the remaining capacity.
tempfloat = datalayer.battery.info.total_capacity_Wh * (RealSoC - MinSoC) / 100;
// Serial.print ("Remaining capacity calculated = ");
// Serial.println (tempfloat);
if (tempfloat > 0) {
datalayer.battery.status.remaining_capacity_Wh = tempfloat;
} else {
datalayer.battery.status.remaining_capacity_Wh = 0;
}
// Calculate the maximum charge power. Taper the charge power between 90% and 100% SoC, as 100% SoC is approached
if (RealSoC < StartChargeTaper) {
datalayer.battery.status.max_charge_power_W = MaxChargePower;
} else if (RealSoC >= 100) {
datalayer.battery.status.max_charge_power_W = TricklePower;
} else {
//Taper the charge to the Trickle value. The shape and start point of the taper is set by the constants
datalayer.battery.status.max_charge_power_W =
(MaxChargePower * pow(((100 - RealSoC) / (100 - StartChargeTaper)), ChargeTaperExponent)) + TricklePower;
}
// Calculate the maximum discharge power. Taper the discharge power between 35% and Min% SoC, as Min% SoC is approached
if (RealSoC > StartDischargeTaper) {
datalayer.battery.status.max_discharge_power_W = MaxDischargePower;
} else if (RealSoC < MinSoC) {
datalayer.battery.status.max_discharge_power_W = TricklePower;
} else {
//Taper the charge to the Trickle value. The shape and start point of the taper is set by the constants
datalayer.battery.status.max_discharge_power_W =
(MaxDischargePower * pow(((RealSoC - MinSoC) / (StartDischargeTaper - MinSoC)), DischargeTaperExponent)) +
TricklePower;
}
break;
default:
break;
}
}
void MgHsPHEVBattery::transmit_can(unsigned long currentMillis) {
// Send 70ms CAN Message
if (currentMillis - previousMillis70 >= INTERVAL_70_MS) {
previousMillis70 = currentMillis;
if (datalayer.system.status.BMS_reset_in_progress || datalayer.system.status.BMS_startup_in_progress) {
// Transmitting towards battery is halted while BMS is being reset
previousMillis100 = currentMillis;
previousMillis200 = currentMillis;
return;
}
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
#if MG_HS_PHEV_DISABLE_CONTACTORS
// Leave the contactors open
MG_HS_8A.data.u8[5] = 0x00;
#else
if (datalayer.battery.status.bms_status == FAULT) {
//Open contactors!
// Fault, so open contactors!
MG_HS_8A.data.u8[5] = 0x00;
} else { // Not in faulted mode, Close contactors!
} else if (!datalayer.system.status.inverter_allows_contactor_closing) {
// Inverter requests contactor opening
MG_HS_8A.data.u8[5] = 0x00;
} else {
// Everything ready, close contactors
MG_HS_8A.data.u8[5] = 0x02;
}
#endif // MG_HS_PHEV_DISABLE_CONTACTORS
transmit_can_frame(&MG_HS_8A, can_config.battery);
transmit_can_frame(&MG_HS_1F1, can_config.battery);
transmit_can_frame(&MG_HS_8A);
transmit_can_frame(&MG_HS_1F1);
}
// Send 200ms CAN Message
if (currentMillis - previousMillis200 >= INTERVAL_200_MS) {
previousMillis200 = currentMillis;
switch (messageindex) {
switch (transmitIndex) {
case 1:
transmit_can_frame(&MG_HS_7E5_B0_42, can_config.battery); //Battery voltage
transmit_can_frame(&MG_HS_7E5_B0_42); //Battery voltage
break;
case 2:
transmit_can_frame(&MG_HS_7E5_B0_43, can_config.battery); //Battery current
break;
case 3:
transmit_can_frame(&MG_HS_7E5_B0_46, can_config.battery); //Battery SoC
break;
case 4:
transmit_can_frame(&MG_HS_7E5_B0_47, can_config.battery); // Get BMS error code
break;
case 5:
transmit_can_frame(&MG_HS_7E5_B0_48, can_config.battery); // Get BMS status
break;
case 6:
transmit_can_frame(&MG_HS_7E5_B0_49, can_config.battery); // Get System main relay B status
break;
case 7:
transmit_can_frame(&MG_HS_7E5_B0_4A, can_config.battery); // Get System main relay G status
break;
case 8:
transmit_can_frame(&MG_HS_7E5_B0_52, can_config.battery); // Get System main relay P status
break;
case 9:
transmit_can_frame(&MG_HS_7E5_B0_56, can_config.battery); //Max cell temperature
break;
case 10:
transmit_can_frame(&MG_HS_7E5_B0_57, can_config.battery); //Min cell temperature
break;
case 11:
transmit_can_frame(&MG_HS_7E5_B0_58, can_config.battery); //Max cell voltage
break;
case 12:
transmit_can_frame(&MG_HS_7E5_B0_59, can_config.battery); //Min cell voltage
break;
case 13:
transmit_can_frame(&MG_HS_7E5_B0_61, can_config.battery); //Battery SoH
messageindex = 0; //Return to the first message index. This goes in the last message entry
transmit_can_frame(&MG_HS_7E5_B0_61); //Battery SoH
transmitIndex = 0; //Return to the first message index. This goes in the last message entry
break;
default:
break;
} //switch
messageindex++; //Increment the message index
transmitIndex++; //Increment the message index
} //endif
}
@ -358,6 +378,6 @@ void MgHsPHEVBattery::setup(void) { // Performs one time setup at startup
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.total_capacity_Wh = BATTERY_WH_MAX;
datalayer.battery.info.number_of_cells = 90;
}
#endif

View file

@ -1,7 +1,5 @@
#ifndef MG_HS_PHEV_BATTERY_H
#define MG_HS_PHEV_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
@ -19,22 +17,28 @@ class MgHsPHEVBattery : public CanBattery {
static constexpr const char* Name = "MG HS PHEV 16.6kWh battery";
private:
static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 3100;
void update_soc(uint16_t soc_times_ten);
static const int MAX_PACK_VOLTAGE_DV = 3780; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 2790;
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 MIN_CELL_VOLTAGE_MV = 2610; //Battery is put into emergency stop if one cell goes below this value
unsigned long previousMillis70 = 0; // will store last time a 70ms 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
int BMS_SOC = 0;
// For calculating charge and discharge power
float RealVoltage;
float RealSoC;
float tempfloat;
uint8_t messageindex = 0; //For polling switchcase
uint8_t previousState = 0;
static const uint16_t CELL_VOLTAGE_TIMEOUT = 10; // in seconds
uint16_t cellVoltageValidTime = 0;
uint8_t transmitIndex = 0; //For polling switchcase
const int MaxChargePower = 3000; // Maximum allowable charge power, excluding the taper
const int StartChargeTaper = 90; // Battery percentage above which the charge power will taper to zero
@ -52,7 +56,7 @@ class MgHsPHEVBattery : public CanBattery {
.ext_ID = false,
.DLC = 8,
.ID = 0x08A,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x36, 0xB0}};
.data = {0x80, 0x00, 0x00, 0x04, 0x00, 0x02, 0x36, 0xB0}};
CAN_frame MG_HS_1F1 = {.FD = false,
.ext_ID = false,
.DLC = 8,

View file

@ -1,14 +1,11 @@
#include "NISSAN-LEAF-BATTERY.h"
#include "../include.h"
#ifdef MQTT
#include "../devboard/mqtt/mqtt.h"
#endif
#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 "../devboard/utils/logging.h"
#include "../charger/CHARGERS.h"
#include "../charger/CanCharger.h"
uint16_t Temp_fromRAW_to_F(uint16_t temperature);
@ -334,7 +331,7 @@ void NissanLeafBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
group_7bb = rx_frame.data.u8[3];
}
transmit_can_frame(&LEAF_NEXT_LINE_REQUEST, can_interface); //Request the next frame for the group
transmit_can_frame(&LEAF_NEXT_LINE_REQUEST); //Request the next frame for the group
if (group_7bb == 0x01) //High precision SOC, Current, voltages etc.
{
@ -584,7 +581,7 @@ void NissanLeafBattery::transmit_can(unsigned long currentMillis) {
LEAF_1D4.data.u8[7] = 0xDE;
break;
}
transmit_can_frame(&LEAF_1D4, can_interface);
transmit_can_frame(&LEAF_1D4);
switch (mprun10r) {
case (0):
@ -677,7 +674,7 @@ void NissanLeafBattery::transmit_can(unsigned long currentMillis) {
//Only send this message when NISSANLEAF_CHARGER is not defined (otherwise it will collide!)
if (!charger || charger->type() != ChargerType::NissanLeaf) {
transmit_can_frame(&LEAF_1F2, can_interface);
transmit_can_frame(&LEAF_1F2);
}
mprun10r = (mprun10r + 1) % 20; // 0x1F2 patter repeats after 20 messages. 0-1..19-0
@ -701,7 +698,7 @@ void NissanLeafBattery::transmit_can(unsigned long currentMillis) {
}
// VCM message, containing info if battery should sleep or stay awake
transmit_can_frame(&LEAF_50B, can_interface); // HCM_WakeUpSleepCommand == 11b == WakeUp, and CANMASK = 1
transmit_can_frame(&LEAF_50B); // HCM_WakeUpSleepCommand == 11b == WakeUp, and CANMASK = 1
LEAF_50C.data.u8[3] = mprun100;
switch (mprun100) {
@ -722,7 +719,7 @@ void NissanLeafBattery::transmit_can(unsigned long currentMillis) {
LEAF_50C.data.u8[5] = 0x9A;
break;
}
transmit_can_frame(&LEAF_50C, can_interface);
transmit_can_frame(&LEAF_50C);
mprun100 = (mprun100 + 1) % 4; // mprun100 cycles between 0-1-2-3-0-1...
}
@ -738,7 +735,7 @@ void NissanLeafBattery::transmit_can(unsigned long currentMillis) {
PIDindex = (PIDindex + 1) % 7; // 7 = amount of elements in the PIDgroups[]
LEAF_GROUP_REQUEST.data.u8[2] = PIDgroups[PIDindex];
transmit_can_frame(&LEAF_GROUP_REQUEST, can_interface);
transmit_can_frame(&LEAF_GROUP_REQUEST);
}
if (hold_off_with_polling_10seconds > 0) {
@ -800,19 +797,19 @@ void NissanLeafBattery::clearSOH(void) {
break;
case 1: // Set CAN_PROCESS_FLAG to 0xC0
LEAF_CLEAR_SOH.data = {0x02, 0x10, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&LEAF_CLEAR_SOH, can_interface);
transmit_can_frame(&LEAF_CLEAR_SOH);
// BMS should reply 02 50 C0 FF FF FF FF FF
stateMachineClearSOH = 2;
break;
case 2: // Set something ?
LEAF_CLEAR_SOH.data = {0x02, 0x3E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&LEAF_CLEAR_SOH, can_interface);
transmit_can_frame(&LEAF_CLEAR_SOH);
// BMS should reply 7E FF FF FF FF FF FF
stateMachineClearSOH = 3;
break;
case 3: // Request challenge to solve
LEAF_CLEAR_SOH.data = {0x02, 0x27, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&LEAF_CLEAR_SOH, can_interface);
transmit_can_frame(&LEAF_CLEAR_SOH);
// BMS should reply with (challenge) 06 67 65 (02 DD 86 43) FF
stateMachineClearSOH = 4;
break;
@ -820,34 +817,34 @@ void NissanLeafBattery::clearSOH(void) {
decodeChallengeData(incomingChallenge, solvedChallenge);
LEAF_CLEAR_SOH.data = {
0x10, 0x0A, 0x27, 0x66, solvedChallenge[0], solvedChallenge[1], solvedChallenge[2], solvedChallenge[3]};
transmit_can_frame(&LEAF_CLEAR_SOH, can_interface);
transmit_can_frame(&LEAF_CLEAR_SOH);
// BMS should reply 7BB 8 30 01 00 FF FF FF FF FF // Proceed with more data (PID ACK)
stateMachineClearSOH = 5;
break;
case 5: // Reply with even more decoded challenge data
LEAF_CLEAR_SOH.data = {
0x21, solvedChallenge[4], solvedChallenge[5], solvedChallenge[6], solvedChallenge[7], 0x00, 0x00, 0x00};
transmit_can_frame(&LEAF_CLEAR_SOH, can_interface);
transmit_can_frame(&LEAF_CLEAR_SOH);
// BMS should reply 02 67 66 FF FF FF FF FF // Thank you for the data
stateMachineClearSOH = 6;
break;
case 6: // Check if solved data was OK
LEAF_CLEAR_SOH.data = {0x03, 0x31, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&LEAF_CLEAR_SOH, can_interface);
transmit_can_frame(&LEAF_CLEAR_SOH);
//7BB 8 03 71 03 01 FF FF FF FF // If all is well, BMS replies with 03 71 03 01.
//Incase you sent wrong challenge, you get 03 7f 31 12
stateMachineClearSOH = 7;
break;
case 7: // Reset SOH% request
LEAF_CLEAR_SOH.data = {0x03, 0x31, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&LEAF_CLEAR_SOH, can_interface);
transmit_can_frame(&LEAF_CLEAR_SOH);
//7BB 8 03 71 03 02 FF FF FF FF // 03 71 03 02 means that BMS accepted command.
//7BB 03 7f 31 12 means your challenge was wrong, so command ignored
stateMachineClearSOH = 8;
break;
case 8: // Please proceed with resetting SOH
LEAF_CLEAR_SOH.data = {0x02, 0x10, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&LEAF_CLEAR_SOH, can_interface);
transmit_can_frame(&LEAF_CLEAR_SOH);
// 7BB 8 02 50 81 FF FF FF FF FF // SOH reset OK
stateMachineClearSOH = 255;
break;

View file

@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../include.h"
#include "CanBattery.h"
#include "NISSAN-LEAF-HTML.h"

View file

@ -2,7 +2,6 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
void findMinMaxCellvoltages(const uint16_t arr[], size_t size, uint16_t& Minimum_Cell_Voltage,
uint16_t& Maximum_Cell_Voltage) {

View file

@ -1,8 +1,8 @@
#ifndef ORION_BMS_H
#define ORION_BMS_H
#include <Arduino.h>
#include "../include.h"
#include "../system_settings.h"
#include "CanBattery.h"
#ifdef ORION_BMS

View file

@ -2,7 +2,6 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
void PylonBattery::update_values() {
@ -117,10 +116,10 @@ void PylonBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
previousMillis1000 = currentMillis;
transmit_can_frame(&PYLON_3010, can_config.battery); // Heartbeat
transmit_can_frame(&PYLON_4200, can_config.battery); // Ensemble OR System equipment info, depends on frame0
transmit_can_frame(&PYLON_8200, can_config.battery); // Control device quit sleep status
transmit_can_frame(&PYLON_8210, can_config.battery); // Charge command
transmit_can_frame(&PYLON_3010); // Heartbeat
transmit_can_frame(&PYLON_4200); // Ensemble OR System equipment info, depends on frame0
transmit_can_frame(&PYLON_8200); // Control device quit sleep status
transmit_can_frame(&PYLON_8210); // Charge command
if (ensemble_info_ack) {
PYLON_4200.data.u8[0] = 0x00; //Request system equipment info

View file

@ -3,7 +3,6 @@
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "CanBattery.h"
#ifdef PYLON_BATTERY

View file

@ -2,7 +2,6 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
/* TODO
- LOG files from vehicle needed to determine CAN content needed to send towards battery!
@ -202,7 +201,7 @@ void RangeRoverPhevBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis50ms >= INTERVAL_50_MS) {
previousMillis50ms = currentMillis;
transmit_can_frame(&RANGE_ROVER_18B, can_config.battery);
transmit_can_frame(&RANGE_ROVER_18B);
}
}

View file

@ -1,7 +1,6 @@
#ifndef RANGE_ROVER_PHEV_BATTERY_H
#define RANGE_ROVER_PHEV_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"

View file

@ -2,7 +2,7 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../devboard/utils/logging.h"
/* TODO:
There seems to be some values on the Kangoo that differ between the 22/33 kWh version
@ -162,7 +162,7 @@ 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;
transmit_can_frame(&KANGOO_423, can_config.battery);
transmit_can_frame(&KANGOO_423);
GVI_Pollcounter++;
GVL_pause = 0;
if (GVI_Pollcounter >= 24) {
@ -174,9 +174,9 @@ void RenaultKangooBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
previousMillis1000 = currentMillis;
if (GVB_79B_Continue)
transmit_can_frame(&KANGOO_79B_Continue, can_config.battery);
transmit_can_frame(&KANGOO_79B_Continue);
} else {
transmit_can_frame(&KANGOO_79B, can_config.battery);
transmit_can_frame(&KANGOO_79B);
}
}

View file

@ -1,7 +1,6 @@
#ifndef RENAULT_KANGOO_BATTERY_H
#define RENAULT_KANGOO_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"

View file

@ -2,7 +2,6 @@
#include <cstdint>
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
int16_t max_value(int16_t* entries, size_t len) {
int result = INT16_MIN;

View file

@ -1,6 +1,5 @@
#ifndef RENAULT_TWIZY_BATTERY_H
#define RENAULT_TWIZY_BATTERY_H
#include "../include.h"
#include "CanBattery.h"
#ifdef RENAULT_TWIZY_BATTERY

View file

@ -2,7 +2,6 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "../include.h"
void transmit_can_frame(CAN_frame* tx_frame, int interface);
@ -166,7 +165,7 @@ void RenaultZoeGen1Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (frame0) {
case 0x10: //PID HEADER, datarow 0
requested_poll = rx_frame.data.u8[3];
transmit_can_frame(&ZOE_ACK_79B, can_interface);
transmit_can_frame(&ZOE_ACK_79B);
if (requested_poll == GROUP1_CELLVOLTAGES_1_POLL) {
cellvoltages[0] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
@ -469,7 +468,7 @@ void RenaultZoeGen1Battery::transmit_can(unsigned long currentMillis) {
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
transmit_can_frame(&ZOE_423, can_interface);
transmit_can_frame(&ZOE_423);
if ((counter_423 / 5) % 2 == 0) { // Alternate every 5 messages between these two
ZOE_423.data.u8[4] = 0xB2;
@ -508,7 +507,7 @@ void RenaultZoeGen1Battery::transmit_can(unsigned long currentMillis) {
ZOE_POLL_79B.data.u8[2] = current_poll;
transmit_can_frame(&ZOE_POLL_79B, can_interface);
transmit_can_frame(&ZOE_POLL_79B);
}
}

View file

@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "../include.h"
/* TODO
- Add //NVROL Reset
@ -108,7 +107,7 @@ void RenaultZoeGen2Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
case 0x18DAF1DB: // LBC Reply from active polling
if (rx_frame.data.u8[0] == 0x10) { //First frame of a group
transmit_can_frame(&ZOE_POLL_FLOW_CONTROL, can_interface);
transmit_can_frame(&ZOE_POLL_FLOW_CONTROL);
//frame 2 & 3 contains which PID is sent
reply_poll = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
}
@ -676,7 +675,7 @@ void RenaultZoeGen2Battery::transmit_can(unsigned long currentMillis) {
counter_373 = (counter_373 + 1) % 10;
*/
transmit_can_frame(&ZOE_373, can_interface);
transmit_can_frame(&ZOE_373);
transmit_can_frame_376();
}
@ -691,7 +690,7 @@ void RenaultZoeGen2Battery::transmit_can(unsigned long currentMillis) {
ZOE_POLL_18DADBF1.data.u8[2] = (uint8_t)((currentpoll & 0xFF00) >> 8);
ZOE_POLL_18DADBF1.data.u8[3] = (uint8_t)(currentpoll & 0x00FF);
transmit_can_frame(&ZOE_POLL_18DADBF1, can_interface);
transmit_can_frame(&ZOE_POLL_18DADBF1);
}
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
@ -733,7 +732,7 @@ void RenaultZoeGen2Battery::transmit_can_frame_376(void) {
ZOE_376.data.u8[4] = hourSeg;
ZOE_376.data.u8[5] = minuteSeg;
transmit_can_frame(&ZOE_376, can_interface);
transmit_can_frame(&ZOE_376);
}
void RenaultZoeGen2Battery::transmit_reset_nvrol_frames(void) {
@ -742,14 +741,14 @@ void RenaultZoeGen2Battery::transmit_reset_nvrol_frames(void) {
startTimeNVROL = millis();
// 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_interface);
transmit_can_frame(&ZOE_POLL_18DADBF1);
NVROLstateMachine = 1;
break;
case 1: // wait 100 ms
if ((millis() - startTimeNVROL) > INTERVAL_100_MS) {
// NVROL reset, part 2: send 0x043101B00900AAAA
ZOE_POLL_18DADBF1.data = {0x04, 0x31, 0x01, 0xB0, 0x09, 0x00, 0xAA, 0xAA};
transmit_can_frame(&ZOE_POLL_18DADBF1, can_interface);
transmit_can_frame(&ZOE_POLL_18DADBF1);
startTimeNVROL = millis(); //Reset time start, so we can check time for next step
NVROLstateMachine = 2;
}
@ -758,7 +757,7 @@ void RenaultZoeGen2Battery::transmit_reset_nvrol_frames(void) {
if ((millis() - startTimeNVROL) > INTERVAL_1_S) {
// Enable temporisation before sleep, part 1: send 0x021003AAAAAAAAAA
ZOE_POLL_18DADBF1.data = {0x02, 0x10, 0x03, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
transmit_can_frame(&ZOE_POLL_18DADBF1, can_interface);
transmit_can_frame(&ZOE_POLL_18DADBF1);
startTimeNVROL = millis(); //Reset time start, so we can check time for next step
NVROLstateMachine = 3;
}
@ -767,7 +766,7 @@ void RenaultZoeGen2Battery::transmit_reset_nvrol_frames(void) {
if ((millis() - startTimeNVROL) > INTERVAL_100_MS) {
// Enable temporisation before sleep, part 2: send 0x042E928101AAAAAA
ZOE_POLL_18DADBF1.data = {0x04, 0x2E, 0x92, 0x81, 0x01, 0xAA, 0xAA, 0xAA};
transmit_can_frame(&ZOE_POLL_18DADBF1, can_interface);
transmit_can_frame(&ZOE_POLL_18DADBF1);
// Set data back to init values, we are done with the ZOE_POLL_18DADBF1 frame
ZOE_POLL_18DADBF1.data = {0x03, 0x22, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00};
poll_index = 0;

View file

@ -1,6 +1,5 @@
#ifndef RENAULT_ZOE_GEN2_BATTERY_H
#define RENAULT_ZOE_GEN2_BATTERY_H
#include "../include.h"
#include "CanBattery.h"
#include "RENAULT-ZOE-GEN2-HTML.h"

View file

@ -2,7 +2,6 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
void RjxzsBms::update_values() {
@ -509,8 +508,8 @@ void RjxzsBms::transmit_can(unsigned long currentMillis) {
}
if (!setup_completed) {
transmit_can_frame(&RJXZS_10, can_config.battery); // Communication connected flag
transmit_can_frame(&RJXZS_1C, can_config.battery); // CAN OK
transmit_can_frame(&RJXZS_10); // Communication connected flag
transmit_can_frame(&RJXZS_1C); // CAN OK
}
}
}

View file

@ -1,8 +1,8 @@
#ifndef RJXZS_BMS_H
#define RJXZS_BMS_H
#include <Arduino.h>
#include "../include.h"
#include "../system_settings.h"
#include "CanBattery.h"
#ifdef RJXZS_BMS
@ -11,7 +11,7 @@
class RjxzsBms : public CanBattery {
public:
RjxzsBms() : CanBattery(true) {}
RjxzsBms() : CanBattery(CAN_Speed::CAN_SPEED_250KBPS) {}
virtual void setup(void);
virtual void handle_incoming_can_frame(CAN_frame rx_frame);

View file

@ -13,7 +13,7 @@ class RS485Battery : public Battery, Transmitter, Rs485Receiver {
public:
virtual void transmit_rs485(unsigned long currentMillis) = 0;
String interface_name() { return "RS485"; }
const char* interface_name() { return "RS485"; }
void transmit(unsigned long currentMillis) { transmit_rs485(currentMillis); }

View file

@ -2,7 +2,6 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
/* Credits go to maciek16c for these findings!
https://github.com/maciek16c/hyundai-santa-fe-phev-battery
@ -116,8 +115,7 @@ void SantaFePhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
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_interface); //Send ack to BMS if the same frame is sent as polled
transmit_can_frame(&SANTAFE_7E4_ack); //Send ack to BMS if the same frame is sent as polled
}
break;
case 0x21: //First frame in PID group
@ -290,9 +288,9 @@ void SantaFePhevBattery::transmit_can(unsigned long currentMillis) {
SANTAFE_200.data.u8[7] = checksum_200;
transmit_can_frame(&SANTAFE_200, can_interface);
transmit_can_frame(&SANTAFE_2A1, can_interface);
transmit_can_frame(&SANTAFE_2F0, can_interface);
transmit_can_frame(&SANTAFE_200);
transmit_can_frame(&SANTAFE_2A1);
transmit_can_frame(&SANTAFE_2F0);
counter_200++;
if (counter_200 > 0xF) {
@ -304,7 +302,7 @@ void SantaFePhevBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
transmit_can_frame(&SANTAFE_523, can_interface);
transmit_can_frame(&SANTAFE_523);
}
// Send 500ms CAN Message
@ -314,7 +312,7 @@ void SantaFePhevBattery::transmit_can(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_interface);
transmit_can_frame(&SANTAFE_7E4_poll);
}
}

View file

@ -2,7 +2,6 @@
#define SANTA_FE_PHEV_BATTERY_H
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "CanBattery.h"
#ifdef SANTA_FE_PHEV_BATTERY

View file

@ -1,7 +1,6 @@
#include "SIMPBMS-BATTERY.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
void SimpBmsBattery::update_values() {

View file

@ -1,7 +1,6 @@
#ifndef SIMPBMS_BATTERY_H
#define SIMPBMS_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"

View file

@ -2,7 +2,6 @@
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
void SonoBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
@ -120,7 +119,7 @@ void SonoBattery::transmit_can(unsigned long currentMillis) {
if (datalayer.battery.status.bms_status == FAULT) {
SONO_400.data.u8[0] = 0x14; //Charging DISABLED
}
transmit_can_frame(&SONO_400, can_config.battery);
transmit_can_frame(&SONO_400);
}
// Send 1000ms CAN Message
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
@ -128,14 +127,14 @@ void SonoBattery::transmit_can(unsigned long currentMillis) {
//Time and date
//Let's see if the battery is happy with just getting seconds incrementing
SONO_401.data.u8[0] = 2025; //Year
SONO_401.data.u8[0] = 25; //Year
SONO_401.data.u8[1] = 1; //Month
SONO_401.data.u8[2] = 1; //Day
SONO_401.data.u8[3] = 12; //Hour
SONO_401.data.u8[4] = 15; //Minute
SONO_401.data.u8[5] = seconds; //Second
seconds = (seconds + 1) % 61;
transmit_can_frame(&SONO_401, can_config.battery);
transmit_can_frame(&SONO_401);
}
}

View file

@ -1,7 +1,6 @@
#ifndef SONO_BATTERY_H
#define SONO_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"

View file

@ -1,10 +1,15 @@
#ifndef _SHUNT_H
#define _SHUNT_H
#include "USER_SETTINGS.h"
#include "src/communication/Transmitter.h"
#include "src/communication/can/CanReceiver.h"
#include "src/communication/can/comm_can.h"
#include "src/devboard/safety/safety.h"
#include "src/devboard/utils/types.h"
enum class ShuntType { None = 0, BmwSbox = 1, Highest };
class CanShunt : public Transmitter, CanReceiver {
public:
virtual void setup() = 0;
@ -12,7 +17,7 @@ class CanShunt : public Transmitter, CanReceiver {
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
// The name of the comm interface the shunt is using.
virtual String interface_name() { return getCANInterfaceName(can_config.shunt); }
virtual const char* interface_name() { return getCANInterfaceName(can_config.shunt); }
void transmit(unsigned long currentMillis) {
if (allowed_to_send_CAN) {
@ -26,12 +31,18 @@ class CanShunt : public Transmitter, CanReceiver {
CAN_Interface can_interface;
CanShunt() {
can_interface = can_config.battery;
can_interface = can_config.shunt;
register_transmitter(this);
register_can_receiver(this, can_interface);
}
void transmit_can_frame(CAN_frame* frame) { transmit_can_frame_to_interface(frame, can_interface); }
};
extern CanShunt* shunt;
extern std::vector<ShuntType> supported_shunt_types();
extern const char* name_for_shunt_type(ShuntType type);
extern ShuntType user_selected_shunt_type;
#endif

View file

@ -1,8 +1,36 @@
#include "../include.h"
#include "BMW-SBOX.h"
#include "Shunt.h"
CanShunt* shunt = nullptr;
ShuntType user_selected_shunt_type = ShuntType::None;
#ifdef COMMON_IMAGE
#ifdef SELECTED_SHUNT_CLASS
#error "Compile time SELECTED_SHUNT_CLASS should not be defined with COMMON_IMAGE"
#endif
void setup_can_shunt() {
if (shunt) {
return;
}
switch (user_selected_shunt_type) {
case ShuntType::None:
shunt = nullptr;
return;
case ShuntType::BmwSbox:
shunt = new BmwSbox();
break;
default:
return;
}
if (shunt) {
shunt->setup();
}
}
#else
void setup_can_shunt() {
if (shunt) {
return;
@ -15,3 +43,25 @@ void setup_can_shunt() {
}
#endif
}
#endif
extern std::vector<ShuntType> supported_shunt_types() {
std::vector<ShuntType> types;
for (int i = 0; i < (int)ShuntType::Highest; i++) {
types.push_back((ShuntType)i);
}
return types;
}
extern const char* name_for_shunt_type(ShuntType type) {
switch (type) {
case ShuntType::None:
return "None";
case ShuntType::BmwSbox:
return BmwSbox::Name;
default:
return nullptr;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
#ifndef TESLA_BATTERY_H
#define TESLA_BATTERY_H
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "CanBattery.h"
#include "TESLA-HTML.h"
@ -12,6 +11,11 @@
#define SELECTED_BATTERY_CLASS TeslaModelSXBattery
#endif
/*NOTE! IMPORTANT INFORMATION!
Be sure to scroll down in this file and configure all "GTW_" parameters to suit your battery.
Failure to configure these will result in VCFRONT and GTW MIA errors
*/
//#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.
class TeslaBattery : public CanBattery {
@ -43,7 +47,25 @@ class TeslaBattery : public CanBattery {
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 */
// Set this to true to try to close contactors/full startup even with no inverter defined/connected
bool batteryTestOverride = false;
// 0x7FF gateway config, "Gen3" vehicles only, not applicable to Gen2 "classic" Model S and Model X
//
// ** MANUALLY SET FOR NOW **, TODO: change based on USER_SETTINGS.h or preset
//
static const uint16_t GTW_country =
18242; // "US" (USA): 21843, "CA" (Canada): 17217, "GB" (UK & N Ireland): 18242, "DK" (Denmark): 17483, "DE" (Germany): 17477, "AU" (Australia): 16725 [HVP shows errors if EU/US region mismatch for example]
// GTW_country is ISO 3166-1 Alpha-2 code, each letter converted to binary (8-bit chunks), those 8-bit chunks concatenated and then converted to decimal
static const uint8_t GTW_rightHandDrive =
1; // Left: 0, Right: 1 (not sure this matters but there for consistency in emulating the car - make sure correct for GTW_country, e.g. 0 for USA)
static const uint8_t GTW_mapRegion =
1; // "ME": 8, "NONE": 2, "CN": 3, "TW": 6, "JP": 5, "US": 0, "KR": 7, "AU": 4, "EU": 1 (not sure this matters but there for consistency)
static const uint8_t GTW_chassisType =
2; // "MODEL_3_CHASSIS": 2, "MODEL_Y_CHASSIS": 3 ("MODEL_S_CHASSIS": 0, "MODEL_X_CHASSIS": 1)
static const uint8_t GTW_packEnergy = 1; // "PACK_50_KWH": 0, "PACK_74_KWH": 1, "PACK_62_KWH": 2, "PACK_100_KWH": 3
/* Do not change anything below this line! */
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
@ -75,53 +97,391 @@ class TeslaBattery : public CanBattery {
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
//UDS session tracker
//static bool uds_SessionInProgress = false; // Future use
//0x221 VCFRONT_LVPowerState
uint8_t muxNumber_TESLA_221 = 0;
uint8_t frameCounter_TESLA_221 = 15; // Start at 15 for Mux 0
uint8_t vehicleState = 1; // "OFF": 0, "DRIVE": 1, "ACCESSORY": 2, "GOING_DOWN": 3
uint16_t powerDownTimer = 180; // Car power down (i.e. contactor open) tracking timer, 3 seconds per sendingState
//0x2E1 VCFRONT_status, 6 mux tracker
uint8_t muxNumber_TESLA_2E1 = 0;
//0x334 UI
bool TESLA_334_INITIAL_SENT = false;
//0x3A1 VCFRONT_vehicleStatus, 15 frame counter (temporary)
uint8_t frameCounter_TESLA_3A1 = 0;
//0x3C2 VCLEFT_switchStatus
uint8_t muxNumber_TESLA_3C2 = 0;
//0x504 TWC_status
bool TESLA_504_INITIAL_SENT = false;
//0x7FF GTW_carConfig, 5 mux tracker
uint8_t muxNumber_TESLA_7FF = 0;
//Max percentage charge tracker
uint16_t previous_max_percentage = datalayer.battery.settings.max_percentage;
//0x082 UI_tripPlanning: "cycle_time" 1000ms
CAN_frame TESLA_082 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x082,
.data = {0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80}};
//0x102 VCLEFT_doorStatus: "cycle_time" 100ms
CAN_frame TESLA_102 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x102,
.data = {0x22, 0x33, 0x00, 0x00, 0xC0, 0x38, 0x21, 0x08}};
//0x103 VCRIGHT_doorStatus: "cycle_time" 100ms
CAN_frame TESLA_103 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x103,
.data = {0x22, 0x33, 0x00, 0x00, 0x30, 0xF2, 0x20, 0x02}};
//0x118 DI_systemStatus: "cycle_time" 50ms, DI_systemStatusChecksum/DI_systemStatusCounter generated via generateFrameCounterChecksum
CAN_frame TESLA_118 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x118,
.data = {0xAB, 0x60, 0x2A, 0x00, 0x00, 0x08, 0x00, 0x00}};
//0x2A8 CMPD_state: "cycle_time" 100ms, different depending on firmware, semi-manual increment for now
CAN_frame TESLA_2A8 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2A8,
.data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x2C}};
//0x213 UI_cruiseControl: "cycle_time" 500ms, UI_speedLimitTick/UI_cruiseControlCounter - different depending on firmware, semi-manual increment for now
CAN_frame TESLA_213 = {.FD = false, .ext_ID = false, .DLC = 2, .ID = 0x213, .data = {0x00, 0x15}};
//0x221 These frames will/should eventually be migrated to 2 base frames (1 per mux), and then just the relevant bits changed
//0x221 VCFRONT_LVPowerState "Drive"
//VCFRONT_vehiclePowerState VEHICLE_POWER_STATE_DRIVE (Mux0, Counter 15): "cycle_time" 50ms each mux/LVPowerStateIndex, VCFRONT_LVPowerStateChecksum/VCFRONT_LVPowerStateCounter generated via generateMuxFrameCounterChecksum
CAN_frame TESLA_221_DRIVE_Mux0 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x60, 0x55, 0x55, 0x15, 0x54, 0x51, 0xF1, 0xD8}};
//0x221 VCFRONT_LVPowerState "Drive"
//VCFRONT_vehiclePowerState VEHICLE_POWER_STATE_DRIVE (Mux1, Counter 0): "cycle_time" 50ms each mux/LVPowerStateIndex, VCFRONT_LVPowerStateChecksum/VCFRONT_LVPowerStateCounter generated via generateMuxFrameCounterChecksum
CAN_frame TESLA_221_DRIVE_Mux1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x61, 0x05, 0x55, 0x05, 0x00, 0x00, 0x00, 0xE3}};
//0x221 VCFRONT_LVPowerState "Accessory"
//VCFRONT_vehiclePowerState VEHICLE_POWER_STATE_ACCESSORY (Mux0, Counter 15): "cycle_time" 50ms each mux/LVPowerStateIndex, VCFRONT_LVPowerStateChecksum/VCFRONT_LVPowerStateCounter generated via generateMuxFrameCounterChecksum
CAN_frame TESLA_221_ACCESSORY_Mux0 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x40, 0x55, 0x55, 0x05, 0x54, 0x51, 0xF5, 0xAC}};
//0x221 VCFRONT_LVPowerState "Accessory"
//VCFRONT_vehiclePowerState VEHICLE_POWER_STATE_ACCESSORY (Mux1, Counter 0): "cycle_time" 50ms each mux/LVPowerStateIndex, VCFRONT_LVPowerStateChecksum/VCFRONT_LVPowerStateCounter generated via generateMuxFrameCounterChecksum
CAN_frame TESLA_221_ACCESSORY_Mux1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x41, 0x05, 0x55, 0x55, 0x01, 0x00, 0x04, 0x18}};
//0x221 VCFRONT_LVPowerState "Going Down"
//VCFRONT_vehiclePowerState VEHICLE_POWER_STATE_OFF, key parts GOING_DOWN (Mux0, Counter 15): "cycle_time" 50ms each mux/LVPowerStateIndex, VCFRONT_LVPowerStateChecksum/VCFRONT_LVPowerStateCounter generated via generateMuxFrameCounterChecksum
CAN_frame TESLA_221_GOING_DOWN_Mux0 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x00, 0x89, 0x55, 0x06, 0xA4, 0x51, 0xF1, 0xED}};
//0x221 VCFRONT_LVPowerState "Going Down"
//VCFRONT_vehiclePowerState VEHICLE_POWER_STATE_OFF, key parts GOING_DOWN (Mux1, Counter 0): "cycle_time" 50ms each mux/LVPowerStateIndex, VCFRONT_LVPowerStateChecksum/VCFRONT_LVPowerStateCounter generated via generateMuxFrameCounterChecksum
CAN_frame TESLA_221_GOING_DOWN_Mux1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x01, 0x09, 0x55, 0x59, 0x00, 0x00, 0x00, 0xDB}};
//0x221 VCFRONT_LVPowerState "Off"
//VCFRONT_vehiclePowerState VEHICLE_POWER_STATE_OFF, key parts OFF (Mux0, Counter 15): "cycle_time" 50ms each mux/LVPowerStateIndex, VCFRONT_LVPowerStateChecksum/VCFRONT_LVPowerStateCounter generated via generateMuxFrameCounterChecksum
CAN_frame TESLA_221_OFF_Mux0 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0xF1, 0x65}};
//0x221 VCFRONT_LVPowerState "Off"
//VCFRONT_vehiclePowerState VEHICLE_POWER_STATE_OFF, key parts OFF (Mux1, Counter 0): "cycle_time" 50ms each mux/LVPowerStateIndex, VCFRONT_LVPowerStateChecksum/VCFRONT_LVPowerStateCounter generated via generateMuxFrameCounterChecksum
CAN_frame TESLA_221_OFF_Mux1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x221,
.data = {0x01, 0x01, 0x01, 0x50, 0x00, 0x00, 0x00, 0x76}};
//0x229 SCCM_rightStalk: "cycle_time" 100ms, SCCM_rightStalkChecksum/SCCM_rightStalkCounter generated via dedicated generateTESLA_229 function for now
//CRC seemingly related to AUTOSAR ID array... "autosarDataIds": [124,182,240,47,105,163,221,28,86,144,202,9,67,125,183,241] found in Model 3 firmware
CAN_frame TESLA_229 = {.FD = false, .ext_ID = false, .DLC = 3, .ID = 0x229, .data = {0x46, 0x00, 0x00}};
//0x241 VCFRONT_coolant: "cycle_time" 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,
.data = {0x35, 0x34, 0x0C, 0x0F, 0x8F, 0x55, 0x00}};
//0x2D1 VCFRONT_okToUseHighPower: "cycle_time" 100ms
CAN_frame TESLA_2D1 = {.FD = false, .ext_ID = false, .DLC = 2, .ID = 0x2D1, .data = {0xFF, 0x01}};
//0x2E1, 6 muxes
//0x2E1 VCFRONT_status: "cycle_time" 10ms each mux/statusIndex
CAN_frame TESLA_2E1_VEHICLE_AND_RAILS = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2E1,
.data = {0x29, 0x0A, 0x00, 0xFF, 0x0F, 0x00, 0x00, 0x00}};
//{0x29, 0x0A, 0x00, 0xFF, 0x0F, 0x00, 0x00, 0x00} INIT
//{0x29, 0x0A, 0x0D, 0xFF, 0x0F, 0x00, 0x00, 0x00} DRIVE
//{0x29, 0x0A, 0x09, 0xFF, 0x0F, 0x00, 0x00, 0x00} HV_UP_STANDBY
//{0x29, 0x0A, 0x0A, 0xFF, 0x0F, 0x00, 0x00, 0x00} ACCESSORY
//{0x29, 0x0A, 0x06, 0xFF, 0x0F, 0x00, 0x00, 0x00} SLEEP_STANDBY
//0x2E1 VCFRONT_status: "cycle_time" 10ms each mux/statusIndex
CAN_frame TESLA_2E1_HOMELINK = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2E1,
.data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00}};
//0x2E1 VCFRONT_status: "cycle_time" 10ms each mux/statusIndex
CAN_frame TESLA_2E1_REFRIGERANT_SYSTEM = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2E1,
.data = {0x03, 0x6D, 0x99, 0x02, 0x1B, 0x57, 0x00, 0x00}};
//0x2E1 VCFRONT_status: "cycle_time" 10ms each mux/statusIndex
CAN_frame TESLA_2E1_LV_BATTERY_DEBUG = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2E1,
.data = {0xFC, 0x1B, 0xD1, 0x99, 0x9A, 0xD8, 0x09, 0x00}};
//0x2E1 VCFRONT_status: "cycle_time" 10ms each mux/statusIndex
CAN_frame TESLA_2E1_MUX_5 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2E1,
.data = {0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
//0x2E1 VCFRONT_status: "cycle_time" 10ms each mux/statusIndex
CAN_frame TESLA_2E1_BODY_CONTROLS = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x2E1,
.data = {0x08, 0x21, 0x04, 0x6E, 0xA0, 0x88, 0x06, 0x04}};
//0x2E8 EPBR_status: "cycle_time" 100ms
CAN_frame TESLA_2E8 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x129,
.data = {0x21, 0x24, 0x36, 0x5F, 0x00, 0x20, 0xFF, 0x3F}};
//0x612 UDS diagnostic requests - on demand
.ID = 0x2E8,
.data = {0x02, 0x00, 0x10, 0x00, 0x00, 0x80, 0x00, 0x6C}};
//0x284 UI_vehicleModes: "cycle_time" 500ms
CAN_frame TESLA_284 = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x284, .data = {0x10, 0x00, 0x00, 0x00, 0x00}};
//0x293 UI_chassisControl: "cycle_time" 500ms, UI_chassisControlChecksum/UI_chassisControlCounter generated via generateFrameCounterChecksum
CAN_frame TESLA_293 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x293,
.data = {0x01, 0x0C, 0x55, 0x91, 0x55, 0x15, 0x01, 0xF3}};
//0x3A1 VCFRONT_vehicleStatus: "cycle_time" 50ms, VCFRONT_vehicleStatusChecksum/VCFRONT_vehicleStatusCounter eventually need to be generated via generateMuxFrameCounterChecksum
//Looks like 2 muxes, counter at bit 52 width 4 and checksum at bit 56 width 8? Need later software Model3_ETH.compact.json signal file or DBC.
//Migrated to an array until figured out
CAN_frame TESLA_3A1[16] = {
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0xC3, 0xFF, 0xFF, 0xFF, 0x3D, 0x00, 0xD0, 0x01}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0x08, 0x62, 0x0B, 0x18, 0x00, 0x28, 0xE2, 0xCB}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0xC3, 0xFF, 0xFF, 0xFF, 0x3D, 0x00, 0xF0, 0x21}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0x08, 0x62, 0x0B, 0x18, 0x00, 0x28, 0x02, 0xEB}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0xC3, 0xFF, 0xFF, 0xFF, 0x3D, 0x00, 0x10, 0x41}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0x08, 0x62, 0x0B, 0x18, 0x00, 0x28, 0x22, 0x0B}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0xC3, 0xFF, 0xFF, 0xFF, 0x3D, 0x00, 0x30, 0x61}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0x08, 0x62, 0x0B, 0x18, 0x00, 0x28, 0x42, 0x2B}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0xC3, 0xFF, 0xFF, 0xFF, 0x3D, 0x00, 0x50, 0x81}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0x08, 0x62, 0x0B, 0x18, 0x00, 0x28, 0x62, 0x4B}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0xC3, 0xFF, 0xFF, 0xFF, 0x3D, 0x00, 0x70, 0xA1}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0x08, 0x62, 0x0B, 0x18, 0x00, 0x28, 0x82, 0x6B}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0xC3, 0xFF, 0xFF, 0xFF, 0x3D, 0x00, 0x90, 0xC1}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0x08, 0x62, 0x0B, 0x18, 0x00, 0x28, 0xA2, 0x8B}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0xC3, 0xFF, 0xFF, 0xFF, 0x3D, 0x00, 0xB0, 0xE1}},
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x3A1, .data = {0x08, 0x62, 0x0B, 0x18, 0x00, 0x28, 0xC2, 0xAB}}};
//0x313 UI_powertrainControl: "cycle_time" 500ms, UI_powertrainControlChecksum/UI_powertrainControlCounter generated via generateFrameCounterChecksum
CAN_frame TESLA_313 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x313,
.data = {0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x1B}};
//0x321 VCFRONT_sensors: "cycle_time" 1000ms
CAN_frame TESLA_321 = {
.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x321,
.data = {0xEC, 0x71, 0xA7, 0x6E, 0x02, 0x6C, 0x00, 0x04}}; // Last 2 bytes are counter and checksum
//0x333 UI_chargeRequest: "cycle_time" 500ms, UI_chargeTerminationPct value = 900 [bit 16, width 10, scale 0.1, min 25, max 100]
CAN_frame TESLA_333 = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x333, .data = {0x84, 0x30, 0x84, 0x07, 0x02}};
//0x334 UI request: "cycle_time" 500ms, initial frame car sends
CAN_frame TESLA_334_INITIAL = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x334,
.data = {0x3F, 0x3F, 0xC8, 0x00, 0xE2, 0x3F, 0x80, 0x1E}};
//0x334 UI request: "cycle_time" 500ms, generated via generateFrameCounterChecksum
CAN_frame TESLA_334 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x334,
.data = {0x3F, 0x3F, 0x00, 0x0F, 0xE2, 0x3F, 0x90, 0x75}};
//0x3B3 UI_vehicleControl2: "cycle_time" 500ms
CAN_frame TESLA_3B3 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x3B3,
.data = {0x90, 0x80, 0x05, 0x08, 0x00, 0x00, 0x00, 0x01}};
//0x39D IBST_status: "cycle_time" 50ms, IBST_statusChecksum/IBST_statusCounter generated via generateFrameCounterChecksum
CAN_frame TESLA_39D = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x39D, .data = {0xE1, 0x59, 0xC1, 0x27, 0x00}};
//0x3C2 VCLEFT_switchStatus (Mux0, initial frame car sends): "cycle_time" 50ms, sent once
CAN_frame TESLA_3C2_INITIAL = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x3C2,
.data = {0x00, 0x55, 0x55, 0x55, 0x00, 0x00, 0x5A, 0x05}};
//0x3C2 VCLEFT_switchStatus (Mux0): "cycle_time" 50ms each mux/SwitchStatusIndex
CAN_frame TESLA_3C2_Mux0 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x3C2,
.data = {0x00, 0x55, 0x55, 0x55, 0x00, 0x00, 0x5A, 0x45}};
//0x3C2 VCLEFT_switchStatus (Mux1): "cycle_time" 50ms each mux/SwitchStatusIndex
CAN_frame TESLA_3C2_Mux1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x3C2,
.data = {0x29, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
//0x504 Initially sent
CAN_frame TESLA_504_INITIAL = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x504,
.data = {0x00, 0x1B, 0x06, 0x03, 0x00, 0x01, 0x00, 0x01}};
//0x55A Unknown but always sent: "cycle_time" 500ms
CAN_frame TESLA_55A = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x55A,
.data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
//0x7FF GTW_carConfig: "cycle_time" 100ms each mux/carConfigMultiplexer (UK/RHD)
CAN_frame TESLA_7FF_Mux1 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x01, 0x49, 0x42, 0x47, 0x00, 0x03, 0x15, 0x01}};
//0x7FF GTW_carConfig: "cycle_time" 100ms each mux/carConfigMultiplexer
CAN_frame TESLA_7FF_Mux2 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x02, 0x66, 0x32, 0x24, 0x04, 0x49, 0x95, 0x82}};
//0x7FF GTW_carConfig: "cycle_time" 100ms each mux/carConfigMultiplexer (EU/Long Range)
CAN_frame TESLA_7FF_Mux3 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x03, 0x01, 0x08, 0x48, 0x01, 0x00, 0x00, 0x12}};
//0x7FF GTW_carConfig: "cycle_time" 100ms each mux/carConfigMultiplexer
CAN_frame TESLA_7FF_Mux4 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x04, 0x73, 0x03, 0x67, 0x5C, 0x00, 0x00, 0x00}};
//0x7FF GTW_carConfig: "cycle_time" 100ms each mux/carConfigMultiplexer
CAN_frame TESLA_7FF_Mux5 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x05, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}};
//0x7FF GTW_carConfig: "cycle_time" 100ms each mux/carConfigMultiplexer - later firmware has muxes 6 & 7, needed?
CAN_frame TESLA_7FF_Mux6 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x06, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0xD0}};
//0x7FF GTW_carConfig: "cycle_time" 100ms each mux/carConfigMultiplexer - later firmware has muxes 6 & 7, needed?
CAN_frame TESLA_7FF_Mux7 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x7FF,
.data = {0x07, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00}};
//0x722 BMS_bmbKeepAlive: "cycle_time" 100ms, should only be sent when testing packs or diagnosing problems
CAN_frame TESLA_722 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x722,
.data = {0x02, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}};
//0x25D CP_status: "cycle_time" 100ms, stops some cpMia errors, but not necessary for standalone pack operation so not used/necessary. Note CP_type for different regions, the below has "IEC_CCS"
CAN_frame TESLA_25D = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x25D,
.data = {0x37, 0x41, 0x01, 0x16, 0x08, 0x00, 0x00, 0x00}};
//0x602 BMS UDS diagnostic request: on demand
CAN_frame TESLA_602 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x602,
.data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}};
.data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}}; // Define initial UDS request
//0x610 BMS Query UDS request: on demand
CAN_frame TESLA_610 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.ID = 0x610,
.data = {0x02, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; // Define initial UDS request
uint8_t stateMachineClearIsolationFault = 0xFF;
uint8_t stateMachineBMSReset = 0xFF;
uint8_t stateMachineBMSQuery = 0xFF;
uint16_t sendContactorClosingMessagesStill = 300;
uint16_t battery_cell_max_v = 3300;
uint16_t battery_cell_min_v = 3300;
@ -162,8 +522,8 @@ class TeslaBattery : public CanBattery {
//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;
uint16_t BMS_max_voltage = 0;
uint16_t 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
@ -198,7 +558,7 @@ class TeslaBattery : public CanBattery {
uint8_t battery_packContNegativeState = 0;
uint8_t battery_packContPositiveState = 0;
uint8_t battery_packContactorSetState = 0;
bool battery_packCtrsClosingAllowed = false; // Change to bool
bool battery_packCtrsClosingBlocked = 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
@ -216,48 +576,69 @@ class TeslaBattery : public CanBattery {
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
//0x72A: BMS_serialNumber
uint8_t battery_serialNumber[14] = {0}; // Stores raw HEX values for ASCII chars
bool parsed_battery_serialNumber = false;
char* battery_manufactureDate; // YYYY-MM-DD\0
//Via UDS
uint8_t battery_partNumber[12] = {0}; //stores raw HEX values for ASCII chars
bool parsed_battery_partNumber = false;
//Via UDS
//static uint8_t BMS_partNumber[12] = {0}; //stores raw HEX values for ASCII chars
//static bool parsed_BMS_partNumber = false;
//0x300: BMS_info
uint16_t BMS_info_buildConfigId = 0;
uint16_t BMS_info_hardwareId = 0;
uint16_t BMS_info_componentId = 0;
uint8_t BMS_info_pcbaId = 0;
uint8_t BMS_info_assemblyId = 0;
uint16_t BMS_info_usageId = 0;
uint16_t BMS_info_subUsageId = 0;
uint8_t BMS_info_platformType = 0;
uint32_t BMS_info_appCrc = 0;
uint64_t BMS_info_bootGitHash = 0;
uint8_t BMS_info_bootUdsProtoVersion = 0;
uint32_t BMS_info_bootCrc = 0;
//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;
bool BMS_hvacPowerRequest = false; //Change to bool
bool BMS_notEnoughPowerForDrive = false; //Change to bool
bool BMS_notEnoughPowerForSupport = false; //Change to bool
bool BMS_preconditionAllowed = false; //Change to bool
bool BMS_updateAllowed = false; //Change to bool
bool BMS_activeHeatingWorthwhile = false; //Change to bool
bool BMS_cpMiaOnHvs = false; //Change to bool
uint8_t BMS_contactorState = 0;
uint8_t BMS_state = 0;
uint8_t BMS_hvState = 0;
uint16_t BMS_isolationResistance = 0;
bool BMS_chargeRequest = false; //Change to bool
bool BMS_keepWarmRequest = false; //Change to bool
uint8_t BMS_uiChargeStatus = 0;
bool BMS_diLimpRequest = false; //Change to bool
bool BMS_okToShipByAir = false; //Change to bool
bool BMS_okToShipByLand = false; //Change to bool
uint32_t BMS_chgPowerAvailable = 0;
uint8_t BMS_chargeRetryCount = 0;
bool BMS_pcsPwmEnabled = false; //Change to bool
bool BMS_ecuLogUploadRequest = false; //Change to bool
uint8_t 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;
uint8_t PCS_dcdcPrechargeStatus = 0;
uint8_t PCS_dcdc12VSupportStatus = 0;
uint8_t PCS_dcdcHvBusDischargeStatus = 0;
uint16_t PCS_dcdcMainState = 0;
uint8_t PCS_dcdcSubState = 0;
bool PCS_dcdcFaulted = false; //Change to bool
bool PCS_dcdcOutputIsLimited = false; //Change to bool
uint32_t PCS_dcdcMaxOutputCurrentAllowed = 0;
uint8_t PCS_dcdcPrechargeRtyCnt = 0;
uint8_t PCS_dcdc12VSupportRtyCnt = 0;
uint8_t PCS_dcdcDischargeRtyCnt = 0;
uint8_t PCS_dcdcPwmEnableLine = 0;
uint8_t PCS_dcdcSupportingFixedLvTarget = 0;
uint8_t PCS_ecuLogUploadRequest = 0;
uint8_t PCS_dcdcPrechargeRestartCnt = 0;
uint8_t PCS_dcdcInitialPrechargeSubState = 0;
//0x312: 786 BMS_thermalStatus
uint16_t BMS_powerDissipation = 0;
uint16_t BMS_flowRequest = 0;
@ -268,6 +649,22 @@ class TeslaBattery : public CanBattery {
uint16_t BMS_packTMax = 0;
bool BMS_pcsNoFlowRequest = false;
bool BMS_noFlowRequest = false;
//0x3C4: PCS_info
uint8_t PCS_partNumber[12] = {0}; //stores raw HEX values for ASCII chars
bool parsed_PCS_partNumber = false;
uint16_t PCS_info_buildConfigId = 0;
uint16_t PCS_info_hardwareId = 0;
uint16_t PCS_info_componentId = 0;
uint8_t PCS_info_pcbaId = 0;
uint8_t PCS_info_assemblyId = 0;
uint16_t PCS_info_usageId = 0;
uint16_t PCS_info_subUsageId = 0;
uint8_t PCS_info_platformType = 0;
uint32_t PCS_info_appCrc = 0;
uint32_t PCS_info_cpu2AppCrc = 0;
uint64_t PCS_info_bootGitHash = 0;
uint8_t PCS_info_bootUdsProtoVersion = 0;
uint32_t PCS_info_bootCrc = 0;
//0x2A4; 676 PCS_thermalStatus
int16_t PCS_chgPhATemp = 0;
int16_t PCS_chgPhBTemp = 0;
@ -330,6 +727,18 @@ class TeslaBattery : public CanBattery {
bool HVP_shuntRefVoltageMismatch = false; //Change to bool
bool HVP_shuntThermistorMia = false; //Change to bool
bool HVP_shuntHwMia = false; //Change to bool
uint16_t HVP_info_buildConfigId = 0;
uint16_t HVP_info_hardwareId = 0;
uint16_t HVP_info_componentId = 0;
uint8_t HVP_info_pcbaId = 0;
uint8_t HVP_info_assemblyId = 0;
uint16_t HVP_info_usageId = 0;
uint16_t HVP_info_subUsageId = 0;
uint8_t HVP_info_platformType = 0;
uint32_t HVP_info_appCrc = 0;
uint64_t HVP_info_bootGitHash = 0;
uint8_t HVP_info_bootUdsProtoVersion = 0;
uint32_t HVP_info_bootCrc = 0;
int16_t HVP_dcLinkVoltage = 0;
int16_t HVP_packVoltage = 0;
int16_t HVP_fcLinkVoltage = 0;
@ -411,104 +820,105 @@ class TeslaBattery : public CanBattery {
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;
uint8_t BMS_matrixIndex = 0; // Changed to bool
bool BMS_a061_robinBrickOverVoltage = false;
bool BMS_a062_SW_BrickV_Imbalance = false;
bool BMS_a063_SW_ChargePort_Fault = false;
bool BMS_a064_SW_SOC_Imbalance = false;
bool BMS_a127_SW_shunt_SNA = false;
bool BMS_a128_SW_shunt_MIA = false;
bool BMS_a069_SW_Low_Power = false;
bool BMS_a130_IO_CAN_Error = false;
bool BMS_a071_SW_SM_TransCon_Not_Met = false;
bool BMS_a132_HW_BMB_OTP_Uncorrctbl = false;
bool BMS_a134_SW_Delayed_Ctr_Off = false;
bool BMS_a075_SW_Chg_Disable_Failure = false;
bool BMS_a076_SW_Dch_While_Charging = false;
bool BMS_a017_SW_Brick_OV = false;
bool BMS_a018_SW_Brick_UV = false;
bool BMS_a019_SW_Module_OT = false;
bool BMS_a021_SW_Dr_Limits_Regulation = false;
bool BMS_a022_SW_Over_Current = false;
bool BMS_a023_SW_Stack_OV = false;
bool BMS_a024_SW_Islanded_Brick = false;
bool BMS_a025_SW_PwrBalance_Anomaly = false;
bool BMS_a026_SW_HFCurrent_Anomaly = false;
bool BMS_a087_SW_Feim_Test_Blocked = false;
bool BMS_a088_SW_VcFront_MIA_InDrive = false;
bool BMS_a089_SW_VcFront_MIA = false;
bool BMS_a090_SW_Gateway_MIA = false;
bool BMS_a091_SW_ChargePort_MIA = false;
bool BMS_a092_SW_ChargePort_Mia_On_Hv = false;
bool BMS_a034_SW_Passive_Isolation = false;
bool BMS_a035_SW_Isolation = false;
bool BMS_a036_SW_HvpHvilFault = false;
bool BMS_a037_SW_Flood_Port_Open = false;
bool BMS_a158_SW_HVP_HVI_Comms = false;
bool BMS_a039_SW_DC_Link_Over_Voltage = false;
bool BMS_a041_SW_Power_On_Reset = false;
bool BMS_a042_SW_MPU_Error = false;
bool BMS_a043_SW_Watch_Dog_Reset = false;
bool BMS_a044_SW_Assertion = false;
bool BMS_a045_SW_Exception = false;
bool BMS_a046_SW_Task_Stack_Usage = false;
bool BMS_a047_SW_Task_Stack_Overflow = false;
bool BMS_a048_SW_Log_Upload_Request = false;
bool BMS_a169_SW_FC_Pack_Weld = false;
bool BMS_a050_SW_Brick_Voltage_MIA = false;
bool BMS_a051_SW_HVC_Vref_Bad = false;
bool BMS_a052_SW_PCS_MIA = false;
bool BMS_a053_SW_ThermalModel_Sanity = false;
bool BMS_a054_SW_Ver_Supply_Fault = false;
bool BMS_a176_SW_GracefulPowerOff = false;
bool BMS_a059_SW_Pack_Voltage_Sensing = false;
bool BMS_a060_SW_Leakage_Test_Failure = false;
bool BMS_a077_SW_Charger_Regulation = false;
bool BMS_a081_SW_Ctr_Close_Blocked = false;
bool BMS_a082_SW_Ctr_Force_Open = false;
bool BMS_a083_SW_Ctr_Close_Failure = false;
bool BMS_a084_SW_Sleep_Wake_Aborted = false;
bool BMS_a094_SW_Drive_Inverter_MIA = false;
bool BMS_a099_SW_BMB_Communication = false;
bool BMS_a105_SW_One_Module_Tsense = false;
bool BMS_a106_SW_All_Module_Tsense = false;
bool BMS_a107_SW_Stack_Voltage_MIA = false;
bool BMS_a121_SW_NVRAM_Config_Error = false;
bool BMS_a122_SW_BMS_Therm_Irrational = false;
bool BMS_a123_SW_Internal_Isolation = false;
bool BMS_a129_SW_VSH_Failure = false;
bool BMS_a131_Bleed_FET_Failure = false;
bool BMS_a136_SW_Module_OT_Warning = false;
bool BMS_a137_SW_Brick_UV_Warning = false;
bool BMS_a138_SW_Brick_OV_Warning = false;
bool BMS_a139_SW_DC_Link_V_Irrational = false;
bool BMS_a141_SW_BMB_Status_Warning = false;
bool BMS_a144_Hvp_Config_Mismatch = false;
bool BMS_a145_SW_SOC_Change = false;
bool BMS_a146_SW_Brick_Overdischarged = false;
bool BMS_a149_SW_Missing_Config_Block = false;
bool BMS_a151_SW_external_isolation = false;
bool BMS_a156_SW_BMB_Vref_bad = false;
bool BMS_a157_SW_HVP_HVS_Comms = false;
bool BMS_a159_SW_HVP_ECU_Error = false;
bool BMS_a161_SW_DI_Open_Request = false;
bool BMS_a162_SW_No_Power_For_Support = false;
bool BMS_a163_SW_Contactor_Mismatch = false;
bool BMS_a164_SW_Uncontrolled_Regen = false;
bool BMS_a165_SW_Pack_Partial_Weld = false;
bool BMS_a166_SW_Pack_Full_Weld = false;
bool BMS_a167_SW_FC_Partial_Weld = false;
bool BMS_a168_SW_FC_Full_Weld = false;
bool BMS_a170_SW_Limp_Mode = false;
bool BMS_a171_SW_Stack_Voltage_Sense = false;
bool BMS_a174_SW_Charge_Failure = false;
bool BMS_a179_SW_Hvp_12V_Fault = false;
bool BMS_a180_SW_ECU_reset_blocked = false;
};
class TeslaModel3YBattery : public TeslaBattery {
public:
TeslaModel3YBattery() {
TeslaModel3YBattery(battery_chemistry_enum chemistry) {
datalayer.battery.info.chemistry = chemistry;
#ifdef EXP_TESLA_BMS_DIGITAL_HVIL
operate_contactors = true;
#endif

View file

@ -39,8 +39,8 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer {
float packMass = static_cast<float>(datalayer_extended.tesla.battery_packMass);
float platformMaxBusVoltage =
static_cast<float>(datalayer_extended.tesla.battery_platformMaxBusVoltage) * 0.1 + 375;
float bms_min_voltage = static_cast<float>(datalayer_extended.tesla.battery_bms_min_voltage) * 0.01 * 2;
float bms_max_voltage = static_cast<float>(datalayer_extended.tesla.battery_bms_max_voltage) * 0.01 * 2;
float bms_min_voltage = static_cast<float>(datalayer_extended.tesla.BMS_min_voltage) * 0.01 * 2;
float bms_max_voltage = static_cast<float>(datalayer_extended.tesla.BMS_max_voltage) * 0.01 * 2;
float max_charge_current = static_cast<float>(datalayer_extended.tesla.battery_max_charge_current);
float max_discharge_current = static_cast<float>(datalayer_extended.tesla.battery_max_discharge_current);
float soc_ave = static_cast<float>(datalayer_extended.tesla.battery_soc_ave) * 0.1;
@ -51,9 +51,9 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer {
float BrickVoltageMin = static_cast<float>(datalayer_extended.tesla.battery_BrickVoltageMin) * 0.002;
float BrickModelTMax = static_cast<float>(datalayer_extended.tesla.battery_BrickModelTMax) * 0.5 - 40;
float BrickModelTMin = static_cast<float>(datalayer_extended.tesla.battery_BrickModelTMin) * 0.5 - 40;
float isolationResistance = static_cast<float>(datalayer_extended.tesla.battery_BMS_isolationResistance) * 10;
float isolationResistance = static_cast<float>(datalayer_extended.tesla.BMS_isolationResistance) * 10;
float PCS_dcdcMaxOutputCurrentAllowed =
static_cast<float>(datalayer_extended.tesla.battery_PCS_dcdcMaxOutputCurrentAllowed) * 0.1;
static_cast<float>(datalayer_extended.tesla.PCS_dcdcMaxOutputCurrentAllowed) * 0.1;
float PCS_dcdcTemp = static_cast<float>(datalayer_extended.tesla.PCS_dcdcTemp) * 0.1 + 40;
float PCS_ambientTemp = static_cast<float>(datalayer_extended.tesla.PCS_ambientTemp) * 0.1 + 40;
float PCS_chgPhATemp = static_cast<float>(datalayer_extended.tesla.PCS_chgPhATemp) * 0.1 + 40;
@ -128,7 +128,7 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer {
static const char* contactorText[] = {"UNKNOWN(0)", "OPEN", "CLOSING", "BLOCKED", "OPENING",
"CLOSED", "UNKNOWN(6)", "WELDED", "POS_CL", "NEG_CL",
"UNKNOWN(10)", "UNKNOWN(11)", "UNKNOWN(12)"};
static const char* hvilStatusState[] = {"NOT Ok",
static const char* hvilStatusState[] = {"UNKNOWN or CONTACTORS OPEN",
"STATUS_OK",
"CURRENT_SOURCE_FAULT",
"INTERNAL_OPEN_FAULT",
@ -183,15 +183,46 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer {
static const char* noYes[] = {"No", "Yes"};
static const char* Fault[] = {"NOT_ACTIVE", "ACTIVE"};
//0x20A 522 HVP_contatorState
content += "<h4>Contactor Status: " + String(contactorText[datalayer_extended.tesla.status_contactor]) + "</h4>";
content += "<h4>HVIL: " + String(hvilStatusState[datalayer_extended.tesla.hvil_status]) + "</h4>";
//Main battery info
char readableBatterySerialNumber[15]; // One extra space for null terminator
memcpy(readableBatterySerialNumber, datalayer_extended.tesla.battery_serialNumber,
sizeof(datalayer_extended.tesla.battery_serialNumber));
readableBatterySerialNumber[14] = '\0'; // Null terminate the string
content += "<h4>Battery Serial Number: " + String(readableBatterySerialNumber) + "</h4>";
char readableBatteryPartNumber[13]; // One extra space for null terminator
memcpy(readableBatteryPartNumber, datalayer_extended.tesla.battery_partNumber,
sizeof(datalayer_extended.tesla.battery_partNumber));
readableBatteryPartNumber[12] = '\0'; // Null terminate the string
content += "<h4>Battery Part Number: " + String(readableBatteryPartNumber) + "</h4>";
//0x3C4 PCS_info
char readablePCSPartNumber[13]; // One extra space for null terminator
memcpy(readablePCSPartNumber, datalayer_extended.tesla.PCS_partNumber,
sizeof(datalayer_extended.tesla.PCS_partNumber));
readablePCSPartNumber[13] = '\0'; // Null terminate the string
content += "<h4>PCS Part Number: " + String(readablePCSPartNumber) + "</h4>";
content += "<h4>Battery Manufacture Date: " + String(datalayer_extended.tesla.battery_manufactureDate) + "</h4>";
content += "<h4>Battery Pack Mass: " + String(packMass) + " KG</h4>";
//0x3D2 978 BMS_kwhCounter
content += "<h4>Battery Total Discharge: " + String(total_discharge) + " kWh</h4>";
content += "<h4>Battery Total Charge: " + String(total_charge) + " kWh</h4>";
//0x20A 522 HVP_contactorState + HVIL
//content += "<h4>HVIL Fault: " + String(noYes[datalayer_extended.tesla.BMS_hvilFault]) + "</h4>";
content += "<h4>HVIL Status: " + String(hvilStatusState[datalayer_extended.tesla.hvil_status]) + "</h4>";
content +=
"<h4>Negative contactor: " + String(contactorState[datalayer_extended.tesla.packContNegativeState]) + "</h4>";
"<h4>HVP Contactor State: " + String(contactorText[datalayer_extended.tesla.packContactorSetState]) + "</h4>";
content +=
"<h4>Positive contactor: " + String(contactorState[datalayer_extended.tesla.packContPositiveState]) + "</h4>";
content += "<h4>Closing allowed?: " + String(noYes[datalayer_extended.tesla.packCtrsClosingAllowed]) + "</h4>";
content += "<h4>Pyrotest in Progress: " + String(noYes[datalayer_extended.tesla.pyroTestInProgress]) + "</h4>";
"<h4>BMS Contactor State: " + String(BMS_contactorState[datalayer_extended.tesla.BMS_contactorState]) + "</h4>";
content +=
"<h4>Negative Contactor: " + String(contactorState[datalayer_extended.tesla.packContNegativeState]) + "</h4>";
content +=
"<h4>Positive Contactor: " + String(contactorState[datalayer_extended.tesla.packContPositiveState]) + "</h4>";
if (datalayer_extended.tesla.packContactorSetState == 5) { //Closed
content += "<h4>Closing blocked: " + String(noYes[datalayer_extended.tesla.packCtrsClosingBlocked]) +
" (already CLOSED)</h4>";
} else {
content += "<h4>Closing blocked: " + String(noYes[datalayer_extended.tesla.packCtrsClosingBlocked]) + "</h4>";
}
content += "<h4>Pyrotest in progress: " + String(noYes[datalayer_extended.tesla.pyroTestInProgress]) + "</h4>";
content += "<h4>Contactors Open Now Requested: " +
String(noYes[datalayer_extended.tesla.battery_packCtrsOpenNowRequested]) + "</h4>";
content +=
@ -204,21 +235,16 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer {
content +=
"<h4>DC Link Allowed to Energize: " + String(noYes[datalayer_extended.tesla.battery_dcLinkAllowedToEnergize]) +
"</h4>";
char readableSerialNumber[15]; // One extra space for null terminator
memcpy(readableSerialNumber, datalayer_extended.tesla.BMS_SerialNumber,
sizeof(datalayer_extended.tesla.BMS_SerialNumber));
readableSerialNumber[14] = '\0'; // Null terminate the string
content += "<h4>BMS Serial number: " + String(readableSerialNumber) + "</h4>";
// Comment what data you would like to display, order can be changed.
//0x352 850 BMS_energyStatus
if (datalayer_extended.tesla.BMS352_mux == false) {
content += "<h3>BMS 0x352 w/o mux</h3>"; //if using older BMS <2021 and comment 0x352 without MUX
content += "<h4>Calculated SOH: " + String(nominal_full_pack_energy * 100 / beginning_of_life) + "</h4>";
content += "<h4>Nominal Full Pack Energy: " + String(nominal_full_pack_energy) + " KWh</h4>";
content += "<h4>Nominal Energy Remaining: " + String(nominal_energy_remaining) + " KWh</h4>";
content += "<h4>Ideal Energy Remaining: " + String(ideal_energy_remaining) + " KWh</h4>";
content += "<h4>Energy to Charge Complete: " + String(energy_to_charge_complete) + " KWh</h4>";
content += "<h4>Energy Buffer: " + String(energy_buffer) + " KWh</h4>";
content += "<h4>Nominal Full Pack Energy: " + String(nominal_full_pack_energy) + " kWh</h4>";
content += "<h4>Nominal Energy Remaining: " + String(nominal_energy_remaining) + " kWh</h4>";
content += "<h4>Ideal Energy Remaining: " + String(ideal_energy_remaining) + " kWh</h4>";
content += "<h4>Energy to Charge Complete: " + String(energy_to_charge_complete) + " kWh</h4>";
content += "<h4>Energy Buffer: " + String(energy_buffer) + " kWh</h4>";
content += "<h4>Full Charge Complete: " + String(noYes[datalayer_extended.tesla.battery_full_charge_complete]) +
"</h4>"; //bool
}
@ -226,19 +252,26 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer {
if (datalayer_extended.tesla.BMS352_mux == true) {
content += "<h3>BMS 0x352 w/ mux</h3>"; //if using newer BMS >2021 and comment 0x352 with MUX
content += "<h4>Calculated SOH: " + String(nominal_full_pack_energy_m0 * 100 / beginning_of_life) + "</h4>";
content += "<h4>Nominal Full Pack Energy: " + String(nominal_full_pack_energy_m0) + " KWh</h4>";
content += "<h4>Nominal Energy Remaining: " + String(nominal_energy_remaining_m0) + " KWh</h4>";
content += "<h4>Ideal Energy Remaining: " + String(ideal_energy_remaining_m0) + " KWh</h4>";
content += "<h4>Energy to Charge Complete: " + String(energy_to_charge_complete_m1) + " KWh</h4>";
content += "<h4>Energy Buffer: " + String(energy_buffer_m1) + " KWh</h4>";
content += "<h4>Expected Energy Remaining: " + String(expected_energy_remaining_m1) + " KWh</h4>";
content += "<h4>Nominal Full Pack Energy: " + String(nominal_full_pack_energy_m0) + " kWh</h4>";
content += "<h4>Nominal Energy Remaining: " + String(nominal_energy_remaining_m0) + " kWh</h4>";
content += "<h4>Ideal Energy Remaining: " + String(ideal_energy_remaining_m0) + " kWh</h4>";
content += "<h4>Energy to Charge Complete: " + String(energy_to_charge_complete_m1) + " kWh</h4>";
content += "<h4>Energy Buffer: " + String(energy_buffer_m1) + " kWh</h4>";
content += "<h4>Expected Energy Remaining: " + String(expected_energy_remaining_m1) + " kWh</h4>";
content += "<h4>Fully Charged: " + String(noYes[datalayer_extended.tesla.battery_fully_charged]) + "</h4>";
}
//0x3D2 978 BMS_kwhCounter
content += "<h4>Total Discharge: " + String(total_discharge) + " KWh</h4>";
content += "<h4>Total Charge: " + String(total_charge) + " KWh</h4>";
//0x212 530 BMS_status
content += "<h4>Isolation Resistance: " + String(isolationResistance) + " kOhms</h4>";
content += "<h4>BMS State: " + String(BMS_state[datalayer_extended.tesla.BMS_state]) + "</h4>";
content += "<h4>BMS HV State: " + String(BMS_hvState[datalayer_extended.tesla.BMS_hvState]) + "</h4>";
content += "<h4>BMS UI Charge Status: " + String(BMS_uiChargeStatus[datalayer_extended.tesla.BMS_uiChargeStatus]) +
"</h4>";
content += "<h4>BMS_buildConfigId: " + String(datalayer_extended.tesla.BMS_info_buildConfigId) + "</h4>";
content += "<h4>BMS_hardwareId: " + String(datalayer_extended.tesla.BMS_info_hardwareId) + "</h4>";
content += "<h4>BMS_componentId: " + String(datalayer_extended.tesla.BMS_info_componentId) + "</h4>";
content += "<h4>BMS PCS PWM Enabled: " + String(Fault[datalayer_extended.tesla.BMS_pcsPwmEnabled]) + "</h4>";
//0x292 658 BMS_socStates
content += "<h4>Battery Beginning of Life: " + String(beginning_of_life) + " KWh</h4>";
content += "<h4>Battery Beginning of Life: " + String(beginning_of_life) + " kWh</h4>";
content += "<h4>Battery SOC UI: " + String(soc_ui) + " </h4>";
content += "<h4>Battery SOC Ave: " + String(soc_ave) + " </h4>";
content += "<h4>Battery SOC Max: " + String(soc_max) + " </h4>";
@ -252,7 +285,7 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer {
//content += "<h4>packConfigMultiplexer: " + String(datalayer_extended.tesla.battery_packConfigMultiplexer) + "</h4>"; // Not giving useable data
//content += "<h4>moduleType: " + String(datalayer_extended.tesla.battery_moduleType) + "</h4>"; // Not giving useable data
//content += "<h4>reserveConfig: " + String(datalayer_extended.tesla.battery_reservedConfig) + "</h4>"; // Not giving useable data
content += "<h4>Battery Pack Mass: " + String(packMass) + " KG</h4>";
//content += "<h4>Battery Pack Mass: " + String(packMass) + " KG</h4>";
content += "<h4>Platform Max Bus Voltage: " + String(platformMaxBusVoltage) + " V</h4>";
//0x2D2 722 BMSVAlimits
content += "<h4>BMS Min Voltage: " + String(bms_min_voltage) + " V</h4>";
@ -273,25 +306,14 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer {
content += "<h4>PCS Chg PhB Temp: " + String(PCS_chgPhBTemp) + " DegC</h4>";
content += "<h4>PCS Chg PhC Temp: " + String(PCS_chgPhCTemp) + " DegC</h4>";
//0x252 594 BMS_powerAvailable
content += "<h4>Max Regen Power: " + String(BMS_maxRegenPower) + " KW</h4>";
content += "<h4>Max Discharge Power: " + String(BMS_maxDischargePower) + " KW</h4>";
//content += "<h4>Max Stationary Heat Power: " + String(BMS_maxStationaryHeatPower) + " KWh</h4>"; // Not giving useable data
//content += "<h4>HVAC Power Budget: " + String(BMS_hvacPowerBudget) + " KW</h4>"; // Not giving useable data
content += "<h4>Max Regen Power: " + String(BMS_maxRegenPower) + " kW</h4>";
content += "<h4>Max Discharge Power: " + String(BMS_maxDischargePower) + " kW</h4>";
//content += "<h4>Max Stationary Heat Power: " + String(BMS_maxStationaryHeatPower) + " kWh</h4>"; // Not giving useable data
//content += "<h4>HVAC Power Budget: " + String(BMS_hvacPowerBudget) + " kW</h4>"; // Not giving useable data
//content += "<h4>Not Enough Power For Heat Pump: " + String(noYes[datalayer_extended.tesla.BMS_notEnoughPowerForHeatPump]) + "</h4>"; // Not giving useable data
content +=
"<h4>Power Limit State: " + String(BMS_powerLimitState[datalayer_extended.tesla.BMS_powerLimitState]) + "</h4>";
//content += "<h4>Inverter TQF: " + String(datalayer_extended.tesla.BMS_inverterTQF) + "</h4>"; // Not giving useable data
//0x212 530 BMS_status
content += "<h4>Isolation Resistance: " + String(isolationResistance) + " kOhms</h4>";
content +=
"<h4>BMS Contactor State: " + String(BMS_contactorState[datalayer_extended.tesla.battery_BMS_contactorState]) +
"</h4>";
content += "<h4>BMS State: " + String(BMS_state[datalayer_extended.tesla.battery_BMS_state]) + "</h4>";
content += "<h4>BMS HV State: " + String(BMS_hvState[datalayer_extended.tesla.battery_BMS_hvState]) + "</h4>";
content += "<h4>BMS UI Charge Status: " + String(BMS_uiChargeStatus[datalayer_extended.tesla.battery_BMS_hvState]) +
"</h4>";
content +=
"<h4>BMS PCS PWM Enabled: " + String(Fault[datalayer_extended.tesla.battery_BMS_pcsPwmEnabled]) + "</h4>";
//0x312 786 BMS_thermalStatus
content += "<h4>Power Dissipation: " + String(BMS_powerDissipation) + " kW</h4>";
content += "<h4>Flow Request: " + String(BMS_flowRequest) + " LPM</h4>";
@ -304,36 +326,34 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer {
content += "<h4>BMS No Flow Request: " + String(Fault[datalayer_extended.tesla.BMS_noFlowRequest]) + "</h4>";
//0x224 548 PCS_dcdcStatus
content +=
"<h4>Precharge Status: " + String(PCS_dcdcStatus[datalayer_extended.tesla.battery_PCS_dcdcPrechargeStatus]) +
"</h4>";
content +=
"<h4>12V Support Status: " + String(PCS_dcdcStatus[datalayer_extended.tesla.battery_PCS_dcdc12VSupportStatus]) +
"</h4>";
"<h4>Precharge Status: " + String(PCS_dcdcStatus[datalayer_extended.tesla.PCS_dcdcPrechargeStatus]) + "</h4>";
content += "<h4>12V Support Status: " + String(PCS_dcdcStatus[datalayer_extended.tesla.PCS_dcdc12VSupportStatus]) +
"</h4>";
content += "<h4>HV Bus Discharge Status: " +
String(PCS_dcdcStatus[datalayer_extended.tesla.battery_PCS_dcdcHvBusDischargeStatus]) + "</h4>";
content +=
"<h4>Main State: " + String(PCS_dcdcMainState[datalayer_extended.tesla.battery_PCS_dcdcMainState]) + "</h4>";
content +=
"<h4>Sub State: " + String(PCS_dcdcSubState[datalayer_extended.tesla.battery_PCS_dcdcSubState]) + "</h4>";
content += "<h4>PCS Faulted: " + String(Fault[datalayer_extended.tesla.battery_PCS_dcdcFaulted]) + "</h4>";
content +=
"<h4>Output Is Limited: " + String(Fault[datalayer_extended.tesla.battery_PCS_dcdcOutputIsLimited]) + "</h4>";
String(PCS_dcdcStatus[datalayer_extended.tesla.PCS_dcdcHvBusDischargeStatus]) + "</h4>";
content += "<h4>Main State: " + String(PCS_dcdcMainState[datalayer_extended.tesla.PCS_dcdcMainState]) + "</h4>";
content += "<h4>Sub State: " + String(PCS_dcdcSubState[datalayer_extended.tesla.PCS_dcdcSubState]) + "</h4>";
content += "<h4>PCS Faulted: " + String(Fault[datalayer_extended.tesla.PCS_dcdcFaulted]) + "</h4>";
content += "<h4>Output Is Limited: " + String(Fault[datalayer_extended.tesla.PCS_dcdcOutputIsLimited]) + "</h4>";
content += "<h4>Max Output Current Allowed: " + String(PCS_dcdcMaxOutputCurrentAllowed) + " A</h4>";
content += "<h4>Precharge Rty Cnt: " + String(falseTrue[datalayer_extended.tesla.battery_PCS_dcdcPrechargeRtyCnt]) +
"</h4>";
content +=
"<h4>12V Support Rty Cnt: " + String(falseTrue[datalayer_extended.tesla.battery_PCS_dcdc12VSupportRtyCnt]) +
"<h4>Precharge Rty Cnt: " + String(falseTrue[datalayer_extended.tesla.PCS_dcdcPrechargeRtyCnt]) + "</h4>";
content +=
"<h4>12V Support Rty Cnt: " + String(falseTrue[datalayer_extended.tesla.PCS_dcdc12VSupportRtyCnt]) + "</h4>";
content +=
"<h4>Discharge Rty Cnt: " + String(falseTrue[datalayer_extended.tesla.PCS_dcdcDischargeRtyCnt]) + "</h4>";
content += "<h4>PWM Enable Line: " + String(Fault[datalayer_extended.tesla.PCS_dcdcPwmEnableLine]) + "</h4>";
content +=
"<h4>Supporting Fixed LV Target: " + String(Fault[datalayer_extended.tesla.PCS_dcdcSupportingFixedLvTarget]) +
"</h4>";
content += "<h4>Discharge Rty Cnt: " + String(falseTrue[datalayer_extended.tesla.battery_PCS_dcdcDischargeRtyCnt]) +
content += "<h4>Precharge Restart Cnt: " + String(falseTrue[datalayer_extended.tesla.PCS_dcdcPrechargeRestartCnt]) +
"</h4>";
content +=
"<h4>PWM Enable Line: " + String(Fault[datalayer_extended.tesla.battery_PCS_dcdcPwmEnableLine]) + "</h4>";
content += "<h4>Supporting Fixed LV Target: " +
String(Fault[datalayer_extended.tesla.battery_PCS_dcdcSupportingFixedLvTarget]) + "</h4>";
content += "<h4>Precharge Restart Cnt: " +
String(falseTrue[datalayer_extended.tesla.battery_PCS_dcdcPrechargeRestartCnt]) + "</h4>";
content += "<h4>Initial Precharge Substate: " +
String(PCS_dcdcSubState[datalayer_extended.tesla.battery_PCS_dcdcInitialPrechargeSubState]) + "</h4>";
String(PCS_dcdcSubState[datalayer_extended.tesla.PCS_dcdcInitialPrechargeSubState]) + "</h4>";
//0x3C4 PCS_info
content += "<h4>PCS_buildConfigId: " + String(datalayer_extended.tesla.PCS_info_buildConfigId) + "</h4>";
content += "<h4>PCS_hardwareId: " + String(datalayer_extended.tesla.PCS_info_hardwareId) + "</h4>";
content += "<h4>PCS_componentId: " + String(datalayer_extended.tesla.PCS_info_componentId) + "</h4>";
//0x2C4 708 PCS_logging
content += "<h4>PCS_dcdcMaxLvOutputCurrent: " + String(PCS_dcdcMaxLvOutputCurrent) + " A</h4>";
content += "<h4>PCS_dcdcCurrentLimit: " + String(PCS_dcdcCurrentLimit) + " A</h4>";
@ -355,6 +375,10 @@ class TeslaHtmlRenderer : public BatteryHtmlRenderer {
content += "<h4>PCS_dcdcIntervalMinLvBusVolt: " + String(PCS_dcdcIntervalMinLvBusVolt) + " V</h4>";
content += "<h4>PCS_dcdcIntervalMinLvOutputCurr: " + String(PCS_dcdcIntervalMinLvOutputCurr) + " A</h4>";
content += "<h4>PCS_dcdc12vSupportLifetimekWh: " + String(PCS_dcdc12vSupportLifetimekWh) + " kWh</h4>";
//0x310 HVP_info
content += "<h4>HVP_buildConfigId: " + String(datalayer_extended.tesla.HVP_info_buildConfigId) + "</h4>";
content += "<h4>HVP_hardwareId: " + String(datalayer_extended.tesla.HVP_info_hardwareId) + "</h4>";
content += "<h4>HVP_componentId: " + String(datalayer_extended.tesla.HVP_info_componentId) + "</h4>";
//0x7AA 1962 HVP_debugMessage
content += "<h4>HVP_battery12V: " + String(HVP_battery12V) + " V</h4>";
content += "<h4>HVP_dcLinkVoltage: " + String(HVP_dcLinkVoltage) + " V</h4>";

View file

@ -1,8 +1,8 @@
#include "TEST-FAKE-BATTERY.h"
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "../devboard/utils/logging.h"
static void print_units(char* header, int value, char* units) {
static void print_units(const char* header, int value, const char* units) {
logging.print(header);
logging.print(value);
logging.print(units);
@ -67,7 +67,7 @@ void TestFakeBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
// Put fake messages here incase you want to test sending CAN
//transmit_can_frame(&TEST, can_interface);
//transmit_can_frame(&TEST);
}
}

View file

@ -1,7 +1,6 @@
#ifndef TEST_FAKE_BATTERY_H
#define TEST_FAKE_BATTERY_H
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "CanBattery.h"
#ifdef TEST_FAKE_BATTERY

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../devboard/utils/logging.h"
void VolvoSpaBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
@ -27,15 +27,15 @@ void VolvoSpaBattery::
// Update requests from webserver datalayer
if (datalayer_extended.VolvoPolestar.UserRequestDTCreset) {
transmit_can_frame(&VOLVO_DTC_Erase, can_config.battery); //Send global DTC erase command
transmit_can_frame(&VOLVO_DTC_Erase); //Send global DTC erase command
datalayer_extended.VolvoPolestar.UserRequestDTCreset = false;
}
if (datalayer_extended.VolvoPolestar.UserRequestBECMecuReset) {
transmit_can_frame(&VOLVO_BECM_ECUreset, can_config.battery); //Send BECM ecu reset command
transmit_can_frame(&VOLVO_BECM_ECUreset); //Send BECM ecu reset command
datalayer_extended.VolvoPolestar.UserRequestBECMecuReset = false;
}
if (datalayer_extended.VolvoPolestar.UserRequestDTCreadout) {
transmit_can_frame(&VOLVO_DTCreadout, can_config.battery); //Send DTC readout command
transmit_can_frame(&VOLVO_DTCreadout); //Send DTC readout command
datalayer_extended.VolvoPolestar.UserRequestDTCreadout = false;
}
@ -287,7 +287,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
(rx_frame.data.u8[3] == 0x6D)) // SOH response frame
{
datalayer.battery.status.soh_pptt = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
transmit_can_frame(&VOLVO_BECMsupplyVoltage_Req, can_config.battery); //Send BECM supply voltage req
transmit_can_frame(&VOLVO_BECMsupplyVoltage_Req); //Send BECM supply voltage req
} else if ((rx_frame.data.u8[0] == 0x05) && (rx_frame.data.u8[1] == 0x62) && (rx_frame.data.u8[2] == 0xF4) &&
(rx_frame.data.u8[3] == 0x42)) // BECM module voltage supply
{
@ -297,12 +297,12 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
{
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]);
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
transmit_can_frame(&VOLVO_FlowControl); // Send flow control
rxConsecutiveFrames = 1;
} else if ((rx_frame.data.u8[0] == 0x10) && (rx_frame.data.u8[2] == 0x59) &&
(rx_frame.data.u8[3] == 0x03)) // First response frame for DTC with more than one code
{
transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x21) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
@ -311,7 +311,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
if (batteryModuleNumber <= 0x2A) // Run until last pack is read
{
VOLVO_CELL_U_Req.data.u8[3] = batteryModuleNumber++;
transmit_can_frame(&VOLVO_CELL_U_Req, can_config.battery); //Send cell voltage read request for next module
transmit_can_frame(&VOLVO_CELL_U_Req); //Send cell voltage read request for next module
} else {
min_max_voltage[0] = 9999;
min_max_voltage[1] = 0;
@ -321,7 +321,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
if (min_max_voltage[1] < cell_voltages[cellcounter])
min_max_voltage[1] = cell_voltages[cellcounter];
}
transmit_can_frame(&VOLVO_SOH_Req, can_config.battery); //Send SOH read request
transmit_can_frame(&VOLVO_SOH_Req); //Send SOH read request
}
rxConsecutiveFrames = 0;
}
@ -336,7 +336,7 @@ void VolvoSpaBattery::readCellVoltages() {
batteryModuleNumber = 0x10;
rxConsecutiveFrames = 0;
VOLVO_CELL_U_Req.data.u8[3] = batteryModuleNumber++;
transmit_can_frame(&VOLVO_CELL_U_Req, can_config.battery); //Send cell voltage read request for first module
transmit_can_frame(&VOLVO_CELL_U_Req); //Send cell voltage read request for first module
}
void VolvoSpaBattery::transmit_can(unsigned long currentMillis) {
@ -344,22 +344,22 @@ void VolvoSpaBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
transmit_can_frame(&VOLVO_536, can_config.battery); //Send 0x536 Network managing frame to keep BMS alive
transmit_can_frame(&VOLVO_372, can_config.battery); //Send 0x372 ECMAmbientTempCalculated
transmit_can_frame(&VOLVO_536); //Send 0x536 Network managing frame to keep BMS alive
transmit_can_frame(&VOLVO_372); //Send 0x372 ECMAmbientTempCalculated
if ((datalayer.battery.status.bms_status == ACTIVE) && startedUp) {
datalayer.system.status.battery_allows_contactor_closing = true;
transmit_can_frame(&VOLVO_140_CLOSE, can_config.battery); //Send 0x140 Close contactors message
transmit_can_frame(&VOLVO_140_CLOSE); //Send 0x140 Close contactors message
} else { //datalayer.battery.status.bms_status == FAULT , OR inverter requested opening contactors, OR system not started yet
datalayer.system.status.battery_allows_contactor_closing = false;
transmit_can_frame(&VOLVO_140_OPEN, can_config.battery); //Send 0x140 Open contactors message
transmit_can_frame(&VOLVO_140_OPEN); //Send 0x140 Open contactors message
}
}
if (currentMillis - previousMillis1s >= INTERVAL_1_S) {
previousMillis1s = currentMillis;
if (!startedUp) {
transmit_can_frame(&VOLVO_DTC_Erase, can_config.battery); //Erase any DTCs preventing startup
transmit_can_frame(&VOLVO_DTC_Erase); //Erase any DTCs preventing startup
DTC_reset_counter++;
if (DTC_reset_counter > 1) { // Performed twice before starting
startedUp = true;

View file

@ -1,7 +1,6 @@
#ifndef VOLVO_SPA_BATTERY_H
#define VOLVO_SPA_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
#include "VOLVO-SPA-HTML.h"

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../devboard/utils/logging.h"
void VolvoSpaHybridBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
@ -27,15 +27,15 @@ void VolvoSpaHybridBattery::
// Update requests from webserver datalayer
if (datalayer_extended.VolvoHybrid.UserRequestDTCreset) {
transmit_can_frame(&VOLVO_DTC_Erase, can_config.battery); //Send global DTC erase command
transmit_can_frame(&VOLVO_DTC_Erase); //Send global DTC erase command
datalayer_extended.VolvoHybrid.UserRequestDTCreset = false;
}
if (datalayer_extended.VolvoHybrid.UserRequestBECMecuReset) {
transmit_can_frame(&VOLVO_BECM_ECUreset, can_config.battery); //Send BECM ecu reset command
transmit_can_frame(&VOLVO_BECM_ECUreset); //Send BECM ecu reset command
datalayer_extended.VolvoHybrid.UserRequestBECMecuReset = false;
}
if (datalayer_extended.VolvoHybrid.UserRequestDTCreadout) {
transmit_can_frame(&VOLVO_DTCreadout, can_config.battery); //Send DTC readout command
transmit_can_frame(&VOLVO_DTCreadout); //Send DTC readout command
datalayer_extended.VolvoHybrid.UserRequestDTCreadout = false;
}
@ -283,7 +283,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
(rx_frame.data.u8[3] == 0x6D)) // SOH response frame
{
datalayer.battery.status.soh_pptt = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
transmit_can_frame(&VOLVO_BECMsupplyVoltage_Req, can_config.battery); //Send BECM supply voltage req
transmit_can_frame(&VOLVO_BECMsupplyVoltage_Req); //Send BECM supply voltage req
} else if ((rx_frame.data.u8[0] == 0x05) && (rx_frame.data.u8[1] == 0x62) && (rx_frame.data.u8[2] == 0xF4) &&
(rx_frame.data.u8[3] == 0x42)) // BECM module voltage supply
{
@ -294,193 +294,193 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
{
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]);
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
transmit_can_frame(&VOLVO_FlowControl); // Send flow control
rxConsecutiveFrames = 1;
} else if ((rx_frame.data.u8[0] == 0x10) && (rx_frame.data.u8[2] == 0x59) &&
(rx_frame.data.u8[3] == 0x03)) // First response frame for DTC with more than one code
{
transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x21) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x22) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x23) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x24) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x25) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x26) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x27) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x28) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x29) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x2A) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x2B) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x2C) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x2D) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x2E) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x2F) && (rxConsecutiveFrames == 1)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
rxConsecutiveFrames = 2;
} else if ((rx_frame.data.u8[0] == 0x20) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x21) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x22) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x23) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x24) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x25) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x26) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x27) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x28) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x29) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x2A) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x2B) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x2C) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]);
cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]);
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6];
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
} else if ((rx_frame.data.u8[0] == 0x2D) && (rxConsecutiveFrames == 2)) {
cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
//cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
//transmit_can_frame(&VOLVO_FlowControl); // Send flow control
if (false) // Run until last pack is read
{
//VOLVO_CELL_U_Req.data.u8[3] = batteryModuleNumber++;
//transmit_can_frame(&VOLVO_CELL_U_Req, can_config.battery); //Send cell voltage read request for next module
//transmit_can_frame(&VOLVO_CELL_U_Req); //Send cell voltage read request for next module
;
} else {
min_max_voltage[0] = 9999;
@ -496,7 +496,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
CELL_U_MAX = min_max_voltage[1];
CELL_U_MIN = min_max_voltage[0];
transmit_can_frame(&VOLVO_SOH_Req, can_config.battery); //Send SOH read request
transmit_can_frame(&VOLVO_SOH_Req); //Send SOH read request
}
rxConsecutiveFrames = 0;
}
@ -511,7 +511,7 @@ void VolvoSpaHybridBattery::readCellVoltages() {
//batteryModuleNumber = 0x10;
rxConsecutiveFrames = 0;
//VOLVO_CELL_U_Req.data.u8[3] = batteryModuleNumber++;
transmit_can_frame(&VOLVO_CELL_U_Req, can_config.battery); //Send cell voltage read request for first module
transmit_can_frame(&VOLVO_CELL_U_Req); //Send cell voltage read request for first module
}
void VolvoSpaHybridBattery::transmit_can(unsigned long currentMillis) {
@ -519,22 +519,22 @@ void VolvoSpaHybridBattery::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
transmit_can_frame(&VOLVO_536, can_config.battery); //Send 0x536 Network managing frame to keep BMS alive
transmit_can_frame(&VOLVO_372, can_config.battery); //Send 0x372 ECMAmbientTempCalculated
transmit_can_frame(&VOLVO_536); //Send 0x536 Network managing frame to keep BMS alive
transmit_can_frame(&VOLVO_372); //Send 0x372 ECMAmbientTempCalculated
if ((datalayer.battery.status.bms_status == ACTIVE) && startedUp) {
datalayer.system.status.battery_allows_contactor_closing = true;
//transmit_can_frame(&VOLVO_140_CLOSE, can_config.battery); //Send 0x140 Close contactors message
//transmit_can_frame(&VOLVO_140_CLOSE); //Send 0x140 Close contactors message
} else { //datalayer.battery.status.bms_status == FAULT , OR inverter requested opening contactors, OR system not started yet
datalayer.system.status.battery_allows_contactor_closing = false;
transmit_can_frame(&VOLVO_140_OPEN, can_config.battery); //Send 0x140 Open contactors message
transmit_can_frame(&VOLVO_140_OPEN); //Send 0x140 Open contactors message
}
}
if (currentMillis - previousMillis1s >= INTERVAL_1_S) {
previousMillis1s = currentMillis;
if (!startedUp) {
transmit_can_frame(&VOLVO_DTC_Erase, can_config.battery); //Erase any DTCs preventing startup
transmit_can_frame(&VOLVO_DTC_Erase); //Erase any DTCs preventing startup
DTC_reset_counter++;
if (DTC_reset_counter > 1) { // Performed twice before starting
startedUp = true;

View file

@ -1,7 +1,6 @@
#ifndef VOLVO_SPA_HYBRID_BATTERY_H
#define VOLVO_SPA_HYBRID_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "CanBattery.h"
#include "VOLVO-SPA-HYBRID-HTML.h"

View file

@ -1,4 +1,6 @@
#include "../include.h"
#include "CHARGERS.h"
#include <vector>
#include "CanCharger.h"
CanCharger* charger = nullptr;

View file

@ -1,7 +1,7 @@
#include "CHEVY-VOLT-CHARGER.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "../devboard/utils/logging.h"
/* This implements Chevy Volt / Ampera charger support (2011-2015 model years).
*
@ -105,7 +105,7 @@ void ChevyVoltCharger::transmit_can(unsigned long currentMillis) {
charger_keepalive_frame.data.u8[0] = charger_mode;
transmit_can_frame(&charger_keepalive_frame, can_config.charger);
transmit_can_frame(&charger_keepalive_frame);
}
/* Send current targets every 200ms */
@ -142,7 +142,7 @@ void ChevyVoltCharger::transmit_can(unsigned long currentMillis) {
/* LSB of the voltage command. Then MSB LSB is divided by 2 */
charger_set_targets.data.u8[3] = lowByte(Vol_temp);
transmit_can_frame(&charger_set_targets, can_config.charger);
transmit_can_frame(&charger_set_targets);
}
#ifdef DEBUG_LOG

View file

@ -2,7 +2,6 @@
#define CHEVYVOLT_CHARGER_H
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "CanCharger.h"
#ifdef CHEVYVOLT_CHARGER

View file

@ -6,6 +6,8 @@
#include "../datalayer/datalayer.h"
#include "src/communication/Transmitter.h"
#include "src/communication/can/CanReceiver.h"
#include "src/communication/can/comm_can.h"
#include "src/devboard/safety/safety.h"
enum class ChargerType { None, NissanLeaf, ChevyVolt, Highest };
@ -56,11 +58,18 @@ class CanCharger : public Charger, Transmitter, CanReceiver {
void receive_can_frame(CAN_frame* frame) { map_can_frame_to_variable(*frame); }
CAN_Interface interface() { return can_interface; }
protected:
CAN_Interface can_interface;
CanCharger(ChargerType type) : Charger(type) {
can_interface = can_config.charger;
register_transmitter(this);
register_can_receiver(this, can_config.charger);
register_can_receiver(this, can_interface);
}
void transmit_can_frame(CAN_frame* frame) { transmit_can_frame_to_interface(frame, can_interface); }
};
#endif

View file

@ -1,7 +1,6 @@
#include "NISSAN-LEAF-CHARGER.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../include.h"
/* This implements Nissan LEAF PDM charger support. 2013-2024 Gen2/3 PDMs are supported
*
@ -107,13 +106,13 @@ void NissanLeafCharger::transmit_can(unsigned long currentMillis) {
#ifndef NISSAN_LEAF_BATTERY
// VCM message, containing info if battery should sleep or stay awake
transmit_can_frame(&LEAF_50B, can_config.charger); // HCM_WakeUpSleepCommand == 11b == WakeUp, and CANMASK = 1
transmit_can_frame(&LEAF_50B); // HCM_WakeUpSleepCommand == 11b == WakeUp, and CANMASK = 1
LEAF_1DB.data.u8[7] = calculate_CRC_Nissan(&LEAF_1DB);
transmit_can_frame(&LEAF_1DB, can_config.charger);
transmit_can_frame(&LEAF_1DB);
LEAF_1DC.data.u8[7] = calculate_CRC_Nissan(&LEAF_1DC);
transmit_can_frame(&LEAF_1DC, can_config.charger);
transmit_can_frame(&LEAF_1DC);
#endif
OBCpowerSetpoint = ((datalayer.charger.charger_setpoint_HV_IDC * 4) + 0x64);
@ -159,9 +158,7 @@ void NissanLeafCharger::transmit_can(unsigned long currentMillis) {
LEAF_1F2.data.u8[6] = mprun10;
LEAF_1F2.data.u8[7] = calculate_checksum_nibble(&LEAF_1F2);
transmit_can_frame(
&LEAF_1F2,
can_config.charger); // Sending of 1F2 message is halted in LEAF-BATTERY function incase used here
transmit_can_frame(&LEAF_1F2); // Sending of 1F2 message is halted in LEAF-BATTERY function incase used here
}
/* Send messages every 100ms here */
@ -176,11 +173,11 @@ void NissanLeafCharger::transmit_can(unsigned long currentMillis) {
LEAF_55B.data.u8[6] = ((0x1 << 4) | (mprun100));
LEAF_55B.data.u8[7] = calculate_CRC_Nissan(&LEAF_55B);
transmit_can_frame(&LEAF_55B, can_config.charger);
transmit_can_frame(&LEAF_55B);
transmit_can_frame(&LEAF_59E, can_config.charger);
transmit_can_frame(&LEAF_59E);
transmit_can_frame(&LEAF_5BC, can_config.charger);
transmit_can_frame(&LEAF_5BC);
#endif
}
}

View file

@ -1,7 +1,6 @@
#ifndef NISSANLEAF_CHARGER_H
#define NISSANLEAF_CHARGER_H
#include <Arduino.h>
#include "../include.h"
#include "CanCharger.h"

View file

@ -8,7 +8,4 @@ class CanReceiver {
virtual void receive_can_frame(CAN_frame* rx_frame) = 0;
};
// Register a receiver object for a given CAN interface
void register_can_receiver(CanReceiver* receiver, CAN_Interface interface, bool halfSpeed = false);
#endif

View file

@ -1,26 +1,24 @@
#include "comm_can.h"
#include <algorithm>
#include <map>
#include "../../include.h"
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "../../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "../../lib/pierremolinaro-acan2515/ACAN2515.h"
#include "CanReceiver.h"
#include "USER_SETTINGS.h"
#include "comm_can.h"
#include "src/datalayer/datalayer.h"
#include "src/devboard/safety/safety.h"
#include "src/devboard/sdcard/sdcard.h"
#include "src/devboard/utils/logging.h"
struct CanReceiverRegistration {
CanReceiver* receiver;
bool halfSpeed;
CAN_Speed speed;
};
static std::multimap<CAN_Interface, CanReceiverRegistration> can_receivers;
bool hasHalfSpeedReceivers(const CAN_Interface& iface) {
auto range = can_receivers.equal_range(iface);
for (auto it = range.first; it != range.second; ++it) {
if (it->second.halfSpeed) {
return true;
}
}
return false;
}
// Parameters
CAN_device_t CAN_cfg; // CAN Config
const uint8_t rx_queue_size = 10; // Receive Queue size
@ -29,107 +27,173 @@ volatile bool send_ok_2515 = 0;
volatile bool send_ok_2518 = 0;
static unsigned long previousMillis10 = 0;
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
const bool use_canfd_as_can_default = true;
#else
const bool use_canfd_as_can_default = false;
#endif
bool use_canfd_as_can = use_canfd_as_can_default;
void map_can_frame_to_variable(CAN_frame* rx_frame, CAN_Interface interface);
#ifdef CAN_ADDON
void register_can_receiver(CanReceiver* receiver, CAN_Interface interface, CAN_Speed speed) {
can_receivers.insert({interface, {receiver, speed}});
DEBUG_PRINTF("CAN receiver registered, total: %d\n", can_receivers.size());
}
static const uint32_t QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL; //MHZ configured in USER_SETTINGS.h
SPIClass SPI2515;
ACAN2515 can(MCP2515_CS, SPI2515, MCP2515_INT);
ACAN2515* can2515;
ACAN2515Settings* settings2515;
static ACAN2515_Buffer16 gBuffer;
#endif //CAN_ADDON
#ifdef CANFD_ADDON
SPIClass SPI2517;
ACAN2517FD canfd(MCP2517_CS, SPI2517, MCP2517_INT);
#endif //CANFD_ADDON
ACAN2517FD* canfd;
ACAN2517FDSettings* settings2517;
// Initialization functions
void init_CAN() {
DEBUG_PRINTF("init_CAN called\n");
// CAN pins
#ifdef CAN_SE_PIN
pinMode(CAN_SE_PIN, OUTPUT);
digitalWrite(CAN_SE_PIN, LOW);
#endif // CAN_SE_PIN
bool native_can_initialized = false;
// Half-speed currently only supported for CAN_NATIVE
auto anyHalfSpeedNative = hasHalfSpeedReceivers(CAN_Interface::CAN_NATIVE);
bool init_CAN() {
CAN_cfg.speed = anyHalfSpeedNative ? CAN_SPEED_250KBPS : CAN_SPEED_500KBPS;
CAN_cfg.tx_pin_id = CAN_TX_PIN;
CAN_cfg.rx_pin_id = CAN_RX_PIN;
CAN_cfg.rx_queue = xQueueCreate(rx_queue_size, sizeof(CAN_frame_t));
// Init CAN Module
ESP32Can.CANInit();
auto nativeIt = can_receivers.find(CAN_NATIVE);
if (nativeIt != can_receivers.end()) {
auto se_pin = esp32hal->CAN_SE_PIN();
auto tx_pin = esp32hal->CAN_TX_PIN();
auto rx_pin = esp32hal->CAN_RX_PIN();
DEBUG_PRINTF("init_CAN performed\n");
if (se_pin != GPIO_NUM_NC) {
if (!esp32hal->alloc_pins("CAN", se_pin)) {
return false;
}
pinMode(se_pin, OUTPUT);
digitalWrite(se_pin, LOW);
}
#ifdef CAN_ADDON
#ifdef DEBUG_LOG
logging.println("Dual CAN Bus (ESP32+MCP2515) selected");
#endif // DEBUG_LOG
gBuffer.initWithSize(25);
SPI2515.begin(MCP2515_SCK, MCP2515_MISO, MCP2515_MOSI);
ACAN2515Settings settings2515(QUARTZ_FREQUENCY, 500UL * 1000UL); // CAN bit rate 500 kb/s
settings2515.mRequestedMode = ACAN2515Settings::NormalMode;
const uint16_t errorCode2515 = can.begin(settings2515, [] { can.isr(); });
if (errorCode2515 == 0) {
#ifdef DEBUG_LOG
logging.println("Can ok");
#endif // DEBUG_LOG
} else {
#ifdef DEBUG_LOG
logging.print("Error Can: 0x");
logging.println(errorCode2515, HEX);
#endif // DEBUG_LOG
set_event(EVENT_CANMCP2515_INIT_FAILURE, (uint8_t)errorCode2515);
CAN_cfg.speed = (CAN_speed_t)nativeIt->second.speed;
if (!esp32hal->alloc_pins("CAN", tx_pin, rx_pin)) {
return false;
}
CAN_cfg.tx_pin_id = tx_pin;
CAN_cfg.rx_pin_id = rx_pin;
CAN_cfg.rx_queue = xQueueCreate(rx_queue_size, sizeof(CAN_frame_t));
// Init CAN Module
ESP32Can.CANInit();
native_can_initialized = true;
}
#endif // CAN_ADDON
#ifdef CANFD_ADDON
auto addonIt = can_receivers.find(CAN_ADDON_MCP2515);
if (addonIt != can_receivers.end()) {
auto cs_pin = esp32hal->MCP2515_CS();
auto int_pin = esp32hal->MCP2515_INT();
auto sck_pin = esp32hal->MCP2515_SCK();
auto miso_pin = esp32hal->MCP2515_MISO();
auto mosi_pin = esp32hal->MCP2515_MOSI();
if (!esp32hal->alloc_pins("CAN", cs_pin, int_pin, sck_pin, miso_pin, mosi_pin)) {
return false;
}
#ifdef DEBUG_LOG
logging.println("CAN FD add-on (ESP32+MCP2517) selected");
logging.println("Dual CAN Bus (ESP32+MCP2515) selected");
#endif // DEBUG_LOG
SPI2517.begin(MCP2517_SCK, MCP2517_SDO, MCP2517_SDI);
ACAN2517FDSettings settings2517(CANFD_ADDON_CRYSTAL_FREQUENCY_MHZ, 500 * 1000,
DataBitRateFactor::x4); // Arbitration bit rate: 500 kbit/s, data bit rate: 2 Mbit/s
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
settings2517.mRequestedMode = ACAN2517FDSettings::Normal20B; // ListenOnly / Normal20B / NormalFD
#else // not USE_CANFD_INTERFACE_AS_CLASSIC_CAN
settings2517.mRequestedMode = ACAN2517FDSettings::NormalFD; // ListenOnly / Normal20B / NormalFD
#endif // USE_CANFD_INTERFACE_AS_CLASSIC_CAN
const uint32_t errorCode2517 = canfd.begin(settings2517, [] { canfd.isr(); });
canfd.poll();
if (errorCode2517 == 0) {
gBuffer.initWithSize(25);
can2515 = new ACAN2515(cs_pin, SPI2515, int_pin);
SPI2515.begin(sck_pin, miso_pin, mosi_pin);
// CAN bit rate 250 or 500 kb/s
auto bitRate = (int)addonIt->second.speed * 1000UL;
settings2515 = new ACAN2515Settings(QUARTZ_FREQUENCY, bitRate);
settings2515->mRequestedMode = ACAN2515Settings::NormalMode;
const uint16_t errorCode2515 = can2515->begin(*settings2515, [] { can2515->isr(); });
if (errorCode2515 == 0) {
#ifdef DEBUG_LOG
logging.print("Bit Rate prescaler: ");
logging.println(settings2517.mBitRatePrescaler);
logging.print("Arbitration Phase segment 1: ");
logging.print(settings2517.mArbitrationPhaseSegment1);
logging.print(" segment 2: ");
logging.print(settings2517.mArbitrationPhaseSegment2);
logging.print(" SJW: ");
logging.println(settings2517.mArbitrationSJW);
logging.print("Actual Arbitration Bit Rate: ");
logging.print(settings2517.actualArbitrationBitRate());
logging.print(" bit/s");
logging.print(" (Exact:");
logging.println(settings2517.exactArbitrationBitRate() ? "yes)" : "no)");
logging.print("Arbitration Sample point: ");
logging.print(settings2517.arbitrationSamplePointFromBitStart());
logging.println("%");
logging.println("Can ok");
#endif // DEBUG_LOG
} else {
} else {
#ifdef DEBUG_LOG
logging.print("CAN-FD Configuration error 0x");
logging.println(errorCode2517, HEX);
logging.print("Error Can: 0x");
logging.println(errorCode2515, HEX);
#endif // DEBUG_LOG
set_event(EVENT_CANMCP2517FD_INIT_FAILURE, (uint8_t)errorCode2517);
set_event(EVENT_CANMCP2515_INIT_FAILURE, (uint8_t)errorCode2515);
return false;
}
}
#endif // CANFD_ADDON
auto fdNativeIt = can_receivers.find(CANFD_NATIVE);
auto fdAddonIt = can_receivers.find(CANFD_ADDON_MCP2518);
if (fdNativeIt != can_receivers.end() || fdAddonIt != can_receivers.end()) {
auto speed = (fdNativeIt != can_receivers.end()) ? fdNativeIt->second.speed : fdAddonIt->second.speed;
auto cs_pin = esp32hal->MCP2517_CS();
auto int_pin = esp32hal->MCP2517_INT();
auto sck_pin = esp32hal->MCP2517_SCK();
auto sdo_pin = esp32hal->MCP2517_SDO();
auto sdi_pin = esp32hal->MCP2517_SDI();
if (!esp32hal->alloc_pins("CAN", cs_pin, int_pin, sck_pin, sdo_pin, sdi_pin)) {
return false;
}
canfd = new ACAN2517FD(cs_pin, SPI2517, int_pin);
#ifdef DEBUG_LOG
logging.println("CAN FD add-on (ESP32+MCP2517) selected");
#endif // DEBUG_LOG
SPI2517.begin(sck_pin, sdo_pin, sdi_pin);
auto bitRate = (int)speed * 1000UL;
settings2517 = new ACAN2517FDSettings(
CANFD_ADDON_CRYSTAL_FREQUENCY_MHZ, bitRate,
DataBitRateFactor::x4); // Arbitration bit rate: 250/500 kbit/s, data bit rate: 1/2 Mbit/s
// ListenOnly / Normal20B / NormalFD
settings2517->mRequestedMode = use_canfd_as_can ? ACAN2517FDSettings::Normal20B : ACAN2517FDSettings::NormalFD;
const uint32_t errorCode2517 = canfd->begin(*settings2517, [] { canfd->isr(); });
canfd->poll();
if (errorCode2517 == 0) {
#ifdef DEBUG_LOG
logging.print("Bit Rate prescaler: ");
logging.println(settings2517->mBitRatePrescaler);
logging.print("Arbitration Phase segment 1: ");
logging.print(settings2517->mArbitrationPhaseSegment1);
logging.print(" segment 2: ");
logging.print(settings2517->mArbitrationPhaseSegment2);
logging.print(" SJW: ");
logging.println(settings2517->mArbitrationSJW);
logging.print("Actual Arbitration Bit Rate: ");
logging.print(settings2517->actualArbitrationBitRate());
logging.print(" bit/s");
logging.print(" (Exact:");
logging.println(settings2517->exactArbitrationBitRate() ? "yes)" : "no)");
logging.print("Arbitration Sample point: ");
logging.print(settings2517->arbitrationSamplePointFromBitStart());
logging.println("%");
#endif // DEBUG_LOG
} else {
#ifdef DEBUG_LOG
logging.print("CAN-FD Configuration error 0x");
logging.println(errorCode2517, HEX);
#endif // DEBUG_LOG
set_event(EVENT_CANMCP2517FD_INIT_FAILURE, (uint8_t)errorCode2517);
return false;
}
}
return true;
}
void transmit_can_frame(CAN_frame* tx_frame, int interface) {
void transmit_can_frame_to_interface(CAN_frame* tx_frame, int interface) {
if (!allowed_to_send_CAN) {
return;
}
@ -156,7 +220,6 @@ void transmit_can_frame(CAN_frame* tx_frame, int interface) {
}
break;
case CAN_ADDON_MCP2515: {
#ifdef CAN_ADDON
//Struct with ACAN2515 library format, needed to use the MCP2515 library for CAN2
CANMessage MCP2515Frame;
MCP2515Frame.id = tx_frame->ID;
@ -167,17 +230,13 @@ void transmit_can_frame(CAN_frame* tx_frame, int interface) {
MCP2515Frame.data[i] = tx_frame->data.u8[i];
}
send_ok_2515 = can.tryToSend(MCP2515Frame);
send_ok_2515 = can2515->tryToSend(MCP2515Frame);
if (!send_ok_2515) {
datalayer.system.info.can_2515_send_fail = true;
}
#else // Interface not compiled, and settings try to use it
set_event(EVENT_INTERFACE_MISSING, interface);
#endif //CAN_ADDON
} break;
case CANFD_NATIVE:
case CANFD_ADDON_MCP2518: {
#ifdef CANFD_ADDON
CANFDMessage MCP2518Frame;
if (tx_frame->FD) {
MCP2518Frame.type = CANFDMessage::CANFD_WITH_BIT_RATE_SWITCH;
@ -190,13 +249,10 @@ void transmit_can_frame(CAN_frame* tx_frame, int interface) {
for (uint8_t i = 0; i < MCP2518Frame.len; i++) {
MCP2518Frame.data[i] = tx_frame->data.u8[i];
}
send_ok_2518 = canfd.tryToSend(MCP2518Frame);
send_ok_2518 = canfd->tryToSend(MCP2518Frame);
if (!send_ok_2518) {
datalayer.system.info.can_2518_send_fail = true;
}
#else // Interface not compiled, and settings try to use it
set_event(EVENT_INTERFACE_MISSING, interface);
#endif //CANFD_ADDON
} break;
default:
// Invalid interface sent with function call. TODO: Raise event that coders messed up
@ -206,13 +262,17 @@ void transmit_can_frame(CAN_frame* tx_frame, int interface) {
// Receive functions
void receive_can() {
receive_frame_can_native(); // Receive CAN messages from native CAN port
#ifdef CAN_ADDON
receive_frame_can_addon(); // Receive CAN messages on add-on MCP2515 chip
#endif // CAN_ADDON
#ifdef CANFD_ADDON
receive_frame_canfd_addon(); // Receive CAN-FD messages.
#endif // CANFD_ADDON
if (native_can_initialized) {
receive_frame_can_native(); // Receive CAN messages from native CAN port
}
if (can2515) {
receive_frame_can_addon(); // Receive CAN messages on add-on MCP2515 chip
}
if (canfd) {
receive_frame_canfd_addon(); // Receive CAN-FD messages.
}
}
void receive_frame_can_native() { // This section checks if we have a complete CAN message incoming on native CAN port
@ -234,13 +294,12 @@ void receive_frame_can_native() { // This section checks if we have a complete
}
}
#ifdef CAN_ADDON
void receive_frame_can_addon() { // This section checks if we have a complete CAN message incoming on add-on CAN port
CAN_frame rx_frame; // Struct with our CAN format
CANMessage MCP2515frame; // Struct with ACAN2515 library format, needed to use the MCP2515 library
if (can.available()) {
can.receive(MCP2515frame);
if (can2515->available()) {
can2515->receive(MCP2515frame);
rx_frame.ID = MCP2515frame.id;
rx_frame.ext_ID = MCP2515frame.ext ? CAN_frame_ext : CAN_frame_std;
@ -253,26 +312,23 @@ void receive_frame_can_addon() { // This section checks if we have a complete C
map_can_frame_to_variable(&rx_frame, CAN_ADDON_MCP2515);
}
}
#endif // CAN_ADDON
#ifdef CANFD_ADDON
void receive_frame_canfd_addon() { // This section checks if we have a complete CAN-FD message incoming
CANFDMessage MCP2518frame;
int count = 0;
while (canfd.available() && count++ < 16) {
canfd.receive(MCP2518frame);
while (canfd->available() && count++ < 16) {
canfd->receive(MCP2518frame);
CAN_frame rx_frame;
rx_frame.ID = MCP2518frame.id;
rx_frame.ext_ID = MCP2518frame.ext;
rx_frame.DLC = MCP2518frame.len;
memcpy(rx_frame.data.u8, MCP2518frame.data, MIN(rx_frame.DLC, 64));
memcpy(rx_frame.data.u8, MCP2518frame.data, std::min(rx_frame.DLC, (uint8_t)64));
//message incoming, pass it on to the handler
map_can_frame_to_variable(&rx_frame, CANFD_ADDON_MCP2518);
map_can_frame_to_variable(&rx_frame, CANFD_NATIVE);
}
}
#endif // CANFD_ADDON
// Support functions
void print_can_frame(CAN_frame frame, frameDirection msgDir) {
@ -299,11 +355,6 @@ void print_can_frame(CAN_frame frame, frameDirection msgDir) {
}
}
void register_can_receiver(CanReceiver* receiver, CAN_Interface interface, bool halfSpeed) {
can_receivers.insert({interface, {receiver, halfSpeed}});
DEBUG_PRINTF("CAN receiver registered, total: %d\n", can_receivers.size());
}
void map_can_frame_to_variable(CAN_frame* rx_frame, CAN_Interface interface) {
if (interface !=
CANFD_NATIVE) { //Avoid printing twice due to receive_frame_canfd_addon sending to both FD interfaces
@ -361,3 +412,45 @@ void dump_can_frame(CAN_frame& frame, frameDirection msgDir) {
datalayer.system.info.logged_can_messages_offset = offset; // Update offset in buffer
}
void stop_can() {
if (can_receivers.find(CAN_NATIVE) != can_receivers.end()) {
ESP32Can.CANStop();
}
if (can2515) {
can2515->end();
SPI2515.end();
}
if (canfd) {
canfd->end();
SPI2517.end();
}
}
void restart_can() {
if (can_receivers.find(CAN_NATIVE) != can_receivers.end()) {
ESP32Can.CANInit();
}
if (can2515) {
SPI2515.begin();
can2515->begin(*settings2515, [] { can2515->isr(); });
}
if (canfd) {
SPI2517.begin();
canfd->begin(*settings2517, [] { can2515->isr(); });
}
}
CAN_Speed change_can_speed(CAN_Interface interface, CAN_Speed speed) {
auto oldSpeed = (CAN_Speed)CAN_cfg.speed;
if (interface == CAN_Interface::CAN_NATIVE) {
CAN_cfg.speed = (CAN_speed_t)speed;
// ReInit native CAN module at new speed
ESP32Can.CANInit();
}
return oldSpeed;
}

View file

@ -1,34 +1,42 @@
#ifndef _COMM_CAN_H_
#define _COMM_CAN_H_
#include "../../include.h"
#include "../../devboard/utils/types.h"
#include "../../datalayer/datalayer.h"
#include "../../devboard/utils/events.h"
#include "../../devboard/utils/value_mapping.h"
#include "../../lib/ESP32Async-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#ifdef CAN_ADDON
#include "../../lib/pierremolinaro-acan2515/ACAN2515.h"
#endif //CAN_ADDON
#ifdef CANFD_ADDON
#include "../../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#endif //CANFD_ADDON
extern bool use_canfd_as_can;
void dump_can_frame(CAN_frame& frame, frameDirection msgDir);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
void transmit_can_frame_to_interface(CAN_frame* tx_frame, int interface);
class CanReceiver;
enum class CAN_Speed {
CAN_SPEED_100KBPS = 100,
CAN_SPEED_125KBPS = 125,
CAN_SPEED_200KBPS = 200,
CAN_SPEED_250KBPS = 250,
CAN_SPEED_500KBPS = 500,
CAN_SPEED_800KBPS = 800,
CAN_SPEED_1000KBPS = 1000
};
// Register a receiver object for a given CAN interface.
// By default receivers expect the CAN interface to be operated at "fast" speed.
// If halfSpeed is true, half speed is used.
void register_can_receiver(CanReceiver* receiver, CAN_Interface interface,
CAN_Speed speed = CAN_Speed::CAN_SPEED_500KBPS);
/**
* @brief Initialization function for CAN.
* @brief Initializes all CAN interfaces requested earlier by other modules (see register_can_receiver)
*
* @param[in] void
*
* @return void
* @return true if CAN interfaces were initialized successfully, false otherwise.
*/
void init_CAN();
bool init_CAN();
/**
* @brief Receive CAN messages from all interfaces
* @brief Receive CAN messages from all interfaces. Respective CanReceivers are called.
*
* @param[in] void
*
@ -72,4 +80,13 @@ void receive_frame_canfd_addon();
*/
void print_can_frame(CAN_frame frame, frameDirection msgDir);
// Stop/pause CAN communication for all interfaces
void stop_can();
// Restart CAN communication for all interfaces
void restart_can();
// Change the speed of the CAN interface and return the old speed.
CAN_Speed change_can_speed(CAN_Interface interface, CAN_Speed speed);
#endif

View file

@ -1,8 +1,7 @@
#include "obd.h"
#include "../../devboard/utils/logging.h"
#include "comm_can.h"
void transmit_can_frame(CAN_frame* tx_frame, int interface);
void show_dtc(uint8_t byte0, uint8_t byte1);
void show_dtc(uint8_t byte0, uint8_t byte1) {
@ -128,21 +127,21 @@ void transmit_obd_can_frame(unsigned int address, int interface, bool canFD) {
static int cnt = 0;
switch (cnt) {
case 2:
transmit_can_frame(&OBD_frame, interface); // DTC TP-ISO
transmit_can_frame_to_interface(&OBD_frame, interface); // DTC TP-ISO
break;
case 3:
OBD_frame.data.u8[1] = 0x07;
transmit_can_frame(&OBD_frame, interface); // DTC TP-ISO
transmit_can_frame_to_interface(&OBD_frame, interface); // DTC TP-ISO
break;
case 4:
OBD_frame.data.u8[1] = 0x0A;
transmit_can_frame(&OBD_frame, interface); // DTC TP-ISO
transmit_can_frame_to_interface(&OBD_frame, interface); // DTC TP-ISO
break;
case 5:
OBD_frame.data.u8[0] = 0x02;
OBD_frame.data.u8[1] = 0x01;
OBD_frame.data.u8[2] = 0x1C;
transmit_can_frame(&OBD_frame, interface); // DTC TP-ISO
transmit_can_frame_to_interface(&OBD_frame, interface); // DTC TP-ISO
break;
}
cnt++;

View file

@ -1,8 +1,8 @@
#ifndef _OBD_H_
#define _OBD_H_
#include "../../include.h"
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "comm_can.h"
void handle_obd_frame(CAN_frame& rx_frame);

View file

@ -1,5 +1,7 @@
#include "comm_contactorcontrol.h"
#include "../../include.h"
#include "../../devboard/hal/hal.h"
#include "../../devboard/safety/safety.h"
#include "../../inverter/INVERTERS.h"
#ifdef CONTACTOR_CONTROL
const bool contactor_control_enabled_default = true;
@ -25,7 +27,7 @@ bool periodic_bms_reset = periodic_bms_reset_default;
#ifdef REMOTE_BMS_RESET
const bool remote_bms_reset_default = true;
#else
const bool remote_bms_reset_default = true;
const bool remote_bms_reset_default = false;
#endif
bool remote_bms_reset = remote_bms_reset_default;
@ -73,7 +75,6 @@ unsigned long currentTime = 0;
unsigned long lastPowerRemovalTime = 0;
unsigned long bmsPowerOnTime = 0;
const unsigned long powerRemovalInterval = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
const unsigned long powerRemovalDuration = 30000; // 30 seconds in milliseconds
const unsigned long bmsWarmupDuration = 3000;
void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) {
@ -92,41 +93,57 @@ void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) {
// Initialization functions
void init_contactors() {
const char* contactors = "Contactors";
bool init_contactors() {
// Init contactor pins
if (contactor_control_enabled) {
auto posPin = esp32hal->POSITIVE_CONTACTOR_PIN();
auto negPin = esp32hal->NEGATIVE_CONTACTOR_PIN();
auto precPin = esp32hal->PRECHARGE_PIN();
if (!esp32hal->alloc_pins(contactors, posPin, negPin, precPin)) {
return false;
}
if (pwm_contactor_control) {
// Setup PWM Channel Frequency and Resolution
ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Positive_Channel);
ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Negative_Channel);
ledcAttachChannel(posPin, PWM_Freq, PWM_Res, PWM_Positive_Channel);
ledcAttachChannel(negPin, PWM_Freq, PWM_Res, PWM_Negative_Channel);
// Set all pins OFF (0% PWM)
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
ledcWrite(posPin, PWM_OFF_DUTY);
ledcWrite(negPin, PWM_OFF_DUTY);
} else { //Normal CONTACTOR_CONTROL
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
set(POSITIVE_CONTACTOR_PIN, OFF);
pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT);
set(NEGATIVE_CONTACTOR_PIN, OFF);
pinMode(posPin, OUTPUT);
set(posPin, OFF);
pinMode(negPin, OUTPUT);
set(negPin, OFF);
} // Precharge never has PWM regardless of setting
pinMode(PRECHARGE_PIN, OUTPUT);
set(PRECHARGE_PIN, OFF);
pinMode(precPin, OUTPUT);
set(precPin, OFF);
}
if (contactor_control_enabled_double_battery) {
pinMode(SECOND_BATTERY_CONTACTORS_PIN, OUTPUT);
set(SECOND_BATTERY_CONTACTORS_PIN, OFF);
}
// Init BMS contactor
#if defined HW_STARK || defined HW_3LB // This hardware has dedicated pin, always enable on start
pinMode(BMS_POWER, OUTPUT); //LilyGo is omitted from this, only enabled if user selects PERIODIC_BMS_RESET
digitalWrite(BMS_POWER, HIGH);
#endif // HW with dedicated BMS pins
#ifdef BMS_POWER
if (periodic_bms_reset || remote_bms_reset) {
pinMode(BMS_POWER, OUTPUT);
digitalWrite(BMS_POWER, HIGH);
if (contactor_control_enabled_double_battery) {
auto second_contactors = esp32hal->SECOND_BATTERY_CONTACTORS_PIN();
if (!esp32hal->alloc_pins(contactors, second_contactors)) {
return false;
}
pinMode(second_contactors, OUTPUT);
set(second_contactors, OFF);
}
#endif
// Init BMS contactor
if (periodic_bms_reset || remote_bms_reset || esp32hal->always_enable_bms_power()) {
auto pin = esp32hal->BMS_POWER();
if (!esp32hal->alloc_pins("BMS power", pin)) {
return false;
}
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
}
return true;
}
static void dbg_contactors(const char* state) {
@ -144,9 +161,14 @@ void handle_contactors() {
datalayer.system.status.inverter_allows_contactor_closing = inverter->allows_contactor_closing();
}
#ifdef BMS_POWER
handle_BMSpower(); // Some batteries need to be periodically power cycled
#endif
auto posPin = esp32hal->POSITIVE_CONTACTOR_PIN();
auto negPin = esp32hal->NEGATIVE_CONTACTOR_PIN();
auto prechargePin = esp32hal->PRECHARGE_PIN();
auto bms_power_pin = esp32hal->BMS_POWER();
if (bms_power_pin != GPIO_NUM_NC) {
handle_BMSpower(); // Some batteries need to be periodically power cycled
}
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
handle_contactors_battery2();
@ -166,9 +188,9 @@ void handle_contactors() {
}
if (contactorStatus == SHUTDOWN_REQUESTED) {
set(PRECHARGE_PIN, OFF);
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set(prechargePin, OFF);
set(negPin, OFF, PWM_OFF_DUTY);
set(posPin, OFF, PWM_OFF_DUTY);
set_event(EVENT_ERROR_OPEN_CONTACTOR, 0);
datalayer.system.status.contactors_engaged = false;
return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured)
@ -176,9 +198,9 @@ void handle_contactors() {
// After that, check if we are OK to start turning on the battery
if (contactorStatus == DISCONNECTED) {
set(PRECHARGE_PIN, OFF);
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set(prechargePin, OFF);
set(negPin, OFF, PWM_OFF_DUTY);
set(posPin, OFF, PWM_OFF_DUTY);
datalayer.system.status.contactors_engaged = false;
if (datalayer.system.status.battery_allows_contactor_closing &&
@ -210,7 +232,7 @@ void handle_contactors() {
// Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge
switch (contactorStatus) {
case START_PRECHARGE:
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
set(negPin, ON, PWM_ON_DUTY);
dbg_contactors("NEGATIVE");
prechargeStartTime = currentTime;
contactorStatus = PRECHARGE;
@ -218,7 +240,7 @@ void handle_contactors() {
case PRECHARGE:
if (currentTime - prechargeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) {
set(PRECHARGE_PIN, ON);
set(prechargePin, ON);
dbg_contactors("PRECHARGE");
negativeStartTime = currentTime;
contactorStatus = POSITIVE;
@ -227,7 +249,7 @@ void handle_contactors() {
case POSITIVE:
if (currentTime - negativeStartTime >= PRECHARGE_TIME_MS) {
set(POSITIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
set(posPin, ON, PWM_ON_DUTY);
dbg_contactors("POSITIVE");
prechargeCompletedTime = currentTime;
contactorStatus = PRECHARGE_OFF;
@ -236,9 +258,9 @@ void handle_contactors() {
case PRECHARGE_OFF:
if (currentTime - prechargeCompletedTime >= PRECHARGE_COMPLETED_TIME_MS) {
set(PRECHARGE_PIN, OFF);
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
set(POSITIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
set(prechargePin, OFF);
set(negPin, ON, PWM_HOLD_DUTY);
set(posPin, ON, PWM_HOLD_DUTY);
dbg_contactors("PRECHARGE_OFF");
contactorStatus = COMPLETED;
datalayer.system.status.contactors_engaged = true;
@ -269,9 +291,10 @@ This makes the BMS recalculate all SOC% and avoid memory leaks
During that time we also set the emulator state to paused in order to not try and send CAN messages towards the battery
Feature is only used if user has enabled PERIODIC_BMS_RESET in the USER_SETTINGS */
#ifdef BMS_POWER
void handle_BMSpower() {
if (periodic_bms_reset || remote_bms_reset) {
auto bms_power_pin = esp32hal->BMS_POWER();
// Get current time
currentTime = millis();
@ -282,13 +305,11 @@ void handle_BMSpower() {
}
}
// If power has been removed for 30 seconds, restore the power
if (datalayer.system.status.BMS_reset_in_progress && currentTime - lastPowerRemovalTime >= powerRemovalDuration) {
// If power has been removed for user configured interval (1-59 seconds), restore the power
if (datalayer.system.status.BMS_reset_in_progress &&
currentTime - lastPowerRemovalTime >= datalayer.battery.settings.user_set_bms_reset_duration_ms) {
// Reapply power to the BMS
digitalWrite(BMS_POWER, HIGH);
#ifdef BMS_2_POWER
digitalWrite(BMS_2_POWER, HIGH); // Same for battery 2
#endif
digitalWrite(bms_power_pin, HIGH);
bmsPowerOnTime = currentTime;
datalayer.system.status.BMS_reset_in_progress = false; // Reset the power removal flag
datalayer.system.status.BMS_startup_in_progress = true; // Set the BMS warmup flag
@ -303,10 +324,11 @@ void handle_BMSpower() {
}
}
}
#endif
void start_bms_reset() {
if (periodic_bms_reset || remote_bms_reset) {
auto bms_power_pin = esp32hal->BMS_POWER();
if (!datalayer.system.status.BMS_reset_in_progress) {
lastPowerRemovalTime = currentTime; // Record the time when BMS reset was started
// we are now resetting at the correct time. We don't need to offset anymore
@ -319,12 +341,7 @@ void start_bms_reset() {
// We try to keep contactors engaged during this pause, and just ramp power down to 0.
setBatteryPause(true, false, false, false);
#ifdef BMS_POWER
digitalWrite(BMS_POWER, LOW); // Remove power by setting the BMS power pin to LOW
#endif
#ifdef BMS_2_POWER
digitalWrite(BMS_2_POWER, LOW); // Same for battery 2
#endif
digitalWrite(bms_power_pin, LOW); // Remove power by setting the BMS power pin to LOW
}
}
}

View file

@ -1,8 +1,6 @@
#ifndef _COMM_CONTACTORCONTROL_H_
#define _COMM_CONTACTORCONTROL_H_
#include "../../include.h"
#include "../../datalayer/datalayer.h"
#include "../../devboard/utils/events.h"
@ -36,9 +34,9 @@ void start_bms_reset();
*
* @param[in] void
*
* @return void
* @return true if contactor init was successful, false otherwise.
*/
void init_contactors();
bool init_contactors();
/**
* @brief Handle contactors

View file

@ -1,33 +1,47 @@
#include "comm_equipmentstopbutton.h"
#include "../../include.h"
#include "../../devboard/hal/hal.h"
#include "../../devboard/safety/safety.h"
#include "USER_SETTINGS.h"
STOP_BUTTON_BEHAVIOR equipment_stop_behavior = stop_button_default_behavior;
// Parameters
#ifdef EQUIPMENT_STOP_BUTTON
const unsigned long equipment_button_long_press_duration =
15000; // 15 seconds for long press in case of MOMENTARY_SWITCH
const unsigned long equipment_button_debounce_duration = 200; // 200ms for debouncing the button
unsigned long timeSincePress = 0; // Variable to store the time since the last press
DebouncedButton equipment_stop_button; // Debounced button object
#endif // EQUIPMENT_STOP_BUTTON
// Initialization functions
#ifdef EQUIPMENT_STOP_BUTTON
void init_equipment_stop_button() {
bool init_equipment_stop_button() {
if (equipment_stop_behavior == STOP_BUTTON_BEHAVIOR::NOT_CONNECTED) {
return true;
}
auto pin = esp32hal->EQUIPMENT_STOP_PIN();
if (!esp32hal->alloc_pins("Equipment stop button", pin)) {
return false;
}
//using external pullup resistors NC
pinMode(EQUIPMENT_STOP_PIN, INPUT);
pinMode(pin, INPUT);
// Initialize the debounced button with NC switch type and equipment_button_debounce_duration debounce time
initDebouncedButton(equipment_stop_button, EQUIPMENT_STOP_PIN, NC, equipment_button_debounce_duration);
initDebouncedButton(equipment_stop_button, pin, NC, equipment_button_debounce_duration);
return true;
}
#endif // EQUIPMENT_STOP_BUTTON
// Main functions
#ifdef EQUIPMENT_STOP_BUTTON
void monitor_equipment_stop_button() {
if (equipment_stop_behavior == STOP_BUTTON_BEHAVIOR::NOT_CONNECTED) {
return;
}
ButtonState changed_state = debounceButton(equipment_stop_button, timeSincePress);
if (equipment_stop_behavior == LATCHING_SWITCH) {
if (equipment_stop_behavior == STOP_BUTTON_BEHAVIOR::LATCHING_SWITCH) {
if (changed_state == PRESSED) {
// Changed to ON initiating equipment stop.
setBatteryPause(true, false, true);
@ -35,7 +49,7 @@ void monitor_equipment_stop_button() {
// Changed to OFF ending equipment stop.
setBatteryPause(false, false, false);
}
} else if (equipment_stop_behavior == MOMENTARY_SWITCH) {
} else if (equipment_stop_behavior == STOP_BUTTON_BEHAVIOR::MOMENTARY_SWITCH) {
if (changed_state == RELEASED) { // button is released
if (timeSincePress < equipment_button_long_press_duration) {
@ -48,4 +62,3 @@ void monitor_equipment_stop_button() {
}
}
}
#endif // EQUIPMENT_STOP_BUTTON

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