diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp new file mode 100644 index 00000000..0884cf45 --- /dev/null +++ b/Software/src/battery/BATTERIES.cpp @@ -0,0 +1,29 @@ +#include "../include.h" + +// These functions adapt the old C-style global functions battery-API to the +// object-oriented battery API. + +#ifdef OO_BATTERY_SELECTED + +static CanBattery* battery; + +void setup_battery() { + // Currently only one battery is implemented as a class. + // TODO: Extend based on build-time or run-time selected battery. + battery = new RenaultZoeGen1Battery(); + battery->setup(); +} + +void update_values_battery() { + battery->update_values(); +} + +void transmit_can_battery(unsigned long currentMillis) { + battery->transmit_can(currentMillis); +} + +void handle_incoming_can_frame_battery(CAN_frame rx_frame) { + battery->handle_incoming_can_frame(rx_frame); +} + +#endif diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 80afe51f..8795f5e5 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -2,6 +2,19 @@ #define BATTERIES_H #include "../../USER_SETTINGS.h" +#include "src/devboard/utils/types.h" + +// Abstract base class for next-generation battery implementations. +// Defines the interface to call battery specific functionality. +// No support for double battery yet. +class CanBattery { + public: + virtual void setup(void) = 0; + virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0; + virtual void update_values() = 0; + virtual void transmit_can(unsigned long currentMillis) = 0; +}; + #ifdef BMW_SBOX #include "BMW-SBOX.h" void handle_incoming_can_frame_shunt(CAN_frame rx_frame); diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp index d628d584..281ce895 100644 --- a/Software/src/battery/BMW-I3-BATTERY.cpp +++ b/Software/src/battery/BMW-I3-BATTERY.cpp @@ -195,7 +195,10 @@ static uint8_t alive_counter_5000ms = 0; static uint8_t BMW_1D0_counter = 0; static uint8_t BMW_13E_counter = 0; static uint8_t BMW_380_counter = 0; -static uint32_t BMW_328_counter = 0; +static uint32_t BMW_328_seconds = 243785948; // Initialized to make the battery think vehicle was made 7.7years ago +static uint16_t BMW_328_days = + 9244; //Time since 1.1.2000. Hacky implementation to make it think current date is 23rd April 2025 +static uint32_t BMS_328_seconds_to_day = 0; //Counter to keep track of days uptime static bool battery_awake = false; static bool battery2_awake = false; @@ -966,12 +969,23 @@ void transmit_can_battery(unsigned long 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 byte0-3 = Second Counter (T_SEC_COU_REL) time_second_counter_relative + // This signal shows the time in seconds since the system time was started (typically in the factory) + BMW_328_seconds++; // Used to increment seconds + BMW_328.data.u8[0] = (uint8_t)(BMW_328_seconds & 0xFF); + BMW_328.data.u8[1] = (uint8_t)((BMW_328_seconds >> 8) & 0xFF); + BMW_328.data.u8[2] = (uint8_t)((BMW_328_seconds >> 16) & 0xFF); + BMW_328.data.u8[3] = (uint8_t)((BMW_328_seconds >> 24) & 0xFF); + //BMW_328 byte 4-5 = Day Counter (T_DAY_COU_ABSL) time_day_counter_absolute + //This value goes from 1 ... 65543 + // Day 1 = 1.1.2000 ... Day 65543 = year 2179 + BMS_328_seconds_to_day++; + if (BMS_328_seconds_to_day > 86400) { + BMW_328_days++; + BMS_328_seconds_to_day = 0; + } + BMW_328.data.u8[4] = (uint8_t)(BMW_328_days & 0xFF); + BMW_328.data.u8[5] = (uint8_t)((BMW_328_days >> 8) & 0xFF); BMW_1D0.data.u8[1] = ((BMW_1D0.data.u8[1] & 0xF0) + alive_counter_1000ms); BMW_1D0.data.u8[0] = calculateCRC(BMW_1D0, 8, 0xF9); diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp index ae1100a5..35e9d084 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp @@ -4,6 +4,8 @@ #include "../devboard/utils/events.h" #include "RENAULT-ZOE-GEN1-BATTERY.h" +void transmit_can_frame(CAN_frame* tx_frame, int interface); + /* 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 The Zoe BMS apparently does not send total pack voltage, so we use the polled 96x cellvoltages summed up as total voltage @@ -75,7 +77,8 @@ static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN static unsigned long previousMillis250 = 0; // will store last time a 250ms CAN Message was sent static uint8_t counter_423 = 0; -void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus +void RenaultZoeGen1Battery:: + update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus datalayer.battery.status.soh_pptt = (LB_SOH * 100); // Increase range from 99% -> 99.00% datalayer.battery.status.real_soc = SOC_polled; @@ -134,7 +137,7 @@ void update_values_battery() { //This function maps all the values fetched via datalayer.battery.status.voltage_dV = static_cast((calculated_total_pack_voltage_mV / 100)); // mV to dV } -void handle_incoming_can_frame_battery(CAN_frame rx_frame) { +void RenaultZoeGen1Battery::handle_incoming_can_frame(CAN_frame rx_frame) { switch (rx_frame.ID) { case 0x155: //10ms - Charging power, current and SOC datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; @@ -490,7 +493,8 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { } } -void transmit_can_battery(unsigned long currentMillis) { +void RenaultZoeGen1Battery::transmit_can(unsigned long currentMillis) { + // Send 100ms CAN Message if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { previousMillis100 = currentMillis; @@ -537,7 +541,7 @@ void transmit_can_battery(unsigned long currentMillis) { } } -void setup_battery(void) { // Performs one time setup at startup +void RenaultZoeGen1Battery::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen1 22/40kWh", 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.status.battery_allows_contactor_closing = true; diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h index 5b092b23..7fb6429a 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h @@ -3,13 +3,22 @@ #include "../include.h" #define BATTERY_SELECTED + +// Indicates that the object-oriented battery interface is to be activated. +#define OO_BATTERY_SELECTED + #define MAX_PACK_VOLTAGE_DV 4200 //5000 = 500.0V #define MIN_PACK_VOLTAGE_DV 3000 #define MAX_CELL_DEVIATION_MV 150 #define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value #define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value -void setup_battery(void); -void transmit_can_frame(CAN_frame* tx_frame, int interface); +class RenaultZoeGen1Battery : public CanBattery { + public: + virtual void setup(void); + virtual void handle_incoming_can_frame(CAN_frame rx_frame); + virtual void update_values(); + virtual void transmit_can(unsigned long currentMillis); +}; #endif diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.h b/Software/src/battery/VOLVO-SPA-BATTERY.h index fb81ff10..61fa51e9 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.h +++ b/Software/src/battery/VOLVO-SPA-BATTERY.h @@ -6,11 +6,11 @@ #define BATTERY_SELECTED #define MAX_PACK_VOLTAGE_108S_DV 4540 #define MIN_PACK_VOLTAGE_108S_DV 2938 -#define MAX_PACK_VOLTAGE_96S_DV 4030 +#define MAX_PACK_VOLTAGE_96S_DV 4080 #define MIN_PACK_VOLTAGE_96S_DV 2620 #define MAX_CELL_DEVIATION_MV 250 -#define MAX_CELL_VOLTAGE_MV 4210 //Battery is put into emergency stop if one cell goes over this value -#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value +#define MAX_CELL_VOLTAGE_MV 4260 // Charging is halted if one cell goes above this +#define MIN_CELL_VOLTAGE_MV 2700 // Charging is halted if one cell goes below this void setup_battery(void); void transmit_can_frame(CAN_frame* tx_frame, int interface); diff --git a/Software/src/communication/can/obd.cpp b/Software/src/communication/can/obd.cpp index 3a451e70..cfe73dd9 100644 --- a/Software/src/communication/can/obd.cpp +++ b/Software/src/communication/can/obd.cpp @@ -1,6 +1,8 @@ #include "obd.h" #include "comm_can.h" +void transmit_can_frame(CAN_frame* tx_frame, int interface); + void show_dtc(uint8_t byte0, uint8_t byte1); void show_dtc(uint8_t byte0, uint8_t byte1) { diff --git a/Software/src/communication/rs485/comm_rs485.cpp b/Software/src/communication/rs485/comm_rs485.cpp index 1288baf6..25152b1c 100644 --- a/Software/src/communication/rs485/comm_rs485.cpp +++ b/Software/src/communication/rs485/comm_rs485.cpp @@ -30,10 +30,8 @@ void init_rs485() { Serial2.begin(RS485_BAUDRATE, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN); #endif // RS485_INVERTER_SELECTED || RS485_BATTERY_SELECTED #ifdef MODBUS_INVERTER_SELECTED -#ifdef BYD_MODBUS // Init Static data to the RTU Modbus - handle_static_data_modbus_byd(); -#endif // BYD_MODBUS + handle_static_data_modbus(); // Init Serial2 connected to the RTU Modbus RTUutils::prepareHardwareSerial(Serial2); Serial2.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN); diff --git a/Software/src/devboard/hal/hw_stark.h b/Software/src/devboard/hal/hw_stark.h index 064db9da..152e189f 100644 --- a/Software/src/devboard/hal/hw_stark.h +++ b/Software/src/devboard/hal/hw_stark.h @@ -52,7 +52,7 @@ GPIOs on extra header #define BMS_POWER 23 // SMA CAN contactor pins -#define INVERTER_CONTACTOR_ENABLE_PIN 19 +#define INVERTER_CONTACTOR_ENABLE_PIN 2 // LED #define LED_PIN 4 diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index c3422226..cdad4375 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -11,6 +11,8 @@ #include "../utils/timer.h" #include "esp_task_wdt.h" +void transmit_can_frame(CAN_frame* tx_frame, int interface); + // Create AsyncWebServer object on port 80 AsyncWebServer server(80); diff --git a/Software/src/inverter/BYD-MODBUS.cpp b/Software/src/inverter/BYD-MODBUS.cpp index 8d1a91ef..9e5d811c 100644 --- a/Software/src/inverter/BYD-MODBUS.cpp +++ b/Software/src/inverter/BYD-MODBUS.cpp @@ -17,14 +17,20 @@ static uint8_t history_index = 0; static uint8_t bms_char_dis_status = STANDBY; static bool all_401_values_equal = false; -void update_modbus_registers_inverter() { +void verify_inverter_modbus(); +void verify_temperature_modbus(); +void handle_update_data_modbusp201_byd(); +void handle_update_data_modbusp301_byd(); +void handle_static_data_modbus_byd(); + +void BydModbusInverter::update_modbus_registers() { verify_temperature_modbus(); verify_inverter_modbus(); handle_update_data_modbusp201_byd(); handle_update_data_modbusp301_byd(); } -void handle_static_data_modbus_byd() { +void BydModbusInverter::handle_static_data() { // Store the data into the array static uint16_t si_data[] = {21321, 1}; static uint16_t byd_data[] = {16985, 17408, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -144,8 +150,10 @@ void verify_inverter_modbus() { history_index = (history_index + 1) % HISTORY_LENGTH; } } -void setup_inverter(void) { // Performs one time setup at startup over CAN bus + +void BydModbusInverter::setup(void) { // Performs one time setup at startup over CAN bus strncpy(datalayer.system.info.inverter_protocol, "BYD 11kWh HVM battery over Modbus RTU", 63); datalayer.system.info.inverter_protocol[63] = '\0'; } + #endif diff --git a/Software/src/inverter/BYD-MODBUS.h b/Software/src/inverter/BYD-MODBUS.h index d1326609..70f1d9c6 100644 --- a/Software/src/inverter/BYD-MODBUS.h +++ b/Software/src/inverter/BYD-MODBUS.h @@ -3,16 +3,18 @@ #include "../include.h" #define MODBUS_INVERTER_SELECTED +#define OO_INVERTER_PROTOCOL_SELECTED #define MB_RTU_NUM_VALUES 13100 #define MAX_POWER 40960 //BYD Modbus specific value extern uint16_t mbPV[MB_RTU_NUM_VALUES]; -void handle_static_data_modbus_byd(); -void verify_temperature_modbus(); -void verify_inverter_modbus(); -void handle_update_data_modbusp201_byd(); -void handle_update_data_modbusp301_byd(); -void setup_inverter(void); +class BydModbusInverter : public ModbusInverterProtocol { + public: + void setup(); + void update_modbus_registers(); + void handle_static_data(); +}; + #endif diff --git a/Software/src/inverter/INVERTERS.cpp b/Software/src/inverter/INVERTERS.cpp new file mode 100644 index 00000000..61e93edc --- /dev/null +++ b/Software/src/inverter/INVERTERS.cpp @@ -0,0 +1,24 @@ +#include "../include.h" + +// These functions adapt the old C-style global functions inverter-API to the +// object-oriented inverter protocol API. + +#ifdef OO_INVERTER_PROTOCOL_SELECTED + +ModbusInverterProtocol* inverter; + +void setup_inverter() { + // Currently only a single inverter protocol is implemented as a class + inverter = new BydModbusInverter(); + inverter->setup(); +} + +void update_modbus_registers_inverter() { + inverter->update_modbus_registers(); +} + +void handle_static_data_modbus() { + inverter->handle_static_data(); +} + +#endif diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h index 5cd3bc50..d4f41f08 100644 --- a/Software/src/inverter/INVERTERS.h +++ b/Software/src/inverter/INVERTERS.h @@ -1,6 +1,19 @@ #ifndef INVERTERS_H #define INVERTERS_H +// The abstract base class for all inverter protocols +class InverterProtocol { + public: + virtual void setup() = 0; +}; + +// The abstract base class for all Modbus inverter protocols +class ModbusInverterProtocol : public InverterProtocol { + public: + virtual void update_modbus_registers() = 0; + virtual void handle_static_data(); +}; + #include "../../USER_SETTINGS.h" #ifdef AFORE_CAN @@ -87,6 +100,8 @@ void transmit_can_inverter(unsigned long currentMillis); #ifdef MODBUS_INVERTER_SELECTED void update_modbus_registers_inverter(); +void setup_inverter(); +void handle_static_data_modbus(); #endif #ifdef RS485_INVERTER_SELECTED