mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 01:39:30 +02:00
Merge pull request #1442 from dalathegreat/feature/RELION-LV-protocol
New Battery 🔋 Add support for RELi³ON LV battery protocol
This commit is contained in:
commit
bcfc745ea6
6 changed files with 205 additions and 0 deletions
|
@ -36,6 +36,7 @@
|
|||
//#define DALY_BMS
|
||||
//#define RJXZS_BMS
|
||||
//#define RANGE_ROVER_PHEV_BATTERY
|
||||
//#define RELION_BATTERY
|
||||
//#define RENAULT_KANGOO_BATTERY
|
||||
//#define RENAULT_TWIZY_BATTERY
|
||||
//#define RENAULT_ZOE_GEN1_BATTERY
|
||||
|
|
|
@ -110,6 +110,8 @@ const char* name_for_battery_type(BatteryType type) {
|
|||
return RjxzsBms::Name;
|
||||
case BatteryType::RangeRoverPhev:
|
||||
return RangeRoverPhevBattery::Name;
|
||||
case BatteryType::RelionBattery:
|
||||
return RelionBattery::Name;
|
||||
case BatteryType::RenaultKangoo:
|
||||
return RenaultKangooBattery::Name;
|
||||
case BatteryType::RenaultTwizy:
|
||||
|
@ -213,6 +215,8 @@ Battery* create_battery(BatteryType type) {
|
|||
return new RjxzsBms();
|
||||
case BatteryType::RangeRoverPhev:
|
||||
return new RangeRoverPhevBattery();
|
||||
case BatteryType::RelionBattery:
|
||||
return new RelionBattery();
|
||||
case BatteryType::RenaultKangoo:
|
||||
return new RenaultKangooBattery();
|
||||
case BatteryType::RenaultTwizy:
|
||||
|
|
|
@ -40,6 +40,7 @@ void setup_can_shunt();
|
|||
#include "ORION-BMS.h"
|
||||
#include "PYLON-BATTERY.h"
|
||||
#include "RANGE-ROVER-PHEV-BATTERY.h"
|
||||
#include "RELION-LV-BATTERY.h"
|
||||
#include "RENAULT-KANGOO-BATTERY.h"
|
||||
#include "RENAULT-TWIZY.h"
|
||||
#include "RENAULT-ZOE-GEN1-BATTERY.h"
|
||||
|
|
|
@ -46,6 +46,7 @@ enum class BatteryType {
|
|||
SamsungSdiLv = 38,
|
||||
HyundaiIoniq28 = 39,
|
||||
Kia64FD = 40,
|
||||
RelionBattery = 41,
|
||||
Highest
|
||||
};
|
||||
|
||||
|
|
155
Software/src/battery/RELION-LV-BATTERY.cpp
Normal file
155
Software/src/battery/RELION-LV-BATTERY.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
#include "RELION-LV-BATTERY.h"
|
||||
#include "../battery/BATTERIES.h"
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
/*CAN Type:CAN2.0(Extended)
|
||||
BPS:250kbps
|
||||
Data Length: 8
|
||||
Data Encoded Format:Motorola*/
|
||||
|
||||
void RelionBattery::update_values() {
|
||||
|
||||
datalayer.battery.status.real_soc = battery_soc * 100;
|
||||
|
||||
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.soh_pptt = battery_soh * 100;
|
||||
|
||||
datalayer.battery.status.voltage_dV = battery_total_voltage;
|
||||
|
||||
datalayer.battery.status.current_dA = battery_total_current; //Charging negative, discharge positive
|
||||
|
||||
datalayer.battery.status.max_charge_power_W =
|
||||
((battery_total_voltage / 10) * charge_current_A); //90A recommended charge current
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W =
|
||||
((battery_total_voltage / 10) * discharge_current_A); //150A max continous discharge current
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = max_cell_temperature * 10;
|
||||
|
||||
datalayer.battery.status.temperature_max_dC = max_cell_temperature * 10;
|
||||
|
||||
datalayer.battery.status.cell_max_voltage_mV = max_cell_voltage;
|
||||
|
||||
datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage;
|
||||
}
|
||||
|
||||
void RelionBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
switch (rx_frame.ID) {
|
||||
case 0x02018100: //ID1 (Example frame 10 08 01 F0 00 00 00 00)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_total_voltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
break;
|
||||
case 0x02028100: //ID2 (Example frame 00 00 00 63 64 10 00 00)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
battery_total_current = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
system_state = rx_frame.data.u8[2];
|
||||
battery_soc = rx_frame.data.u8[3];
|
||||
battery_soh = rx_frame.data.u8[4];
|
||||
most_serious_fault = rx_frame.data.u8[5];
|
||||
break;
|
||||
case 0x02038100: //ID3 (Example frame 0C F9 01 04 0C A7 01 0F)
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
max_cell_voltage = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
min_cell_voltage = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
break;
|
||||
case 0x02648100: //Charging limitis
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
charge_current_A = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]) - 800;
|
||||
regen_charge_current_A = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]) - 800;
|
||||
discharge_current_A = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]) - 800;
|
||||
break;
|
||||
case 0x02048100: ///Temperatures min/max 2048100 [8] 47 01 01 47 01 01 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
max_cell_temperature = rx_frame.data.u8[0] - 50;
|
||||
min_cell_temperature = rx_frame.data.u8[2] - 50;
|
||||
break;
|
||||
case 0x02468100: ///Raw temperatures 2468100 [8] 47 47 47 47 47 47 47 47
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02478100: ///? 2478100 [8] 32 32 32 32 32 32 32 32
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
//ID6 = 0x02108100 ~ 0x023F8100****** Cell Voltage 1~192******
|
||||
case 0x02108100: ///Cellvoltages 1 2108100 [8] 0C F9 0C F8 0C F8 0C F9
|
||||
datalayer.battery.status.cell_voltages_mV[0] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
datalayer.battery.status.cell_voltages_mV[1] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
datalayer.battery.status.cell_voltages_mV[2] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
datalayer.battery.status.cell_voltages_mV[3] = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02118100: ///Cellvoltages 2 2118100 [8] 0C F8 0C F8 0C F9 0C F8
|
||||
datalayer.battery.status.cell_voltages_mV[4] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
datalayer.battery.status.cell_voltages_mV[5] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
datalayer.battery.status.cell_voltages_mV[6] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
datalayer.battery.status.cell_voltages_mV[7] = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02128100: ///Cellvoltages 3 2128100 [8] 0C F8 0C F8 0C F9 0C F8
|
||||
datalayer.battery.status.cell_voltages_mV[8] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
datalayer.battery.status.cell_voltages_mV[9] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
datalayer.battery.status.cell_voltages_mV[10] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
datalayer.battery.status.cell_voltages_mV[11] = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02138100: ///Cellvoltages 4 2138100 [8] 0C F9 0C CD 0C A7 00 00
|
||||
datalayer.battery.status.cell_voltages_mV[12] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]);
|
||||
datalayer.battery.status.cell_voltages_mV[13] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||
datalayer.battery.status.cell_voltages_mV[14] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02058100: ///? 2058100 [8] 00 0C 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02068100: ///? 2068100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02148100: ///? 2148100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02508100: ///? 2508100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02518100: ///? 2518100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02528100: ///? 2528100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02548100: ///? 2548100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x024A8100: ///? 24A8100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02558100: ///? 2558100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02538100: ///? 2538100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x02568100: ///? 2568100 [8] 00 00 00 00 00 00 00 00
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RelionBattery::transmit_can(unsigned long currentMillis) {
|
||||
// No periodic sending for this protocol
|
||||
}
|
||||
|
||||
void RelionBattery::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer.battery.info.chemistry = LFP;
|
||||
datalayer.battery.info.number_of_cells = 16;
|
||||
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;
|
||||
}
|
43
Software/src/battery/RELION-LV-BATTERY.h
Normal file
43
Software/src/battery/RELION-LV-BATTERY.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef RELION_BATTERY_H
|
||||
#define RELION_BATTERY_H
|
||||
|
||||
#include "../system_settings.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#ifdef RELION_BATTERY
|
||||
#define SELECTED_BATTERY_CLASS RelionBattery
|
||||
#endif
|
||||
|
||||
class RelionBattery : public CanBattery {
|
||||
public:
|
||||
RelionBattery() : CanBattery(CAN_Speed::CAN_SPEED_250KBPS) {}
|
||||
|
||||
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);
|
||||
static constexpr const char* Name = "Relion LV protocol via 250kbps CAN";
|
||||
|
||||
private:
|
||||
static const int MAX_PACK_VOLTAGE_DV = 584; //58.4V recommended charge voltage. BMS protection steps in at 60.8V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 440; //44.0V Recommended LV disconnect. BMS protection steps in at 40.0V
|
||||
static const int MAX_CELL_DEVIATION_MV = 300;
|
||||
static const int MAX_CELL_VOLTAGE_MV = 3800; //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
|
||||
|
||||
uint16_t battery_total_voltage = 500;
|
||||
int16_t battery_total_current = 0;
|
||||
uint8_t system_state = 0;
|
||||
uint8_t battery_soc = 50;
|
||||
uint8_t battery_soh = 99;
|
||||
uint8_t most_serious_fault = 0;
|
||||
uint16_t max_cell_voltage = 3300;
|
||||
uint16_t min_cell_voltage = 3300;
|
||||
int16_t max_cell_temperature = 0;
|
||||
int16_t min_cell_temperature = 0;
|
||||
int16_t charge_current_A = 0;
|
||||
int16_t regen_charge_current_A = 0;
|
||||
int16_t discharge_current_A = 0;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue