diff --git a/Software/src/battery/PYLON-BATTERY.cpp b/Software/src/battery/PYLON-BATTERY.cpp index 41b8a9ba..02a8c96b 100644 --- a/Software/src/battery/PYLON-BATTERY.cpp +++ b/Software/src/battery/PYLON-BATTERY.cpp @@ -1,89 +1,44 @@ #include "../include.h" #ifdef PYLON_BATTERY +#include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" #include "PYLON-BATTERY.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 +void PylonBattery::update_values() { -//Actual content messages -CAN_frame PYLON_3010 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3010, - .data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame PYLON_8200 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x8200, - .data = {0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame PYLON_8210 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x8210, - .data = {0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame PYLON_4200 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x4200, - .data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + datalayer_battery->status.real_soc = (SOC * 100); //increase SOC range from 0-100 -> 100.00 -static int16_t celltemperature_max_dC = 0; -static int16_t celltemperature_min_dC = 0; -static int16_t current_dA = 0; -static uint16_t voltage_dV = 0; -static uint16_t cellvoltage_max_mV = 3700; -static uint16_t cellvoltage_min_mV = 3700; -static uint16_t charge_cutoff_voltage = 0; -static uint16_t discharge_cutoff_voltage = 0; -static int16_t max_charge_current = 0; -static int16_t max_discharge_current = 0; -static uint8_t ensemble_info_ack = 0; -static uint8_t battery_module_quantity = 0; -static uint8_t battery_modules_in_series = 0; -static uint8_t cell_quantity_in_module = 0; -static uint8_t voltage_level = 0; -static uint8_t ah_number = 0; -static uint8_t SOC = 0; -static uint8_t SOH = 0; -static uint8_t charge_forbidden = 0; -static uint8_t discharge_forbidden = 0; + datalayer_battery->status.soh_pptt = (SOH * 100); //Increase decimals from 100% -> 100.00% -void update_values_battery() { + datalayer_battery->status.voltage_dV = voltage_dV; //value is *10 (3700 = 370.0) - datalayer.battery.status.real_soc = (SOC * 100); //increase SOC range from 0-100 -> 100.00 + datalayer_battery->status.current_dA = current_dA; //value is *10 (150 = 15.0) , invert the sign - datalayer.battery.status.soh_pptt = (SOH * 100); //Increase decimals from 100% -> 100.00% + datalayer_battery->status.max_charge_power_W = (max_charge_current * (voltage_dV / 10)); - datalayer.battery.status.voltage_dV = voltage_dV; //value is *10 (3700 = 370.0) + datalayer_battery->status.max_discharge_power_W = (-max_discharge_current * (voltage_dV / 10)); - datalayer.battery.status.current_dA = current_dA; //value is *10 (150 = 15.0) , invert the sign + 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 = (max_charge_current * (voltage_dV / 10)); + datalayer_battery->status.cell_max_voltage_mV = cellvoltage_max_mV; + datalayer_battery->status.cell_voltages_mV[0] = cellvoltage_max_mV; - datalayer.battery.status.max_discharge_power_W = (-max_discharge_current * (voltage_dV / 10)); + datalayer_battery->status.cell_min_voltage_mV = cellvoltage_min_mV; + datalayer_battery->status.cell_voltages_mV[1] = cellvoltage_min_mV; - 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.temperature_min_dC = celltemperature_min_dC; - datalayer.battery.status.cell_max_voltage_mV = cellvoltage_max_mV; - datalayer.battery.status.cell_voltages_mV[0] = cellvoltage_max_mV; + datalayer_battery->status.temperature_max_dC = celltemperature_max_dC; - datalayer.battery.status.cell_min_voltage_mV = cellvoltage_min_mV; - datalayer.battery.status.cell_voltages_mV[1] = cellvoltage_min_mV; + datalayer_battery->info.max_design_voltage_dV = charge_cutoff_voltage; - datalayer.battery.status.temperature_min_dC = celltemperature_min_dC; - - datalayer.battery.status.temperature_max_dC = celltemperature_max_dC; - - datalayer.battery.info.max_design_voltage_dV = charge_cutoff_voltage; - - datalayer.battery.info.min_design_voltage_dV = discharge_cutoff_voltage; + datalayer_battery->info.min_design_voltage_dV = discharge_cutoff_voltage; } -void handle_incoming_can_frame_battery(CAN_frame rx_frame) { - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; +void PylonBattery::handle_incoming_can_frame(CAN_frame rx_frame) { + datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE; switch (rx_frame.ID) { case 0x7310: case 0x7311: @@ -158,7 +113,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { } } -void transmit_can_battery(unsigned long currentMillis) { +void PylonBattery::transmit_can(unsigned long currentMillis) { // Send 1s CAN Message if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { previousMillis1000 = currentMillis; @@ -168,170 +123,26 @@ void transmit_can_battery(unsigned long currentMillis) { transmit_can_frame(&PYLON_8200, can_config.battery); // Control device quit sleep status transmit_can_frame(&PYLON_8210, can_config.battery); // Charge command -#ifdef DOUBLE_BATTERY - transmit_can_frame(&PYLON_3010, can_config.battery_double); // Heartbeat - transmit_can_frame(&PYLON_4200, can_config.battery_double); // Ensemble OR System equipment info, depends on frame0 - transmit_can_frame(&PYLON_8200, can_config.battery_double); // Control device quit sleep status - transmit_can_frame(&PYLON_8210, can_config.battery_double); // Charge command -#endif //DOUBLE_BATTERY - if (ensemble_info_ack) { PYLON_4200.data.u8[0] = 0x00; //Request system equipment info } } } -#ifdef DOUBLE_BATTERY - -static int16_t battery2_celltemperature_max_dC = 0; -static int16_t battery2_celltemperature_min_dC = 0; -static int16_t battery2_current_dA = 0; -static uint16_t battery2_voltage_dV = 0; -static uint16_t battery2_cellvoltage_max_mV = 3700; -static uint16_t battery2_cellvoltage_min_mV = 3700; -static uint16_t battery2_charge_cutoff_voltage = 0; -static uint16_t battery2_discharge_cutoff_voltage = 0; -static int16_t battery2_max_charge_current = 0; -static int16_t battery2_max_discharge_current = 0; -static uint8_t battery2_ensemble_info_ack = 0; -static uint8_t battery2_module_quantity = 0; -static uint8_t battery2_modules_in_series = 0; -static uint8_t battery2_cell_quantity_in_module = 0; -static uint8_t battery2_voltage_level = 0; -static uint8_t battery2_ah_number = 0; -static uint8_t battery2_SOC = 0; -static uint8_t battery2_SOH = 0; -static uint8_t battery2_charge_forbidden = 0; -static uint8_t battery2_discharge_forbidden = 0; - -void update_values_battery2() { - - datalayer.battery2.status.real_soc = (battery2_SOC * 100); //increase SOC range from 0-100 -> 100.00 - - datalayer.battery2.status.soh_pptt = (battery2_SOH * 100); //Increase decimals from 100% -> 100.00% - - datalayer.battery2.status.voltage_dV = battery2_voltage_dV; //value is *10 (3700 = 370.0) - - datalayer.battery2.status.current_dA = battery2_current_dA; //value is *10 (150 = 15.0) , invert the sign - - datalayer.battery2.status.max_charge_power_W = (battery2_max_charge_current * (battery2_voltage_dV / 10)); - - datalayer.battery2.status.max_discharge_power_W = (-battery2_max_discharge_current * (battery2_voltage_dV / 10)); - - datalayer.battery2.status.remaining_capacity_Wh = static_cast( - (static_cast(datalayer.battery2.status.real_soc) / 10000) * datalayer.battery2.info.total_capacity_Wh); - - datalayer.battery2.status.cell_max_voltage_mV = battery2_cellvoltage_max_mV; - datalayer.battery2.status.cell_voltages_mV[0] = battery2_cellvoltage_max_mV; - - datalayer.battery2.status.cell_min_voltage_mV = battery2_cellvoltage_min_mV; - datalayer.battery2.status.cell_voltages_mV[1] = battery2_cellvoltage_min_mV; - - datalayer.battery2.status.temperature_min_dC = battery2_celltemperature_min_dC; - - datalayer.battery2.status.temperature_max_dC = battery2_celltemperature_max_dC; - - datalayer.battery2.info.max_design_voltage_dV = battery2_charge_cutoff_voltage; - - datalayer.battery2.info.min_design_voltage_dV = battery2_discharge_cutoff_voltage; - - datalayer.battery2.info.number_of_cells = datalayer.battery.info.number_of_cells; -} - -void handle_incoming_can_frame_battery2(CAN_frame rx_frame) { - datalayer.battery2.status.CAN_battery_still_alive = CAN_STILL_ALIVE; - switch (rx_frame.ID) { - case 0x7310: - case 0x7311: - battery2_ensemble_info_ack = true; - // This message contains software/hardware version info. No interest to us - break; - case 0x7320: - case 0x7321: - battery2_ensemble_info_ack = true; - battery2_module_quantity = rx_frame.data.u8[0]; - battery2_modules_in_series = rx_frame.data.u8[2]; - battery2_cell_quantity_in_module = rx_frame.data.u8[3]; - battery2_voltage_level = rx_frame.data.u8[4]; - battery2_ah_number = rx_frame.data.u8[6]; - break; - case 0x4210: - case 0x4211: - battery2_voltage_dV = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); - battery2_current_dA = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 30000; - battery2_SOC = rx_frame.data.u8[6]; - battery2_SOH = rx_frame.data.u8[7]; - break; - case 0x4220: - case 0x4221: - battery2_charge_cutoff_voltage = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); - battery2_discharge_cutoff_voltage = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); - battery2_max_charge_current = (((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) * 0.1) - 3000; - battery2_max_discharge_current = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) * 0.1) - 3000; - break; - case 0x4230: - case 0x4231: - battery2_cellvoltage_max_mV = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); - battery2_cellvoltage_min_mV = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); - break; - case 0x4240: - case 0x4241: - battery2_celltemperature_max_dC = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) - 1000; - battery2_celltemperature_min_dC = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 1000; - break; - case 0x4250: - case 0x4251: - //Byte0 Basic Status - //Byte1-2 Cycle Period - //Byte3 Error - //Byte4-5 Alarm - //Byte6-7 Protection - break; - case 0x4260: - case 0x4261: - //Byte0-1 Module Max Voltage - //Byte2-3 Module Min Voltage - //Byte4-5 Module Max. Voltage Number - //Byte6-7 Module Min. Voltage Number - break; - case 0x4270: - case 0x4271: - //Byte0-1 Module Max. Temperature - //Byte2-3 Module Min. Temperature - //Byte4-5 Module Max. Temperature Number - //Byte6-7 Module Min. Temperature Number - break; - case 0x4280: - case 0x4281: - battery2_charge_forbidden = rx_frame.data.u8[0]; - battery2_discharge_forbidden = rx_frame.data.u8[1]; - break; - case 0x4290: - case 0x4291: - break; - default: - break; - } -} -#endif //DOUBLE_BATTERY - -void setup_battery(void) { // Performs one time setup at startup +void PylonBattery::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", 63); datalayer.system.info.battery_protocol[63] = '\0'; - datalayer.battery.info.number_of_cells = 2; - datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; - datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; - datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; - datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; - datalayer.system.status.battery_allows_contactor_closing = true; -#ifdef DOUBLE_BATTERY - datalayer.battery2.info.number_of_cells = datalayer.battery.info.number_of_cells; - 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.info.max_cell_voltage_mV = datalayer.battery.info.max_cell_voltage_mV; - datalayer.battery2.info.min_cell_voltage_mV = datalayer.battery.info.min_cell_voltage_mV; - datalayer.battery2.info.max_cell_voltage_deviation_mV = datalayer.battery.info.max_cell_voltage_deviation_mV; -#endif //DOUBLE_BATTERY + datalayer_battery->info.number_of_cells = 2; + datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; + datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; + datalayer_battery->info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; + datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + + datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; + + if (allows_contactor_closing) { + *allows_contactor_closing = true; + } } #endif diff --git a/Software/src/battery/PYLON-BATTERY.h b/Software/src/battery/PYLON-BATTERY.h index 026a8c75..326f8157 100644 --- a/Software/src/battery/PYLON-BATTERY.h +++ b/Software/src/battery/PYLON-BATTERY.h @@ -1,18 +1,99 @@ #ifndef PYLON_BATTERY_H #define PYLON_BATTERY_H #include + +#include "../datalayer/datalayer.h" #include "../include.h" +#include "CanBattery.h" #define BATTERY_SELECTED +#define SELECTED_BATTERY_CLASS PylonBattery -/* Change the following to suit your battery */ -#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V -#define MIN_PACK_VOLTAGE_DV 1500 -#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 -#define MAX_CELL_DEVIATION_MV 150 +class PylonBattery : public CanBattery { + public: + // Use this constructor for the second battery. + PylonBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* contactor_closing_allowed_ptr, int targetCan) { + datalayer_battery = datalayer_ptr; + contactor_closing_allowed = contactor_closing_allowed_ptr; + allows_contactor_closing = nullptr; + can_interface = targetCan; + } -void setup_battery(void); -void transmit_can_frame(CAN_frame* tx_frame, int interface); + // Use the default constructor to create the first or single battery. + PylonBattery() { + datalayer_battery = &datalayer.battery; + allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing; + contactor_closing_allowed = nullptr; + can_interface = can_config.battery; + } + + 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); + + private: + /* Change the following to suit your battery */ + static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V + static const int MIN_PACK_VOLTAGE_DV = 1500; + static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value + static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value + static const int MAX_CELL_DEVIATION_MV = 150; + + DATALAYER_BATTERY_TYPE* datalayer_battery; + + // If not null, this battery decides when the contactor can be closed and writes the value here. + bool* allows_contactor_closing; + + // If not null, this battery listens to this boolean to determine whether contactor closing is allowed + bool* contactor_closing_allowed; + + int can_interface; + + unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent + + //Actual content messages + CAN_frame PYLON_3010 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3010, + .data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame PYLON_8200 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x8200, + .data = {0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame PYLON_8210 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x8210, + .data = {0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame PYLON_4200 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x4200, + .data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + int16_t celltemperature_max_dC = 0; + int16_t celltemperature_min_dC = 0; + int16_t current_dA = 0; + uint16_t voltage_dV = 0; + uint16_t cellvoltage_max_mV = 3700; + uint16_t cellvoltage_min_mV = 3700; + uint16_t charge_cutoff_voltage = 0; + uint16_t discharge_cutoff_voltage = 0; + int16_t max_charge_current = 0; + int16_t max_discharge_current = 0; + uint8_t ensemble_info_ack = 0; + uint8_t battery_module_quantity = 0; + uint8_t battery_modules_in_series = 0; + uint8_t cell_quantity_in_module = 0; + uint8_t voltage_level = 0; + uint8_t ah_number = 0; + uint8_t SOC = 0; + uint8_t SOH = 0; + uint8_t charge_forbidden = 0; + uint8_t discharge_forbidden = 0; +}; #endif