diff --git a/.github/workflows/compile-all-batteries.yml b/.github/workflows/compile-all-batteries.yml index 280b664f..705e7512 100644 --- a/.github/workflows/compile-all-batteries.yml +++ b/.github/workflows/compile-all-batteries.yml @@ -115,4 +115,5 @@ jobs: # in the build matrix, and using build flags to define the # battery and inverter set in the build matrix. - name: Compile Sketch - run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software + run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software + \ No newline at end of file diff --git a/.github/workflows/compile-all-combinations.yml b/.github/workflows/compile-all-combinations.yml index d4e890d2..57e4860d 100644 --- a/.github/workflows/compile-all-combinations.yml +++ b/.github/workflows/compile-all-combinations.yml @@ -288,4 +288,4 @@ jobs: # in the build matrix, and using build flags to define the # battery and inverter set in the build matrix. - name: Compile Sketch - run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software + run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software diff --git a/.github/workflows/compile-all-double-batteries.yml b/.github/workflows/compile-all-double-batteries.yml index d858e240..f5807acf 100644 --- a/.github/workflows/compile-all-double-batteries.yml +++ b/.github/workflows/compile-all-double-batteries.yml @@ -90,4 +90,5 @@ jobs: # in the build matrix, and using build flags to define the # battery and inverter set in the build matrix. - name: Compile Sketch - run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -DDOUBLE_BATTERY -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software + run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -DDOUBLE_BATTERY -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software + diff --git a/.github/workflows/compile-all-hardware.yml b/.github/workflows/compile-all-hardware.yml index eafdf496..8c3c7152 100644 --- a/.github/workflows/compile-all-hardware.yml +++ b/.github/workflows/compile-all-hardware.yml @@ -90,4 +90,4 @@ jobs: # in the build matrix, and using build flags to define the # battery and inverter set in the build matrix. - name: Compile Sketch - run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software + run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software diff --git a/.github/workflows/compile-all-inverters.yml b/.github/workflows/compile-all-inverters.yml index 5b421c9e..2bd79df2 100644 --- a/.github/workflows/compile-all-inverters.yml +++ b/.github/workflows/compile-all-inverters.yml @@ -105,4 +105,4 @@ jobs: # in the build matrix, and using build flags to define the # battery and inverter set in the build matrix. - name: Compile Sketch - run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software + run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software diff --git a/Software/Software.ino b/Software/Software.ino index 1ea9b3c6..5b847925 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -9,6 +9,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "src/communication/Transmitter.h" #include "src/communication/can/comm_can.h" #include "src/communication/contactorcontrol/comm_contactorcontrol.h" #include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h" @@ -88,11 +89,6 @@ void setup() { &logging_loop_task, WIFI_CORE); #endif -#ifdef MQTT - xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, NULL, TASK_MQTT_PRIO, &mqtt_loop_task, - WIFI_CORE); -#endif - init_CAN(); init_contactors(); @@ -110,9 +106,8 @@ void setup() { #ifdef EQUIPMENT_STOP_BUTTON init_equipment_stop_button(); #endif -#ifdef CAN_SHUNT_SELECTED + setup_can_shunt(); -#endif // BOOT button at runtime is used as an input for various things pinMode(0, INPUT_PULLUP); @@ -126,6 +121,12 @@ void setup() { }; // Start tasks + +#ifdef MQTT + xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, NULL, TASK_MQTT_PRIO, &mqtt_loop_task, + WIFI_CORE); +#endif + xTaskCreatePinnedToCore((TaskFunction_t)&core_loop, "core_loop", 4096, NULL, TASK_CORE_PRIO, &main_loop_task, CORE_FUNCTION_CORE); #ifdef PERIODIC_BMS_RESET_AT @@ -202,6 +203,12 @@ void mqtt_loop(void*) { } #endif +static std::list transmitters; + +void register_transmitter(Transmitter* transmitter) { + transmitters.push_back(transmitter); +} + void core_loop(void*) { esp_task_wdt_add(NULL); // Register this task with WDT TickType_t xLastWakeTime = xTaskGetTickCount(); @@ -253,15 +260,25 @@ void core_loop(void*) { #ifdef FUNCTION_TIME_MEASUREMENT START_TIME_MEASUREMENT(time_values); #endif - update_pause_state(); // Check if we are OK to send CAN or need to pause - update_values_battery(); // Fetch battery values -#ifdef DOUBLE_BATTERY - update_values_battery2(); - check_interconnect_available(); -#endif // DOUBLE_BATTERY + update_pause_state(); // Check if we are OK to send CAN or need to pause + + // Fetch battery values + if (battery) { + battery->update_values(); + } + + if (battery2) { + battery2->update_values(); + check_interconnect_available(); + } update_calculated_values(); update_machineryprotection(); // Check safeties - update_values_inverter(); // Update values heading towards inverter + + // Update values heading towards inverter + if (inverter) { + inverter->update_values(); + } + #ifdef FUNCTION_TIME_MEASUREMENT END_TIME_MEASUREMENT_MAX(time_values, datalayer.system.status.time_values_us); #endif @@ -269,12 +286,12 @@ void core_loop(void*) { #ifdef FUNCTION_TIME_MEASUREMENT START_TIME_MEASUREMENT(cantx); #endif - // Output - transmit_can(currentMillis); // Send CAN messages to all components -#ifdef RS485_BATTERY_SELECTED - transmit_rs485(currentMillis); -#endif // RS485_BATTERY_SELECTED + // Let all transmitter objects send their messages + for (auto& transmitter : transmitters) { + transmitter->transmit(currentMillis); + } + #ifdef FUNCTION_TIME_MEASUREMENT END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us); END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us); @@ -506,12 +523,6 @@ void update_calculated_values() { lastMillisOverflowCheck = currentMillis; } -void update_values_inverter() { - if (inverter) { - inverter->update_values(); - } -} - void check_reset_reason() { esp_reset_reason_t reason = esp_reset_reason(); switch (reason) { diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index b3c01535..77e5868f 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -41,43 +41,21 @@ void setup_battery() { #endif } -void update_values_battery() { - battery->update_values(); -} - // transmit_can_battery is called once and we need to // call both batteries. -void transmit_can_battery(unsigned long currentMillis) { - ((CanBattery*)battery)->transmit_can(currentMillis); - -#ifdef DOUBLE_BATTERY - ((CanBattery*)battery2)->transmit_can(currentMillis); -#endif -} void handle_incoming_can_frame_battery(CAN_frame rx_frame) { ((CanBattery*)battery)->handle_incoming_can_frame(rx_frame); } -#ifdef DOUBLE_BATTERY -void update_values_battery2() { - battery2->update_values(); -} - void handle_incoming_can_frame_battery2(CAN_frame rx_frame) { ((CanBattery*)battery2)->handle_incoming_can_frame(rx_frame); } -#endif #ifdef RS485_BATTERY_SELECTED -void transmit_rs485(unsigned long currentMillis) { - ((RS485Battery*)battery)->transmit_rs485(currentMillis); -} - void receive_RS485() { ((RS485Battery*)battery)->receive_RS485(); } - #endif #endif diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 95ce6399..4e6b0ccb 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -9,11 +9,11 @@ class Battery; extern Battery* battery; extern Battery* battery2; +void setup_can_shunt(); + #ifdef BMW_SBOX #include "BMW-SBOX.h" void handle_incoming_can_frame_shunt(CAN_frame rx_frame); -void transmit_can_shunt(unsigned long currentMillis); -void setup_can_shunt(); #endif #ifdef BMW_I3_BATTERY @@ -159,7 +159,6 @@ void setup_can_shunt(); #endif void setup_battery(void); -void update_values_battery(); #ifdef RS485_BATTERY_SELECTED void transmit_rs485(unsigned long currentMillis); @@ -169,7 +168,6 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame); void transmit_can_battery(unsigned long currentMillis); #endif -void update_values_battery2(); void handle_incoming_can_frame_battery2(CAN_frame rx_frame); #endif diff --git a/Software/src/battery/BMW-SBOX.cpp b/Software/src/battery/BMW-SBOX.cpp index dcd9807d..ed045fe6 100644 --- a/Software/src/battery/BMW-SBOX.cpp +++ b/Software/src/battery/BMW-SBOX.cpp @@ -1,41 +1,9 @@ #include "../include.h" #ifdef BMW_SBOX +#include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "BMW-SBOX.h" -#define MAX_ALLOWED_FAULT_TICKS 1000 - -enum SboxState { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED }; -SboxState contactorStatus = DISCONNECTED; - -unsigned long prechargeStartTime = 0; -unsigned long negativeStartTime = 0; -unsigned long positiveStartTime = 0; -unsigned long timeSpentInFaultedMode = 0; -unsigned long LastMsgTime = 0; // will store last time a 20ms CAN Message was send -unsigned long LastAvgTime = 0; // Last current storage time -unsigned long ShuntLastSeen = 0; - -uint32_t avg_mA_array[10]; -uint32_t avg_sum; - -uint8_t k; //avg array pointer - -uint8_t CAN100_cnt = 0; - -CAN_frame SBOX_100 = {.FD = false, - .ext_ID = false, - .DLC = 4, - .ID = 0x100, - .data = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0x00}}; // Byte 0: relay control, Byte 1: counter 0-E, Byte 4: CRC - -CAN_frame SBOX_300 = {.FD = false, - .ext_ID = false, - .DLC = 4, - .ID = 0x300, - .data = {0xFF, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}}; // Static frame - uint8_t reverse_bits(uint8_t byte) { uint8_t reversed = 0; for (int i = 0; i < 8; i++) { @@ -64,7 +32,7 @@ uint8_t calculateCRC(CAN_frame CAN) { return crc; } -void handle_incoming_can_frame_shunt(CAN_frame rx_frame) { +void BmwSbox::handle_incoming_can_frame(CAN_frame rx_frame) { unsigned long currentTime = millis(); if (rx_frame.ID == 0x200) { ShuntLastSeen = currentTime; @@ -100,7 +68,7 @@ void handle_incoming_can_frame_shunt(CAN_frame rx_frame) { } } -void transmit_can_shunt(unsigned long currentMillis) { +void BmwSbox::transmit_can(unsigned long currentMillis) { /** Shunt can frames seen? **/ if (ShuntLastSeen + 1000 < currentMillis) { @@ -210,7 +178,7 @@ void transmit_can_shunt(unsigned long currentMillis) { } } -void setup_can_shunt() { +void BmwSbox::setup() { strncpy(datalayer.system.info.shunt_protocol, "BMW SBOX", 63); datalayer.system.info.shunt_protocol[63] = '\0'; } diff --git a/Software/src/battery/BMW-SBOX.h b/Software/src/battery/BMW-SBOX.h index 131fa3a0..97ef3cd5 100644 --- a/Software/src/battery/BMW-SBOX.h +++ b/Software/src/battery/BMW-SBOX.h @@ -1,22 +1,67 @@ #ifndef BMW_SBOX_CONTROL_H #define BMW_SBOX_CONTROL_H #include "../include.h" + #define CAN_SHUNT_SELECTED -void transmit_can(CAN_frame* tx_frame, int interface); +#define SELECTED_SHUNT_CLASS BmwSbox -/** Minimum input voltage required to enable relay control **/ -#define MINIMUM_INPUT_VOLTAGE 250 +#include "Shunt.h" -/** Minimum required percentage of input voltage at the output port to engage the positive relay. **/ -/** Prevents engagement of the positive contactor if the precharge resistor is faulty. **/ -#define MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT 0.99 +class BmwSbox : public CanShunt { + public: + void setup(); + void transmit_can(unsigned long currentMillis); + void handle_incoming_can_frame(CAN_frame rx_frame); -/* NOTE: modify the T2 time constant below to account for the resistance and capacitance of the target system. + private: + /** Minimum input voltage required to enable relay control **/ + static const int MINIMUM_INPUT_VOLTAGE = 250; + + /** Minimum required percentage of input voltage at the output port to engage the positive relay. **/ + /** Prevents engagement of the positive contactor if the precharge resistor is faulty. **/ + static constexpr const double MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT = 0.99; + + /* NOTE: modify the T2 time constant below to account for the resistance and capacitance of the target system. * t=3RC at minimum, t=5RC ideally */ -#define CONTACTOR_CONTROL_T1 5000 // Time before negative contactor engages and precharging starts -#define CONTACTOR_CONTROL_T2 5000 // Precharge time before precharge resistor is bypassed by positive contactor -#define CONTACTOR_CONTROL_T3 2000 // Precharge relay lead time after positive contactor has been engaged + static const int CONTACTOR_CONTROL_T1 = 5000; // Time before negative contactor engages and precharging starts + static const int CONTACTOR_CONTROL_T2 = + 5000; // Precharge time before precharge resistor is bypassed by positive contactor + static const int CONTACTOR_CONTROL_T3 = 2000; // Precharge relay lead time after positive contactor has been engaged + + static const int MAX_ALLOWED_FAULT_TICKS = 1000; + + enum SboxState { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED }; + SboxState contactorStatus = DISCONNECTED; + + unsigned long prechargeStartTime = 0; + unsigned long negativeStartTime = 0; + unsigned long positiveStartTime = 0; + unsigned long timeSpentInFaultedMode = 0; + unsigned long LastMsgTime = 0; // will store last time a 20ms CAN Message was send + unsigned long LastAvgTime = 0; // Last current storage time + unsigned long ShuntLastSeen = 0; + + uint32_t avg_mA_array[10]; + uint32_t avg_sum; + + uint8_t k; //avg array pointer + + uint8_t CAN100_cnt = 0; + + CAN_frame SBOX_100 = {.FD = false, + .ext_ID = false, + .DLC = 4, + .ID = 0x100, + .data = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00}}; // Byte 0: relay control, Byte 1: counter 0-E, Byte 4: CRC + + CAN_frame SBOX_300 = {.FD = false, + .ext_ID = false, + .DLC = 4, + .ID = 0x300, + .data = {0xFF, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}}; // Static frame +}; #endif diff --git a/Software/src/battery/Battery.h b/Software/src/battery/Battery.h index bc380ef0..9a862f37 100644 --- a/Software/src/battery/Battery.h +++ b/Software/src/battery/Battery.h @@ -47,6 +47,9 @@ class Battery { // This allows for battery specific SOC plausibility calculations to be performed. virtual bool soc_plausible() { return true; } + // Battery reports total_charged_battery_Wh and total_discharged_battery_Wh + virtual bool supports_charged_energy() { return false; } + virtual BatteryHtmlRenderer& get_status_renderer() { return defaultRenderer; } private: diff --git a/Software/src/battery/CanBattery.h b/Software/src/battery/CanBattery.h index 2fc2bc28..181301d3 100644 --- a/Software/src/battery/CanBattery.h +++ b/Software/src/battery/CanBattery.h @@ -3,22 +3,31 @@ #include "Battery.h" +#include "src/communication/Transmitter.h" #include "src/devboard/utils/types.h" // Abstract base class for batteries using the CAN bus -class CanBattery : public Battery { +class CanBattery : public Battery, Transmitter { public: virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0; virtual void transmit_can(unsigned long currentMillis) = 0; String interface_name() { return getCANInterfaceName(can_interface); } + void transmit(unsigned long currentMillis) { transmit_can(currentMillis); } + protected: CAN_Interface can_interface; - CanBattery() { can_interface = can_config.battery; } + CanBattery() { + can_interface = can_config.battery; + register_transmitter(this); + } - CanBattery(CAN_Interface interface) { can_interface = interface; } + CanBattery(CAN_Interface interface) { + can_interface = interface; + register_transmitter(this); + } }; #endif diff --git a/Software/src/battery/MEB-BATTERY.h b/Software/src/battery/MEB-BATTERY.h index 184d5c8f..7895247c 100644 --- a/Software/src/battery/MEB-BATTERY.h +++ b/Software/src/battery/MEB-BATTERY.h @@ -15,6 +15,7 @@ class MebBattery : public CanBattery { virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); bool supports_real_BMS_status() { return true; } + bool supports_charged_energy() { return true; } BatteryHtmlRenderer& get_status_renderer() { return renderer; } diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.h b/Software/src/battery/NISSAN-LEAF-BATTERY.h index a488f071..16a68bc2 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.h +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.h @@ -43,9 +43,14 @@ class NissanLeafBattery : public CanBattery { virtual void transmit_can(unsigned long currentMillis); bool supports_reset_SOH(); - void reset_SOH() { datalayer_extended.nissanleaf.UserRequestSOHreset = true; } + bool soc_plausible() { + // When pack voltage is close to max, and SOC% is still low, SOC is not plausible + return !((datalayer.battery.status.voltage_dV > (datalayer.battery.info.max_design_voltage_dV - 100)) && + (datalayer.battery.status.real_soc < 6500)); + } + BatteryHtmlRenderer& get_status_renderer() { return renderer; } private: diff --git a/Software/src/battery/RS485Battery.h b/Software/src/battery/RS485Battery.h index 1274573e..53477f35 100644 --- a/Software/src/battery/RS485Battery.h +++ b/Software/src/battery/RS485Battery.h @@ -3,14 +3,20 @@ #include "Battery.h" +#include "src/communication/Transmitter.h" #include "src/devboard/utils/types.h" // Abstract base class for batteries using the RS485 interface -class RS485Battery : public Battery { +class RS485Battery : public Battery, Transmitter { public: virtual void receive_RS485() = 0; virtual void transmit_rs485(unsigned long currentMillis) = 0; + String interface_name() { return "RS485"; } + + void transmit(unsigned long currentMillis) { transmit_rs485(currentMillis); } + + RS485Battery() { register_transmitter(this); } }; #endif diff --git a/Software/src/battery/Shunt.h b/Software/src/battery/Shunt.h new file mode 100644 index 00000000..c39a7ed5 --- /dev/null +++ b/Software/src/battery/Shunt.h @@ -0,0 +1,31 @@ +#ifndef _SHUNT_H +#define _SHUNT_H + +#include "../communication/can/comm_can.h" +#include "src/communication/Transmitter.h" +#include "src/devboard/utils/types.h" + +class CanShunt : public Transmitter { + public: + virtual void setup() = 0; + virtual void transmit_can(unsigned long currentMillis) = 0; + virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0; + + void transmit(unsigned long currentMillis) { + if (allowed_to_send_CAN) { + transmit_can(currentMillis); + } + } + + protected: + CAN_Interface can_interface; + + CanShunt() { + can_interface = can_config.battery; + register_transmitter(this); + } +}; + +extern CanShunt* shunt; + +#endif diff --git a/Software/src/battery/Shunts.cpp b/Software/src/battery/Shunts.cpp new file mode 100644 index 00000000..7aadcd76 --- /dev/null +++ b/Software/src/battery/Shunts.cpp @@ -0,0 +1,19 @@ +#include "../include.h" +#include "Shunt.h" + +CanShunt* shunt = nullptr; + +void setup_can_shunt() { +#if defined(CAN_SHUNT_SELECTED) && defined(SELECTED_SHUNT_CLASS) + shunt = new SELECTED_SHUNT_CLASS(); + if (shunt) { + shunt->setup(); + } +#endif +} + +void handle_incoming_can_frame_shunt(CAN_frame rx_frame) { + if (shunt) { + shunt->handle_incoming_can_frame(rx_frame); + } +} diff --git a/Software/src/battery/TESLA-BATTERY.h b/Software/src/battery/TESLA-BATTERY.h index cbbab2e3..7b60cb91 100644 --- a/Software/src/battery/TESLA-BATTERY.h +++ b/Software/src/battery/TESLA-BATTERY.h @@ -30,6 +30,8 @@ class TeslaBattery : public CanBattery { bool supports_reset_BMS() { return true; } void reset_BMS() { datalayer.battery.settings.user_requests_tesla_bms_reset = true; } + bool supports_charged_energy() { return true; } + BatteryHtmlRenderer& get_status_renderer() { return renderer; } private: diff --git a/Software/src/charger/CanCharger.h b/Software/src/charger/CanCharger.h index 6fb824f8..8d7631dc 100644 --- a/Software/src/charger/CanCharger.h +++ b/Software/src/charger/CanCharger.h @@ -4,6 +4,7 @@ #include "src/devboard/utils/types.h" #include "../datalayer/datalayer.h" +#include "src/communication/Transmitter.h" enum class ChargerType { NissanLeaf, ChevyVolt }; @@ -36,13 +37,19 @@ class Charger { }; // Base class for chargers on a CAN bus -class CanCharger : public Charger { +class CanCharger : public Charger, Transmitter { public: virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0; virtual void transmit_can(unsigned long currentMillis) = 0; + void transmit(unsigned long currentMillis) { + if (allowed_to_send_CAN) { + transmit_can(currentMillis); + } + } + protected: - CanCharger(ChargerType type) : Charger(type) {} + CanCharger(ChargerType type) : Charger(type) { register_transmitter(this); } }; #endif diff --git a/Software/src/communication/Transmitter.h b/Software/src/communication/Transmitter.h new file mode 100644 index 00000000..f25e19fb --- /dev/null +++ b/Software/src/communication/Transmitter.h @@ -0,0 +1,11 @@ +#ifndef _TRANSMITTER_H +#define _TRANSMITTER_H + +class Transmitter { + public: + virtual void transmit(unsigned long currentMillis) = 0; +}; + +void register_transmitter(Transmitter* transmitter); + +#endif diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index 51547529..afb3fd0c 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -104,30 +104,6 @@ void init_CAN() { #endif // CANFD_ADDON } -// Transmit functions -void transmit_can(unsigned long currentMillis) { - - if (!allowed_to_send_CAN) { - return; //Global block of CAN messages - } - -#ifndef RS485_BATTERY_SELECTED - transmit_can_battery(currentMillis); -#endif - -#ifdef CAN_INVERTER_SELECTED - transmit_can_inverter(currentMillis); -#endif // CAN_INVERTER_SELECTED - - if (charger) { - charger->transmit_can(currentMillis); - } - -#ifdef CAN_SHUNT_SELECTED - transmit_can_shunt(currentMillis); -#endif // CAN_SHUNT_SELECTED -} - void transmit_can_frame(CAN_frame* tx_frame, int interface) { if (!allowed_to_send_CAN) { return; @@ -325,10 +301,8 @@ void map_can_frame_to_variable(CAN_frame* rx_frame, int interface) { map_can_frame_to_variable_inverter(*rx_frame); #endif } - if (interface == can_config.battery_double) { -#ifdef DOUBLE_BATTERY + if (interface == can_config.battery_double && battery2) { handle_incoming_can_frame_battery2(*rx_frame); -#endif } if (interface == can_config.charger && charger) { charger->map_can_frame_to_variable(*rx_frame); diff --git a/Software/src/communication/can/comm_can.h b/Software/src/communication/can/comm_can.h index 1ebdcc6b..b51be935 100644 --- a/Software/src/communication/can/comm_can.h +++ b/Software/src/communication/can/comm_can.h @@ -37,16 +37,6 @@ void init_CAN(); */ void transmit_can_frame(); -/** - * @brief Send CAN messages to all components - * - * @param[in] void - * @param[in] unsigned long currentMillis - * - * @return void - */ -void transmit_can(unsigned long currentMillis); - /** * @brief Receive CAN messages from all interfaces * diff --git a/Software/src/devboard/hal/hw_3LB.h b/Software/src/devboard/hal/hw_3LB.h index e5d01c01..7d070726 100644 --- a/Software/src/devboard/hal/hw_3LB.h +++ b/Software/src/devboard/hal/hw_3LB.h @@ -79,12 +79,8 @@ #define EQUIPMENT_STOP_PIN 35 // BMW_I3_BATTERY wake up pin -#ifdef BMW_I3_BATTERY #define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1 -#ifdef DOUBLE_BATTERY #define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2 -#endif // DOUBLE_BATTERY -#endif // BMW_I3_BATTERY /* ----- Error checks below, don't change (can't be moved to separate file) ----- */ #ifndef HW_CONFIGURED diff --git a/Software/src/devboard/hal/hw_devkit.h b/Software/src/devboard/hal/hw_devkit.h index a9dd49c6..f028bbbc 100644 --- a/Software/src/devboard/hal/hw_devkit.h +++ b/Software/src/devboard/hal/hw_devkit.h @@ -67,12 +67,8 @@ The pin layout below supports the following: #define INVERTER_DISCONNECT_CONTACTOR_PIN GPIO_NUM_5 // BMW_I3_BATTERY wake up pin -#ifdef BMW_I3_BATTERY #define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1 -#ifdef DOUBLE_BATTERY #define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2 -#endif // DOUBLE_BATTERY -#endif // BMW_I3_BATTERY /* ----- Error checks below, don't change (can't be moved to separate file) ----- */ #ifndef HW_CONFIGURED diff --git a/Software/src/devboard/hal/hw_lilygo.h b/Software/src/devboard/hal/hw_lilygo.h index 7d6f3175..f560019d 100644 --- a/Software/src/devboard/hal/hw_lilygo.h +++ b/Software/src/devboard/hal/hw_lilygo.h @@ -74,12 +74,8 @@ #define EQUIPMENT_STOP_PIN 35 // BMW_I3_BATTERY wake up pin -#ifdef BMW_I3_BATTERY #define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1 -#ifdef DOUBLE_BATTERY #define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2 -#endif // DOUBLE_BATTERY -#endif // BMW_I3_BATTERY /* ----- Error checks below, don't change (can't be moved to separate file) ----- */ #ifndef HW_CONFIGURED diff --git a/Software/src/devboard/hal/hw_stark.h b/Software/src/devboard/hal/hw_stark.h index 74f2ce2f..9d098b62 100644 --- a/Software/src/devboard/hal/hw_stark.h +++ b/Software/src/devboard/hal/hw_stark.h @@ -66,12 +66,8 @@ GPIOs on extra header #define EQUIPMENT_STOP_PIN 2 // BMW_I3_BATTERY wake up pin -#ifdef BMW_I3_BATTERY #define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1 -#ifdef DOUBLE_BATTERY #define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2 -#endif // DOUBLE_BATTERY -#endif // BMW_I3_BATTERY /* ----- Error checks below, don't change (can't be moved to separate file) ----- */ #ifndef HW_CONFIGURED diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index 6baaec41..898067c7 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -62,55 +62,62 @@ struct SensorConfig { const char* value_template; const char* unit; const char* device_class; + + // A function that returns true for the battery if it supports this config + std::function condition; +}; + +static std::function always = [](Battery* b) { + return true; +}; +static std::function supports_charged = [](Battery* b) { + return b->supports_charged_energy(); }; SensorConfig sensorConfigTemplate[] = { - {"SOC", "SOC (Scaled)", "", "%", "battery"}, - {"SOC_real", "SOC (real)", "", "%", "battery"}, - {"state_of_health", "State Of Health", "", "%", "battery"}, - {"temperature_min", "Temperature Min", "", "°C", "temperature"}, - {"temperature_max", "Temperature Max", "", "°C", "temperature"}, - {"cpu_temp", "CPU Temperature", "", "°C", "temperature"}, - {"stat_batt_power", "Stat Batt Power", "", "W", "power"}, - {"battery_current", "Battery Current", "", "A", "current"}, - {"cell_max_voltage", "Cell Max Voltage", "", "V", "voltage"}, - {"cell_min_voltage", "Cell Min Voltage", "", "V", "voltage"}, - {"cell_voltage_delta", "Cell Voltage Delta", "", "mV", "voltage"}, - {"battery_voltage", "Battery Voltage", "", "V", "voltage"}, - {"total_capacity", "Battery Total Capacity", "", "Wh", "energy"}, - {"remaining_capacity", "Battery Remaining Capacity (scaled)", "", "Wh", "energy"}, - {"remaining_capacity_real", "Battery Remaining Capacity (real)", "", "Wh", "energy"}, - {"max_discharge_power", "Battery Max Discharge Power", "", "W", "power"}, - {"max_charge_power", "Battery Max Charge Power", "", "W", "power"}, -#if defined(MEB_BATTERY) || defined(TESLA_BATTERY) - {"charged_energy", "Battery Charged Energy", "", "Wh", "energy"}, - {"discharged_energy", "Battery Discharged Energy", "", "Wh", "energy"}, -#endif - {"bms_status", "BMS Status", "", "", ""}, - {"pause_status", "Pause Status", "", "", ""}}; + {"SOC", "SOC (Scaled)", "", "%", "battery", always}, + {"SOC_real", "SOC (real)", "", "%", "battery", always}, + {"state_of_health", "State Of Health", "", "%", "battery", always}, + {"temperature_min", "Temperature Min", "", "°C", "temperature", always}, + {"temperature_max", "Temperature Max", "", "°C", "temperature", always}, + {"cpu_temp", "CPU Temperature", "", "°C", "temperature", always}, + {"stat_batt_power", "Stat Batt Power", "", "W", "power", always}, + {"battery_current", "Battery Current", "", "A", "current", always}, + {"cell_max_voltage", "Cell Max Voltage", "", "V", "voltage", always}, + {"cell_min_voltage", "Cell Min Voltage", "", "V", "voltage", always}, + {"cell_voltage_delta", "Cell Voltage Delta", "", "mV", "voltage", always}, + {"battery_voltage", "Battery Voltage", "", "V", "voltage", always}, + {"total_capacity", "Battery Total Capacity", "", "Wh", "energy", always}, + {"remaining_capacity", "Battery Remaining Capacity (scaled)", "", "Wh", "energy", always}, + {"remaining_capacity_real", "Battery Remaining Capacity (real)", "", "Wh", "energy", always}, + {"max_discharge_power", "Battery Max Discharge Power", "", "W", "power", always}, + {"max_charge_power", "Battery Max Charge Power", "", "W", "power", always}, + {"charged_energy", "Battery Charged Energy", "", "Wh", "energy", supports_charged}, + {"discharged_energy", "Battery Discharged Energy", "", "Wh", "energy", supports_charged}, + {"bms_status", "BMS Status", "", "", "", always}, + {"pause_status", "Pause Status", "", "", "", always}}; -#ifdef DOUBLE_BATTERY +// Enough space for two batteries SensorConfig sensorConfigs[((sizeof(sensorConfigTemplate) / sizeof(sensorConfigTemplate[0])) * 2) - 2]; -#else -SensorConfig sensorConfigs[sizeof(sensorConfigTemplate) / sizeof(sensorConfigTemplate[0])]; -#endif // DOUBLE_BATTERY void create_sensor_configs() { int number_of_templates = sizeof(sensorConfigTemplate) / sizeof(sensorConfigTemplate[0]); + for (int i = 0; i < number_of_templates; i++) { SensorConfig config = sensorConfigTemplate[i]; config.value_template = strdup(("{{ value_json." + std::string(config.object_id) + " }}").c_str()); sensorConfigs[i] = config; -#ifdef DOUBLE_BATTERY - if (config.object_id == "pause_status" || config.object_id == "bms_status") { - continue; + + if (battery2) { + if (config.object_id == "pause_status" || config.object_id == "bms_status") { + continue; + } + sensorConfigs[i + number_of_templates] = config; + sensorConfigs[i + number_of_templates].name = strdup(String(config.name + String(" 2")).c_str()); + sensorConfigs[i + number_of_templates].object_id = strdup(String(config.object_id + String("_2")).c_str()); + sensorConfigs[i + number_of_templates].value_template = + strdup(("{{ value_json." + std::string(config.object_id) + "_2 }}").c_str()); } - sensorConfigs[i + number_of_templates] = config; - sensorConfigs[i + number_of_templates].name = strdup(String(config.name + String(" 2")).c_str()); - sensorConfigs[i + number_of_templates].object_id = strdup(String(config.object_id + String("_2")).c_str()); - sensorConfigs[i + number_of_templates].value_template = - strdup(("{{ value_json." + std::string(config.object_id) + "_2 }}").c_str()); -#endif // DOUBLE_BATTERY } } @@ -164,7 +171,8 @@ static String generateButtonTopic(const char* subtype) { return topic_name + "/command/" + String(subtype); } -void set_battery_attributes(JsonDocument& doc, const DATALAYER_BATTERY_TYPE& battery, const String& suffix) { +void set_battery_attributes(JsonDocument& doc, const DATALAYER_BATTERY_TYPE& battery, const String& suffix, + bool supports_charged) { doc["SOC" + suffix] = ((float)battery.status.reported_soc) / 100.0; doc["SOC_real" + suffix] = ((float)battery.status.real_soc) / 100.0; doc["state_of_health" + suffix] = ((float)battery.status.soh_pptt) / 100.0; @@ -185,13 +193,14 @@ void set_battery_attributes(JsonDocument& doc, const DATALAYER_BATTERY_TYPE& bat doc["remaining_capacity" + suffix] = ((float)battery.status.reported_remaining_capacity_Wh); doc["max_discharge_power" + suffix] = ((float)battery.status.max_discharge_power_W); doc["max_charge_power" + suffix] = ((float)battery.status.max_charge_power_W); -#if defined(MEB_BATTERY) || defined(TESLA_BATTERY) - if (datalayer.battery.status.total_charged_battery_Wh != 0 && - datalayer.battery.status.total_discharged_battery_Wh != 0) { - doc["charged_energy" + suffix] = ((float)datalayer.battery.status.total_charged_battery_Wh); - doc["discharged_energy" + suffix] = ((float)datalayer.battery.status.total_discharged_battery_Wh); + + if (supports_charged) { + if (datalayer.battery.status.total_charged_battery_Wh != 0 && + datalayer.battery.status.total_discharged_battery_Wh != 0) { + doc["charged_energy" + suffix] = ((float)datalayer.battery.status.total_charged_battery_Wh); + doc["discharged_energy" + suffix] = ((float)datalayer.battery.status.total_discharged_battery_Wh); + } } -#endif } static std::vector order_events; @@ -203,13 +212,19 @@ static bool publish_common_info(void) { if (ha_common_info_published == false) { for (int i = 0; i < sizeof(sensorConfigs) / sizeof(sensorConfigs[0]); i++) { SensorConfig& config = sensorConfigs[i]; + + if (!config.condition(battery)) { + continue; + } + doc["name"] = config.name; doc["state_topic"] = state_topic; doc["unique_id"] = topic_name + "_" + String(config.object_id); doc["object_id"] = object_id_prefix + String(config.object_id); doc["value_template"] = config.value_template; - if (config.unit != nullptr && strlen(config.unit) > 0) + if (config.unit != nullptr && strlen(config.unit) > 0) { doc["unit_of_measurement"] = config.unit; + } if (config.device_class != nullptr && strlen(config.device_class) > 0) { doc["device_class"] = config.device_class; doc["state_class"] = "measurement"; @@ -231,14 +246,15 @@ static bool publish_common_info(void) { //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.CAN_battery_still_alive && allowed_to_send_CAN && millis() > BOOTUP_TIME) { - set_battery_attributes(doc, datalayer.battery, ""); + set_battery_attributes(doc, datalayer.battery, "", battery->supports_charged_energy()); } -#ifdef DOUBLE_BATTERY - //only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery) - if (datalayer.battery2.status.CAN_battery_still_alive && allowed_to_send_CAN && millis() > BOOTUP_TIME) { - set_battery_attributes(doc, datalayer.battery2, "_2"); + + if (battery2) { + //only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery) + if (datalayer.battery2.status.CAN_battery_still_alive && allowed_to_send_CAN && millis() > BOOTUP_TIME) { + set_battery_attributes(doc, datalayer.battery2, "_2", battery2->supports_charged_energy()); + } } -#endif // DOUBLE_BATTERY serializeJson(doc, mqtt_msg); if (mqtt_publish(state_topic.c_str(), mqtt_msg, false) == false) { #ifdef DEBUG_LOG @@ -256,9 +272,7 @@ static bool publish_common_info(void) { static bool publish_cell_voltages(void) { static JsonDocument doc; static String state_topic = topic_name + "/spec_data"; -#ifdef DOUBLE_BATTERY static String state_topic_2 = topic_name + "/spec_data_2"; -#endif // DOUBLE_BATTERY #ifdef HA_AUTODISCOVERY bool failed_to_publish = false; @@ -280,24 +294,26 @@ static bool publish_cell_voltages(void) { } doc.clear(); // clear after sending autoconfig } -#ifdef DOUBLE_BATTERY - // If the cell voltage number isn't initialized... - if (datalayer.battery2.info.number_of_cells != 0u) { - for (int i = 0; i < datalayer.battery.info.number_of_cells; i++) { - int cellNumber = i + 1; - set_battery_voltage_attributes(doc, i, cellNumber, state_topic_2, object_id_prefix + "2_", " 2"); - set_common_discovery_attributes(doc); + if (battery2) { + // TODO: Combine this identical block with the previous one. + // If the cell voltage number isn't initialized... + if (datalayer.battery2.info.number_of_cells != 0u) { - serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); - if (mqtt_publish(generateCellVoltageAutoConfigTopic(cellNumber, "_2_").c_str(), mqtt_msg, true) == false) { - failed_to_publish = true; - return false; + for (int i = 0; i < datalayer.battery.info.number_of_cells; i++) { + int cellNumber = i + 1; + set_battery_voltage_attributes(doc, i, cellNumber, state_topic_2, object_id_prefix + "2_", " 2"); + set_common_discovery_attributes(doc); + + serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); + if (mqtt_publish(generateCellVoltageAutoConfigTopic(cellNumber, "_2_").c_str(), mqtt_msg, true) == false) { + failed_to_publish = true; + return false; + } } + doc.clear(); // clear after sending autoconfig } - doc.clear(); // clear after sending autoconfig } -#endif // DOUBLE_BATTERY } if (failed_to_publish == false) { ha_cell_voltages_published = true; @@ -324,27 +340,27 @@ static bool publish_cell_voltages(void) { doc.clear(); } -#ifdef DOUBLE_BATTERY - // If cell voltages have been populated... - if (datalayer.battery2.info.number_of_cells != 0u && - datalayer.battery2.status.cell_voltages_mV[datalayer.battery2.info.number_of_cells - 1] != 0u) { + if (battery2) { + // If cell voltages have been populated... + if (datalayer.battery2.info.number_of_cells != 0u && + datalayer.battery2.status.cell_voltages_mV[datalayer.battery2.info.number_of_cells - 1] != 0u) { - JsonArray cell_voltages = doc["cell_voltages"].to(); - for (size_t i = 0; i < datalayer.battery2.info.number_of_cells; ++i) { - cell_voltages.add(((float)datalayer.battery2.status.cell_voltages_mV[i]) / 1000.0); - } + JsonArray cell_voltages = doc["cell_voltages"].to(); + for (size_t i = 0; i < datalayer.battery2.info.number_of_cells; ++i) { + cell_voltages.add(((float)datalayer.battery2.status.cell_voltages_mV[i]) / 1000.0); + } - serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); + serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); - if (!mqtt_publish(state_topic_2.c_str(), mqtt_msg, false)) { + if (!mqtt_publish(state_topic_2.c_str(), mqtt_msg, false)) { #ifdef DEBUG_LOG - logging.println("Cell voltage MQTT msg could not be sent"); + logging.println("Cell voltage MQTT msg could not be sent"); #endif // DEBUG_LOG - return false; + return false; + } + doc.clear(); } - doc.clear(); } -#endif // DOUBLE_BATTERY return true; } diff --git a/Software/src/devboard/safety/safety.cpp b/Software/src/devboard/safety/safety.cpp index 8c011ffc..d29c6bce 100644 --- a/Software/src/devboard/safety/safety.cpp +++ b/Software/src/devboard/safety/safety.cpp @@ -156,18 +156,9 @@ void update_machineryprotection() { clear_event(EVENT_SOH_LOW); } -#ifdef NISSAN_LEAF_BATTERY - // Check if SOC% is plausible - if (datalayer.battery.status.voltage_dV > - (datalayer.battery.info.max_design_voltage_dV - - 100)) { // When pack voltage is close to max, and SOC% is still low, raise event - if (datalayer.battery.status.real_soc < 6500) { // 65.00% - set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc); - } else { - clear_event(EVENT_SOC_PLAUSIBILITY_ERROR); - } + if (!battery->soc_plausible()) { + set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc); } -#endif //NISSAN_LEAF_BATTERY // Check diff between highest and lowest cell cell_deviation_mV = diff --git a/Software/src/inverter/CanInverterProtocol.h b/Software/src/inverter/CanInverterProtocol.h index 68f9b400..c4f63235 100644 --- a/Software/src/inverter/CanInverterProtocol.h +++ b/Software/src/inverter/CanInverterProtocol.h @@ -5,11 +5,20 @@ #include "src/devboard/utils/types.h" -class CanInverterProtocol : public InverterProtocol { +class CanInverterProtocol : public InverterProtocol, Transmitter { public: virtual const char* interface_name() { return getCANInterfaceName(can_config.inverter); } virtual void transmit_can(unsigned long currentMillis) = 0; virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0; + + void transmit(unsigned long currentMillis) { + if (allowed_to_send_CAN) { + transmit_can(currentMillis); + } + } + + protected: + CanInverterProtocol() { register_transmitter(this); } }; #endif diff --git a/Software/src/inverter/INVERTERS.cpp b/Software/src/inverter/INVERTERS.cpp index b454504b..77d858fb 100644 --- a/Software/src/inverter/INVERTERS.cpp +++ b/Software/src/inverter/INVERTERS.cpp @@ -34,17 +34,10 @@ void setup_inverter() { } #ifdef CAN_INVERTER_SELECTED -void update_values_can_inverter() { - can_inverter->update_values(); -} void map_can_frame_to_variable_inverter(CAN_frame rx_frame) { can_inverter->map_can_frame_to_variable(rx_frame); } - -void transmit_can_inverter(unsigned long currentMillis) { - can_inverter->transmit_can(currentMillis); -} #endif #ifdef RS485_INVERTER_SELECTED diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h index 9996fb03..98e6c5a6 100644 --- a/Software/src/inverter/INVERTERS.h +++ b/Software/src/inverter/INVERTERS.h @@ -34,7 +34,6 @@ extern InverterProtocol* inverter; void setup_inverter(); #ifdef CAN_INVERTER_SELECTED -void update_values_can_inverter(); void map_can_frame_to_variable_inverter(CAN_frame rx_frame); void transmit_can_inverter(unsigned long currentMillis); #endif diff --git a/Software/src/inverter/KOSTAL-RS485.cpp b/Software/src/inverter/KOSTAL-RS485.cpp index 3cd0d520..d97eea02 100644 --- a/Software/src/inverter/KOSTAL-RS485.cpp +++ b/Software/src/inverter/KOSTAL-RS485.cpp @@ -251,7 +251,10 @@ void KostalInverterProtocol::receive_RS485() // Runs as fast as possible to han int code = RS485_RXFRAME[6] + RS485_RXFRAME[7] * 0x100; if (code == 0x44a) { //Send cyclic data - update_values_battery(); + // TODO: Probably not a good idea to use the battery object here like this. + if (battery) { + battery->update_values(); + } update_values(); if (f2_startup_count < 15) { f2_startup_count++;