From 856a0838d1f057843eb09de49b499103280a1275 Mon Sep 17 00:00:00 2001
From: amarofarinha <151563493+amarofarinha@users.noreply.github.com>
Date: Fri, 13 Sep 2024 15:32:54 +0100
Subject: [PATCH 1/9] Improvement: Add Battery Pause Feature for Max
Charge/Discharge Power and OTA Update Optimization
---
Software/src/battery/BMW-I3-BATTERY.cpp | 1345 +++++++++--------
Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 10 +-
Software/src/battery/CHADEMO-BATTERY.cpp | 9 +-
.../src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 12 +-
Software/src/battery/JAGUAR-IPACE-BATTERY.cpp | 13 +-
Software/src/battery/KIA-E-GMP-BATTERY.cpp | 18 +-
.../src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 10 +-
.../battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp | 10 +-
Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 21 +-
Software/src/battery/PYLON-BATTERY.cpp | 10 +-
.../src/battery/RENAULT-KANGOO-BATTERY.cpp | 18 +-
.../src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp | 10 +-
.../src/battery/SANTA-FE-PHEV-BATTERY.cpp | 10 +-
Software/src/battery/TESLA-BATTERY.cpp | 55 +-
Software/src/battery/TEST-FAKE-BATTERY.cpp | 12 +-
Software/src/battery/VOLVO-SPA-BATTERY.cpp | 11 +-
Software/src/devboard/mqtt/mqtt.cpp | 43 +-
Software/src/devboard/utils/events.cpp | 6 +
Software/src/devboard/utils/events.h | 2 +
Software/src/devboard/utils/pause.cpp | 73 +
Software/src/devboard/utils/pause.h | 17 +
Software/src/devboard/webserver/webserver.cpp | 51 +-
.../src/ElegantOTA.cpp | 11 +-
23 files changed, 1004 insertions(+), 773 deletions(-)
create mode 100644 Software/src/devboard/utils/pause.cpp
create mode 100644 Software/src/devboard/utils/pause.h
diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp
index f0e67617..71e987d2 100644
--- a/Software/src/battery/BMW-I3-BATTERY.cpp
+++ b/Software/src/battery/BMW-I3-BATTERY.cpp
@@ -376,766 +376,775 @@ void update_values_battery2() { //This function maps all the values fetched via
datalayer.battery2.status.soh_pptt = battery2_soh * 100;
- if (battery2_BEV_available_power_longterm_discharge > 65000) {
- datalayer.battery2.status.max_discharge_power_W = 65000;
+ if (emulator_pause_request_ON) {
+ datalayer.battery.status.max_discharge_power_W = 0;
+ datalayer.battery.status.max_charge_power_W = 0;
} else {
- datalayer.battery2.status.max_discharge_power_W = battery2_BEV_available_power_longterm_discharge;
- }
- if (battery2_BEV_available_power_longterm_charge > 65000) {
- datalayer.battery2.status.max_charge_power_W = 65000;
- } else {
- datalayer.battery2.status.max_charge_power_W = battery2_BEV_available_power_longterm_charge;
+ if (battery2_BEV_available_power_longterm_discharge > 65000) {
+ datalayer.battery2.status.max_discharge_power_W = 65000;
+ } else {
+ datalayer.battery2.status.max_discharge_power_W = battery2_BEV_available_power_longterm_discharge;
+ }
+ if (battery2_BEV_available_power_longterm_charge > 65000) {
+ datalayer.battery2.status.max_charge_power_W = 65000;
+ } else {
+ datalayer.battery2.status.max_charge_power_W = battery2_BEV_available_power_longterm_charge;
+ }
+
+ battery2_power = (datalayer.battery2.status.current_dA * (datalayer.battery2.status.voltage_dV / 100));
+
+ datalayer.battery2.status.active_power_W = battery2_power;
+
+ datalayer.battery2.status.temperature_min_dC = battery2_temperature_min * 10; // Add a decimal
+
+ datalayer.battery2.status.temperature_max_dC = battery2_temperature_max * 10; // Add a decimal
+
+ 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];
}
- battery2_power = (datalayer.battery2.status.current_dA * (datalayer.battery2.status.voltage_dV / 100));
+ void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
+ if (!battery_awake) {
+ return;
+ }
- datalayer.battery2.status.active_power_W = battery2_power;
+ datalayer.battery.status.real_soc = (battery_display_SOC * 50);
- datalayer.battery2.status.temperature_min_dC = battery2_temperature_min * 10; // Add a decimal
+ datalayer.battery.status.voltage_dV = battery_volts; //Unit V+1 (5000 = 500.0V)
- datalayer.battery2.status.temperature_max_dC = battery2_temperature_max * 10; // Add a decimal
+ datalayer.battery.status.current_dA = battery_current;
- 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];
-}
+ datalayer.battery.info.total_capacity_Wh = (battery_energy_content_maximum_kWh * 1000); // Convert kWh to Wh
-void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
- if (!battery_awake) {
- return;
- }
+ datalayer.battery.status.remaining_capacity_Wh = battery_predicted_energy_charge_condition;
- datalayer.battery.status.real_soc = (battery_display_SOC * 50);
+ datalayer.battery.status.soh_pptt = battery_soh * 100;
- datalayer.battery.status.voltage_dV = battery_volts; //Unit V+1 (5000 = 500.0V)
+ 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;
+ }
- datalayer.battery.status.current_dA = battery_current;
+ battery_power = (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100));
- datalayer.battery.info.total_capacity_Wh = (battery_energy_content_maximum_kWh * 1000); // Convert kWh to Wh
+ datalayer.battery.status.active_power_W = battery_power;
- datalayer.battery.status.remaining_capacity_Wh = battery_predicted_energy_charge_condition;
+ datalayer.battery.status.temperature_min_dC = battery_temperature_min * 10; // Add a decimal
- datalayer.battery.status.soh_pptt = battery_soh * 100;
+ datalayer.battery.status.temperature_max_dC = battery_temperature_max * 10; // Add a decimal
- 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));
-
- datalayer.battery.status.active_power_W = battery_power;
-
- datalayer.battery.status.temperature_min_dC = battery_temperature_min * 10; // Add a decimal
-
- datalayer.battery.status.temperature_max_dC = battery_temperature_max * 10; // Add a decimal
-
- if (battery_info_available) {
- // Start checking safeties. First up, cellvoltages!
- if (detectedBattery == BATTERY_60AH) {
- datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
- datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
- if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_60AH) {
- set_event(EVENT_CELL_OVER_VOLTAGE, 0);
- }
- if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_60AH) {
- set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
- }
- } else if (detectedBattery == BATTERY_94AH) {
- datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_94AH;
- datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_94AH;
- if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_94AH) {
- set_event(EVENT_CELL_OVER_VOLTAGE, 0);
- }
- if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_94AH) {
- set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
- }
- } else { // BATTERY_120AH
- datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_120AH;
- datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_120AH;
- if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_120AH) {
- set_event(EVENT_CELL_OVER_VOLTAGE, 0);
- }
- if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_120AH) {
- set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
+ if (battery_info_available) {
+ // Start checking safeties. First up, cellvoltages!
+ if (detectedBattery == BATTERY_60AH) {
+ datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
+ datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
+ if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_60AH) {
+ set_event(EVENT_CELL_OVER_VOLTAGE, 0);
+ }
+ if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_60AH) {
+ set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
+ }
+ } else if (detectedBattery == BATTERY_94AH) {
+ datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_94AH;
+ datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_94AH;
+ if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_94AH) {
+ set_event(EVENT_CELL_OVER_VOLTAGE, 0);
+ }
+ if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_94AH) {
+ set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
+ }
+ } else { // BATTERY_120AH
+ datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_120AH;
+ datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_120AH;
+ if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_120AH) {
+ set_event(EVENT_CELL_OVER_VOLTAGE, 0);
+ }
+ if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_120AH) {
+ set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
+ }
}
}
- }
- // Perform other safety checks
- if (battery_status_error_locking == 2) { // HVIL seated?
- set_event(EVENT_HVIL_FAILURE, 0);
- } else {
- clear_event(EVENT_HVIL_FAILURE);
- }
- if (battery_status_precharge_locked == 2) { // Capacitor seated?
- set_event(EVENT_PRECHARGE_FAILURE, 0);
- } else {
- clear_event(EVENT_PRECHARGE_FAILURE);
- }
+ // Perform other safety checks
+ if (battery_status_error_locking == 2) { // HVIL seated?
+ set_event(EVENT_HVIL_FAILURE, 0);
+ } else {
+ clear_event(EVENT_HVIL_FAILURE);
+ }
+ if (battery_status_precharge_locked == 2) { // Capacitor seated?
+ set_event(EVENT_PRECHARGE_FAILURE, 0);
+ } else {
+ clear_event(EVENT_PRECHARGE_FAILURE);
+ }
#ifdef DEBUG_VIA_USB
- Serial.println(" ");
- Serial.print("Battery display SOC%: ");
- Serial.print(battery_display_SOC * 50);
- Serial.print("Battery display SOC%: ");
- Serial.print(battery_HVBatt_SOC * 10);
- Serial.print("Battery polled SOC%: ");
- Serial.print(battery_soc);
- Serial.print(" Battery voltage: ");
- Serial.print(datalayer.battery.status.voltage_dV * 0.1);
- Serial.print(" Battery current: ");
- Serial.print(datalayer.battery.status.current_dA * 0.1);
- Serial.print(" Wh when full: ");
- Serial.print(datalayer.battery.info.total_capacity_Wh);
- Serial.print(" Remaining Wh: ");
- Serial.print(datalayer.battery.status.remaining_capacity_Wh);
- Serial.print(" Max charge power: ");
- Serial.print(datalayer.battery.status.max_charge_power_W);
- Serial.print(" Max discharge power: ");
- Serial.print(datalayer.battery.status.max_discharge_power_W);
- Serial.print(" Active power: ");
- Serial.print(datalayer.battery.status.active_power_W);
- Serial.print(" Min temp: ");
- Serial.print(datalayer.battery.status.temperature_min_dC * 0.1);
- Serial.print(" Max temp: ");
- Serial.print(datalayer.battery.status.temperature_max_dC * 0.1);
+ Serial.println(" ");
+ Serial.print("Battery display SOC%: ");
+ Serial.print(battery_display_SOC * 50);
+ Serial.print("Battery display SOC%: ");
+ Serial.print(battery_HVBatt_SOC * 10);
+ Serial.print("Battery polled SOC%: ");
+ Serial.print(battery_soc);
+ Serial.print(" Battery voltage: ");
+ Serial.print(datalayer.battery.status.voltage_dV * 0.1);
+ Serial.print(" Battery current: ");
+ Serial.print(datalayer.battery.status.current_dA * 0.1);
+ Serial.print(" Wh when full: ");
+ Serial.print(datalayer.battery.info.total_capacity_Wh);
+ Serial.print(" Remaining Wh: ");
+ Serial.print(datalayer.battery.status.remaining_capacity_Wh);
+ Serial.print(" Max charge power: ");
+ Serial.print(datalayer.battery.status.max_charge_power_W);
+ Serial.print(" Max discharge power: ");
+ Serial.print(datalayer.battery.status.max_discharge_power_W);
+ Serial.print(" Active power: ");
+ Serial.print(datalayer.battery.status.active_power_W);
+ Serial.print(" Min temp: ");
+ Serial.print(datalayer.battery.status.temperature_min_dC * 0.1);
+ Serial.print(" Max temp: ");
+ Serial.print(datalayer.battery.status.temperature_max_dC * 0.1);
#endif
-}
-
-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;
- datalayer.battery.status.CAN_battery_still_alive =
- CAN_STILL_ALIVE; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
- battery_current = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) - 8192; //deciAmps (-819.2 to 819.0A)
- battery_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
- datalayer.battery.status.voltage_dV = battery_volts; // Update the datalayer as soon as possible with this info
- battery_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
- battery_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
- battery_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
- battery_request_open_contactors_fast = (rx_frame.data.u8[6] & 0x0C) >> 2;
- battery_charging_condition_delta = (rx_frame.data.u8[6] & 0xF0) >> 4;
- battery_DC_link_voltage = rx_frame.data.u8[7];
- break;
- case 0x1FA: //BMS [1000ms] Status Of High-Voltage Battery - 1
- battery_status_error_isolation_external_Bordnetz = (rx_frame.data.u8[0] & 0x03);
- battery_status_error_isolation_internal_Bordnetz = (rx_frame.data.u8[0] & 0x0C) >> 2;
- battery_request_cooling = (rx_frame.data.u8[0] & 0x30) >> 4;
- battery_status_valve_cooling = (rx_frame.data.u8[0] & 0xC0) >> 6;
- battery_status_error_locking = (rx_frame.data.u8[1] & 0x03);
- battery_status_precharge_locked = (rx_frame.data.u8[1] & 0x0C) >> 2;
- battery_status_disconnecting_switch = (rx_frame.data.u8[1] & 0x30) >> 4;
- battery_status_emergency_mode = (rx_frame.data.u8[1] & 0xC0) >> 6;
- battery_request_service = (rx_frame.data.u8[2] & 0x03);
- battery_error_emergency_mode = (rx_frame.data.u8[2] & 0x0C) >> 2;
- battery_status_error_disconnecting_switch = (rx_frame.data.u8[2] & 0x30) >> 4;
- battery_status_warning_isolation = (rx_frame.data.u8[2] & 0xC0) >> 6;
- battery_status_cold_shutoff_valve = (rx_frame.data.u8[3] & 0x0F);
- battery_temperature_HV = (rx_frame.data.u8[4] - 50);
- battery_temperature_heat_exchanger = (rx_frame.data.u8[5] - 50);
- battery_temperature_min = (rx_frame.data.u8[6] - 50);
- battery_temperature_max = (rx_frame.data.u8[7] - 50);
- break;
- case 0x239: //BMS [200ms]
- battery_predicted_energy_charge_condition = (rx_frame.data.u8[2] << 8 | rx_frame.data.u8[1]); //Wh
- battery_predicted_energy_charging_target = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[3]) * 0.02); //kWh
- break;
- case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
- battery_awake = true;
- if (!skipCRCCheck) {
- if (calculateCRC(rx_frame, rx_frame.DLC, 0x15) != rx_frame.data.u8[0]) {
- // If calculated CRC does not match transmitted CRC, increase CANerror counter
- datalayer.battery.status.CAN_error_counter++;
-
- // If the CRC check has never passed before, set the flag to skip future checks. Some SMEs have differing CRC checks.
- if (!CRCCheckPassedPreviously) {
- skipCRCCheck = true;
- }
- break;
- } else {
- // If CRC check passes, update the flag
- CRCCheckPassedPreviously = true;
- }
- }
-
- // Process the data since CRC check is either passed or skipped
- battery_status_diagnostics_HV = (rx_frame.data.u8[2] & 0x0F);
- break;
- case 0x2F5: //BMS [100ms] High-Voltage Battery Charge/Discharge Limitations
- battery_max_charge_voltage = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- battery_max_charge_amperage = (((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 819.2);
- battery_min_discharge_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
- battery_max_discharge_amperage = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) - 819.2);
- break;
- case 0x2FF: //BMS [100ms] Status Heating High-Voltage Battery
- battery_awake = true;
- battery_actual_value_power_heating = (rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4);
- break;
- case 0x363: //BMS [1s] Identification High-Voltage Battery
- battery_serial_number =
- (rx_frame.data.u8[3] << 24 | rx_frame.data.u8[2] << 16 | rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- break;
- case 0x3C2: //BMS (94AH exclusive) - Status diagnostics OBD 2 powertrain
- battery_status_diagnosis_powertrain_maximum_multiplexer =
- ((rx_frame.data.u8[1] & 0x03) << 4 | rx_frame.data.u8[0] >> 4);
- battery_status_diagnosis_powertrain_immediate_multiplexer = (rx_frame.data.u8[0] & 0xFC) >> 2;
- break;
- case 0x3EB: //BMS [1s] Status of charging high-voltage storage - 3
- battery_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
- battery_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
- battery_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
- battery_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
- break;
- case 0x40D: //BMS [1s] Charging status of high-voltage storage - 1
- battery_BEV_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
- battery_BEV_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
- battery_BEV_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
- battery_BEV_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
- break;
- case 0x41C: //BMS [1s] Operating Mode Status Of Hybrid - 2
- battery_status_cooling_HV = (rx_frame.data.u8[1] & 0x03);
- break;
- case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
- battery_prediction_voltage_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- battery_prediction_voltage_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
- battery_prediction_voltage_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
- battery_prediction_voltage_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]);
- break;
- case 0x431: //BMS [200ms] Data High-Voltage Battery Unit
- battery_status_service_disconnection_plug = (rx_frame.data.u8[0] & 0x0F);
- battery_status_measurement_isolation = (rx_frame.data.u8[0] & 0x0C) >> 2;
- battery_request_abort_charging = (rx_frame.data.u8[0] & 0x30) >> 4;
- battery_prediction_duration_charging_minutes = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
- battery_prediction_time_end_of_charging_minutes = rx_frame.data.u8[4];
- battery_energy_content_maximum_kWh = (((rx_frame.data.u8[6] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
- if (battery_energy_content_maximum_kWh > 33) {
- detectedBattery = BATTERY_120AH;
- } else if (battery_energy_content_maximum_kWh > 20) {
- detectedBattery = BATTERY_94AH;
- } else {
- detectedBattery = BATTERY_60AH;
- }
- break;
- case 0x432: //BMS [200ms] SOC% info
- battery_request_operating_mode = (rx_frame.data.u8[0] & 0x03);
- battery_target_voltage_in_CV_mode = ((rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4)) / 10;
- battery_request_charging_condition_minimum = (rx_frame.data.u8[2] / 2);
- battery_request_charging_condition_maximum = (rx_frame.data.u8[3] / 2);
- battery_display_SOC = rx_frame.data.u8[4];
- break;
- case 0x507: //BMS [640ms] Network Management - 2 - This message is sent on the bus for sleep coordination purposes
- break;
- case 0x587: //BMS [5s] Services
- 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 (rx_frame.DLC == 6) {
- transmit_can(&BMW_6F4_CELL_CONTINUE, can_config.battery); // 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] =
- (rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]);
- }
- }
-
- if (rx_frame.DLC > 6 && next_data == 0 && rx_frame.data.u8[0] == 0xf1) {
- uint8_t count = 6;
- while (count < rx_frame.DLC && next_data < 49) {
- message_data[next_data++] = rx_frame.data.u8[count++];
- }
- transmit_can(&BMW_6F1_CONTINUE, can_config.battery); // 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)) {
- uint8_t count = 2;
- while (count < rx_frame.DLC && next_data < 49) {
- message_data[next_data++] = rx_frame.data.u8[count++];
- }
-
- switch (cmdState) {
- case CELL_VOLTAGE_MINMAX:
- if (next_data >= 4) {
- datalayer.battery.status.cell_min_voltage_mV = (message_data[0] << 8 | message_data[1]);
- datalayer.battery.status.cell_max_voltage_mV = (message_data[2] << 8 | message_data[3]);
- }
- break;
- case SOH:
- if (next_data >= 4) {
- battery_soh = message_data[3];
- battery_info_available = true;
- }
- break;
- case SOC:
- if (next_data >= 6) {
- battery_soc = (message_data[0] << 8 | message_data[1]);
- battery_soc_hvmax = (message_data[2] << 8 | message_data[3]);
- battery_soc_hvmin = (message_data[4] << 8 | message_data[5]);
- }
- break;
- }
- }
- break;
- default:
- break;
}
-}
-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;
- datalayer.battery2.status.CAN_battery_still_alive =
- CAN_STILL_ALIVE; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
- battery2_current = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) - 8192; //deciAmps (-819.2 to 819.0A)
- battery2_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
- datalayer.battery2.status.voltage_dV =
- battery2_volts; // Update the datalayer as soon as possible with this info, needed for contactor control
- battery2_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
- battery2_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
- battery2_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
- battery2_request_open_contactors_fast = (rx_frame.data.u8[6] & 0x0C) >> 2;
- battery2_charging_condition_delta = (rx_frame.data.u8[6] & 0xF0) >> 4;
- battery2_DC_link_voltage = rx_frame.data.u8[7];
- break;
- case 0x1FA: //BMS [1000ms] Status Of High-Voltage Battery - 1
- battery2_status_error_isolation_external_Bordnetz = (rx_frame.data.u8[0] & 0x03);
- battery2_status_error_isolation_internal_Bordnetz = (rx_frame.data.u8[0] & 0x0C) >> 2;
- battery2_request_cooling = (rx_frame.data.u8[0] & 0x30) >> 4;
- battery2_status_valve_cooling = (rx_frame.data.u8[0] & 0xC0) >> 6;
- battery2_status_error_locking = (rx_frame.data.u8[1] & 0x03);
- battery2_status_precharge_locked = (rx_frame.data.u8[1] & 0x0C) >> 2;
- battery2_status_disconnecting_switch = (rx_frame.data.u8[1] & 0x30) >> 4;
- battery2_status_emergency_mode = (rx_frame.data.u8[1] & 0xC0) >> 6;
- battery2_request_service = (rx_frame.data.u8[2] & 0x03);
- battery2_error_emergency_mode = (rx_frame.data.u8[2] & 0x0C) >> 2;
- battery2_status_error_disconnecting_switch = (rx_frame.data.u8[2] & 0x30) >> 4;
- battery2_status_warning_isolation = (rx_frame.data.u8[2] & 0xC0) >> 6;
- battery2_status_cold_shutoff_valve = (rx_frame.data.u8[3] & 0x0F);
- battery2_temperature_HV = (rx_frame.data.u8[4] - 50);
- battery2_temperature_heat_exchanger = (rx_frame.data.u8[5] - 50);
- battery2_temperature_min = (rx_frame.data.u8[6] - 50);
- battery2_temperature_max = (rx_frame.data.u8[7] - 50);
- break;
- case 0x239: //BMS [200ms]
- battery2_predicted_energy_charge_condition = (rx_frame.data.u8[2] << 8 | rx_frame.data.u8[1]); //Wh
- battery2_predicted_energy_charging_target = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[3]) * 0.02); //kWh
- break;
- case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
- battery2_awake = true;
- if (!skipCRCCheck_battery2) {
- if (calculateCRC(rx_frame, rx_frame.DLC, 0x15) != rx_frame.data.u8[0]) {
- // If calculated CRC does not match transmitted CRC, increase CANerror counter
- datalayer.battery2.status.CAN_error_counter++;
- // If the CRC check has never passed before, set the flag to skip future checks. Some SMEs have differing CRC checks.
- if (!CRCCheckPassedPreviously_battery2) {
- skipCRCCheck_battery2 = true;
+ 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;
+ datalayer.battery.status.CAN_battery_still_alive =
+ CAN_STILL_ALIVE; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
+ battery_current = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) - 8192; //deciAmps (-819.2 to 819.0A)
+ battery_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
+ datalayer.battery.status.voltage_dV = battery_volts; // Update the datalayer as soon as possible with this info
+ battery_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
+ battery_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
+ battery_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
+ battery_request_open_contactors_fast = (rx_frame.data.u8[6] & 0x0C) >> 2;
+ battery_charging_condition_delta = (rx_frame.data.u8[6] & 0xF0) >> 4;
+ battery_DC_link_voltage = rx_frame.data.u8[7];
+ break;
+ case 0x1FA: //BMS [1000ms] Status Of High-Voltage Battery - 1
+ battery_status_error_isolation_external_Bordnetz = (rx_frame.data.u8[0] & 0x03);
+ battery_status_error_isolation_internal_Bordnetz = (rx_frame.data.u8[0] & 0x0C) >> 2;
+ battery_request_cooling = (rx_frame.data.u8[0] & 0x30) >> 4;
+ battery_status_valve_cooling = (rx_frame.data.u8[0] & 0xC0) >> 6;
+ battery_status_error_locking = (rx_frame.data.u8[1] & 0x03);
+ battery_status_precharge_locked = (rx_frame.data.u8[1] & 0x0C) >> 2;
+ battery_status_disconnecting_switch = (rx_frame.data.u8[1] & 0x30) >> 4;
+ battery_status_emergency_mode = (rx_frame.data.u8[1] & 0xC0) >> 6;
+ battery_request_service = (rx_frame.data.u8[2] & 0x03);
+ battery_error_emergency_mode = (rx_frame.data.u8[2] & 0x0C) >> 2;
+ battery_status_error_disconnecting_switch = (rx_frame.data.u8[2] & 0x30) >> 4;
+ battery_status_warning_isolation = (rx_frame.data.u8[2] & 0xC0) >> 6;
+ battery_status_cold_shutoff_valve = (rx_frame.data.u8[3] & 0x0F);
+ battery_temperature_HV = (rx_frame.data.u8[4] - 50);
+ battery_temperature_heat_exchanger = (rx_frame.data.u8[5] - 50);
+ battery_temperature_min = (rx_frame.data.u8[6] - 50);
+ battery_temperature_max = (rx_frame.data.u8[7] - 50);
+ break;
+ case 0x239: //BMS [200ms]
+ battery_predicted_energy_charge_condition = (rx_frame.data.u8[2] << 8 | rx_frame.data.u8[1]); //Wh
+ battery_predicted_energy_charging_target = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[3]) * 0.02); //kWh
+ break;
+ case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
+ battery_awake = true;
+ if (!skipCRCCheck) {
+ if (calculateCRC(rx_frame, rx_frame.DLC, 0x15) != rx_frame.data.u8[0]) {
+ // If calculated CRC does not match transmitted CRC, increase CANerror counter
+ datalayer.battery.status.CAN_error_counter++;
+
+ // If the CRC check has never passed before, set the flag to skip future checks. Some SMEs have differing CRC checks.
+ if (!CRCCheckPassedPreviously) {
+ skipCRCCheck = true;
+ }
+ break;
+ } else {
+ // If CRC check passes, update the flag
+ CRCCheckPassedPreviously = true;
}
- break;
+ }
+
+ // Process the data since CRC check is either passed or skipped
+ battery_status_diagnostics_HV = (rx_frame.data.u8[2] & 0x0F);
+ break;
+ case 0x2F5: //BMS [100ms] High-Voltage Battery Charge/Discharge Limitations
+ battery_max_charge_voltage = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ battery_max_charge_amperage = (((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 819.2);
+ battery_min_discharge_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
+ battery_max_discharge_amperage = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) - 819.2);
+ break;
+ case 0x2FF: //BMS [100ms] Status Heating High-Voltage Battery
+ battery_awake = true;
+ battery_actual_value_power_heating = (rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4);
+ break;
+ case 0x363: //BMS [1s] Identification High-Voltage Battery
+ battery_serial_number =
+ (rx_frame.data.u8[3] << 24 | rx_frame.data.u8[2] << 16 | rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ break;
+ case 0x3C2: //BMS (94AH exclusive) - Status diagnostics OBD 2 powertrain
+ battery_status_diagnosis_powertrain_maximum_multiplexer =
+ ((rx_frame.data.u8[1] & 0x03) << 4 | rx_frame.data.u8[0] >> 4);
+ battery_status_diagnosis_powertrain_immediate_multiplexer = (rx_frame.data.u8[0] & 0xFC) >> 2;
+ break;
+ case 0x3EB: //BMS [1s] Status of charging high-voltage storage - 3
+ battery_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
+ battery_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
+ battery_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
+ battery_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
+ break;
+ case 0x40D: //BMS [1s] Charging status of high-voltage storage - 1
+ battery_BEV_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
+ battery_BEV_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
+ battery_BEV_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
+ battery_BEV_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
+ break;
+ case 0x41C: //BMS [1s] Operating Mode Status Of Hybrid - 2
+ battery_status_cooling_HV = (rx_frame.data.u8[1] & 0x03);
+ break;
+ case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
+ battery_prediction_voltage_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ battery_prediction_voltage_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
+ battery_prediction_voltage_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
+ battery_prediction_voltage_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]);
+ break;
+ case 0x431: //BMS [200ms] Data High-Voltage Battery Unit
+ battery_status_service_disconnection_plug = (rx_frame.data.u8[0] & 0x0F);
+ battery_status_measurement_isolation = (rx_frame.data.u8[0] & 0x0C) >> 2;
+ battery_request_abort_charging = (rx_frame.data.u8[0] & 0x30) >> 4;
+ battery_prediction_duration_charging_minutes = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
+ battery_prediction_time_end_of_charging_minutes = rx_frame.data.u8[4];
+ battery_energy_content_maximum_kWh = (((rx_frame.data.u8[6] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
+ if (battery_energy_content_maximum_kWh > 33) {
+ detectedBattery = BATTERY_120AH;
+ } else if (battery_energy_content_maximum_kWh > 20) {
+ detectedBattery = BATTERY_94AH;
} else {
- // If CRC check passes, update the flag
- CRCCheckPassedPreviously_battery2 = true;
+ detectedBattery = BATTERY_60AH;
}
- }
-
- // Process the data since CRC check is either passed or skipped
- battery2_status_diagnostics_HV = (rx_frame.data.u8[2] & 0x0F);
- break;
- case 0x2F5: //BMS [100ms] High-Voltage Battery Charge/Discharge Limitations
- battery2_max_charge_voltage = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- battery2_max_charge_amperage = (((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 819.2);
- battery2_min_discharge_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
- battery2_max_discharge_amperage = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) - 819.2);
- break;
- case 0x2FF: //BMS [100ms] Status Heating High-Voltage Battery
- battery2_awake = true;
- battery2_actual_value_power_heating = (rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4);
- break;
- case 0x363: //BMS [1s] Identification High-Voltage Battery
- battery2_serial_number =
- (rx_frame.data.u8[3] << 24 | rx_frame.data.u8[2] << 16 | rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- break;
- case 0x3C2: //BMS (94AH exclusive) - Status diagnostics OBD 2 powertrain
- battery2_status_diagnosis_powertrain_maximum_multiplexer =
- ((rx_frame.data.u8[1] & 0x03) << 4 | rx_frame.data.u8[0] >> 4);
- battery2_status_diagnosis_powertrain_immediate_multiplexer = (rx_frame.data.u8[0] & 0xFC) >> 2;
- break;
- case 0x3EB: //BMS [1s] Status of charging high-voltage storage - 3
- battery2_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
- battery2_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
- battery2_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
- battery2_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
- break;
- case 0x40D: //BMS [1s] Charging status of high-voltage storage - 1
- battery2_BEV_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
- battery2_BEV_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
- battery2_BEV_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
- battery2_BEV_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
- break;
- case 0x41C: //BMS [1s] Operating Mode Status Of Hybrid - 2
- battery2_status_cooling_HV = (rx_frame.data.u8[1] & 0x03);
- break;
- case 0x426: // TODO: Figure out how to trigger sending of this. Does the SME require some CAN command?
- battery2_cellvoltage_mux = rx_frame.data.u8[0];
- if (battery2_cellvoltage_mux == 0) {
- datalayer.battery2.status.cell_voltages_mV[0] = ((rx_frame.data.u8[1] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[1] = ((rx_frame.data.u8[2] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[2] = ((rx_frame.data.u8[3] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[3] = ((rx_frame.data.u8[4] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[4] = ((rx_frame.data.u8[5] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[5] = ((rx_frame.data.u8[6] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[5] = ((rx_frame.data.u8[7] * 10) + 1800);
- }
- break;
- case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
- battery2_prediction_voltage_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- battery2_prediction_voltage_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
- battery2_prediction_voltage_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
- battery2_prediction_voltage_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]);
- break;
- case 0x431: //BMS [200ms] Data High-Voltage Battery Unit
- battery2_status_service_disconnection_plug = (rx_frame.data.u8[0] & 0x0F);
- battery2_status_measurement_isolation = (rx_frame.data.u8[0] & 0x0C) >> 2;
- battery2_request_abort_charging = (rx_frame.data.u8[0] & 0x30) >> 4;
- battery2_prediction_duration_charging_minutes = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
- battery2_prediction_time_end_of_charging_minutes = rx_frame.data.u8[4];
- battery2_energy_content_maximum_kWh = (((rx_frame.data.u8[6] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
- break;
- case 0x432: //BMS [200ms] SOC% info
- battery2_request_operating_mode = (rx_frame.data.u8[0] & 0x03);
- battery2_target_voltage_in_CV_mode = ((rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4)) / 10;
- battery2_request_charging_condition_minimum = (rx_frame.data.u8[2] / 2);
- battery2_request_charging_condition_maximum = (rx_frame.data.u8[3] / 2);
- battery2_display_SOC = rx_frame.data.u8[4];
- break;
- case 0x507: //BMS [640ms] Network Management - 2 - This message is sent on the bus for sleep coordination purposes
- break;
- case 0x587: //BMS [5s] Services
- battery2_ID2 = rx_frame.data.u8[0];
- break;
- case 0x607: //BMS - responses to message requests on 0x615
- if (rx_frame.DLC > 6 && next_data == 0 && rx_frame.data.u8[0] == 0xf1) {
- uint8_t count2 = 6;
- while (count2 < rx_frame.DLC && next_data < 49) {
- message_data[next_data++] = rx_frame.data.u8[count2++];
- }
- transmit_can(&BMW_6F1_CONTINUE, can_config.battery_double);
-
- } else if (rx_frame.DLC > 3 && next_data > 0 && rx_frame.data.u8[0] == 0xf1 &&
- ((rx_frame.data.u8[1] & 0xF0) == 0x20)) {
- uint8_t count2 = 2;
- while (count2 < rx_frame.DLC && next_data < 49) {
- message_data[next_data++] = rx_frame.data.u8[count2++];
+ break;
+ case 0x432: //BMS [200ms] SOC% info
+ battery_request_operating_mode = (rx_frame.data.u8[0] & 0x03);
+ battery_target_voltage_in_CV_mode = ((rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4)) / 10;
+ battery_request_charging_condition_minimum = (rx_frame.data.u8[2] / 2);
+ battery_request_charging_condition_maximum = (rx_frame.data.u8[3] / 2);
+ battery_display_SOC = rx_frame.data.u8[4];
+ break;
+ case 0x507: //BMS [640ms] Network Management - 2 - This message is sent on the bus for sleep coordination purposes
+ break;
+ case 0x587: //BMS [5s] Services
+ 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 (rx_frame.DLC == 6) {
+ transmit_can(&BMW_6F4_CELL_CONTINUE, can_config.battery); // 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] =
+ (rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]);
+ }
}
- switch (cmdState) {
- case CELL_VOLTAGE_MINMAX:
- if (next_data >= 4) {
- datalayer.battery2.status.cell_min_voltage_mV = (message_data[0] << 8 | message_data[1]);
- datalayer.battery2.status.cell_max_voltage_mV = (message_data[2] << 8 | message_data[3]);
- }
- break;
- case SOH:
- if (next_data >= 4) {
- battery2_soh = message_data[3];
- battery2_info_available = true;
- }
- break;
- case SOC:
- if (next_data >= 6) {
- battery2_soc = (message_data[0] << 8 | message_data[1]);
- battery2_soc_hvmax = (message_data[2] << 8 | message_data[3]);
- battery2_soc_hvmin = (message_data[4] << 8 | message_data[5]);
- }
- break;
+ if (rx_frame.DLC > 6 && next_data == 0 && rx_frame.data.u8[0] == 0xf1) {
+ uint8_t count = 6;
+ while (count < rx_frame.DLC && next_data < 49) {
+ message_data[next_data++] = rx_frame.data.u8[count++];
+ }
+ transmit_can(&BMW_6F1_CONTINUE, can_config.battery); // 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)) {
+ uint8_t count = 2;
+ while (count < rx_frame.DLC && next_data < 49) {
+ message_data[next_data++] = rx_frame.data.u8[count++];
+ }
+
+ switch (cmdState) {
+ case CELL_VOLTAGE_MINMAX:
+ if (next_data >= 4) {
+ datalayer.battery.status.cell_min_voltage_mV = (message_data[0] << 8 | message_data[1]);
+ datalayer.battery.status.cell_max_voltage_mV = (message_data[2] << 8 | message_data[3]);
+ }
+ break;
+ case SOH:
+ if (next_data >= 4) {
+ battery_soh = message_data[3];
+ battery_info_available = true;
+ }
+ break;
+ case SOC:
+ if (next_data >= 6) {
+ battery_soc = (message_data[0] << 8 | message_data[1]);
+ battery_soc_hvmax = (message_data[2] << 8 | message_data[3]);
+ battery_soc_hvmin = (message_data[4] << 8 | message_data[5]);
+ }
+ break;
+ }
}
- }
- break;
- default:
- break;
+ break;
+ default:
+ break;
+ }
}
-}
-void send_can_battery() {
- unsigned long currentMillis = millis();
+ 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;
+ datalayer.battery2.status.CAN_battery_still_alive =
+ CAN_STILL_ALIVE; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
+ battery2_current = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) - 8192; //deciAmps (-819.2 to 819.0A)
+ battery2_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
+ datalayer.battery2.status.voltage_dV =
+ battery2_volts; // Update the datalayer as soon as possible with this info, needed for contactor control
+ battery2_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
+ battery2_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
+ battery2_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
+ battery2_request_open_contactors_fast = (rx_frame.data.u8[6] & 0x0C) >> 2;
+ battery2_charging_condition_delta = (rx_frame.data.u8[6] & 0xF0) >> 4;
+ battery2_DC_link_voltage = rx_frame.data.u8[7];
+ break;
+ case 0x1FA: //BMS [1000ms] Status Of High-Voltage Battery - 1
+ battery2_status_error_isolation_external_Bordnetz = (rx_frame.data.u8[0] & 0x03);
+ battery2_status_error_isolation_internal_Bordnetz = (rx_frame.data.u8[0] & 0x0C) >> 2;
+ battery2_request_cooling = (rx_frame.data.u8[0] & 0x30) >> 4;
+ battery2_status_valve_cooling = (rx_frame.data.u8[0] & 0xC0) >> 6;
+ battery2_status_error_locking = (rx_frame.data.u8[1] & 0x03);
+ battery2_status_precharge_locked = (rx_frame.data.u8[1] & 0x0C) >> 2;
+ battery2_status_disconnecting_switch = (rx_frame.data.u8[1] & 0x30) >> 4;
+ battery2_status_emergency_mode = (rx_frame.data.u8[1] & 0xC0) >> 6;
+ battery2_request_service = (rx_frame.data.u8[2] & 0x03);
+ battery2_error_emergency_mode = (rx_frame.data.u8[2] & 0x0C) >> 2;
+ battery2_status_error_disconnecting_switch = (rx_frame.data.u8[2] & 0x30) >> 4;
+ battery2_status_warning_isolation = (rx_frame.data.u8[2] & 0xC0) >> 6;
+ battery2_status_cold_shutoff_valve = (rx_frame.data.u8[3] & 0x0F);
+ battery2_temperature_HV = (rx_frame.data.u8[4] - 50);
+ battery2_temperature_heat_exchanger = (rx_frame.data.u8[5] - 50);
+ battery2_temperature_min = (rx_frame.data.u8[6] - 50);
+ battery2_temperature_max = (rx_frame.data.u8[7] - 50);
+ break;
+ case 0x239: //BMS [200ms]
+ battery2_predicted_energy_charge_condition = (rx_frame.data.u8[2] << 8 | rx_frame.data.u8[1]); //Wh
+ battery2_predicted_energy_charging_target = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[3]) * 0.02); //kWh
+ break;
+ case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
+ battery2_awake = true;
+ if (!skipCRCCheck_battery2) {
+ if (calculateCRC(rx_frame, rx_frame.DLC, 0x15) != rx_frame.data.u8[0]) {
+ // If calculated CRC does not match transmitted CRC, increase CANerror counter
+ datalayer.battery2.status.CAN_error_counter++;
- if (battery_awake) {
- //Send 20ms message
- if (currentMillis - previousMillis20 >= INTERVAL_20_MS) {
- // Check if sending of CAN messages has been delayed too much.
- if ((currentMillis - previousMillis20 >= INTERVAL_20_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) {
- set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis20));
- } else {
- clear_event(EVENT_CAN_OVERRUN);
- }
- previousMillis20 = currentMillis;
+ // If the CRC check has never passed before, set the flag to skip future checks. Some SMEs have differing CRC checks.
+ if (!CRCCheckPassedPreviously_battery2) {
+ skipCRCCheck_battery2 = true;
+ }
+ break;
+ } else {
+ // If CRC check passes, update the flag
+ CRCCheckPassedPreviously_battery2 = true;
+ }
+ }
- if (startup_counter_contactor < 160) {
- startup_counter_contactor++;
- } else { //After 160 messages, turn on the request
- BMW_10B.data.u8[1] = 0x10; // Close contactors
- }
+ // Process the data since CRC check is either passed or skipped
+ battery2_status_diagnostics_HV = (rx_frame.data.u8[2] & 0x0F);
+ break;
+ case 0x2F5: //BMS [100ms] High-Voltage Battery Charge/Discharge Limitations
+ battery2_max_charge_voltage = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ battery2_max_charge_amperage = (((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 819.2);
+ battery2_min_discharge_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
+ battery2_max_discharge_amperage = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) - 819.2);
+ break;
+ case 0x2FF: //BMS [100ms] Status Heating High-Voltage Battery
+ battery2_awake = true;
+ battery2_actual_value_power_heating = (rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4);
+ break;
+ case 0x363: //BMS [1s] Identification High-Voltage Battery
+ battery2_serial_number =
+ (rx_frame.data.u8[3] << 24 | rx_frame.data.u8[2] << 16 | rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ break;
+ case 0x3C2: //BMS (94AH exclusive) - Status diagnostics OBD 2 powertrain
+ battery2_status_diagnosis_powertrain_maximum_multiplexer =
+ ((rx_frame.data.u8[1] & 0x03) << 4 | rx_frame.data.u8[0] >> 4);
+ battery2_status_diagnosis_powertrain_immediate_multiplexer = (rx_frame.data.u8[0] & 0xFC) >> 2;
+ break;
+ case 0x3EB: //BMS [1s] Status of charging high-voltage storage - 3
+ battery2_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
+ battery2_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
+ battery2_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
+ battery2_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
+ break;
+ case 0x40D: //BMS [1s] Charging status of high-voltage storage - 1
+ battery2_BEV_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
+ battery2_BEV_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
+ battery2_BEV_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
+ battery2_BEV_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
+ break;
+ case 0x41C: //BMS [1s] Operating Mode Status Of Hybrid - 2
+ battery2_status_cooling_HV = (rx_frame.data.u8[1] & 0x03);
+ break;
+ case 0x426: // TODO: Figure out how to trigger sending of this. Does the SME require some CAN command?
+ battery2_cellvoltage_mux = rx_frame.data.u8[0];
+ if (battery2_cellvoltage_mux == 0) {
+ datalayer.battery2.status.cell_voltages_mV[0] = ((rx_frame.data.u8[1] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[1] = ((rx_frame.data.u8[2] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[2] = ((rx_frame.data.u8[3] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[3] = ((rx_frame.data.u8[4] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[4] = ((rx_frame.data.u8[5] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[5] = ((rx_frame.data.u8[6] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[5] = ((rx_frame.data.u8[7] * 10) + 1800);
+ }
+ break;
+ case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
+ battery2_prediction_voltage_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ battery2_prediction_voltage_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
+ battery2_prediction_voltage_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
+ battery2_prediction_voltage_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]);
+ break;
+ case 0x431: //BMS [200ms] Data High-Voltage Battery Unit
+ battery2_status_service_disconnection_plug = (rx_frame.data.u8[0] & 0x0F);
+ battery2_status_measurement_isolation = (rx_frame.data.u8[0] & 0x0C) >> 2;
+ battery2_request_abort_charging = (rx_frame.data.u8[0] & 0x30) >> 4;
+ battery2_prediction_duration_charging_minutes = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
+ battery2_prediction_time_end_of_charging_minutes = rx_frame.data.u8[4];
+ battery2_energy_content_maximum_kWh = (((rx_frame.data.u8[6] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
+ break;
+ case 0x432: //BMS [200ms] SOC% info
+ battery2_request_operating_mode = (rx_frame.data.u8[0] & 0x03);
+ battery2_target_voltage_in_CV_mode = ((rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4)) / 10;
+ battery2_request_charging_condition_minimum = (rx_frame.data.u8[2] / 2);
+ battery2_request_charging_condition_maximum = (rx_frame.data.u8[3] / 2);
+ battery2_display_SOC = rx_frame.data.u8[4];
+ break;
+ case 0x507: //BMS [640ms] Network Management - 2 - This message is sent on the bus for sleep coordination purposes
+ break;
+ case 0x587: //BMS [5s] Services
+ battery2_ID2 = rx_frame.data.u8[0];
+ break;
+ case 0x607: //BMS - responses to message requests on 0x615
+ if (rx_frame.DLC > 6 && next_data == 0 && rx_frame.data.u8[0] == 0xf1) {
+ uint8_t count2 = 6;
+ while (count2 < rx_frame.DLC && next_data < 49) {
+ message_data[next_data++] = rx_frame.data.u8[count2++];
+ }
+ transmit_can(&BMW_6F1_CONTINUE, can_config.battery_double);
- BMW_10B.data.u8[1] = ((BMW_10B.data.u8[1] & 0xF0) + alive_counter_20ms);
- BMW_10B.data.u8[0] = calculateCRC(BMW_10B, 3, 0x3F);
+ } else if (rx_frame.DLC > 3 && next_data > 0 && rx_frame.data.u8[0] == 0xf1 &&
+ ((rx_frame.data.u8[1] & 0xF0) == 0x20)) {
+ uint8_t count2 = 2;
+ while (count2 < rx_frame.DLC && next_data < 49) {
+ message_data[next_data++] = rx_frame.data.u8[count2++];
+ }
- alive_counter_20ms = increment_alive_counter(alive_counter_20ms);
+ switch (cmdState) {
+ case CELL_VOLTAGE_MINMAX:
+ if (next_data >= 4) {
+ datalayer.battery2.status.cell_min_voltage_mV = (message_data[0] << 8 | message_data[1]);
+ datalayer.battery2.status.cell_max_voltage_mV = (message_data[2] << 8 | message_data[3]);
+ }
+ break;
+ case SOH:
+ if (next_data >= 4) {
+ battery2_soh = message_data[3];
+ battery2_info_available = true;
+ }
+ break;
+ case SOC:
+ if (next_data >= 6) {
+ battery2_soc = (message_data[0] << 8 | message_data[1]);
+ battery2_soc_hvmax = (message_data[2] << 8 | message_data[3]);
+ battery2_soc_hvmin = (message_data[4] << 8 | message_data[5]);
+ }
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ void send_can_battery() {
+ unsigned long currentMillis = millis();
- BMW_13E_counter++;
- BMW_13E.data.u8[4] = BMW_13E_counter;
+ if (battery_awake) {
+ //Send 20ms message
+ if (currentMillis - previousMillis20 >= INTERVAL_20_MS) {
+ // Check if sending of CAN messages has been delayed too much.
+ if ((currentMillis - previousMillis20 >= INTERVAL_20_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) {
+ set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis20));
+ } else {
+ clear_event(EVENT_CAN_OVERRUN);
+ }
+ previousMillis20 = currentMillis;
- if (datalayer.battery.status.bms_status == FAULT) {
- } //If battery is not in Fault mode, allow contactor to close by sending 10B
- else {
- transmit_can(&BMW_10B, can_config.battery);
- }
+ if (startup_counter_contactor < 160) {
+ startup_counter_contactor++;
+ } else { //After 160 messages, turn on the request
+ BMW_10B.data.u8[1] = 0x10; // Close contactors
+ }
+
+ BMW_10B.data.u8[1] = ((BMW_10B.data.u8[1] & 0xF0) + alive_counter_20ms);
+ BMW_10B.data.u8[0] = calculateCRC(BMW_10B, 3, 0x3F);
+
+ alive_counter_20ms = increment_alive_counter(alive_counter_20ms);
+
+ BMW_13E_counter++;
+ BMW_13E.data.u8[4] = BMW_13E_counter;
+
+ if (datalayer.battery.status.bms_status == FAULT) {
+ } //If battery is not in Fault mode, allow contactor to close by sending 10B
+ else {
+ transmit_can(&BMW_10B, can_config.battery);
+ }
#ifdef DOUBLE_BATTERY //If second battery is allowed to join in, also send 10B
- if (datalayer.system.status.battery2_allows_contactor_closing == true) {
- transmit_can(&BMW_10B, can_config.battery_double);
+ if (datalayer.system.status.battery2_allows_contactor_closing == true) {
+ transmit_can(&BMW_10B, can_config.battery_double);
+ }
+#endif
}
-#endif
- }
- // Send 100ms CAN Message
- if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
- previousMillis100 = currentMillis;
+ // Send 100ms CAN Message
+ if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
+ previousMillis100 = currentMillis;
- BMW_12F.data.u8[1] = ((BMW_12F.data.u8[1] & 0xF0) + alive_counter_100ms);
- BMW_12F.data.u8[0] = calculateCRC(BMW_12F, 8, 0x60);
+ BMW_12F.data.u8[1] = ((BMW_12F.data.u8[1] & 0xF0) + alive_counter_100ms);
+ BMW_12F.data.u8[0] = calculateCRC(BMW_12F, 8, 0x60);
- alive_counter_100ms = increment_alive_counter(alive_counter_100ms);
+ alive_counter_100ms = increment_alive_counter(alive_counter_100ms);
- transmit_can(&BMW_12F, can_config.battery);
+ transmit_can(&BMW_12F, can_config.battery);
#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_12F, can_config.battery_double);
+ transmit_can(&BMW_12F, can_config.battery_double);
#endif
- }
- // Send 200ms CAN Message
- if (currentMillis - previousMillis200 >= INTERVAL_200_MS) {
- previousMillis200 = currentMillis;
+ }
+ // Send 200ms CAN Message
+ if (currentMillis - previousMillis200 >= INTERVAL_200_MS) {
+ previousMillis200 = currentMillis;
- BMW_19B.data.u8[1] = ((BMW_19B.data.u8[1] & 0xF0) + alive_counter_200ms);
- BMW_19B.data.u8[0] = calculateCRC(BMW_19B, 8, 0x6C);
+ BMW_19B.data.u8[1] = ((BMW_19B.data.u8[1] & 0xF0) + alive_counter_200ms);
+ BMW_19B.data.u8[0] = calculateCRC(BMW_19B, 8, 0x6C);
- alive_counter_200ms = increment_alive_counter(alive_counter_200ms);
+ alive_counter_200ms = increment_alive_counter(alive_counter_200ms);
- transmit_can(&BMW_19B, can_config.battery);
+ transmit_can(&BMW_19B, can_config.battery);
#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_19B, can_config.battery_double);
+ transmit_can(&BMW_19B, can_config.battery_double);
#endif
- }
- // Send 500ms CAN Message
- if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
- previousMillis500 = currentMillis;
+ }
+ // Send 500ms CAN Message
+ if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
+ previousMillis500 = currentMillis;
- BMW_30B.data.u8[1] = ((BMW_30B.data.u8[1] & 0xF0) + alive_counter_500ms);
- BMW_30B.data.u8[0] = calculateCRC(BMW_30B, 8, 0xBE);
+ BMW_30B.data.u8[1] = ((BMW_30B.data.u8[1] & 0xF0) + alive_counter_500ms);
+ BMW_30B.data.u8[0] = calculateCRC(BMW_30B, 8, 0xBE);
- alive_counter_500ms = increment_alive_counter(alive_counter_500ms);
+ alive_counter_500ms = increment_alive_counter(alive_counter_500ms);
- transmit_can(&BMW_30B, can_config.battery);
+ transmit_can(&BMW_30B, can_config.battery);
#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_30B, can_config.battery_double);
+ transmit_can(&BMW_30B, can_config.battery_double);
#endif
- }
- // Send 640ms CAN Message
- if (currentMillis - previousMillis640 >= INTERVAL_640_MS) {
- previousMillis640 = currentMillis;
+ }
+ // Send 640ms CAN Message
+ if (currentMillis - previousMillis640 >= INTERVAL_640_MS) {
+ previousMillis640 = currentMillis;
- transmit_can(&BMW_512, can_config.battery); // Keep BMS alive
- transmit_can(&BMW_5F8, can_config.battery);
+ transmit_can(&BMW_512, can_config.battery); // Keep BMS alive
+ transmit_can(&BMW_5F8, can_config.battery);
#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_512, can_config.battery_double);
- transmit_can(&BMW_5F8, can_config.battery_double);
+ transmit_can(&BMW_512, can_config.battery_double);
+ transmit_can(&BMW_5F8, can_config.battery_double);
#endif
- }
- // Send 1000ms CAN Message
- if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
- previousMillis1000 = currentMillis;
+ }
+ // Send 1000ms CAN Message
+ if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
+ previousMillis1000 = currentMillis;
- BMW_328_counter++; // Used to increment seconds
- BMW_328.data.u8[0] = BMW_328_counter;
- BMW_328.data.u8[1] = BMW_328_counter << 8;
- BMW_328.data.u8[2] = BMW_328_counter << 16;
- BMW_328.data.u8[3] = BMW_328_counter << 24;
+ BMW_328_counter++; // Used to increment seconds
+ BMW_328.data.u8[0] = BMW_328_counter;
+ BMW_328.data.u8[1] = BMW_328_counter << 8;
+ BMW_328.data.u8[2] = BMW_328_counter << 16;
+ BMW_328.data.u8[3] = BMW_328_counter << 24;
- BMW_1D0.data.u8[1] = ((BMW_1D0.data.u8[1] & 0xF0) + alive_counter_1000ms);
- BMW_1D0.data.u8[0] = calculateCRC(BMW_1D0, 8, 0xF9);
+ BMW_1D0.data.u8[1] = ((BMW_1D0.data.u8[1] & 0xF0) + alive_counter_1000ms);
+ BMW_1D0.data.u8[0] = calculateCRC(BMW_1D0, 8, 0xF9);
- BMW_3F9.data.u8[1] = ((BMW_3F9.data.u8[1] & 0xF0) + alive_counter_1000ms);
- BMW_3F9.data.u8[0] = calculateCRC(BMW_3F9, 8, 0x38);
+ BMW_3F9.data.u8[1] = ((BMW_3F9.data.u8[1] & 0xF0) + alive_counter_1000ms);
+ BMW_3F9.data.u8[0] = calculateCRC(BMW_3F9, 8, 0x38);
- BMW_3EC.data.u8[1] = ((BMW_3EC.data.u8[1] & 0xF0) + alive_counter_1000ms);
- BMW_3EC.data.u8[0] = calculateCRC(BMW_3EC, 8, 0x53);
+ BMW_3EC.data.u8[1] = ((BMW_3EC.data.u8[1] & 0xF0) + alive_counter_1000ms);
+ BMW_3EC.data.u8[0] = calculateCRC(BMW_3EC, 8, 0x53);
- BMW_3A7.data.u8[1] = ((BMW_3A7.data.u8[1] & 0xF0) + alive_counter_1000ms);
- BMW_3A7.data.u8[0] = calculateCRC(BMW_3A7, 8, 0x05);
+ BMW_3A7.data.u8[1] = ((BMW_3A7.data.u8[1] & 0xF0) + alive_counter_1000ms);
+ BMW_3A7.data.u8[0] = calculateCRC(BMW_3A7, 8, 0x05);
- alive_counter_1000ms = increment_alive_counter(alive_counter_1000ms);
+ alive_counter_1000ms = increment_alive_counter(alive_counter_1000ms);
- transmit_can(&BMW_3E8, can_config.battery); //Order comes from CAN logs
- transmit_can(&BMW_328, can_config.battery);
- transmit_can(&BMW_3F9, can_config.battery);
- transmit_can(&BMW_2E2, can_config.battery);
- transmit_can(&BMW_41D, can_config.battery);
- transmit_can(&BMW_3D0, can_config.battery);
- transmit_can(&BMW_3CA, can_config.battery);
- transmit_can(&BMW_3A7, can_config.battery);
- transmit_can(&BMW_2CA, can_config.battery);
- transmit_can(&BMW_3FB, can_config.battery);
- transmit_can(&BMW_418, can_config.battery);
- transmit_can(&BMW_1D0, can_config.battery);
- transmit_can(&BMW_3EC, can_config.battery);
- transmit_can(&BMW_192, can_config.battery);
- transmit_can(&BMW_13E, can_config.battery);
- transmit_can(&BMW_433, can_config.battery);
+ transmit_can(&BMW_3E8, can_config.battery); //Order comes from CAN logs
+ transmit_can(&BMW_328, can_config.battery);
+ transmit_can(&BMW_3F9, can_config.battery);
+ transmit_can(&BMW_2E2, can_config.battery);
+ transmit_can(&BMW_41D, can_config.battery);
+ transmit_can(&BMW_3D0, can_config.battery);
+ transmit_can(&BMW_3CA, can_config.battery);
+ transmit_can(&BMW_3A7, can_config.battery);
+ transmit_can(&BMW_2CA, can_config.battery);
+ transmit_can(&BMW_3FB, can_config.battery);
+ transmit_can(&BMW_418, can_config.battery);
+ transmit_can(&BMW_1D0, can_config.battery);
+ transmit_can(&BMW_3EC, can_config.battery);
+ transmit_can(&BMW_192, can_config.battery);
+ transmit_can(&BMW_13E, can_config.battery);
+ transmit_can(&BMW_433, can_config.battery);
#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_3E8, can_config.battery_double);
- transmit_can(&BMW_328, can_config.battery_double);
- transmit_can(&BMW_3F9, can_config.battery_double);
- transmit_can(&BMW_2E2, can_config.battery_double);
- transmit_can(&BMW_41D, can_config.battery_double);
- transmit_can(&BMW_3D0, can_config.battery_double);
- transmit_can(&BMW_3CA, can_config.battery_double);
- transmit_can(&BMW_3A7, can_config.battery_double);
- transmit_can(&BMW_2CA, can_config.battery_double);
- transmit_can(&BMW_3FB, can_config.battery_double);
- transmit_can(&BMW_418, can_config.battery_double);
- transmit_can(&BMW_1D0, can_config.battery_double);
- transmit_can(&BMW_3EC, can_config.battery_double);
- transmit_can(&BMW_192, can_config.battery_double);
- transmit_can(&BMW_13E, can_config.battery_double);
- transmit_can(&BMW_433, can_config.battery_double);
+ transmit_can(&BMW_3E8, can_config.battery_double);
+ transmit_can(&BMW_328, can_config.battery_double);
+ transmit_can(&BMW_3F9, can_config.battery_double);
+ transmit_can(&BMW_2E2, can_config.battery_double);
+ transmit_can(&BMW_41D, can_config.battery_double);
+ transmit_can(&BMW_3D0, can_config.battery_double);
+ transmit_can(&BMW_3CA, can_config.battery_double);
+ transmit_can(&BMW_3A7, can_config.battery_double);
+ transmit_can(&BMW_2CA, can_config.battery_double);
+ transmit_can(&BMW_3FB, can_config.battery_double);
+ transmit_can(&BMW_418, can_config.battery_double);
+ transmit_can(&BMW_1D0, can_config.battery_double);
+ transmit_can(&BMW_3EC, can_config.battery_double);
+ transmit_can(&BMW_192, can_config.battery_double);
+ transmit_can(&BMW_13E, can_config.battery_double);
+ transmit_can(&BMW_433, can_config.battery_double);
#endif
- 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
+ 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
- next_data = 0;
- switch (cmdState) {
- case SOC:
- transmit_can(&BMW_6F1_CELL, can_config.battery);
+ next_data = 0;
+ switch (cmdState) {
+ case SOC:
+ transmit_can(&BMW_6F1_CELL, can_config.battery);
#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_6F1_CELL, can_config.battery_double);
+ transmit_can(&BMW_6F1_CELL, can_config.battery_double);
#endif
- cmdState = CELL_VOLTAGE_MINMAX;
- break;
- case CELL_VOLTAGE_MINMAX:
- transmit_can(&BMW_6F1_SOH, can_config.battery);
+ cmdState = CELL_VOLTAGE_MINMAX;
+ break;
+ case CELL_VOLTAGE_MINMAX:
+ transmit_can(&BMW_6F1_SOH, can_config.battery);
#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_6F1_SOH, can_config.battery_double);
+ transmit_can(&BMW_6F1_SOH, can_config.battery_double);
#endif
- cmdState = SOH;
- break;
- case SOH:
- transmit_can(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery);
+ cmdState = SOH;
+ break;
+ case SOH:
+ transmit_can(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery);
#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery_double);
+ transmit_can(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery_double);
#endif
- cmdState = CELL_VOLTAGE_CELLNO;
- current_cell_polled = 0;
-
- break;
- case CELL_VOLTAGE_CELLNO:
- current_cell_polled++;
- if (current_cell_polled > 96) {
- datalayer.battery.info.number_of_cells = 97;
- cmdState = CELL_VOLTAGE_CELLNO_LAST;
- } else {
cmdState = CELL_VOLTAGE_CELLNO;
+ current_cell_polled = 0;
- BMW_6F4_CELL_VOLTAGE_CELLNO.data.u8[6] = current_cell_polled;
- transmit_can(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery);
+ break;
+ case CELL_VOLTAGE_CELLNO:
+ current_cell_polled++;
+ if (current_cell_polled > 96) {
+ datalayer.battery.info.number_of_cells = 97;
+ cmdState = CELL_VOLTAGE_CELLNO_LAST;
+ } else {
+ cmdState = CELL_VOLTAGE_CELLNO;
+
+ BMW_6F4_CELL_VOLTAGE_CELLNO.data.u8[6] = current_cell_polled;
+ transmit_can(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery);
#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery_double);
+ transmit_can(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery_double);
#endif
- }
- break;
- case CELL_VOLTAGE_CELLNO_LAST:
- transmit_can(&BMW_6F1_SOC, can_config.battery);
+ }
+ break;
+ case CELL_VOLTAGE_CELLNO_LAST:
+ transmit_can(&BMW_6F1_SOC, can_config.battery);
#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_6F1_SOC, can_config.battery_double);
+ transmit_can(&BMW_6F1_SOC, can_config.battery_double);
#endif
- cmdState = SOC;
- break;
+ cmdState = SOC;
+ break;
+ }
}
- }
- // Send 5000ms CAN Message
- if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
+ // Send 5000ms CAN Message
+ if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
+ previousMillis5000 = 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(&BMW_3FC, can_config.battery); //Order comes from CAN logs
+ transmit_can(&BMW_3C5, can_config.battery);
+ transmit_can(&BMW_3A0, can_config.battery);
+ transmit_can(&BMW_592_0, can_config.battery);
+ transmit_can(&BMW_592_1, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_3FC, can_config.battery_double);
+ transmit_can(&BMW_3C5, can_config.battery_double);
+ transmit_can(&BMW_3A0, can_config.battery_double);
+ transmit_can(&BMW_592_0, can_config.battery_double);
+ transmit_can(&BMW_592_1, can_config.battery_double);
+#endif
+
+ alive_counter_5000ms = increment_alive_counter(alive_counter_5000ms);
+
+ if (BMW_380_counter < 3) {
+ transmit_can(&BMW_380, can_config.battery); // This message stops after 3 times on startup
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_380, can_config.battery_double);
+#endif
+ BMW_380_counter++;
+ }
+ }
+ // Send 10000ms CAN Message
+ if (currentMillis - previousMillis10000 >= INTERVAL_10_S) {
+ previousMillis10000 = currentMillis;
+
+ transmit_can(&BMW_3E5, can_config.battery); //Order comes from CAN logs
+ transmit_can(&BMW_3E4, can_config.battery);
+ transmit_can(&BMW_37B, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_3E5, can_config.battery_double);
+ transmit_can(&BMW_3E4, can_config.battery_double);
+ transmit_can(&BMW_37B, can_config.battery_double);
+#endif
+
+ BMW_3E5.data.u8[0] = 0xFD; // First 3E5 message byte0 we send is unique, once we sent initial value send this
+ }
+ } else {
+ previousMillis20 = currentMillis;
+ previousMillis100 = currentMillis;
+ previousMillis200 = currentMillis;
+ previousMillis500 = currentMillis;
+ previousMillis640 = currentMillis;
+ previousMillis1000 = currentMillis;
previousMillis5000 = 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(&BMW_3FC, can_config.battery); //Order comes from CAN logs
- transmit_can(&BMW_3C5, can_config.battery);
- transmit_can(&BMW_3A0, can_config.battery);
- transmit_can(&BMW_592_0, can_config.battery);
- transmit_can(&BMW_592_1, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_3FC, can_config.battery_double);
- transmit_can(&BMW_3C5, can_config.battery_double);
- transmit_can(&BMW_3A0, can_config.battery_double);
- transmit_can(&BMW_592_0, can_config.battery_double);
- transmit_can(&BMW_592_1, can_config.battery_double);
-#endif
-
- alive_counter_5000ms = increment_alive_counter(alive_counter_5000ms);
-
- if (BMW_380_counter < 3) {
- transmit_can(&BMW_380, can_config.battery); // This message stops after 3 times on startup
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_380, can_config.battery_double);
-#endif
- BMW_380_counter++;
- }
- }
- // Send 10000ms CAN Message
- if (currentMillis - previousMillis10000 >= INTERVAL_10_S) {
previousMillis10000 = currentMillis;
-
- transmit_can(&BMW_3E5, can_config.battery); //Order comes from CAN logs
- transmit_can(&BMW_3E4, can_config.battery);
- transmit_can(&BMW_37B, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_3E5, can_config.battery_double);
- transmit_can(&BMW_3E4, can_config.battery_double);
- transmit_can(&BMW_37B, can_config.battery_double);
-#endif
-
- BMW_3E5.data.u8[0] = 0xFD; // First 3E5 message byte0 we send is unique, once we sent initial value send this
}
- } else {
- previousMillis20 = currentMillis;
- previousMillis100 = currentMillis;
- previousMillis200 = currentMillis;
- previousMillis500 = currentMillis;
- previousMillis640 = currentMillis;
- previousMillis1000 = currentMillis;
- 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");
+ Serial.println("BMW i3 battery selected");
#endif
- //Before we have started up and detected which battery is in use, use 60AH values
- datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
- datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
+ //Before we have started up and detected which battery is in use, use 60AH values
+ datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
+ datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
- datalayer.system.status.battery_allows_contactor_closing = true;
+ datalayer.system.status.battery_allows_contactor_closing = true;
#ifdef DOUBLE_BATTERY
- Serial.println("Another BMW i3 battery also selected!");
- datalayer.battery2.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV;
- datalayer.battery2.info.min_design_voltage_dV = datalayer.battery.info.min_design_voltage_dV;
- datalayer.battery2.status.voltage_dV =
- 0; //Init voltage to 0 to allow contactor check to operate without fear of default values colliding
+ Serial.println("Another BMW i3 battery also selected!");
+ datalayer.battery2.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV;
+ datalayer.battery2.info.min_design_voltage_dV = datalayer.battery.info.min_design_voltage_dV;
+ datalayer.battery2.status.voltage_dV =
+ 0; //Init voltage to 0 to allow contactor check to operate without fear of default values colliding
#endif
- pinMode(WUP_PIN, OUTPUT);
- digitalWrite(WUP_PIN, HIGH); // Wake up the battery
-}
+ pinMode(WUP_PIN, OUTPUT);
+ digitalWrite(WUP_PIN, HIGH); // Wake up the battery
+ }
#endif
diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
index d36e4166..4aba2657 100644
--- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
+++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
@@ -101,9 +101,13 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
- 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
+ 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));
diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp
index 90ecd950..ff9105fb 100644
--- a/Software/src/battery/CHADEMO-BATTERY.cpp
+++ b/Software/src/battery/CHADEMO-BATTERY.cpp
@@ -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;
diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
index a3a54b50..66136c74 100644
--- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
+++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
@@ -48,10 +48,14 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
- //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
+ 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_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?
diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
index 0a2807bd..720864c2 100644
--- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
+++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
@@ -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)
-
- datalayer.battery.status.max_charge_power_W =
- HVBattChargeContiniousPowerLimit * 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);
diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp
index 1f553511..0994a310 100644
--- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp
+++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp
@@ -310,13 +310,17 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
- //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;
+ 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);
diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
index d27e60eb..fbe8eed8 100644
--- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
+++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
@@ -123,9 +123,13 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
- datalayer.battery.status.max_charge_power_W = allowedChargePower * 10;
-
- datalayer.battery.status.max_discharge_power_W = allowedDischargePower * 10;
+ 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 =
diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
index 5ef5996e..664c9c27 100644
--- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
+++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
@@ -64,9 +64,13 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
- datalayer.battery.status.max_discharge_power_W = available_discharge_power * 10;
-
- datalayer.battery.status.max_charge_power_W = available_charge_power * 10;
+ 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 =
diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp
index 32cafddf..284e0188 100644
--- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp
+++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp
@@ -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
}
}
- 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
+ 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
}
}
- 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
+ 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!
diff --git a/Software/src/battery/PYLON-BATTERY.cpp b/Software/src/battery/PYLON-BATTERY.cpp
index aa678368..7f053f22 100644
--- a/Software/src/battery/PYLON-BATTERY.cpp
+++ b/Software/src/battery/PYLON-BATTERY.cpp
@@ -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);
- 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));
+ 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;
diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
index 34a37032..75a992c4 100644
--- a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
+++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
@@ -87,13 +87,19 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
- /* Define power able to be discharged from battery */
- datalayer.battery.status.max_discharge_power_W =
- (LB_Discharge_Power_Limit * 500); //Convert value fetched from battery to watts
+ if (emulator_pause_request_ON) {
+ datalayer.battery.status.max_discharge_power_W = 0;
+ datalayer.battery.status.max_charge_power_W = 0;
+ } else {
- 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.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);
diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
index 0573b215..126b6b70 100644
--- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
+++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
@@ -47,9 +47,13 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
- datalayer.battery.status.max_discharge_power_W = 5000; //TODO: Take from CAN
-
- datalayer.battery.status.max_charge_power_W = LB_Charge_Power_W;
+ 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;
diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
index 4b4c53aa..797b5290 100644
--- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
+++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
@@ -81,9 +81,13 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
- datalayer.battery.status.max_discharge_power_W = allowedDischargePower * 10;
-
- datalayer.battery.status.max_charge_power_W = allowedChargePower * 10;
+ 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 =
diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp
index a5759dcb..5067f971 100644
--- a/Software/src/battery/TESLA-BATTERY.cpp
+++ b/Software/src/battery/TESLA-BATTERY.cpp
@@ -278,32 +278,37 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
- // 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
- if (datalayer.battery.status.max_discharge_power_W > MAXDISCHARGEPOWERALLOWED) {
- datalayer.battery.status.max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
- }
-
- //The allowed charge power behaves strangely. We instead estimate this value
- if (battery_soc_vi > 990) {
- datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W;
- } else if (battery_soc_vi >
- RAMPDOWN_SOC) { // When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0
- datalayer.battery.status.max_charge_power_W =
- RAMPDOWNPOWERALLOWED * (1 - (battery_soc_vi - RAMPDOWN_SOC) / (1000.0 - RAMPDOWN_SOC));
- //If the cellvoltages start to reach overvoltage, only allow a small amount of power in
- if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
- if (battery_cell_max_v > (MAX_CELL_VOLTAGE_LFP - FLOAT_START_MV)) {
- datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W;
- }
- } else { //NCM/A
- if (battery_cell_max_v > (MAX_CELL_VOLTAGE_NCA_NCM - FLOAT_START_MV)) {
- datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W;
- }
+ 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
+ if (datalayer.battery.status.max_discharge_power_W > MAXDISCHARGEPOWERALLOWED) {
+ datalayer.battery.status.max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
+ }
+
+ //The allowed charge power behaves strangely. We instead estimate this value
+ if (battery_soc_vi > 990) {
+ datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W;
+ } else if (battery_soc_vi >
+ RAMPDOWN_SOC) { // When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0
+ datalayer.battery.status.max_charge_power_W =
+ RAMPDOWNPOWERALLOWED * (1 - (battery_soc_vi - RAMPDOWN_SOC) / (1000.0 - RAMPDOWN_SOC));
+ //If the cellvoltages start to reach overvoltage, only allow a small amount of power in
+ if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
+ if (battery_cell_max_v > (MAX_CELL_VOLTAGE_LFP - FLOAT_START_MV)) {
+ datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W;
+ }
+ } else { //NCM/A
+ if (battery_cell_max_v > (MAX_CELL_VOLTAGE_NCA_NCM - FLOAT_START_MV)) {
+ datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W;
+ }
+ }
+ } else { // No limits, max charging power allowed
+ datalayer.battery.status.max_charge_power_W = MAXCHARGEPOWERALLOWED;
}
- } 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);
diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp
index eb462a26..0134bf9d 100644
--- a/Software/src/battery/TEST-FAKE-BATTERY.cpp
+++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp
@@ -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
- datalayer.battery.status.max_discharge_power_W = 5000; // 5kW
-
- datalayer.battery.status.max_charge_power_W = 5000; // 5kW
+ 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;
diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp
index 5d392906..2c927890 100644
--- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp
+++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp
@@ -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;
- //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;
+ 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;
diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp
index 420d904a..fc5b29e7 100644
--- a/Software/src/devboard/mqtt/mqtt.cpp
+++ b/Software/src/devboard/mqtt/mqtt.cpp
@@ -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,25 +114,30 @@ static void publish_common_info(void) {
} else {
#endif // HA_AUTODISCOVERY
- 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;
- doc["temperature_min"] = ((float)((int16_t)datalayer.battery.status.temperature_min_dC)) / 10.0;
- doc["temperature_max"] = ((float)((int16_t)datalayer.battery.status.temperature_max_dC)) / 10.0;
- doc["stat_batt_power"] = ((float)((int32_t)datalayer.battery.status.active_power_W));
- doc["battery_current"] = ((float)((int16_t)datalayer.battery.status.current_dA)) / 10.0;
- doc["battery_voltage"] = ((float)datalayer.battery.status.voltage_dV) / 10.0;
- // publish only if cell voltages have been populated...
- if (datalayer.battery.info.number_of_cells != 0u &&
- datalayer.battery.status.cell_voltages_mV[datalayer.battery.info.number_of_cells - 1] != 0u) {
- doc["cell_max_voltage"] = ((float)datalayer.battery.status.cell_max_voltage_mV) / 1000.0;
- doc["cell_min_voltage"] = ((float)datalayer.battery.status.cell_min_voltage_mV) / 1000.0;
- }
- doc["total_capacity"] = ((float)datalayer.battery.info.total_capacity_Wh);
- 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);
+ 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;
+ doc["temperature_min"] = ((float)((int16_t)datalayer.battery.status.temperature_min_dC)) / 10.0;
+ doc["temperature_max"] = ((float)((int16_t)datalayer.battery.status.temperature_max_dC)) / 10.0;
+ doc["stat_batt_power"] = ((float)((int32_t)datalayer.battery.status.active_power_W));
+ doc["battery_current"] = ((float)((int16_t)datalayer.battery.status.current_dA)) / 10.0;
+ doc["battery_voltage"] = ((float)datalayer.battery.status.voltage_dV) / 10.0;
+ // publish only if cell voltages have been populated...
+ if (datalayer.battery.info.number_of_cells != 0u &&
+ datalayer.battery.status.cell_voltages_mV[datalayer.battery.info.number_of_cells - 1] != 0u) {
+ doc["cell_max_voltage"] = ((float)datalayer.battery.status.cell_max_voltage_mV) / 1000.0;
+ doc["cell_min_voltage"] = ((float)datalayer.battery.status.cell_min_voltage_mV) / 1000.0;
+ }
+ doc["total_capacity"] = ((float)datalayer.battery.info.total_capacity_Wh);
+ 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);
+ }
serializeJson(doc, mqtt_msg);
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp
index 8ceeb75d..edaa046f 100644
--- a/Software/src/devboard/utils/events.cpp
+++ b/Software/src/devboard/utils/events.cpp
@@ -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 "";
}
diff --git a/Software/src/devboard/utils/events.h b/Software/src/devboard/utils/events.h
index 9cecf393..34ce9282 100644
--- a/Software/src/devboard/utils/events.h
+++ b/Software/src/devboard/utils/events.h
@@ -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;
diff --git a/Software/src/devboard/utils/pause.cpp b/Software/src/devboard/utils/pause.cpp
new file mode 100644
index 00000000..18798956
--- /dev/null
+++ b/Software/src/devboard/utils/pause.cpp
@@ -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";
+ }
+}
\ No newline at end of file
diff --git a/Software/src/devboard/utils/pause.h b/Software/src/devboard/utils/pause.h
new file mode 100644
index 00000000..5524db8e
--- /dev/null
+++ b/Software/src/devboard/utils/pause.h
@@ -0,0 +1,17 @@
+#ifndef _PAUSE_H_
+#define _PAUSE_H_
+
+#include
+
+
+//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
\ No newline at end of file
diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp
index 634807e1..9ab41c72 100644
--- a/Software/src/devboard/webserver/webserver.cpp
+++ b/Software/src/devboard/webserver/webserver.cpp
@@ -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 += "✕";
}
+ if (emulator_pause_status == NORMAL)
+ content += "Pause status: " + String(get_emulator_pause_status().c_str()) + "
";
+ else
+ content +=
+ "Pause status: " + String(get_emulator_pause_status().c_str()) +
+ "
";
// Close the block
content += "";
@@ -778,6 +796,12 @@ String processor(const String& var) {
} else {
content += "✕";
}
+ if (emulator_pause_status == NORMAL)
+ content += "Pause status: " + String(get_emulator_pause_status().c_str()) + "
";
+ else
+ content +=
+ "Pause status: " + String(get_emulator_pause_status().c_str()) +
+ "
";
content += "";
content += "";
@@ -839,6 +863,11 @@ String processor(const String& var) {
content += "";
#endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
+ if (emulator_pause_request_ON)
+ content += "";
+ else
+ content += "";
+
content += "";
content += " ";
content += "";
@@ -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 += "";
@@ -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);
diff --git a/Software/src/lib/ayushsharma82-ElegantOTA/src/ElegantOTA.cpp b/Software/src/lib/ayushsharma82-ElegantOTA/src/ElegantOTA.cpp
index 42b84778..a414f349 100644
--- a/Software/src/lib/ayushsharma82-ElegantOTA/src/ElegantOTA.cpp
+++ b/Software/src/lib/ayushsharma82-ElegantOTA/src/ElegantOTA.cpp
@@ -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)
From bc70c70e09cc549092837a88771ee10042acbdd4 Mon Sep 17 00:00:00 2001
From: amarofarinha <151563493+amarofarinha@users.noreply.github.com>
Date: Fri, 13 Sep 2024 15:55:37 +0100
Subject: [PATCH 2/9] pre-commit and includes missing
---
Software/src/battery/BMW-I3-BATTERY.cpp | 2 ++
Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 1 +
Software/src/battery/CHADEMO-BATTERY.cpp | 2 ++
Software/src/battery/CHADEMO-SHUNTS.cpp | 1 +
.../src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 1 +
Software/src/battery/JAGUAR-IPACE-BATTERY.cpp | 1 +
Software/src/battery/KIA-E-GMP-BATTERY.cpp | 1 +
.../src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 1 +
.../battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp | 1 +
Software/src/battery/MG-5-BATTERY.cpp | 1 +
Software/src/battery/PYLON-BATTERY.cpp | 1 +
.../src/battery/RENAULT-KANGOO-BATTERY.cpp | 1 +
.../src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp | 1 +
.../src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp | 1 +
.../src/battery/SANTA-FE-PHEV-BATTERY.cpp | 1 +
Software/src/battery/TESLA-BATTERY.cpp | 1 +
Software/src/battery/VOLVO-SPA-BATTERY.cpp | 1 +
Software/src/devboard/utils/pause.cpp | 25 ++++++++-----------
Software/src/devboard/utils/pause.h | 5 ++--
Software/src/devboard/webserver/webserver.cpp | 8 ++----
20 files changed, 34 insertions(+), 23 deletions(-)
diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp
index 71e987d2..dda9e4bc 100644
--- a/Software/src/battery/BMW-I3-BATTERY.cpp
+++ b/Software/src/battery/BMW-I3-BATTERY.cpp
@@ -3,6 +3,8 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "BMW-I3-BATTERY.h"
+#include "../devboard/utils/pause.h"
+
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send
diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
index 4aba2657..dff8f188 100644
--- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
+++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "BYD-ATTO-3-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* TODO:
- Map all values from battery CAN messages
diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp
index ff9105fb..b9c15bf7 100644
--- a/Software/src/battery/CHADEMO-BATTERY.cpp
+++ b/Software/src/battery/CHADEMO-BATTERY.cpp
@@ -7,6 +7,8 @@
#include "CHADEMO-BATTERY-INTERNAL.h"
#include "CHADEMO-BATTERY.h"
#include "CHADEMO-SHUNTS.h"
+#include "../devboard/utils/pause.h"
+
/* CHADEMO handling runs at 6.25 times the rate of most other code, so, rather than the
* default value of 12 (for 12 iterations of the 5s value update loop) * 5 for a 60s timeout,
diff --git a/Software/src/battery/CHADEMO-SHUNTS.cpp b/Software/src/battery/CHADEMO-SHUNTS.cpp
index ec78e616..af733fed 100644
--- a/Software/src/battery/CHADEMO-SHUNTS.cpp
+++ b/Software/src/battery/CHADEMO-SHUNTS.cpp
@@ -22,6 +22,7 @@
#include "CHADEMO-BATTERY-INTERNAL.h"
#include "CHADEMO-BATTERY.h"
#include "CHADEMO-SHUNTS.h"
+#include "../devboard/utils/pause.h"
/* Initial frames received from ISA shunts provide invalid during initialization */
static int framecount = 0;
diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
index 66136c74..ed6daf84 100644
--- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
+++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "IMIEV-CZERO-ION-BATTERY.h"
+#include "../devboard/utils/pause.h"
//Code still work in progress, TODO:
//Figure out if CAN messages need to be sent to keep the system happy?
diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
index 720864c2..8de1a8c2 100644
--- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
+++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "JAGUAR-IPACE-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillisKeepAlive = 0;
diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp
index 0994a310..07036924 100644
--- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp
+++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp
@@ -4,6 +4,7 @@
#include "../devboard/utils/events.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "KIA-E-GMP-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis20ms = 0; // will store last time a 20ms CAN Message was send
diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
index fbe8eed8..8747e6c7 100644
--- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
+++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "KIA-HYUNDAI-64-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
index 664c9c27..1b270395 100644
--- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
+++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* TODO:
- The HEV battery seems to turn off after 1 minute of use. When this happens SOC% stops updating.
diff --git a/Software/src/battery/MG-5-BATTERY.cpp b/Software/src/battery/MG-5-BATTERY.cpp
index 3a4f8a82..7cb49495 100644
--- a/Software/src/battery/MG-5-BATTERY.cpp
+++ b/Software/src/battery/MG-5-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "MG-5-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* TODO:
- Get contactor closing working
diff --git a/Software/src/battery/PYLON-BATTERY.cpp b/Software/src/battery/PYLON-BATTERY.cpp
index 7f053f22..d6f41e6d 100644
--- a/Software/src/battery/PYLON-BATTERY.cpp
+++ b/Software/src/battery/PYLON-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "PYLON-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
index 75a992c4..304a88b2 100644
--- a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
+++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RENAULT-KANGOO-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* TODO:
There seems to be some values on the Kangoo that differ between the 22/33 kWh version
diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
index 126b6b70..1b29361d 100644
--- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
+++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RENAULT-ZOE-GEN1-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* Information in this file is based of the OVMS V3 vehicle_renaultzoe.cpp component
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/blob/master/vehicle/OVMS.V3/components/vehicle_renaultzoe/src/vehicle_renaultzoe.cpp
diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp
index d90fb3e4..0da8b9a1 100644
--- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp
+++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RENAULT-ZOE-GEN2-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* Information in this file is based of the OVMS V3 vehicle_renaultzoe.cpp component
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/blob/master/vehicle/OVMS.V3/components/vehicle_renaultzoe_ph2_obd/src/vehicle_renaultzoe_ph2_obd.cpp
diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
index 797b5290..1a2654d4 100644
--- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
+++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SANTA-FE-PHEV-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* Credits go to maciek16c for these findings!
https://github.com/maciek16c/hyundai-santa-fe-phev-battery
diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp
index 5067f971..6085ef0f 100644
--- a/Software/src/battery/TESLA-BATTERY.cpp
+++ b/Software/src/battery/TESLA-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "TESLA-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
/* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */
diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp
index 2c927890..ac5784a3 100644
--- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp
+++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp
@@ -3,6 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "VOLVO-SPA-BATTERY.h"
+#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
diff --git a/Software/src/devboard/utils/pause.cpp b/Software/src/devboard/utils/pause.cpp
index 18798956..e2a01960 100644
--- a/Software/src/devboard/utils/pause.cpp
+++ b/Software/src/devboard/utils/pause.cpp
@@ -2,28 +2,26 @@
#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) {
-
+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
+ 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 {
@@ -37,15 +35,14 @@ void setBatteryPause(bool pause_battery,bool pause_CAN) {
/// @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() {
-
+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 ) {
+ datalayer.battery.status.current_dA > -18) {
emulator_pause_status = PAUSED;
}
@@ -70,4 +67,4 @@ std::string get_emulator_pause_status() {
default:
return "UNKNOWN";
}
-}
\ No newline at end of file
+}
diff --git a/Software/src/devboard/utils/pause.h b/Software/src/devboard/utils/pause.h
index 5524db8e..f0908595 100644
--- a/Software/src/devboard/utils/pause.h
+++ b/Software/src/devboard/utils/pause.h
@@ -3,7 +3,6 @@
#include
-
//battery pause status
enum battery_pause_status { NORMAL = 0, PAUSING = 1, PAUSED = 2, RESUMING = 3 };
extern bool emulator_pause_request_ON;
@@ -11,7 +10,7 @@ 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 setBatteryPause(bool pause_battery, bool pause_CAN);
void emulator_pause_state_send_CAN_battery();
std::string get_emulator_pause_status();
-#endif
\ No newline at end of file
+#endif
diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp
index 9ab41c72..d6f71976 100644
--- a/Software/src/devboard/webserver/webserver.cpp
+++ b/Software/src/devboard/webserver/webserver.cpp
@@ -717,9 +717,7 @@ String processor(const String& var) {
if (emulator_pause_status == NORMAL)
content += "Pause status: " + String(get_emulator_pause_status().c_str()) + "
";
else
- content +=
- "Pause status: " + String(get_emulator_pause_status().c_str()) +
- "
";
+ content += "Pause status: " + String(get_emulator_pause_status().c_str()) + "
";
// Close the block
content += "";
@@ -799,9 +797,7 @@ String processor(const String& var) {
if (emulator_pause_status == NORMAL)
content += "Pause status: " + String(get_emulator_pause_status().c_str()) + "
";
else
- content +=
- "Pause status: " + String(get_emulator_pause_status().c_str()) +
- "
";
+ content += "Pause status: " + String(get_emulator_pause_status().c_str()) + "
";
content += "";
content += "";
From a8010abdd9b30444280975c4294183508ef2076c Mon Sep 17 00:00:00 2001
From: amarofarinha <151563493+amarofarinha@users.noreply.github.com>
Date: Fri, 13 Sep 2024 17:57:26 +0100
Subject: [PATCH 3/9] Refactor: Code cleanup and optimization without
functional changes
---
Software/src/battery/BMW-I3-BATTERY.cpp | 2 -
Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 1 -
Software/src/battery/CHADEMO-BATTERY.cpp | 2 -
Software/src/battery/CHADEMO-SHUNTS.cpp | 1 -
.../src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 1 -
Software/src/battery/JAGUAR-IPACE-BATTERY.cpp | 1 -
Software/src/battery/KIA-E-GMP-BATTERY.cpp | 1 -
.../src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 1 -
.../battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp | 1 -
Software/src/battery/MG-5-BATTERY.cpp | 1 -
Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 1 -
Software/src/battery/PYLON-BATTERY.cpp | 1 -
.../src/battery/RENAULT-KANGOO-BATTERY.cpp | 1 -
.../src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp | 1 -
.../src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp | 1 -
.../src/battery/SANTA-FE-PHEV-BATTERY.cpp | 1 -
Software/src/battery/TESLA-BATTERY.cpp | 1 -
Software/src/battery/TEST-FAKE-BATTERY.cpp | 1 -
Software/src/battery/VOLVO-SPA-BATTERY.cpp | 1 -
Software/src/devboard/mqtt/mqtt.cpp | 1 -
Software/src/devboard/safety/safety.cpp | 71 +++++++++++++++++++
Software/src/devboard/safety/safety.h | 15 ++++
Software/src/devboard/utils/pause.cpp | 70 ------------------
Software/src/devboard/utils/pause.h | 16 -----
Software/src/devboard/webserver/webserver.cpp | 1 -
25 files changed, 86 insertions(+), 109 deletions(-)
delete mode 100644 Software/src/devboard/utils/pause.cpp
delete mode 100644 Software/src/devboard/utils/pause.h
diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp
index dda9e4bc..71e987d2 100644
--- a/Software/src/battery/BMW-I3-BATTERY.cpp
+++ b/Software/src/battery/BMW-I3-BATTERY.cpp
@@ -3,8 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "BMW-I3-BATTERY.h"
-#include "../devboard/utils/pause.h"
-
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send
diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
index dff8f188..4aba2657 100644
--- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
+++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "BYD-ATTO-3-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* TODO:
- Map all values from battery CAN messages
diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp
index b9c15bf7..ff9105fb 100644
--- a/Software/src/battery/CHADEMO-BATTERY.cpp
+++ b/Software/src/battery/CHADEMO-BATTERY.cpp
@@ -7,8 +7,6 @@
#include "CHADEMO-BATTERY-INTERNAL.h"
#include "CHADEMO-BATTERY.h"
#include "CHADEMO-SHUNTS.h"
-#include "../devboard/utils/pause.h"
-
/* CHADEMO handling runs at 6.25 times the rate of most other code, so, rather than the
* default value of 12 (for 12 iterations of the 5s value update loop) * 5 for a 60s timeout,
diff --git a/Software/src/battery/CHADEMO-SHUNTS.cpp b/Software/src/battery/CHADEMO-SHUNTS.cpp
index af733fed..ec78e616 100644
--- a/Software/src/battery/CHADEMO-SHUNTS.cpp
+++ b/Software/src/battery/CHADEMO-SHUNTS.cpp
@@ -22,7 +22,6 @@
#include "CHADEMO-BATTERY-INTERNAL.h"
#include "CHADEMO-BATTERY.h"
#include "CHADEMO-SHUNTS.h"
-#include "../devboard/utils/pause.h"
/* Initial frames received from ISA shunts provide invalid during initialization */
static int framecount = 0;
diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
index ed6daf84..66136c74 100644
--- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
+++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "IMIEV-CZERO-ION-BATTERY.h"
-#include "../devboard/utils/pause.h"
//Code still work in progress, TODO:
//Figure out if CAN messages need to be sent to keep the system happy?
diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
index 8de1a8c2..720864c2 100644
--- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
+++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "JAGUAR-IPACE-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillisKeepAlive = 0;
diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp
index 07036924..0994a310 100644
--- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp
+++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp
@@ -4,7 +4,6 @@
#include "../devboard/utils/events.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "KIA-E-GMP-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis20ms = 0; // will store last time a 20ms CAN Message was send
diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
index 8747e6c7..fbe8eed8 100644
--- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
+++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "KIA-HYUNDAI-64-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
index 1b270395..664c9c27 100644
--- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
+++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* TODO:
- The HEV battery seems to turn off after 1 minute of use. When this happens SOC% stops updating.
diff --git a/Software/src/battery/MG-5-BATTERY.cpp b/Software/src/battery/MG-5-BATTERY.cpp
index 7cb49495..3a4f8a82 100644
--- a/Software/src/battery/MG-5-BATTERY.cpp
+++ b/Software/src/battery/MG-5-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "MG-5-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* TODO:
- Get contactor closing working
diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp
index 284e0188..a4a47285 100644
--- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp
+++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp
@@ -6,7 +6,6 @@
#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
diff --git a/Software/src/battery/PYLON-BATTERY.cpp b/Software/src/battery/PYLON-BATTERY.cpp
index d6f41e6d..7f053f22 100644
--- a/Software/src/battery/PYLON-BATTERY.cpp
+++ b/Software/src/battery/PYLON-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "PYLON-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
index 304a88b2..75a992c4 100644
--- a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
+++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RENAULT-KANGOO-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* TODO:
There seems to be some values on the Kangoo that differ between the 22/33 kWh version
diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
index 1b29361d..126b6b70 100644
--- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
+++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RENAULT-ZOE-GEN1-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* Information in this file is based of the OVMS V3 vehicle_renaultzoe.cpp component
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/blob/master/vehicle/OVMS.V3/components/vehicle_renaultzoe/src/vehicle_renaultzoe.cpp
diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp
index 0da8b9a1..d90fb3e4 100644
--- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp
+++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RENAULT-ZOE-GEN2-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* Information in this file is based of the OVMS V3 vehicle_renaultzoe.cpp component
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/blob/master/vehicle/OVMS.V3/components/vehicle_renaultzoe_ph2_obd/src/vehicle_renaultzoe_ph2_obd.cpp
diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
index 1a2654d4..797b5290 100644
--- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
+++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SANTA-FE-PHEV-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* Credits go to maciek16c for these findings!
https://github.com/maciek16c/hyundai-santa-fe-phev-battery
diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp
index 6085ef0f..5067f971 100644
--- a/Software/src/battery/TESLA-BATTERY.cpp
+++ b/Software/src/battery/TESLA-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "TESLA-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
/* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */
diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp
index 0134bf9d..d8963340 100644
--- a/Software/src/battery/TEST-FAKE-BATTERY.cpp
+++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp
@@ -1,7 +1,6 @@
#include "../include.h"
#ifdef TEST_FAKE_BATTERY
#include "../datalayer/datalayer.h"
-#include "../devboard/utils/pause.h"
#include "TEST-FAKE-BATTERY.h"
diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp
index ac5784a3..2c927890 100644
--- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp
+++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp
@@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "VOLVO-SPA-BATTERY.h"
-#include "../devboard/utils/pause.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp
index fc5b29e7..a6f3b4bf 100644
--- a/Software/src/devboard/mqtt/mqtt.cpp
+++ b/Software/src/devboard/mqtt/mqtt.cpp
@@ -8,7 +8,6 @@
#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;
diff --git a/Software/src/devboard/safety/safety.cpp b/Software/src/devboard/safety/safety.cpp
index 51148ed7..1987d4ad 100644
--- a/Software/src/devboard/safety/safety.cpp
+++ b/Software/src/devboard/safety/safety.cpp
@@ -9,6 +9,14 @@ static bool battery_empty_event_fired = false;
#define MAX_SOH_DEVIATION_PPTT 2500
+//battery pause status begin
+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;
+//battery pause status end
+
void update_machineryprotection() {
// Start checking that the battery is within reason. Incase we see any funny business, raise an event!
@@ -171,3 +179,66 @@ void update_machineryprotection() {
#endif // DOUBLE_BATTERY
}
+
+//battery pause status begin
+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";
+ }
+}
+//battery pause status
diff --git a/Software/src/devboard/safety/safety.h b/Software/src/devboard/safety/safety.h
index 8c547728..097f5d35 100644
--- a/Software/src/devboard/safety/safety.h
+++ b/Software/src/devboard/safety/safety.h
@@ -1,11 +1,26 @@
#ifndef SAFETY_H
#define SAFETY_H
#include
+#include
#define MAX_CAN_FAILURES 50
#define MAX_CHARGE_DISCHARGE_LIMIT_FAILURES 1
+//battery pause status begin
+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;
+//battery pause status end
+
void update_machineryprotection();
+//battery pause status begin
+void setBatteryPause(bool pause_battery, bool pause_CAN);
+void emulator_pause_state_send_CAN_battery();
+std::string get_emulator_pause_status();
+//battery pause status end
+
#endif
diff --git a/Software/src/devboard/utils/pause.cpp b/Software/src/devboard/utils/pause.cpp
deleted file mode 100644
index e2a01960..00000000
--- a/Software/src/devboard/utils/pause.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#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";
- }
-}
diff --git a/Software/src/devboard/utils/pause.h b/Software/src/devboard/utils/pause.h
deleted file mode 100644
index f0908595..00000000
--- a/Software/src/devboard/utils/pause.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef _PAUSE_H_
-#define _PAUSE_H_
-
-#include
-
-//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
diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp
index d6f71976..e2bb79d8 100644
--- a/Software/src/devboard/webserver/webserver.cpp
+++ b/Software/src/devboard/webserver/webserver.cpp
@@ -3,7 +3,6 @@
#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
From 2a4dcb3d630ea6852852ad4eef1c1919b6376413 Mon Sep 17 00:00:00 2001
From: amarofarinha <151563493+amarofarinha@users.noreply.github.com>
Date: Fri, 13 Sep 2024 18:57:22 +0100
Subject: [PATCH 4/9] compilation fix
---
Software/src/battery/BMW-I3-BATTERY.cpp | 1406 +++++++++++------------
1 file changed, 703 insertions(+), 703 deletions(-)
diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp
index 71e987d2..beaaaafe 100644
--- a/Software/src/battery/BMW-I3-BATTERY.cpp
+++ b/Software/src/battery/BMW-I3-BATTERY.cpp
@@ -390,761 +390,761 @@ void update_values_battery2() { //This function maps all the values fetched via
} else {
datalayer.battery2.status.max_charge_power_W = battery2_BEV_available_power_longterm_charge;
}
-
- battery2_power = (datalayer.battery2.status.current_dA * (datalayer.battery2.status.voltage_dV / 100));
-
- datalayer.battery2.status.active_power_W = battery2_power;
-
- datalayer.battery2.status.temperature_min_dC = battery2_temperature_min * 10; // Add a decimal
-
- datalayer.battery2.status.temperature_max_dC = battery2_temperature_max * 10; // Add a decimal
-
- 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
- if (!battery_awake) {
- return;
- }
+ battery2_power = (datalayer.battery2.status.current_dA * (datalayer.battery2.status.voltage_dV / 100));
- datalayer.battery.status.real_soc = (battery_display_SOC * 50);
+ datalayer.battery2.status.active_power_W = battery2_power;
- datalayer.battery.status.voltage_dV = battery_volts; //Unit V+1 (5000 = 500.0V)
+ datalayer.battery2.status.temperature_min_dC = battery2_temperature_min * 10; // Add a decimal
- datalayer.battery.status.current_dA = battery_current;
+ datalayer.battery2.status.temperature_max_dC = battery2_temperature_max * 10; // Add a decimal
- datalayer.battery.info.total_capacity_Wh = (battery_energy_content_maximum_kWh * 1000); // Convert kWh to Wh
+ 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];
+}
- datalayer.battery.status.remaining_capacity_Wh = battery_predicted_energy_charge_condition;
+void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
+ if (!battery_awake) {
+ return;
+ }
- datalayer.battery.status.soh_pptt = battery_soh * 100;
+ datalayer.battery.status.real_soc = (battery_display_SOC * 50);
- 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;
- }
+ datalayer.battery.status.voltage_dV = battery_volts; //Unit V+1 (5000 = 500.0V)
- battery_power = (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100));
+ datalayer.battery.status.current_dA = battery_current;
- datalayer.battery.status.active_power_W = battery_power;
+ datalayer.battery.info.total_capacity_Wh = (battery_energy_content_maximum_kWh * 1000); // Convert kWh to Wh
- datalayer.battery.status.temperature_min_dC = battery_temperature_min * 10; // Add a decimal
+ datalayer.battery.status.remaining_capacity_Wh = battery_predicted_energy_charge_condition;
- datalayer.battery.status.temperature_max_dC = battery_temperature_max * 10; // Add a decimal
+ datalayer.battery.status.soh_pptt = battery_soh * 100;
- if (battery_info_available) {
- // Start checking safeties. First up, cellvoltages!
- if (detectedBattery == BATTERY_60AH) {
- datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
- datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
- if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_60AH) {
- set_event(EVENT_CELL_OVER_VOLTAGE, 0);
- }
- if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_60AH) {
- set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
- }
- } else if (detectedBattery == BATTERY_94AH) {
- datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_94AH;
- datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_94AH;
- if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_94AH) {
- set_event(EVENT_CELL_OVER_VOLTAGE, 0);
- }
- if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_94AH) {
- set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
- }
- } else { // BATTERY_120AH
- datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_120AH;
- datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_120AH;
- if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_120AH) {
- set_event(EVENT_CELL_OVER_VOLTAGE, 0);
- }
- if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_120AH) {
- set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
- }
+ 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));
+
+ datalayer.battery.status.active_power_W = battery_power;
+
+ datalayer.battery.status.temperature_min_dC = battery_temperature_min * 10; // Add a decimal
+
+ datalayer.battery.status.temperature_max_dC = battery_temperature_max * 10; // Add a decimal
+
+ if (battery_info_available) {
+ // Start checking safeties. First up, cellvoltages!
+ if (detectedBattery == BATTERY_60AH) {
+ datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
+ datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
+ if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_60AH) {
+ set_event(EVENT_CELL_OVER_VOLTAGE, 0);
+ }
+ if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_60AH) {
+ set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
+ }
+ } else if (detectedBattery == BATTERY_94AH) {
+ datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_94AH;
+ datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_94AH;
+ if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_94AH) {
+ set_event(EVENT_CELL_OVER_VOLTAGE, 0);
+ }
+ if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_94AH) {
+ set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
+ }
+ } else { // BATTERY_120AH
+ datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_120AH;
+ datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_120AH;
+ if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_120AH) {
+ set_event(EVENT_CELL_OVER_VOLTAGE, 0);
+ }
+ if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_120AH) {
+ set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
}
+ }
- // Perform other safety checks
- if (battery_status_error_locking == 2) { // HVIL seated?
- set_event(EVENT_HVIL_FAILURE, 0);
- } else {
- clear_event(EVENT_HVIL_FAILURE);
- }
- if (battery_status_precharge_locked == 2) { // Capacitor seated?
- set_event(EVENT_PRECHARGE_FAILURE, 0);
- } else {
- clear_event(EVENT_PRECHARGE_FAILURE);
- }
+ // Perform other safety checks
+ if (battery_status_error_locking == 2) { // HVIL seated?
+ set_event(EVENT_HVIL_FAILURE, 0);
+ } else {
+ clear_event(EVENT_HVIL_FAILURE);
+ }
+ if (battery_status_precharge_locked == 2) { // Capacitor seated?
+ set_event(EVENT_PRECHARGE_FAILURE, 0);
+ } else {
+ clear_event(EVENT_PRECHARGE_FAILURE);
+ }
#ifdef DEBUG_VIA_USB
- Serial.println(" ");
- Serial.print("Battery display SOC%: ");
- Serial.print(battery_display_SOC * 50);
- Serial.print("Battery display SOC%: ");
- Serial.print(battery_HVBatt_SOC * 10);
- Serial.print("Battery polled SOC%: ");
- Serial.print(battery_soc);
- Serial.print(" Battery voltage: ");
- Serial.print(datalayer.battery.status.voltage_dV * 0.1);
- Serial.print(" Battery current: ");
- Serial.print(datalayer.battery.status.current_dA * 0.1);
- Serial.print(" Wh when full: ");
- Serial.print(datalayer.battery.info.total_capacity_Wh);
- Serial.print(" Remaining Wh: ");
- Serial.print(datalayer.battery.status.remaining_capacity_Wh);
- Serial.print(" Max charge power: ");
- Serial.print(datalayer.battery.status.max_charge_power_W);
- Serial.print(" Max discharge power: ");
- Serial.print(datalayer.battery.status.max_discharge_power_W);
- Serial.print(" Active power: ");
- Serial.print(datalayer.battery.status.active_power_W);
- Serial.print(" Min temp: ");
- Serial.print(datalayer.battery.status.temperature_min_dC * 0.1);
- Serial.print(" Max temp: ");
- Serial.print(datalayer.battery.status.temperature_max_dC * 0.1);
+ Serial.println(" ");
+ Serial.print("Battery display SOC%: ");
+ Serial.print(battery_display_SOC * 50);
+ Serial.print("Battery display SOC%: ");
+ Serial.print(battery_HVBatt_SOC * 10);
+ Serial.print("Battery polled SOC%: ");
+ Serial.print(battery_soc);
+ Serial.print(" Battery voltage: ");
+ Serial.print(datalayer.battery.status.voltage_dV * 0.1);
+ Serial.print(" Battery current: ");
+ Serial.print(datalayer.battery.status.current_dA * 0.1);
+ Serial.print(" Wh when full: ");
+ Serial.print(datalayer.battery.info.total_capacity_Wh);
+ Serial.print(" Remaining Wh: ");
+ Serial.print(datalayer.battery.status.remaining_capacity_Wh);
+ Serial.print(" Max charge power: ");
+ Serial.print(datalayer.battery.status.max_charge_power_W);
+ Serial.print(" Max discharge power: ");
+ Serial.print(datalayer.battery.status.max_discharge_power_W);
+ Serial.print(" Active power: ");
+ Serial.print(datalayer.battery.status.active_power_W);
+ Serial.print(" Min temp: ");
+ Serial.print(datalayer.battery.status.temperature_min_dC * 0.1);
+ Serial.print(" Max temp: ");
+ Serial.print(datalayer.battery.status.temperature_max_dC * 0.1);
#endif
- }
+}
- 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;
- datalayer.battery.status.CAN_battery_still_alive =
- CAN_STILL_ALIVE; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
- battery_current = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) - 8192; //deciAmps (-819.2 to 819.0A)
- battery_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
- datalayer.battery.status.voltage_dV = battery_volts; // Update the datalayer as soon as possible with this info
- battery_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
- battery_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
- battery_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
- battery_request_open_contactors_fast = (rx_frame.data.u8[6] & 0x0C) >> 2;
- battery_charging_condition_delta = (rx_frame.data.u8[6] & 0xF0) >> 4;
- battery_DC_link_voltage = rx_frame.data.u8[7];
- break;
- case 0x1FA: //BMS [1000ms] Status Of High-Voltage Battery - 1
- battery_status_error_isolation_external_Bordnetz = (rx_frame.data.u8[0] & 0x03);
- battery_status_error_isolation_internal_Bordnetz = (rx_frame.data.u8[0] & 0x0C) >> 2;
- battery_request_cooling = (rx_frame.data.u8[0] & 0x30) >> 4;
- battery_status_valve_cooling = (rx_frame.data.u8[0] & 0xC0) >> 6;
- battery_status_error_locking = (rx_frame.data.u8[1] & 0x03);
- battery_status_precharge_locked = (rx_frame.data.u8[1] & 0x0C) >> 2;
- battery_status_disconnecting_switch = (rx_frame.data.u8[1] & 0x30) >> 4;
- battery_status_emergency_mode = (rx_frame.data.u8[1] & 0xC0) >> 6;
- battery_request_service = (rx_frame.data.u8[2] & 0x03);
- battery_error_emergency_mode = (rx_frame.data.u8[2] & 0x0C) >> 2;
- battery_status_error_disconnecting_switch = (rx_frame.data.u8[2] & 0x30) >> 4;
- battery_status_warning_isolation = (rx_frame.data.u8[2] & 0xC0) >> 6;
- battery_status_cold_shutoff_valve = (rx_frame.data.u8[3] & 0x0F);
- battery_temperature_HV = (rx_frame.data.u8[4] - 50);
- battery_temperature_heat_exchanger = (rx_frame.data.u8[5] - 50);
- battery_temperature_min = (rx_frame.data.u8[6] - 50);
- battery_temperature_max = (rx_frame.data.u8[7] - 50);
- break;
- case 0x239: //BMS [200ms]
- battery_predicted_energy_charge_condition = (rx_frame.data.u8[2] << 8 | rx_frame.data.u8[1]); //Wh
- battery_predicted_energy_charging_target = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[3]) * 0.02); //kWh
- break;
- case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
- battery_awake = true;
- if (!skipCRCCheck) {
- if (calculateCRC(rx_frame, rx_frame.DLC, 0x15) != rx_frame.data.u8[0]) {
- // If calculated CRC does not match transmitted CRC, increase CANerror counter
- datalayer.battery.status.CAN_error_counter++;
+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;
+ datalayer.battery.status.CAN_battery_still_alive =
+ CAN_STILL_ALIVE; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
+ battery_current = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) - 8192; //deciAmps (-819.2 to 819.0A)
+ battery_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
+ datalayer.battery.status.voltage_dV = battery_volts; // Update the datalayer as soon as possible with this info
+ battery_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
+ battery_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
+ battery_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
+ battery_request_open_contactors_fast = (rx_frame.data.u8[6] & 0x0C) >> 2;
+ battery_charging_condition_delta = (rx_frame.data.u8[6] & 0xF0) >> 4;
+ battery_DC_link_voltage = rx_frame.data.u8[7];
+ break;
+ case 0x1FA: //BMS [1000ms] Status Of High-Voltage Battery - 1
+ battery_status_error_isolation_external_Bordnetz = (rx_frame.data.u8[0] & 0x03);
+ battery_status_error_isolation_internal_Bordnetz = (rx_frame.data.u8[0] & 0x0C) >> 2;
+ battery_request_cooling = (rx_frame.data.u8[0] & 0x30) >> 4;
+ battery_status_valve_cooling = (rx_frame.data.u8[0] & 0xC0) >> 6;
+ battery_status_error_locking = (rx_frame.data.u8[1] & 0x03);
+ battery_status_precharge_locked = (rx_frame.data.u8[1] & 0x0C) >> 2;
+ battery_status_disconnecting_switch = (rx_frame.data.u8[1] & 0x30) >> 4;
+ battery_status_emergency_mode = (rx_frame.data.u8[1] & 0xC0) >> 6;
+ battery_request_service = (rx_frame.data.u8[2] & 0x03);
+ battery_error_emergency_mode = (rx_frame.data.u8[2] & 0x0C) >> 2;
+ battery_status_error_disconnecting_switch = (rx_frame.data.u8[2] & 0x30) >> 4;
+ battery_status_warning_isolation = (rx_frame.data.u8[2] & 0xC0) >> 6;
+ battery_status_cold_shutoff_valve = (rx_frame.data.u8[3] & 0x0F);
+ battery_temperature_HV = (rx_frame.data.u8[4] - 50);
+ battery_temperature_heat_exchanger = (rx_frame.data.u8[5] - 50);
+ battery_temperature_min = (rx_frame.data.u8[6] - 50);
+ battery_temperature_max = (rx_frame.data.u8[7] - 50);
+ break;
+ case 0x239: //BMS [200ms]
+ battery_predicted_energy_charge_condition = (rx_frame.data.u8[2] << 8 | rx_frame.data.u8[1]); //Wh
+ battery_predicted_energy_charging_target = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[3]) * 0.02); //kWh
+ break;
+ case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
+ battery_awake = true;
+ if (!skipCRCCheck) {
+ if (calculateCRC(rx_frame, rx_frame.DLC, 0x15) != rx_frame.data.u8[0]) {
+ // If calculated CRC does not match transmitted CRC, increase CANerror counter
+ datalayer.battery.status.CAN_error_counter++;
- // If the CRC check has never passed before, set the flag to skip future checks. Some SMEs have differing CRC checks.
- if (!CRCCheckPassedPreviously) {
- skipCRCCheck = true;
- }
- break;
- } else {
- // If CRC check passes, update the flag
- CRCCheckPassedPreviously = true;
+ // If the CRC check has never passed before, set the flag to skip future checks. Some SMEs have differing CRC checks.
+ if (!CRCCheckPassedPreviously) {
+ skipCRCCheck = true;
}
- }
-
- // Process the data since CRC check is either passed or skipped
- battery_status_diagnostics_HV = (rx_frame.data.u8[2] & 0x0F);
- break;
- case 0x2F5: //BMS [100ms] High-Voltage Battery Charge/Discharge Limitations
- battery_max_charge_voltage = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- battery_max_charge_amperage = (((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 819.2);
- battery_min_discharge_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
- battery_max_discharge_amperage = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) - 819.2);
- break;
- case 0x2FF: //BMS [100ms] Status Heating High-Voltage Battery
- battery_awake = true;
- battery_actual_value_power_heating = (rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4);
- break;
- case 0x363: //BMS [1s] Identification High-Voltage Battery
- battery_serial_number =
- (rx_frame.data.u8[3] << 24 | rx_frame.data.u8[2] << 16 | rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- break;
- case 0x3C2: //BMS (94AH exclusive) - Status diagnostics OBD 2 powertrain
- battery_status_diagnosis_powertrain_maximum_multiplexer =
- ((rx_frame.data.u8[1] & 0x03) << 4 | rx_frame.data.u8[0] >> 4);
- battery_status_diagnosis_powertrain_immediate_multiplexer = (rx_frame.data.u8[0] & 0xFC) >> 2;
- break;
- case 0x3EB: //BMS [1s] Status of charging high-voltage storage - 3
- battery_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
- battery_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
- battery_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
- battery_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
- break;
- case 0x40D: //BMS [1s] Charging status of high-voltage storage - 1
- battery_BEV_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
- battery_BEV_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
- battery_BEV_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
- battery_BEV_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
- break;
- case 0x41C: //BMS [1s] Operating Mode Status Of Hybrid - 2
- battery_status_cooling_HV = (rx_frame.data.u8[1] & 0x03);
- break;
- case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
- battery_prediction_voltage_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- battery_prediction_voltage_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
- battery_prediction_voltage_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
- battery_prediction_voltage_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]);
- break;
- case 0x431: //BMS [200ms] Data High-Voltage Battery Unit
- battery_status_service_disconnection_plug = (rx_frame.data.u8[0] & 0x0F);
- battery_status_measurement_isolation = (rx_frame.data.u8[0] & 0x0C) >> 2;
- battery_request_abort_charging = (rx_frame.data.u8[0] & 0x30) >> 4;
- battery_prediction_duration_charging_minutes = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
- battery_prediction_time_end_of_charging_minutes = rx_frame.data.u8[4];
- battery_energy_content_maximum_kWh = (((rx_frame.data.u8[6] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
- if (battery_energy_content_maximum_kWh > 33) {
- detectedBattery = BATTERY_120AH;
- } else if (battery_energy_content_maximum_kWh > 20) {
- detectedBattery = BATTERY_94AH;
+ break;
} else {
- detectedBattery = BATTERY_60AH;
+ // If CRC check passes, update the flag
+ CRCCheckPassedPreviously = true;
}
- break;
- case 0x432: //BMS [200ms] SOC% info
- battery_request_operating_mode = (rx_frame.data.u8[0] & 0x03);
- battery_target_voltage_in_CV_mode = ((rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4)) / 10;
- battery_request_charging_condition_minimum = (rx_frame.data.u8[2] / 2);
- battery_request_charging_condition_maximum = (rx_frame.data.u8[3] / 2);
- battery_display_SOC = rx_frame.data.u8[4];
- break;
- case 0x507: //BMS [640ms] Network Management - 2 - This message is sent on the bus for sleep coordination purposes
- break;
- case 0x587: //BMS [5s] Services
- 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 (rx_frame.DLC == 6) {
- transmit_can(&BMW_6F4_CELL_CONTINUE, can_config.battery); // 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] =
- (rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]);
- }
- }
-
- if (rx_frame.DLC > 6 && next_data == 0 && rx_frame.data.u8[0] == 0xf1) {
- uint8_t count = 6;
- while (count < rx_frame.DLC && next_data < 49) {
- message_data[next_data++] = rx_frame.data.u8[count++];
- }
- transmit_can(&BMW_6F1_CONTINUE, can_config.battery); // 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)) {
- uint8_t count = 2;
- while (count < rx_frame.DLC && next_data < 49) {
- message_data[next_data++] = rx_frame.data.u8[count++];
- }
-
- switch (cmdState) {
- case CELL_VOLTAGE_MINMAX:
- if (next_data >= 4) {
- datalayer.battery.status.cell_min_voltage_mV = (message_data[0] << 8 | message_data[1]);
- datalayer.battery.status.cell_max_voltage_mV = (message_data[2] << 8 | message_data[3]);
- }
- break;
- case SOH:
- if (next_data >= 4) {
- battery_soh = message_data[3];
- battery_info_available = true;
- }
- break;
- case SOC:
- if (next_data >= 6) {
- battery_soc = (message_data[0] << 8 | message_data[1]);
- battery_soc_hvmax = (message_data[2] << 8 | message_data[3]);
- battery_soc_hvmin = (message_data[4] << 8 | message_data[5]);
- }
- break;
- }
- }
- break;
- default:
- break;
- }
- }
- 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;
- datalayer.battery2.status.CAN_battery_still_alive =
- CAN_STILL_ALIVE; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
- battery2_current = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) - 8192; //deciAmps (-819.2 to 819.0A)
- battery2_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
- datalayer.battery2.status.voltage_dV =
- battery2_volts; // Update the datalayer as soon as possible with this info, needed for contactor control
- battery2_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
- battery2_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
- battery2_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
- battery2_request_open_contactors_fast = (rx_frame.data.u8[6] & 0x0C) >> 2;
- battery2_charging_condition_delta = (rx_frame.data.u8[6] & 0xF0) >> 4;
- battery2_DC_link_voltage = rx_frame.data.u8[7];
- break;
- case 0x1FA: //BMS [1000ms] Status Of High-Voltage Battery - 1
- battery2_status_error_isolation_external_Bordnetz = (rx_frame.data.u8[0] & 0x03);
- battery2_status_error_isolation_internal_Bordnetz = (rx_frame.data.u8[0] & 0x0C) >> 2;
- battery2_request_cooling = (rx_frame.data.u8[0] & 0x30) >> 4;
- battery2_status_valve_cooling = (rx_frame.data.u8[0] & 0xC0) >> 6;
- battery2_status_error_locking = (rx_frame.data.u8[1] & 0x03);
- battery2_status_precharge_locked = (rx_frame.data.u8[1] & 0x0C) >> 2;
- battery2_status_disconnecting_switch = (rx_frame.data.u8[1] & 0x30) >> 4;
- battery2_status_emergency_mode = (rx_frame.data.u8[1] & 0xC0) >> 6;
- battery2_request_service = (rx_frame.data.u8[2] & 0x03);
- battery2_error_emergency_mode = (rx_frame.data.u8[2] & 0x0C) >> 2;
- battery2_status_error_disconnecting_switch = (rx_frame.data.u8[2] & 0x30) >> 4;
- battery2_status_warning_isolation = (rx_frame.data.u8[2] & 0xC0) >> 6;
- battery2_status_cold_shutoff_valve = (rx_frame.data.u8[3] & 0x0F);
- battery2_temperature_HV = (rx_frame.data.u8[4] - 50);
- battery2_temperature_heat_exchanger = (rx_frame.data.u8[5] - 50);
- battery2_temperature_min = (rx_frame.data.u8[6] - 50);
- battery2_temperature_max = (rx_frame.data.u8[7] - 50);
- break;
- case 0x239: //BMS [200ms]
- battery2_predicted_energy_charge_condition = (rx_frame.data.u8[2] << 8 | rx_frame.data.u8[1]); //Wh
- battery2_predicted_energy_charging_target = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[3]) * 0.02); //kWh
- break;
- case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
- battery2_awake = true;
- if (!skipCRCCheck_battery2) {
- if (calculateCRC(rx_frame, rx_frame.DLC, 0x15) != rx_frame.data.u8[0]) {
- // If calculated CRC does not match transmitted CRC, increase CANerror counter
- datalayer.battery2.status.CAN_error_counter++;
-
- // If the CRC check has never passed before, set the flag to skip future checks. Some SMEs have differing CRC checks.
- if (!CRCCheckPassedPreviously_battery2) {
- skipCRCCheck_battery2 = true;
- }
- break;
- } else {
- // If CRC check passes, update the flag
- CRCCheckPassedPreviously_battery2 = true;
- }
- }
-
- // Process the data since CRC check is either passed or skipped
- battery2_status_diagnostics_HV = (rx_frame.data.u8[2] & 0x0F);
- break;
- case 0x2F5: //BMS [100ms] High-Voltage Battery Charge/Discharge Limitations
- battery2_max_charge_voltage = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- battery2_max_charge_amperage = (((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 819.2);
- battery2_min_discharge_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
- battery2_max_discharge_amperage = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) - 819.2);
- break;
- case 0x2FF: //BMS [100ms] Status Heating High-Voltage Battery
- battery2_awake = true;
- battery2_actual_value_power_heating = (rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4);
- break;
- case 0x363: //BMS [1s] Identification High-Voltage Battery
- battery2_serial_number =
- (rx_frame.data.u8[3] << 24 | rx_frame.data.u8[2] << 16 | rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- break;
- case 0x3C2: //BMS (94AH exclusive) - Status diagnostics OBD 2 powertrain
- battery2_status_diagnosis_powertrain_maximum_multiplexer =
- ((rx_frame.data.u8[1] & 0x03) << 4 | rx_frame.data.u8[0] >> 4);
- battery2_status_diagnosis_powertrain_immediate_multiplexer = (rx_frame.data.u8[0] & 0xFC) >> 2;
- break;
- case 0x3EB: //BMS [1s] Status of charging high-voltage storage - 3
- battery2_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
- battery2_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
- battery2_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
- battery2_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
- break;
- case 0x40D: //BMS [1s] Charging status of high-voltage storage - 1
- battery2_BEV_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
- battery2_BEV_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
- battery2_BEV_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
- battery2_BEV_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
- break;
- case 0x41C: //BMS [1s] Operating Mode Status Of Hybrid - 2
- battery2_status_cooling_HV = (rx_frame.data.u8[1] & 0x03);
- break;
- case 0x426: // TODO: Figure out how to trigger sending of this. Does the SME require some CAN command?
- battery2_cellvoltage_mux = rx_frame.data.u8[0];
- if (battery2_cellvoltage_mux == 0) {
- datalayer.battery2.status.cell_voltages_mV[0] = ((rx_frame.data.u8[1] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[1] = ((rx_frame.data.u8[2] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[2] = ((rx_frame.data.u8[3] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[3] = ((rx_frame.data.u8[4] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[4] = ((rx_frame.data.u8[5] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[5] = ((rx_frame.data.u8[6] * 10) + 1800);
- datalayer.battery2.status.cell_voltages_mV[5] = ((rx_frame.data.u8[7] * 10) + 1800);
- }
- break;
- case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
- battery2_prediction_voltage_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
- battery2_prediction_voltage_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
- battery2_prediction_voltage_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
- battery2_prediction_voltage_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]);
- break;
- case 0x431: //BMS [200ms] Data High-Voltage Battery Unit
- battery2_status_service_disconnection_plug = (rx_frame.data.u8[0] & 0x0F);
- battery2_status_measurement_isolation = (rx_frame.data.u8[0] & 0x0C) >> 2;
- battery2_request_abort_charging = (rx_frame.data.u8[0] & 0x30) >> 4;
- battery2_prediction_duration_charging_minutes = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
- battery2_prediction_time_end_of_charging_minutes = rx_frame.data.u8[4];
- battery2_energy_content_maximum_kWh = (((rx_frame.data.u8[6] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
- break;
- case 0x432: //BMS [200ms] SOC% info
- battery2_request_operating_mode = (rx_frame.data.u8[0] & 0x03);
- battery2_target_voltage_in_CV_mode = ((rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4)) / 10;
- battery2_request_charging_condition_minimum = (rx_frame.data.u8[2] / 2);
- battery2_request_charging_condition_maximum = (rx_frame.data.u8[3] / 2);
- battery2_display_SOC = rx_frame.data.u8[4];
- break;
- case 0x507: //BMS [640ms] Network Management - 2 - This message is sent on the bus for sleep coordination purposes
- break;
- case 0x587: //BMS [5s] Services
- battery2_ID2 = rx_frame.data.u8[0];
- break;
- case 0x607: //BMS - responses to message requests on 0x615
- if (rx_frame.DLC > 6 && next_data == 0 && rx_frame.data.u8[0] == 0xf1) {
- uint8_t count2 = 6;
- while (count2 < rx_frame.DLC && next_data < 49) {
- message_data[next_data++] = rx_frame.data.u8[count2++];
- }
- transmit_can(&BMW_6F1_CONTINUE, can_config.battery_double);
-
- } else if (rx_frame.DLC > 3 && next_data > 0 && rx_frame.data.u8[0] == 0xf1 &&
- ((rx_frame.data.u8[1] & 0xF0) == 0x20)) {
- uint8_t count2 = 2;
- while (count2 < rx_frame.DLC && next_data < 49) {
- message_data[next_data++] = rx_frame.data.u8[count2++];
- }
-
- switch (cmdState) {
- case CELL_VOLTAGE_MINMAX:
- if (next_data >= 4) {
- datalayer.battery2.status.cell_min_voltage_mV = (message_data[0] << 8 | message_data[1]);
- datalayer.battery2.status.cell_max_voltage_mV = (message_data[2] << 8 | message_data[3]);
- }
- break;
- case SOH:
- if (next_data >= 4) {
- battery2_soh = message_data[3];
- battery2_info_available = true;
- }
- break;
- case SOC:
- if (next_data >= 6) {
- battery2_soc = (message_data[0] << 8 | message_data[1]);
- battery2_soc_hvmax = (message_data[2] << 8 | message_data[3]);
- battery2_soc_hvmin = (message_data[4] << 8 | message_data[5]);
- }
- break;
- }
- }
- break;
- default:
- break;
- }
- }
- void send_can_battery() {
- unsigned long currentMillis = millis();
-
- if (battery_awake) {
- //Send 20ms message
- if (currentMillis - previousMillis20 >= INTERVAL_20_MS) {
- // Check if sending of CAN messages has been delayed too much.
- if ((currentMillis - previousMillis20 >= INTERVAL_20_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) {
- set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis20));
- } else {
- clear_event(EVENT_CAN_OVERRUN);
- }
- previousMillis20 = currentMillis;
-
- if (startup_counter_contactor < 160) {
- startup_counter_contactor++;
- } else { //After 160 messages, turn on the request
- BMW_10B.data.u8[1] = 0x10; // Close contactors
- }
-
- BMW_10B.data.u8[1] = ((BMW_10B.data.u8[1] & 0xF0) + alive_counter_20ms);
- BMW_10B.data.u8[0] = calculateCRC(BMW_10B, 3, 0x3F);
-
- alive_counter_20ms = increment_alive_counter(alive_counter_20ms);
-
- BMW_13E_counter++;
- BMW_13E.data.u8[4] = BMW_13E_counter;
-
- if (datalayer.battery.status.bms_status == FAULT) {
- } //If battery is not in Fault mode, allow contactor to close by sending 10B
- else {
- transmit_can(&BMW_10B, can_config.battery);
- }
-
-#ifdef DOUBLE_BATTERY //If second battery is allowed to join in, also send 10B
- if (datalayer.system.status.battery2_allows_contactor_closing == true) {
- transmit_can(&BMW_10B, can_config.battery_double);
- }
-#endif
}
- // Send 100ms CAN Message
- if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
- previousMillis100 = currentMillis;
- BMW_12F.data.u8[1] = ((BMW_12F.data.u8[1] & 0xF0) + alive_counter_100ms);
- BMW_12F.data.u8[0] = calculateCRC(BMW_12F, 8, 0x60);
-
- alive_counter_100ms = increment_alive_counter(alive_counter_100ms);
-
- transmit_can(&BMW_12F, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_12F, can_config.battery_double);
-#endif
+ // Process the data since CRC check is either passed or skipped
+ battery_status_diagnostics_HV = (rx_frame.data.u8[2] & 0x0F);
+ break;
+ case 0x2F5: //BMS [100ms] High-Voltage Battery Charge/Discharge Limitations
+ battery_max_charge_voltage = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ battery_max_charge_amperage = (((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 819.2);
+ battery_min_discharge_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
+ battery_max_discharge_amperage = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) - 819.2);
+ break;
+ case 0x2FF: //BMS [100ms] Status Heating High-Voltage Battery
+ battery_awake = true;
+ battery_actual_value_power_heating = (rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4);
+ break;
+ case 0x363: //BMS [1s] Identification High-Voltage Battery
+ battery_serial_number =
+ (rx_frame.data.u8[3] << 24 | rx_frame.data.u8[2] << 16 | rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ break;
+ case 0x3C2: //BMS (94AH exclusive) - Status diagnostics OBD 2 powertrain
+ battery_status_diagnosis_powertrain_maximum_multiplexer =
+ ((rx_frame.data.u8[1] & 0x03) << 4 | rx_frame.data.u8[0] >> 4);
+ battery_status_diagnosis_powertrain_immediate_multiplexer = (rx_frame.data.u8[0] & 0xFC) >> 2;
+ break;
+ case 0x3EB: //BMS [1s] Status of charging high-voltage storage - 3
+ battery_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
+ battery_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
+ battery_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
+ battery_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
+ break;
+ case 0x40D: //BMS [1s] Charging status of high-voltage storage - 1
+ battery_BEV_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
+ battery_BEV_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
+ battery_BEV_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
+ battery_BEV_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
+ break;
+ case 0x41C: //BMS [1s] Operating Mode Status Of Hybrid - 2
+ battery_status_cooling_HV = (rx_frame.data.u8[1] & 0x03);
+ break;
+ case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
+ battery_prediction_voltage_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ battery_prediction_voltage_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
+ battery_prediction_voltage_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
+ battery_prediction_voltage_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]);
+ break;
+ case 0x431: //BMS [200ms] Data High-Voltage Battery Unit
+ battery_status_service_disconnection_plug = (rx_frame.data.u8[0] & 0x0F);
+ battery_status_measurement_isolation = (rx_frame.data.u8[0] & 0x0C) >> 2;
+ battery_request_abort_charging = (rx_frame.data.u8[0] & 0x30) >> 4;
+ battery_prediction_duration_charging_minutes = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
+ battery_prediction_time_end_of_charging_minutes = rx_frame.data.u8[4];
+ battery_energy_content_maximum_kWh = (((rx_frame.data.u8[6] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
+ if (battery_energy_content_maximum_kWh > 33) {
+ detectedBattery = BATTERY_120AH;
+ } else if (battery_energy_content_maximum_kWh > 20) {
+ detectedBattery = BATTERY_94AH;
+ } else {
+ detectedBattery = BATTERY_60AH;
}
- // Send 200ms CAN Message
- if (currentMillis - previousMillis200 >= INTERVAL_200_MS) {
- previousMillis200 = currentMillis;
-
- BMW_19B.data.u8[1] = ((BMW_19B.data.u8[1] & 0xF0) + alive_counter_200ms);
- BMW_19B.data.u8[0] = calculateCRC(BMW_19B, 8, 0x6C);
-
- alive_counter_200ms = increment_alive_counter(alive_counter_200ms);
-
- transmit_can(&BMW_19B, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_19B, can_config.battery_double);
-#endif
+ break;
+ case 0x432: //BMS [200ms] SOC% info
+ battery_request_operating_mode = (rx_frame.data.u8[0] & 0x03);
+ battery_target_voltage_in_CV_mode = ((rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4)) / 10;
+ battery_request_charging_condition_minimum = (rx_frame.data.u8[2] / 2);
+ battery_request_charging_condition_maximum = (rx_frame.data.u8[3] / 2);
+ battery_display_SOC = rx_frame.data.u8[4];
+ break;
+ case 0x507: //BMS [640ms] Network Management - 2 - This message is sent on the bus for sleep coordination purposes
+ break;
+ case 0x587: //BMS [5s] Services
+ 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 (rx_frame.DLC == 6) {
+ transmit_can(&BMW_6F4_CELL_CONTINUE, can_config.battery); // 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] =
+ (rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]);
+ }
}
- // Send 500ms CAN Message
- if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
- previousMillis500 = currentMillis;
- BMW_30B.data.u8[1] = ((BMW_30B.data.u8[1] & 0xF0) + alive_counter_500ms);
- BMW_30B.data.u8[0] = calculateCRC(BMW_30B, 8, 0xBE);
+ if (rx_frame.DLC > 6 && next_data == 0 && rx_frame.data.u8[0] == 0xf1) {
+ uint8_t count = 6;
+ while (count < rx_frame.DLC && next_data < 49) {
+ message_data[next_data++] = rx_frame.data.u8[count++];
+ }
+ transmit_can(&BMW_6F1_CONTINUE, can_config.battery); // tell battery to send additional messages
- alive_counter_500ms = increment_alive_counter(alive_counter_500ms);
+ } else if (rx_frame.DLC > 3 && next_data > 0 && rx_frame.data.u8[0] == 0xf1 &&
+ ((rx_frame.data.u8[1] & 0xF0) == 0x20)) {
+ uint8_t count = 2;
+ while (count < rx_frame.DLC && next_data < 49) {
+ message_data[next_data++] = rx_frame.data.u8[count++];
+ }
- transmit_can(&BMW_30B, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_30B, can_config.battery_double);
-#endif
- }
- // Send 640ms CAN Message
- if (currentMillis - previousMillis640 >= INTERVAL_640_MS) {
- previousMillis640 = currentMillis;
-
- transmit_can(&BMW_512, can_config.battery); // Keep BMS alive
- transmit_can(&BMW_5F8, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_512, can_config.battery_double);
- transmit_can(&BMW_5F8, can_config.battery_double);
-#endif
- }
- // Send 1000ms CAN Message
- if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
- previousMillis1000 = currentMillis;
-
- BMW_328_counter++; // Used to increment seconds
- BMW_328.data.u8[0] = BMW_328_counter;
- BMW_328.data.u8[1] = BMW_328_counter << 8;
- BMW_328.data.u8[2] = BMW_328_counter << 16;
- BMW_328.data.u8[3] = BMW_328_counter << 24;
-
- BMW_1D0.data.u8[1] = ((BMW_1D0.data.u8[1] & 0xF0) + alive_counter_1000ms);
- BMW_1D0.data.u8[0] = calculateCRC(BMW_1D0, 8, 0xF9);
-
- BMW_3F9.data.u8[1] = ((BMW_3F9.data.u8[1] & 0xF0) + alive_counter_1000ms);
- BMW_3F9.data.u8[0] = calculateCRC(BMW_3F9, 8, 0x38);
-
- BMW_3EC.data.u8[1] = ((BMW_3EC.data.u8[1] & 0xF0) + alive_counter_1000ms);
- BMW_3EC.data.u8[0] = calculateCRC(BMW_3EC, 8, 0x53);
-
- BMW_3A7.data.u8[1] = ((BMW_3A7.data.u8[1] & 0xF0) + alive_counter_1000ms);
- BMW_3A7.data.u8[0] = calculateCRC(BMW_3A7, 8, 0x05);
-
- alive_counter_1000ms = increment_alive_counter(alive_counter_1000ms);
-
- transmit_can(&BMW_3E8, can_config.battery); //Order comes from CAN logs
- transmit_can(&BMW_328, can_config.battery);
- transmit_can(&BMW_3F9, can_config.battery);
- transmit_can(&BMW_2E2, can_config.battery);
- transmit_can(&BMW_41D, can_config.battery);
- transmit_can(&BMW_3D0, can_config.battery);
- transmit_can(&BMW_3CA, can_config.battery);
- transmit_can(&BMW_3A7, can_config.battery);
- transmit_can(&BMW_2CA, can_config.battery);
- transmit_can(&BMW_3FB, can_config.battery);
- transmit_can(&BMW_418, can_config.battery);
- transmit_can(&BMW_1D0, can_config.battery);
- transmit_can(&BMW_3EC, can_config.battery);
- transmit_can(&BMW_192, can_config.battery);
- transmit_can(&BMW_13E, can_config.battery);
- transmit_can(&BMW_433, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_3E8, can_config.battery_double);
- transmit_can(&BMW_328, can_config.battery_double);
- transmit_can(&BMW_3F9, can_config.battery_double);
- transmit_can(&BMW_2E2, can_config.battery_double);
- transmit_can(&BMW_41D, can_config.battery_double);
- transmit_can(&BMW_3D0, can_config.battery_double);
- transmit_can(&BMW_3CA, can_config.battery_double);
- transmit_can(&BMW_3A7, can_config.battery_double);
- transmit_can(&BMW_2CA, can_config.battery_double);
- transmit_can(&BMW_3FB, can_config.battery_double);
- transmit_can(&BMW_418, can_config.battery_double);
- transmit_can(&BMW_1D0, can_config.battery_double);
- transmit_can(&BMW_3EC, can_config.battery_double);
- transmit_can(&BMW_192, can_config.battery_double);
- transmit_can(&BMW_13E, can_config.battery_double);
- transmit_can(&BMW_433, can_config.battery_double);
-#endif
-
- 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
-
- next_data = 0;
switch (cmdState) {
- case SOC:
- transmit_can(&BMW_6F1_CELL, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_6F1_CELL, can_config.battery_double);
-#endif
- cmdState = CELL_VOLTAGE_MINMAX;
- break;
case CELL_VOLTAGE_MINMAX:
- transmit_can(&BMW_6F1_SOH, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_6F1_SOH, can_config.battery_double);
-#endif
- cmdState = SOH;
+ if (next_data >= 4) {
+ datalayer.battery.status.cell_min_voltage_mV = (message_data[0] << 8 | message_data[1]);
+ datalayer.battery.status.cell_max_voltage_mV = (message_data[2] << 8 | message_data[3]);
+ }
break;
case SOH:
- transmit_can(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery_double);
-#endif
- cmdState = CELL_VOLTAGE_CELLNO;
- current_cell_polled = 0;
-
- break;
- case CELL_VOLTAGE_CELLNO:
- current_cell_polled++;
- if (current_cell_polled > 96) {
- datalayer.battery.info.number_of_cells = 97;
- cmdState = CELL_VOLTAGE_CELLNO_LAST;
- } else {
- cmdState = CELL_VOLTAGE_CELLNO;
-
- BMW_6F4_CELL_VOLTAGE_CELLNO.data.u8[6] = current_cell_polled;
- transmit_can(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery_double);
-#endif
+ if (next_data >= 4) {
+ battery_soh = message_data[3];
+ battery_info_available = true;
}
break;
- case CELL_VOLTAGE_CELLNO_LAST:
- transmit_can(&BMW_6F1_SOC, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_6F1_SOC, can_config.battery_double);
-#endif
- cmdState = SOC;
+ case SOC:
+ if (next_data >= 6) {
+ battery_soc = (message_data[0] << 8 | message_data[1]);
+ battery_soc_hvmax = (message_data[2] << 8 | message_data[3]);
+ battery_soc_hvmin = (message_data[4] << 8 | message_data[5]);
+ }
break;
}
}
- // Send 5000ms CAN Message
- if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
- previousMillis5000 = currentMillis;
+ break;
+ default:
+ break;
+ }
+}
+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;
+ datalayer.battery2.status.CAN_battery_still_alive =
+ CAN_STILL_ALIVE; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
+ battery2_current = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) - 8192; //deciAmps (-819.2 to 819.0A)
+ battery2_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
+ datalayer.battery2.status.voltage_dV =
+ battery2_volts; // Update the datalayer as soon as possible with this info, needed for contactor control
+ battery2_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
+ battery2_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
+ battery2_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
+ battery2_request_open_contactors_fast = (rx_frame.data.u8[6] & 0x0C) >> 2;
+ battery2_charging_condition_delta = (rx_frame.data.u8[6] & 0xF0) >> 4;
+ battery2_DC_link_voltage = rx_frame.data.u8[7];
+ break;
+ case 0x1FA: //BMS [1000ms] Status Of High-Voltage Battery - 1
+ battery2_status_error_isolation_external_Bordnetz = (rx_frame.data.u8[0] & 0x03);
+ battery2_status_error_isolation_internal_Bordnetz = (rx_frame.data.u8[0] & 0x0C) >> 2;
+ battery2_request_cooling = (rx_frame.data.u8[0] & 0x30) >> 4;
+ battery2_status_valve_cooling = (rx_frame.data.u8[0] & 0xC0) >> 6;
+ battery2_status_error_locking = (rx_frame.data.u8[1] & 0x03);
+ battery2_status_precharge_locked = (rx_frame.data.u8[1] & 0x0C) >> 2;
+ battery2_status_disconnecting_switch = (rx_frame.data.u8[1] & 0x30) >> 4;
+ battery2_status_emergency_mode = (rx_frame.data.u8[1] & 0xC0) >> 6;
+ battery2_request_service = (rx_frame.data.u8[2] & 0x03);
+ battery2_error_emergency_mode = (rx_frame.data.u8[2] & 0x0C) >> 2;
+ battery2_status_error_disconnecting_switch = (rx_frame.data.u8[2] & 0x30) >> 4;
+ battery2_status_warning_isolation = (rx_frame.data.u8[2] & 0xC0) >> 6;
+ battery2_status_cold_shutoff_valve = (rx_frame.data.u8[3] & 0x0F);
+ battery2_temperature_HV = (rx_frame.data.u8[4] - 50);
+ battery2_temperature_heat_exchanger = (rx_frame.data.u8[5] - 50);
+ battery2_temperature_min = (rx_frame.data.u8[6] - 50);
+ battery2_temperature_max = (rx_frame.data.u8[7] - 50);
+ break;
+ case 0x239: //BMS [200ms]
+ battery2_predicted_energy_charge_condition = (rx_frame.data.u8[2] << 8 | rx_frame.data.u8[1]); //Wh
+ battery2_predicted_energy_charging_target = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[3]) * 0.02); //kWh
+ break;
+ case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
+ battery2_awake = true;
+ if (!skipCRCCheck_battery2) {
+ if (calculateCRC(rx_frame, rx_frame.DLC, 0x15) != rx_frame.data.u8[0]) {
+ // If calculated CRC does not match transmitted CRC, increase CANerror counter
+ datalayer.battery2.status.CAN_error_counter++;
- 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(&BMW_3FC, can_config.battery); //Order comes from CAN logs
- transmit_can(&BMW_3C5, can_config.battery);
- transmit_can(&BMW_3A0, can_config.battery);
- transmit_can(&BMW_592_0, can_config.battery);
- transmit_can(&BMW_592_1, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_3FC, can_config.battery_double);
- transmit_can(&BMW_3C5, can_config.battery_double);
- transmit_can(&BMW_3A0, can_config.battery_double);
- transmit_can(&BMW_592_0, can_config.battery_double);
- transmit_can(&BMW_592_1, can_config.battery_double);
-#endif
-
- alive_counter_5000ms = increment_alive_counter(alive_counter_5000ms);
-
- if (BMW_380_counter < 3) {
- transmit_can(&BMW_380, can_config.battery); // This message stops after 3 times on startup
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_380, can_config.battery_double);
-#endif
- BMW_380_counter++;
+ // If the CRC check has never passed before, set the flag to skip future checks. Some SMEs have differing CRC checks.
+ if (!CRCCheckPassedPreviously_battery2) {
+ skipCRCCheck_battery2 = true;
+ }
+ break;
+ } else {
+ // If CRC check passes, update the flag
+ CRCCheckPassedPreviously_battery2 = true;
}
}
- // Send 10000ms CAN Message
- if (currentMillis - previousMillis10000 >= INTERVAL_10_S) {
- previousMillis10000 = currentMillis;
- transmit_can(&BMW_3E5, can_config.battery); //Order comes from CAN logs
- transmit_can(&BMW_3E4, can_config.battery);
- transmit_can(&BMW_37B, can_config.battery);
-#ifdef DOUBLE_BATTERY
- transmit_can(&BMW_3E5, can_config.battery_double);
- transmit_can(&BMW_3E4, can_config.battery_double);
- transmit_can(&BMW_37B, can_config.battery_double);
-#endif
-
- BMW_3E5.data.u8[0] = 0xFD; // First 3E5 message byte0 we send is unique, once we sent initial value send this
+ // Process the data since CRC check is either passed or skipped
+ battery2_status_diagnostics_HV = (rx_frame.data.u8[2] & 0x0F);
+ break;
+ case 0x2F5: //BMS [100ms] High-Voltage Battery Charge/Discharge Limitations
+ battery2_max_charge_voltage = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ battery2_max_charge_amperage = (((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 819.2);
+ battery2_min_discharge_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
+ battery2_max_discharge_amperage = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) - 819.2);
+ break;
+ case 0x2FF: //BMS [100ms] Status Heating High-Voltage Battery
+ battery2_awake = true;
+ battery2_actual_value_power_heating = (rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4);
+ break;
+ case 0x363: //BMS [1s] Identification High-Voltage Battery
+ battery2_serial_number =
+ (rx_frame.data.u8[3] << 24 | rx_frame.data.u8[2] << 16 | rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ break;
+ case 0x3C2: //BMS (94AH exclusive) - Status diagnostics OBD 2 powertrain
+ battery2_status_diagnosis_powertrain_maximum_multiplexer =
+ ((rx_frame.data.u8[1] & 0x03) << 4 | rx_frame.data.u8[0] >> 4);
+ battery2_status_diagnosis_powertrain_immediate_multiplexer = (rx_frame.data.u8[0] & 0xFC) >> 2;
+ break;
+ case 0x3EB: //BMS [1s] Status of charging high-voltage storage - 3
+ battery2_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
+ battery2_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
+ battery2_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
+ battery2_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
+ break;
+ case 0x40D: //BMS [1s] Charging status of high-voltage storage - 1
+ battery2_BEV_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
+ battery2_BEV_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
+ battery2_BEV_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
+ battery2_BEV_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
+ break;
+ case 0x41C: //BMS [1s] Operating Mode Status Of Hybrid - 2
+ battery2_status_cooling_HV = (rx_frame.data.u8[1] & 0x03);
+ break;
+ case 0x426: // TODO: Figure out how to trigger sending of this. Does the SME require some CAN command?
+ battery2_cellvoltage_mux = rx_frame.data.u8[0];
+ if (battery2_cellvoltage_mux == 0) {
+ datalayer.battery2.status.cell_voltages_mV[0] = ((rx_frame.data.u8[1] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[1] = ((rx_frame.data.u8[2] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[2] = ((rx_frame.data.u8[3] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[3] = ((rx_frame.data.u8[4] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[4] = ((rx_frame.data.u8[5] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[5] = ((rx_frame.data.u8[6] * 10) + 1800);
+ datalayer.battery2.status.cell_voltages_mV[5] = ((rx_frame.data.u8[7] * 10) + 1800);
}
- } else {
- previousMillis20 = currentMillis;
- previousMillis100 = currentMillis;
- previousMillis200 = currentMillis;
- previousMillis500 = currentMillis;
- previousMillis640 = currentMillis;
- previousMillis1000 = currentMillis;
- previousMillis5000 = currentMillis;
- previousMillis10000 = currentMillis;
- }
- }
+ break;
+ case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
+ battery2_prediction_voltage_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
+ battery2_prediction_voltage_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
+ battery2_prediction_voltage_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
+ battery2_prediction_voltage_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]);
+ break;
+ case 0x431: //BMS [200ms] Data High-Voltage Battery Unit
+ battery2_status_service_disconnection_plug = (rx_frame.data.u8[0] & 0x0F);
+ battery2_status_measurement_isolation = (rx_frame.data.u8[0] & 0x0C) >> 2;
+ battery2_request_abort_charging = (rx_frame.data.u8[0] & 0x30) >> 4;
+ battery2_prediction_duration_charging_minutes = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
+ battery2_prediction_time_end_of_charging_minutes = rx_frame.data.u8[4];
+ battery2_energy_content_maximum_kWh = (((rx_frame.data.u8[6] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
+ break;
+ case 0x432: //BMS [200ms] SOC% info
+ battery2_request_operating_mode = (rx_frame.data.u8[0] & 0x03);
+ battery2_target_voltage_in_CV_mode = ((rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4)) / 10;
+ battery2_request_charging_condition_minimum = (rx_frame.data.u8[2] / 2);
+ battery2_request_charging_condition_maximum = (rx_frame.data.u8[3] / 2);
+ battery2_display_SOC = rx_frame.data.u8[4];
+ break;
+ case 0x507: //BMS [640ms] Network Management - 2 - This message is sent on the bus for sleep coordination purposes
+ break;
+ case 0x587: //BMS [5s] Services
+ battery2_ID2 = rx_frame.data.u8[0];
+ break;
+ case 0x607: //BMS - responses to message requests on 0x615
+ if (rx_frame.DLC > 6 && next_data == 0 && rx_frame.data.u8[0] == 0xf1) {
+ uint8_t count2 = 6;
+ while (count2 < rx_frame.DLC && next_data < 49) {
+ message_data[next_data++] = rx_frame.data.u8[count2++];
+ }
+ transmit_can(&BMW_6F1_CONTINUE, can_config.battery_double);
- void setup_battery(void) { // Performs one time setup at startup
-#ifdef DEBUG_VIA_USB
- Serial.println("BMW i3 battery selected");
+ } else if (rx_frame.DLC > 3 && next_data > 0 && rx_frame.data.u8[0] == 0xf1 &&
+ ((rx_frame.data.u8[1] & 0xF0) == 0x20)) {
+ uint8_t count2 = 2;
+ while (count2 < rx_frame.DLC && next_data < 49) {
+ message_data[next_data++] = rx_frame.data.u8[count2++];
+ }
+
+ switch (cmdState) {
+ case CELL_VOLTAGE_MINMAX:
+ if (next_data >= 4) {
+ datalayer.battery2.status.cell_min_voltage_mV = (message_data[0] << 8 | message_data[1]);
+ datalayer.battery2.status.cell_max_voltage_mV = (message_data[2] << 8 | message_data[3]);
+ }
+ break;
+ case SOH:
+ if (next_data >= 4) {
+ battery2_soh = message_data[3];
+ battery2_info_available = true;
+ }
+ break;
+ case SOC:
+ if (next_data >= 6) {
+ battery2_soc = (message_data[0] << 8 | message_data[1]);
+ battery2_soc_hvmax = (message_data[2] << 8 | message_data[3]);
+ battery2_soc_hvmin = (message_data[4] << 8 | message_data[5]);
+ }
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+void send_can_battery() {
+ unsigned long currentMillis = millis();
+
+ if (battery_awake) {
+ //Send 20ms message
+ if (currentMillis - previousMillis20 >= INTERVAL_20_MS) {
+ // Check if sending of CAN messages has been delayed too much.
+ if ((currentMillis - previousMillis20 >= INTERVAL_20_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) {
+ set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis20));
+ } else {
+ clear_event(EVENT_CAN_OVERRUN);
+ }
+ previousMillis20 = currentMillis;
+
+ if (startup_counter_contactor < 160) {
+ startup_counter_contactor++;
+ } else { //After 160 messages, turn on the request
+ BMW_10B.data.u8[1] = 0x10; // Close contactors
+ }
+
+ BMW_10B.data.u8[1] = ((BMW_10B.data.u8[1] & 0xF0) + alive_counter_20ms);
+ BMW_10B.data.u8[0] = calculateCRC(BMW_10B, 3, 0x3F);
+
+ alive_counter_20ms = increment_alive_counter(alive_counter_20ms);
+
+ BMW_13E_counter++;
+ BMW_13E.data.u8[4] = BMW_13E_counter;
+
+ if (datalayer.battery.status.bms_status == FAULT) {
+ } //If battery is not in Fault mode, allow contactor to close by sending 10B
+ else {
+ transmit_can(&BMW_10B, can_config.battery);
+ }
+
+#ifdef DOUBLE_BATTERY //If second battery is allowed to join in, also send 10B
+ if (datalayer.system.status.battery2_allows_contactor_closing == true) {
+ transmit_can(&BMW_10B, can_config.battery_double);
+ }
+#endif
+ }
+ // Send 100ms CAN Message
+ if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
+ previousMillis100 = currentMillis;
+
+ BMW_12F.data.u8[1] = ((BMW_12F.data.u8[1] & 0xF0) + alive_counter_100ms);
+ BMW_12F.data.u8[0] = calculateCRC(BMW_12F, 8, 0x60);
+
+ alive_counter_100ms = increment_alive_counter(alive_counter_100ms);
+
+ transmit_can(&BMW_12F, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_12F, can_config.battery_double);
+#endif
+ }
+ // Send 200ms CAN Message
+ if (currentMillis - previousMillis200 >= INTERVAL_200_MS) {
+ previousMillis200 = currentMillis;
+
+ BMW_19B.data.u8[1] = ((BMW_19B.data.u8[1] & 0xF0) + alive_counter_200ms);
+ BMW_19B.data.u8[0] = calculateCRC(BMW_19B, 8, 0x6C);
+
+ alive_counter_200ms = increment_alive_counter(alive_counter_200ms);
+
+ transmit_can(&BMW_19B, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_19B, can_config.battery_double);
+#endif
+ }
+ // Send 500ms CAN Message
+ if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
+ previousMillis500 = currentMillis;
+
+ BMW_30B.data.u8[1] = ((BMW_30B.data.u8[1] & 0xF0) + alive_counter_500ms);
+ BMW_30B.data.u8[0] = calculateCRC(BMW_30B, 8, 0xBE);
+
+ alive_counter_500ms = increment_alive_counter(alive_counter_500ms);
+
+ transmit_can(&BMW_30B, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_30B, can_config.battery_double);
+#endif
+ }
+ // Send 640ms CAN Message
+ if (currentMillis - previousMillis640 >= INTERVAL_640_MS) {
+ previousMillis640 = currentMillis;
+
+ transmit_can(&BMW_512, can_config.battery); // Keep BMS alive
+ transmit_can(&BMW_5F8, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_512, can_config.battery_double);
+ transmit_can(&BMW_5F8, can_config.battery_double);
+#endif
+ }
+ // Send 1000ms CAN Message
+ if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
+ previousMillis1000 = currentMillis;
+
+ BMW_328_counter++; // Used to increment seconds
+ BMW_328.data.u8[0] = BMW_328_counter;
+ BMW_328.data.u8[1] = BMW_328_counter << 8;
+ BMW_328.data.u8[2] = BMW_328_counter << 16;
+ BMW_328.data.u8[3] = BMW_328_counter << 24;
+
+ BMW_1D0.data.u8[1] = ((BMW_1D0.data.u8[1] & 0xF0) + alive_counter_1000ms);
+ BMW_1D0.data.u8[0] = calculateCRC(BMW_1D0, 8, 0xF9);
+
+ BMW_3F9.data.u8[1] = ((BMW_3F9.data.u8[1] & 0xF0) + alive_counter_1000ms);
+ BMW_3F9.data.u8[0] = calculateCRC(BMW_3F9, 8, 0x38);
+
+ BMW_3EC.data.u8[1] = ((BMW_3EC.data.u8[1] & 0xF0) + alive_counter_1000ms);
+ BMW_3EC.data.u8[0] = calculateCRC(BMW_3EC, 8, 0x53);
+
+ BMW_3A7.data.u8[1] = ((BMW_3A7.data.u8[1] & 0xF0) + alive_counter_1000ms);
+ BMW_3A7.data.u8[0] = calculateCRC(BMW_3A7, 8, 0x05);
+
+ alive_counter_1000ms = increment_alive_counter(alive_counter_1000ms);
+
+ transmit_can(&BMW_3E8, can_config.battery); //Order comes from CAN logs
+ transmit_can(&BMW_328, can_config.battery);
+ transmit_can(&BMW_3F9, can_config.battery);
+ transmit_can(&BMW_2E2, can_config.battery);
+ transmit_can(&BMW_41D, can_config.battery);
+ transmit_can(&BMW_3D0, can_config.battery);
+ transmit_can(&BMW_3CA, can_config.battery);
+ transmit_can(&BMW_3A7, can_config.battery);
+ transmit_can(&BMW_2CA, can_config.battery);
+ transmit_can(&BMW_3FB, can_config.battery);
+ transmit_can(&BMW_418, can_config.battery);
+ transmit_can(&BMW_1D0, can_config.battery);
+ transmit_can(&BMW_3EC, can_config.battery);
+ transmit_can(&BMW_192, can_config.battery);
+ transmit_can(&BMW_13E, can_config.battery);
+ transmit_can(&BMW_433, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_3E8, can_config.battery_double);
+ transmit_can(&BMW_328, can_config.battery_double);
+ transmit_can(&BMW_3F9, can_config.battery_double);
+ transmit_can(&BMW_2E2, can_config.battery_double);
+ transmit_can(&BMW_41D, can_config.battery_double);
+ transmit_can(&BMW_3D0, can_config.battery_double);
+ transmit_can(&BMW_3CA, can_config.battery_double);
+ transmit_can(&BMW_3A7, can_config.battery_double);
+ transmit_can(&BMW_2CA, can_config.battery_double);
+ transmit_can(&BMW_3FB, can_config.battery_double);
+ transmit_can(&BMW_418, can_config.battery_double);
+ transmit_can(&BMW_1D0, can_config.battery_double);
+ transmit_can(&BMW_3EC, can_config.battery_double);
+ transmit_can(&BMW_192, can_config.battery_double);
+ transmit_can(&BMW_13E, can_config.battery_double);
+ transmit_can(&BMW_433, can_config.battery_double);
#endif
- //Before we have started up and detected which battery is in use, use 60AH values
- datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
- datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
+ 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
- datalayer.system.status.battery_allows_contactor_closing = true;
+ next_data = 0;
+ switch (cmdState) {
+ case SOC:
+ transmit_can(&BMW_6F1_CELL, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_6F1_CELL, can_config.battery_double);
+#endif
+ cmdState = CELL_VOLTAGE_MINMAX;
+ break;
+ case CELL_VOLTAGE_MINMAX:
+ transmit_can(&BMW_6F1_SOH, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_6F1_SOH, can_config.battery_double);
+#endif
+ cmdState = SOH;
+ break;
+ case SOH:
+ transmit_can(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery_double);
+#endif
+ cmdState = CELL_VOLTAGE_CELLNO;
+ current_cell_polled = 0;
+
+ break;
+ case CELL_VOLTAGE_CELLNO:
+ current_cell_polled++;
+ if (current_cell_polled > 96) {
+ datalayer.battery.info.number_of_cells = 97;
+ cmdState = CELL_VOLTAGE_CELLNO_LAST;
+ } else {
+ cmdState = CELL_VOLTAGE_CELLNO;
+
+ BMW_6F4_CELL_VOLTAGE_CELLNO.data.u8[6] = current_cell_polled;
+ transmit_can(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery_double);
+#endif
+ }
+ break;
+ case CELL_VOLTAGE_CELLNO_LAST:
+ transmit_can(&BMW_6F1_SOC, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_6F1_SOC, can_config.battery_double);
+#endif
+ cmdState = SOC;
+ break;
+ }
+ }
+ // Send 5000ms CAN Message
+ if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
+ previousMillis5000 = 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(&BMW_3FC, can_config.battery); //Order comes from CAN logs
+ transmit_can(&BMW_3C5, can_config.battery);
+ transmit_can(&BMW_3A0, can_config.battery);
+ transmit_can(&BMW_592_0, can_config.battery);
+ transmit_can(&BMW_592_1, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_3FC, can_config.battery_double);
+ transmit_can(&BMW_3C5, can_config.battery_double);
+ transmit_can(&BMW_3A0, can_config.battery_double);
+ transmit_can(&BMW_592_0, can_config.battery_double);
+ transmit_can(&BMW_592_1, can_config.battery_double);
+#endif
+
+ alive_counter_5000ms = increment_alive_counter(alive_counter_5000ms);
+
+ if (BMW_380_counter < 3) {
+ transmit_can(&BMW_380, can_config.battery); // This message stops after 3 times on startup
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_380, can_config.battery_double);
+#endif
+ BMW_380_counter++;
+ }
+ }
+ // Send 10000ms CAN Message
+ if (currentMillis - previousMillis10000 >= INTERVAL_10_S) {
+ previousMillis10000 = currentMillis;
+
+ transmit_can(&BMW_3E5, can_config.battery); //Order comes from CAN logs
+ transmit_can(&BMW_3E4, can_config.battery);
+ transmit_can(&BMW_37B, can_config.battery);
+#ifdef DOUBLE_BATTERY
+ transmit_can(&BMW_3E5, can_config.battery_double);
+ transmit_can(&BMW_3E4, can_config.battery_double);
+ transmit_can(&BMW_37B, can_config.battery_double);
+#endif
+
+ BMW_3E5.data.u8[0] = 0xFD; // First 3E5 message byte0 we send is unique, once we sent initial value send this
+ }
+ } else {
+ previousMillis20 = currentMillis;
+ previousMillis100 = currentMillis;
+ previousMillis200 = currentMillis;
+ previousMillis500 = currentMillis;
+ previousMillis640 = currentMillis;
+ previousMillis1000 = currentMillis;
+ previousMillis5000 = currentMillis;
+ previousMillis10000 = currentMillis;
+ }
+}
+
+void setup_battery(void) { // Performs one time setup at startup
+#ifdef DEBUG_VIA_USB
+ Serial.println("BMW i3 battery selected");
+#endif
+
+ //Before we have started up and detected which battery is in use, use 60AH values
+ datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
+ datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
+
+ datalayer.system.status.battery_allows_contactor_closing = true;
#ifdef DOUBLE_BATTERY
- Serial.println("Another BMW i3 battery also selected!");
- datalayer.battery2.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV;
- datalayer.battery2.info.min_design_voltage_dV = datalayer.battery.info.min_design_voltage_dV;
- datalayer.battery2.status.voltage_dV =
- 0; //Init voltage to 0 to allow contactor check to operate without fear of default values colliding
+ Serial.println("Another BMW i3 battery also selected!");
+ datalayer.battery2.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV;
+ datalayer.battery2.info.min_design_voltage_dV = datalayer.battery.info.min_design_voltage_dV;
+ datalayer.battery2.status.voltage_dV =
+ 0; //Init voltage to 0 to allow contactor check to operate without fear of default values colliding
#endif
- pinMode(WUP_PIN, OUTPUT);
- digitalWrite(WUP_PIN, HIGH); // Wake up the battery
- }
+ pinMode(WUP_PIN, OUTPUT);
+ digitalWrite(WUP_PIN, HIGH); // Wake up the battery
+}
#endif
From 9d2a3be708b72f9b9ac451e2da869da3aa258854 Mon Sep 17 00:00:00 2001
From: amarofarinha <151563493+amarofarinha@users.noreply.github.com>
Date: Fri, 13 Sep 2024 22:22:57 +0100
Subject: [PATCH 5/9] Refactor: Code cleanup and optimization
---
Software/src/battery/BMW-I3-BATTERY.cpp | 10 ++--------
Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 5 +----
Software/src/battery/CHADEMO-BATTERY.cpp | 5 +----
Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 5 +----
Software/src/battery/JAGUAR-IPACE-BATTERY.cpp | 5 +----
Software/src/battery/KIA-E-GMP-BATTERY.cpp | 5 +----
Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 5 +----
Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp | 5 +----
Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 10 ++--------
Software/src/battery/PYLON-BATTERY.cpp | 5 +----
Software/src/battery/RENAULT-KANGOO-BATTERY.cpp | 5 +----
Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp | 5 +----
Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp | 5 +----
Software/src/battery/TESLA-BATTERY.cpp | 5 +----
Software/src/battery/TEST-FAKE-BATTERY.cpp | 5 +----
Software/src/battery/VOLVO-SPA-BATTERY.cpp | 5 +----
Software/src/devboard/mqtt/mqtt.cpp | 2 +-
Software/src/devboard/safety/safety.cpp | 4 ++--
Software/src/devboard/webserver/webserver.cpp | 10 ++++++----
19 files changed, 27 insertions(+), 79 deletions(-)
diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp
index beaaaafe..574a5948 100644
--- a/Software/src/battery/BMW-I3-BATTERY.cpp
+++ b/Software/src/battery/BMW-I3-BATTERY.cpp
@@ -376,10 +376,7 @@ 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 (!emulator_pause_request_ON) {
if (battery2_BEV_available_power_longterm_discharge > 65000) {
datalayer.battery2.status.max_discharge_power_W = 65000;
} else {
@@ -421,10 +418,7 @@ 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 {
+ if (!emulator_pause_request_ON) {
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;
}
diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
index 4aba2657..f7f93489 100644
--- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
+++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp
@@ -101,10 +101,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(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 {
+ if (!emulator_pause_request_ON) {
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
}
diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp
index ff9105fb..7e4a4c26 100644
--- a/Software/src/battery/CHADEMO-BATTERY.cpp
+++ b/Software/src/battery/CHADEMO-BATTERY.cpp
@@ -114,10 +114,7 @@ void update_values_battery() {
datalayer.battery.status.real_soc = x102_chg_session.StateOfCharge;
- if (emulator_pause_request_ON) {
- datalayer.battery.status.max_discharge_power_W = 0;
- datalayer.battery.status.max_charge_power_W = 0;
- } else {
+ if (!emulator_pause_request_ON) {
datalayer.battery.status.max_discharge_power_W = 10000; //TODO: Map from CAN later on
datalayer.battery.status.max_charge_power_W = 1000;
}
diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
index 66136c74..8bba7d8d 100644
--- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
+++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
@@ -48,10 +48,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(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 {
+ if (!emulator_pause_request_ON) {
//We do not know the max charge/discharge power is sent by the battery. We hardcode value for now.
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
diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
index 720864c2..d24a8f3f 100644
--- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
+++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
@@ -89,10 +89,7 @@ void update_values_battery() {
datalayer.battery.status.temperature_max_dC = HVBattCellTempHottest * 10; // C to dC
- if (emulator_pause_request_ON) {
- datalayer.battery.status.max_discharge_power_W = 0;
- datalayer.battery.status.max_charge_power_W = 0;
- } else {
+ if (!emulator_pause_request_ON) {
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)
diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp
index 0994a310..36dac059 100644
--- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp
+++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp
@@ -310,10 +310,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(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 {
+ if (!emulator_pause_request_ON) {
//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;
diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
index fbe8eed8..8a531e9d 100644
--- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
+++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
@@ -123,10 +123,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(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 {
+ if (!emulator_pause_request_ON) {
datalayer.battery.status.max_charge_power_W = allowedChargePower * 10;
datalayer.battery.status.max_discharge_power_W = allowedDischargePower * 10;
}
diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
index 664c9c27..a2e9b5f2 100644
--- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
+++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
@@ -64,10 +64,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(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 {
+ if (!emulator_pause_request_ON) {
datalayer.battery.status.max_discharge_power_W = available_discharge_power * 10;
datalayer.battery.status.max_charge_power_W = available_charge_power * 10;
}
diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp
index a4a47285..70f526e7 100644
--- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp
+++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp
@@ -228,10 +228,7 @@ 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 {
+ if (!emulator_pause_request_ON) {
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
}
@@ -383,10 +380,7 @@ 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 {
+ if (!emulator_pause_request_ON) {
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
}
diff --git a/Software/src/battery/PYLON-BATTERY.cpp b/Software/src/battery/PYLON-BATTERY.cpp
index 7f053f22..97758eab 100644
--- a/Software/src/battery/PYLON-BATTERY.cpp
+++ b/Software/src/battery/PYLON-BATTERY.cpp
@@ -63,10 +63,7 @@ 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 {
+ if (!emulator_pause_request_ON) {
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));
}
diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
index 75a992c4..b25bdb44 100644
--- a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
+++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
@@ -87,10 +87,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(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 {
+ if (!emulator_pause_request_ON) {
datalayer.battery.status.max_discharge_power_W =
(LB_Discharge_Power_Limit * 500); //Convert value fetched from battery to watts
diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
index 126b6b70..5a54fe76 100644
--- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
+++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
@@ -47,10 +47,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(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 {
+ if (!emulator_pause_request_ON) {
datalayer.battery.status.max_discharge_power_W = 5000; //TODO: Take from CAN
datalayer.battery.status.max_charge_power_W = LB_Charge_Power_W;
}
diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
index 797b5290..a77ff101 100644
--- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
+++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
@@ -81,10 +81,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(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 {
+ if (!emulator_pause_request_ON) {
datalayer.battery.status.max_discharge_power_W = allowedDischargePower * 10;
datalayer.battery.status.max_charge_power_W = allowedChargePower * 10;
}
diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp
index 5067f971..5f8cd94c 100644
--- a/Software/src/battery/TESLA-BATTERY.cpp
+++ b/Software/src/battery/TESLA-BATTERY.cpp
@@ -278,10 +278,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.remaining_capacity_Wh = static_cast(
(static_cast(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 {
+ if (!emulator_pause_request_ON) {
// 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
diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp
index d8963340..f7b1875f 100644
--- a/Software/src/battery/TEST-FAKE-BATTERY.cpp
+++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp
@@ -45,10 +45,7 @@ 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 {
+ if (!emulator_pause_request_ON) {
datalayer.battery.status.max_discharge_power_W = 5000; // 5kW
datalayer.battery.status.max_charge_power_W = 5000; // 5kW
}
diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp
index 2c927890..6e876130 100644
--- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp
+++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp
@@ -83,10 +83,7 @@ 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 {
+ if (!emulator_pause_request_ON) {
//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;
diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp
index a6f3b4bf..921ad6e8 100644
--- a/Software/src/devboard/mqtt/mqtt.cpp
+++ b/Software/src/devboard/mqtt/mqtt.cpp
@@ -117,7 +117,7 @@ static void publish_common_info(void) {
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) {
+ if (datalayer.battery.status.bms_status == ACTIVE && can_send_CAN && millis() > BOOTUP_TIME) {
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;
diff --git a/Software/src/devboard/safety/safety.cpp b/Software/src/devboard/safety/safety.cpp
index 1987d4ad..d453cbf8 100644
--- a/Software/src/devboard/safety/safety.cpp
+++ b/Software/src/devboard/safety/safety.cpp
@@ -189,6 +189,7 @@ void setBatteryPause(bool pause_battery, bool pause_CAN) {
set_event(EVENT_PAUSE_BEGIN, 1);
emulator_pause_request_ON = true;
+ emulator_pause_status = PAUSING;
datalayer.battery.status.max_discharge_power_W = 0;
datalayer.battery.status.max_charge_power_W = 0;
#ifdef DOUBLE_BATTERY
@@ -196,10 +197,9 @@ void setBatteryPause(bool pause_battery, bool pause_CAN) {
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);
+ set_event(EVENT_PAUSE_END, 1);
emulator_pause_request_ON = false;
emulator_pause_CAN_send_ON = false;
emulator_pause_status = RESUMING;
diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp
index e2bb79d8..c06298c8 100644
--- a/Software/src/devboard/webserver/webserver.cpp
+++ b/Software/src/devboard/webserver/webserver.cpp
@@ -942,10 +942,12 @@ void onOTAProgress(size_t current, size_t final) {
void onOTAEnd(bool success) {
- //try to Resume the battery
- setBatteryPause(false, false);
+ ota_active = false;
+ clear_event(EVENT_OTA_UPDATE);
+
// Log when OTA has finished
if (success) {
+ // a reboot will be done by the OTA library. no need to do anything here
#ifdef DEBUG_VIA_USB
Serial.println("OTA update finished successfully!");
#endif // DEBUG_VIA_USB
@@ -953,9 +955,9 @@ void onOTAEnd(bool success) {
#ifdef DEBUG_VIA_USB
Serial.println("There was an error during OTA update!");
#endif // DEBUG_VIA_USB
+ //try to Resume the battery pause and CAN communication
+ setBatteryPause(false, false);
}
- ota_active = false;
- clear_event(EVENT_OTA_UPDATE);
}
template // This function makes power values appear as W when under 1000, and kW when over
From 07fb0c43d53b26ba9a77899de1f4a54a7d4c618b Mon Sep 17 00:00:00 2001
From: amarofarinha <151563493+amarofarinha@users.noreply.github.com>
Date: Sat, 14 Sep 2024 08:52:44 +0100
Subject: [PATCH 6/9] bugfix EVENT_PAUSE_END
---
Software/src/devboard/safety/safety.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Software/src/devboard/safety/safety.cpp b/Software/src/devboard/safety/safety.cpp
index d453cbf8..d7fc9601 100644
--- a/Software/src/devboard/safety/safety.cpp
+++ b/Software/src/devboard/safety/safety.cpp
@@ -199,10 +199,11 @@ void setBatteryPause(bool pause_battery, bool pause_CAN) {
} else {
clear_event(EVENT_PAUSE_BEGIN);
- set_event(EVENT_PAUSE_END, 1);
+ set_event(EVENT_PAUSE_END, 0);
emulator_pause_request_ON = false;
emulator_pause_CAN_send_ON = false;
emulator_pause_status = RESUMING;
+ clear_event(EVENT_PAUSE_END);
}
}
From 4a4eff43ba3e504a5e43a0698a6dbaea2d46cdfe Mon Sep 17 00:00:00 2001
From: amarofarinha <151563493+amarofarinha@users.noreply.github.com>
Date: Sun, 15 Sep 2024 10:26:24 +0100
Subject: [PATCH 7/9] Customized ElegantOTA Web Interface ("Back to Emulator"
Button+Firmware Version & Hardware ID)
---
Software/src/devboard/webserver/webserver.cpp | 27 ++
Software/src/devboard/webserver/webserver.h | 1 +
.../CurrentPlainHTML.txt | 176 +++++++++
.../howtochangeHTML.txt | 30 ++
.../lib/ayushsharma82-ElegantOTA/src/elop.cpp | 344 +-----------------
.../lib/ayushsharma82-ElegantOTA/src/elop.h | 2 +-
6 files changed, 236 insertions(+), 344 deletions(-)
create mode 100644 Software/src/lib/ayushsharma82-ElegantOTA/CurrentPlainHTML.txt
create mode 100644 Software/src/lib/ayushsharma82-ElegantOTA/howtochangeHTML.txt
diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp
index c06298c8..90438185 100644
--- a/Software/src/devboard/webserver/webserver.cpp
+++ b/Software/src/devboard/webserver/webserver.cpp
@@ -1,6 +1,7 @@
#include "webserver.h"
#include
#include "../../datalayer/datalayer.h"
+#include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h"
#include "../utils/events.h"
#include "../utils/led_handler.h"
#include "../utils/timer.h"
@@ -34,6 +35,7 @@ unsigned const long MAX_WIFI_RETRY_INTERVAL = 90000; // Maximum wifi ret
unsigned long last_wifi_monitor_time = millis(); //init millis so wifi monitor doesn't run immediately
unsigned long wifi_reconnect_interval = DEFAULT_WIFI_RECONNECT_INTERVAL;
unsigned long last_wifi_attempt_time = millis(); //init millis so wifi monitor doesn't run immediately
+const char get_firmware_info_html[] = R"rawliteral(%X%)rawliteral";
void init_webserver() {
// Configure WiFi
@@ -53,6 +55,13 @@ void init_webserver() {
server.on("/logout", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(401); });
+ // Route for firmware info from ota update page
+ server.on("/GetFirmwareInfo", HTTP_GET, [](AsyncWebServerRequest* request) {
+ if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
+ return request->requestAuthentication();
+ request->send_P(200, "application/json", get_firmware_info_html, get_firmware_info_processor);
+ });
+
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
@@ -455,6 +464,24 @@ void init_ElegantOTA() {
ElegantOTA.onEnd(onOTAEnd);
}
+String get_firmware_info_processor(const String& var) {
+ if (var == "X") {
+ String content = "";
+ static JsonDocument doc;
+#ifdef HW_LILYGO
+ doc["hardware"] = "LilyGo T-CAN485";
+#endif // HW_LILYGO
+#ifdef HW_STARK
+ doc["hardware"] = "Stark CMR Module";
+#endif // HW_STARK
+
+ doc["firmware"] = String(version_number);
+ serializeJson(doc, content);
+ return content;
+ }
+ return String();
+}
+
String processor(const String& var) {
if (var == "X") {
String content = "";
diff --git a/Software/src/devboard/webserver/webserver.h b/Software/src/devboard/webserver/webserver.h
index 09486d9e..e447f9a3 100644
--- a/Software/src/devboard/webserver/webserver.h
+++ b/Software/src/devboard/webserver/webserver.h
@@ -103,6 +103,7 @@ void init_ElegantOTA();
* @return String
*/
String processor(const String& var);
+String get_firmware_info_processor(const String& var);
/**
* @brief Executes on OTA start
diff --git a/Software/src/lib/ayushsharma82-ElegantOTA/CurrentPlainHTML.txt b/Software/src/lib/ayushsharma82-ElegantOTA/CurrentPlainHTML.txt
new file mode 100644
index 00000000..174ec045
--- /dev/null
+++ b/Software/src/lib/ayushsharma82-ElegantOTA/CurrentPlainHTML.txt
@@ -0,0 +1,176 @@
+
+
+
+
+
+ Battery Emulator OTA Update
+
+
+
+
+
+
+ Battery Emulator
+ OTA update
+ based on
+
+
+
+
+
+
+
+
+
+
+ Update Successful
+
+
+
+
+
+
+ Unexpected Error
+
+
+ Please try again
+
+
+
+
+
+
+
+
+ OTA Mode
+
+
+
+
+
+
+
+
+ Firmware Version
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Software/src/lib/ayushsharma82-ElegantOTA/howtochangeHTML.txt b/Software/src/lib/ayushsharma82-ElegantOTA/howtochangeHTML.txt
new file mode 100644
index 00000000..09191a52
--- /dev/null
+++ b/Software/src/lib/ayushsharma82-ElegantOTA/howtochangeHTML.txt
@@ -0,0 +1,30 @@
+Modifying HTML for the ElegantOTA Library
+
+This guide provides the necessary steps to update and modify the HTML used in the ElegantOTA library.
+Follow these steps carefully to ensure that your changes are properly integrated.
+
+Steps to Modify the HTML:
+
+1 - Edit the CurrentPlainHTML.txt:
+
+Locate the file CurrentPlainHTML.txt in your project directory.
+Modify the HTML content as needed. This file contains the plain HTML code that will be served through the OTA interface.
+
+Convert the HTML to GZIP and Decimal Format:
+
+Copy the content of the updated CurrentPlainHTML.txt.
+Navigate to the CyberChef tool for encoding and compression.
+Apply the following recipe:
+Gzip with the "Dynamic Huffman Coding" option enabled.
+Convert to Decimal with a comma separator.
+Use this link for the process: https://gchq.github.io/CyberChef/#recipe=Gzip('Dynamic%20Huffman%20Coding','','',false)To_Decimal('Comma',false)
+
+2 - Update the ELEGANT_HTML Array:
+
+Copy the resulting decimal output from CyberChef.
+Replace the existing content of the ELEGANT_HTML array in elop.cpp with the new decimal data from CyberChef.
+
+3 - Adjust the ELEGANT_HTML Array Size:
+
+After updating the ELEGANT_HTML array in both elop.h and elop.cpp, update the array size to match the length of the new output from CyberChef.
+Ensure that the array size reflects the new length of the compressed HTML to avoid errors during compilation.
diff --git a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp
index de3abdc2..b54e62b3 100644
--- a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp
+++ b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp
@@ -1,345 +1,3 @@
#include "elop.h"
-const uint8_t ELEGANT_HTML[10214] PROGMEM = {
-31,139,8,0,0,0,0,0,2,3,164,86,231,118,219,184,18,254,159,167,160,121,206,114,9,11,162,37,89,150,
-21,73,112,122,110,154,203,246,226,235,155,3,131,160,136,24,2,184,0,232,114,101,189,251,14,72,213,56,61,127,
-72,148,153,111,190,105,0,70,91,169,102,238,166,224,65,238,38,242,224,222,200,255,2,73,213,152,132,92,133,7,
-247,130,96,148,115,154,250,1,12,39,220,209,128,229,212,88,238,72,248,219,175,207,155,253,48,216,89,223,84,116,
-194,73,120,41,248,85,161,141,11,3,166,149,227,10,132,175,68,234,114,146,242,75,193,120,179,154,224,64,40,225,
-4,149,77,203,168,228,164,157,180,86,96,78,56,201,15,158,73,62,166,202,29,255,250,40,120,35,28,31,237,212,
-203,181,136,101,70,20,46,240,236,73,56,209,105,41,57,216,51,218,90,109,196,88,168,131,123,113,86,42,230,132,
-86,49,154,2,17,235,130,140,128,191,229,4,24,37,204,112,234,56,88,240,179,56,148,66,93,132,40,49,92,190,
-17,214,13,69,22,103,81,148,37,182,44,188,35,118,125,28,207,173,21,32,172,105,26,34,100,184,43,141,26,102,
-218,196,181,29,26,232,44,88,154,250,167,228,230,230,23,46,57,115,218,60,146,50,254,209,91,59,5,117,242,30,
-212,217,143,8,165,49,69,67,197,175,130,195,210,81,207,254,248,220,114,115,201,77,76,201,193,116,101,131,121,27,
-20,1,83,150,84,65,32,36,100,185,144,169,119,32,68,43,65,225,5,89,66,211,148,167,71,58,229,22,137,196,
-209,241,17,157,84,58,111,94,30,189,14,163,72,120,223,97,190,201,8,54,210,88,160,25,74,116,205,34,94,120,
-133,167,75,99,131,173,22,182,229,185,51,156,195,112,134,134,139,184,7,54,166,139,208,51,50,157,13,235,64,5,
-52,17,202,241,177,17,238,38,138,98,182,154,145,181,29,132,41,80,202,184,49,220,156,104,41,88,45,187,185,68,
-222,151,241,90,85,17,28,87,69,224,29,42,45,111,50,195,83,174,124,181,217,240,1,75,214,166,36,20,138,201,
-50,229,225,224,142,38,85,90,221,76,116,121,87,71,79,132,11,7,239,45,90,136,104,179,46,190,16,179,217,50,
-10,169,143,2,228,137,38,188,88,212,138,31,147,173,214,112,17,28,235,179,158,113,199,114,16,203,193,39,204,208,
-108,134,98,52,148,154,81,249,139,211,134,142,121,226,114,94,103,45,165,230,34,188,189,221,138,195,106,9,156,8,
-214,5,81,20,93,9,149,234,171,100,66,1,243,144,167,130,198,97,92,84,193,178,77,166,165,54,208,119,94,117,
-16,120,44,20,162,90,148,219,7,203,28,39,139,193,188,77,18,38,169,181,62,229,190,154,226,154,4,194,75,241,
-49,95,72,62,190,121,57,223,63,212,41,127,146,115,118,113,174,175,193,6,243,67,158,130,235,104,240,37,118,12,
-159,232,75,254,125,166,218,104,248,53,122,224,218,179,75,16,240,246,185,226,38,14,153,20,236,34,196,107,199,201,
-55,240,248,234,176,126,32,241,243,173,175,143,220,135,209,164,24,231,46,68,235,29,251,40,150,104,58,111,83,25,
-69,50,121,251,150,219,195,234,72,136,162,227,243,119,156,185,164,48,218,105,127,228,36,57,181,199,87,234,196,232,
-130,27,119,147,128,5,25,75,28,166,60,163,165,4,228,7,50,153,143,7,114,118,73,77,240,134,76,249,117,117,
-138,14,166,179,25,190,222,152,14,215,15,108,47,45,73,248,232,241,147,167,207,158,255,231,197,203,87,175,223,28,
-30,29,159,252,244,243,47,191,254,246,251,31,127,254,245,55,61,103,128,61,206,197,187,11,57,81,186,248,199,88,
-87,94,94,93,223,252,191,213,238,236,118,247,122,251,253,251,141,29,200,26,153,2,95,57,88,98,91,156,46,93,
-180,163,81,122,107,15,14,14,118,59,205,116,134,65,208,124,84,16,68,106,89,16,228,42,21,84,173,137,86,13,
-110,147,170,157,77,233,15,122,66,142,202,201,57,55,104,142,144,37,158,6,128,246,81,212,238,237,183,219,247,119,
-247,110,151,139,157,46,138,186,157,253,126,103,111,111,183,215,170,174,18,31,129,148,180,134,233,200,38,146,171,177,
-203,135,105,163,129,236,105,122,70,178,164,102,16,251,25,26,46,72,130,7,84,165,122,242,248,198,113,187,193,110,
-5,120,122,54,180,7,173,161,109,54,81,154,20,165,205,227,67,234,242,36,147,90,155,122,88,99,196,104,187,179,
-215,67,75,112,240,250,220,195,254,170,255,208,38,253,56,58,166,164,133,25,240,166,43,222,180,209,192,172,65,250,
-40,61,101,16,192,189,179,91,98,79,233,217,104,212,233,54,217,15,187,157,53,27,87,0,14,54,62,237,1,166,
-27,248,219,128,64,1,126,225,16,96,87,86,224,3,6,40,24,136,32,174,31,240,228,5,191,254,82,27,222,135,
-5,190,55,224,193,187,40,113,250,23,103,132,26,199,109,8,21,94,223,143,218,123,155,219,43,251,201,59,45,84,
-28,66,231,225,156,95,127,165,179,222,211,206,130,73,65,141,229,47,149,139,109,2,55,176,117,38,166,184,131,112,
-251,131,121,123,76,45,239,117,191,198,206,46,90,8,176,121,194,218,189,91,24,52,218,48,236,87,163,206,25,22,
-160,39,70,221,161,128,8,209,237,126,67,108,247,70,100,153,155,254,131,57,87,153,248,167,227,35,23,251,18,232,
-109,199,187,77,129,162,222,46,66,131,185,64,72,194,15,198,232,188,226,253,161,48,89,48,99,120,33,41,227,241,
-206,233,255,30,53,255,110,53,239,55,254,187,115,182,35,198,56,4,180,207,21,38,35,141,6,253,161,139,216,22,
-105,69,209,34,125,50,17,42,229,215,199,208,209,11,206,180,217,70,40,170,218,163,208,87,113,7,55,59,219,172,
-209,71,176,60,26,177,237,206,237,135,116,16,242,158,54,97,123,45,27,179,225,117,50,63,244,72,86,221,240,158,
-224,11,178,92,197,207,201,180,116,89,127,48,181,85,237,220,113,124,117,66,63,79,206,133,74,54,196,226,82,113,
-203,104,193,99,174,152,78,249,111,63,191,124,162,39,133,86,92,57,208,68,104,89,14,117,101,126,8,54,229,119,
-52,231,152,181,193,13,128,26,20,80,133,250,20,227,69,38,50,159,9,11,89,176,35,185,200,130,133,194,201,214,
-139,228,9,152,135,0,90,180,209,183,217,93,234,95,141,95,107,38,153,209,147,39,115,67,177,60,181,103,104,101,
-101,85,121,224,213,75,242,124,184,179,189,117,47,216,14,158,114,199,205,68,40,30,136,44,160,42,208,213,109,24,
-8,27,208,224,113,153,101,220,128,148,23,124,72,75,151,107,19,4,193,115,238,31,148,193,163,115,93,94,228,52,
-21,239,120,30,140,114,231,10,59,216,217,201,170,205,68,155,241,65,165,37,5,227,202,242,32,56,124,249,43,44,
-236,120,111,142,201,7,210,35,183,136,42,165,140,162,248,16,86,111,111,79,170,239,214,22,92,214,194,214,76,208,
-108,117,165,31,174,84,65,102,253,138,138,34,127,139,235,44,216,88,77,22,32,132,132,11,144,48,138,62,44,3,
-216,171,135,238,201,26,201,37,178,225,52,125,46,53,117,111,158,109,2,46,37,172,247,124,115,15,56,215,203,113,
-11,183,160,188,238,190,12,94,224,140,188,76,124,159,64,182,143,113,10,19,168,64,76,87,1,99,88,160,41,219,
-188,146,235,244,63,16,81,36,146,170,63,96,70,192,52,168,82,115,3,207,124,146,190,215,78,12,13,24,201,238,
-46,250,15,136,63,50,134,222,172,94,68,53,233,250,33,196,128,249,96,171,22,16,182,250,131,78,20,109,80,218,
-34,228,55,161,92,191,218,134,140,50,194,86,151,6,90,29,96,134,200,100,253,250,5,36,92,16,182,60,100,177,
-34,237,253,221,206,94,191,11,239,10,204,73,179,179,15,243,221,254,254,125,172,73,115,185,213,197,142,44,118,250,
-184,132,54,41,71,102,209,38,37,180,137,57,45,207,72,236,191,254,128,135,127,125,137,174,61,89,230,155,157,238,
-98,183,191,241,114,49,167,197,252,134,111,119,250,163,81,1,151,47,54,167,113,209,232,117,97,253,254,104,212,69,
-141,118,247,140,20,75,215,38,132,38,111,179,12,143,253,127,60,198,185,255,231,57,190,241,127,33,238,178,36,237,
-94,93,5,79,136,194,191,16,142,47,136,198,111,137,27,42,50,137,21,230,88,99,7,54,203,198,191,108,213,59,
-110,245,58,12,4,224,245,4,144,1,14,135,207,197,156,242,34,233,110,147,38,187,255,45,41,205,137,220,143,136,
-15,67,194,150,215,200,113,69,73,101,52,227,99,124,223,129,239,177,35,51,128,215,128,142,139,213,30,230,117,39,
-254,191,19,243,249,204,204,132,222,137,28,33,1,241,66,127,140,255,238,192,124,62,35,51,192,215,208,123,4,196,
-204,213,73,249,24,127,25,182,24,200,48,84,117,158,12,95,12,168,136,148,152,62,48,98,49,46,88,82,9,26,
-78,72,110,136,121,138,119,241,100,212,100,32,83,132,110,136,147,209,187,13,180,151,193,12,121,58,32,27,98,42,
-193,211,0,108,4,186,197,196,16,122,50,160,203,81,98,33,140,210,211,1,110,136,9,13,16,60,48,108,51,224,
-162,34,170,45,15,22,95,22,40,195,233,212,158,148,207,119,202,107,248,61,36,60,59,28,50,37,159,111,146,120,
-141,190,3,18,237,130,160,78,201,231,187,4,183,196,70,24,19,153,224,116,124,190,57,228,102,200,184,152,172,78,
-138,158,12,95,140,20,184,87,52,78,6,100,58,88,130,144,226,3,194,23,226,138,16,203,34,253,84,216,86,152,
-184,51,203,234,84,244,84,120,148,89,24,235,1,97,191,101,160,75,24,45,167,131,155,129,74,6,59,112,50,106,
-49,128,160,19,46,56,25,224,94,138,153,69,193,34,79,137,46,136,195,132,89,118,50,114,49,144,116,106,89,242,
-100,64,119,29,104,141,144,76,218,132,124,253,221,138,205,181,149,123,77,195,215,155,161,94,3,24,151,138,170,167,
-89,112,50,190,206,211,136,129,98,11,197,67,167,227,235,221,97,183,131,227,162,83,218,61,78,5,150,2,78,233,
-86,9,57,37,182,36,208,212,106,210,31,32,57,29,115,136,91,103,240,201,33,219,1,105,75,74,152,60,72,56,
-41,81,208,108,164,157,16,217,149,208,203,153,170,122,66,184,33,169,234,138,206,62,33,177,28,25,162,141,234,19,
-209,171,142,48,97,152,85,158,8,232,86,152,162,224,69,63,21,240,197,112,74,154,186,202,169,208,221,70,183,147,
-21,142,233,248,249,251,103,153,141,118,5,165,204,166,227,231,205,49,59,151,1,104,84,195,176,24,63,231,55,108,
-93,58,130,110,45,62,33,63,111,144,91,170,24,151,167,209,196,253,116,64,39,4,41,98,229,158,56,33,92,144,
-11,213,214,21,33,241,32,145,95,137,56,92,121,50,176,25,42,110,173,154,221,39,164,150,163,146,4,233,125,58,
-224,27,66,9,32,205,78,70,252,42,60,4,93,44,121,128,112,73,64,105,56,194,30,32,182,55,99,238,74,73,
-121,128,96,67,0,21,133,48,251,164,232,162,36,42,43,213,251,132,244,110,132,198,217,251,190,144,127,140,152,1,
-6,3,49,16,69,111,19,137,31,20,128,221,128,30,161,128,106,138,10,59,202,38,198,0,146,61,123,179,73,131,
-130,6,248,24,207,3,60,179,227,90,11,231,82,111,3,110,109,69,23,241,110,75,156,224,121,174,50,26,126,60,
-112,238,95,249,135,41,75,235,160,159,128,181,108,83,119,239,197,35,238,5,77,138,243,65,42,25,232,253,100,26,
-196,165,99,181,172,107,200,210,255,89,193,128,138,109,77,53,195,76,153,213,49,205,220,182,9,166,103,63,13,36,
-250,15,100,175,41,87,199,15,181,198,181,30,39,175,188,255,159,2,147,6,137,192,37,157,61,164,184,59,61,110,
-167,23,1,179,187,138,5,34,146,240,102,205,199,187,31,141,0,47,222,244,92,229,191,131,145,166,207,32,52,51,
-63,74,51,225,34,61,83,236,2,204,31,35,190,103,108,2,74,247,128,87,253,133,123,233,202,128,29,149,216,222,
-176,124,61,149,98,230,96,51,102,71,74,33,61,247,128,99,75,138,59,84,78,108,217,213,113,239,164,254,168,255,
-65,31,214,174,60,138,212,22,215,74,123,229,160,202,174,61,149,81,247,218,221,71,178,165,43,181,244,35,30,46,
-106,84,230,189,185,172,20,156,196,151,130,119,221,131,195,248,185,119,226,147,243,152,199,79,190,90,153,230,254,23,
-106,195,83,150,101,80,96,141,101,243,199,145,105,150,13,49,103,223,196,116,75,41,38,18,148,58,198,214,157,235,
-135,172,40,64,238,31,191,126,21,243,134,236,124,27,23,208,232,95,68,60,254,1,196,67,160,74,20,203,152,219,
-49,85,243,34,117,12,62,250,213,212,174,115,166,192,243,198,68,249,134,36,7,237,100,177,235,18,26,227,242,46,
-227,96,232,100,32,77,114,138,2,91,98,131,104,241,235,44,62,196,246,27,149,19,64,171,170,138,107,159,140,189,
-204,199,248,147,6,241,121,123,241,91,92,150,125,242,102,33,65,189,233,185,85,137,84,183,4,175,114,35,44,49,
-16,5,90,155,40,80,151,176,243,133,25,123,216,8,5,29,199,95,181,128,208,20,219,16,70,251,115,202,43,24,
-105,57,183,186,169,152,206,40,211,206,182,145,101,52,243,220,35,35,59,50,115,176,201,106,140,145,26,30,174,223,
-5,86,214,109,110,251,82,255,91,53,68,87,21,34,60,205,13,237,248,122,61,110,110,78,169,154,154,39,213,252,
-207,199,174,233,74,22,138,179,229,188,233,216,88,30,101,203,195,25,83,198,33,187,100,69,59,86,89,168,225,147,
-246,90,176,224,16,114,49,25,162,75,80,165,89,129,200,37,180,199,77,91,87,9,153,111,83,77,173,101,152,5,
-253,237,245,171,125,173,203,67,48,29,88,108,234,134,162,132,194,115,223,189,61,58,118,137,107,213,105,173,111,108,
-204,194,246,241,11,237,159,222,246,131,14,144,244,173,53,203,184,45,152,139,170,200,60,25,34,62,100,171,50,212,
-134,52,191,189,190,182,230,143,190,155,24,155,84,26,47,41,61,55,202,182,93,242,242,142,123,195,37,223,69,60,
-69,199,14,195,187,69,108,200,202,250,64,29,81,244,251,227,161,244,198,85,253,141,26,139,124,149,166,9,135,223,
-88,169,134,48,163,10,102,212,92,105,170,33,157,210,98,2,11,149,172,70,44,180,27,142,112,67,28,223,243,45,
-8,183,87,42,142,55,140,224,191,32,183,107,20,70,193,127,0,115,40,118,143,183,185,216,49,76,103,85,165,233,
-48,155,129,43,112,174,136,123,207,136,91,255,54,242,218,15,234,37,96,199,115,79,108,116,45,178,24,3,71,198,
-44,236,243,243,216,100,241,232,24,15,46,171,218,159,70,17,114,236,185,71,118,220,194,105,15,110,200,156,214,37,
-14,214,196,205,121,223,187,200,111,72,26,210,178,132,194,8,51,54,216,46,225,132,135,56,1,131,233,167,16,158,
-250,100,214,179,96,197,196,96,119,27,154,20,123,237,216,194,248,117,57,85,152,27,36,58,1,172,190,159,226,129,
-216,23,188,159,172,223,188,185,146,87,92,179,146,195,83,143,114,144,218,115,255,46,42,39,197,186,117,193,231,78,
-213,146,19,5,56,30,22,78,125,7,85,112,40,206,174,104,150,67,232,250,248,29,137,248,63,215,254,109,69,14,
-85,201,153,161,98,22,194,82,148,158,191,98,171,166,238,55,201,15,72,171,158,226,202,154,253,141,249,248,245,179,
-171,69,222,172,180,22,168,252,245,175,239,68,194,7,69,89,105,19,129,31,127,160,113,207,253,154,135,165,4,220,
-181,221,54,166,61,195,32,180,251,188,43,141,240,11,220,140,7,218,169,121,240,187,242,249,250,232,141,135,22,25,
-212,188,247,60,191,238,70,45,176,197,142,176,176,157,157,49,244,186,5,81,236,246,130,198,23,163,14,40,65,129,
-62,101,48,139,247,70,127,252,241,151,213,118,158,169,31,110,210,115,28,116,186,77,162,4,198,66,2,137,232,88,
-131,172,19,241,41,80,236,2,155,31,137,144,25,200,192,64,70,221,163,205,155,104,173,127,181,52,34,37,56,203,
-122,144,157,251,136,174,193,125,120,8,73,179,68,59,8,244,44,232,6,182,34,199,117,27,28,6,171,57,43,32,
-152,2,78,10,68,235,225,253,81,48,131,228,140,233,0,143,105,20,5,2,154,125,168,148,142,48,237,70,65,46,
-46,2,77,19,187,18,221,27,5,98,248,54,120,28,27,62,193,152,230,140,207,163,138,5,138,22,42,80,32,217,
-152,168,185,210,144,7,21,35,1,45,75,14,65,11,32,155,56,52,245,154,166,71,246,117,215,224,147,35,152,8,
-112,78,14,200,161,72,132,22,100,31,248,57,104,150,82,231,13,84,64,158,75,70,57,121,99,86,156,35,67,159,
-12,152,184,207,145,180,179,133,246,112,118,114,241,129,185,196,237,201,245,128,203,247,163,121,158,8,78,92,75,106,
-136,211,169,1,84,87,18,130,254,191,45,42,132,204,41,111,23,77,172,51,27,21,203,203,77,34,178,121,157,83,
-57,97,133,113,218,208,206,172,152,26,41,117,51,149,117,11,49,235,214,117,253,74,239,79,45,202,206,235,235,229,
-167,134,38,137,140,102,102,3,120,255,180,147,114,255,246,235,43,238,194,190,158,180,194,68,85,97,240,145,167,147,
-9,173,33,27,125,111,67,51,93,39,211,13,50,189,75,166,247,200,244,62,153,62,168,81,191,214,159,189,88,22,
-50,187,170,4,173,175,138,190,204,168,223,151,16,165,165,40,38,245,144,136,49,187,17,163,193,79,47,57,75,50,
-162,104,94,146,82,66,189,20,63,185,40,132,42,105,10,228,104,247,181,121,14,14,97,82,113,42,201,107,40,184,
-32,6,68,83,65,182,236,63,37,85,228,21,75,160,101,239,152,37,92,168,36,3,105,194,102,70,46,73,141,22,
-250,173,67,222,40,227,51,62,208,249,209,218,141,70,85,70,234,170,28,64,31,222,191,49,112,37,58,174,20,138,
-89,69,37,240,255,179,111,157,235,145,234,72,244,85,216,60,1,177,64,67,7,122,211,115,76,248,161,6,129,245,
-13,105,37,176,221,195,199,187,95,149,68,145,125,221,247,255,56,215,57,135,82,160,84,10,96,85,228,51,187,62,
-51,1,65,154,19,154,243,172,140,224,241,54,92,2,222,58,21,199,77,85,68,196,241,67,85,38,248,110,170,90,
-153,96,233,53,90,167,251,143,151,9,43,155,105,172,175,163,3,193,156,214,146,69,248,71,127,211,57,213,230,144,
-137,236,170,110,50,81,181,181,45,245,171,137,54,56,166,130,209,69,223,162,203,253,64,71,118,29,233,91,30,251,
-7,178,196,94,152,236,197,255,42,230,199,161,82,211,36,129,12,232,98,123,76,3,76,207,52,130,150,50,173,68,
-17,149,85,201,80,241,85,191,33,105,140,239,131,165,19,48,26,170,239,11,222,124,31,199,11,76,236,84,185,138,
-89,100,174,186,222,104,252,35,211,139,226,161,183,117,73,53,21,172,108,230,36,47,104,198,76,225,145,78,135,105,
-21,183,18,182,245,93,213,54,208,204,136,182,77,53,144,45,39,188,124,166,57,79,76,110,127,162,106,110,48,87,
-227,178,160,123,43,94,162,8,107,171,87,63,68,214,188,36,67,131,39,78,21,186,228,48,167,232,90,12,173,103,
-84,196,79,187,173,135,78,77,57,203,147,235,80,123,82,165,169,100,77,68,252,250,117,86,5,227,98,54,176,247,
-156,149,213,162,218,48,125,19,51,219,99,221,222,238,127,8,154,49,89,200,182,40,168,184,119,9,151,117,78,239,
-81,206,101,67,120,195,138,94,159,249,252,191,173,26,102,39,185,157,36,246,38,111,217,79,194,78,121,214,10,102,
-155,92,130,129,213,235,118,170,182,117,59,161,150,179,140,149,73,55,1,85,110,183,185,93,176,178,237,160,116,61,
-221,154,22,238,69,106,194,105,94,101,179,203,199,209,38,152,30,24,120,147,123,61,56,35,19,29,250,205,150,39,
-157,3,199,225,185,165,186,170,166,49,111,238,145,119,29,38,248,75,76,15,52,69,87,251,94,30,113,128,163,71,
-84,249,56,122,186,184,21,82,105,234,138,151,13,19,125,148,112,9,137,41,65,98,120,227,175,231,69,102,203,231,
-204,126,230,9,171,236,152,170,48,151,54,109,19,94,217,60,21,180,96,54,43,110,44,177,205,123,20,227,173,212,
-55,112,157,35,11,158,36,57,3,151,198,93,87,208,87,156,249,32,163,44,66,218,156,77,125,71,143,38,234,62,
-217,123,203,29,92,42,65,251,203,140,188,70,150,123,221,35,238,72,152,252,146,211,134,161,120,129,77,58,81,53,
-10,24,77,249,131,189,224,21,104,142,98,243,6,60,208,222,220,190,143,118,77,75,96,39,227,62,26,188,84,163,
-238,103,85,21,136,200,88,84,121,78,100,73,107,34,27,193,227,166,100,82,70,112,240,242,202,11,222,220,141,44,
-19,52,225,12,146,186,168,10,50,78,82,214,138,125,230,244,109,178,169,54,92,37,18,94,210,28,77,153,83,249,
-196,18,242,147,137,10,177,178,45,152,170,22,49,99,112,133,98,143,111,196,130,198,243,114,4,220,21,94,74,214,
-44,16,147,155,134,200,176,220,250,117,203,153,8,183,254,146,166,233,68,34,42,178,219,135,240,98,121,7,215,242,
-131,163,245,79,203,9,63,110,92,96,150,182,92,245,249,23,87,125,76,146,55,184,223,135,77,233,44,217,208,183,
-188,21,216,190,155,128,16,55,247,242,58,174,213,5,149,205,236,190,220,117,228,32,240,212,50,140,195,1,225,37,
-12,44,180,36,76,231,51,86,178,154,83,52,18,81,213,88,237,1,130,89,14,96,172,215,18,220,86,16,169,77,
-77,145,216,86,25,153,157,186,35,133,141,88,195,152,196,214,248,186,149,72,96,115,251,40,66,232,87,82,248,149,
-20,20,249,43,41,252,74,10,142,20,4,142,178,186,49,152,233,77,86,121,219,176,235,184,227,198,101,7,252,57,
-174,236,112,245,71,0,173,84,165,210,92,117,150,89,147,92,227,156,215,145,96,113,243,193,181,245,167,122,8,243,
-196,27,166,35,28,86,143,47,130,214,171,243,156,222,193,146,183,117,233,29,220,85,78,28,34,189,99,54,147,196,
-197,93,165,114,149,179,180,81,0,252,2,19,238,14,216,250,247,4,248,3,224,132,130,21,189,3,199,12,174,222,
-136,186,198,240,193,64,246,39,9,221,238,167,222,146,190,70,161,82,20,55,226,185,195,26,154,12,69,251,70,172,
-185,227,138,11,144,241,87,196,116,77,176,98,60,67,228,211,37,186,69,120,129,152,240,69,67,138,102,86,51,104,
-130,63,35,252,57,113,24,225,96,1,79,250,133,124,130,23,114,15,209,227,2,29,212,122,165,187,92,247,246,14,
-47,97,147,53,162,198,236,157,52,103,175,35,8,6,42,201,130,65,76,11,76,204,45,150,193,10,36,62,110,0,
-77,53,0,249,230,132,35,120,244,17,30,49,207,25,176,23,114,233,76,84,250,19,244,237,171,127,86,73,245,219,
-119,164,192,50,196,193,157,19,96,33,225,47,8,127,32,210,54,207,187,105,89,223,255,239,7,187,235,173,130,180,
-96,3,171,162,174,155,246,247,38,17,124,56,28,221,132,101,31,251,222,161,37,47,20,66,180,212,24,48,30,192,
-180,60,105,65,207,80,97,241,50,229,37,135,161,99,246,43,100,216,200,172,247,53,142,57,85,32,208,109,227,182,
-180,149,76,16,67,68,64,152,3,207,13,186,6,122,103,218,207,46,29,78,248,228,111,3,46,108,140,6,34,170,
-151,78,255,145,112,193,204,60,168,32,67,194,228,177,38,99,253,52,161,119,96,155,44,73,204,116,163,245,246,138,
-104,40,50,80,239,192,113,46,79,239,40,65,19,143,134,215,178,27,107,94,24,43,55,58,157,210,144,237,157,140,
-214,196,235,212,207,8,67,7,16,223,32,19,112,48,192,105,66,2,133,224,80,210,71,44,44,49,129,50,164,73,
-88,120,180,50,186,168,143,250,117,146,228,217,82,128,133,32,255,186,118,128,101,26,20,201,49,217,35,65,18,189,
-122,233,22,167,236,6,67,137,10,240,111,127,241,206,222,197,163,223,190,47,214,148,227,116,228,45,79,238,96,137,
-225,7,150,31,90,62,44,48,158,169,248,176,115,217,199,143,139,18,110,238,237,112,59,255,129,18,188,211,209,242,
-78,23,203,59,7,15,21,2,19,49,241,93,247,241,38,248,23,203,63,120,234,59,124,164,0,68,136,255,70,17,
-142,191,145,30,222,146,30,54,210,224,45,105,160,164,25,172,87,24,57,142,173,203,22,45,91,31,246,65,235,14,
-39,235,114,217,180,45,91,180,43,195,78,123,220,45,118,218,67,142,195,63,224,55,80,126,67,87,125,123,191,235,
-87,47,64,30,247,26,134,22,126,191,233,21,134,49,46,136,112,88,213,175,196,115,17,53,211,181,153,125,175,136,
-233,169,26,64,212,251,75,249,158,122,18,7,75,177,183,213,122,40,13,87,82,199,223,120,54,40,94,112,92,95,
-176,171,31,228,119,226,161,92,79,247,43,239,184,182,241,39,185,187,208,251,206,158,222,159,220,251,11,249,174,122,
-18,31,150,226,211,174,26,111,145,192,174,217,246,2,28,28,226,140,160,255,54,7,115,6,25,104,89,204,158,132,
-56,103,83,22,30,236,111,189,189,202,185,124,71,173,165,90,82,176,132,183,197,226,209,80,232,186,189,211,214,53,
-19,49,149,108,125,248,63,18,67,89,183,92,5,179,9,114,176,231,97,62,197,182,107,193,39,70,245,74,10,113,
-13,182,25,135,39,215,125,207,155,26,32,199,208,58,123,143,57,188,188,239,208,59,89,126,96,29,46,15,56,100,
-172,36,225,251,30,15,129,229,93,84,86,11,222,117,41,88,242,136,67,95,213,238,120,134,175,247,28,222,89,158,
-87,47,36,120,192,39,228,48,55,176,124,239,81,167,15,85,52,208,83,225,251,21,93,207,38,8,78,115,9,42,
-142,110,135,184,115,236,29,8,70,179,37,51,233,83,118,51,164,30,254,103,62,210,148,189,78,181,246,124,106,181,
-87,143,76,7,52,229,121,174,159,153,254,96,215,153,231,134,23,48,124,241,5,133,40,110,111,60,38,55,246,147,
-51,241,193,9,108,215,118,124,219,251,56,191,36,105,141,227,200,241,66,217,59,250,124,252,219,184,199,28,206,203,
-113,89,55,246,214,0,124,188,110,119,165,232,66,239,54,191,125,253,141,157,235,80,146,27,132,161,191,194,212,44,
-87,49,6,182,228,67,210,123,159,146,222,219,183,7,43,111,86,112,43,219,226,198,201,180,235,119,200,226,137,166,
-202,216,15,190,254,108,55,164,90,243,163,123,238,172,176,212,204,249,217,138,247,229,105,152,229,169,212,12,248,190,
-40,248,224,18,150,108,165,95,58,203,47,251,173,232,233,47,81,219,7,68,169,248,217,117,235,157,155,237,72,237,
-218,185,173,241,93,159,191,183,83,174,29,203,1,199,66,33,192,130,142,7,192,139,189,253,72,177,32,210,185,204,
-124,255,228,84,1,116,112,224,175,229,207,123,35,151,111,174,143,74,155,35,243,55,159,177,210,165,183,187,113,59,
-34,140,248,188,27,230,99,31,21,28,139,122,170,84,238,222,75,71,78,118,204,72,212,172,14,36,56,221,58,189,
-217,0,134,210,52,133,214,64,66,211,160,20,12,221,129,50,101,189,190,170,101,67,235,160,47,74,216,6,150,87,
-204,242,178,76,156,115,182,198,209,39,72,66,242,217,202,52,26,118,153,81,86,128,228,244,232,147,85,200,99,59,
-158,194,187,8,8,164,226,166,129,196,12,154,61,169,137,23,91,209,112,194,249,105,91,77,162,180,246,128,225,149,
-159,79,182,3,174,102,181,53,20,54,116,59,148,188,165,21,88,125,51,150,160,66,101,172,183,207,178,254,199,75,
-131,126,211,127,167,248,239,238,110,249,176,85,1,9,127,178,5,211,123,211,165,222,172,195,229,48,161,43,139,178,
-154,202,178,10,197,56,186,29,192,97,193,158,182,2,115,245,184,61,17,90,179,176,6,169,70,94,50,250,63,19,
-134,146,29,53,23,245,60,52,223,170,219,169,31,170,106,220,4,129,75,124,22,20,176,48,69,59,81,80,140,48,
-184,11,89,124,217,224,214,156,44,32,29,6,136,39,104,229,102,125,44,119,203,10,250,74,53,183,169,230,201,249,
-132,86,233,157,107,80,103,93,88,83,244,232,99,63,171,206,100,88,104,150,86,220,62,152,33,206,236,214,126,2,
-119,247,234,195,234,108,120,247,153,161,159,119,119,69,226,212,7,31,125,186,123,207,170,253,145,62,153,126,109,250,
-141,124,16,20,144,253,163,254,113,255,164,5,50,110,76,182,131,201,93,26,242,89,120,150,158,61,110,129,244,126,
-99,124,239,96,43,218,81,201,50,38,231,244,136,235,104,54,49,155,167,118,184,42,123,108,199,210,199,2,239,139,
-42,59,111,181,38,77,78,207,79,239,184,23,194,110,211,64,241,110,211,67,237,211,82,90,24,78,80,53,193,20,
-90,219,254,152,186,19,123,200,77,105,140,178,112,97,231,178,52,66,193,66,200,211,200,64,124,4,20,64,194,206,
-87,3,145,213,157,69,96,107,218,142,192,9,38,17,230,44,29,242,241,237,78,28,203,224,156,21,174,142,82,119,
-69,195,200,58,196,68,254,92,183,102,225,101,62,107,213,178,252,111,49,64,58,237,156,229,60,93,119,248,220,116,
-120,61,166,106,54,227,29,189,168,44,0,186,209,159,82,216,1,253,41,157,138,192,237,210,33,184,140,14,243,94,
-142,27,248,90,67,47,143,28,140,42,116,97,228,139,6,160,140,93,250,56,144,67,118,176,54,206,181,136,209,59,
-147,2,118,251,34,82,192,223,250,67,201,85,36,68,20,67,177,171,132,183,254,110,88,59,200,26,46,128,187,195,
-184,28,159,151,10,174,227,243,181,77,189,77,242,207,96,84,21,202,162,68,91,253,16,12,42,216,130,104,13,48,
-121,52,13,55,20,57,193,121,7,120,243,90,150,134,253,208,250,30,34,78,18,130,187,124,93,183,31,42,237,67,
-112,221,77,130,30,110,41,77,97,46,110,103,241,82,113,166,25,142,154,4,207,106,66,96,51,33,242,158,240,249,
-142,36,43,129,196,8,188,103,57,224,45,43,34,220,21,48,202,95,199,226,241,225,73,111,73,219,190,181,60,159,
-207,231,217,188,166,107,95,78,191,174,92,143,16,4,197,106,93,9,252,164,178,255,77,31,244,237,254,194,74,1,
-102,38,125,9,56,107,111,133,67,34,129,159,188,183,162,204,19,182,152,78,89,22,183,166,225,154,213,203,6,14,
-144,212,132,209,138,171,33,222,109,166,99,97,220,30,33,122,203,134,145,158,25,168,166,27,23,86,118,171,18,101,
-117,186,138,85,20,208,159,105,151,85,229,106,186,146,181,157,228,61,195,248,4,204,115,197,187,135,52,117,127,223,
-161,239,240,139,52,147,119,200,193,17,169,224,88,94,1,197,144,122,59,253,235,126,188,88,68,86,235,193,241,109,
-95,67,94,102,149,252,5,231,119,128,174,52,5,199,31,47,232,87,85,241,10,127,87,188,0,241,69,222,122,9,
-26,224,129,97,170,164,35,53,57,182,66,113,104,255,226,34,166,212,51,157,229,129,194,58,14,132,225,171,248,2,
-18,30,85,235,60,161,4,200,163,99,224,157,126,191,15,153,44,97,89,192,158,209,52,255,211,148,252,244,198,2,
-254,137,51,215,163,71,94,159,36,155,122,94,247,28,47,192,228,121,156,121,29,53,211,106,158,150,203,103,230,5,
-29,127,255,196,202,253,136,192,167,228,146,134,62,235,35,127,204,175,50,255,170,57,110,131,28,255,53,153,26,180,
-111,132,145,35,93,45,183,87,138,220,19,40,82,5,154,180,229,235,56,63,105,248,135,18,128,161,195,0,213,88,
-26,173,140,115,234,10,27,130,79,224,130,186,168,190,84,143,164,8,68,21,12,23,89,113,108,63,102,67,179,174,
-217,245,107,214,63,81,1,223,52,234,26,181,20,53,205,220,49,25,160,138,154,175,157,197,24,232,128,87,95,15,
-122,235,151,10,25,75,169,227,157,230,248,203,124,254,182,237,127,91,84,58,53,243,10,191,83,192,189,83,165,26,
-240,61,247,119,61,115,200,230,245,78,95,190,190,177,40,90,191,83,15,184,139,108,254,254,169,65,171,9,114,21,
-28,81,18,4,22,252,229,18,112,207,241,114,2,154,213,75,84,194,6,213,36,115,33,58,61,198,65,116,153,118,
-32,199,179,146,93,180,60,233,20,118,141,56,29,163,9,13,141,100,134,131,100,155,59,161,135,204,74,86,252,109,
-161,249,98,164,19,75,191,92,125,19,160,105,207,55,119,23,202,194,68,136,155,139,102,60,5,69,32,115,115,168,
-150,16,65,35,164,122,110,89,223,28,62,21,109,169,4,209,186,12,3,81,146,82,242,84,60,94,194,15,192,24,
-147,134,233,101,224,135,11,253,141,213,252,126,217,28,175,126,167,70,33,168,230,200,227,38,42,117,112,132,210,32,
-219,233,71,58,8,31,166,11,123,88,191,98,90,114,101,230,41,236,238,0,197,22,12,225,109,110,23,222,148,194,
-13,208,206,117,106,119,1,49,242,167,92,230,215,5,22,155,27,187,130,145,154,213,142,196,80,146,170,99,91,78,
-91,245,244,163,184,77,155,225,37,186,9,12,0,239,227,191,138,161,210,115,28,22,95,198,151,194,137,117,127,142,
-233,171,96,214,28,27,42,9,202,133,17,45,240,21,75,119,185,185,67,214,84,57,56,190,233,178,192,54,17,77,
-151,18,128,68,222,40,199,43,237,49,25,60,51,57,101,210,139,133,75,86,240,101,178,40,135,211,16,196,42,64,
-218,156,125,177,236,77,100,56,193,89,106,92,63,193,112,56,242,123,39,225,141,88,143,181,41,24,68,40,87,240,
-144,210,94,135,40,5,182,250,72,189,54,171,54,182,126,193,184,50,49,240,152,155,184,177,207,158,52,19,241,243,
-231,70,74,241,165,93,73,242,146,149,159,199,32,2,207,39,132,203,128,223,197,53,57,128,140,186,236,165,180,243,
-114,220,79,125,237,92,177,73,186,155,135,203,64,211,165,134,3,245,114,169,121,164,139,186,213,194,212,198,100,148,
-82,181,192,14,132,187,202,83,80,37,7,101,115,194,83,11,17,97,120,190,110,7,220,169,39,84,39,9,66,137,
-101,138,173,157,156,181,235,55,66,144,59,180,69,68,80,178,144,190,205,254,142,14,23,231,93,64,134,157,249,236,
-89,177,218,188,88,230,159,78,36,13,150,27,118,167,216,63,15,135,43,224,18,59,28,77,67,104,221,53,222,14,
-208,253,171,150,74,248,185,125,69,130,32,130,49,189,142,90,28,160,167,89,0,53,72,169,248,200,137,201,13,84,
-224,172,111,70,13,66,92,214,158,249,226,72,170,218,156,137,56,116,70,97,208,101,12,231,22,122,175,9,54,200,
-183,116,119,196,142,52,131,180,135,150,162,5,188,152,186,84,49,241,180,79,216,87,5,193,107,83,236,77,23,119,
-237,165,136,201,45,186,153,151,231,151,192,140,199,164,12,33,91,26,220,168,206,128,189,132,97,144,204,111,203,88,
-222,79,170,238,156,191,28,111,167,97,41,200,237,80,128,19,231,238,229,225,134,214,176,247,201,242,166,129,250,227,
-238,151,233,79,146,145,84,77,51,45,78,139,187,229,224,216,144,234,164,32,152,119,93,224,173,86,245,214,184,42,
-109,183,28,222,14,120,237,118,201,9,228,113,148,215,247,81,118,202,37,53,82,165,167,83,161,196,211,247,41,218,
-202,25,69,170,61,235,57,188,49,73,197,118,221,224,240,83,212,210,62,54,40,243,41,5,55,168,132,103,249,34,
-178,144,58,174,149,103,79,118,72,241,29,66,242,198,194,79,106,149,185,13,225,28,209,48,208,51,186,69,202,89,
-200,245,185,229,164,197,10,197,73,179,186,91,234,87,19,28,117,130,117,49,88,45,23,235,136,108,110,67,32,73,
-74,96,121,65,64,251,243,135,45,56,205,28,34,0,159,142,168,170,251,253,80,167,215,165,9,49,65,11,154,202,
-40,60,191,208,110,90,217,176,117,54,65,151,89,163,20,205,75,112,74,167,134,201,19,223,30,155,113,85,159,43,
-104,236,227,240,85,55,55,204,124,223,106,237,8,2,59,16,255,180,119,220,93,138,227,190,255,247,83,232,151,223,
-245,59,7,108,28,200,108,239,189,247,190,155,217,201,13,243,142,5,94,166,207,167,63,203,10,70,194,217,169,183,
-203,21,30,143,98,197,13,91,150,37,89,146,25,119,228,153,120,193,124,215,193,99,40,158,12,231,194,145,169,95,
-91,57,39,131,144,53,178,249,92,94,18,140,190,229,204,52,54,118,197,223,172,4,5,144,195,48,40,197,50,224,
-41,92,72,34,128,50,97,39,100,220,151,152,123,39,73,113,106,8,11,164,139,168,191,212,155,90,173,248,14,34,
-51,96,240,42,23,110,248,0,194,146,15,188,136,44,206,171,8,84,159,53,241,68,168,129,155,252,0,26,149,2,
-90,91,2,250,158,192,116,228,48,14,154,55,107,23,98,18,19,76,73,59,252,142,32,161,159,111,106,181,73,13,
-15,237,71,79,234,255,26,160,97,50,142,37,192,114,9,10,127,255,21,2,172,78,179,227,138,176,131,205,79,206,
-217,27,232,107,18,45,128,203,178,218,111,61,55,187,133,1,67,167,111,202,253,218,210,12,0,238,87,95,27,14,
-80,230,185,35,90,119,169,48,174,155,179,227,209,96,23,59,1,222,249,195,181,172,45,24,112,31,57,152,54,228,
-46,11,107,211,61,48,91,93,14,115,27,7,232,76,117,240,5,29,33,70,179,153,24,23,67,9,2,160,75,193,
-124,148,65,153,185,37,115,35,132,22,138,128,121,255,126,186,1,141,34,80,249,101,20,130,34,133,113,172,131,53,
-66,225,130,142,141,221,212,250,72,249,248,145,174,238,37,48,26,214,193,212,18,22,167,232,39,31,179,233,103,190,
-182,91,114,237,146,78,70,194,24,69,65,103,186,61,135,36,161,19,222,254,27,215,136,14,43,119,234,138,1,194,
-183,34,160,182,60,229,129,8,194,87,77,164,175,133,47,193,107,91,101,222,10,83,59,17,128,169,140,34,59,78,
-144,142,67,192,76,163,97,91,145,111,83,180,16,199,231,175,86,197,42,20,195,21,192,161,1,12,211,227,168,95,
-213,58,239,49,35,34,119,140,158,17,76,30,4,192,212,170,25,102,204,84,147,243,163,138,106,118,149,174,225,243,
-17,144,47,18,143,199,117,182,53,198,30,206,7,155,130,126,41,66,47,218,45,100,134,128,109,15,235,136,40,251,
-108,100,227,58,203,81,183,50,11,222,107,17,232,207,9,253,16,214,43,195,152,134,106,217,204,99,121,35,81,75,
-78,99,114,94,12,185,208,167,137,237,66,108,20,222,1,10,177,202,228,12,171,226,42,200,119,14,150,191,180,65,
-161,248,144,69,219,142,172,79,14,99,8,174,55,105,130,217,30,81,117,162,5,36,230,187,168,63,155,184,89,127,
-159,156,151,83,44,166,84,140,174,136,254,40,70,23,7,83,246,209,85,204,146,98,72,69,237,49,246,60,193,152,
-139,251,34,143,140,202,216,140,59,241,236,16,43,36,48,231,56,123,175,101,202,99,59,111,229,177,116,35,224,91,
-174,147,20,51,200,211,174,41,44,88,191,159,122,21,71,174,220,71,15,2,12,249,86,252,41,51,249,15,6,107,
-83,182,0,160,74,100,38,148,248,51,94,147,106,106,174,141,217,186,175,197,174,188,4,218,0,190,172,178,241,166,
-124,86,208,191,103,227,21,100,90,107,28,113,8,125,74,162,152,100,87,145,202,33,105,61,151,132,40,126,63,253,
-156,212,180,115,121,14,12,108,237,79,115,100,70,182,227,202,50,7,155,136,169,181,184,97,27,207,212,34,154,55,
-113,182,102,14,156,237,81,215,150,182,97,109,225,239,249,174,45,201,213,22,149,27,72,239,86,150,112,134,18,53,
-127,170,167,122,224,222,146,253,68,172,190,153,53,49,153,55,70,112,217,241,3,167,34,158,81,146,68,70,19,41,
-60,243,62,20,177,196,231,71,163,135,230,95,74,15,131,15,20,159,39,163,211,30,170,22,84,174,180,101,82,72,
-39,181,254,212,37,7,109,47,5,48,160,19,83,95,72,39,88,94,117,94,207,74,24,75,91,118,22,164,123,253,
-180,173,155,232,216,116,166,36,119,130,11,54,17,68,110,88,238,140,29,31,88,174,208,196,75,26,39,43,20,49,
-212,197,48,236,172,55,177,55,162,161,135,3,87,174,4,23,116,28,138,213,98,109,216,208,208,130,152,218,127,23,
-49,93,16,211,39,20,246,114,95,14,147,178,28,89,60,105,39,135,145,28,100,193,152,52,107,8,110,177,1,201,
-89,148,11,137,20,227,253,68,210,72,38,22,101,33,12,197,33,88,245,230,241,33,99,245,230,1,154,178,221,93,
-47,111,183,165,100,180,255,16,201,160,21,13,236,124,36,184,203,52,248,107,8,240,110,5,1,142,197,105,26,88,
-252,87,225,50,134,73,151,124,168,23,152,9,9,226,85,35,142,76,48,41,82,120,7,75,2,23,139,121,131,85,
-70,13,137,54,73,51,40,130,205,154,133,197,52,70,82,12,9,109,50,229,131,25,162,28,209,34,217,84,99,47,
-228,160,227,240,141,198,136,93,224,111,175,112,147,88,37,53,166,150,43,231,175,175,85,159,183,139,202,169,48,40,
-211,65,101,29,114,222,93,219,112,59,227,245,39,208,130,39,15,111,93,191,254,132,149,21,179,72,141,196,50,235,
-183,66,174,171,104,201,245,236,214,1,184,53,40,150,203,193,164,15,147,120,79,192,130,255,200,166,165,190,74,244,
-33,210,252,124,154,92,92,142,152,27,95,103,62,25,212,128,203,117,152,44,24,151,77,53,11,13,197,18,244,85,
-38,84,20,88,72,197,219,44,193,197,94,203,32,194,95,129,112,170,241,105,30,41,233,75,182,165,134,221,158,59,
-212,144,239,76,228,119,212,144,71,184,220,128,7,5,215,206,55,63,254,248,142,64,193,35,154,146,222,163,25,29,
-154,39,143,113,167,18,128,229,85,81,227,140,51,175,128,214,9,241,15,9,212,87,182,254,181,29,126,73,143,88,
-26,5,105,181,47,71,130,123,154,5,69,142,68,74,143,132,251,175,20,220,21,191,234,114,121,92,46,143,70,27,
-77,171,229,248,236,77,206,216,155,124,206,236,141,140,106,128,51,129,209,156,26,187,170,19,112,157,236,36,176,75,
-137,106,199,183,91,185,164,17,76,79,15,180,126,222,43,50,200,106,221,137,118,95,91,65,67,50,15,82,51,119,
-162,34,189,145,36,85,145,143,79,72,86,22,100,133,134,241,232,116,5,148,18,192,126,197,25,84,102,221,15,211,
-83,146,164,245,205,54,238,155,69,181,130,76,9,220,186,250,245,200,209,130,28,225,142,178,160,73,11,154,52,23,
-154,4,74,205,79,50,152,72,61,240,156,172,230,255,115,84,102,65,101,22,84,102,65,101,14,56,228,126,54,198,
-64,236,101,179,10,242,120,234,198,2,250,85,249,59,145,145,117,71,71,74,188,2,119,136,183,65,166,227,106,148,
-0,221,126,122,46,113,215,237,22,195,63,146,102,2,136,22,112,52,60,76,75,69,163,38,157,138,99,5,62,83,
-112,69,177,64,97,178,46,177,195,255,58,226,103,17,83,132,30,219,155,189,186,239,44,205,117,39,28,35,105,7,
-53,189,12,252,231,93,244,150,24,248,12,224,51,79,179,1,207,166,29,88,15,124,14,85,87,39,243,41,202,103,
-52,214,71,89,168,245,217,118,85,168,112,230,32,43,131,206,150,141,20,238,189,25,88,7,178,254,76,46,12,44,
-209,23,196,57,86,75,79,148,164,24,4,28,48,114,165,36,151,97,37,108,140,224,26,97,108,125,197,45,40,184,
-81,110,64,225,79,250,241,241,178,195,62,127,61,163,251,130,207,163,170,252,159,84,91,159,4,181,12,67,173,121,
-31,98,6,218,89,7,121,7,31,202,93,216,83,162,83,214,86,151,77,176,251,117,51,227,105,101,158,231,12,83,
-92,186,223,109,54,161,204,0,67,29,209,199,18,102,241,143,119,180,123,212,78,96,199,184,127,162,19,216,197,180,
-117,223,46,45,76,39,229,102,252,111,98,103,246,245,93,187,103,211,204,187,37,160,95,148,95,171,93,101,32,195,
-247,122,39,237,89,149,102,152,250,148,246,180,74,115,235,45,246,117,71,165,237,37,180,127,215,133,75,229,224,63,
-218,248,242,64,124,186,39,13,86,77,109,176,90,24,3,102,58,153,206,233,38,187,132,158,43,57,208,39,61,192,
-76,228,75,96,20,26,231,160,139,6,58,187,185,210,105,39,3,250,172,141,115,192,136,166,238,249,35,54,187,158,
-102,153,171,189,221,1,163,44,186,142,161,101,124,59,71,118,12,223,209,97,119,182,149,173,99,118,229,202,129,117,
-255,23,115,43,44,7,109,149,225,59,224,138,52,48,111,21,209,230,40,126,210,7,130,208,53,249,188,115,91,198,
-107,46,207,159,250,19,178,188,1,176,11,153,0,0
-};
+const uint8_t ELEGANT_HTML[39251] PROGMEM = {31,139,8,0,101,163,230,102,0,255,237,189,121,159,219,198,145,55,254,183,243,249,228,61,64,204,122,68,106,0,12,110,30,51,148,87,146,173,88,187,58,252,88,146,55,137,60,241,98,72,144,132,5,18,12,0,206,161,17,243,218,247,91,213,141,147,152,25,142,19,103,127,207,243,51,79,160,187,186,186,186,174,174,62,0,156,60,152,198,147,236,106,29,40,139,108,25,61,254,253,239,78,232,95,137,252,213,124,220,9,86,29,164,40,202,201,34,240,167,124,132,227,101,144,249,202,100,225,39,105,144,141,59,239,223,61,215,6,29,229,168,150,187,242,151,193,184,115,30,6,23,235,56,201,58,202,36,94,101,193,10,208,23,225,52,91,140,167,193,121,56,9,52,62,81,149,112,21,102,161,31,105,233,196,143,130,177,169,27,21,108,89,152,69,193,227,167,126,150,5,201,149,242,205,114,19,249,89,156,40,111,222,61,81,222,175,167,126,22,156,28,9,16,208,157,78,146,112,157,225,104,182,89,77,178,48,94,41,233,213,106,242,109,56,157,6,171,103,145,159,166,221,158,114,253,251,223,125,1,98,210,76,153,134,231,166,50,86,208,248,205,18,164,233,243,32,251,38,10,232,240,233,213,139,105,247,225,58,137,231,73,144,166,207,226,104,179,92,61,236,29,87,75,90,183,149,244,185,242,244,233,38,203,240,199,37,81,54,156,41,93,170,83,159,16,41,47,195,52,211,137,43,126,184,74,187,15,23,76,228,195,158,32,80,225,42,244,52,187,138,2,125,26,166,235,200,191,26,63,60,139,226,201,199,135,200,222,42,65,148,6,183,64,174,226,85,192,128,191,255,29,62,130,232,248,44,13,146,243,32,1,225,171,224,66,121,181,201,124,162,242,141,76,238,130,55,227,199,140,243,139,29,174,161,1,91,250,241,41,71,41,184,139,118,127,237,103,126,55,9,32,148,240,60,248,206,207,22,220,0,69,201,32,44,62,80,72,244,168,30,156,92,227,32,64,245,254,133,31,102,202,44,200,38,139,122,81,212,64,5,136,81,57,184,30,127,236,129,36,38,75,50,31,53,22,72,10,176,159,211,120,5,58,149,163,35,229,59,210,203,178,62,63,85,254,227,237,155,215,132,32,9,178,77,178,98,12,36,76,176,70,81,182,202,196,7,33,74,55,72,146,56,145,212,43,192,67,181,197,96,42,167,119,31,62,39,114,21,62,25,61,84,197,65,78,176,196,187,218,68,17,167,84,217,158,172,163,9,200,237,102,151,153,170,156,251,81,206,101,8,46,87,159,191,109,160,218,111,131,40,152,64,179,159,68,17,84,239,97,79,159,197,201,55,62,56,180,46,10,8,206,172,245,44,184,204,158,9,123,210,179,36,92,118,123,122,184,154,68,155,105,144,82,53,61,229,224,64,89,235,43,64,73,173,124,27,158,69,225,106,222,83,4,22,165,53,87,143,55,48,177,111,223,189,122,9,114,65,168,104,9,139,30,223,139,112,53,141,47,244,120,21,197,254,148,218,83,104,75,20,100,10,92,193,244,194,79,32,220,206,87,29,98,45,37,206,194,100,73,137,121,90,174,45,15,143,254,24,100,207,101,230,139,213,44,70,99,179,69,176,234,10,201,74,29,100,107,65,130,48,8,200,14,124,132,20,100,49,229,135,32,73,161,130,144,4,1,233,121,93,108,164,57,240,183,57,85,47,190,206,225,114,66,5,28,132,244,5,183,240,31,240,8,185,89,233,242,128,77,92,85,174,21,120,172,36,60,3,79,211,17,172,97,19,168,101,202,243,48,2,171,71,202,135,135,236,10,30,158,150,124,62,58,34,103,8,98,147,143,202,38,252,253,239,10,90,242,3,73,80,197,137,248,211,105,183,67,5,58,132,4,62,194,143,222,66,145,252,121,64,108,37,55,204,153,200,59,57,42,60,164,244,174,226,92,33,239,63,238,44,227,233,38,10,224,171,147,56,77,227,36,156,135,43,64,118,115,115,239,246,174,5,155,102,227,130,170,73,18,192,5,75,154,186,29,232,17,168,208,97,212,68,217,113,56,235,206,14,14,102,122,186,89,83,47,144,86,143,187,178,186,53,128,161,82,157,94,79,88,209,49,244,190,43,234,241,149,120,118,155,149,80,109,31,80,60,167,60,71,117,10,55,58,237,250,189,227,86,63,231,143,31,95,151,117,76,168,14,191,7,74,39,58,115,97,60,238,76,22,97,52,165,6,116,122,37,96,72,128,19,98,118,48,125,29,195,212,122,161,158,249,243,215,212,207,161,204,203,23,175,255,179,115,112,16,82,219,233,188,78,209,193,193,180,27,246,182,189,82,73,100,171,212,235,162,178,209,3,67,77,55,103,89,18,4,56,132,66,148,157,24,218,34,89,63,25,95,111,143,165,187,241,97,245,89,48,79,194,236,234,224,0,228,23,103,227,74,78,79,245,65,210,12,238,42,72,190,139,163,112,34,96,235,73,40,80,79,160,82,172,5,111,88,11,168,65,155,52,208,32,109,244,9,212,85,167,157,175,38,36,252,252,116,220,145,30,168,51,218,41,233,163,55,186,90,198,155,221,50,241,50,204,58,163,70,98,10,142,106,66,251,58,234,100,91,112,129,36,122,13,57,249,122,176,206,117,133,142,199,15,140,227,156,57,196,169,99,209,177,192,214,209,38,117,210,219,110,123,232,24,90,204,2,164,177,97,124,254,252,160,219,225,36,52,66,169,2,246,14,14,164,223,91,82,31,241,42,152,134,126,183,211,133,84,193,172,84,155,196,81,156,32,104,161,162,35,54,217,30,148,159,65,131,244,171,66,198,251,153,174,122,147,215,225,252,87,208,184,103,139,96,242,241,44,190,68,29,168,96,242,49,152,162,233,189,209,62,245,36,193,50,134,214,253,67,85,153,189,227,251,148,67,211,190,57,7,0,213,31,172,96,119,157,9,52,235,99,71,173,184,147,95,64,199,189,217,122,163,63,252,69,156,107,195,22,133,243,5,92,69,213,98,159,116,163,222,181,52,211,232,224,32,210,127,250,41,72,209,32,184,132,131,131,55,103,63,195,135,233,232,76,178,152,92,14,58,165,244,205,197,234,187,36,94,7,73,118,165,163,134,168,27,169,157,105,48,243,55,17,48,127,21,233,242,120,20,109,207,253,68,121,57,190,14,46,217,139,142,174,183,91,245,178,118,122,92,117,216,4,13,255,248,228,233,179,175,191,121,254,199,111,95,252,199,127,190,124,245,250,205,119,255,231,251,183,239,222,255,240,95,127,250,243,95,252,179,9,112,207,23,225,207,31,163,229,42,94,255,45,73,179,205,249,197,229,213,39,195,180,108,199,245,250,131,225,225,17,164,54,190,6,189,209,168,192,157,170,211,162,137,233,201,201,244,115,250,248,241,99,219,210,166,91,21,128,201,141,128,4,194,176,0,12,86,48,168,85,5,148,13,60,165,176,56,69,151,73,142,126,60,126,189,89,158,5,137,180,120,101,166,19,25,64,58,232,29,152,94,223,52,135,182,251,185,72,180,156,222,129,99,245,7,150,235,218,158,193,93,9,113,96,58,54,142,167,39,169,30,5,171,121,182,56,158,30,30,246,210,15,211,211,241,76,23,20,116,233,172,151,251,213,20,45,240,97,245,203,167,87,212,121,87,169,43,17,126,56,61,78,31,27,199,169,166,245,166,250,122,147,46,186,175,16,193,234,179,40,6,8,31,10,28,221,222,35,203,245,122,5,114,180,250,140,208,190,139,255,43,78,166,55,99,87,253,177,161,78,64,183,95,210,237,31,30,170,147,195,241,160,55,253,48,1,3,221,211,207,227,244,131,127,122,114,98,57,218,228,75,219,170,212,113,65,200,223,197,183,183,128,234,168,224,127,4,12,62,163,23,13,2,110,174,5,63,168,192,71,5,7,224,107,75,75,190,13,46,247,173,131,218,144,227,167,10,8,185,131,240,47,126,139,208,104,53,239,154,96,149,90,205,63,48,221,70,118,81,191,254,115,28,174,186,29,88,158,186,8,46,239,217,88,106,169,149,83,178,166,17,195,11,4,48,41,194,147,51,168,94,215,87,173,158,90,171,173,104,237,83,63,13,60,231,62,245,216,28,70,16,0,122,40,22,152,233,125,198,193,161,137,195,1,31,89,167,106,136,114,225,137,115,28,130,67,254,163,193,97,248,200,59,25,23,178,25,124,37,105,141,224,9,253,228,73,214,37,21,240,30,117,109,45,236,29,120,118,175,55,146,0,157,49,226,192,22,30,157,49,221,109,108,74,81,77,18,96,228,56,9,186,71,31,254,250,68,251,139,161,13,15,127,60,58,61,10,231,42,202,86,236,168,93,49,39,227,195,67,255,75,167,55,121,48,54,16,234,72,241,69,136,66,166,193,229,27,182,104,65,179,175,153,189,222,1,155,199,58,190,232,90,170,102,61,154,28,14,122,72,62,57,153,60,178,62,183,149,233,245,168,165,26,178,43,210,216,30,95,234,210,233,141,103,220,195,19,129,223,142,139,84,245,249,248,122,147,205,6,163,107,200,19,186,179,211,240,210,67,63,215,207,194,21,134,208,21,176,238,102,21,96,54,98,29,116,131,213,4,29,209,251,239,95,60,139,151,24,80,82,152,27,245,122,196,79,161,14,66,51,219,208,78,131,157,146,18,167,168,176,134,64,32,5,214,112,117,27,197,185,36,102,36,137,20,82,72,79,162,92,10,41,20,103,86,85,146,103,168,30,12,76,123,53,187,157,221,70,250,158,248,69,73,125,150,196,203,103,178,162,110,244,33,61,45,237,101,86,106,30,90,245,98,252,252,248,232,209,3,12,58,30,41,95,7,24,251,44,195,85,64,163,89,127,133,121,9,234,14,149,48,69,188,255,116,51,67,100,69,96,12,250,239,254,38,91,96,178,71,81,158,7,20,83,42,79,206,226,205,199,133,63,13,127,14,22,152,146,202,178,117,58,58,58,66,17,100,234,113,50,167,73,35,20,67,160,17,208,208,95,121,245,226,29,165,28,81,147,222,140,91,100,20,61,24,211,168,29,49,241,43,164,126,254,252,29,255,62,120,128,30,59,196,252,13,81,211,219,150,253,58,1,201,162,4,83,233,167,14,14,168,43,199,16,161,150,170,231,72,16,107,230,72,48,24,104,135,1,238,50,218,37,66,114,34,11,204,24,108,77,159,99,64,145,189,252,166,142,176,128,72,169,233,245,60,208,44,146,187,134,106,64,199,118,195,131,111,209,187,191,208,201,88,32,242,55,234,20,39,80,67,24,122,1,57,81,67,140,64,234,253,178,208,129,175,66,26,244,176,145,224,140,98,106,20,245,147,43,196,250,227,105,195,166,38,189,209,4,125,238,78,34,253,0,252,73,146,248,87,149,176,136,137,22,209,208,4,148,143,30,8,128,48,229,127,148,57,56,168,145,244,96,60,126,143,113,207,128,179,105,148,51,198,144,46,55,47,104,102,174,219,201,56,202,109,143,251,96,96,82,215,128,205,61,173,186,26,155,125,219,114,7,14,130,11,53,24,107,86,31,231,246,160,63,84,227,177,86,100,57,106,54,206,115,6,234,6,182,178,57,73,114,91,217,192,86,146,15,155,211,113,151,126,201,203,211,63,247,164,149,184,69,102,90,78,158,139,160,166,18,190,36,31,214,178,155,55,173,193,201,201,26,61,176,154,124,232,174,15,61,7,233,195,147,19,167,119,104,58,167,227,117,209,180,37,70,114,63,205,102,234,156,254,231,115,117,65,255,139,133,74,35,188,159,194,112,151,202,49,250,56,214,130,103,227,149,250,118,28,168,31,199,177,250,211,56,59,94,141,151,221,149,26,168,177,154,161,206,205,161,113,170,246,85,205,27,24,131,190,55,180,189,30,26,191,236,102,170,0,33,0,243,84,53,225,207,237,193,208,245,28,119,0,136,24,16,84,156,96,8,2,125,156,217,87,61,195,51,13,119,96,14,123,96,237,178,43,106,88,49,128,125,170,90,64,97,26,142,227,90,174,109,27,61,136,162,78,134,195,100,152,125,207,49,7,131,97,127,151,12,151,201,48,45,195,48,6,134,99,181,144,225,49,25,154,233,64,144,182,105,59,230,46,33,125,65,136,227,246,13,119,56,176,119,201,24,16,25,102,191,111,24,182,235,128,133,59,100,12,5,55,204,33,169,138,227,152,160,180,73,135,9,150,18,33,142,101,160,251,222,161,193,4,67,153,27,195,161,225,24,168,4,1,73,147,12,19,44,5,29,104,168,103,216,222,0,16,59,82,1,79,137,16,199,64,67,77,3,109,221,33,3,44,101,126,184,6,152,102,89,67,112,125,135,22,112,21,180,96,64,224,185,182,107,91,144,221,106,60,175,147,114,170,186,64,226,185,253,161,231,154,192,145,1,160,74,9,216,62,36,217,122,67,215,48,61,27,180,198,128,168,81,66,42,228,168,158,99,195,170,250,38,243,100,94,163,3,28,179,12,232,88,31,90,214,183,13,230,72,157,12,16,10,50,250,134,233,66,5,135,104,110,147,12,226,250,80,181,7,160,193,32,201,238,16,65,10,228,64,211,61,195,233,15,108,219,221,165,2,12,35,42,28,3,86,218,31,56,131,93,42,32,125,87,117,189,129,227,160,49,200,223,33,2,40,152,25,230,112,0,193,17,203,155,116,144,220,64,134,57,232,219,158,61,244,88,73,235,100,64,7,65,134,9,94,186,54,132,7,136,29,153,0,9,9,197,1,29,3,211,241,216,92,234,148,64,129,64,136,107,66,63,208,148,93,50,96,9,32,3,78,14,82,31,192,98,118,201,32,29,36,118,152,67,11,60,235,247,109,96,89,141,23,77,169,160,45,168,1,82,33,26,22,53,26,208,16,211,68,36,8,229,115,251,32,149,165,178,216,85,13,15,154,110,15,13,219,112,201,22,2,128,212,232,32,177,216,168,197,181,141,161,139,33,215,14,21,192,65,28,165,252,33,108,142,117,180,78,9,153,130,9,53,239,91,131,33,68,223,66,8,241,195,35,36,174,51,236,179,22,239,208,65,74,10,58,76,99,8,126,25,158,195,94,172,65,9,4,3,69,31,160,166,161,217,7,195,154,132,144,107,0,75,108,119,0,21,179,44,54,150,58,33,164,31,32,4,153,174,101,14,251,236,77,235,132,192,224,64,71,223,51,172,161,57,96,139,173,19,1,37,37,61,135,228,61,199,25,176,110,212,137,32,201,18,21,142,101,14,76,119,64,166,176,35,23,178,22,79,5,75,251,14,8,97,215,81,167,130,180,3,220,24,14,225,207,7,240,12,68,199,85,179,103,33,142,14,7,158,109,192,98,136,142,171,26,29,196,115,82,116,203,27,12,77,7,227,65,144,113,181,235,195,88,211,201,26,156,161,193,86,123,85,35,132,92,24,26,3,13,179,201,114,119,233,160,230,66,197,224,211,29,232,105,159,125,71,157,16,226,57,105,250,96,232,128,90,116,100,45,148,144,228,136,18,195,53,93,139,205,165,78,6,121,117,86,118,144,105,65,252,44,152,58,33,176,7,82,117,4,22,38,148,16,0,77,58,152,235,228,7,209,149,66,125,216,108,235,100,80,31,71,84,184,30,185,24,120,187,22,66,168,183,133,178,195,26,76,215,244,156,22,66,192,85,146,140,227,162,37,70,159,237,165,65,8,153,37,113,4,253,173,101,194,133,0,73,147,20,82,34,87,237,163,61,131,190,69,173,105,18,2,61,36,142,216,144,11,248,46,52,100,117,248,12,17,142,1,216,224,240,45,31,197,227,248,240,35,31,101,227,236,240,39,58,218,230,49,124,62,139,243,65,18,127,138,144,157,227,160,90,0,171,38,234,90,208,46,34,158,108,60,57,236,134,7,201,231,191,135,7,235,222,97,119,69,56,123,135,177,28,193,116,179,147,147,224,115,38,38,181,130,222,97,184,197,156,56,98,170,125,112,174,63,39,7,127,223,27,231,98,177,7,206,191,38,127,221,27,97,24,222,141,48,249,107,55,252,12,26,247,197,201,107,207,105,248,9,203,243,30,157,79,195,121,144,102,121,194,203,114,44,94,27,50,208,178,202,152,71,88,189,108,145,196,23,188,242,252,13,47,168,118,94,68,81,48,247,35,197,79,230,60,247,170,116,14,39,98,20,79,1,122,117,2,171,235,51,182,98,108,201,67,14,63,229,188,175,146,145,60,149,227,145,105,99,92,157,244,70,69,184,143,137,42,156,139,5,1,170,232,253,184,32,92,46,33,124,63,126,210,125,223,83,207,199,17,86,137,110,154,158,142,48,27,189,51,65,44,22,240,105,162,229,233,254,133,121,162,186,44,121,113,107,201,78,190,252,248,142,246,59,96,74,60,92,97,94,157,22,108,199,209,86,253,230,246,178,188,94,221,90,240,221,30,5,191,15,124,172,175,55,75,126,61,22,251,1,80,158,228,138,9,236,101,152,6,221,238,76,77,123,64,73,107,192,211,113,167,131,129,36,101,99,209,51,0,158,105,144,192,56,197,58,114,69,91,176,24,48,254,158,214,224,160,14,1,113,53,197,116,119,79,157,117,167,224,11,45,79,249,211,39,98,224,87,14,150,123,234,235,146,130,235,167,221,206,102,77,88,197,234,44,38,235,145,130,13,42,25,180,64,174,216,34,237,188,100,99,158,118,204,139,213,229,154,102,147,3,113,230,211,90,4,90,143,85,241,77,112,140,173,13,220,182,116,44,246,32,124,13,90,142,47,186,157,183,160,157,42,227,189,41,96,198,4,117,0,187,92,90,150,192,98,113,234,191,143,128,244,40,165,2,95,97,165,48,24,255,219,245,108,123,128,117,128,5,142,210,237,127,247,104,233,244,193,148,54,63,236,216,13,87,195,117,160,33,84,135,50,243,193,90,172,160,202,170,124,89,213,148,55,10,64,213,243,157,12,81,60,175,22,207,119,72,140,32,161,188,236,68,136,42,78,150,180,84,207,156,9,57,233,79,175,94,126,139,233,150,239,3,44,195,210,202,174,142,197,10,76,239,124,247,230,237,187,142,218,225,230,8,238,131,199,161,46,14,91,214,128,114,222,87,150,129,146,30,115,115,61,22,179,230,241,102,53,237,98,132,138,242,193,244,40,193,8,62,243,163,71,166,97,220,178,252,148,99,125,234,39,144,146,216,11,35,246,25,173,15,59,95,118,110,94,239,202,11,254,64,130,173,169,55,23,220,170,88,245,42,155,19,175,114,248,82,113,65,61,68,149,143,168,105,166,15,235,205,103,81,240,255,225,70,109,209,162,120,69,22,117,5,13,204,2,76,21,174,230,65,217,36,110,17,45,100,3,0,218,146,97,62,201,161,5,242,16,68,248,217,38,29,143,49,80,236,253,2,186,59,104,240,62,132,139,146,37,217,178,28,204,185,105,186,100,206,233,102,66,86,80,88,51,111,141,170,145,235,128,220,187,189,234,191,138,94,118,168,69,194,55,221,206,123,214,174,210,138,73,113,146,49,9,64,216,231,59,88,241,241,59,234,184,184,105,255,215,52,68,125,7,111,35,182,156,137,142,59,152,42,66,36,216,18,54,13,208,223,231,34,130,139,199,206,138,53,60,10,136,153,161,116,71,141,212,72,167,61,132,100,126,41,165,99,146,14,62,86,84,65,78,182,115,40,1,182,188,127,139,214,49,126,57,157,169,190,68,33,90,241,135,117,252,64,29,98,62,235,253,216,60,56,120,128,77,135,89,184,142,130,175,186,216,159,152,96,159,203,159,227,13,182,141,97,238,122,21,93,41,194,59,224,56,80,186,52,113,218,195,206,167,8,251,206,104,3,75,22,46,3,29,85,192,143,140,162,15,198,41,147,172,99,151,94,8,44,200,192,50,200,186,219,123,192,179,166,157,91,209,87,80,167,57,70,236,124,160,48,230,111,55,247,93,162,176,216,136,136,66,255,118,51,36,33,126,177,130,247,130,6,254,109,143,213,123,76,79,71,152,172,13,8,234,107,177,58,221,69,5,8,110,0,215,173,173,134,127,34,96,234,208,126,160,85,14,57,125,110,30,191,198,146,129,129,88,189,128,251,35,92,143,220,111,65,235,236,148,70,219,105,208,0,224,43,54,160,81,28,193,132,142,63,29,203,68,72,60,200,126,192,94,211,241,31,121,171,101,101,159,147,216,229,68,182,128,147,71,234,232,44,192,92,105,160,142,252,25,86,33,174,177,181,64,67,12,75,171,32,103,8,56,131,68,67,202,177,60,100,219,25,25,249,41,35,25,161,35,13,167,121,18,111,0,25,253,33,112,131,126,112,182,109,224,214,180,236,2,16,188,77,111,164,116,58,91,218,85,123,141,61,75,129,182,8,104,203,192,200,212,221,99,237,34,56,251,24,102,26,117,213,68,74,160,249,211,159,55,216,13,68,166,119,172,45,227,79,26,250,19,206,25,57,199,90,92,61,171,28,206,80,143,54,243,151,97,116,53,218,132,90,234,175,82,13,59,141,66,132,98,112,242,193,82,219,132,170,6,11,139,2,77,36,168,79,105,247,212,43,127,242,150,79,159,163,188,250,54,152,199,129,242,254,133,250,125,124,134,73,120,245,219,32,58,15,178,112,226,43,175,3,236,95,123,146,96,131,142,250,26,57,202,91,224,87,43,149,116,158,16,106,5,70,134,213,154,111,150,241,207,33,130,130,28,93,158,80,156,191,189,90,158,197,216,224,192,168,170,101,100,51,176,165,108,147,128,80,25,187,141,86,8,71,176,31,145,51,161,239,33,107,70,51,123,123,22,79,175,174,151,8,31,177,130,102,28,87,249,28,174,22,160,50,219,46,146,107,153,66,59,134,72,116,50,39,151,103,22,175,165,212,205,245,229,214,63,59,75,70,23,0,8,186,31,120,139,241,105,239,186,38,46,90,224,75,152,152,17,250,247,32,161,58,225,158,177,83,121,122,124,23,192,118,97,170,11,75,93,216,234,194,81,23,174,186,240,176,246,134,246,177,60,115,178,56,229,162,222,8,31,59,193,170,164,55,43,202,225,206,84,172,139,196,171,185,64,43,145,128,237,32,99,75,238,87,253,120,54,133,8,151,107,21,70,44,128,74,253,89,198,171,56,93,99,61,88,125,251,252,21,142,181,239,131,57,54,94,39,234,171,0,129,187,138,36,127,18,171,216,131,10,115,240,83,245,101,136,29,26,92,189,66,208,200,216,36,33,220,254,235,224,66,45,80,137,214,112,251,204,96,185,77,33,179,168,210,230,129,241,229,22,139,239,216,2,183,174,164,246,221,47,107,162,52,142,215,113,138,205,226,104,104,190,115,248,24,29,12,41,105,164,249,216,137,179,26,209,58,55,21,33,108,48,112,120,190,229,72,211,45,151,234,4,110,200,24,167,116,198,113,218,53,243,143,22,157,97,165,133,173,183,106,7,18,35,127,141,120,57,63,216,98,47,39,252,170,26,146,55,82,227,117,54,71,160,183,86,81,61,22,52,85,66,140,157,166,126,141,183,53,201,238,40,122,45,183,69,211,107,249,130,147,228,37,90,212,164,77,255,27,58,95,152,202,26,206,158,60,160,145,183,71,52,64,112,38,195,54,150,20,110,109,9,51,3,79,37,196,7,222,42,41,78,78,229,25,59,225,252,4,188,199,222,190,211,194,94,168,115,247,129,106,18,140,68,169,227,51,127,242,145,248,181,154,74,110,115,77,216,137,1,65,84,51,195,37,186,101,81,249,136,221,225,12,61,88,74,67,251,107,108,88,166,102,142,176,74,28,203,76,232,110,184,194,48,45,156,10,223,142,181,226,248,66,148,206,67,131,235,155,244,101,52,202,169,229,8,72,75,215,225,74,147,13,46,243,120,151,116,53,47,247,41,76,133,108,61,218,58,89,180,182,158,152,58,11,131,104,122,44,169,215,226,217,12,140,27,105,22,92,78,89,141,64,81,49,236,54,100,130,43,69,25,234,194,53,209,227,231,180,221,204,127,82,154,194,89,164,155,37,212,225,234,90,94,64,48,138,208,237,107,33,250,133,45,207,251,252,109,19,103,129,58,141,212,233,84,221,241,91,234,34,81,103,225,28,138,140,105,38,242,37,185,98,109,185,157,104,91,233,149,75,85,195,220,15,162,186,235,50,1,29,194,38,82,17,143,108,208,71,162,118,209,221,82,11,219,52,21,243,125,24,205,86,138,23,214,6,17,147,97,228,66,222,178,113,130,71,164,29,188,197,101,193,62,176,48,207,221,172,235,24,238,10,91,103,71,166,180,152,63,12,39,190,237,207,114,84,237,88,246,65,144,91,79,130,209,120,110,61,215,147,77,130,205,214,163,53,182,75,64,179,182,35,200,128,28,211,52,207,144,91,255,182,225,114,174,166,231,115,245,60,156,6,177,138,240,240,28,158,215,223,76,195,88,13,103,9,194,74,53,192,70,185,169,42,246,83,20,162,100,1,54,125,228,18,83,78,81,192,40,25,29,36,116,153,247,124,228,81,106,42,45,230,167,78,11,140,172,117,59,161,20,135,59,121,168,68,237,95,205,181,203,145,98,28,183,101,92,21,25,108,245,112,228,65,5,184,76,43,225,176,43,0,9,197,105,250,49,184,168,148,224,211,18,152,175,35,162,108,179,122,142,124,121,190,246,87,148,91,158,32,75,158,96,27,243,66,251,20,163,207,144,41,136,36,225,238,181,116,229,175,161,147,73,56,201,176,51,8,91,250,225,78,46,67,248,184,43,1,54,79,48,44,129,235,210,104,79,140,86,116,82,18,73,145,123,30,250,55,103,102,241,78,30,216,134,237,20,81,65,12,122,219,69,48,213,62,97,199,75,158,182,66,60,15,178,96,253,100,131,205,84,201,241,29,224,68,92,163,148,167,147,63,133,219,35,71,84,77,17,190,73,106,134,98,172,47,119,243,132,134,43,127,152,205,102,149,76,153,154,204,207,186,238,80,193,34,131,98,57,158,114,164,232,216,135,180,131,66,122,105,197,192,251,15,88,184,207,69,78,32,55,228,221,154,44,106,15,166,59,217,103,209,6,68,201,99,236,48,95,72,89,138,20,138,213,19,31,161,119,41,151,43,214,156,60,97,177,9,10,61,20,41,232,107,96,88,133,116,168,59,175,228,166,193,58,244,243,147,41,182,247,22,100,75,18,208,203,113,114,141,174,34,113,135,192,34,171,73,105,145,177,67,114,145,179,75,123,145,85,111,68,145,156,59,177,102,122,179,149,101,134,104,46,58,164,60,233,55,167,240,155,83,248,205,41,252,230,20,224,20,244,52,209,104,58,235,186,80,102,255,12,131,71,4,211,152,196,145,35,238,60,236,160,195,34,178,203,163,63,141,82,177,210,150,96,139,253,197,72,196,36,199,152,108,90,99,32,56,201,104,111,35,189,177,16,179,64,220,202,26,78,209,227,69,226,175,27,243,57,91,61,175,121,151,150,45,205,55,241,168,178,204,203,83,182,186,24,76,106,70,62,170,4,170,40,152,101,72,160,63,58,101,233,224,156,255,203,4,75,38,232,46,86,11,183,152,253,95,3,134,6,162,128,160,19,139,79,100,238,39,205,53,174,63,241,144,244,114,228,2,98,121,134,237,4,50,134,134,207,228,170,45,9,76,121,94,35,207,201,115,172,70,70,89,198,105,228,152,34,35,42,139,112,139,242,2,73,153,94,107,200,50,171,80,70,77,40,200,66,70,81,132,50,236,34,185,168,154,147,75,248,26,120,153,92,3,151,100,102,90,209,100,78,149,208,28,233,214,227,222,45,38,179,105,144,85,164,138,211,45,174,211,8,46,139,68,58,201,33,181,90,78,37,109,171,11,157,171,135,193,250,2,132,75,181,149,100,32,229,71,221,45,18,61,43,79,46,210,76,204,70,112,218,133,54,196,132,39,105,165,85,38,253,248,193,26,192,169,254,120,154,103,209,153,200,176,141,106,6,159,201,12,171,150,65,103,148,49,195,194,187,76,163,176,126,251,239,31,131,43,30,42,164,10,13,96,161,117,215,229,248,94,56,130,46,54,158,78,131,57,230,193,117,127,133,177,55,219,18,64,197,9,217,3,157,42,102,170,16,103,48,249,28,174,102,116,197,42,88,33,198,43,232,173,120,32,211,28,215,96,22,159,102,21,52,98,91,49,44,197,69,133,232,255,57,67,12,245,196,32,190,153,218,76,0,109,197,120,182,142,176,49,56,22,248,154,137,205,17,52,107,3,28,225,197,53,31,76,67,242,40,108,250,241,133,204,68,231,209,204,68,18,86,20,160,55,24,38,227,90,64,244,213,104,52,15,175,120,228,156,142,68,210,86,167,233,220,112,118,149,131,20,167,114,106,184,9,118,22,100,23,1,244,172,9,199,46,45,207,221,234,115,132,30,230,53,126,71,185,234,80,138,37,82,202,4,91,36,244,203,20,135,83,132,41,241,20,11,66,7,86,20,233,38,41,240,216,164,163,33,94,164,69,57,72,52,111,0,200,74,242,252,203,38,130,188,78,145,154,103,150,211,171,50,67,155,114,244,146,231,139,97,191,72,43,64,126,252,240,227,31,176,7,109,104,250,208,241,106,232,88,116,71,102,125,230,142,66,12,203,81,44,87,177,40,192,192,84,90,183,165,24,54,195,87,107,56,51,206,236,179,193,61,106,192,54,100,197,236,35,146,25,56,123,85,66,29,49,118,126,25,251,55,193,26,42,216,163,140,175,187,87,5,50,5,117,180,87,161,91,59,160,246,77,160,246,14,168,115,19,168,3,208,57,197,43,129,230,21,173,67,32,87,105,89,115,178,143,90,103,247,149,33,181,175,222,182,162,28,183,107,222,100,218,221,104,111,98,90,43,98,116,181,123,227,117,128,215,69,204,234,154,183,226,229,0,100,127,172,46,116,84,126,111,196,74,102,156,7,68,185,89,173,47,169,239,149,169,162,187,22,189,111,30,58,201,174,58,239,146,1,111,213,193,219,160,75,96,167,14,76,254,162,1,43,92,8,64,221,6,168,112,72,77,232,220,77,161,128,215,44,208,10,47,193,175,224,230,242,44,142,13,234,216,243,216,38,199,14,240,146,45,101,76,210,132,47,216,2,229,170,163,111,197,94,0,23,130,16,192,44,140,29,232,92,68,73,133,53,13,46,240,228,186,236,17,248,88,76,204,229,157,129,88,14,92,86,86,66,244,129,168,171,190,128,88,197,118,153,86,193,91,160,25,148,65,150,184,146,125,35,177,203,133,3,215,64,168,137,123,51,4,201,4,19,226,205,201,255,34,67,214,117,134,217,206,143,66,201,249,188,162,230,165,110,211,64,203,168,106,117,21,148,244,154,207,217,14,177,235,246,46,108,48,16,207,85,6,53,227,187,25,225,240,110,132,102,31,227,79,197,30,238,129,16,61,46,92,197,157,24,109,71,193,94,108,101,88,235,14,90,81,98,32,186,15,66,11,212,121,3,250,220,133,240,42,136,48,46,130,143,190,27,39,249,48,3,157,227,221,156,148,72,247,34,20,77,71,87,120,55,161,205,222,164,142,147,250,146,28,194,51,138,137,116,221,67,105,82,70,30,146,9,247,153,138,216,85,164,96,226,133,47,158,167,168,44,78,112,173,107,221,213,170,213,174,149,39,237,43,43,43,50,21,171,39,17,175,153,126,164,45,126,5,102,236,224,32,243,205,55,41,140,38,155,51,76,149,156,5,159,176,192,217,213,29,12,58,117,92,25,211,171,22,153,210,80,152,96,117,211,77,17,171,210,252,248,143,197,24,83,206,151,231,97,93,193,45,153,208,43,87,56,203,81,169,68,193,163,77,140,15,40,214,191,19,141,232,18,40,208,146,165,105,164,185,111,97,118,156,149,178,11,205,185,179,76,205,205,200,114,208,200,59,203,201,144,176,90,172,26,151,222,89,190,61,110,149,152,68,230,190,56,42,209,105,173,188,240,41,24,112,221,137,104,239,208,206,64,52,97,218,248,214,252,207,110,104,151,211,33,3,139,61,9,248,39,5,30,185,230,148,186,141,69,251,187,149,167,197,46,81,238,87,177,42,89,41,5,240,15,241,206,53,187,185,249,134,182,183,182,83,139,145,57,205,42,21,146,206,189,207,192,24,113,198,13,49,239,160,44,87,198,168,181,18,255,164,72,149,151,187,11,234,56,200,134,63,30,113,242,222,3,137,218,116,235,45,202,38,43,203,151,166,105,116,44,107,202,215,218,225,20,20,222,3,133,123,90,149,235,245,141,181,108,246,28,18,23,79,216,226,118,9,37,189,109,179,188,5,77,229,156,115,143,163,7,163,145,85,157,124,238,181,231,113,179,171,51,202,123,84,130,233,209,73,215,65,219,14,111,171,175,89,161,172,169,178,211,160,181,176,200,195,94,196,90,230,78,170,72,80,203,249,233,82,32,92,160,77,246,162,154,82,242,119,79,182,55,40,220,145,61,39,23,10,125,223,170,110,80,233,59,235,34,215,122,239,186,218,93,104,179,174,117,0,255,47,239,30,244,119,62,211,228,25,219,238,175,49,108,189,163,74,17,79,222,167,78,203,82,112,101,155,210,175,207,51,236,93,103,213,141,139,101,181,253,122,214,221,197,56,222,29,80,14,11,138,60,186,250,92,6,125,37,120,69,183,43,171,119,176,34,57,213,88,138,140,207,145,65,75,118,101,178,88,207,147,201,127,110,164,19,30,94,213,248,83,37,67,44,241,229,57,213,34,98,177,111,79,70,229,209,192,61,58,220,61,122,252,27,58,220,29,39,204,4,178,49,72,242,90,188,242,47,240,199,55,161,21,14,250,55,215,124,95,215,124,43,63,217,171,192,169,221,195,157,225,134,4,60,122,196,157,15,238,116,103,55,85,43,60,203,61,235,197,208,141,198,153,102,223,186,165,222,81,152,118,117,190,43,37,255,22,54,66,19,167,22,93,251,235,253,72,219,99,247,180,5,219,83,224,70,237,218,104,113,199,16,110,171,210,246,237,51,123,114,159,42,221,129,130,126,208,171,205,71,220,171,202,192,9,188,224,236,62,85,90,214,0,156,69,199,123,199,244,237,77,181,114,207,136,110,98,255,26,251,152,32,113,169,123,250,37,213,149,179,199,55,84,136,233,227,221,178,243,218,236,124,94,114,159,233,212,150,233,249,219,53,78,84,213,212,182,61,122,236,54,109,187,171,170,124,90,106,255,106,218,38,168,238,172,166,226,181,123,215,183,237,137,221,45,205,211,24,213,133,11,73,234,109,51,94,173,11,22,141,121,154,155,43,106,152,192,45,211,65,237,154,191,87,69,220,235,222,93,67,123,111,186,87,13,229,4,83,107,53,152,108,218,41,87,217,221,41,52,131,174,41,107,217,58,42,246,11,85,128,219,229,224,66,14,136,231,76,216,107,133,248,150,114,109,109,184,129,150,127,53,25,57,23,113,179,151,114,158,206,220,133,187,125,120,125,147,171,25,220,140,72,184,129,188,7,168,163,217,195,74,219,250,129,91,173,244,182,17,248,61,250,159,253,134,224,55,213,206,221,113,173,221,213,250,247,27,6,182,180,252,174,94,254,230,241,231,189,106,222,103,0,90,169,187,26,227,72,157,111,13,176,6,133,123,222,139,12,144,224,209,52,245,109,67,211,251,81,33,226,173,123,146,65,131,57,195,84,220,219,194,173,223,255,142,159,193,80,187,118,68,225,11,176,142,113,151,114,177,1,5,91,47,56,200,23,59,188,149,202,42,146,34,150,145,142,9,141,37,145,240,170,144,66,251,112,142,21,185,165,74,49,244,129,29,44,41,170,213,77,131,14,142,149,234,146,144,66,151,11,201,164,127,136,6,187,70,131,213,160,193,224,170,139,131,95,135,6,92,18,40,47,2,196,245,128,71,197,195,44,78,232,170,49,133,111,200,32,110,11,76,238,229,131,12,103,78,149,124,214,149,111,238,44,58,143,15,178,23,60,85,104,33,179,243,24,247,212,255,226,100,97,237,60,161,2,149,88,168,226,139,147,245,227,147,133,253,152,46,184,223,200,231,85,224,244,228,104,45,50,243,186,229,130,159,66,123,176,176,141,84,115,59,143,233,194,20,186,172,20,215,47,158,207,21,56,57,186,225,61,174,201,213,173,142,114,185,140,86,40,69,119,64,196,13,16,47,46,46,244,11,155,110,128,120,68,55,62,59,2,124,71,161,103,111,60,141,47,199,29,26,180,12,28,131,6,23,29,37,191,32,216,196,51,54,196,132,253,184,99,227,184,202,130,122,59,59,184,104,23,156,67,94,157,223,199,226,193,32,226,117,178,198,117,238,116,113,106,164,37,184,201,49,158,27,130,235,66,227,233,180,195,105,184,165,250,6,55,23,95,225,169,5,176,130,14,221,21,98,105,26,186,173,184,166,62,140,208,131,232,182,230,234,195,243,190,55,1,153,122,31,186,48,84,108,29,17,34,190,142,110,69,125,156,59,186,247,105,105,14,117,87,49,77,148,193,142,41,205,163,50,195,136,142,149,126,145,169,215,179,113,42,0,116,115,23,164,79,0,78,129,193,211,77,109,224,232,206,68,195,242,179,6,42,52,27,164,209,191,163,15,20,35,210,60,138,35,65,32,23,0,126,197,27,18,208,80,71,97,205,165,68,7,9,17,90,132,44,2,25,234,3,202,246,168,145,67,80,100,131,134,1,90,133,83,84,91,1,243,8,204,37,176,65,29,204,141,48,199,213,199,49,128,92,2,114,52,211,214,250,186,11,16,15,84,153,182,62,16,173,240,60,228,129,60,123,34,169,39,254,105,88,233,230,127,240,241,92,235,123,159,58,164,124,144,86,171,244,218,36,133,187,114,233,38,57,240,115,220,120,79,55,23,154,13,166,224,216,213,221,133,13,207,193,135,67,36,231,199,246,2,16,168,204,236,99,211,27,212,245,124,128,70,125,90,218,38,36,14,36,3,80,180,32,84,246,57,29,126,90,90,104,41,238,117,165,155,19,82,4,208,169,56,26,24,66,114,178,249,96,128,36,40,6,196,166,0,59,29,56,10,210,81,210,70,35,77,71,239,147,192,60,168,136,3,61,50,137,91,184,39,17,128,232,192,35,125,34,105,187,64,13,57,88,144,3,49,126,65,252,70,141,38,56,229,64,227,108,13,212,177,34,226,12,55,121,130,118,176,48,44,104,144,198,36,242,17,213,134,124,36,192,141,67,87,197,17,178,134,68,34,117,116,164,187,134,72,115,197,17,202,0,55,162,97,32,97,104,210,51,19,44,177,44,32,67,178,7,137,161,189,32,134,112,66,110,92,158,65,249,136,57,71,184,208,46,226,15,31,225,199,61,215,160,111,30,184,233,233,222,57,176,50,31,52,27,141,118,161,34,168,196,5,133,242,192,37,178,161,90,164,188,104,22,29,89,125,106,140,130,76,232,17,85,130,4,143,200,147,71,146,241,54,16,64,230,4,71,86,229,224,129,51,108,169,57,187,64,19,52,215,33,234,233,8,16,196,65,83,3,181,22,138,66,55,209,84,210,94,180,6,162,146,242,176,200,168,196,1,73,7,137,125,144,65,4,243,105,153,129,198,16,21,64,5,9,179,237,161,34,46,1,1,247,89,226,44,222,34,3,186,198,250,163,15,65,229,16,64,16,1,255,192,78,197,17,204,135,27,134,102,33,25,234,171,193,188,225,114,32,68,228,224,28,255,30,42,118,22,200,244,38,80,17,82,19,15,223,62,168,118,248,127,0,187,195,189,65,209,34,106,44,50,161,105,26,68,53,212,44,144,36,142,72,46,196,118,230,13,21,194,17,88,141,162,145,9,229,96,149,103,211,196,49,97,180,161,215,226,159,229,5,1,64,18,40,65,255,16,47,41,145,102,146,107,68,25,250,39,239,73,146,65,203,39,224,51,68,4,145,176,91,0,23,217,59,176,219,99,59,129,246,79,132,31,19,148,178,163,33,246,81,10,249,90,58,238,163,26,82,31,155,156,19,112,232,125,8,27,102,2,10,6,200,102,157,71,13,228,188,233,232,211,210,101,243,128,195,1,23,168,56,181,131,140,129,252,20,253,147,46,65,8,100,212,248,210,63,92,226,185,13,13,90,128,54,231,156,12,31,202,68,194,35,91,133,149,33,139,21,69,28,9,242,72,101,153,66,86,85,240,29,58,193,90,63,56,119,209,8,66,5,90,250,68,155,131,162,200,2,255,206,145,8,202,73,105,97,244,84,55,201,140,254,177,71,23,37,73,157,23,166,113,14,153,88,164,206,100,0,172,38,196,109,252,208,64,185,42,9,112,3,242,33,11,131,111,19,189,4,123,12,50,98,82,14,232,62,255,195,33,51,143,185,0,255,83,231,70,76,66,117,194,69,130,2,19,114,193,156,4,56,193,10,196,194,2,161,132,4,226,2,86,58,2,77,100,129,148,64,186,46,14,56,7,244,162,255,17,58,143,4,240,77,102,177,235,3,207,57,129,250,9,113,64,57,232,163,208,34,34,22,122,135,161,51,217,8,75,134,77,75,254,179,79,50,112,76,52,185,244,207,201,192,7,120,220,167,143,88,76,86,45,51,72,74,172,64,180,68,65,62,86,160,65,231,4,193,160,90,217,19,80,139,61,116,227,231,124,8,98,134,231,30,252,222,194,36,89,226,0,138,68,237,19,105,48,222,136,187,19,132,214,250,0,194,130,187,230,4,71,225,4,146,77,4,69,32,10,157,5,245,43,104,25,177,23,67,122,88,22,217,62,236,18,93,18,97,162,44,242,101,184,127,32,25,44,228,200,138,3,129,0,33,52,5,12,57,183,135,160,23,155,209,169,123,2,48,32,224,239,207,233,8,165,73,193,161,164,144,18,105,32,190,164,202,100,78,226,43,217,69,102,47,114,160,7,112,31,164,21,148,66,103,197,151,97,57,95,230,16,60,204,147,61,38,154,66,226,58,7,29,92,21,28,26,228,72,14,149,187,68,86,120,80,69,228,17,251,168,243,97,237,198,30,38,98,12,107,182,201,255,176,27,34,137,60,22,247,137,96,10,89,204,144,252,24,169,40,186,3,217,229,17,201,244,79,157,142,1,197,21,94,142,254,97,49,144,27,122,159,5,106,64,224,64,76,193,17,248,132,67,54,12,184,46,232,43,121,89,234,88,137,16,254,7,98,38,138,136,32,211,17,113,27,245,111,112,69,68,1,209,71,22,73,254,143,236,91,252,147,246,146,209,80,205,212,201,88,200,177,161,10,178,135,38,75,131,70,50,217,84,152,188,170,112,73,32,158,34,34,242,13,14,117,117,228,45,112,134,186,161,173,116,64,166,42,93,16,197,2,56,133,50,80,143,204,71,20,196,9,219,135,15,132,53,121,164,16,187,65,18,198,15,231,116,123,241,47,68,228,206,73,120,210,83,30,55,211,222,112,37,223,41,174,84,183,132,43,124,45,132,194,23,106,148,17,243,201,3,77,195,243,235,248,206,46,226,214,52,138,166,149,185,132,57,68,236,85,187,161,89,123,93,245,221,229,101,109,78,45,60,167,234,158,241,179,154,112,83,26,113,229,113,181,66,64,208,122,94,229,156,134,70,2,172,164,67,222,70,102,119,172,32,198,72,229,158,90,12,218,104,90,67,254,97,40,33,102,40,49,158,106,238,40,86,120,246,166,186,135,66,140,223,242,217,87,12,252,48,218,174,45,125,201,36,177,108,85,61,41,48,84,119,15,209,40,205,85,104,227,166,66,151,177,48,223,4,243,232,182,106,188,67,190,46,174,114,16,40,38,154,79,69,74,65,231,7,57,3,35,211,43,245,55,114,138,198,203,212,170,60,196,93,105,48,170,219,99,28,39,71,108,86,101,192,70,199,181,225,29,77,166,59,249,88,139,152,68,131,54,218,201,214,140,231,69,170,88,51,163,161,164,91,36,17,127,241,112,130,113,135,185,87,75,166,59,249,23,233,82,248,209,102,130,11,199,21,241,151,223,121,0,86,195,35,9,104,204,43,138,4,21,235,91,207,71,248,42,86,242,192,125,235,220,172,36,224,31,29,162,85,77,208,172,31,224,2,95,138,194,159,58,71,64,24,71,87,124,223,20,190,144,4,53,155,104,43,76,23,91,13,45,244,202,12,82,212,137,12,235,220,171,166,161,27,66,124,135,136,152,194,123,155,114,164,33,215,69,129,235,61,27,105,138,34,158,53,198,247,45,108,128,31,237,192,159,28,9,107,169,39,242,13,3,228,195,213,248,182,86,108,76,229,157,150,114,102,202,59,64,42,62,238,100,182,134,124,249,246,251,252,140,136,249,167,14,102,0,228,29,218,58,149,155,31,117,249,70,80,189,154,145,31,213,108,248,228,8,94,164,238,112,112,83,66,190,5,198,109,46,167,113,7,173,189,157,142,195,46,14,6,45,218,82,27,93,214,16,139,155,80,214,230,61,114,52,184,100,77,152,63,38,66,170,51,17,210,125,176,83,174,34,110,117,192,100,215,53,139,230,139,94,148,11,77,92,87,213,176,195,42,14,113,193,84,225,199,118,92,9,69,141,238,174,131,216,49,236,38,43,233,182,106,121,29,149,29,39,18,95,181,138,98,174,37,191,184,246,75,106,115,85,142,187,146,109,97,177,184,243,95,99,106,169,73,166,241,101,29,103,141,179,117,221,217,213,164,183,226,166,123,183,41,82,253,190,124,123,116,148,66,78,162,3,107,209,162,251,185,75,7,147,100,185,187,164,227,127,134,187,4,154,95,230,44,235,187,200,171,94,18,67,5,44,25,226,34,65,31,19,93,236,2,121,140,59,64,36,213,239,35,37,79,67,224,66,135,117,32,249,83,164,97,122,155,192,138,4,137,164,6,68,67,62,26,60,212,128,118,170,227,17,162,247,151,154,35,197,104,136,28,53,77,206,56,45,126,20,243,156,85,125,18,207,230,205,245,4,186,93,85,174,18,84,168,86,85,179,248,46,167,183,233,85,245,118,126,123,106,21,2,162,255,87,181,74,94,72,80,209,169,37,6,66,125,138,208,53,140,143,156,74,247,139,81,2,207,97,98,156,233,60,41,251,92,186,18,96,81,235,150,169,188,102,215,164,79,93,235,240,156,229,94,235,109,251,11,221,48,91,181,161,148,85,221,225,211,165,190,117,87,244,30,207,162,93,163,167,197,100,55,11,191,93,83,234,56,229,13,137,107,172,184,76,219,59,141,106,93,223,69,40,24,240,211,137,253,57,158,188,92,175,171,120,62,238,174,90,190,149,247,224,186,213,227,213,111,54,188,119,215,9,233,237,211,169,53,74,238,104,185,137,5,6,121,149,142,232,69,193,144,202,69,183,141,126,175,88,121,104,225,153,216,75,86,189,214,162,217,121,228,236,216,183,11,105,229,18,111,160,187,129,77,69,103,224,81,207,205,151,58,223,210,115,183,51,74,94,43,219,210,203,236,116,217,117,231,69,47,90,176,161,219,62,55,67,191,38,228,137,96,49,55,45,191,83,116,209,217,211,138,148,210,184,86,153,6,35,38,13,75,202,88,167,118,217,82,99,240,180,19,138,68,243,234,72,171,50,254,200,215,103,101,90,99,189,250,166,129,77,49,76,169,165,182,237,49,168,88,87,101,81,172,141,128,74,70,131,138,38,227,193,63,220,55,143,110,28,200,247,214,134,36,201,63,50,71,131,233,227,252,105,207,39,71,2,232,206,194,208,211,151,97,6,119,243,252,45,22,87,223,126,247,226,249,243,183,55,20,134,199,226,106,238,138,172,126,93,45,251,154,86,154,223,191,184,83,201,34,255,44,136,114,58,242,59,82,228,11,159,76,87,173,250,250,165,248,45,76,175,142,76,120,247,43,61,100,149,117,120,231,209,171,57,115,11,173,150,119,242,80,104,53,28,137,98,239,236,110,21,181,192,122,136,96,215,173,69,214,229,90,122,125,116,95,73,151,67,252,102,74,190,183,82,106,106,91,46,86,228,27,179,0,128,42,44,160,152,92,168,110,254,21,251,124,119,246,72,183,193,84,183,7,43,34,41,191,12,229,195,67,60,204,91,36,229,87,111,201,83,186,250,138,46,190,202,179,249,90,174,106,66,177,214,92,171,36,223,29,95,75,149,39,181,22,138,36,92,167,37,143,112,229,85,94,115,237,234,157,154,213,231,59,12,235,173,172,140,81,218,134,31,164,160,172,143,119,153,206,2,138,88,233,99,202,13,67,74,185,3,11,177,195,191,208,220,42,79,132,223,207,228,208,252,189,154,250,235,146,221,124,234,125,27,237,120,234,253,61,72,110,31,224,125,81,233,166,197,173,160,240,104,66,154,214,72,111,220,4,32,7,170,182,75,247,129,250,117,166,40,209,48,121,218,78,131,220,248,145,95,32,143,117,54,108,22,167,91,160,228,183,14,82,92,166,13,243,40,116,111,106,26,92,215,239,49,77,15,10,199,99,255,31,30,61,4,148,240,137,162,194,127,237,84,103,30,179,221,119,202,147,188,67,229,170,238,157,233,79,120,80,84,39,166,63,41,250,110,153,3,181,254,151,230,64,21,229,41,246,232,41,184,13,116,115,251,204,13,147,107,164,161,255,92,101,40,238,29,222,237,253,255,64,248,64,240,191,42,124,200,15,55,133,150,179,3,188,154,34,69,219,50,213,84,30,3,10,219,180,112,136,189,83,216,22,247,248,127,0,151,167,131,53,178,136,0,0};
diff --git a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h
index 42369c0c..f026503d 100644
--- a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h
+++ b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h
@@ -3,6 +3,6 @@
#include
-extern const uint8_t ELEGANT_HTML[10214];
+extern const uint8_t ELEGANT_HTML[39251];
#endif
From 6cb5fe77a6ede31a83d73d574e868ad19b3c1705 Mon Sep 17 00:00:00 2001
From: amarofarinha <151563493+amarofarinha@users.noreply.github.com>
Date: Sun, 15 Sep 2024 19:16:51 +0100
Subject: [PATCH 8/9] Added wait effect during ESP32 reboot with a loading
animation and 'Rebooting...' message. Displays the latest installed firmware
and hardware version once the reboot is complete.
---
.../CurrentPlainHTML.txt | 66 ++++++++++++++-----
.../lib/ayushsharma82-ElegantOTA/src/elop.cpp | 2 +-
.../lib/ayushsharma82-ElegantOTA/src/elop.h | 2 +-
3 files changed, 53 insertions(+), 17 deletions(-)
diff --git a/Software/src/lib/ayushsharma82-ElegantOTA/CurrentPlainHTML.txt b/Software/src/lib/ayushsharma82-ElegantOTA/CurrentPlainHTML.txt
index 174ec045..c1dd1c04 100644
--- a/Software/src/lib/ayushsharma82-ElegantOTA/CurrentPlainHTML.txt
+++ b/Software/src/lib/ayushsharma82-ElegantOTA/CurrentPlainHTML.txt
@@ -5,6 +5,32 @@
Battery Emulator OTA Update
-