Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Add SimpBMS battery support #917

Merged
merged 6 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Software/USER_SETTINGS.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
//#define RENAULT_ZOE_GEN2_BATTERY
//#define SONO_BATTERY
//#define SANTA_FE_PHEV_BATTERY
//#define SIMPBMS_BATTERY
//#define STELLANTIS_ECMP_BATTERY
//#define TESLA_MODEL_3Y_BATTERY
//#define TESLA_MODEL_SX_BATTERY
Expand Down
4 changes: 4 additions & 0 deletions Software/src/battery/BATTERIES.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ void setup_can_shunt();
#include "SANTA-FE-PHEV-BATTERY.h"
#endif

#ifdef SIMPBMS_BATTERY
#include "SIMPBMS-BATTERY.h"
#endif

#if defined(TESLA_MODEL_SX_BATTERY) || defined(TESLA_MODEL_3Y_BATTERY)
#define TESLA_BATTERY
#include "TESLA-BATTERY.h"
Expand Down
140 changes: 140 additions & 0 deletions Software/src/battery/SIMPBMS-BATTERY.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include "../include.h"
#ifdef SIMPBMS_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SIMPBMS-BATTERY.h"

#define SIMPBMS_MAX_CELLS 128

/* 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

//Actual content messages

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 cells_in_series = 0;
static uint8_t voltage_level = 0;
static uint8_t ah_total = 0;
static uint8_t SOC = 0;
static uint8_t SOH = 0;
static uint8_t charge_forbidden = 0;
static uint8_t discharge_forbidden = 0;
static uint16_t cellvoltages_mV[SIMPBMS_MAX_CELLS] = {0};


void update_values_battery() {

datalayer.battery.status.real_soc = (SOC * 100); //increase SOC range from 0-100 -> 100.00

datalayer.battery.status.soh_pptt = (SOH * 100); //Increase decimals from 100% -> 100.00%

datalayer.battery.status.voltage_dV = voltage_dV; //value is *10 (3700 = 370.0)

datalayer.battery.status.current_dA = current_dA; //value is *10 (150 = 15.0) , invert the sign

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.info.total_capacity_Wh = ah_total * (voltage_dV / 10);

datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);

datalayer.battery.status.cell_max_voltage_mV = cellvoltage_max_mV;

datalayer.battery.status.cell_min_voltage_mV = cellvoltage_min_mV;

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;

memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_mV, SIMPBMS_MAX_CELLS * sizeof(uint16_t));

datalayer.battery.info.number_of_cells = cells_in_series;
}

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 0x355:
SOC = (rx_frame.data.u8[1] << 8) + rx_frame.data.u8[0];
SOH = (rx_frame.data.u8[3] << 8) + rx_frame.data.u8[2];

break;
case 0x351:
//discharge and charge limits
charge_cutoff_voltage = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
discharge_cutoff_voltage = ((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]);
max_charge_current = (((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2])) /10 ;
max_discharge_current = (((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4])) / 10;
break;
case 0x356:
//current status
current_dA = ((rx_frame.data.u8[3]<< 8) + rx_frame.data.u8[2]) / 1000;
voltage_dV = ((rx_frame.data.u8[1]<< 8) + rx_frame.data.u8[0]) / 10 ;
break;
case 0x373:
//min/max values
cellvoltage_max_mV = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
cellvoltage_min_mV = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
celltemperature_max_dC = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) - 273.15) * 10;
celltemperature_min_dC = (((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) - 273.15) * 10;
break;
case 0x372:
//cells_in_series = rx_frame.data.u8[0];
if (rx_frame.data.u8[3] > 0 && rx_frame.data.u8[3] <= SIMPBMS_MAX_CELLS) {
uint8_t cellnumber = rx_frame.data.u8[3];
if (cellnumber > cells_in_series) {
cells_in_series = cellnumber;
}
cellvoltages_mV[cellnumber - 1] = ((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]);
}

break;
case 0x379:
ah_total = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);

break;


default:
break;
}
}

void transmit_can_battery() {
unsigned long currentMillis = millis();
// Send 1s CAN Message
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
previousMillis1000 = currentMillis;
}
}

void setup_battery(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "SIMPBMS battery", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = CELL_COUNT;
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;

}

#endif
19 changes: 19 additions & 0 deletions Software/src/battery/SIMPBMS-BATTERY.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef SIMPBMS_BATTERY_H
#define SIMPBMS_BATTERY_H
#include <Arduino.h>
#include "../include.h"

#define BATTERY_SELECTED

/* DEFAULT VALUES BMS will send configured */
#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 500
#define CELL_COUNT 96

void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);

#endif
11 changes: 10 additions & 1 deletion Software/src/devboard/webserver/advanced_battery_html.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,15 @@ String advanced_battery_processor(const String& var) {
content += "<h4>Temperature sensor 10: " + String(datalayer_extended.bydAtto3.battery_temperatures[9]) + "</h4>";
#endif //BYD_ATTO_3_BATTERY

#ifdef SIMPBMS_BATTERY
content += "<h4>Max Design Voltage: " + String(datalayer.battery.info.max_design_voltage_dV) + " dV</h4>";
content += "<h4>Min Design Voltage: " + String(datalayer.battery.info.min_design_voltage_dV) + " dV</h4>";
content += "<h4>Max Charge Current: " + String(datalayer.battery.status.max_charge_current_dA) + " dA</h4>";
content += "<h4>Max Discharge Current: " + String(datalayer.battery.status.max_discharge_current_dA) + " dA</h4>";
content += "<h4>Pack Voltage: " + String( datalayer.battery.status.voltage_dV) + " dV</h4>";
content += "<h4>Reported Current: " + String( datalayer.battery.status.current_dA) + " dA</h4>";
content += "<h4>Series Cells: " + String(datalayer.battery.info.number_of_cells) + " dV</h4>";
#endif
#ifdef TESLA_BATTERY
float beginning_of_life = static_cast<float>(datalayer_extended.tesla.battery_beginning_of_life);
float battTempPct = static_cast<float>(datalayer_extended.tesla.battery_battTempPct) * 0.4;
Expand Down Expand Up @@ -1415,7 +1424,7 @@ String advanced_battery_processor(const String& var) {
!defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && \
!defined(BYD_ATTO_3_BATTERY) && !defined(RENAULT_ZOE_GEN2_BATTERY) && !defined(CELLPOWER_BMS) && \
!defined(MEB_BATTERY) && !defined(VOLVO_SPA_BATTERY) && !defined(VOLVO_SPA_HYBRID_BATTERY) && \
!defined(KIA_HYUNDAI_64_BATTERY) //Only the listed types have extra info
!defined(KIA_HYUNDAI_64_BATTERY) && !defined(SIMPBMS_BATTERY) //Only the listed types have extra info
content += "No extra information available for this battery type";
#endif

Expand Down