mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 10:49:42 +02:00
Functional SimpBMS support
This commit is contained in:
parent
566039be5d
commit
131312a8cd
5 changed files with 225 additions and 1 deletions
|
@ -34,6 +34,7 @@
|
||||||
//#define RENAULT_ZOE_GEN2_BATTERY
|
//#define RENAULT_ZOE_GEN2_BATTERY
|
||||||
//#define SONO_BATTERY
|
//#define SONO_BATTERY
|
||||||
//#define SANTA_FE_PHEV_BATTERY
|
//#define SANTA_FE_PHEV_BATTERY
|
||||||
|
//#define SIMPBMS_BATTERY
|
||||||
//#define STELLANTIS_ECMP_BATTERY
|
//#define STELLANTIS_ECMP_BATTERY
|
||||||
//#define TESLA_MODEL_3Y_BATTERY
|
//#define TESLA_MODEL_3Y_BATTERY
|
||||||
//#define TESLA_MODEL_SX_BATTERY
|
//#define TESLA_MODEL_SX_BATTERY
|
||||||
|
|
|
@ -114,6 +114,10 @@ void setup_can_shunt();
|
||||||
#include "SANTA-FE-PHEV-BATTERY.h"
|
#include "SANTA-FE-PHEV-BATTERY.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SIMPBMS_BATTERY
|
||||||
|
#include "SIMPBMS-BATTERY.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(TESLA_MODEL_SX_BATTERY) || defined(TESLA_MODEL_3Y_BATTERY)
|
#if defined(TESLA_MODEL_SX_BATTERY) || defined(TESLA_MODEL_3Y_BATTERY)
|
||||||
#define TESLA_BATTERY
|
#define TESLA_BATTERY
|
||||||
#include "TESLA-BATTERY.h"
|
#include "TESLA-BATTERY.h"
|
||||||
|
|
191
Software/src/battery/SIMPBMS-BATTERY.cpp
Normal file
191
Software/src/battery/SIMPBMS-BATTERY.cpp
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
#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]);
|
||||||
|
// msg.buf[0] = lowByte(settings.CAP);
|
||||||
|
// msg.buf[1] = highByte(settings.CAP);
|
||||||
|
// msg.buf[2] = lowByte(uint16_t(amphours));
|
||||||
|
// msg.buf[3] = highByte(uint16_t(amphours));
|
||||||
|
|
||||||
|
break;
|
||||||
|
// case 0x7310:
|
||||||
|
// case 0x7311:
|
||||||
|
// ensemble_info_ack = true;
|
||||||
|
// // This message contains software/hardware version info. No interest to us
|
||||||
|
// break;
|
||||||
|
// case 0x7320:
|
||||||
|
// case 0x7321:
|
||||||
|
// ensemble_info_ack = true;
|
||||||
|
// battery_module_quantity = rx_frame.data.u8[0];
|
||||||
|
// battery_modules_in_series = rx_frame.data.u8[2];
|
||||||
|
// cell_quantity_in_module = rx_frame.data.u8[3];
|
||||||
|
// voltage_level = rx_frame.data.u8[4];
|
||||||
|
// ah_number = rx_frame.data.u8[6];
|
||||||
|
// break;
|
||||||
|
// case 0x4210:
|
||||||
|
// case 0x4211:
|
||||||
|
// voltage_dV = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
|
||||||
|
// current_dA = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 30000;
|
||||||
|
// SOC = rx_frame.data.u8[6];
|
||||||
|
// SOH = rx_frame.data.u8[7];
|
||||||
|
// break;
|
||||||
|
// case 0x4220:
|
||||||
|
// case 0x4221:
|
||||||
|
// charge_cutoff_voltage = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
|
||||||
|
// discharge_cutoff_voltage = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
|
||||||
|
// max_charge_current = (((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) * 0.1) - 3000;
|
||||||
|
// max_discharge_current = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) * 0.1) - 3000;
|
||||||
|
// break;
|
||||||
|
// case 0x4230:
|
||||||
|
// case 0x4231:
|
||||||
|
// cellvoltage_max_mV = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
|
||||||
|
// cellvoltage_min_mV = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
|
||||||
|
// break;
|
||||||
|
// case 0x4240:
|
||||||
|
// case 0x4241:
|
||||||
|
// celltemperature_max_dC = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) - 1000;
|
||||||
|
// celltemperature_min_dC = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 1000;
|
||||||
|
// break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void transmit_can_battery() {
|
||||||
|
unsigned long currentMillis = millis();
|
||||||
|
// Send 1s CAN Message
|
||||||
|
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
|
||||||
|
|
||||||
|
previousMillis1000 = currentMillis;
|
||||||
|
|
||||||
|
// transmit_can_frame(&PYLON_3010, can_config.battery); // Heartbeat
|
||||||
|
// transmit_can_frame(&PYLON_4200, can_config.battery); // Ensemble OR System equipment info, depends on frame0
|
||||||
|
// transmit_can_frame(&PYLON_8200, can_config.battery); // Control device quit sleep status
|
||||||
|
// transmit_can_frame(&PYLON_8210, can_config.battery); // Charge command
|
||||||
|
|
||||||
|
// if (ensemble_info_ack) {
|
||||||
|
// PYLON_4200.data.u8[0] = 0x00; //Request system equipment info
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
Software/src/battery/SIMPBMS-BATTERY.h
Normal file
19
Software/src/battery/SIMPBMS-BATTERY.h
Normal file
|
@ -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
|
|
@ -471,6 +471,15 @@ String advanced_battery_processor(const String& var) {
|
||||||
content += "<h4>Voltage OBD2: " + String(datalayer_extended.bydAtto3.voltage_polled) + "</h4>";
|
content += "<h4>Voltage OBD2: " + String(datalayer_extended.bydAtto3.voltage_polled) + "</h4>";
|
||||||
#endif //BYD_ATTO_3_BATTERY
|
#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
|
#ifdef TESLA_BATTERY
|
||||||
float beginning_of_life = static_cast<float>(datalayer_extended.tesla.battery_beginning_of_life);
|
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;
|
float battTempPct = static_cast<float>(datalayer_extended.tesla.battery_battTempPct) * 0.4;
|
||||||
|
@ -1315,7 +1324,7 @@ String advanced_battery_processor(const String& var) {
|
||||||
!defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && \
|
!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(BYD_ATTO_3_BATTERY) && !defined(RENAULT_ZOE_GEN2_BATTERY) && !defined(CELLPOWER_BMS) && \
|
||||||
!defined(MEB_BATTERY) && !defined(VOLVO_SPA_BATTERY) && \
|
!defined(MEB_BATTERY) && !defined(VOLVO_SPA_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";
|
content += "No extra information available for this battery type";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue