mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 10:19:29 +02:00
Improvement: Add Battery Pause Feature for Max Charge/Discharge Power and OTA Update Optimization
This commit is contained in:
parent
ccb2db87ec
commit
856a0838d1
23 changed files with 1004 additions and 773 deletions
|
@ -376,6 +376,10 @@ void update_values_battery2() { //This function maps all the values fetched via
|
|||
|
||||
datalayer.battery2.status.soh_pptt = battery2_soh * 100;
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
if (battery2_BEV_available_power_longterm_discharge > 65000) {
|
||||
datalayer.battery2.status.max_discharge_power_W = 65000;
|
||||
} else {
|
||||
|
@ -397,9 +401,9 @@ void update_values_battery2() { //This function maps all the values fetched via
|
|||
|
||||
datalayer.battery2.status.cell_min_voltage_mV = datalayer.battery2.status.cell_voltages_mV[0];
|
||||
datalayer.battery2.status.cell_max_voltage_mV = datalayer.battery2.status.cell_voltages_mV[1];
|
||||
}
|
||||
}
|
||||
|
||||
void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
|
||||
void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
|
||||
if (!battery_awake) {
|
||||
return;
|
||||
}
|
||||
|
@ -416,9 +420,13 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
|
||||
datalayer.battery.status.soh_pptt = battery_soh * 100;
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery.status.max_discharge_power_W = battery_BEV_available_power_longterm_discharge;
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = battery_BEV_available_power_longterm_charge;
|
||||
}
|
||||
|
||||
battery_power = (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100));
|
||||
|
||||
|
@ -499,9 +507,9 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
Serial.print(" Max temp: ");
|
||||
Serial.print(datalayer.battery.status.temperature_max_dC * 0.1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void receive_can_battery(CAN_frame rx_frame) {
|
||||
void receive_can_battery(CAN_frame rx_frame) {
|
||||
switch (rx_frame.ID) {
|
||||
case 0x112: //BMS [10ms] Status Of High-Voltage Battery - 2
|
||||
battery_awake = true;
|
||||
|
@ -629,7 +637,8 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
battery_ID2 = rx_frame.data.u8[0];
|
||||
break;
|
||||
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 ((cmdState == CELL_VOLTAGE_CELLNO || cmdState == CELL_VOLTAGE_CELLNO_LAST) &&
|
||||
(rx_frame.data.u8[0] == 0xF4)) {
|
||||
if (rx_frame.DLC == 6) {
|
||||
transmit_can(&BMW_6F4_CELL_CONTINUE, can_config.battery); // tell battery to send the cellvoltage
|
||||
}
|
||||
|
@ -679,8 +688,8 @@ void receive_can_battery(CAN_frame rx_frame) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void receive_can_battery2(CAN_frame rx_frame) {
|
||||
}
|
||||
void receive_can_battery2(CAN_frame rx_frame) {
|
||||
switch (rx_frame.ID) {
|
||||
case 0x112: //BMS [10ms] Status Of High-Voltage Battery - 2
|
||||
battery2_awake = true;
|
||||
|
@ -854,8 +863,8 @@ void receive_can_battery2(CAN_frame rx_frame) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void send_can_battery() {
|
||||
}
|
||||
void send_can_battery() {
|
||||
unsigned long currentMillis = millis();
|
||||
|
||||
if (battery_awake) {
|
||||
|
@ -1114,9 +1123,9 @@ void send_can_battery() {
|
|||
previousMillis5000 = currentMillis;
|
||||
previousMillis10000 = currentMillis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup_battery(void) { // Performs one time setup at startup
|
||||
void setup_battery(void) { // Performs one time setup at startup
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("BMW i3 battery selected");
|
||||
#endif
|
||||
|
@ -1136,6 +1145,6 @@ void setup_battery(void) { // Performs one time setup at startup
|
|||
#endif
|
||||
pinMode(WUP_PIN, OUTPUT);
|
||||
digitalWrite(WUP_PIN, HIGH); // Wake up the battery
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -101,9 +101,13 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
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);
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery.status.max_discharge_power_W = 10000; //TODO: Map from CAN later on
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = 10000; //TODO: Map from CAN later on
|
||||
}
|
||||
|
||||
datalayer.battery.status.active_power_W =
|
||||
(datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100));
|
||||
|
|
|
@ -114,8 +114,13 @@ void update_values_battery() {
|
|||
|
||||
datalayer.battery.status.real_soc = x102_chg_session.StateOfCharge;
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W =
|
||||
(x200_discharge_limits.MaximumDischargeCurrent * x100_chg_lim.MaximumBatteryVoltage); //In Watts, Convert A to P
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery.status.max_discharge_power_W = 10000; //TODO: Map from CAN later on
|
||||
datalayer.battery.status.max_charge_power_W = 1000;
|
||||
}
|
||||
|
||||
datalayer.battery.status.voltage_dV = get_measured_voltage() * 10;
|
||||
|
||||
|
|
|
@ -48,10 +48,14 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
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);
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
//We do not know the max charge/discharge power is sent by the battery. We hardcode value for now.
|
||||
datalayer.battery.status.max_charge_power_W = 10000; // 10kW //TODO: Fix when CAN is decoded
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W = 10000; // 10kW //TODO: Fix when CAN is decoded
|
||||
datalayer.battery.status.max_charge_power_W = 10000; // 10kW //TODO: Fix when CAN is decoded
|
||||
}
|
||||
|
||||
datalayer.battery.status.active_power_W = BMU_Power; //TODO: Scaling?
|
||||
|
||||
|
|
|
@ -89,11 +89,14 @@ void update_values_battery() {
|
|||
|
||||
datalayer.battery.status.temperature_max_dC = HVBattCellTempHottest * 10; // C to dC
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W =
|
||||
HVBattDischargeContiniousPowerLimit * 10; // kWh+2 to W (TODO: Check that scaling is right way)
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery.status.max_discharge_power_W = HVBattDischargeContiniousPowerLimit * 10; // kWh+2 to W
|
||||
datalayer.battery.status.max_charge_power_W =
|
||||
HVBattChargeContiniousPowerLimit * 10; // kWh+2 to W (TODO: Check that scaling is right way)
|
||||
}
|
||||
|
||||
if (HVBattHVILError) { // Alert user incase the high voltage interlock is not OK
|
||||
set_event(EVENT_HVIL_FAILURE, 0);
|
||||
|
|
|
@ -310,13 +310,17 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
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);
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
//datalayer.battery.status.max_charge_power_W = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts
|
||||
//The allowed charge power is not available. We hardcode this value for now
|
||||
datalayer.battery.status.max_charge_power_W = MAXCHARGEPOWERALLOWED;
|
||||
|
||||
//datalayer.battery.status.max_discharge_power_W = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts
|
||||
//The allowed discharge power is not available. We hardcode this value for now
|
||||
datalayer.battery.status.max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
|
||||
}
|
||||
|
||||
powerWatt = ((batteryVoltage * batteryAmps) / 100);
|
||||
|
||||
|
|
|
@ -123,9 +123,13 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
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);
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery.status.max_charge_power_W = allowedChargePower * 10;
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W = allowedDischargePower * 10;
|
||||
}
|
||||
|
||||
//Power in watts, Negative = charging batt
|
||||
datalayer.battery.status.active_power_W =
|
||||
|
|
|
@ -64,9 +64,13 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
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);
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery.status.max_discharge_power_W = available_discharge_power * 10;
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = available_charge_power * 10;
|
||||
}
|
||||
|
||||
//Power in watts, Negative = charging batt
|
||||
datalayer.battery.status.active_power_W =
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#endif
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
#include "../devboard/utils/pause.h"
|
||||
|
||||
/* Do not change code below unless you are sure what you are doing */
|
||||
static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send
|
||||
|
@ -228,9 +229,13 @@ void update_values_battery() { /* This function maps all the values fetched via
|
|||
}
|
||||
}
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery.status.max_discharge_power_W = (battery_Discharge_Power_Limit * 1000); //kW to W
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = (battery_Charge_Power_Limit * 1000); //kW to W
|
||||
}
|
||||
|
||||
/*Extra safety functions below*/
|
||||
if (battery_GIDS < 10) //700Wh left in battery!
|
||||
|
@ -379,9 +384,13 @@ void update_values_battery2() { // Handle the values coming in from battery #2
|
|||
}
|
||||
}
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery2.status.max_discharge_power_W = 0;
|
||||
datalayer.battery2.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery2.status.max_discharge_power_W = (battery2_Discharge_Power_Limit * 1000); //kW to W
|
||||
|
||||
datalayer.battery2.status.max_charge_power_W = (battery2_Charge_Power_Limit * 1000); //kW to W
|
||||
}
|
||||
|
||||
/*Extra safety functions below*/
|
||||
if (battery2_GIDS < 10) //700Wh left in battery!
|
||||
|
|
|
@ -63,9 +63,13 @@ void update_values_battery() {
|
|||
datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt
|
||||
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery.status.max_charge_power_W = (max_charge_current * (voltage_dV / 10));
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W = (-max_discharge_current * (voltage_dV / 10));
|
||||
}
|
||||
|
||||
datalayer.battery.status.cell_max_voltage_mV = cellvoltage_max_mV;
|
||||
|
||||
|
|
|
@ -87,13 +87,19 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
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);
|
||||
|
||||
/* Define power able to be discharged from battery */
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W =
|
||||
(LB_Discharge_Power_Limit * 500); //Convert value fetched from battery to watts
|
||||
|
||||
LB_Charge_Power_Limit_Watts = (LB_Charge_Power_Limit * 500); //Convert value fetched from battery to watts
|
||||
|
||||
//The above value is 0 on some packs. We instead hardcode this now.
|
||||
datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_W;
|
||||
}
|
||||
|
||||
datalayer.battery.status.active_power_W =
|
||||
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
||||
|
|
|
@ -47,9 +47,13 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
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);
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery.status.max_discharge_power_W = 5000; //TODO: Take from CAN
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = LB_Charge_Power_W;
|
||||
}
|
||||
|
||||
datalayer.battery.status.active_power_W;
|
||||
|
||||
|
|
|
@ -81,9 +81,13 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
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);
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery.status.max_discharge_power_W = allowedDischargePower * 10;
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = allowedChargePower * 10;
|
||||
}
|
||||
|
||||
//Power in watts, Negative = charging batt
|
||||
datalayer.battery.status.active_power_W =
|
||||
|
|
|
@ -278,6 +278,10 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
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);
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
} else {
|
||||
// Define the allowed discharge power
|
||||
datalayer.battery.status.max_discharge_power_W = (battery_max_discharge_current * battery_volts);
|
||||
// Cap the allowed discharge power if higher than the maximum discharge power allowed
|
||||
|
@ -305,6 +309,7 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
} else { // No limits, max charging power allowed
|
||||
datalayer.battery.status.max_charge_power_W = MAXCHARGEPOWERALLOWED;
|
||||
}
|
||||
}
|
||||
|
||||
datalayer.battery.status.active_power_W = ((battery_volts / 10) * battery_amps);
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "../include.h"
|
||||
#ifdef TEST_FAKE_BATTERY
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/pause.h"
|
||||
|
||||
#include "TEST-FAKE-BATTERY.h"
|
||||
|
||||
/* Do not change code below unless you are sure what you are doing */
|
||||
|
@ -44,9 +46,13 @@ void update_values_battery() { /* This function puts fake values onto the parame
|
|||
|
||||
datalayer.battery.status.temperature_max_dC = 60; // 6.0*C
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
datalayer.battery.status.max_discharge_power_W = 5000; // 5kW
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = 5000; // 5kW
|
||||
}
|
||||
|
||||
for (int i = 0; i < 97; ++i) {
|
||||
datalayer.battery.status.cell_voltages_mV[i] = 3500 + i;
|
||||
|
|
|
@ -83,9 +83,14 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer.battery.status.current_dA = BATT_I * 10;
|
||||
datalayer.battery.status.remaining_capacity_Wh = remaining_capacity;
|
||||
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
} else {
|
||||
//datalayer.battery.status.max_discharge_power_W = HvBattPwrLimDchaSoft * 1000; // Use power limit reported from BMS, not trusted ATM
|
||||
datalayer.battery.status.max_discharge_power_W = 30000;
|
||||
datalayer.battery.status.max_charge_power_W = 30000;
|
||||
}
|
||||
datalayer.battery.status.active_power_W = (BATT_U)*BATT_I;
|
||||
datalayer.battery.status.temperature_min_dC = BATT_T_MIN;
|
||||
datalayer.battery.status.temperature_max_dC = BATT_T_MAX;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h"
|
||||
#include "../../lib/knolleary-pubsubclient/PubSubClient.h"
|
||||
#include "../utils/events.h"
|
||||
#include "../utils/pause.h"
|
||||
#include "../utils/timer.h"
|
||||
|
||||
WiFiClient espClient;
|
||||
|
@ -56,6 +57,7 @@ SensorConfig sensorConfigs[] = {
|
|||
{"max_charge_power", "Battery Emulator Battery Max Charge Power", "{{ value_json.max_charge_power }}", "W",
|
||||
"power"},
|
||||
{"bms_status", "Battery Emulator BMS Status", "{{ value_json.bms_status }}", "", ""},
|
||||
{"pause_status", "Battery Emulator Pause Status", "{{ value_json.pause_status }}", "", ""},
|
||||
|
||||
};
|
||||
|
||||
|
@ -112,6 +114,11 @@ static void publish_common_info(void) {
|
|||
|
||||
} else {
|
||||
#endif // HA_AUTODISCOVERY
|
||||
doc["bms_status"] = getBMSStatus(datalayer.battery.status.bms_status);
|
||||
doc["pause_status"] = get_emulator_pause_status();
|
||||
|
||||
//only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery)
|
||||
if (datalayer.battery.status.bms_status == ACTIVE && can_send_CAN) {
|
||||
doc["SOC"] = ((float)datalayer.battery.status.reported_soc) / 100.0;
|
||||
doc["SOC_real"] = ((float)datalayer.battery.status.real_soc) / 100.0;
|
||||
doc["state_of_health"] = ((float)datalayer.battery.status.soh_pptt) / 100.0;
|
||||
|
@ -130,7 +137,7 @@ static void publish_common_info(void) {
|
|||
doc["remaining_capacity"] = ((float)datalayer.battery.status.remaining_capacity_Wh);
|
||||
doc["max_discharge_power"] = ((float)datalayer.battery.status.max_discharge_power_W);
|
||||
doc["max_charge_power"] = ((float)datalayer.battery.status.max_charge_power_W);
|
||||
doc["bms_status"] = getBMSStatus(datalayer.battery.status.bms_status);
|
||||
}
|
||||
|
||||
serializeJson(doc, mqtt_msg);
|
||||
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||
|
|
|
@ -198,6 +198,8 @@ void init_events(void) {
|
|||
events.entries[EVENT_RESET_EFUSE].level = EVENT_LEVEL_INFO;
|
||||
events.entries[EVENT_RESET_PWR_GLITCH].level = EVENT_LEVEL_INFO;
|
||||
events.entries[EVENT_RESET_CPU_LOCKUP].level = EVENT_LEVEL_WARNING;
|
||||
events.entries[EVENT_PAUSE_BEGIN].level = EVENT_LEVEL_WARNING;
|
||||
events.entries[EVENT_PAUSE_END].level = EVENT_LEVEL_INFO;
|
||||
|
||||
events.entries[EVENT_EEPROM_WRITE].log = false; // Don't log the logger...
|
||||
|
||||
|
@ -367,6 +369,10 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
|
|||
return "Info: The board was reset due to a detected power glitch";
|
||||
case EVENT_RESET_CPU_LOCKUP:
|
||||
return "Warning: The board was reset due to CPU lockup. Inform developers!";
|
||||
case EVENT_PAUSE_BEGIN:
|
||||
return "Warning: The emulator is trying to pause the battery.";
|
||||
case EVENT_PAUSE_END:
|
||||
return "Info: The emulator is attempting to resume battery operation from pause.";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -93,6 +93,8 @@
|
|||
XX(EVENT_RESET_EFUSE) \
|
||||
XX(EVENT_RESET_PWR_GLITCH) \
|
||||
XX(EVENT_RESET_CPU_LOCKUP) \
|
||||
XX(EVENT_PAUSE_BEGIN) \
|
||||
XX(EVENT_PAUSE_END) \
|
||||
XX(EVENT_NOF_EVENTS)
|
||||
|
||||
typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE;
|
||||
|
|
73
Software/src/devboard/utils/pause.cpp
Normal file
73
Software/src/devboard/utils/pause.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include "pause.h"
|
||||
#include "../../datalayer/datalayer.h"
|
||||
#include "events.h"
|
||||
|
||||
|
||||
bool emulator_pause_request_ON = false;
|
||||
bool emulator_pause_CAN_send_ON = false;
|
||||
bool can_send_CAN = true;
|
||||
|
||||
|
||||
battery_pause_status emulator_pause_status = NORMAL;
|
||||
|
||||
void setBatteryPause(bool pause_battery,bool pause_CAN) {
|
||||
|
||||
emulator_pause_CAN_send_ON = pause_CAN;
|
||||
|
||||
if (pause_battery) {
|
||||
|
||||
set_event(EVENT_PAUSE_BEGIN, 1);
|
||||
emulator_pause_request_ON = true;
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
#ifdef DOUBLE_BATTERY
|
||||
datalayer.battery2.status.max_discharge_power_W = 0;
|
||||
datalayer.battery2.status.max_charge_power_W = 0;
|
||||
#endif
|
||||
|
||||
emulator_pause_status = PAUSING;
|
||||
} else {
|
||||
clear_event(EVENT_PAUSE_BEGIN);
|
||||
set_event(EVENT_PAUSE_END, 0);
|
||||
emulator_pause_request_ON = false;
|
||||
emulator_pause_CAN_send_ON = false;
|
||||
emulator_pause_status = RESUMING;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief handle emulator pause status
|
||||
/// @return true if CAN messages should be sent to battery, false if not
|
||||
void emulator_pause_state_send_CAN_battery() {
|
||||
|
||||
|
||||
if (emulator_pause_status == NORMAL)
|
||||
can_send_CAN = true;
|
||||
|
||||
// in some inverters this values are not accurate, so we need to check if we are consider 1.8 amps as the limit
|
||||
if (emulator_pause_request_ON && emulator_pause_status == PAUSING && datalayer.battery.status.current_dA < 18 &&
|
||||
datalayer.battery.status.current_dA > -18 ) {
|
||||
emulator_pause_status = PAUSED;
|
||||
}
|
||||
|
||||
if (!emulator_pause_request_ON && emulator_pause_status == RESUMING) {
|
||||
emulator_pause_status = NORMAL;
|
||||
can_send_CAN = true;
|
||||
}
|
||||
|
||||
can_send_CAN = (!emulator_pause_CAN_send_ON || emulator_pause_status == NORMAL);
|
||||
}
|
||||
|
||||
std::string get_emulator_pause_status() {
|
||||
switch (emulator_pause_status) {
|
||||
case NORMAL:
|
||||
return "RUNNING";
|
||||
case PAUSING:
|
||||
return "PAUSING";
|
||||
case PAUSED:
|
||||
return "PAUSED";
|
||||
case RESUMING:
|
||||
return "RESUMING";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
17
Software/src/devboard/utils/pause.h
Normal file
17
Software/src/devboard/utils/pause.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef _PAUSE_H_
|
||||
#define _PAUSE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
//battery pause status
|
||||
enum battery_pause_status { NORMAL = 0, PAUSING = 1, PAUSED = 2, RESUMING = 3 };
|
||||
extern bool emulator_pause_request_ON;
|
||||
extern bool emulator_pause_CAN_send_ON;
|
||||
extern battery_pause_status emulator_pause_status;
|
||||
extern bool can_send_CAN;
|
||||
|
||||
void setBatteryPause(bool pause_battery,bool pause_CAN) ;
|
||||
void emulator_pause_state_send_CAN_battery();
|
||||
std::string get_emulator_pause_status();
|
||||
#endif
|
|
@ -3,6 +3,7 @@
|
|||
#include "../../datalayer/datalayer.h"
|
||||
#include "../utils/events.h"
|
||||
#include "../utils/led_handler.h"
|
||||
#include "../utils/pause.h"
|
||||
#include "../utils/timer.h"
|
||||
|
||||
// Create AsyncWebServer object on port 80
|
||||
|
@ -159,6 +160,19 @@ void init_webserver() {
|
|||
}
|
||||
});
|
||||
|
||||
// Route for pause/resume Battery emulator
|
||||
server.on("/pause", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||
return request->requestAuthentication();
|
||||
if (request->hasParam("p")) {
|
||||
String valueStr = request->getParam("p")->value();
|
||||
setBatteryPause(valueStr == "true" || valueStr == "1", false);
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Bad Request");
|
||||
}
|
||||
});
|
||||
|
||||
// Route for editing SOCMin
|
||||
server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||
|
@ -414,10 +428,8 @@ void wifi_monitor() {
|
|||
|
||||
if (ota_active && ota_timeout_timer.elapsed()) {
|
||||
// OTA timeout, try to restore can and clear the update event
|
||||
ESP32Can.CANInit();
|
||||
clear_event(EVENT_OTA_UPDATE);
|
||||
set_event(EVENT_OTA_UPDATE_TIMEOUT, 0);
|
||||
ota_active = false;
|
||||
onOTAEnd(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -702,6 +714,12 @@ String processor(const String& var) {
|
|||
} else {
|
||||
content += "<span style='color: red;'>✕</span></h4>";
|
||||
}
|
||||
if (emulator_pause_status == NORMAL)
|
||||
content += "<h4>Pause status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
else
|
||||
content +=
|
||||
"<h4 style='color: red;'>Pause status: " + String(get_emulator_pause_status().c_str()) +
|
||||
" </h4>";
|
||||
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
|
@ -778,6 +796,12 @@ String processor(const String& var) {
|
|||
} else {
|
||||
content += "<span style='color: red;'>✕</span></h4>";
|
||||
}
|
||||
if (emulator_pause_status == NORMAL)
|
||||
content += "<h4>Pause status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
else
|
||||
content +=
|
||||
"<h4 style='color: red;'>Pause status: " + String(get_emulator_pause_status().c_str()) +
|
||||
" </h4>";
|
||||
|
||||
content += "</div>";
|
||||
content += "</div>";
|
||||
|
@ -839,6 +863,11 @@ String processor(const String& var) {
|
|||
content += "</div>";
|
||||
#endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
|
||||
|
||||
if (emulator_pause_request_ON)
|
||||
content += "<button onclick='PauseBattery(false)'>Resume Battery</button>";
|
||||
else
|
||||
content += "<button onclick='PauseBattery(true)'>Pause Battery</button>";
|
||||
|
||||
content += "<button onclick='OTA()'>Perform OTA update</button>";
|
||||
content += " ";
|
||||
content += "<button onclick='Settings()'>Change Settings</button>";
|
||||
|
@ -872,6 +901,12 @@ String processor(const String& var) {
|
|||
content += " setTimeout(function(){ window.open(\"/\",\"_self\"); }, 1000);";
|
||||
content += "}";
|
||||
}
|
||||
content += "function PauseBattery(pause){";
|
||||
content +=
|
||||
"var xhr=new "
|
||||
"XMLHttpRequest();xhr.onload=function() { "
|
||||
"window.location.reload();};xhr.open('GET','/pause?p='+pause,true);xhr.send();";
|
||||
content += "}";
|
||||
|
||||
content += "</script>";
|
||||
|
||||
|
@ -886,8 +921,10 @@ String processor(const String& var) {
|
|||
}
|
||||
|
||||
void onOTAStart() {
|
||||
//try to Pause the battery
|
||||
setBatteryPause(true, true);
|
||||
|
||||
// Log when OTA has started
|
||||
ESP32Can.CANStop();
|
||||
set_event(EVENT_OTA_UPDATE, 0);
|
||||
|
||||
// If already set, make a new attempt
|
||||
|
@ -909,6 +946,9 @@ void onOTAProgress(size_t current, size_t final) {
|
|||
}
|
||||
|
||||
void onOTAEnd(bool success) {
|
||||
|
||||
//try to Resume the battery
|
||||
setBatteryPause(false, false);
|
||||
// Log when OTA has finished
|
||||
if (success) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
|
@ -918,9 +958,6 @@ void onOTAEnd(bool success) {
|
|||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("There was an error during OTA update!");
|
||||
#endif // DEBUG_VIA_USB
|
||||
|
||||
// If we fail without a timeout, try to restore CAN
|
||||
ESP32Can.CANInit();
|
||||
}
|
||||
ota_active = false;
|
||||
clear_event(EVENT_OTA_UPDATE);
|
||||
|
|
|
@ -43,6 +43,15 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
|||
return request->requestAuthentication();
|
||||
}
|
||||
|
||||
// Pre-OTA update callback
|
||||
if (preUpdateCallback != NULL) preUpdateCallback();
|
||||
|
||||
// Sleep for 3 seconds to allow asynchronous preUpdateCallback tasks to complete
|
||||
unsigned long sleepStart = millis();
|
||||
while (millis() - sleepStart < 3000) { // Sleep for 3 second
|
||||
delay(1); // Yield to other tasks
|
||||
}
|
||||
|
||||
// Get header x-ota-mode value, if present
|
||||
OTA_Mode mode = OTA_MODE_FIRMWARE;
|
||||
// Get mode from arg
|
||||
|
@ -73,7 +82,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
|||
#endif
|
||||
|
||||
// Pre-OTA update callback
|
||||
if (preUpdateCallback != NULL) preUpdateCallback();
|
||||
//if (preUpdateCallback != NULL) preUpdateCallback();
|
||||
|
||||
// Start update process
|
||||
#if defined(ESP8266)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue