mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 10:49:42 +02:00
Merge branch 'main' into feature/zoe-ph2
This commit is contained in:
commit
b5d2684981
15 changed files with 674 additions and 52 deletions
1
.github/workflows/compile-all-batteries.yml
vendored
1
.github/workflows/compile-all-batteries.yml
vendored
|
@ -35,6 +35,7 @@ jobs:
|
||||||
battery:
|
battery:
|
||||||
- BMW_I3_BATTERY
|
- BMW_I3_BATTERY
|
||||||
- BYD_ATTO_3_BATTERY
|
- BYD_ATTO_3_BATTERY
|
||||||
|
- CELLPOWER_BMS
|
||||||
- CHADEMO_BATTERY
|
- CHADEMO_BATTERY
|
||||||
- IMIEV_CZERO_ION_BATTERY
|
- IMIEV_CZERO_ION_BATTERY
|
||||||
- JAGUAR_IPACE_BATTERY
|
- JAGUAR_IPACE_BATTERY
|
||||||
|
|
|
@ -53,10 +53,10 @@
|
||||||
|
|
||||||
Preferences settings; // Store user settings
|
Preferences settings; // Store user settings
|
||||||
// The current software version, shown on webserver
|
// The current software version, shown on webserver
|
||||||
const char* version_number = "7.6.dev";
|
const char* version_number = "7.7.dev";
|
||||||
|
|
||||||
// Interval settings
|
// Interval settings
|
||||||
uint16_t intervalUpdateValues = INTERVAL_5_S; // Interval at which to update inverter values / Modbus registers
|
uint16_t intervalUpdateValues = INTERVAL_1_S; // Interval at which to update inverter values / Modbus registers
|
||||||
unsigned long previousMillis10ms = 50;
|
unsigned long previousMillis10ms = 50;
|
||||||
unsigned long previousMillisUpdateVal = 0;
|
unsigned long previousMillisUpdateVal = 0;
|
||||||
|
|
||||||
|
@ -85,6 +85,9 @@ uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory
|
||||||
// Create a ModbusRTU server instance listening on Serial2 with 2000ms timeout
|
// Create a ModbusRTU server instance listening on Serial2 with 2000ms timeout
|
||||||
ModbusServerRTU MBserver(Serial2, 2000);
|
ModbusServerRTU MBserver(Serial2, 2000);
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||||
|
#define SERIAL_LINK_BAUDRATE 112500
|
||||||
|
#endif
|
||||||
|
|
||||||
// Common charger parameters
|
// Common charger parameters
|
||||||
volatile float charger_setpoint_HV_VDC = 0.0f;
|
volatile float charger_setpoint_HV_VDC = 0.0f;
|
||||||
|
@ -283,9 +286,8 @@ void core_loop(void* task_time_us) {
|
||||||
}
|
}
|
||||||
END_TIME_MEASUREMENT_MAX(time_10ms, datalayer.system.status.time_10ms_us);
|
END_TIME_MEASUREMENT_MAX(time_10ms, datalayer.system.status.time_10ms_us);
|
||||||
|
|
||||||
START_TIME_MEASUREMENT(time_5s);
|
START_TIME_MEASUREMENT(time_values);
|
||||||
if (millis() - previousMillisUpdateVal >= intervalUpdateValues) // Every 5s normally
|
if (millis() - previousMillisUpdateVal >= intervalUpdateValues) {
|
||||||
{
|
|
||||||
previousMillisUpdateVal = millis(); // Order matters on the update_loop!
|
previousMillisUpdateVal = millis(); // Order matters on the update_loop!
|
||||||
update_values_battery(); // Fetch battery values
|
update_values_battery(); // Fetch battery values
|
||||||
#ifdef DOUBLE_BATTERY
|
#ifdef DOUBLE_BATTERY
|
||||||
|
@ -300,7 +302,7 @@ void core_loop(void* task_time_us) {
|
||||||
set_event(EVENT_DUMMY_ERROR, (uint8_t)millis());
|
set_event(EVENT_DUMMY_ERROR, (uint8_t)millis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
END_TIME_MEASUREMENT_MAX(time_5s, datalayer.system.status.time_5s_us);
|
END_TIME_MEASUREMENT_MAX(time_values, datalayer.system.status.time_values_us);
|
||||||
|
|
||||||
START_TIME_MEASUREMENT(cantx);
|
START_TIME_MEASUREMENT(cantx);
|
||||||
// Output
|
// Output
|
||||||
|
@ -316,7 +318,7 @@ void core_loop(void* task_time_us) {
|
||||||
// Record snapshots of task times
|
// Record snapshots of task times
|
||||||
datalayer.system.status.time_snap_comm_us = datalayer.system.status.time_comm_us;
|
datalayer.system.status.time_snap_comm_us = datalayer.system.status.time_comm_us;
|
||||||
datalayer.system.status.time_snap_10ms_us = datalayer.system.status.time_10ms_us;
|
datalayer.system.status.time_snap_10ms_us = datalayer.system.status.time_10ms_us;
|
||||||
datalayer.system.status.time_snap_5s_us = datalayer.system.status.time_5s_us;
|
datalayer.system.status.time_snap_values_us = datalayer.system.status.time_values_us;
|
||||||
datalayer.system.status.time_snap_cantx_us = datalayer.system.status.time_cantx_us;
|
datalayer.system.status.time_snap_cantx_us = datalayer.system.status.time_cantx_us;
|
||||||
datalayer.system.status.time_snap_ota_us = datalayer.system.status.time_ota_us;
|
datalayer.system.status.time_snap_ota_us = datalayer.system.status.time_ota_us;
|
||||||
}
|
}
|
||||||
|
@ -327,7 +329,7 @@ void core_loop(void* task_time_us) {
|
||||||
datalayer.system.status.time_ota_us = 0;
|
datalayer.system.status.time_ota_us = 0;
|
||||||
datalayer.system.status.time_comm_us = 0;
|
datalayer.system.status.time_comm_us = 0;
|
||||||
datalayer.system.status.time_10ms_us = 0;
|
datalayer.system.status.time_10ms_us = 0;
|
||||||
datalayer.system.status.time_5s_us = 0;
|
datalayer.system.status.time_values_us = 0;
|
||||||
datalayer.system.status.time_cantx_us = 0;
|
datalayer.system.status.time_cantx_us = 0;
|
||||||
datalayer.system.status.core_task_10s_max_us = 0;
|
datalayer.system.status.core_task_10s_max_us = 0;
|
||||||
}
|
}
|
||||||
|
@ -454,7 +456,7 @@ void init_CAN() {
|
||||||
Serial.println("CAN FD add-on (ESP32+MCP2517) selected");
|
Serial.println("CAN FD add-on (ESP32+MCP2517) selected");
|
||||||
#endif
|
#endif
|
||||||
SPI.begin(MCP2517_SCK, MCP2517_SDO, MCP2517_SDI);
|
SPI.begin(MCP2517_SCK, MCP2517_SDO, MCP2517_SDI);
|
||||||
ACAN2517FDSettings settings(ACAN2517FDSettings::OSC_40MHz, 500 * 1000,
|
ACAN2517FDSettings settings(CAN_FD_CRYSTAL_FREQUENCY_MHZ, 500 * 1000,
|
||||||
DataBitRateFactor::x4); // Arbitration bit rate: 500 kbit/s, data bit rate: 2 Mbit/s
|
DataBitRateFactor::x4); // Arbitration bit rate: 500 kbit/s, data bit rate: 2 Mbit/s
|
||||||
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||||
settings.mRequestedMode = ACAN2517FDSettings::Normal20B; // ListenOnly / Normal20B / NormalFD
|
settings.mRequestedMode = ACAN2517FDSettings::Normal20B; // ListenOnly / Normal20B / NormalFD
|
||||||
|
@ -742,6 +744,7 @@ void handle_contactors() {
|
||||||
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS ||
|
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS ||
|
||||||
(datalayer.system.settings.equipment_stop_active && contactorStatus != SHUTDOWN_REQUESTED)) {
|
(datalayer.system.settings.equipment_stop_active && contactorStatus != SHUTDOWN_REQUESTED)) {
|
||||||
contactorStatus = SHUTDOWN_REQUESTED;
|
contactorStatus = SHUTDOWN_REQUESTED;
|
||||||
|
datalayer.system.settings.equipment_stop_active = true;
|
||||||
}
|
}
|
||||||
if (contactorStatus == SHUTDOWN_REQUESTED && !datalayer.system.settings.equipment_stop_active) {
|
if (contactorStatus == SHUTDOWN_REQUESTED && !datalayer.system.settings.equipment_stop_active) {
|
||||||
contactorStatus = DISCONNECTED;
|
contactorStatus = DISCONNECTED;
|
||||||
|
@ -932,7 +935,7 @@ void runSerialDataLink() {
|
||||||
|
|
||||||
void init_serialDataLink() {
|
void init_serialDataLink() {
|
||||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||||
Serial2.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
Serial2.begin(SERIAL_LINK_BAUDRATE, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
/* Select battery used */
|
/* Select battery used */
|
||||||
//#define BMW_I3_BATTERY
|
//#define BMW_I3_BATTERY
|
||||||
//#define BYD_ATTO_3_BATTERY
|
//#define BYD_ATTO_3_BATTERY
|
||||||
|
//#define CELLPOWER_BMS
|
||||||
//#define CHADEMO_BATTERY //NOTE: inherently enables CONTACTOR_CONTROL below
|
//#define CHADEMO_BATTERY //NOTE: inherently enables CONTACTOR_CONTROL below
|
||||||
//#define IMIEV_CZERO_ION_BATTERY
|
//#define IMIEV_CZERO_ION_BATTERY
|
||||||
//#define JAGUAR_IPACE_BATTERY
|
//#define JAGUAR_IPACE_BATTERY
|
||||||
|
@ -57,6 +58,11 @@
|
||||||
//#define DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery)
|
//#define DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery)
|
||||||
#define CRYSTAL_FREQUENCY_MHZ 8 //DUAL_CAN option, what is your MCP2515 add-on boards crystal frequency?
|
#define CRYSTAL_FREQUENCY_MHZ 8 //DUAL_CAN option, what is your MCP2515 add-on boards crystal frequency?
|
||||||
//#define CAN_FD //Enable this line to activate an isolated secondary CAN-FD bus using add-on MCP2518FD chip / Native CANFD on Stark board
|
//#define CAN_FD //Enable this line to activate an isolated secondary CAN-FD bus using add-on MCP2518FD chip / Native CANFD on Stark board
|
||||||
|
#ifdef CAN_FD // CAN_FD additional options if enabled
|
||||||
|
#define CAN_FD_CRYSTAL_FREQUENCY_MHZ \
|
||||||
|
ACAN2517FDSettings:: \
|
||||||
|
OSC_40MHz //CAN_FD option, what is your MCP2518 add-on boards crystal frequency? (Default OSC_40MHz)
|
||||||
|
#endif
|
||||||
//#define USE_CANFD_INTERFACE_AS_CLASSIC_CAN // Enable this line if you intend to use the CANFD as normal CAN
|
//#define USE_CANFD_INTERFACE_AS_CLASSIC_CAN // Enable this line if you intend to use the CANFD as normal CAN
|
||||||
//#define SERIAL_LINK_RECEIVER //Enable this line to receive battery data over RS485 pins from another Lilygo (This LilyGo interfaces with inverter)
|
//#define SERIAL_LINK_RECEIVER //Enable this line to receive battery data over RS485 pins from another Lilygo (This LilyGo interfaces with inverter)
|
||||||
//#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery)
|
//#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery)
|
||||||
|
|
|
@ -10,6 +10,10 @@
|
||||||
#include "BYD-ATTO-3-BATTERY.h"
|
#include "BYD-ATTO-3-BATTERY.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CELLPOWER_BMS
|
||||||
|
#include "CELLPOWER-BMS.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CHADEMO_BATTERY
|
#ifdef CHADEMO_BATTERY
|
||||||
#include "CHADEMO-BATTERY.h"
|
#include "CHADEMO-BATTERY.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -430,6 +430,12 @@ void update_values_battery2() { //This function maps all the values fetched via
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
|
void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
|
||||||
|
if (datalayer.system.settings.equipment_stop_active == true) {
|
||||||
|
digitalWrite(WUP_PIN, LOW); // Turn off WUP_PIN
|
||||||
|
} else {
|
||||||
|
digitalWrite(WUP_PIN, HIGH); // Wake up the battery
|
||||||
|
}
|
||||||
|
|
||||||
if (!battery_awake) {
|
if (!battery_awake) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
349
Software/src/battery/CELLPOWER-BMS.cpp
Normal file
349
Software/src/battery/CELLPOWER-BMS.cpp
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
#include "../include.h"
|
||||||
|
#ifdef CELLPOWER_BMS
|
||||||
|
#include "../datalayer/datalayer.h"
|
||||||
|
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
|
||||||
|
#include "../devboard/utils/events.h"
|
||||||
|
#include "CELLPOWER-BMS.h"
|
||||||
|
|
||||||
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
|
static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was sent
|
||||||
|
|
||||||
|
//Actual content messages
|
||||||
|
// Optional add-on charger module. Might not be needed to send these towards the BMS to keep it happy.
|
||||||
|
CAN_frame CELLPOWER_18FF50E9 = {.FD = false,
|
||||||
|
.ext_ID = true,
|
||||||
|
.DLC = 5,
|
||||||
|
.ID = 0x18FF50E9,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame CELLPOWER_18FF50E8 = {.FD = false,
|
||||||
|
.ext_ID = true,
|
||||||
|
.DLC = 5,
|
||||||
|
.ID = 0x18FF50E8,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame CELLPOWER_18FF50E7 = {.FD = false,
|
||||||
|
.ext_ID = true,
|
||||||
|
.DLC = 5,
|
||||||
|
.ID = 0x18FF50E7,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame CELLPOWER_18FF50E5 = {.FD = false,
|
||||||
|
.ext_ID = true,
|
||||||
|
.DLC = 5,
|
||||||
|
.ID = 0x18FF50E5,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
|
||||||
|
static bool system_state_discharge = false;
|
||||||
|
static bool system_state_charge = false;
|
||||||
|
static bool system_state_cellbalancing = false;
|
||||||
|
static bool system_state_tricklecharge = false;
|
||||||
|
static bool system_state_idle = false;
|
||||||
|
static bool system_state_chargecompleted = false;
|
||||||
|
static bool system_state_maintenancecharge = false;
|
||||||
|
static bool IO_state_main_positive_relay = false;
|
||||||
|
static bool IO_state_main_negative_relay = false;
|
||||||
|
static bool IO_state_charge_enable = false;
|
||||||
|
static bool IO_state_precharge_relay = false;
|
||||||
|
static bool IO_state_discharge_enable = false;
|
||||||
|
static bool IO_state_IO_6 = false;
|
||||||
|
static bool IO_state_IO_7 = false;
|
||||||
|
static bool IO_state_IO_8 = false;
|
||||||
|
static bool error_Cell_overvoltage = false;
|
||||||
|
static bool error_Cell_undervoltage = false;
|
||||||
|
static bool error_Cell_end_of_life_voltage = false;
|
||||||
|
static bool error_Cell_voltage_misread = false;
|
||||||
|
static bool error_Cell_over_temperature = false;
|
||||||
|
static bool error_Cell_under_temperature = false;
|
||||||
|
static bool error_Cell_unmanaged = false;
|
||||||
|
static bool error_LMU_over_temperature = false;
|
||||||
|
static bool error_LMU_under_temperature = false;
|
||||||
|
static bool error_Temp_sensor_open_circuit = false;
|
||||||
|
static bool error_Temp_sensor_short_circuit = false;
|
||||||
|
static bool error_SUB_communication = false;
|
||||||
|
static bool error_LMU_communication = false;
|
||||||
|
static bool error_Over_current_IN = false;
|
||||||
|
static bool error_Over_current_OUT = false;
|
||||||
|
static bool error_Short_circuit = false;
|
||||||
|
static bool error_Leak_detected = false;
|
||||||
|
static bool error_Leak_detection_failed = false;
|
||||||
|
static bool error_Voltage_difference = false;
|
||||||
|
static bool error_BMCU_supply_over_voltage = false;
|
||||||
|
static bool error_BMCU_supply_under_voltage = false;
|
||||||
|
static bool error_Main_positive_contactor = false;
|
||||||
|
static bool error_Main_negative_contactor = false;
|
||||||
|
static bool error_Precharge_contactor = false;
|
||||||
|
static bool error_Midpack_contactor = false;
|
||||||
|
static bool error_Precharge_timeout = false;
|
||||||
|
static bool error_Emergency_connector_override = false;
|
||||||
|
static bool warning_High_cell_voltage = false;
|
||||||
|
static bool warning_Low_cell_voltage = false;
|
||||||
|
static bool warning_High_cell_temperature = false;
|
||||||
|
static bool warning_Low_cell_temperature = false;
|
||||||
|
static bool warning_High_LMU_temperature = false;
|
||||||
|
static bool warning_Low_LMU_temperature = false;
|
||||||
|
static bool warning_SUB_communication_interfered = false;
|
||||||
|
static bool warning_LMU_communication_interfered = false;
|
||||||
|
static bool warning_High_current_IN = false;
|
||||||
|
static bool warning_High_current_OUT = false;
|
||||||
|
static bool warning_Pack_resistance_difference = false;
|
||||||
|
static bool warning_High_pack_resistance = false;
|
||||||
|
static bool warning_Cell_resistance_difference = false;
|
||||||
|
static bool warning_High_cell_resistance = false;
|
||||||
|
static bool warning_High_BMCU_supply_voltage = false;
|
||||||
|
static bool warning_Low_BMCU_supply_voltage = false;
|
||||||
|
static bool warning_Low_SOC = false;
|
||||||
|
static bool warning_Balancing_required_OCV_model = false;
|
||||||
|
static bool warning_Charger_not_responding = false;
|
||||||
|
static uint16_t cell_voltage_max_mV = 3700;
|
||||||
|
static uint16_t cell_voltage_min_mV = 3700;
|
||||||
|
static int8_t pack_temperature_high_C = 0;
|
||||||
|
static int8_t pack_temperature_low_C = 0;
|
||||||
|
static uint16_t battery_pack_voltage_dV = 3700;
|
||||||
|
static int16_t battery_pack_current_dA = 0;
|
||||||
|
static uint8_t battery_SOH_percentage = 99;
|
||||||
|
static uint8_t battery_SOC_percentage = 50;
|
||||||
|
static uint16_t battery_remaining_dAh = 0;
|
||||||
|
static uint8_t cell_with_highest_voltage = 0;
|
||||||
|
static uint8_t cell_with_lowest_voltage = 0;
|
||||||
|
static uint16_t requested_charge_current_dA = 0;
|
||||||
|
static uint16_t average_charge_current_dA = 0;
|
||||||
|
static uint16_t actual_charge_current_dA = 0;
|
||||||
|
static bool requested_exceeding_average_current = 0;
|
||||||
|
static bool error_state = false;
|
||||||
|
|
||||||
|
void update_values_battery() {
|
||||||
|
|
||||||
|
/* Update values from CAN */
|
||||||
|
|
||||||
|
datalayer.battery.status.real_soc = battery_SOC_percentage * 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_percentage * 100;
|
||||||
|
|
||||||
|
datalayer.battery.status.voltage_dV = battery_pack_voltage_dV;
|
||||||
|
|
||||||
|
datalayer.battery.status.current_dA = battery_pack_current_dA;
|
||||||
|
|
||||||
|
datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt
|
||||||
|
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
||||||
|
|
||||||
|
datalayer.battery.status.max_charge_power_W = 5000; //TODO, is this available via CAN?
|
||||||
|
|
||||||
|
datalayer.battery.status.max_discharge_power_W = 5000; //TODO, is this available via CAN?
|
||||||
|
|
||||||
|
datalayer.battery.status.temperature_min_dC = (int16_t)(pack_temperature_low_C * 10);
|
||||||
|
|
||||||
|
datalayer.battery.status.temperature_max_dC = (int16_t)(pack_temperature_high_C * 10);
|
||||||
|
|
||||||
|
datalayer.battery.status.cell_max_voltage_mV = cell_voltage_max_mV;
|
||||||
|
|
||||||
|
datalayer.battery.status.cell_min_voltage_mV = cell_voltage_min_mV;
|
||||||
|
|
||||||
|
/* Update webserver datalayer */
|
||||||
|
datalayer_extended.cellpower.system_state_discharge = system_state_discharge;
|
||||||
|
datalayer_extended.cellpower.system_state_charge = system_state_charge;
|
||||||
|
datalayer_extended.cellpower.system_state_cellbalancing = system_state_cellbalancing;
|
||||||
|
datalayer_extended.cellpower.system_state_tricklecharge = system_state_tricklecharge;
|
||||||
|
datalayer_extended.cellpower.system_state_idle = system_state_idle;
|
||||||
|
datalayer_extended.cellpower.system_state_chargecompleted = system_state_chargecompleted;
|
||||||
|
datalayer_extended.cellpower.system_state_maintenancecharge = system_state_maintenancecharge;
|
||||||
|
datalayer_extended.cellpower.IO_state_main_positive_relay = IO_state_main_positive_relay;
|
||||||
|
datalayer_extended.cellpower.IO_state_main_negative_relay = IO_state_main_negative_relay;
|
||||||
|
datalayer_extended.cellpower.IO_state_charge_enable = IO_state_charge_enable;
|
||||||
|
datalayer_extended.cellpower.IO_state_precharge_relay = IO_state_precharge_relay;
|
||||||
|
datalayer_extended.cellpower.IO_state_discharge_enable = IO_state_discharge_enable;
|
||||||
|
datalayer_extended.cellpower.IO_state_IO_6 = IO_state_IO_6;
|
||||||
|
datalayer_extended.cellpower.IO_state_IO_7 = IO_state_IO_7;
|
||||||
|
datalayer_extended.cellpower.IO_state_IO_8 = IO_state_IO_8;
|
||||||
|
datalayer_extended.cellpower.error_Cell_overvoltage = error_Cell_overvoltage;
|
||||||
|
datalayer_extended.cellpower.error_Cell_undervoltage = error_Cell_undervoltage;
|
||||||
|
datalayer_extended.cellpower.error_Cell_end_of_life_voltage = error_Cell_end_of_life_voltage;
|
||||||
|
datalayer_extended.cellpower.error_Cell_voltage_misread = error_Cell_voltage_misread;
|
||||||
|
datalayer_extended.cellpower.error_Cell_over_temperature = error_Cell_over_temperature;
|
||||||
|
datalayer_extended.cellpower.error_Cell_under_temperature = error_Cell_under_temperature;
|
||||||
|
datalayer_extended.cellpower.error_Cell_unmanaged = error_Cell_unmanaged;
|
||||||
|
datalayer_extended.cellpower.error_LMU_over_temperature = error_LMU_over_temperature;
|
||||||
|
datalayer_extended.cellpower.error_LMU_under_temperature = error_LMU_under_temperature;
|
||||||
|
datalayer_extended.cellpower.error_Temp_sensor_open_circuit = error_Temp_sensor_open_circuit;
|
||||||
|
datalayer_extended.cellpower.error_Temp_sensor_short_circuit = error_Temp_sensor_short_circuit;
|
||||||
|
datalayer_extended.cellpower.error_SUB_communication = error_SUB_communication;
|
||||||
|
datalayer_extended.cellpower.error_LMU_communication = error_LMU_communication;
|
||||||
|
datalayer_extended.cellpower.error_Over_current_IN = error_Over_current_IN;
|
||||||
|
datalayer_extended.cellpower.error_Over_current_OUT = error_Over_current_OUT;
|
||||||
|
datalayer_extended.cellpower.error_Short_circuit = error_Short_circuit;
|
||||||
|
datalayer_extended.cellpower.error_Leak_detected = error_Leak_detected;
|
||||||
|
datalayer_extended.cellpower.error_Leak_detection_failed = error_Leak_detection_failed;
|
||||||
|
datalayer_extended.cellpower.error_Voltage_difference = error_Voltage_difference;
|
||||||
|
datalayer_extended.cellpower.error_BMCU_supply_over_voltage = error_BMCU_supply_over_voltage;
|
||||||
|
datalayer_extended.cellpower.error_BMCU_supply_under_voltage = error_BMCU_supply_under_voltage;
|
||||||
|
datalayer_extended.cellpower.error_Main_positive_contactor = error_Main_positive_contactor;
|
||||||
|
datalayer_extended.cellpower.error_Main_negative_contactor = error_Main_negative_contactor;
|
||||||
|
datalayer_extended.cellpower.error_Precharge_contactor = error_Precharge_contactor;
|
||||||
|
datalayer_extended.cellpower.error_Midpack_contactor = error_Midpack_contactor;
|
||||||
|
datalayer_extended.cellpower.error_Precharge_timeout = error_Precharge_timeout;
|
||||||
|
datalayer_extended.cellpower.error_Emergency_connector_override = error_Emergency_connector_override;
|
||||||
|
datalayer_extended.cellpower.warning_High_cell_voltage = warning_High_cell_voltage;
|
||||||
|
datalayer_extended.cellpower.warning_Low_cell_voltage = warning_Low_cell_voltage;
|
||||||
|
datalayer_extended.cellpower.warning_High_cell_temperature = warning_High_cell_temperature;
|
||||||
|
datalayer_extended.cellpower.warning_Low_cell_temperature = warning_Low_cell_temperature;
|
||||||
|
datalayer_extended.cellpower.warning_High_LMU_temperature = warning_High_LMU_temperature;
|
||||||
|
datalayer_extended.cellpower.warning_Low_LMU_temperature = warning_Low_LMU_temperature;
|
||||||
|
datalayer_extended.cellpower.warning_SUB_communication_interfered = warning_SUB_communication_interfered;
|
||||||
|
datalayer_extended.cellpower.warning_LMU_communication_interfered = warning_LMU_communication_interfered;
|
||||||
|
datalayer_extended.cellpower.warning_High_current_IN = warning_High_current_IN;
|
||||||
|
datalayer_extended.cellpower.warning_High_current_OUT = warning_High_current_OUT;
|
||||||
|
datalayer_extended.cellpower.warning_Pack_resistance_difference = warning_Pack_resistance_difference;
|
||||||
|
datalayer_extended.cellpower.warning_High_pack_resistance = warning_High_pack_resistance;
|
||||||
|
datalayer_extended.cellpower.warning_Cell_resistance_difference = warning_Cell_resistance_difference;
|
||||||
|
datalayer_extended.cellpower.warning_High_cell_resistance = warning_High_cell_resistance;
|
||||||
|
datalayer_extended.cellpower.warning_High_BMCU_supply_voltage = warning_High_BMCU_supply_voltage;
|
||||||
|
datalayer_extended.cellpower.warning_Low_BMCU_supply_voltage = warning_Low_BMCU_supply_voltage;
|
||||||
|
datalayer_extended.cellpower.warning_Low_SOC = warning_Low_SOC;
|
||||||
|
datalayer_extended.cellpower.warning_Balancing_required_OCV_model = warning_Balancing_required_OCV_model;
|
||||||
|
datalayer_extended.cellpower.warning_Charger_not_responding = warning_Charger_not_responding;
|
||||||
|
|
||||||
|
/* Peform safety checks */
|
||||||
|
if (system_state_chargecompleted) {
|
||||||
|
//TODO, shall we set max_charge_power_W to 0 incase this is true?
|
||||||
|
}
|
||||||
|
if (IO_state_charge_enable) {
|
||||||
|
//TODO, shall we react on this?
|
||||||
|
}
|
||||||
|
if (IO_state_discharge_enable) {
|
||||||
|
//TODO, shall we react on this?
|
||||||
|
}
|
||||||
|
if (error_state) {
|
||||||
|
//TODO, shall we react on this?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void receive_can_battery(CAN_frame rx_frame) {
|
||||||
|
|
||||||
|
switch (rx_frame.ID) {
|
||||||
|
case 0x1A4: //PDO1_TX - 200ms
|
||||||
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
cell_voltage_max_mV = (uint16_t)((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
|
||||||
|
cell_voltage_min_mV = (uint16_t)((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
|
||||||
|
pack_temperature_high_C = (int8_t)rx_frame.data.u8[4];
|
||||||
|
pack_temperature_low_C = (int8_t)rx_frame.data.u8[5];
|
||||||
|
system_state_discharge = (rx_frame.data.u8[6] & 0x01);
|
||||||
|
system_state_charge = ((rx_frame.data.u8[6] & 0x02) >> 1);
|
||||||
|
system_state_cellbalancing = ((rx_frame.data.u8[6] & 0x04) >> 2);
|
||||||
|
system_state_tricklecharge = ((rx_frame.data.u8[6] & 0x08) >> 3);
|
||||||
|
system_state_idle = ((rx_frame.data.u8[6] & 0x10) >> 4);
|
||||||
|
system_state_chargecompleted = ((rx_frame.data.u8[6] & 0x20) >> 5);
|
||||||
|
system_state_maintenancecharge = ((rx_frame.data.u8[6] & 0x40) >> 6);
|
||||||
|
IO_state_main_positive_relay = (rx_frame.data.u8[7] & 0x01);
|
||||||
|
IO_state_main_negative_relay = ((rx_frame.data.u8[7] & 0x02) >> 1);
|
||||||
|
IO_state_charge_enable = ((rx_frame.data.u8[7] & 0x04) >> 2);
|
||||||
|
IO_state_precharge_relay = ((rx_frame.data.u8[7] & 0x08) >> 3);
|
||||||
|
IO_state_discharge_enable = ((rx_frame.data.u8[7] & 0x10) >> 4);
|
||||||
|
IO_state_IO_6 = ((rx_frame.data.u8[7] & 0x20) >> 5);
|
||||||
|
IO_state_IO_7 = ((rx_frame.data.u8[7] & 0x40) >> 6);
|
||||||
|
IO_state_IO_8 = ((rx_frame.data.u8[7] & 0x80) >> 7);
|
||||||
|
break;
|
||||||
|
case 0x2A4: //PDO2_TX - 200ms
|
||||||
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
battery_pack_voltage_dV = (uint16_t)((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
|
||||||
|
battery_pack_current_dA = (int16_t)((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
|
||||||
|
battery_SOH_percentage = (uint8_t)rx_frame.data.u8[4];
|
||||||
|
battery_SOC_percentage = (uint8_t)rx_frame.data.u8[5];
|
||||||
|
battery_remaining_dAh = (uint16_t)((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]);
|
||||||
|
break;
|
||||||
|
case 0x3A4: //PDO3_TX - 200ms
|
||||||
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
cell_with_highest_voltage = (uint8_t)rx_frame.data.u8[0];
|
||||||
|
cell_with_lowest_voltage = (uint8_t)rx_frame.data.u8[1];
|
||||||
|
break;
|
||||||
|
case 0x4A4: //PDO4_TX - 200ms
|
||||||
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
error_Cell_overvoltage = (rx_frame.data.u8[0] & 0x01);
|
||||||
|
error_Cell_undervoltage = ((rx_frame.data.u8[0] & 0x02) >> 1);
|
||||||
|
error_Cell_end_of_life_voltage = ((rx_frame.data.u8[0] & 0x04) >> 2);
|
||||||
|
error_Cell_voltage_misread = ((rx_frame.data.u8[0] & 0x08) >> 3);
|
||||||
|
error_Cell_over_temperature = ((rx_frame.data.u8[0] & 0x10) >> 4);
|
||||||
|
error_Cell_under_temperature = ((rx_frame.data.u8[0] & 0x20) >> 5);
|
||||||
|
error_Cell_unmanaged = ((rx_frame.data.u8[0] & 0x40) >> 6);
|
||||||
|
error_LMU_over_temperature = ((rx_frame.data.u8[0] & 0x80) >> 7);
|
||||||
|
error_LMU_under_temperature = (rx_frame.data.u8[1] & 0x01);
|
||||||
|
error_Temp_sensor_open_circuit = ((rx_frame.data.u8[1] & 0x02) >> 1);
|
||||||
|
error_Temp_sensor_short_circuit = ((rx_frame.data.u8[1] & 0x04) >> 2);
|
||||||
|
error_SUB_communication = ((rx_frame.data.u8[1] & 0x08) >> 3);
|
||||||
|
error_LMU_communication = ((rx_frame.data.u8[1] & 0x10) >> 4);
|
||||||
|
error_Over_current_IN = ((rx_frame.data.u8[1] & 0x20) >> 5);
|
||||||
|
error_Over_current_OUT = ((rx_frame.data.u8[1] & 0x40) >> 6);
|
||||||
|
error_Short_circuit = ((rx_frame.data.u8[1] & 0x80) >> 7);
|
||||||
|
error_Leak_detected = (rx_frame.data.u8[2] & 0x01);
|
||||||
|
error_Leak_detection_failed = ((rx_frame.data.u8[2] & 0x02) >> 1);
|
||||||
|
error_Voltage_difference = ((rx_frame.data.u8[2] & 0x04) >> 2);
|
||||||
|
error_BMCU_supply_over_voltage = ((rx_frame.data.u8[2] & 0x08) >> 3);
|
||||||
|
error_BMCU_supply_under_voltage = ((rx_frame.data.u8[2] & 0x10) >> 4);
|
||||||
|
error_Main_positive_contactor = ((rx_frame.data.u8[2] & 0x20) >> 5);
|
||||||
|
error_Main_negative_contactor = ((rx_frame.data.u8[2] & 0x40) >> 6);
|
||||||
|
error_Precharge_contactor = ((rx_frame.data.u8[2] & 0x80) >> 7);
|
||||||
|
error_Midpack_contactor = (rx_frame.data.u8[3] & 0x01);
|
||||||
|
error_Precharge_timeout = ((rx_frame.data.u8[3] & 0x02) >> 1);
|
||||||
|
error_Emergency_connector_override = ((rx_frame.data.u8[3] & 0x04) >> 2);
|
||||||
|
warning_High_cell_voltage = (rx_frame.data.u8[4] & 0x01);
|
||||||
|
warning_Low_cell_voltage = ((rx_frame.data.u8[4] & 0x02) >> 1);
|
||||||
|
warning_High_cell_temperature = ((rx_frame.data.u8[4] & 0x04) >> 2);
|
||||||
|
warning_Low_cell_temperature = ((rx_frame.data.u8[4] & 0x08) >> 3);
|
||||||
|
warning_High_LMU_temperature = ((rx_frame.data.u8[4] & 0x10) >> 4);
|
||||||
|
warning_Low_LMU_temperature = ((rx_frame.data.u8[4] & 0x20) >> 5);
|
||||||
|
warning_SUB_communication_interfered = ((rx_frame.data.u8[4] & 0x40) >> 6);
|
||||||
|
warning_LMU_communication_interfered = ((rx_frame.data.u8[4] & 0x80) >> 7);
|
||||||
|
warning_High_current_IN = (rx_frame.data.u8[5] & 0x01);
|
||||||
|
warning_High_current_OUT = ((rx_frame.data.u8[5] & 0x02) >> 1);
|
||||||
|
warning_Pack_resistance_difference = ((rx_frame.data.u8[5] & 0x04) >> 2);
|
||||||
|
warning_High_pack_resistance = ((rx_frame.data.u8[5] & 0x08) >> 3);
|
||||||
|
warning_Cell_resistance_difference = ((rx_frame.data.u8[5] & 0x10) >> 4);
|
||||||
|
warning_High_cell_resistance = ((rx_frame.data.u8[5] & 0x20) >> 5);
|
||||||
|
warning_High_BMCU_supply_voltage = ((rx_frame.data.u8[5] & 0x40) >> 6);
|
||||||
|
warning_Low_BMCU_supply_voltage = ((rx_frame.data.u8[5] & 0x80) >> 7);
|
||||||
|
warning_Low_SOC = (rx_frame.data.u8[6] & 0x01);
|
||||||
|
warning_Balancing_required_OCV_model = ((rx_frame.data.u8[6] & 0x02) >> 1);
|
||||||
|
warning_Charger_not_responding = ((rx_frame.data.u8[6] & 0x04) >> 2);
|
||||||
|
break;
|
||||||
|
case 0x7A4: //PDO7_TX - 200ms
|
||||||
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
requested_charge_current_dA = (uint16_t)((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
|
||||||
|
average_charge_current_dA = (uint16_t)((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
|
||||||
|
actual_charge_current_dA = (uint16_t)((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]);
|
||||||
|
requested_exceeding_average_current = (rx_frame.data.u8[6] & 0x01);
|
||||||
|
break;
|
||||||
|
case 0x7A5: //PDO7.1_TX - 200ms
|
||||||
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
error_state = (rx_frame.data.u8[0] & 0x01);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_can_battery() {
|
||||||
|
unsigned long currentMillis = millis();
|
||||||
|
// Send 1s CAN Message
|
||||||
|
if (currentMillis - previousMillis1s >= INTERVAL_1_S) {
|
||||||
|
|
||||||
|
previousMillis1s = currentMillis;
|
||||||
|
|
||||||
|
/*
|
||||||
|
transmit_can(&CELLPOWER_18FF50E9, can_config.battery);
|
||||||
|
transmit_can(&CELLPOWER_18FF50E8, can_config.battery);
|
||||||
|
transmit_can(&CELLPOWER_18FF50E7, can_config.battery);
|
||||||
|
transmit_can(&CELLPOWER_18FF50E5, can_config.battery);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
|
#ifdef DEBUG_VIA_USB
|
||||||
|
Serial.println("Cellpower BMS selected");
|
||||||
|
#endif
|
||||||
|
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||||
|
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 // CELLPOWER_BMS
|
19
Software/src/battery/CELLPOWER-BMS.h
Normal file
19
Software/src/battery/CELLPOWER-BMS.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef CELLPOWER_BMS_H
|
||||||
|
#define CELLPOWER_BMS_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "../include.h"
|
||||||
|
|
||||||
|
/* Tweak these according to your battery build */
|
||||||
|
#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
|
||||||
|
|
||||||
|
/* Do not modify any rows below*/
|
||||||
|
#define BATTERY_SELECTED
|
||||||
|
#define NATIVECAN_250KBPS
|
||||||
|
|
||||||
|
void setup_battery(void);
|
||||||
|
void transmit_can(CAN_frame* tx_frame, int interface);
|
||||||
|
|
||||||
|
#endif
|
|
@ -145,8 +145,8 @@ typedef struct {
|
||||||
int64_t time_comm_us = 0;
|
int64_t time_comm_us = 0;
|
||||||
/** 10 ms function measurement variable */
|
/** 10 ms function measurement variable */
|
||||||
int64_t time_10ms_us = 0;
|
int64_t time_10ms_us = 0;
|
||||||
/** 5 s function measurement variable */
|
/** Value update function measurement variable */
|
||||||
int64_t time_5s_us = 0;
|
int64_t time_values_us = 0;
|
||||||
/** CAN TX function measurement variable */
|
/** CAN TX function measurement variable */
|
||||||
int64_t time_cantx_us = 0;
|
int64_t time_cantx_us = 0;
|
||||||
|
|
||||||
|
@ -163,9 +163,9 @@ typedef struct {
|
||||||
*/
|
*/
|
||||||
int64_t time_snap_10ms_us = 0;
|
int64_t time_snap_10ms_us = 0;
|
||||||
/** Function measurement snapshot variable.
|
/** Function measurement snapshot variable.
|
||||||
* This will show the performance of the 5 s functionality of the core task when the total time reached a new worst case
|
* This will show the performance of the values functionality of the core task when the total time reached a new worst case
|
||||||
*/
|
*/
|
||||||
int64_t time_snap_5s_us = 0;
|
int64_t time_snap_values_us = 0;
|
||||||
/** Function measurement snapshot variable.
|
/** Function measurement snapshot variable.
|
||||||
* This will show the performance of CAN TX when the total time reached a new worst case
|
* This will show the performance of CAN TX when the total time reached a new worst case
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -64,6 +64,72 @@ typedef struct {
|
||||||
|
|
||||||
} DATALAYER_INFO_BYDATTO3;
|
} DATALAYER_INFO_BYDATTO3;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/** bool */
|
||||||
|
/** All values either True or false */
|
||||||
|
bool system_state_discharge = false;
|
||||||
|
bool system_state_charge = false;
|
||||||
|
bool system_state_cellbalancing = false;
|
||||||
|
bool system_state_tricklecharge = false;
|
||||||
|
bool system_state_idle = false;
|
||||||
|
bool system_state_chargecompleted = false;
|
||||||
|
bool system_state_maintenancecharge = false;
|
||||||
|
bool IO_state_main_positive_relay = false;
|
||||||
|
bool IO_state_main_negative_relay = false;
|
||||||
|
bool IO_state_charge_enable = false;
|
||||||
|
bool IO_state_precharge_relay = false;
|
||||||
|
bool IO_state_discharge_enable = false;
|
||||||
|
bool IO_state_IO_6 = false;
|
||||||
|
bool IO_state_IO_7 = false;
|
||||||
|
bool IO_state_IO_8 = false;
|
||||||
|
bool error_Cell_overvoltage = false;
|
||||||
|
bool error_Cell_undervoltage = false;
|
||||||
|
bool error_Cell_end_of_life_voltage = false;
|
||||||
|
bool error_Cell_voltage_misread = false;
|
||||||
|
bool error_Cell_over_temperature = false;
|
||||||
|
bool error_Cell_under_temperature = false;
|
||||||
|
bool error_Cell_unmanaged = false;
|
||||||
|
bool error_LMU_over_temperature = false;
|
||||||
|
bool error_LMU_under_temperature = false;
|
||||||
|
bool error_Temp_sensor_open_circuit = false;
|
||||||
|
bool error_Temp_sensor_short_circuit = false;
|
||||||
|
bool error_SUB_communication = false;
|
||||||
|
bool error_LMU_communication = false;
|
||||||
|
bool error_Over_current_IN = false;
|
||||||
|
bool error_Over_current_OUT = false;
|
||||||
|
bool error_Short_circuit = false;
|
||||||
|
bool error_Leak_detected = false;
|
||||||
|
bool error_Leak_detection_failed = false;
|
||||||
|
bool error_Voltage_difference = false;
|
||||||
|
bool error_BMCU_supply_over_voltage = false;
|
||||||
|
bool error_BMCU_supply_under_voltage = false;
|
||||||
|
bool error_Main_positive_contactor = false;
|
||||||
|
bool error_Main_negative_contactor = false;
|
||||||
|
bool error_Precharge_contactor = false;
|
||||||
|
bool error_Midpack_contactor = false;
|
||||||
|
bool error_Precharge_timeout = false;
|
||||||
|
bool error_Emergency_connector_override = false;
|
||||||
|
bool warning_High_cell_voltage = false;
|
||||||
|
bool warning_Low_cell_voltage = false;
|
||||||
|
bool warning_High_cell_temperature = false;
|
||||||
|
bool warning_Low_cell_temperature = false;
|
||||||
|
bool warning_High_LMU_temperature = false;
|
||||||
|
bool warning_Low_LMU_temperature = false;
|
||||||
|
bool warning_SUB_communication_interfered = false;
|
||||||
|
bool warning_LMU_communication_interfered = false;
|
||||||
|
bool warning_High_current_IN = false;
|
||||||
|
bool warning_High_current_OUT = false;
|
||||||
|
bool warning_Pack_resistance_difference = false;
|
||||||
|
bool warning_High_pack_resistance = false;
|
||||||
|
bool warning_Cell_resistance_difference = false;
|
||||||
|
bool warning_High_cell_resistance = false;
|
||||||
|
bool warning_High_BMCU_supply_voltage = false;
|
||||||
|
bool warning_Low_BMCU_supply_voltage = false;
|
||||||
|
bool warning_Low_SOC = false;
|
||||||
|
bool warning_Balancing_required_OCV_model = false;
|
||||||
|
bool warning_Charger_not_responding = false;
|
||||||
|
} DATALAYER_INFO_CELLPOWER;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/** uint8_t */
|
/** uint8_t */
|
||||||
/** Contactor status */
|
/** Contactor status */
|
||||||
|
@ -185,6 +251,7 @@ class DataLayerExtended {
|
||||||
public:
|
public:
|
||||||
DATALAYER_INFO_BMWI3 bmwi3;
|
DATALAYER_INFO_BMWI3 bmwi3;
|
||||||
DATALAYER_INFO_BYDATTO3 bydAtto3;
|
DATALAYER_INFO_BYDATTO3 bydAtto3;
|
||||||
|
DATALAYER_INFO_CELLPOWER cellpower;
|
||||||
DATALAYER_INFO_TESLA tesla;
|
DATALAYER_INFO_TESLA tesla;
|
||||||
DATALAYER_INFO_NISSAN_LEAF nissanleaf;
|
DATALAYER_INFO_NISSAN_LEAF nissanleaf;
|
||||||
DATALAYER_INFO_ZOE_PH2 zoePH2;
|
DATALAYER_INFO_ZOE_PH2 zoePH2;
|
||||||
|
|
|
@ -47,27 +47,25 @@ struct SensorConfig {
|
||||||
};
|
};
|
||||||
|
|
||||||
SensorConfig sensorConfigs[] = {
|
SensorConfig sensorConfigs[] = {
|
||||||
{"SOC", "Battery Emulator SOC (scaled)", "{{ value_json.SOC }}", "%", "battery"},
|
{"SOC", "SOC (scaled)", "{{ value_json.SOC }}", "%", "battery"},
|
||||||
{"SOC_real", "Battery Emulator SOC (real)", "{{ value_json.SOC_real }}", "%", "battery"},
|
{"SOC_real", "SOC (real)", "{{ value_json.SOC_real }}", "%", "battery"},
|
||||||
{"state_of_health", "Battery Emulator State Of Health", "{{ value_json.state_of_health }}", "%", "battery"},
|
{"state_of_health", "State Of Health", "{{ value_json.state_of_health }}", "%", "battery"},
|
||||||
{"temperature_min", "Battery Emulator Temperature Min", "{{ value_json.temperature_min }}", "°C", "temperature"},
|
{"temperature_min", "Temperature Min", "{{ value_json.temperature_min }}", "°C", "temperature"},
|
||||||
{"temperature_max", "Battery Emulator Temperature Max", "{{ value_json.temperature_max }}", "°C", "temperature"},
|
{"temperature_max", "Temperature Max", "{{ value_json.temperature_max }}", "°C", "temperature"},
|
||||||
{"stat_batt_power", "Battery Emulator Stat Batt Power", "{{ value_json.stat_batt_power }}", "W", "power"},
|
{"stat_batt_power", "Stat Batt Power", "{{ value_json.stat_batt_power }}", "W", "power"},
|
||||||
{"battery_current", "Battery Emulator Battery Current", "{{ value_json.battery_current }}", "A", "current"},
|
{"battery_current", "Battery Current", "{{ value_json.battery_current }}", "A", "current"},
|
||||||
{"cell_max_voltage", "Battery Emulator Cell Max Voltage", "{{ value_json.cell_max_voltage }}", "V", "voltage"},
|
{"cell_max_voltage", "Cell Max Voltage", "{{ value_json.cell_max_voltage }}", "V", "voltage"},
|
||||||
{"cell_min_voltage", "Battery Emulator Cell Min Voltage", "{{ value_json.cell_min_voltage }}", "V", "voltage"},
|
{"cell_min_voltage", "Cell Min Voltage", "{{ value_json.cell_min_voltage }}", "V", "voltage"},
|
||||||
{"battery_voltage", "Battery Emulator Battery Voltage", "{{ value_json.battery_voltage }}", "V", "voltage"},
|
{"battery_voltage", "Battery Voltage", "{{ value_json.battery_voltage }}", "V", "voltage"},
|
||||||
{"total_capacity", "Battery Emulator Battery Total Capacity", "{{ value_json.total_capacity }}", "Wh", "energy"},
|
{"total_capacity", "Battery Total Capacity", "{{ value_json.total_capacity }}", "Wh", "energy"},
|
||||||
{"remaining_capacity", "Battery Emulator Battery Remaining Capacity (scaled)",
|
{"remaining_capacity", "Battery Remaining Capacity (scaled)", "{{ value_json.remaining_capacity }}", "Wh",
|
||||||
"{{ value_json.remaining_capacity }}", "Wh", "energy"},
|
"energy"},
|
||||||
{"remaining_capacity_real", "Battery Emulator Battery Remaining Capacity (real)",
|
{"remaining_capacity_real", "Battery Remaining Capacity (real)", "{{ value_json.remaining_capacity_real }}", "Wh",
|
||||||
"{{ value_json.remaining_capacity_real }}", "Wh", "energy"},
|
"energy"},
|
||||||
{"max_discharge_power", "Battery Emulator Battery Max Discharge Power", "{{ value_json.max_discharge_power }}", "W",
|
{"max_discharge_power", "Battery Max Discharge Power", "{{ value_json.max_discharge_power }}", "W", "power"},
|
||||||
"power"},
|
{"max_charge_power", "Battery Max Charge Power", "{{ value_json.max_charge_power }}", "W", "power"},
|
||||||
{"max_charge_power", "Battery Emulator Battery Max Charge Power", "{{ value_json.max_charge_power }}", "W",
|
{"bms_status", "BMS Status", "{{ value_json.bms_status }}", "", ""},
|
||||||
"power"},
|
{"pause_status", "Pause Status", "{{ value_json.pause_status }}", "", ""},
|
||||||
{"bms_status", "Battery Emulator BMS Status", "{{ value_json.bms_status }}", "", ""},
|
|
||||||
{"pause_status", "Battery Emulator Pause Status", "{{ value_json.pause_status }}", "", ""},
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -129,7 +127,7 @@ static void publish_common_info(void) {
|
||||||
doc["pause_status"] = get_emulator_pause_status();
|
doc["pause_status"] = get_emulator_pause_status();
|
||||||
|
|
||||||
//only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery)
|
//only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery)
|
||||||
if (datalayer.battery.status.bms_status == ACTIVE && allowed_to_send_CAN && millis() > BOOTUP_TIME) {
|
if (datalayer.battery.status.CAN_battery_still_alive && allowed_to_send_CAN && millis() > BOOTUP_TIME) {
|
||||||
doc["SOC"] = ((float)datalayer.battery.status.reported_soc) / 100.0;
|
doc["SOC"] = ((float)datalayer.battery.status.reported_soc) / 100.0;
|
||||||
doc["SOC_real"] = ((float)datalayer.battery.status.real_soc) / 100.0;
|
doc["SOC_real"] = ((float)datalayer.battery.status.real_soc) / 100.0;
|
||||||
doc["state_of_health"] = ((float)datalayer.battery.status.soh_pptt) / 100.0;
|
doc["state_of_health"] = ((float)datalayer.battery.status.soh_pptt) / 100.0;
|
||||||
|
@ -182,7 +180,7 @@ static void publish_cell_voltages(void) {
|
||||||
for (int i = 0; i < datalayer.battery.info.number_of_cells; i++) {
|
for (int i = 0; i < datalayer.battery.info.number_of_cells; i++) {
|
||||||
int cellNumber = i + 1;
|
int cellNumber = i + 1;
|
||||||
doc["name"] = "Battery Cell Voltage " + String(cellNumber);
|
doc["name"] = "Battery Cell Voltage " + String(cellNumber);
|
||||||
doc["object_id"] = "battery_voltage_cell" + String(cellNumber);
|
doc["object_id"] = object_id_prefix + "battery_voltage_cell" + String(cellNumber);
|
||||||
doc["unique_id"] = topic_name + "_battery_voltage_cell" + String(cellNumber);
|
doc["unique_id"] = topic_name + "_battery_voltage_cell" + String(cellNumber);
|
||||||
doc["device_class"] = "voltage";
|
doc["device_class"] = "voltage";
|
||||||
doc["state_class"] = "measurement";
|
doc["state_class"] = "measurement";
|
||||||
|
@ -240,7 +238,7 @@ void publish_events() {
|
||||||
if (mqtt_first_transmission == true) {
|
if (mqtt_first_transmission == true) {
|
||||||
mqtt_first_transmission = false;
|
mqtt_first_transmission = false;
|
||||||
|
|
||||||
doc["name"] = "Battery Emulator Event";
|
doc["name"] = "Event";
|
||||||
doc["state_topic"] = state_topic;
|
doc["state_topic"] = state_topic;
|
||||||
doc["unique_id"] = topic_name + "_event";
|
doc["unique_id"] = topic_name + "_event";
|
||||||
doc["object_id"] = object_id_prefix + "event";
|
doc["object_id"] = object_id_prefix + "event";
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#define MAX_CAN_FAILURES 50
|
#define MAX_CAN_FAILURES 50
|
||||||
|
|
||||||
#define MAX_CHARGE_DISCHARGE_LIMIT_FAILURES 1
|
#define MAX_CHARGE_DISCHARGE_LIMIT_FAILURES 5
|
||||||
|
|
||||||
//battery pause status begin
|
//battery pause status begin
|
||||||
enum battery_pause_status { NORMAL = 0, PAUSING = 1, PAUSED = 2, RESUMING = 3 };
|
enum battery_pause_status { NORMAL = 0, PAUSING = 1, PAUSED = 2, RESUMING = 3 };
|
||||||
|
|
|
@ -33,8 +33,8 @@ enum led_color { GREEN, YELLOW, RED, BLUE, RGB };
|
||||||
#define INTERVAL_200_MS_DELAYED 240
|
#define INTERVAL_200_MS_DELAYED 240
|
||||||
#define INTERVAL_500_MS_DELAYED 550
|
#define INTERVAL_500_MS_DELAYED 550
|
||||||
|
|
||||||
#define CAN_STILL_ALIVE 12
|
#define CAN_STILL_ALIVE 60
|
||||||
// Set by battery each time we get a CAN message. Decrements every 5seconds. When reaching 0, sets event
|
// Set by battery each time we get a CAN message. Decrements every second. When reaching 0, sets event
|
||||||
|
|
||||||
/* CAN Frame structure */
|
/* CAN Frame structure */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -100,6 +100,141 @@ String advanced_battery_processor(const String& var) {
|
||||||
|
|
||||||
#endif //BMW_I3_BATTERY
|
#endif //BMW_I3_BATTERY
|
||||||
|
|
||||||
|
#ifdef CELLPOWER_BMS
|
||||||
|
static const char* falseTrue[2] = {"False", "True"};
|
||||||
|
content += "<h3>States:</h3>";
|
||||||
|
content += "<h4>Discharge: " + String(falseTrue[datalayer_extended.cellpower.system_state_discharge]) + "</h4>";
|
||||||
|
content += "<h4>Charge: " + String(falseTrue[datalayer_extended.cellpower.system_state_charge]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Cellbalancing: " + String(falseTrue[datalayer_extended.cellpower.system_state_cellbalancing]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Tricklecharging: " + String(falseTrue[datalayer_extended.cellpower.system_state_tricklecharge]) + "</h4>";
|
||||||
|
content += "<h4>Idle: " + String(falseTrue[datalayer_extended.cellpower.system_state_idle]) + "</h4>";
|
||||||
|
content += "<h4>Charge completed: " + String(falseTrue[datalayer_extended.cellpower.system_state_chargecompleted]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Maintenance charge: " + String(falseTrue[datalayer_extended.cellpower.system_state_maintenancecharge]) +
|
||||||
|
"</h4>";
|
||||||
|
content += "<h3>IO:</h3>";
|
||||||
|
content +=
|
||||||
|
"<h4>Main positive relay: " + String(falseTrue[datalayer_extended.cellpower.IO_state_main_positive_relay]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Main negative relay: " + String(falseTrue[datalayer_extended.cellpower.IO_state_main_negative_relay]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Charge enabled: " + String(falseTrue[datalayer_extended.cellpower.IO_state_charge_enable]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Precharge relay: " + String(falseTrue[datalayer_extended.cellpower.IO_state_precharge_relay]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Discharge enable: " + String(falseTrue[datalayer_extended.cellpower.IO_state_discharge_enable]) + "</h4>";
|
||||||
|
content += "<h4>IO 6: " + String(falseTrue[datalayer_extended.cellpower.IO_state_IO_6]) + "</h4>";
|
||||||
|
content += "<h4>IO 7: " + String(falseTrue[datalayer_extended.cellpower.IO_state_IO_7]) + "</h4>";
|
||||||
|
content += "<h4>IO 8: " + String(falseTrue[datalayer_extended.cellpower.IO_state_IO_8]) + "</h4>";
|
||||||
|
content += "<h3>Errors:</h3>";
|
||||||
|
content +=
|
||||||
|
"<h4>Cell overvoltage: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_overvoltage]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Cell undervoltage: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_undervoltage]) + "</h4>";
|
||||||
|
content += "<h4>Cell end of life voltage: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.error_Cell_end_of_life_voltage]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Cell voltage misread: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_voltage_misread]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Cell over temperature: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_over_temperature]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Cell under temperature: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_under_temperature]) +
|
||||||
|
"</h4>";
|
||||||
|
content += "<h4>Cell unmanaged: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_unmanaged]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>LMU over temperature: " + String(falseTrue[datalayer_extended.cellpower.error_LMU_over_temperature]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>LMU under temperature: " + String(falseTrue[datalayer_extended.cellpower.error_LMU_under_temperature]) +
|
||||||
|
"</h4>";
|
||||||
|
content += "<h4>Temp sensor open circuit: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.error_Temp_sensor_open_circuit]) + "</h4>";
|
||||||
|
content += "<h4>Temp sensor short circuit: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.error_Temp_sensor_short_circuit]) + "</h4>";
|
||||||
|
content += "<h4>SUB comm: " + String(falseTrue[datalayer_extended.cellpower.error_SUB_communication]) + "</h4>";
|
||||||
|
content += "<h4>LMU comm: " + String(falseTrue[datalayer_extended.cellpower.error_LMU_communication]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Over current In: " + String(falseTrue[datalayer_extended.cellpower.error_Over_current_IN]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Over current Out: " + String(falseTrue[datalayer_extended.cellpower.error_Over_current_OUT]) + "</h4>";
|
||||||
|
content += "<h4>Short circuit: " + String(falseTrue[datalayer_extended.cellpower.error_Short_circuit]) + "</h4>";
|
||||||
|
content += "<h4>Leak detected: " + String(falseTrue[datalayer_extended.cellpower.error_Leak_detected]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Leak detection failed: " + String(falseTrue[datalayer_extended.cellpower.error_Leak_detection_failed]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Voltage diff: " + String(falseTrue[datalayer_extended.cellpower.error_Voltage_difference]) + "</h4>";
|
||||||
|
content += "<h4>BMCU supply overvoltage: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.error_BMCU_supply_over_voltage]) + "</h4>";
|
||||||
|
content += "<h4>BMCU supply undervoltage: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.error_BMCU_supply_under_voltage]) + "</h4>";
|
||||||
|
content += "<h4>Main positive contactor: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.error_Main_positive_contactor]) + "</h4>";
|
||||||
|
content += "<h4>Main negative contactor: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.error_Main_negative_contactor]) + "</h4>";
|
||||||
|
content += "<h4>Precharge contactor: " + String(falseTrue[datalayer_extended.cellpower.error_Precharge_contactor]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Midpack contactor: " + String(falseTrue[datalayer_extended.cellpower.error_Midpack_contactor]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Precharge timeout: " + String(falseTrue[datalayer_extended.cellpower.error_Precharge_timeout]) + "</h4>";
|
||||||
|
content += "<h4>EMG connector override: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.error_Emergency_connector_override]) + "</h4>";
|
||||||
|
content += "<h3>Warnings:</h3>";
|
||||||
|
content +=
|
||||||
|
"<h4>High cell voltage: " + String(falseTrue[datalayer_extended.cellpower.warning_High_cell_voltage]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Low cell voltage: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_cell_voltage]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>High cell temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_High_cell_temperature]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Low cell temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_cell_temperature]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>High LMU temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_High_LMU_temperature]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Low LMU temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_LMU_temperature]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>SUB comm interf: " + String(falseTrue[datalayer_extended.cellpower.warning_SUB_communication_interfered]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>LMU comm interf: " + String(falseTrue[datalayer_extended.cellpower.warning_LMU_communication_interfered]) +
|
||||||
|
"</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>High current In: " + String(falseTrue[datalayer_extended.cellpower.warning_High_current_IN]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>High current Out: " + String(falseTrue[datalayer_extended.cellpower.warning_High_current_OUT]) + "</h4>";
|
||||||
|
content += "<h4>Pack resistance diff: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.warning_Pack_resistance_difference]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>High pack resistance: " + String(falseTrue[datalayer_extended.cellpower.warning_High_pack_resistance]) +
|
||||||
|
"</h4>";
|
||||||
|
content += "<h4>Cell resistance diff: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.warning_Cell_resistance_difference]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>High cell resistance: " + String(falseTrue[datalayer_extended.cellpower.warning_High_cell_resistance]) +
|
||||||
|
"</h4>";
|
||||||
|
content += "<h4>High BMCU supply voltage: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.warning_High_BMCU_supply_voltage]) + "</h4>";
|
||||||
|
content += "<h4>Low BMCU supply voltage: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.warning_Low_BMCU_supply_voltage]) + "</h4>";
|
||||||
|
content += "<h4>Low SOC: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_SOC]) + "</h4>";
|
||||||
|
content += "<h4>Balancing required: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.warning_Balancing_required_OCV_model]) + "</h4>";
|
||||||
|
content += "<h4>Charger not responding: " +
|
||||||
|
String(falseTrue[datalayer_extended.cellpower.warning_Charger_not_responding]) + "</h4>";
|
||||||
|
#endif //CELLPOWER_BMS
|
||||||
|
|
||||||
#ifdef BYD_ATTO_3_BATTERY
|
#ifdef BYD_ATTO_3_BATTERY
|
||||||
content += "<h4>SOC estimated: " + String(datalayer_extended.bydAtto3.SOC_estimated) + "</h4>";
|
content += "<h4>SOC estimated: " + String(datalayer_extended.bydAtto3.SOC_estimated) + "</h4>";
|
||||||
content += "<h4>SOC highprec: " + String(datalayer_extended.bydAtto3.SOC_highprec) + "</h4>";
|
content += "<h4>SOC highprec: " + String(datalayer_extended.bydAtto3.SOC_highprec) + "</h4>";
|
||||||
|
@ -207,7 +342,7 @@ String advanced_battery_processor(const String& var) {
|
||||||
#endif //RENAULT_ZOE_GEN2_BATTERY
|
#endif //RENAULT_ZOE_GEN2_BATTERY
|
||||||
|
|
||||||
#if !defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && \
|
#if !defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && \
|
||||||
!defined(BYD_ATTO_3_BATTERY) && !defined(RENAULT_ZOE_GEN2_BATTERY) //Only the listed types have extra info
|
!defined(BYD_ATTO_3_BATTERY) && !defined(RENAULT_ZOE_GEN2_BATTERY) && !defined(CELLPOWER_BMS)
|
||||||
content += "No extra information available for this battery type";
|
content += "No extra information available for this battery type";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -459,7 +459,7 @@ String processor(const String& var) {
|
||||||
"<h4>loop() task max load last 10 s: " + String(datalayer.system.status.loop_task_10s_max_us) + " us</h4>";
|
"<h4>loop() task max load last 10 s: " + String(datalayer.system.status.loop_task_10s_max_us) + " us</h4>";
|
||||||
content += "<h4>Max load @ worst case execution of core task:</h4>";
|
content += "<h4>Max load @ worst case execution of core task:</h4>";
|
||||||
content += "<h4>10ms function timing: " + String(datalayer.system.status.time_snap_10ms_us) + " us</h4>";
|
content += "<h4>10ms function timing: " + String(datalayer.system.status.time_snap_10ms_us) + " us</h4>";
|
||||||
content += "<h4>5s function timing: " + String(datalayer.system.status.time_snap_5s_us) + " us</h4>";
|
content += "<h4>Values function timing: " + String(datalayer.system.status.time_snap_values_us) + " us</h4>";
|
||||||
content += "<h4>CAN/serial RX function timing: " + String(datalayer.system.status.time_snap_comm_us) + " us</h4>";
|
content += "<h4>CAN/serial RX function timing: " + String(datalayer.system.status.time_snap_comm_us) + " us</h4>";
|
||||||
content += "<h4>CAN TX function timing: " + String(datalayer.system.status.time_snap_cantx_us) + " us</h4>";
|
content += "<h4>CAN TX function timing: " + String(datalayer.system.status.time_snap_cantx_us) + " us</h4>";
|
||||||
content += "<h4>OTA function timing: " + String(datalayer.system.status.time_snap_ota_us) + " us</h4>";
|
content += "<h4>OTA function timing: " + String(datalayer.system.status.time_snap_ota_us) + " us</h4>";
|
||||||
|
@ -516,6 +516,9 @@ String processor(const String& var) {
|
||||||
#ifdef BYD_ATTO_3_BATTERY
|
#ifdef BYD_ATTO_3_BATTERY
|
||||||
content += "BYD Atto 3";
|
content += "BYD Atto 3";
|
||||||
#endif // BYD_ATTO_3_BATTERY
|
#endif // BYD_ATTO_3_BATTERY
|
||||||
|
#ifdef CELLPOWER_BMS
|
||||||
|
content += "Cellpower BMS";
|
||||||
|
#endif // CELLPOWER_BMS
|
||||||
#ifdef CHADEMO_BATTERY
|
#ifdef CHADEMO_BATTERY
|
||||||
content += "Chademo V2X mode";
|
content += "Chademo V2X mode";
|
||||||
#endif // CHADEMO_BATTERY
|
#endif // CHADEMO_BATTERY
|
||||||
|
@ -716,6 +719,27 @@ String processor(const String& var) {
|
||||||
content += "<span style='color: red;'>OFF</span>";
|
content += "<span style='color: red;'>OFF</span>";
|
||||||
}
|
}
|
||||||
content += "</h4>";
|
content += "</h4>";
|
||||||
|
|
||||||
|
content += "<h4>Pre Charge: ";
|
||||||
|
if (digitalRead(PRECHARGE_PIN) == HIGH) {
|
||||||
|
content += "<span style='color: green;'>✓</span>";
|
||||||
|
} else {
|
||||||
|
content += "<span style='color: red;'>✕</span>";
|
||||||
|
}
|
||||||
|
content += " Cont. Neg.: ";
|
||||||
|
if (digitalRead(NEGATIVE_CONTACTOR_PIN) == HIGH) {
|
||||||
|
content += "<span style='color: green;'>✓</span>";
|
||||||
|
} else {
|
||||||
|
content += "<span style='color: red;'>✕</span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
content += " Cont. Pos.: ";
|
||||||
|
if (digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH) {
|
||||||
|
content += "<span style='color: green;'>✓</span>";
|
||||||
|
} else {
|
||||||
|
content += "<span style='color: red;'>✕</span>";
|
||||||
|
}
|
||||||
|
content += "</h4>";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Close the block
|
// Close the block
|
||||||
|
|
|
@ -84,12 +84,14 @@ static uint16_t charge_current = 0;
|
||||||
static int16_t temperature_average = 0;
|
static int16_t temperature_average = 0;
|
||||||
static uint16_t inverter_voltage = 0;
|
static uint16_t inverter_voltage = 0;
|
||||||
static uint16_t inverter_SOC = 0;
|
static uint16_t inverter_SOC = 0;
|
||||||
|
static uint16_t remaining_capacity_ah = 0;
|
||||||
|
static uint16_t fully_charged_capacity_ah = 0;
|
||||||
static long inverter_timestamp = 0;
|
static long inverter_timestamp = 0;
|
||||||
static bool initialDataSent = 0;
|
static bool initialDataSent = 0;
|
||||||
|
|
||||||
void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
||||||
//Calculate values
|
|
||||||
|
|
||||||
|
/* Calculate allowed charge/discharge currents*/
|
||||||
if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0
|
if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0
|
||||||
charge_current =
|
charge_current =
|
||||||
((datalayer.battery.status.max_charge_power_W * 10) /
|
((datalayer.battery.status.max_charge_power_W * 10) /
|
||||||
|
@ -103,22 +105,30 @@ void update_values_can_inverter() { //This function maps all the values fetched
|
||||||
//The above calculation results in (30 000*10)/3700=81A
|
//The above calculation results in (30 000*10)/3700=81A
|
||||||
discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
|
discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
|
||||||
}
|
}
|
||||||
|
/* Restrict values from user settings if needed*/
|
||||||
if (charge_current > datalayer.battery.info.max_charge_amp_dA) {
|
if (charge_current > datalayer.battery.info.max_charge_amp_dA) {
|
||||||
charge_current =
|
charge_current =
|
||||||
datalayer.battery.info
|
datalayer.battery.info
|
||||||
.max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
.max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discharge_current > datalayer.battery.info.max_discharge_amp_dA) {
|
if (discharge_current > datalayer.battery.info.max_discharge_amp_dA) {
|
||||||
discharge_current =
|
discharge_current =
|
||||||
datalayer.battery.info
|
datalayer.battery.info
|
||||||
.max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
.max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calculate temperature */
|
||||||
temperature_average =
|
temperature_average =
|
||||||
((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2);
|
((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2);
|
||||||
|
|
||||||
|
/* Calculate capacity, Amp hours(Ah) = Watt hours (Wh) / Voltage (V)*/
|
||||||
|
if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0
|
||||||
|
remaining_capacity_ah =
|
||||||
|
((datalayer.battery.status.reported_remaining_capacity_Wh / datalayer.battery.status.voltage_dV) * 100);
|
||||||
|
fully_charged_capacity_ah =
|
||||||
|
((datalayer.battery.info.total_capacity_Wh / datalayer.battery.status.voltage_dV) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
//Map values to CAN messages
|
//Map values to CAN messages
|
||||||
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
|
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
|
||||||
BYD_110.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8);
|
BYD_110.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8);
|
||||||
|
@ -139,12 +149,12 @@ void update_values_can_inverter() { //This function maps all the values fetched
|
||||||
//StateOfHealth (100.00%)
|
//StateOfHealth (100.00%)
|
||||||
BYD_150.data.u8[2] = (datalayer.battery.status.soh_pptt >> 8);
|
BYD_150.data.u8[2] = (datalayer.battery.status.soh_pptt >> 8);
|
||||||
BYD_150.data.u8[3] = (datalayer.battery.status.soh_pptt & 0x00FF);
|
BYD_150.data.u8[3] = (datalayer.battery.status.soh_pptt & 0x00FF);
|
||||||
//Maximum discharge power allowed (Unit: A+1)
|
//Remaining capacity (Ah+1)
|
||||||
BYD_150.data.u8[4] = (discharge_current >> 8);
|
BYD_150.data.u8[4] = (remaining_capacity_ah >> 8);
|
||||||
BYD_150.data.u8[5] = (discharge_current & 0x00FF);
|
BYD_150.data.u8[5] = (remaining_capacity_ah & 0x00FF);
|
||||||
//Maximum charge power allowed (Unit: A+1)
|
//Fully charged capacity (Ah+1)
|
||||||
BYD_150.data.u8[6] = (charge_current >> 8);
|
BYD_150.data.u8[6] = (fully_charged_capacity_ah >> 8);
|
||||||
BYD_150.data.u8[7] = (charge_current & 0x00FF);
|
BYD_150.data.u8[7] = (fully_charged_capacity_ah & 0x00FF);
|
||||||
|
|
||||||
//Voltage (ex 370.0)
|
//Voltage (ex 370.0)
|
||||||
BYD_1D0.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
|
BYD_1D0.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue