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 + + + +

+
+ +
+ +
+ + +
+
+ + + + + + + +
+
+

+ Settings +

+
+
+
+

+ OTA Mode +

+ +
+
+

+ Dark UI +

+ +
+
+
+

+ Hardware ID +

+ +
+
+

+ 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 -