mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 17:59:27 +02:00
136 lines
5 KiB
C++
136 lines
5 KiB
C++
#include "../../datalayer/datalayer.h"
|
|
#include "../utils/events.h"
|
|
|
|
static uint16_t cell_deviation_mV = 0;
|
|
static uint8_t charge_limit_failures = 0;
|
|
static uint8_t discharge_limit_failures = 0;
|
|
static bool battery_full_event_fired = false;
|
|
static bool battery_empty_event_fired = false;
|
|
|
|
void update_machineryprotection() {
|
|
// Start checking that the battery is within reason. Incase we see any funny business, raise an event!
|
|
|
|
// Battery is overheated!
|
|
if (datalayer.battery.status.temperature_max_dC > 500) {
|
|
set_event(EVENT_BATTERY_OVERHEAT, datalayer.battery.status.temperature_max_dC);
|
|
} else {
|
|
clear_event(EVENT_BATTERY_OVERHEAT);
|
|
}
|
|
|
|
// Battery is frozen!
|
|
if (datalayer.battery.status.temperature_min_dC < -250) {
|
|
set_event(EVENT_BATTERY_FROZEN, datalayer.battery.status.temperature_min_dC);
|
|
} else {
|
|
clear_event(EVENT_BATTERY_FROZEN);
|
|
}
|
|
|
|
// Battery voltage is over designed max voltage!
|
|
if (datalayer.battery.status.voltage_dV > datalayer.battery.info.max_design_voltage_dV) {
|
|
set_event(EVENT_BATTERY_OVERVOLTAGE, datalayer.battery.status.voltage_dV);
|
|
} else {
|
|
clear_event(EVENT_BATTERY_OVERVOLTAGE);
|
|
}
|
|
|
|
// Battery voltage is under designed min voltage!
|
|
if (datalayer.battery.status.voltage_dV < datalayer.battery.info.min_design_voltage_dV) {
|
|
set_event(EVENT_BATTERY_UNDERVOLTAGE, datalayer.battery.status.voltage_dV);
|
|
} else {
|
|
clear_event(EVENT_BATTERY_UNDERVOLTAGE);
|
|
}
|
|
|
|
// Battery is fully charged. Dont allow any more power into it
|
|
// Normally the BMS will send 0W allowed, but this acts as an additional layer of safety
|
|
if (datalayer.battery.status.reported_soc == 10000) //Scaled SOC% value is 100.00%
|
|
{
|
|
if (!battery_full_event_fired) {
|
|
set_event(EVENT_BATTERY_FULL, 0);
|
|
battery_full_event_fired = true;
|
|
}
|
|
datalayer.battery.status.max_charge_power_W = 0;
|
|
} else {
|
|
clear_event(EVENT_BATTERY_FULL);
|
|
battery_full_event_fired = false;
|
|
}
|
|
|
|
// Battery is empty. Do not allow further discharge.
|
|
// Normally the BMS will send 0W allowed, but this acts as an additional layer of safety
|
|
if (datalayer.battery.status.reported_soc == 0) { //Scaled SOC% value is 0.00%
|
|
if (!battery_empty_event_fired) {
|
|
set_event(EVENT_BATTERY_EMPTY, 0);
|
|
battery_empty_event_fired = true;
|
|
}
|
|
datalayer.battery.status.max_discharge_power_W = 0;
|
|
} else {
|
|
clear_event(EVENT_BATTERY_EMPTY);
|
|
battery_empty_event_fired = false;
|
|
}
|
|
|
|
// Battery is extremely degraded, not fit for secondlifestorage!
|
|
if (datalayer.battery.status.soh_pptt < 2500) {
|
|
set_event(EVENT_LOW_SOH, datalayer.battery.status.soh_pptt);
|
|
} else {
|
|
clear_event(EVENT_LOW_SOH);
|
|
}
|
|
|
|
// Check if SOC% is plausible
|
|
if (datalayer.battery.status.voltage_dV >
|
|
(datalayer.battery.info.max_design_voltage_dV -
|
|
100)) { // When pack voltage is close to max, and SOC% is still low, raise event
|
|
if (datalayer.battery.status.real_soc < 6500) { // 65.00%
|
|
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc);
|
|
} else {
|
|
clear_event(EVENT_SOC_PLAUSIBILITY_ERROR);
|
|
}
|
|
}
|
|
|
|
// Check diff between highest and lowest cell
|
|
cell_deviation_mV = (datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV);
|
|
if (cell_deviation_mV > MAX_CELL_DEVIATION_MV) {
|
|
set_event(EVENT_CELL_DEVIATION_HIGH, (cell_deviation_mV / 20));
|
|
} else {
|
|
clear_event(EVENT_CELL_DEVIATION_HIGH);
|
|
}
|
|
|
|
// Inverter is charging with more power than battery wants!
|
|
if (datalayer.battery.status.active_power_W > 0) { // Charging
|
|
if (datalayer.battery.status.active_power_W > (datalayer.battery.status.max_charge_power_W + 2000)) {
|
|
if (charge_limit_failures > MAX_CHARGE_DISCHARGE_LIMIT_FAILURES) {
|
|
set_event(EVENT_CHARGE_LIMIT_EXCEEDED, 0); // Alert when 2kW over requested max
|
|
} else {
|
|
charge_limit_failures++;
|
|
}
|
|
} else {
|
|
clear_event(EVENT_CHARGE_LIMIT_EXCEEDED);
|
|
charge_limit_failures = 0;
|
|
}
|
|
}
|
|
|
|
// Inverter is pulling too much power from battery!
|
|
if (datalayer.battery.status.active_power_W < 0) { // Discharging
|
|
if (-datalayer.battery.status.active_power_W > (datalayer.battery.status.max_discharge_power_W + 2000)) {
|
|
if (discharge_limit_failures > MAX_CHARGE_DISCHARGE_LIMIT_FAILURES) {
|
|
set_event(EVENT_DISCHARGE_LIMIT_EXCEEDED, 0); // Alert when 2kW over requested max
|
|
} else {
|
|
discharge_limit_failures++;
|
|
}
|
|
} else {
|
|
clear_event(EVENT_DISCHARGE_LIMIT_EXCEEDED);
|
|
discharge_limit_failures = 0;
|
|
}
|
|
}
|
|
|
|
// Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error
|
|
if (!datalayer.battery.status.CAN_battery_still_alive) {
|
|
set_event(EVENT_CAN_RX_FAILURE, 0);
|
|
} else {
|
|
datalayer.battery.status.CAN_battery_still_alive--;
|
|
clear_event(EVENT_CAN_RX_FAILURE);
|
|
}
|
|
|
|
// Too many malformed CAN messages recieved!
|
|
if (datalayer.battery.status.CAN_error_counter > MAX_CAN_FAILURES) {
|
|
set_event(EVENT_CAN_RX_WARNING, 0);
|
|
} else {
|
|
clear_event(EVENT_CAN_RX_WARNING);
|
|
}
|
|
}
|