From 9ebf99445e3c7befecbb173d20836dda1d718276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 12 Feb 2025 19:47:27 +0200 Subject: [PATCH 1/6] Add Orion BMS support --- Software/USER_SETTINGS.h | 1 + Software/src/battery/BATTERIES.h | 4 + Software/src/battery/ORION-BMS.cpp | 117 +++++++++++++++++++++++++++++ Software/src/battery/ORION-BMS.h | 19 +++++ 4 files changed, 141 insertions(+) create mode 100644 Software/src/battery/ORION-BMS.cpp create mode 100644 Software/src/battery/ORION-BMS.h diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index c6db1bc2..61245080 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -25,6 +25,7 @@ //#define MEB_BATTERY //#define MG_5_BATTERY //#define NISSAN_LEAF_BATTERY +//#define ORION_BMS //#define PYLON_BATTERY //#define DALY_BMS //#define RJXZS_BMS diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 27b684bf..81a3ab40 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -42,6 +42,10 @@ void setup_can_shunt(); #include "FOXESS-BATTERY.h" #endif +#ifdef ORION_BMS +#include "ORION-BMS.h" +#endif + #ifdef SONO_BATTERY #include "SONO-BATTERY.h" #endif diff --git a/Software/src/battery/ORION-BMS.cpp b/Software/src/battery/ORION-BMS.cpp new file mode 100644 index 00000000..12d92834 --- /dev/null +++ b/Software/src/battery/ORION-BMS.cpp @@ -0,0 +1,117 @@ +#include "../include.h" +#ifdef ORION_BMS +#include "../datalayer/datalayer.h" +#include "../devboard/utils/events.h" +#include "ORION-BMS.h" + +/* Do not change code below unless you are sure what you are doing */ +static uint16_t cellvoltages[MAX_AMOUNT_CELLS]; //array with all the cellvoltages +static uint16_t Maximum_Cell_Voltage = 3700; +static uint16_t Minimum_Cell_Voltage = 3700; +static uint16_t Pack_Health = 0; +static int16_t Pack_Current = 0; +static int16_t Average_Temperature = 0; +static uint16_t Pack_Summed_Voltage = 0; +static int16_t Average_Current = 0; +static uint16_t High_Temperature = 0; +static uint16_t Pack_SOC_ppt = 0; +static uint16_t Pack_CCL = 0; +static uint16_t Pack_DCL = 0; +static uint16_t Maximum_Pack_Voltage = 0; +static uint16_t Minimum_Pack_Voltage = 0; +static uint16_t CellID = 0; +static uint16_t CellVoltage = 0; +static uint16_t CellResistance = 0; +static uint16_t CellOpenVoltage = 0; +static uint16_t Checksum = 0; +static uint16_t CellBalancing = 0; +static uint8_t amount_of_detected_cells = 0; + +void update_values_battery() { + + datalayer.battery.status.real_soc = Pack_SOC_ppt * 10; + + datalayer.battery.status.soh_pptt = Pack_Health; + + datalayer.battery.status.voltage_dV = (Pack_Summed_Voltage / 10); + + datalayer.battery.status.current_dA = Average_Current; + + datalayer.battery.status.max_charge_power_W = (Pack_CCL * datalayer.battery.status.voltage_dV) / 100; + + datalayer.battery.status.max_discharge_power_W = (Pack_DCL * datalayer.battery.status.voltage_dV) / 100; + + 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.cell_max_voltage_mV = Maximum_Cell_Voltage; + + datalayer.battery.status.cell_min_voltage_mV = Minimum_Cell_Voltage; + + datalayer.battery.status.temperature_min_dC = (High_Temperature - 10); + + datalayer.battery.status.temperature_max_dC = High_Temperature; + + //Map all cell voltages to the global array + memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages, MAX_AMOUNT_CELLS * sizeof(uint16_t)); +} + +void handle_incoming_can_frame_battery(CAN_frame rx_frame) { + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + switch (rx_frame.ID) { + case 0x356: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + Pack_Summed_Voltage = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]; + Average_Current = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]; + High_Temperature = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; + break; + case 0x351: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + Maximum_Pack_Voltage = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]; + Pack_CCL = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]; + Pack_DCL = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; + Minimum_Pack_Voltage = (rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]; + break; + case 0x355: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + Pack_Health = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]; + Pack_SOC_ppt = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; + break; + case 0x35A: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x36: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + CellID = rx_frame.data.u8[0]; + CellBalancing = (rx_frame.data.u8[3] & 0x80) >> 7; + CellVoltage = (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]; + CellResistance = ((rx_frame.data.u8[3] & 0x7F) << 8) | rx_frame.data.u8[4]; + CellOpenVoltage = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + Checksum = rx_frame.data.u8[7]; //Value = (0x36 + 8 + byte0 + byte1 + ... + byte6) & 0xFF + + cellvoltages[CellID] = (CellVoltage / 10); + if (CellID > amount_of_detected_cells) { + amount_of_detected_cells = CellID; + } + break; + default: + break; + } +} + +void transmit_can_battery() { + unsigned long currentMillis = millis(); + // No transmission needed for this integration +} + +void setup_battery(void) { // Performs one time setup at startup + strncpy(datalayer.system.info.battery_protocol, "DIY battery with Orion BMS (Victron setting)", 63); + datalayer.system.info.battery_protocol[63] = '\0'; + datalayer.battery.info.number_of_cells = NUMBER_OF_CELLS; + 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; +} + +#endif diff --git a/Software/src/battery/ORION-BMS.h b/Software/src/battery/ORION-BMS.h new file mode 100644 index 00000000..6412d92f --- /dev/null +++ b/Software/src/battery/ORION-BMS.h @@ -0,0 +1,19 @@ +#ifndef ORION_BMS_H +#define ORION_BMS_H +#include +#include "../include.h" + +#define BATTERY_SELECTED + +/* Change the following to suit your battery */ +#define NUMBER_OF_CELLS 96 +#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 + +void setup_battery(void); +void transmit_can_frame(CAN_frame* tx_frame, int interface); + +#endif From a6655206b5f2ad44f07bd4810bd9a433b3efd439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 17 Feb 2025 15:13:46 +0200 Subject: [PATCH 2/6] Add min/max voltage mapping --- Software/src/battery/ORION-BMS.cpp | 36 +++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/Software/src/battery/ORION-BMS.cpp b/Software/src/battery/ORION-BMS.cpp index 12d92834..48087622 100644 --- a/Software/src/battery/ORION-BMS.cpp +++ b/Software/src/battery/ORION-BMS.cpp @@ -27,6 +27,29 @@ static uint16_t Checksum = 0; static uint16_t CellBalancing = 0; static uint8_t amount_of_detected_cells = 0; +void findMinMaxCellvoltages(const uint16_t arr[], size_t size, uint16_t& Minimum_Cell_Voltage, + uint16_t& Maximum_Cell_Voltage) { + Minimum_Cell_Voltage = std::numeric_limits::max(); + Maximum_Cell_Voltage = 0; + bool foundValidValue = false; + + for (size_t i = 0; i < size; ++i) { + if (arr[i] != 0) { // Skip zero values + if (arr[i] < Minimum_Cell_Voltage) + Minimum_Cell_Voltage = arr[i]; + if (arr[i] > Maximum_Cell_Voltage) + Maximum_Cell_Voltage = arr[i]; + foundValidValue = true; + } + } + + // If all values were zero, set min and max to 3700 + if (!foundValidValue) { + Minimum_Cell_Voltage = 3700; + Maximum_Cell_Voltage = 3700; + } +} + void update_values_battery() { datalayer.battery.status.real_soc = Pack_SOC_ppt * 10; @@ -44,20 +67,22 @@ void update_values_battery() { 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.cell_max_voltage_mV = Maximum_Cell_Voltage; - - datalayer.battery.status.cell_min_voltage_mV = Minimum_Cell_Voltage; - datalayer.battery.status.temperature_min_dC = (High_Temperature - 10); datalayer.battery.status.temperature_max_dC = High_Temperature; //Map all cell voltages to the global array memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages, MAX_AMOUNT_CELLS * sizeof(uint16_t)); + + //Find min and max cellvoltage from the array + findMinMaxCellvoltages(cellvoltages, MAX_AMOUNT_CELLS, Minimum_Cell_Voltage, Maximum_Cell_Voltage); + + datalayer.battery.status.cell_max_voltage_mV = Maximum_Cell_Voltage; + + datalayer.battery.status.cell_min_voltage_mV = Minimum_Cell_Voltage; } void handle_incoming_can_frame_battery(CAN_frame rx_frame) { - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; switch (rx_frame.ID) { case 0x356: datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; @@ -112,6 +137,7 @@ void setup_battery(void) { // Performs one time setup at startup 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; } #endif From 2654d333203803689e7cd3d65e89f20acce8e905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 17 Feb 2025 15:37:10 +0200 Subject: [PATCH 3/6] Update cellvoltage amount on the fly if it exceeds configuration --- Software/src/battery/ORION-BMS.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Software/src/battery/ORION-BMS.cpp b/Software/src/battery/ORION-BMS.cpp index 48087622..f137cb86 100644 --- a/Software/src/battery/ORION-BMS.cpp +++ b/Software/src/battery/ORION-BMS.cpp @@ -80,6 +80,11 @@ void update_values_battery() { datalayer.battery.status.cell_max_voltage_mV = Maximum_Cell_Voltage; datalayer.battery.status.cell_min_voltage_mV = Minimum_Cell_Voltage; + + //If user did not configure amount of cells correctly in the header file, update the value + if ((amount_of_detected_cells > NUMBER_OF_CELLS) && (amount_of_detected_cells < MAX_AMOUNT_CELLS)) { + datalayer.battery.info.number_of_cells = amount_of_detected_cells; + } } void handle_incoming_can_frame_battery(CAN_frame rx_frame) { From 97097238e64dbddc7e9311d5c627badb91ea610d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 17 Feb 2025 23:11:49 +0200 Subject: [PATCH 4/6] Change scaling of SOH --- Software/src/battery/ORION-BMS.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/ORION-BMS.cpp b/Software/src/battery/ORION-BMS.cpp index f137cb86..2f502601 100644 --- a/Software/src/battery/ORION-BMS.cpp +++ b/Software/src/battery/ORION-BMS.cpp @@ -8,15 +8,15 @@ static uint16_t cellvoltages[MAX_AMOUNT_CELLS]; //array with all the cellvoltages static uint16_t Maximum_Cell_Voltage = 3700; static uint16_t Minimum_Cell_Voltage = 3700; -static uint16_t Pack_Health = 0; +static uint16_t Pack_Health = 99; static int16_t Pack_Current = 0; static int16_t Average_Temperature = 0; static uint16_t Pack_Summed_Voltage = 0; static int16_t Average_Current = 0; static uint16_t High_Temperature = 0; static uint16_t Pack_SOC_ppt = 0; -static uint16_t Pack_CCL = 0; -static uint16_t Pack_DCL = 0; +static uint16_t Pack_CCL = 0; //Charge current limit (A) +static uint16_t Pack_DCL = 0; //Discharge current limit (A) static uint16_t Maximum_Pack_Voltage = 0; static uint16_t Minimum_Pack_Voltage = 0; static uint16_t CellID = 0; @@ -54,7 +54,7 @@ void update_values_battery() { datalayer.battery.status.real_soc = Pack_SOC_ppt * 10; - datalayer.battery.status.soh_pptt = Pack_Health; + datalayer.battery.status.soh_pptt = Pack_Health * 100; datalayer.battery.status.voltage_dV = (Pack_Summed_Voltage / 10); From e938f2c8d2e4eaaf323dda58da90ac0706b03e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 19 Feb 2025 21:14:09 +0200 Subject: [PATCH 5/6] Add OrionBMS to workflow --- .github/workflows/compile-all-batteries.yml | 1 + .../compile-all-combinations-part2-batteries-N-to-Z.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/compile-all-batteries.yml b/.github/workflows/compile-all-batteries.yml index 7a60ec63..aa553e24 100644 --- a/.github/workflows/compile-all-batteries.yml +++ b/.github/workflows/compile-all-batteries.yml @@ -64,6 +64,7 @@ jobs: - MEB_BATTERY - MG_5_BATTERY - NISSAN_LEAF_BATTERY + - ORION_BMS - PYLON_BATTERY - RJXZS_BMS - RANGE_ROVER_PHEV_BATTERY diff --git a/.github/workflows/compile-all-combinations-part2-batteries-N-to-Z.yml b/.github/workflows/compile-all-combinations-part2-batteries-N-to-Z.yml index 8a4f76e2..a522b186 100644 --- a/.github/workflows/compile-all-combinations-part2-batteries-N-to-Z.yml +++ b/.github/workflows/compile-all-combinations-part2-batteries-N-to-Z.yml @@ -55,6 +55,7 @@ jobs: # These are the batteries for which the code will be compiled. battery: - NISSAN_LEAF_BATTERY + - ORION_BMS - PYLON_BATTERY - RJXZS_BMS - RANGE_ROVER_PHEV_BATTERY From be0d05471f591fad53f8f22b7794922ffd2be695 Mon Sep 17 00:00:00 2001 From: Marijn van Galen Date: Thu, 20 Feb 2025 21:17:17 +0100 Subject: [PATCH 6/6] Prevent overflow of cellvoltages array. --- Software/src/battery/ORION-BMS.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Software/src/battery/ORION-BMS.cpp b/Software/src/battery/ORION-BMS.cpp index 2f502601..f0ba33a8 100644 --- a/Software/src/battery/ORION-BMS.cpp +++ b/Software/src/battery/ORION-BMS.cpp @@ -119,6 +119,9 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { CellOpenVoltage = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; Checksum = rx_frame.data.u8[7]; //Value = (0x36 + 8 + byte0 + byte1 + ... + byte6) & 0xFF + if (CellID >= MAX_AMOUNT_CELLS) { + CellID = MAX_AMOUNT_CELLS; + } cellvoltages[CellID] = (CellVoltage / 10); if (CellID > amount_of_detected_cells) { amount_of_detected_cells = CellID;