mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 19:42:08 +02:00
Merge branch 'main' into feature/double-battery
This commit is contained in:
commit
1bf6167757
4 changed files with 55 additions and 27 deletions
|
@ -7,10 +7,6 @@
|
||||||
#include "BYD-ATTO-3-BATTERY.h"
|
#include "BYD-ATTO-3-BATTERY.h"
|
||||||
|
|
||||||
/* TODO:
|
/* TODO:
|
||||||
- Get contactor closing working
|
|
||||||
- NOTE: Some packs can be locked hard? after a crash has occured. Bypassing contactors manually might be required?
|
|
||||||
- Figure out which CAN messages need to be sent towards the battery to keep it alive
|
|
||||||
-Maybe already enough with 0x12D and 0x411? Plus the PID polls might keep it alive.
|
|
||||||
- Map all values from battery CAN messages
|
- Map all values from battery CAN messages
|
||||||
-SOC% still not found (Lets take it from PID poll, not working right yet)
|
-SOC% still not found (Lets take it from PID poll, not working right yet)
|
||||||
-SOC% is now ESTIMATED. This is bad, and should be fixed as soon as possible with the real value from CAN
|
-SOC% is now ESTIMATED. This is bad, and should be fixed as soon as possible with the real value from CAN
|
||||||
|
@ -315,6 +311,13 @@ void send_can_battery() {
|
||||||
}
|
}
|
||||||
previousMillis50 = currentMillis;
|
previousMillis50 = currentMillis;
|
||||||
|
|
||||||
|
// Set close contactors to allowed (Useful for crashed packs, started via contactor control thru GPIO)
|
||||||
|
if (datalayer.battery.status.bms_status == ACTIVE) {
|
||||||
|
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||||
|
} else { // Fault state, open contactors!
|
||||||
|
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||||
|
}
|
||||||
|
|
||||||
counter_50ms++;
|
counter_50ms++;
|
||||||
if (counter_50ms > 23) {
|
if (counter_50ms > 23) {
|
||||||
ATTO_3_12D.data.u8[2] = 0x00; // Goes from 02->00
|
ATTO_3_12D.data.u8[2] = 0x00; // Goes from 02->00
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
//Figure out if CAN messages need to be sent to keep the system happy?
|
//Figure out if CAN messages need to be sent to keep the system happy?
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
|
#define MAX_CELL_VOLTAGE 4100
|
||||||
|
#define MIN_CELL_VOLTAGE 2750
|
||||||
static uint8_t errorCode = 0; //stores if we have an error code active from battery control logic
|
static uint8_t errorCode = 0; //stores if we have an error code active from battery control logic
|
||||||
static uint8_t BMU_Detected = 0;
|
static uint8_t BMU_Detected = 0;
|
||||||
static uint8_t CMU_Detected = 0;
|
static uint8_t CMU_Detected = 0;
|
||||||
|
@ -96,7 +98,15 @@ void update_values_battery() { //This function maps all the values fetched via
|
||||||
|
|
||||||
datalayer.battery.status.temperature_min_dC = (int16_t)(min_temp_cel * 10);
|
datalayer.battery.status.temperature_min_dC = (int16_t)(min_temp_cel * 10);
|
||||||
|
|
||||||
datalayer.battery.status.temperature_min_dC = (int16_t)(max_temp_cel * 10);
|
datalayer.battery.status.temperature_max_dC = (int16_t)(max_temp_cel * 10);
|
||||||
|
|
||||||
|
//Check safeties
|
||||||
|
if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE) {
|
||||||
|
set_event(EVENT_CELL_OVER_VOLTAGE, datalayer.battery.status.cell_max_voltage_mV);
|
||||||
|
}
|
||||||
|
if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE) {
|
||||||
|
set_event(EVENT_CELL_UNDER_VOLTAGE, datalayer.battery.status.cell_min_voltage_mV);
|
||||||
|
}
|
||||||
|
|
||||||
if (!BMU_Detected) {
|
if (!BMU_Detected) {
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
|
@ -126,16 +136,26 @@ void update_values_battery() { //This function maps all the values fetched via
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_battery(CAN_frame_t rx_frame) {
|
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
datalayer.battery.status.CAN_battery_still_alive =
|
|
||||||
CAN_STILL_ALIVE; //TODO: move this inside a known message ID to prevent CAN inverter from keeping battery alive detection going
|
|
||||||
switch (rx_frame.MsgID) {
|
switch (rx_frame.MsgID) {
|
||||||
case 0x374: //BMU message, 10ms - SOC
|
case 0x374: //BMU message, 10ms - SOC
|
||||||
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
temp_value = ((rx_frame.data.u8[1] - 10) / 2);
|
temp_value = ((rx_frame.data.u8[1] - 10) / 2);
|
||||||
if (temp_value >= 0 && temp_value <= 101) {
|
|
||||||
BMU_SOC = temp_value;
|
if (temp_value == 205) {
|
||||||
|
// Use the value we sampled last time
|
||||||
|
} else { // We have a valid value
|
||||||
|
if (temp_value < 0) {
|
||||||
|
BMU_SOC = 0;
|
||||||
|
} else if (temp_value > 100) {
|
||||||
|
BMU_SOC = 100;
|
||||||
|
} else { // Between 0-100
|
||||||
|
BMU_SOC = temp_value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 0x373: //BMU message, 100ms - Pack Voltage and current
|
case 0x373: //BMU message, 100ms - Pack Voltage and current
|
||||||
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
BMU_Current = ((((((rx_frame.data.u8[2] * 256.0) + rx_frame.data.u8[3])) - 32768)) * 0.01);
|
BMU_Current = ((((((rx_frame.data.u8[2] * 256.0) + rx_frame.data.u8[3])) - 32768)) * 0.01);
|
||||||
BMU_PackVoltage = ((rx_frame.data.u8[4] * 256.0 + rx_frame.data.u8[5]) * 0.1);
|
BMU_PackVoltage = ((rx_frame.data.u8[4] * 256.0 + rx_frame.data.u8[5]) * 0.1);
|
||||||
BMU_Power = (BMU_Current * BMU_PackVoltage);
|
BMU_Power = (BMU_Current * BMU_PackVoltage);
|
||||||
|
@ -144,15 +164,22 @@ void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
case 0x6e2:
|
case 0x6e2:
|
||||||
case 0x6e3:
|
case 0x6e3:
|
||||||
case 0x6e4:
|
case 0x6e4:
|
||||||
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
BMU_Detected = 1;
|
BMU_Detected = 1;
|
||||||
//Pid index 0-3
|
//Pid index 0-3
|
||||||
pid_index = (rx_frame.MsgID) - 1761;
|
pid_index = (rx_frame.MsgID) - 1761;
|
||||||
//cmu index 1-12: ignore high order nibble which appears to sometimes contain other status bits
|
//cmu index 1-12: ignore high order nibble which appears to sometimes contain other status bits
|
||||||
cmu_id = (rx_frame.data.u8[0] & 0x0f);
|
cmu_id = (rx_frame.data.u8[0] & 0x0f);
|
||||||
//
|
//
|
||||||
temp1 = rx_frame.data.u8[1] - 50.0;
|
if (rx_frame.data.u8[1] != 0) { // Only update temperatures if value is available
|
||||||
temp2 = rx_frame.data.u8[2] - 50.0;
|
temp1 = rx_frame.data.u8[1] - 50.0;
|
||||||
temp3 = rx_frame.data.u8[3] - 50.0;
|
}
|
||||||
|
if (rx_frame.data.u8[2] != 0) {
|
||||||
|
temp2 = rx_frame.data.u8[1] - 50.0;
|
||||||
|
}
|
||||||
|
if (rx_frame.data.u8[3] != 0) {
|
||||||
|
temp3 = rx_frame.data.u8[1] - 50.0;
|
||||||
|
}
|
||||||
|
|
||||||
voltage1 = (((rx_frame.data.u8[4] * 256.0 + rx_frame.data.u8[5]) * 0.005) + 2.1);
|
voltage1 = (((rx_frame.data.u8[4] * 256.0 + rx_frame.data.u8[5]) * 0.005) + 2.1);
|
||||||
voltage2 = (((rx_frame.data.u8[6] * 256.0 + rx_frame.data.u8[7]) * 0.005) + 2.1);
|
voltage2 = (((rx_frame.data.u8[6] * 256.0 + rx_frame.data.u8[7]) * 0.005) + 2.1);
|
||||||
|
@ -163,8 +190,13 @@ void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
voltage_index -= 4;
|
voltage_index -= 4;
|
||||||
temp_index -= 3;
|
temp_index -= 3;
|
||||||
}
|
}
|
||||||
cell_voltages[voltage_index] = voltage1;
|
|
||||||
cell_voltages[voltage_index + 1] = voltage2;
|
if (voltage1 > 2.2) { // Only update cellvoltages incase we have a value
|
||||||
|
cell_voltages[voltage_index] = voltage1;
|
||||||
|
}
|
||||||
|
if (voltage2 > 2.2) {
|
||||||
|
cell_voltages[voltage_index + 1] = voltage2;
|
||||||
|
}
|
||||||
|
|
||||||
if (pid_index == 0) {
|
if (pid_index == 0) {
|
||||||
cell_temperatures[temp_index] = temp2;
|
cell_temperatures[temp_index] = temp2;
|
||||||
|
@ -202,9 +234,8 @@ void setup_battery(void) { // Performs one time setup at startup
|
||||||
Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected");
|
Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
datalayer.battery.info.max_design_voltage_dV =
|
datalayer.battery.info.max_design_voltage_dV = 3696; // 369.6V
|
||||||
3600; // 360.0V, over this, charging is not possible (goes into forced discharge)
|
datalayer.battery.info.min_design_voltage_dV = 3160; // 316.0V
|
||||||
datalayer.battery.info.min_design_voltage_dV = 3160; // 316.0V under this, discharging further is disabled
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -357,12 +357,6 @@ void update_values_battery() { //This function maps all the values fetched via
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if BMS is in need of recalibration
|
|
||||||
if (battery_nominal_full_pack_energy > 1 && battery_nominal_full_pack_energy < REASONABLE_ENERGYAMOUNT) {
|
|
||||||
set_event(EVENT_KWH_PLAUSIBILITY_ERROR, battery_nominal_full_pack_energy);
|
|
||||||
} else if (battery_nominal_full_pack_energy <= 1) {
|
|
||||||
set_event(EVENT_KWH_PLAUSIBILITY_ERROR, battery_nominal_full_pack_energy);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { //LFP limits used for voltage safeties
|
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { //LFP limits used for voltage safeties
|
||||||
if (battery_cell_max_v >= MAX_CELL_VOLTAGE_LFP) {
|
if (battery_cell_max_v >= MAX_CELL_VOLTAGE_LFP) {
|
||||||
|
|
|
@ -123,7 +123,7 @@ void init_webserver() {
|
||||||
server.on("/updateSocMax", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/updateSocMax", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (request->hasParam("value")) {
|
if (request->hasParam("value")) {
|
||||||
String value = request->getParam("value")->value();
|
String value = request->getParam("value")->value();
|
||||||
datalayer.battery.settings.max_percentage = value.toInt() * 100;
|
datalayer.battery.settings.max_percentage = static_cast<uint16_t>(value.toFloat() * 100);
|
||||||
storeSettings();
|
storeSettings();
|
||||||
request->send(200, "text/plain", "Updated successfully");
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
} else {
|
} else {
|
||||||
|
@ -135,7 +135,7 @@ void init_webserver() {
|
||||||
server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (request->hasParam("value")) {
|
if (request->hasParam("value")) {
|
||||||
String value = request->getParam("value")->value();
|
String value = request->getParam("value")->value();
|
||||||
datalayer.battery.settings.min_percentage = value.toInt() * 100;
|
datalayer.battery.settings.min_percentage = static_cast<uint16_t>(value.toFloat() * 100);
|
||||||
storeSettings();
|
storeSettings();
|
||||||
request->send(200, "text/plain", "Updated successfully");
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
} else {
|
} else {
|
||||||
|
@ -147,7 +147,7 @@ void init_webserver() {
|
||||||
server.on("/updateMaxChargeA", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/updateMaxChargeA", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (request->hasParam("value")) {
|
if (request->hasParam("value")) {
|
||||||
String value = request->getParam("value")->value();
|
String value = request->getParam("value")->value();
|
||||||
datalayer.battery.info.max_charge_amp_dA = value.toInt() * 10;
|
datalayer.battery.info.max_charge_amp_dA = static_cast<uint16_t>(value.toFloat() * 10);
|
||||||
storeSettings();
|
storeSettings();
|
||||||
request->send(200, "text/plain", "Updated successfully");
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
} else {
|
} else {
|
||||||
|
@ -159,7 +159,7 @@ void init_webserver() {
|
||||||
server.on("/updateMaxDischargeA", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/updateMaxDischargeA", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (request->hasParam("value")) {
|
if (request->hasParam("value")) {
|
||||||
String value = request->getParam("value")->value();
|
String value = request->getParam("value")->value();
|
||||||
datalayer.battery.info.max_discharge_amp_dA = value.toInt() * 10;
|
datalayer.battery.info.max_discharge_amp_dA = static_cast<uint16_t>(value.toFloat() * 10);
|
||||||
storeSettings();
|
storeSettings();
|
||||||
request->send(200, "text/plain", "Updated successfully");
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue