mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 10:19:29 +02:00
Merge remote-tracking branch 'origin' into feature/bmwi3
This commit is contained in:
commit
25b0a7da65
78 changed files with 10827 additions and 2100 deletions
1
.github/workflows/compile-all-batteries.yml
vendored
1
.github/workflows/compile-all-batteries.yml
vendored
|
@ -49,6 +49,7 @@ jobs:
|
||||||
# - LUNA2000_MODBUS
|
# - LUNA2000_MODBUS
|
||||||
# - PYLON_CAN
|
# - PYLON_CAN
|
||||||
# - SMA_CAN
|
# - SMA_CAN
|
||||||
|
# - SMA_TRIPOWER_CAN
|
||||||
# - SOFAR_CAN
|
# - SOFAR_CAN
|
||||||
# - SOLAX_CAN
|
# - SOLAX_CAN
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,10 @@ jobs:
|
||||||
- LUNA2000_MODBUS
|
- LUNA2000_MODBUS
|
||||||
- PYLON_CAN
|
- PYLON_CAN
|
||||||
- SMA_CAN
|
- SMA_CAN
|
||||||
|
- SMA_TRIPOWER_CAN
|
||||||
- SOFAR_CAN
|
- SOFAR_CAN
|
||||||
- SOLAX_CAN
|
- SOLAX_CAN
|
||||||
|
- SERIAL_LINK_TRANSMITTER
|
||||||
|
|
||||||
# This is the platform GitHub will use to run our workflow.
|
# This is the platform GitHub will use to run our workflow.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
2
.github/workflows/compile-all-inverters.yml
vendored
2
.github/workflows/compile-all-inverters.yml
vendored
|
@ -47,8 +47,10 @@ jobs:
|
||||||
- LUNA2000_MODBUS
|
- LUNA2000_MODBUS
|
||||||
- PYLON_CAN
|
- PYLON_CAN
|
||||||
- SMA_CAN
|
- SMA_CAN
|
||||||
|
- SMA_TRIPOWER_CAN
|
||||||
- SOFAR_CAN
|
- SOFAR_CAN
|
||||||
- SOLAX_CAN
|
- SOLAX_CAN
|
||||||
|
- SERIAL_LINK_TRANSMITTER
|
||||||
|
|
||||||
# This is the platform GitHub will use to run our workflow.
|
# This is the platform GitHub will use to run our workflow.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
30
.github/workflows/unit-tests.yml
vendored
Normal file
30
.github/workflows/unit-tests.yml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
name: Run Unit Tests
|
||||||
|
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Configure and build with CMake
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
cmake --build .
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: |
|
||||||
|
set -e # Exit immediately on non-zero exit code
|
||||||
|
cd build/test
|
||||||
|
dir -s
|
||||||
|
for test_executable in *; do
|
||||||
|
if [ -f "$test_executable" ] && [ -x "$test_executable" ]; then
|
||||||
|
./"$test_executable"
|
||||||
|
fi
|
||||||
|
done
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,5 +1,9 @@
|
||||||
# Ignore any .vscode folder
|
# Ignore any .vscode folder
|
||||||
*.vscode/
|
*.vscode/
|
||||||
|
|
||||||
# Ignore any files in the build folder
|
# Ignore any files in any build folder
|
||||||
Software/build/
|
*build/
|
||||||
|
|
||||||
|
# Ignore .exe (unit tests)
|
||||||
|
*.exe
|
||||||
|
**/.DS_Store
|
||||||
|
|
10
CMakeLists.txt
Normal file
10
CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
# Set the C++ standard to C++20
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
project(BatteryEmulator)
|
||||||
|
|
||||||
|
# add_subdirectory(Software/src/devboard/utils)
|
||||||
|
add_subdirectory(test)
|
|
@ -11,6 +11,7 @@
|
||||||
#include "src/devboard/utils/events.h"
|
#include "src/devboard/utils/events.h"
|
||||||
#include "src/inverter/INVERTERS.h"
|
#include "src/inverter/INVERTERS.h"
|
||||||
#include "src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h"
|
#include "src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h"
|
||||||
|
#include "src/lib/bblanchon-ArduinoJson/ArduinoJson.h"
|
||||||
#include "src/lib/eModbus-eModbus/Logging.h"
|
#include "src/lib/eModbus-eModbus/Logging.h"
|
||||||
#include "src/lib/eModbus-eModbus/ModbusServerRTU.h"
|
#include "src/lib/eModbus-eModbus/ModbusServerRTU.h"
|
||||||
#include "src/lib/eModbus-eModbus/scripts/mbServerFCs.h"
|
#include "src/lib/eModbus-eModbus/scripts/mbServerFCs.h"
|
||||||
|
@ -22,7 +23,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Preferences settings; // Store user settings
|
Preferences settings; // Store user settings
|
||||||
const char* version_number = "5.2.0"; // The current software version, shown on webserver
|
const char* version_number = "5.3.RC"; // The current software version, shown on webserver
|
||||||
// Interval settings
|
// Interval settings
|
||||||
int intervalUpdateValues = 4800; // Interval at which to update inverter values / Modbus registers
|
int intervalUpdateValues = 4800; // Interval at which to update inverter values / Modbus registers
|
||||||
const int interval10 = 10; // Interval for 10ms tasks
|
const int interval10 = 10; // Interval for 10ms tasks
|
||||||
|
@ -53,27 +54,27 @@ uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory
|
||||||
ModbusServerRTU MBserver(Serial2, 2000);
|
ModbusServerRTU MBserver(Serial2, 2000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Common inverter parameters. Batteries map their values to these variables
|
// Common system parameters. Batteries map their values to these variables
|
||||||
uint16_t max_voltage = ABSOLUTE_MAX_VOLTAGE; // If higher charging is not possible (goes into forced discharge)
|
uint32_t system_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-150000Wh
|
||||||
uint16_t min_voltage = ABSOLUTE_MIN_VOLTAGE; // If lower disables discharging battery
|
uint32_t system_remaining_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-150000Wh
|
||||||
uint16_t battery_voltage = 3700; //V+1, 0-500.0 (0-5000)
|
int16_t system_temperature_max_dC = 0; //C+1, -50.0 - 50.0
|
||||||
uint16_t battery_current = 0;
|
int16_t system_temperature_min_dC = 0; //C+1, -50.0 - 50.0
|
||||||
uint16_t SOC = 5000; //SOC%, 0-100.00 (0-10000)
|
int16_t system_active_power_W = 0; //Watts, -32000 to 32000
|
||||||
uint16_t StateOfHealth = 9900; //SOH%, 0-100.00 (0-10000)
|
int16_t system_battery_current_dA = 0; //A+1, -1000 - 1000
|
||||||
uint16_t capacity_Wh = BATTERY_WH_MAX; //Wh, 0-60000
|
uint16_t system_battery_voltage_dV = 3700; //V+1, 0-500.0 (0-5000)
|
||||||
uint16_t remaining_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-60000
|
uint16_t system_max_design_voltage_dV = 5000; //V+1, 0-500.0 (0-5000)
|
||||||
uint16_t max_target_discharge_power = 0; // 0W (0W > restricts to no discharge), Updates later on from CAN
|
uint16_t system_min_design_voltage_dV = 2500; //V+1, 0-500.0 (0-5000)
|
||||||
uint16_t max_target_charge_power = 4312; // Init to 4.3kW, Updates later on from CAN
|
uint16_t system_scaled_SOC_pptt = 5000; //SOC%, 0-100.00 (0-10000)
|
||||||
uint16_t temperature_max = 50; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
uint16_t system_real_SOC_pptt = 5000; //SOC%, 0-100.00 (0-10000)
|
||||||
uint16_t temperature_min = 60; // Reads from battery later
|
uint16_t system_SOH_pptt = 9900; //SOH%, 0-100.00 (0-10000)
|
||||||
uint8_t bms_char_dis_status = STANDBY; // 0 standby, 1 discharging, 2, charging
|
uint16_t system_max_discharge_power_W = 0; //Watts, 0 to 65535
|
||||||
uint8_t bms_status = ACTIVE; // ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING]
|
uint16_t system_max_charge_power_W = 4312; //Watts, 0 to 65535
|
||||||
uint16_t stat_batt_power = 0; // Power going in/out of battery
|
uint16_t system_cell_max_voltage_mV = 3700; //mV, 0-5000 , Stores the highest cell millivolt value
|
||||||
uint16_t cell_max_voltage = 3700; // Stores the highest cell voltage value in the system
|
uint16_t system_cell_min_voltage_mV = 3700; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
uint16_t cell_min_voltage = 3700; // Stores the minimum cell voltage value in the system
|
uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV. Oversized to accomodate all setups
|
||||||
uint16_t cellvoltages[120]; // Stores all cell voltages
|
uint8_t system_bms_status = ACTIVE; //ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING]
|
||||||
uint8_t nof_cellvoltages = 0; // Total number of cell voltages, set by each battery.
|
uint8_t system_number_of_cells = 0; //Total number of cell voltages, set by each battery
|
||||||
bool LFP_Chemistry = false;
|
bool system_LFP_Chemistry = false; //Set to true or false depending on cell chemistry
|
||||||
|
|
||||||
// Common charger parameters
|
// Common charger parameters
|
||||||
volatile float charger_setpoint_HV_VDC = 0.0f;
|
volatile float charger_setpoint_HV_VDC = 0.0f;
|
||||||
|
@ -96,6 +97,7 @@ static uint8_t brightness = 0;
|
||||||
static bool rampUp = true;
|
static bool rampUp = true;
|
||||||
const uint8_t maxBrightness = 100;
|
const uint8_t maxBrightness = 100;
|
||||||
uint8_t LEDcolor = GREEN;
|
uint8_t LEDcolor = GREEN;
|
||||||
|
bool test_all_colors = false;
|
||||||
|
|
||||||
// Contactor parameters
|
// Contactor parameters
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
|
@ -144,11 +146,10 @@ void setup() {
|
||||||
|
|
||||||
inform_user_on_inverter();
|
inform_user_on_inverter();
|
||||||
|
|
||||||
inform_user_on_battery();
|
|
||||||
|
|
||||||
#ifdef BATTERY_HAS_INIT
|
|
||||||
init_battery();
|
init_battery();
|
||||||
#endif
|
|
||||||
|
// BOOT button at runtime is used as an input for various things
|
||||||
|
pinMode(0, INPUT_PULLUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform main program functions
|
// Perform main program functions
|
||||||
|
@ -185,9 +186,10 @@ void loop() {
|
||||||
if (millis() - previousMillisUpdateVal >= intervalUpdateValues) // Every 4.8s
|
if (millis() - previousMillisUpdateVal >= intervalUpdateValues) // Every 4.8s
|
||||||
{
|
{
|
||||||
previousMillisUpdateVal = millis();
|
previousMillisUpdateVal = millis();
|
||||||
|
update_SOC(); // Check if real or calculated SOC% value should be sent
|
||||||
update_values(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus.
|
update_values(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus.
|
||||||
if (DUMMY_EVENT_ENABLED) {
|
if (DUMMY_EVENT_ENABLED) {
|
||||||
set_event(EVENT_DUMMY, (uint8_t)millis());
|
set_event(EVENT_DUMMY_ERROR, (uint8_t)millis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +198,13 @@ void loop() {
|
||||||
#ifdef DUAL_CAN
|
#ifdef DUAL_CAN
|
||||||
send_can2();
|
send_can2();
|
||||||
#endif
|
#endif
|
||||||
update_event_timestamps();
|
run_event_handling();
|
||||||
|
|
||||||
|
if (digitalRead(0) == HIGH) {
|
||||||
|
test_all_colors = false;
|
||||||
|
} else {
|
||||||
|
test_all_colors = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialization functions
|
// Initialization functions
|
||||||
|
@ -214,7 +222,7 @@ void init_stored_settings() {
|
||||||
settings.clear(); // If this clear function is executed, no settings will be read from storage
|
settings.clear(); // If this clear function is executed, no settings will be read from storage
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static uint16_t temp = 0;
|
static uint32_t temp = 0;
|
||||||
temp = settings.getUInt("BATTERY_WH_MAX", false);
|
temp = settings.getUInt("BATTERY_WH_MAX", false);
|
||||||
if (temp != 0) {
|
if (temp != 0) {
|
||||||
BATTERY_WH_MAX = temp;
|
BATTERY_WH_MAX = temp;
|
||||||
|
@ -234,7 +242,10 @@ void init_stored_settings() {
|
||||||
temp = settings.getUInt("MAXDISCHARGEAMP", false);
|
temp = settings.getUInt("MAXDISCHARGEAMP", false);
|
||||||
if (temp != 0) {
|
if (temp != 0) {
|
||||||
MAXDISCHARGEAMP = temp;
|
MAXDISCHARGEAMP = temp;
|
||||||
}
|
temp = settings.getBool("USE_SCALED_SOC", false);
|
||||||
|
USE_SCALED_SOC = temp; //This bool needs to be checked inside the temp!= block
|
||||||
|
} // No way to know if it wasnt reset otherwise
|
||||||
|
|
||||||
settings.end();
|
settings.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,6 +346,9 @@ void inform_user_on_inverter() {
|
||||||
#ifdef SMA_CAN
|
#ifdef SMA_CAN
|
||||||
Serial.println("SMA CAN protocol selected");
|
Serial.println("SMA CAN protocol selected");
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef SMA_TRIPOWER_CAN
|
||||||
|
Serial.println("SMA Tripower CAN protocol selected");
|
||||||
|
#endif
|
||||||
#ifdef SOFAR_CAN
|
#ifdef SOFAR_CAN
|
||||||
Serial.println("SOFAR CAN protocol selected");
|
Serial.println("SOFAR CAN protocol selected");
|
||||||
#endif
|
#endif
|
||||||
|
@ -345,48 +359,11 @@ void inform_user_on_inverter() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void inform_user_on_battery() {
|
void init_battery() {
|
||||||
// Inform user what battery is used
|
// Inform user what battery is used and perform setup
|
||||||
#ifdef BMW_I3_BATTERY
|
setup_battery();
|
||||||
Serial.println("BMW i3 battery selected");
|
|
||||||
pinMode(WUP_PIN, OUTPUT); //This pin used for WUP relay
|
#ifndef BATTERY_SELECTED
|
||||||
digitalWrite(WUP_PIN, LOW);
|
|
||||||
#ifdef CONTACTOR_CONTROL
|
|
||||||
// Contactor control cannot be used when WUP signal is sent on GPIO pins
|
|
||||||
#error CONTACTOR CONTROL CANNOT BE USED ON BMW i3
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#ifdef CHADEMO_BATTERY
|
|
||||||
Serial.println("Chademo battery selected");
|
|
||||||
#endif
|
|
||||||
#ifdef IMIEV_CZERO_ION_BATTERY
|
|
||||||
Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected");
|
|
||||||
#endif
|
|
||||||
#ifdef KIA_HYUNDAI_64_BATTERY
|
|
||||||
Serial.println("Kia Niro / Hyundai Kona 64kWh battery selected");
|
|
||||||
#endif
|
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
|
||||||
Serial.println("Nissan LEAF battery selected");
|
|
||||||
#endif
|
|
||||||
#ifdef RENAULT_KANGOO_BATTERY
|
|
||||||
Serial.println("Renault Kangoo battery selected");
|
|
||||||
#endif
|
|
||||||
#ifdef SANTA_FE_PHEV_BATTERY
|
|
||||||
Serial.println("Hyundai Santa Fe PHEV battery selected");
|
|
||||||
#endif
|
|
||||||
#ifdef RENAULT_ZOE_BATTERY
|
|
||||||
Serial.println("Renault Zoe battery selected");
|
|
||||||
#endif
|
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
|
||||||
Serial.println("Tesla Model 3 battery selected");
|
|
||||||
#endif
|
|
||||||
#ifdef TEST_FAKE_BATTERY
|
|
||||||
Serial.println("Test mode with fake battery selected");
|
|
||||||
#endif
|
|
||||||
#ifdef SERIAL_LINK_RECEIVER
|
|
||||||
Serial.println("SERIAL_DATA_LINK_RECEIVER selected");
|
|
||||||
#endif
|
|
||||||
#if !defined(ABSOLUTE_MAX_VOLTAGE)
|
|
||||||
#error No battery selected! Choose one from the USER_SETTINGS.h file
|
#error No battery selected! Choose one from the USER_SETTINGS.h file
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -397,37 +374,10 @@ void receive_can() { // This section checks if we have a complete CAN message i
|
||||||
CAN_frame_t rx_frame;
|
CAN_frame_t rx_frame;
|
||||||
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE) {
|
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE) {
|
||||||
if (rx_frame.FIR.B.FF == CAN_frame_std) {
|
if (rx_frame.FIR.B.FF == CAN_frame_std) {
|
||||||
//printf("New standard frame");
|
//printf("New standard frame");
|
||||||
// Battery
|
// Battery
|
||||||
#ifdef BMW_I3_BATTERY
|
#ifndef SERIAL_LINK_RECEIVER
|
||||||
receive_can_i3_battery(rx_frame);
|
receive_can_battery(rx_frame);
|
||||||
#endif
|
|
||||||
#ifdef CHADEMO_BATTERY
|
|
||||||
receive_can_chademo_battery(rx_frame);
|
|
||||||
#endif
|
|
||||||
#ifdef IMIEV_CZERO_ION_BATTERY
|
|
||||||
receive_can_imiev_battery(rx_frame);
|
|
||||||
#endif
|
|
||||||
#ifdef KIA_HYUNDAI_64_BATTERY
|
|
||||||
receive_can_kiaHyundai_64_battery(rx_frame);
|
|
||||||
#endif
|
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
|
||||||
receive_can_leaf_battery(rx_frame);
|
|
||||||
#endif
|
|
||||||
#ifdef RENAULT_KANGOO_BATTERY
|
|
||||||
receive_can_kangoo_battery(rx_frame);
|
|
||||||
#endif
|
|
||||||
#ifdef SANTA_FE_PHEV_BATTERY
|
|
||||||
receive_can_santafe_phev_battery(rx_frame);
|
|
||||||
#endif
|
|
||||||
#ifdef RENAULT_ZOE_BATTERY
|
|
||||||
receive_can_zoe_battery(rx_frame);
|
|
||||||
#endif
|
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
|
||||||
receive_can_tesla_model_3_battery(rx_frame);
|
|
||||||
#endif
|
|
||||||
#ifdef TEST_FAKE_BATTERY
|
|
||||||
receive_can_test_battery(rx_frame);
|
|
||||||
#endif
|
#endif
|
||||||
// Inverter
|
// Inverter
|
||||||
#ifdef BYD_CAN
|
#ifdef BYD_CAN
|
||||||
|
@ -435,6 +385,9 @@ void receive_can() { // This section checks if we have a complete CAN message i
|
||||||
#endif
|
#endif
|
||||||
#ifdef SMA_CAN
|
#ifdef SMA_CAN
|
||||||
receive_can_sma(rx_frame);
|
receive_can_sma(rx_frame);
|
||||||
|
#endif
|
||||||
|
#ifdef SMA_TRIPOWER_CAN
|
||||||
|
receive_can_sma_tripower(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
// Charger
|
// Charger
|
||||||
#ifdef CHEVYVOLT_CHARGER
|
#ifdef CHEVYVOLT_CHARGER
|
||||||
|
@ -467,40 +420,14 @@ void send_can() {
|
||||||
#ifdef SMA_CAN
|
#ifdef SMA_CAN
|
||||||
send_can_sma();
|
send_can_sma();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef SMA_TRIPOWER_CAN
|
||||||
|
send_can_sma_tripower();
|
||||||
|
#endif
|
||||||
#ifdef SOFAR_CAN
|
#ifdef SOFAR_CAN
|
||||||
send_can_sofar();
|
send_can_sofar();
|
||||||
#endif
|
#endif
|
||||||
// Battery
|
// Battery
|
||||||
#ifdef BMW_I3_BATTERY
|
send_can_battery();
|
||||||
send_can_i3_battery();
|
|
||||||
#endif
|
|
||||||
#ifdef CHADEMO_BATTERY
|
|
||||||
send_can_chademo_battery();
|
|
||||||
#endif
|
|
||||||
#ifdef IMIEV_CZERO_ION_BATTERY
|
|
||||||
send_can_imiev_battery();
|
|
||||||
#endif
|
|
||||||
#ifdef KIA_HYUNDAI_64_BATTERY
|
|
||||||
send_can_kiaHyundai_64_battery();
|
|
||||||
#endif
|
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
|
||||||
send_can_leaf_battery();
|
|
||||||
#endif
|
|
||||||
#ifdef RENAULT_KANGOO_BATTERY
|
|
||||||
send_can_kangoo_battery();
|
|
||||||
#endif
|
|
||||||
#ifdef SANTA_FE_PHEV_BATTERY
|
|
||||||
send_can_santafe_phev_battery();
|
|
||||||
#endif
|
|
||||||
#ifdef RENAULT_ZOE_BATTERY
|
|
||||||
send_can_zoe_battery();
|
|
||||||
#endif
|
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
|
||||||
send_can_tesla_model_3_battery();
|
|
||||||
#endif
|
|
||||||
#ifdef TEST_FAKE_BATTERY
|
|
||||||
send_can_test_battery();
|
|
||||||
#endif
|
|
||||||
#ifdef CHEVYVOLT_CHARGER
|
#ifdef CHEVYVOLT_CHARGER
|
||||||
send_can_chevyvolt_charger();
|
send_can_chevyvolt_charger();
|
||||||
#endif
|
#endif
|
||||||
|
@ -563,30 +490,30 @@ void handle_LED_state() {
|
||||||
} else if (!rampUp && brightness == 0) {
|
} else if (!rampUp && brightness == 0) {
|
||||||
rampUp = true;
|
rampUp = true;
|
||||||
}
|
}
|
||||||
switch (LEDcolor) {
|
if (test_all_colors == false) {
|
||||||
case GREEN:
|
switch (get_event_level()) {
|
||||||
|
case EVENT_LEVEL_INFO:
|
||||||
|
LEDcolor = GREEN;
|
||||||
pixels.setPixelColor(0, pixels.Color(0, brightness, 0)); // Green pulsing LED
|
pixels.setPixelColor(0, pixels.Color(0, brightness, 0)); // Green pulsing LED
|
||||||
break;
|
break;
|
||||||
case YELLOW:
|
case EVENT_LEVEL_WARNING:
|
||||||
|
LEDcolor = YELLOW;
|
||||||
pixels.setPixelColor(0, pixels.Color(brightness, brightness, 0)); // Yellow pulsing LED
|
pixels.setPixelColor(0, pixels.Color(brightness, brightness, 0)); // Yellow pulsing LED
|
||||||
break;
|
break;
|
||||||
case BLUE:
|
case EVENT_LEVEL_DEBUG:
|
||||||
|
case EVENT_LEVEL_UPDATE:
|
||||||
|
LEDcolor = BLUE;
|
||||||
pixels.setPixelColor(0, pixels.Color(0, 0, brightness)); // Blue pulsing LED
|
pixels.setPixelColor(0, pixels.Color(0, 0, brightness)); // Blue pulsing LED
|
||||||
break;
|
break;
|
||||||
case RED:
|
case EVENT_LEVEL_ERROR:
|
||||||
|
LEDcolor = RED;
|
||||||
pixels.setPixelColor(0, pixels.Color(150, 0, 0)); // Red LED full brightness
|
pixels.setPixelColor(0, pixels.Color(150, 0, 0)); // Red LED full brightness
|
||||||
break;
|
break;
|
||||||
case TEST_ALL_COLORS:
|
|
||||||
pixels.setPixelColor(0, pixels.Color(brightness, abs((100 - brightness)), abs((50 - brightness)))); // RGB
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// BMS in fault state overrides everything
|
pixels.setPixelColor(0, pixels.Color(brightness, abs((100 - brightness)), abs((50 - brightness)))); // RGB
|
||||||
if (bms_status == FAULT) {
|
|
||||||
LEDcolor = RED;
|
|
||||||
pixels.setPixelColor(0, pixels.Color(255, 0, 0)); // Red LED full brightness
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pixels.show(); // This sends the updated pixel color to the hardware.
|
pixels.show(); // This sends the updated pixel color to the hardware.
|
||||||
|
@ -595,7 +522,7 @@ void handle_LED_state() {
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
void handle_contactors() {
|
void handle_contactors() {
|
||||||
// First check if we have any active errors, incase we do, turn off the battery
|
// First check if we have any active errors, incase we do, turn off the battery
|
||||||
if (bms_status == FAULT) {
|
if (system_bms_status == FAULT) {
|
||||||
timeSpentInFaultedMode++;
|
timeSpentInFaultedMode++;
|
||||||
} else {
|
} else {
|
||||||
timeSpentInFaultedMode = 0;
|
timeSpentInFaultedMode = 0;
|
||||||
|
@ -678,38 +605,26 @@ void handle_contactors() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void update_SOC() {
|
||||||
|
if (USE_SCALED_SOC) { //User has configred a SOC window. Calculate a SOC% to send towards inverter
|
||||||
|
static int16_t CalculatedSOC = 0;
|
||||||
|
CalculatedSOC = system_real_SOC_pptt;
|
||||||
|
CalculatedSOC = (10000) * (CalculatedSOC - (MINPERCENTAGE * 10)) / (MAXPERCENTAGE * 10 - MINPERCENTAGE * 10);
|
||||||
|
if (CalculatedSOC < 0) { //We are in the real SOC% range of 0-MINPERCENTAGE%
|
||||||
|
CalculatedSOC = 0;
|
||||||
|
}
|
||||||
|
if (CalculatedSOC > 10000) { //We are in the real SOC% range of MAXPERCENTAGE-100%
|
||||||
|
CalculatedSOC = 10000;
|
||||||
|
}
|
||||||
|
system_scaled_SOC_pptt = CalculatedSOC;
|
||||||
|
} else { // No SOC window wanted. Set scaled to same as real.
|
||||||
|
system_scaled_SOC_pptt = system_real_SOC_pptt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void update_values() {
|
void update_values() {
|
||||||
// Battery
|
// Battery
|
||||||
#ifdef BMW_I3_BATTERY
|
update_values_battery(); // Map the fake values to the correct registers
|
||||||
update_values_i3_battery(); // Map the values to the correct registers
|
|
||||||
#endif
|
|
||||||
#ifdef CHADEMO_BATTERY
|
|
||||||
update_values_chademo_battery(); // Map the values to the correct registers
|
|
||||||
#endif
|
|
||||||
#ifdef IMIEV_CZERO_ION_BATTERY
|
|
||||||
update_values_imiev_battery(); // Map the values to the correct registers
|
|
||||||
#endif
|
|
||||||
#ifdef KIA_HYUNDAI_64_BATTERY
|
|
||||||
update_values_kiaHyundai_64_battery(); // Map the values to the correct registers
|
|
||||||
#endif
|
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
|
||||||
update_values_leaf_battery(); // Map the values to the correct registers
|
|
||||||
#endif
|
|
||||||
#ifdef RENAULT_KANGOO_BATTERY
|
|
||||||
update_values_kangoo_battery(); // Map the values to the correct registers
|
|
||||||
#endif
|
|
||||||
#ifdef SANTA_FE_PHEV_BATTERY
|
|
||||||
update_values_santafe_phev_battery(); // Map the values to the correct registers
|
|
||||||
#endif
|
|
||||||
#ifdef RENAULT_ZOE_BATTERY
|
|
||||||
update_values_zoe_battery(); // Map the values to the correct registers
|
|
||||||
#endif
|
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
|
||||||
update_values_tesla_model_3_battery(); // Map the values to the correct registers
|
|
||||||
#endif
|
|
||||||
#ifdef TEST_FAKE_BATTERY
|
|
||||||
update_values_test_battery(); // Map the fake values to the correct registers
|
|
||||||
#endif
|
|
||||||
// Inverter
|
// Inverter
|
||||||
#ifdef BYD_CAN
|
#ifdef BYD_CAN
|
||||||
update_values_can_byd();
|
update_values_can_byd();
|
||||||
|
@ -726,6 +641,9 @@ void update_values() {
|
||||||
#ifdef SMA_CAN
|
#ifdef SMA_CAN
|
||||||
update_values_can_sma();
|
update_values_can_sma();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef SMA_TRIPOWER_CAN
|
||||||
|
update_values_can_sma_tripower();
|
||||||
|
#endif
|
||||||
#ifdef SOFAR_CAN
|
#ifdef SOFAR_CAN
|
||||||
update_values_can_sofar();
|
update_values_can_sofar();
|
||||||
#endif
|
#endif
|
||||||
|
@ -734,23 +652,22 @@ void update_values() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||||
void runSerialDataLink() {
|
void runSerialDataLink() {
|
||||||
static unsigned long updateTime = 0;
|
static unsigned long updateTime = 0;
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
#ifdef SERIAL_LINK_RECEIVER
|
|
||||||
if ((currentMillis - updateTime) > 1) { //Every 2ms
|
|
||||||
updateTime = currentMillis;
|
|
||||||
manageSerialLinkReceiver();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SERIAL_LINK_TRANSMITTER
|
|
||||||
if ((currentMillis - updateTime) > 1) { //Every 2ms
|
if ((currentMillis - updateTime) > 1) { //Every 2ms
|
||||||
updateTime = currentMillis;
|
updateTime = currentMillis;
|
||||||
manageSerialLinkTransmitter();
|
#ifdef SERIAL_LINK_RECEIVER
|
||||||
}
|
manageSerialLinkReceiver();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef SERIAL_LINK_TRANSMITTER
|
||||||
|
manageSerialLinkTransmitter();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void init_serialDataLink() {
|
void init_serialDataLink() {
|
||||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||||
|
@ -765,5 +682,7 @@ void storeSettings() {
|
||||||
settings.putUInt("MINPERCENTAGE", MINPERCENTAGE);
|
settings.putUInt("MINPERCENTAGE", MINPERCENTAGE);
|
||||||
settings.putUInt("MAXCHARGEAMP", MAXCHARGEAMP);
|
settings.putUInt("MAXCHARGEAMP", MAXCHARGEAMP);
|
||||||
settings.putUInt("MAXDISCHARGEAMP", MAXDISCHARGEAMP);
|
settings.putUInt("MAXDISCHARGEAMP", MAXDISCHARGEAMP);
|
||||||
|
settings.putBool("USE_SCALED_SOC", USE_SCALED_SOC);
|
||||||
|
|
||||||
settings.end();
|
settings.end();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,17 @@
|
||||||
/* They can be defined here, or later on in the WebUI */
|
/* They can be defined here, or later on in the WebUI */
|
||||||
|
|
||||||
/* Battery settings */
|
/* Battery settings */
|
||||||
volatile uint16_t BATTERY_WH_MAX =
|
volatile bool USE_SCALED_SOC =
|
||||||
30000; //Battery size in Wh (Maximum value for most inverters is 65000 [65kWh], you can use larger batteries but do not set value over 65000!
|
true; //Increases battery life. If true will rescale SOC between the configured min/max-percentage
|
||||||
|
volatile uint32_t BATTERY_WH_MAX = 30000; //Battery size in Wh
|
||||||
volatile uint16_t MAXPERCENTAGE =
|
volatile uint16_t MAXPERCENTAGE =
|
||||||
800; //80.0% , Max percentage the battery will charge to (App will show 100% once this value is reached)
|
800; //80.0% , Max percentage the battery will charge to (Inverter gets 100% when reached)
|
||||||
volatile uint16_t MINPERCENTAGE =
|
volatile uint16_t MINPERCENTAGE =
|
||||||
200; //20.0% , Min percentage the battery will discharge to (App will show 0% once this value is reached)
|
200; //20.0% , Min percentage the battery will discharge to (Inverter gets 0% when reached)
|
||||||
volatile uint16_t MAXCHARGEAMP =
|
volatile uint16_t MAXCHARGEAMP =
|
||||||
300; //30.0A , BYD CAN specific setting, Max charge speed in Amp (Some inverters needs to be artificially limited)
|
300; //30.0A , BYD CAN specific setting, Max charge in Amp (Some inverters needs to be limited)
|
||||||
volatile uint16_t MAXDISCHARGEAMP =
|
volatile uint16_t MAXDISCHARGEAMP =
|
||||||
300; //30.0A , BYD CAN specific setting, Max discharge speed in Amp (Some inverters needs to be artificially limited)
|
300; //30.0A , BYD CAN specific setting, Max discharge in Amp (Some inverters needs to be limited)
|
||||||
|
|
||||||
/* Charger settings (Optional, when generator charging) */
|
/* Charger settings (Optional, when generator charging) */
|
||||||
/* Charger settings */
|
/* Charger settings */
|
||||||
|
|
|
@ -25,11 +25,12 @@
|
||||||
//#define LUNA2000_MODBUS //Enable this line to emulate a "Luna2000 battery" over Modbus RTU
|
//#define LUNA2000_MODBUS //Enable this line to emulate a "Luna2000 battery" over Modbus RTU
|
||||||
//#define PYLON_CAN //Enable this line to emulate a "Pylontech battery" over CAN bus
|
//#define PYLON_CAN //Enable this line to emulate a "Pylontech battery" over CAN bus
|
||||||
//#define SMA_CAN //Enable this line to emulate a "BYD Battery-Box H 8.9kWh, 7 mod" over CAN bus
|
//#define SMA_CAN //Enable this line to emulate a "BYD Battery-Box H 8.9kWh, 7 mod" over CAN bus
|
||||||
|
//#define SMA_TRIPOWER_CAN //Enable this line to emulate a "SMA Home Storage battery" over CAN bus
|
||||||
//#define SOFAR_CAN //Enable this line to emulate a "Sofar Energy Storage Inverter High Voltage BMS General Protocol (Extended Frame)" over CAN bus
|
//#define SOFAR_CAN //Enable this line to emulate a "Sofar Energy Storage Inverter High Voltage BMS General Protocol (Extended Frame)" over CAN bus
|
||||||
//#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus
|
//#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus
|
||||||
|
|
||||||
/* Other options */
|
/* Other options */
|
||||||
#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs
|
//#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs
|
||||||
//#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting
|
//#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting
|
||||||
//#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence
|
//#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence
|
||||||
//#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM logic for contactors, which lower power consumption and heat generation
|
//#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM logic for contactors, which lower power consumption and heat generation
|
||||||
|
@ -37,10 +38,10 @@
|
||||||
//#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)
|
||||||
#define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.
|
#define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.
|
||||||
#define LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot
|
//#define LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot (overrides any battery settings set in USER_SETTINGS.cpp)
|
||||||
|
|
||||||
/* MQTT options */
|
/* MQTT options */
|
||||||
//#define MQTT // Enable this line to enable MQTT
|
// #define MQTT // Enable this line to enable MQTT
|
||||||
#define MQTT_SUBSCRIPTIONS \
|
#define MQTT_SUBSCRIPTIONS \
|
||||||
{ "my/topic/abc", "my/other/topic" }
|
{ "my/topic/abc", "my/other/topic" }
|
||||||
#define MQTT_SERVER "192.168.xxx.yyy"
|
#define MQTT_SERVER "192.168.xxx.yyy"
|
||||||
|
@ -54,12 +55,13 @@
|
||||||
//#define NISSANLEAF_CHARGER //Enable this line to control a Nissan LEAF PDM connected to battery - for example, when generator charging
|
//#define NISSANLEAF_CHARGER //Enable this line to control a Nissan LEAF PDM connected to battery - for example, when generator charging
|
||||||
|
|
||||||
/* Battery limits: These are set in the USER_SETTINGS.cpp file, or later on via the Webserver */
|
/* Battery limits: These are set in the USER_SETTINGS.cpp file, or later on via the Webserver */
|
||||||
extern volatile uint16_t BATTERY_WH_MAX;
|
extern volatile uint32_t BATTERY_WH_MAX;
|
||||||
extern volatile uint16_t MAXPERCENTAGE;
|
extern volatile uint16_t MAXPERCENTAGE;
|
||||||
extern volatile uint16_t MINPERCENTAGE;
|
extern volatile uint16_t MINPERCENTAGE;
|
||||||
extern volatile uint16_t MAXCHARGEAMP;
|
extern volatile uint16_t MAXCHARGEAMP;
|
||||||
extern volatile uint16_t MAXDISCHARGEAMP;
|
extern volatile uint16_t MAXDISCHARGEAMP;
|
||||||
extern volatile uint8_t AccessPointEnabled;
|
extern volatile uint8_t AccessPointEnabled;
|
||||||
|
extern volatile bool USE_SCALED_SOC;
|
||||||
|
|
||||||
/* Charger limits (Optional): Set in the USER_SETTINGS.cpp or later in the webserver */
|
/* Charger limits (Optional): Set in the USER_SETTINGS.cpp or later in the webserver */
|
||||||
extern volatile float charger_setpoint_HV_VDC;
|
extern volatile float charger_setpoint_HV_VDC;
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
|
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
#ifdef NISSAN_LEAF_BATTERY
|
||||||
#include "NISSAN-LEAF-BATTERY.h" //See this file for more LEAF battery settings
|
#include "NISSAN-LEAF-BATTERY.h" //See this file for more LEAF battery settings
|
||||||
#define BATTERY_HAS_INIT
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef RENAULT_KANGOO_BATTERY
|
#ifdef RENAULT_KANGOO_BATTERY
|
||||||
|
@ -48,4 +47,13 @@
|
||||||
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h" //See this file for more Serial-battery settings
|
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h" //See this file for more Serial-battery settings
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SERIAL_LINK_RECEIVER // The serial thing does its thing
|
||||||
|
void receive_can_battery();
|
||||||
|
#else
|
||||||
|
void receive_can_battery(CAN_frame_t rx_frame);
|
||||||
|
#endif
|
||||||
|
void update_values_battery();
|
||||||
|
void send_can_battery();
|
||||||
|
void setup_battery(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
#include "BMW-I3-BATTERY.h"
|
#include "BATTERIES.h"
|
||||||
|
#ifdef BMW_I3_BATTERY
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
#include "BMW-I3-BATTERY.h"
|
||||||
//TODO: before using
|
|
||||||
// Map the final values in update_values_i3_battery, set some to static values if not available (current, discharge max , charge max)
|
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send
|
static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send
|
||||||
|
@ -40,79 +39,6 @@ unsigned long turnOnTime; // Variables to store timestamps
|
||||||
enum State { POWERON, STATE_ON, STATE_OFF, STATE_STAY_ON };
|
enum State { POWERON, STATE_ON, STATE_OFF, STATE_STAY_ON };
|
||||||
static State WUPState = POWERON;
|
static State WUPState = POWERON;
|
||||||
|
|
||||||
#define LB_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Inverter
|
|
||||||
#define LB_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Inverter
|
|
||||||
|
|
||||||
CAN_frame_t BMW_000 = {.FIR = {.B =
|
|
||||||
{
|
|
||||||
.DLC = 4,
|
|
||||||
.FF = CAN_frame_std,
|
|
||||||
}},
|
|
||||||
.MsgID = 0x000,
|
|
||||||
.data = {0x10, 0x44, 0x00, 0x01}};
|
|
||||||
CAN_frame_t BMW_0A5 = {.FIR = {.B =
|
|
||||||
{
|
|
||||||
.DLC = 8,
|
|
||||||
.FF = CAN_frame_std,
|
|
||||||
}},
|
|
||||||
.MsgID = 0x0A5,
|
|
||||||
.data = {0x47, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF}};
|
|
||||||
CAN_frame_t BMW_0A8 = {.FIR = {.B =
|
|
||||||
{
|
|
||||||
.DLC = 8,
|
|
||||||
.FF = CAN_frame_std,
|
|
||||||
}},
|
|
||||||
.MsgID = 0x0A8,
|
|
||||||
.data = {0xD1, 0xF2, 0xFF, 0xBF, 0x5D, 0xE8, 0xD3, 0xC3}};
|
|
||||||
CAN_frame_t BMW_0AA = {.FIR = {.B =
|
|
||||||
{
|
|
||||||
.DLC = 8,
|
|
||||||
.FF = CAN_frame_std,
|
|
||||||
}},
|
|
||||||
.MsgID = 0x0AA,
|
|
||||||
.data = {0x00, 0xFC, 0x00, 0x7D, 0xC0, 0x5D, 0xD0, 0xF7}};
|
|
||||||
CAN_frame_t BMW_0AD = {.FIR = {.B =
|
|
||||||
{
|
|
||||||
.DLC = 8,
|
|
||||||
.FF = CAN_frame_std,
|
|
||||||
}},
|
|
||||||
.MsgID = 0x0AD,
|
|
||||||
.data = {0xFF, 0xFF, 0xFE, 0xE7, 0x7F, 0xFE, 0x7F, 0xFF}};
|
|
||||||
CAN_frame_t BMW_0BB = {.FIR = {.B =
|
|
||||||
{
|
|
||||||
.DLC = 3,
|
|
||||||
.FF = CAN_frame_std,
|
|
||||||
}},
|
|
||||||
.MsgID = 0x0BB,
|
|
||||||
.data = {0x7D, 0xFF, 0xFF}};
|
|
||||||
CAN_frame_t BMW_0CD = {.FIR = {.B =
|
|
||||||
{
|
|
||||||
.DLC = 8,
|
|
||||||
.FF = CAN_frame_std,
|
|
||||||
}},
|
|
||||||
.MsgID = 0x0CD,
|
|
||||||
.data = {0xFF, 0xFF, 0xD0, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF}};
|
|
||||||
CAN_frame_t BMW_100 = {.FIR = {.B =
|
|
||||||
{
|
|
||||||
.DLC = 8,
|
|
||||||
.FF = CAN_frame_std,
|
|
||||||
}},
|
|
||||||
.MsgID = 0x100,
|
|
||||||
.data = {0x9D, 0xF0, 0x7F, 0xC0, 0x5D, 0xA1, 0x87, 0x70}};
|
|
||||||
CAN_frame_t BMW_105 = {.FIR = {.B =
|
|
||||||
{
|
|
||||||
.DLC = 8,
|
|
||||||
.FF = CAN_frame_std,
|
|
||||||
}},
|
|
||||||
.MsgID = 0x105,
|
|
||||||
.data = {0x03, 0xF0, 0x7F, 0xE0, 0x2E, 0x00, 0xFC, 0x0F}};
|
|
||||||
CAN_frame_t BMW_108 = {.FIR = {.B =
|
|
||||||
{
|
|
||||||
.DLC = 8,
|
|
||||||
.FF = CAN_frame_std,
|
|
||||||
}},
|
|
||||||
.MsgID = 0x108,
|
|
||||||
.data = {0xDD, 0x7D, 0xFF, 0x2C, 0x48, 0xF3, 0xFF, 0xFF}};
|
|
||||||
CAN_frame_t BMW_10B = {.FIR = {.B =
|
CAN_frame_t BMW_10B = {.FIR = {.B =
|
||||||
{
|
{
|
||||||
.DLC = 3,
|
.DLC = 3,
|
||||||
|
@ -613,59 +539,47 @@ static uint16_t MaxDischargeVoltage = 0;
|
||||||
static uint16_t MaxChargeWattMaybe = 0;
|
static uint16_t MaxChargeWattMaybe = 0;
|
||||||
static uint16_t MaxDischargeWattMaybe = 0;
|
static uint16_t MaxDischargeWattMaybe = 0;
|
||||||
|
|
||||||
void update_values_i3_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
|
||||||
|
|
||||||
//Calculate the SOC% value to send to inverter
|
//Calculate the SOC% value to send to inverter
|
||||||
Calculated_SOC = (Display_SOC * 10); //Increase decimal amount
|
system_real_SOC_pptt = (Display_SOC * 100); //increase Display_SOC range from 0-100 -> 100.00
|
||||||
Calculated_SOC =
|
|
||||||
LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (Calculated_SOC - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
|
||||||
if (Calculated_SOC < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to inverter as 0%
|
|
||||||
Calculated_SOC = 0;
|
|
||||||
}
|
|
||||||
if (Calculated_SOC > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to inverter as 100%
|
|
||||||
Calculated_SOC = 1000;
|
|
||||||
}
|
|
||||||
SOC = (Calculated_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
|
|
||||||
|
|
||||||
battery_voltage = Battery_Volts; //Unit V+1 (5000 = 500.0V)
|
system_battery_voltage_dV = Battery_Volts; //Unit V+1 (5000 = 500.0V)
|
||||||
|
|
||||||
battery_current = Battery_Current;
|
system_battery_current_dA = Battery_Current;
|
||||||
|
|
||||||
capacity_Wh = BATTERY_WH_MAX;
|
system_capacity_Wh = BATTERY_WH_MAX;
|
||||||
|
|
||||||
remaining_capacity_Wh = (Battery_Capacity_kWh * 1000);
|
system_remaining_capacity_Wh = (Battery_Capacity_kWh * 1000);
|
||||||
|
|
||||||
if (SOC > 9900) //If Soc is over 99%, stop charging
|
if (system_scaled_SOC_pptt > 9900) //If Soc is over 99%, stop charging
|
||||||
{
|
{
|
||||||
max_target_charge_power = 0;
|
system_max_charge_power_W = 0;
|
||||||
} else {
|
} else {
|
||||||
max_target_charge_power = 5000; //Hardcoded value for testing. TODO: read real value from battery when discovered
|
system_max_charge_power_W = 5000; //Hardcoded value for testing. TODO: read real value from battery when discovered
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SOC < 500) //If Soc is under 5%, stop dicharging
|
if (system_scaled_SOC_pptt < 500) //If Soc is under 5%, stop dicharging
|
||||||
{
|
{
|
||||||
max_target_discharge_power = 0;
|
system_max_discharge_power_W = 0;
|
||||||
} else {
|
} else {
|
||||||
max_target_discharge_power =
|
system_max_discharge_power_W =
|
||||||
5000; //Hardcoded value for testing. TODO: read real value from battery when discovered
|
5000; //Hardcoded value for testing. TODO: read real value from battery when discovered
|
||||||
}
|
}
|
||||||
|
|
||||||
Battery_Power = (Battery_Current * (Battery_Volts / 10));
|
Battery_Power = (Battery_Current * (Battery_Volts / 10));
|
||||||
|
|
||||||
stat_batt_power = Battery_Power; //TODO:, is mapping OK?
|
system_active_power_W = Battery_Power; //TODO:, is mapping OK?
|
||||||
|
|
||||||
temperature_min; //hardcoded to 5*C in startup, TODO:, find from battery CAN later
|
system_temperature_min_dC; //hardcoded to 5*C in startup, TODO:, find from battery CAN later
|
||||||
|
|
||||||
temperature_max; //hardcoded to 6*C in startup, TODO:, find from battery CAN later
|
system_temperature_max_dC; //hardcoded to 6*C in startup, TODO:, find from battery CAN later
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if (!CANstillAlive) {
|
if (!CANstillAlive) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_CAN_RX_FAILURE, 0);
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
|
||||||
set_event(EVENT_CAN_FAILURE, 0);
|
|
||||||
} else {
|
} else {
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
|
clear_event(EVENT_CAN_RX_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
|
@ -694,16 +608,19 @@ void update_values_i3_battery() { //This function maps all the values fetched v
|
||||||
Serial.println(" ");
|
Serial.println(" ");
|
||||||
Serial.print("Values sent to inverter: ");
|
Serial.print("Values sent to inverter: ");
|
||||||
Serial.print("SOC%: ");
|
Serial.print("SOC%: ");
|
||||||
Serial.print(SOC);
|
Serial.print(system_scaled_SOC_pptt);
|
||||||
|
Serial.print(" Battery voltage: ");
|
||||||
|
Serial.print(system_battery_voltage_dV);
|
||||||
|
Serial.print(" Remaining Wh: ");
|
||||||
|
Serial.print(system_remaining_capacity_Wh);
|
||||||
Serial.print(" Max charge power: ");
|
Serial.print(" Max charge power: ");
|
||||||
Serial.print(max_target_charge_power);
|
Serial.print(system_max_charge_power_W);
|
||||||
Serial.print(" Max discharge power: ");
|
Serial.print(" Max discharge power: ");
|
||||||
Serial.print(max_target_discharge_power);
|
Serial.print(system_max_discharge_power_W);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_i3_battery(CAN_frame_t rx_frame) {
|
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
|
|
||||||
switch (rx_frame.MsgID) {
|
switch (rx_frame.MsgID) {
|
||||||
case 0x112: //BMS status [10ms]
|
case 0x112: //BMS status [10ms]
|
||||||
CANstillAlive = 12; //This message is only sent if 30C signal is active
|
CANstillAlive = 12; //This message is only sent if 30C signal is active
|
||||||
|
@ -759,7 +676,7 @@ void receive_can_i3_battery(CAN_frame_t rx_frame) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_i3_battery() {
|
void send_can_battery() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
|
|
||||||
//Handle WUP signal
|
//Handle WUP signal
|
||||||
|
@ -1072,3 +989,12 @@ void send_can_i3_battery() {
|
||||||
ESP32Can.CANWriteFrame(&BMW_3E5);
|
ESP32Can.CANWriteFrame(&BMW_3E5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
|
Serial.println("BMW i3 battery selected");
|
||||||
|
|
||||||
|
system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
|
||||||
|
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -5,30 +5,30 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE \
|
#define BATTERY_SELECTED
|
||||||
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
|
||||||
|
|
||||||
// These parameters need to be mapped for the inverter
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC;
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth;
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage;
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current;
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh;
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh;
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power;
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power;
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status;
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status;
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power;
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min;
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max;
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t CANerror;
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
void update_values_i3_battery();
|
void setup_battery(void);
|
||||||
void receive_can_i3_battery(CAN_frame_t rx_frame);
|
|
||||||
void send_can_i3_battery();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#include "CHADEMO-BATTERY.h"
|
#include "BATTERIES.h"
|
||||||
|
#ifdef CHADEMO_BATTERY
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
#include "CHADEMO-BATTERY.h"
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
||||||
|
@ -89,28 +91,26 @@ uint8_t DynamicControlStatus = 0;
|
||||||
uint8_t HighCurrentControlStatus = 0;
|
uint8_t HighCurrentControlStatus = 0;
|
||||||
uint8_t HighVoltageControlStatus = 0;
|
uint8_t HighVoltageControlStatus = 0;
|
||||||
|
|
||||||
void update_values_chademo_battery() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
|
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
|
||||||
|
|
||||||
SOC = ChargingRate;
|
system_real_SOC_pptt = ChargingRate;
|
||||||
|
|
||||||
max_target_discharge_power = (MaximumDischargeCurrent * MaximumBatteryVoltage); //In Watts, Convert A to P
|
system_max_discharge_power_W = (MaximumDischargeCurrent * MaximumBatteryVoltage); //In Watts, Convert A to P
|
||||||
|
|
||||||
battery_voltage = TargetBatteryVoltage; //TODO: scaling?
|
system_battery_voltage_dV = TargetBatteryVoltage; //TODO: scaling?
|
||||||
|
|
||||||
capacity_Wh = ((RatedBatteryCapacity / 0.11) *
|
system_capacity_Wh = ((RatedBatteryCapacity / 0.11) *
|
||||||
1000); //(Added in CHAdeMO v1.0.1), maybe handle hardcoded on lower protocol version?
|
1000); //(Added in CHAdeMO v1.0.1), maybe handle hardcoded on lower protocol version?
|
||||||
|
|
||||||
remaining_capacity_Wh = (SOC / 100) * capacity_Wh;
|
system_remaining_capacity_Wh = (system_real_SOC_pptt / 100) * system_capacity_Wh;
|
||||||
|
|
||||||
/* Check if the Vehicle is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the Vehicle is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if (!CANstillAlive) {
|
if (!CANstillAlive) {
|
||||||
bms_status = FAULT;
|
|
||||||
errorCode = 7;
|
errorCode = 7;
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
set_event(EVENT_CAN_RX_FAILURE, 0);
|
||||||
set_event(EVENT_CAN_FAILURE, 0);
|
|
||||||
} else {
|
} else {
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
|
clear_event(EVENT_CAN_RX_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
|
@ -119,36 +119,23 @@ void update_values_chademo_battery() { //This function maps all the values fetc
|
||||||
Serial.println(errorCode);
|
Serial.println(errorCode);
|
||||||
}
|
}
|
||||||
Serial.print("BMS Status (3=OK): ");
|
Serial.print("BMS Status (3=OK): ");
|
||||||
Serial.println(bms_status);
|
Serial.println(system_bms_status);
|
||||||
switch (bms_char_dis_status) {
|
|
||||||
case 0:
|
|
||||||
Serial.println("Battery Idle");
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
Serial.println("Battery Discharging");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
Serial.println("Battery Charging");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Serial.print("Max discharge power: ");
|
Serial.print("Max discharge power: ");
|
||||||
Serial.println(max_target_discharge_power);
|
Serial.println(system_max_discharge_power_W);
|
||||||
Serial.print("Max charge power: ");
|
Serial.print("Max charge power: ");
|
||||||
Serial.println(max_target_charge_power);
|
Serial.println(system_max_charge_power_W);
|
||||||
Serial.print("SOH%: ");
|
Serial.print("SOH%: ");
|
||||||
Serial.println(StateOfHealth);
|
Serial.println(system_SOH_pptt);
|
||||||
Serial.print("SOC% to Inverter: ");
|
Serial.print("SOC% to Inverter: ");
|
||||||
Serial.println(SOC);
|
Serial.println(system_scaled_SOC_pptt);
|
||||||
Serial.print("Temperature Min: ");
|
Serial.print("Temperature Min: ");
|
||||||
Serial.println(temperature_min);
|
Serial.println(system_temperature_min_dC);
|
||||||
Serial.print("Temperature Max: ");
|
Serial.print("Temperature Max: ");
|
||||||
Serial.println(temperature_max);
|
Serial.println(system_temperature_max_dC);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_chademo_battery(CAN_frame_t rx_frame) {
|
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
CANstillAlive == 12; //We are getting CAN messages from the vehicle, inform the watchdog
|
CANstillAlive == 12; //We are getting CAN messages from the vehicle, inform the watchdog
|
||||||
|
|
||||||
switch (rx_frame.MsgID) {
|
switch (rx_frame.MsgID) {
|
||||||
|
@ -204,7 +191,7 @@ void receive_can_chademo_battery(CAN_frame_t rx_frame) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_chademo_battery() {
|
void send_can_battery() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100) {
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
|
@ -220,3 +207,11 @@ void send_can_chademo_battery() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
|
Serial.println("Chademo battery selected");
|
||||||
|
|
||||||
|
system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
|
||||||
|
system_min_design_voltage_dV = 2000; // 200.0V under this, discharging further is disabled
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -5,30 +5,30 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE \
|
#define BATTERY_SELECTED
|
||||||
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
|
||||||
|
|
||||||
// These parameters need to be mapped
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC;
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth;
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage;
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current;
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh;
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh;
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power;
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power;
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status;
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status;
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power;
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min;
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max;
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t CANerror;
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
void update_values_chademo_battery();
|
void setup_battery(void);
|
||||||
void receive_can_chademo_battery(CAN_frame_t rx_frame);
|
|
||||||
void send_can_chademo_battery();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
#include "IMIEV-CZERO-ION-BATTERY.h"
|
#include "BATTERIES.h"
|
||||||
|
#ifdef IMIEV_CZERO_ION_BATTERY
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
#include "IMIEV-CZERO-ION-BATTERY.h"
|
||||||
|
|
||||||
//Code still work in progress, TODO:
|
//Code still work in progress, TODO:
|
||||||
//Figure out if CAN messages need to be sent to keep the system happy?
|
//Figure out if CAN messages need to be sent to keep the system happy?
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
#define BMU_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to inverter
|
|
||||||
#define BMU_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to inverter
|
|
||||||
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
||||||
static uint8_t errorCode = 0; //stores if we have an error code active from battery control logic
|
static uint8_t errorCode = 0; //stores if we have an error code active from battery control logic
|
||||||
static uint8_t BMU_Detected = 0;
|
static uint8_t BMU_Detected = 0;
|
||||||
|
@ -40,33 +40,31 @@ static double min_volt_cel = 3.70;
|
||||||
static double max_temp_cel = 20.00;
|
static double max_temp_cel = 20.00;
|
||||||
static double min_temp_cel = 19.00;
|
static double min_temp_cel = 19.00;
|
||||||
|
|
||||||
void update_values_imiev_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
system_real_SOC_pptt = (uint16_t)(BMU_SOC * 100); //increase BMU_SOC range from 0-100 -> 100.00
|
||||||
|
|
||||||
SOC = (uint16_t)(BMU_SOC * 100); //increase BMU_SOC range from 0-100 -> 100.00
|
system_battery_voltage_dV = (uint16_t)(BMU_PackVoltage * 10); // Multiply by 10 and cast to uint16_t
|
||||||
|
|
||||||
battery_voltage = (uint16_t)(BMU_PackVoltage * 10); // Multiply by 10 and cast to uint16_t
|
system_battery_current_dA = (BMU_Current * 10); //Todo, scaling?
|
||||||
|
|
||||||
battery_current = (BMU_Current * 10); //Todo, scaling?
|
system_capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value
|
||||||
|
|
||||||
capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value
|
system_remaining_capacity_Wh = (uint16_t)((system_real_SOC_pptt / 10000) * system_capacity_Wh);
|
||||||
|
|
||||||
remaining_capacity_Wh = (uint16_t)((SOC / 10000) * capacity_Wh);
|
|
||||||
|
|
||||||
//We do not know if the max charge power is sent by the battery. So we estimate the value based on SOC%
|
//We do not know if the max charge power is sent by the battery. So we estimate the value based on SOC%
|
||||||
if (SOC == 10000) { //100.00%
|
if (system_scaled_SOC_pptt == 10000) { //100.00%
|
||||||
max_target_charge_power = 0; //When battery is 100% full, set allowed charge W to 0
|
system_max_charge_power_W = 0; //When battery is 100% full, set allowed charge W to 0
|
||||||
} else {
|
} else {
|
||||||
max_target_charge_power = 10000; //Otherwise we can push 10kW into the pack!
|
system_max_charge_power_W = 10000; //Otherwise we can push 10kW into the pack!
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SOC < 200) { //2.00%
|
if (system_scaled_SOC_pptt < 200) { //2.00%
|
||||||
max_target_discharge_power = 0; //When battery is empty (below 2%), set allowed discharge W to 0
|
system_max_discharge_power_W = 0; //When battery is empty (below 2%), set allowed discharge W to 0
|
||||||
} else {
|
} else {
|
||||||
max_target_discharge_power = 10000; //Otherwise we can discharge 10kW from the pack!
|
system_max_discharge_power_W = 10000; //Otherwise we can discharge 10kW from the pack!
|
||||||
}
|
}
|
||||||
|
|
||||||
stat_batt_power = BMU_Power; //TODO: Scaling?
|
system_active_power_W = BMU_Power; //TODO: Scaling?
|
||||||
|
|
||||||
static int n = sizeof(cell_voltages) / sizeof(cell_voltages[0]);
|
static int n = sizeof(cell_voltages) / sizeof(cell_voltages[0]);
|
||||||
max_volt_cel = cell_voltages[0]; // Initialize max with the first element of the array
|
max_volt_cel = cell_voltages[0]; // Initialize max with the first element of the array
|
||||||
|
@ -98,21 +96,20 @@ void update_values_imiev_battery() { //This function maps all the values fetche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cell_max_voltage = (uint16_t)(max_volt_cel * 1000);
|
system_cell_max_voltage_mV = (uint16_t)(max_volt_cel * 1000);
|
||||||
|
|
||||||
cell_min_voltage = (uint16_t)(min_volt_cel * 1000);
|
system_cell_min_voltage_mV = (uint16_t)(min_volt_cel * 1000);
|
||||||
|
|
||||||
temperature_min = (uint16_t)(min_temp_cel * 1000);
|
system_temperature_min_dC = (int16_t)(min_temp_cel * 1000);
|
||||||
|
|
||||||
temperature_max = (uint16_t)(max_temp_cel * 1000);
|
system_temperature_min_dC = (int16_t)(max_temp_cel * 1000);
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if (!CANstillAlive) {
|
if (!CANstillAlive) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_CAN_RX_FAILURE, 0);
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
|
||||||
set_event(EVENT_CAN_FAILURE, 0);
|
|
||||||
} else {
|
} else {
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
|
clear_event(EVENT_CAN_RX_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!BMU_Detected) {
|
if (!BMU_Detected) {
|
||||||
|
@ -141,30 +138,30 @@ void update_values_imiev_battery() { //This function maps all the values fetche
|
||||||
|
|
||||||
Serial.println("Values sent to inverter");
|
Serial.println("Values sent to inverter");
|
||||||
Serial.print("SOC% (0-100.00): ");
|
Serial.print("SOC% (0-100.00): ");
|
||||||
Serial.print(SOC);
|
Serial.print(system_scaled_SOC_pptt);
|
||||||
Serial.print(" Voltage (0-400.0): ");
|
Serial.print(" Voltage (0-400.0): ");
|
||||||
Serial.print(battery_voltage);
|
Serial.print(system_battery_voltage_dV);
|
||||||
Serial.print(" Capacity WH full (0-60000): ");
|
Serial.print(" Capacity WH full (0-60000): ");
|
||||||
Serial.print(capacity_Wh);
|
Serial.print(system_capacity_Wh);
|
||||||
Serial.print(" Capacity WH remain (0-60000): ");
|
Serial.print(" Capacity WH remain (0-60000): ");
|
||||||
Serial.print(remaining_capacity_Wh);
|
Serial.print(system_remaining_capacity_Wh);
|
||||||
Serial.print(" Max charge power W (0-10000): ");
|
Serial.print(" Max charge power W (0-10000): ");
|
||||||
Serial.print(max_target_charge_power);
|
Serial.print(system_max_charge_power_W);
|
||||||
Serial.print(" Max discharge power W (0-10000): ");
|
Serial.print(" Max discharge power W (0-10000): ");
|
||||||
Serial.print(max_target_discharge_power);
|
Serial.print(system_max_discharge_power_W);
|
||||||
Serial.print(" Temp max ");
|
Serial.print(" Temp max ");
|
||||||
Serial.print(temperature_max);
|
Serial.print(system_temperature_max_dC);
|
||||||
Serial.print(" Temp min ");
|
Serial.print(" Temp min ");
|
||||||
Serial.print(temperature_min);
|
Serial.print(system_temperature_min_dC);
|
||||||
Serial.print(" Cell mV max ");
|
Serial.print(" Cell mV max ");
|
||||||
Serial.print(cell_max_voltage);
|
Serial.print(system_cell_max_voltage_mV);
|
||||||
Serial.print(" Cell mV min ");
|
Serial.print(" Cell mV min ");
|
||||||
Serial.print(cell_min_voltage);
|
Serial.print(system_cell_min_voltage_mV);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_imiev_battery(CAN_frame_t rx_frame) {
|
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
CANstillAlive =
|
CANstillAlive =
|
||||||
12; //TODO: move this inside a known message ID to prevent CAN inverter from keeping battery alive detection going
|
12; //TODO: move this inside a known message ID to prevent CAN inverter from keeping battery alive detection going
|
||||||
switch (rx_frame.MsgID) {
|
switch (rx_frame.MsgID) {
|
||||||
|
@ -220,10 +217,19 @@ void receive_can_imiev_battery(CAN_frame_t rx_frame) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_can_imiev_battery() {
|
void send_can_battery() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100) {
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
previousMillis100 = currentMillis;
|
previousMillis100 = currentMillis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
|
Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected");
|
||||||
|
|
||||||
|
system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
|
||||||
|
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -5,33 +5,31 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE \
|
#define BATTERY_SELECTED
|
||||||
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
|
||||||
|
|
||||||
// These parameters need to be mapped for the Gen24
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC;
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth;
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage;
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current;
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh;
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh;
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power;
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power;
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status;
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status;
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power;
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min;
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max;
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t CANerror;
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_max_voltage;
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t cell_min_voltage;
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
||||||
extern uint8_t LEDcolor;
|
|
||||||
|
|
||||||
void update_values_imiev_battery();
|
void setup_battery(void);
|
||||||
void receive_can_imiev_battery(CAN_frame_t rx_frame);
|
|
||||||
void send_can_imiev_battery();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#include "KIA-HYUNDAI-64-BATTERY.h"
|
#include "BATTERIES.h"
|
||||||
|
#ifdef KIA_HYUNDAI_64_BATTERY
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
#include "KIA-HYUNDAI-64-BATTERY.h"
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
||||||
|
@ -10,8 +12,6 @@ static const int interval100 = 100; // interval (ms) at which send CAN
|
||||||
static const int interval10ms = 10; // interval (ms) at which send CAN Messages
|
static const int interval10ms = 10; // interval (ms) at which send CAN Messages
|
||||||
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
||||||
|
|
||||||
#define MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Inverter
|
|
||||||
#define MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Inverter
|
|
||||||
#define MAX_CELL_VOLTAGE 4250 //Battery is put into emergency stop if one cell goes over this value
|
#define MAX_CELL_VOLTAGE 4250 //Battery is put into emergency stop if one cell goes over this value
|
||||||
#define MIN_CELL_VOLTAGE 2950 //Battery is put into emergency stop if one cell goes below this value
|
#define MIN_CELL_VOLTAGE 2950 //Battery is put into emergency stop if one cell goes below this value
|
||||||
#define MAX_CELL_DEVIATION 150 //LED turns yellow on the board if mv delta exceeds this value
|
#define MAX_CELL_DEVIATION 150 //LED turns yellow on the board if mv delta exceeds this value
|
||||||
|
@ -146,100 +146,80 @@ CAN_frame_t KIA64_7E4_ack = {
|
||||||
.MsgID = 0x7E4,
|
.MsgID = 0x7E4,
|
||||||
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned
|
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned
|
||||||
|
|
||||||
void update_values_kiaHyundai_64_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
|
|
||||||
//Calculate the SOC% value to send to inverter
|
system_real_SOC_pptt = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
|
||||||
soc_calculated = SOC_Display;
|
|
||||||
soc_calculated = MIN_SOC + (MAX_SOC - MIN_SOC) * (soc_calculated - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
|
||||||
if (soc_calculated < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Inverter as 0%
|
|
||||||
soc_calculated = 0;
|
|
||||||
}
|
|
||||||
if (soc_calculated > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Inverter as 100%
|
|
||||||
soc_calculated = 1000;
|
|
||||||
}
|
|
||||||
SOC = (soc_calculated * 10); //increase SOC range from 0-100.0 -> 100.00
|
|
||||||
|
|
||||||
StateOfHealth = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
|
system_SOH_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
|
||||||
|
|
||||||
battery_voltage = batteryVoltage; //value is *10 (3700 = 370.0)
|
system_battery_voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0)
|
||||||
|
|
||||||
battery_current = convertToUnsignedInt16(batteryAmps); //value is *10 (150 = 15.0)
|
system_battery_current_dA = batteryAmps; //value is *10 (150 = 15.0)
|
||||||
|
|
||||||
capacity_Wh = BATTERY_WH_MAX;
|
system_capacity_Wh = BATTERY_WH_MAX;
|
||||||
|
|
||||||
remaining_capacity_Wh = static_cast<int>((static_cast<double>(SOC) / 10000) * BATTERY_WH_MAX);
|
system_remaining_capacity_Wh = static_cast<int>((static_cast<double>(system_real_SOC_pptt) / 10000) * BATTERY_WH_MAX);
|
||||||
|
|
||||||
//max_target_charge_power = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts
|
//system_max_charge_power_W = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts
|
||||||
//The allowed charge power is not available. We estimate this value
|
//The allowed charge power is not available. We estimate this value
|
||||||
if (SOC == 10000) { // When scaled SOC is 100%, set allowed charge power to 0
|
if (system_scaled_SOC_pptt == 10000) { // When scaled SOC is 100%, set allowed charge power to 0
|
||||||
max_target_charge_power = 0;
|
system_max_charge_power_W = 0;
|
||||||
} else { // No limits, max charging power allowed
|
} else { // No limits, max charging power allowed
|
||||||
max_target_charge_power = MAXCHARGEPOWERALLOWED;
|
system_max_charge_power_W = MAXCHARGEPOWERALLOWED;
|
||||||
}
|
}
|
||||||
//max_target_discharge_power = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts
|
//system_max_discharge_power_W = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts
|
||||||
if (SOC < 100) { // When scaled SOC is <1%, set allowed charge power to 0
|
if (system_scaled_SOC_pptt < 100) { // When scaled SOC is <1%, set allowed charge power to 0
|
||||||
max_target_discharge_power = 0;
|
system_max_discharge_power_W = 0;
|
||||||
} else { // No limits, max charging power allowed
|
} else { // No limits, max charging power allowed
|
||||||
max_target_discharge_power = MAXDISCHARGEPOWERALLOWED;
|
system_max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
powerWatt = ((batteryVoltage * batteryAmps) / 100);
|
powerWatt = ((batteryVoltage * batteryAmps) / 100);
|
||||||
|
|
||||||
stat_batt_power = convertToUnsignedInt16(powerWatt); //Power in watts, Negative = charging batt
|
system_active_power_W = powerWatt; //Power in watts, Negative = charging batt
|
||||||
|
|
||||||
temperature_min = convertToUnsignedInt16((int8_t)temperatureMin * 10); //Increase decimals, 17C -> 17.0C
|
system_temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C
|
||||||
|
|
||||||
temperature_max = convertToUnsignedInt16((int8_t)temperatureMax * 10); //Increase decimals, 18C -> 18.0C
|
system_temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C
|
||||||
|
|
||||||
cell_max_voltage = CellVoltMax_mV;
|
system_cell_max_voltage_mV = CellVoltMax_mV;
|
||||||
|
|
||||||
cell_min_voltage = CellVoltMin_mV;
|
system_cell_min_voltage_mV = CellVoltMin_mV;
|
||||||
|
|
||||||
bms_status = ACTIVE; //Startout in active mode. Then check safeties
|
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if (!CANstillAlive) {
|
if (!CANstillAlive) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_CAN_RX_FAILURE, 0);
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
|
||||||
set_event(EVENT_CAN_FAILURE, 0);
|
|
||||||
} else {
|
} else {
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
|
clear_event(EVENT_CAN_RX_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waterleakageSensor == 0) {
|
if (waterleakageSensor == 0) {
|
||||||
Serial.println("Water leakage inside battery detected. Operation halted. Inspect battery!");
|
|
||||||
bms_status = FAULT;
|
|
||||||
set_event(EVENT_WATER_INGRESS, 0);
|
set_event(EVENT_WATER_INGRESS, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leadAcidBatteryVoltage < 110) {
|
if (leadAcidBatteryVoltage < 110) {
|
||||||
Serial.println("12V battery source below required voltage to safely close contactors. Inspect the supply/battery!");
|
|
||||||
LEDcolor = YELLOW;
|
|
||||||
set_event(EVENT_12V_LOW, leadAcidBatteryVoltage);
|
set_event(EVENT_12V_LOW, leadAcidBatteryVoltage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if cell voltages are within allowed range
|
// Check if cell voltages are within allowed range
|
||||||
cell_deviation_mV = (cell_max_voltage - cell_min_voltage);
|
cell_deviation_mV = (system_cell_max_voltage_mV - system_cell_min_voltage_mV);
|
||||||
|
|
||||||
if (cell_max_voltage >= MAX_CELL_VOLTAGE) {
|
if (CellVoltMax_mV >= MAX_CELL_VOLTAGE) {
|
||||||
bms_status = FAULT;
|
|
||||||
Serial.println("ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
||||||
}
|
}
|
||||||
if (cell_min_voltage <= MIN_CELL_VOLTAGE) {
|
if (CellVoltMin_mV <= MIN_CELL_VOLTAGE) {
|
||||||
bms_status = FAULT;
|
|
||||||
Serial.println("ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
||||||
}
|
}
|
||||||
if (cell_deviation_mV > MAX_CELL_DEVIATION) {
|
if (cell_deviation_mV > MAX_CELL_DEVIATION) {
|
||||||
LEDcolor = YELLOW;
|
|
||||||
Serial.println("ERROR: HIGH CELL DEVIATION!!! Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
|
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
|
||||||
|
} else {
|
||||||
|
clear_event(EVENT_CELL_DEVIATION_HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
|
if (system_bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
|
||||||
max_target_charge_power = 0;
|
system_max_charge_power_W = 0;
|
||||||
max_target_discharge_power = 0;
|
system_max_discharge_power_W = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Safeties verified. Perform USB serial printout if configured to do so */
|
/* Safeties verified. Perform USB serial printout if configured to do so */
|
||||||
|
@ -258,7 +238,7 @@ void update_values_kiaHyundai_64_battery() { //This function maps all the value
|
||||||
Serial.print(" Amps | ");
|
Serial.print(" Amps | ");
|
||||||
Serial.print((uint16_t)batteryVoltage / 10.0, 1);
|
Serial.print((uint16_t)batteryVoltage / 10.0, 1);
|
||||||
Serial.print(" Volts | ");
|
Serial.print(" Volts | ");
|
||||||
Serial.print((int16_t)stat_batt_power);
|
Serial.print((int16_t)system_active_power_W);
|
||||||
Serial.println(" Watts");
|
Serial.println(" Watts");
|
||||||
Serial.print("Allowed Charge ");
|
Serial.print("Allowed Charge ");
|
||||||
Serial.print((uint16_t)allowedChargePower * 10);
|
Serial.print((uint16_t)allowedChargePower * 10);
|
||||||
|
@ -304,7 +284,7 @@ void update_values_kiaHyundai_64_battery() { //This function maps all the value
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_kiaHyundai_64_battery(CAN_frame_t rx_frame) {
|
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
switch (rx_frame.MsgID) {
|
switch (rx_frame.MsgID) {
|
||||||
case 0x4DE:
|
case 0x4DE:
|
||||||
break;
|
break;
|
||||||
|
@ -373,53 +353,53 @@ void receive_can_kiaHyundai_64_battery(CAN_frame_t rx_frame) {
|
||||||
allowedDischargePower = ((rx_frame.data.u8[5] << 8) + rx_frame.data.u8[6]);
|
allowedDischargePower = ((rx_frame.data.u8[5] << 8) + rx_frame.data.u8[6]);
|
||||||
batteryRelay = rx_frame.data.u8[7];
|
batteryRelay = rx_frame.data.u8[7];
|
||||||
} else if (poll_data_pid == 2) {
|
} else if (poll_data_pid == 2) {
|
||||||
cellvoltages[0] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[0] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[1] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[1] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[2] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[2] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[3] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[3] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[4] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[4] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[5] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[5] = (rx_frame.data.u8[7] * 20);
|
||||||
} else if (poll_data_pid == 3) {
|
} else if (poll_data_pid == 3) {
|
||||||
cellvoltages[32] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[32] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[33] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[33] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[34] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[34] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[35] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[35] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[36] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[36] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[37] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[37] = (rx_frame.data.u8[7] * 20);
|
||||||
} else if (poll_data_pid == 4) {
|
} else if (poll_data_pid == 4) {
|
||||||
cellvoltages[64] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[64] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[65] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[65] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[66] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[66] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[67] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[67] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[68] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[68] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[69] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[69] = (rx_frame.data.u8[7] * 20);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x22: //Second datarow in PID group
|
case 0x22: //Second datarow in PID group
|
||||||
if (poll_data_pid == 2) {
|
if (poll_data_pid == 2) {
|
||||||
cellvoltages[6] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[6] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[7] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[7] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[8] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[8] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[9] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[9] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[10] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[10] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[11] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[11] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[12] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[12] = (rx_frame.data.u8[7] * 20);
|
||||||
} else if (poll_data_pid == 3) {
|
} else if (poll_data_pid == 3) {
|
||||||
cellvoltages[38] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[38] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[39] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[39] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[40] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[40] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[41] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[41] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[42] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[42] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[43] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[43] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[44] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[44] = (rx_frame.data.u8[7] * 20);
|
||||||
} else if (poll_data_pid == 4) {
|
} else if (poll_data_pid == 4) {
|
||||||
cellvoltages[70] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[70] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[71] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[71] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[72] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[72] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[73] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[73] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[74] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[74] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[75] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[75] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[76] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[76] = (rx_frame.data.u8[7] * 20);
|
||||||
} else if (poll_data_pid == 6) {
|
} else if (poll_data_pid == 6) {
|
||||||
batteryManagementMode = rx_frame.data.u8[5];
|
batteryManagementMode = rx_frame.data.u8[5];
|
||||||
}
|
}
|
||||||
|
@ -429,29 +409,29 @@ void receive_can_kiaHyundai_64_battery(CAN_frame_t rx_frame) {
|
||||||
temperature_water_inlet = rx_frame.data.u8[6];
|
temperature_water_inlet = rx_frame.data.u8[6];
|
||||||
CellVoltMax_mV = (rx_frame.data.u8[7] * 20); //(volts *50) *20 =mV
|
CellVoltMax_mV = (rx_frame.data.u8[7] * 20); //(volts *50) *20 =mV
|
||||||
} else if (poll_data_pid == 2) {
|
} else if (poll_data_pid == 2) {
|
||||||
cellvoltages[13] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[13] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[14] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[14] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[15] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[15] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[16] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[16] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[17] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[17] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[18] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[18] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[19] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[19] = (rx_frame.data.u8[7] * 20);
|
||||||
} else if (poll_data_pid == 3) {
|
} else if (poll_data_pid == 3) {
|
||||||
cellvoltages[45] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[45] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[46] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[46] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[47] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[47] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[48] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[48] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[49] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[49] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[50] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[50] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[51] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[51] = (rx_frame.data.u8[7] * 20);
|
||||||
} else if (poll_data_pid == 4) {
|
} else if (poll_data_pid == 4) {
|
||||||
cellvoltages[77] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[77] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[78] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[78] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[79] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[79] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[80] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[80] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[81] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[81] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[82] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[82] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[83] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[83] = (rx_frame.data.u8[7] * 20);
|
||||||
} else if (poll_data_pid == 5) {
|
} else if (poll_data_pid == 5) {
|
||||||
heatertemp = rx_frame.data.u8[7];
|
heatertemp = rx_frame.data.u8[7];
|
||||||
}
|
}
|
||||||
|
@ -462,56 +442,56 @@ void receive_can_kiaHyundai_64_battery(CAN_frame_t rx_frame) {
|
||||||
CellVminNo = rx_frame.data.u8[3];
|
CellVminNo = rx_frame.data.u8[3];
|
||||||
CellVoltMin_mV = (rx_frame.data.u8[2] * 20); //(volts *50) *20 =mV
|
CellVoltMin_mV = (rx_frame.data.u8[2] * 20); //(volts *50) *20 =mV
|
||||||
} else if (poll_data_pid == 2) {
|
} else if (poll_data_pid == 2) {
|
||||||
cellvoltages[20] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[20] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[21] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[21] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[22] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[22] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[23] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[23] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[24] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[24] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[25] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[25] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[26] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[26] = (rx_frame.data.u8[7] * 20);
|
||||||
} else if (poll_data_pid == 3) {
|
} else if (poll_data_pid == 3) {
|
||||||
cellvoltages[52] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[52] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[53] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[53] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[54] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[54] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[55] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[55] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[56] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[56] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[57] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[57] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[58] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[58] = (rx_frame.data.u8[7] * 20);
|
||||||
} else if (poll_data_pid == 4) {
|
} else if (poll_data_pid == 4) {
|
||||||
cellvoltages[84] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[84] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[85] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[85] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[86] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[86] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[87] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[87] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[88] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[88] = (rx_frame.data.u8[5] * 20);
|
||||||
cellvoltages[89] = (rx_frame.data.u8[6] * 20);
|
system_cellvoltages_mV[89] = (rx_frame.data.u8[6] * 20);
|
||||||
cellvoltages[90] = (rx_frame.data.u8[7] * 20);
|
system_cellvoltages_mV[90] = (rx_frame.data.u8[7] * 20);
|
||||||
} else if (poll_data_pid == 5) {
|
} else if (poll_data_pid == 5) {
|
||||||
batterySOH = ((rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3]);
|
batterySOH = ((rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x25: //Fifth datarow in PID group
|
case 0x25: //Fifth datarow in PID group
|
||||||
if (poll_data_pid == 2) {
|
if (poll_data_pid == 2) {
|
||||||
cellvoltages[27] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[27] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[28] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[28] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[29] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[29] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[30] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[30] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[31] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[31] = (rx_frame.data.u8[5] * 20);
|
||||||
} else if (poll_data_pid == 3) {
|
} else if (poll_data_pid == 3) {
|
||||||
cellvoltages[59] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[59] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[60] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[60] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[61] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[61] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[62] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[62] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[63] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[63] = (rx_frame.data.u8[5] * 20);
|
||||||
} else if (poll_data_pid == 4) {
|
} else if (poll_data_pid == 4) {
|
||||||
cellvoltages[91] = (rx_frame.data.u8[1] * 20);
|
system_cellvoltages_mV[91] = (rx_frame.data.u8[1] * 20);
|
||||||
cellvoltages[92] = (rx_frame.data.u8[2] * 20);
|
system_cellvoltages_mV[92] = (rx_frame.data.u8[2] * 20);
|
||||||
cellvoltages[93] = (rx_frame.data.u8[3] * 20);
|
system_cellvoltages_mV[93] = (rx_frame.data.u8[3] * 20);
|
||||||
cellvoltages[94] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[94] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[95] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[95] = (rx_frame.data.u8[5] * 20);
|
||||||
} else if (poll_data_pid == 5) {
|
} else if (poll_data_pid == 5) {
|
||||||
cellvoltages[96] = (rx_frame.data.u8[4] * 20);
|
system_cellvoltages_mV[96] = (rx_frame.data.u8[4] * 20);
|
||||||
cellvoltages[97] = (rx_frame.data.u8[5] * 20);
|
system_cellvoltages_mV[97] = (rx_frame.data.u8[5] * 20);
|
||||||
nof_cellvoltages = 98;
|
system_number_of_cells = 98;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x26: //Sixth datarow in PID group
|
case 0x26: //Sixth datarow in PID group
|
||||||
|
@ -534,7 +514,7 @@ void receive_can_kiaHyundai_64_battery(CAN_frame_t rx_frame) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_can_kiaHyundai_64_battery() {
|
void send_can_battery() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
//Send 100ms message
|
//Send 100ms message
|
||||||
if (currentMillis - previousMillis100 >= interval100) {
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
|
@ -600,10 +580,11 @@ void send_can_kiaHyundai_64_battery() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t convertToUnsignedInt16(int16_t signed_value) {
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
if (signed_value < 0) {
|
Serial.println("Kia Niro / Hyundai Kona 64kWh battery selected");
|
||||||
return (65535 + signed_value);
|
|
||||||
} else {
|
system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
|
||||||
return (uint16_t)signed_value;
|
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -5,37 +5,34 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE \
|
#define BATTERY_SELECTED
|
||||||
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
|
||||||
#define MAXCHARGEPOWERALLOWED 10000
|
#define MAXCHARGEPOWERALLOWED 10000
|
||||||
#define MAXDISCHARGEPOWERALLOWED 10000
|
#define MAXDISCHARGEPOWERALLOWED 10000
|
||||||
|
|
||||||
// These parameters need to be mapped for the Gen24
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh; //Wh, 0-60000
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power; //W, 0-60000
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power; //W, 0-60000
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status; //Enum, 0-5
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status; //Enum, 0-2
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery.
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
extern uint8_t LEDcolor; //Enum, 0-10
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
||||||
|
|
||||||
void update_values_kiaHyundai_64_battery();
|
void setup_battery(void);
|
||||||
void receive_can_kiaHyundai_64_battery(CAN_frame_t rx_frame);
|
|
||||||
void send_can_kiaHyundai_64_battery();
|
|
||||||
uint16_t convertToUnsignedInt16(int16_t signed_value);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include "BATTERIES.h"
|
||||||
|
#ifdef NISSAN_LEAF_BATTERY
|
||||||
#include "NISSAN-LEAF-BATTERY.h"
|
#include "NISSAN-LEAF-BATTERY.h"
|
||||||
#ifdef MQTT
|
#ifdef MQTT
|
||||||
#include "../devboard/mqtt/mqtt.h"
|
#include "../devboard/mqtt/mqtt.h"
|
||||||
|
@ -95,16 +97,13 @@ static uint8_t LEAF_Battery_Type = ZE0_BATTERY;
|
||||||
#define MIN_CELL_VOLTAGE 2700 //Battery is put into emergency stop if one cell goes below this value
|
#define MIN_CELL_VOLTAGE 2700 //Battery is put into emergency stop if one cell goes below this value
|
||||||
#define MAX_CELL_DEVIATION 500 //LED turns yellow on the board if mv delta exceeds this value
|
#define MAX_CELL_DEVIATION 500 //LED turns yellow on the board if mv delta exceeds this value
|
||||||
#define WH_PER_GID 77 //One GID is this amount of Watt hours
|
#define WH_PER_GID 77 //One GID is this amount of Watt hours
|
||||||
#define LB_MAX_SOC 1000 //LEAF BMS never goes over this value. We use this info to rescale SOC% sent to Fronius
|
|
||||||
#define LB_MIN_SOC 0 //LEAF BMS never goes below this value. We use this info to rescale SOC% sent to Fronius
|
|
||||||
static uint16_t LB_Discharge_Power_Limit = 0; //Limit in kW
|
static uint16_t LB_Discharge_Power_Limit = 0; //Limit in kW
|
||||||
static uint16_t LB_Charge_Power_Limit = 0; //Limit in kW
|
static uint16_t LB_Charge_Power_Limit = 0; //Limit in kW
|
||||||
static int16_t LB_MAX_POWER_FOR_CHARGER = 0; //Limit in kW
|
static int16_t LB_MAX_POWER_FOR_CHARGER = 0; //Limit in kW
|
||||||
static int16_t LB_SOC = 500; //0 - 100.0 % (0-1000) The real SOC% in the battery
|
static int16_t LB_SOC = 500; //0 - 100.0 % (0-1000) The real SOC% in the battery
|
||||||
static int16_t CalculatedSOC = 0; // Temporary value used for calculating SOC
|
|
||||||
static uint16_t LB_TEMP = 0; //Temporary value used in status checks
|
static uint16_t LB_TEMP = 0; //Temporary value used in status checks
|
||||||
static uint16_t LB_Wh_Remaining = 0; //Amount of energy in battery, in Wh
|
static uint16_t LB_Wh_Remaining = 0; //Amount of energy in battery, in Wh
|
||||||
static uint16_t LB_GIDS = 0;
|
static uint16_t LB_GIDS = 273; //Startup in 24kWh mode
|
||||||
static uint16_t LB_MAX = 0;
|
static uint16_t LB_MAX = 0;
|
||||||
static uint16_t LB_Max_GIDS = 273; //Startup in 24kWh mode
|
static uint16_t LB_Max_GIDS = 273; //Startup in 24kWh mode
|
||||||
static uint16_t LB_StateOfHealth = 99; //State of health %
|
static uint16_t LB_StateOfHealth = 99; //State of health %
|
||||||
|
@ -165,114 +164,106 @@ void print_with_units(char* header, int value, char* units) {
|
||||||
Serial.print(units);
|
Serial.print(units);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_values_leaf_battery() { /* This function maps all the values fetched via CAN to the correct parameters used for modbus */
|
void update_values_battery() { /* This function maps all the values fetched via CAN to the correct parameters used for modbus */
|
||||||
/* Start with mapping all values */
|
/* Start with mapping all values */
|
||||||
|
|
||||||
StateOfHealth = (LB_StateOfHealth * 100); //Increase range from 99% -> 99.00%
|
system_SOH_pptt = (LB_StateOfHealth * 100); //Increase range from 99% -> 99.00%
|
||||||
|
|
||||||
//Calculate the SOC% value to send to Fronius
|
system_real_SOC_pptt = (LB_SOC * 10);
|
||||||
CalculatedSOC = LB_SOC;
|
|
||||||
CalculatedSOC =
|
|
||||||
LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (CalculatedSOC - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
|
||||||
if (CalculatedSOC < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Fronius as 0%
|
|
||||||
CalculatedSOC = 0;
|
|
||||||
}
|
|
||||||
if (CalculatedSOC > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Fronius as 100%
|
|
||||||
CalculatedSOC = 1000;
|
|
||||||
}
|
|
||||||
SOC = (CalculatedSOC * 10); //increase CalculatedSOC range from 0-100.0 -> 100.00
|
|
||||||
|
|
||||||
battery_voltage = (LB_Total_Voltage2 * 5); //0.5V /bit, multiply by 5 to get Voltage+1decimal (350.5V = 701)
|
system_battery_voltage_dV = (LB_Total_Voltage2 * 5); //0.5V/bit, multiply by 5 to get Voltage+1decimal (350.5V = 701)
|
||||||
|
|
||||||
battery_current = convert2unsignedint16((LB_Current2 * 5)); //0.5A/bit, multiply by 5 to get Amp+1decimal (5,5A = 11)
|
system_battery_current_dA = (LB_Current2 * 5); //0.5A/bit, multiply by 5 to get Amp+1decimal (5,5A = 11)
|
||||||
|
|
||||||
capacity_Wh = (LB_Max_GIDS * WH_PER_GID);
|
system_capacity_Wh = (LB_Max_GIDS * WH_PER_GID);
|
||||||
|
|
||||||
remaining_capacity_Wh = LB_Wh_Remaining;
|
system_remaining_capacity_Wh = LB_Wh_Remaining;
|
||||||
|
|
||||||
LB_Power =
|
LB_Power =
|
||||||
((LB_Total_Voltage2 * LB_Current2) / 4); //P = U * I (Both values are 0.5 per bit so the math is non-intuitive)
|
((LB_Total_Voltage2 * LB_Current2) / 4); //P = U * I (Both values are 0.5 per bit so the math is non-intuitive)
|
||||||
stat_batt_power = convert2unsignedint16(LB_Power); //add sign if needed
|
|
||||||
|
system_active_power_W = LB_Power;
|
||||||
|
|
||||||
//Update temperature readings. Method depends on which generation LEAF battery is used
|
//Update temperature readings. Method depends on which generation LEAF battery is used
|
||||||
if (LEAF_Battery_Type == ZE0_BATTERY) {
|
if (LEAF_Battery_Type == ZE0_BATTERY) {
|
||||||
//Since we only have average value, send the minimum as -1.0 degrees below average
|
//Since we only have average value, send the minimum as -1.0 degrees below average
|
||||||
temperature_min =
|
system_temperature_min_dC = ((LB_AverageTemperature * 10) - 10); //Increase range from C to C+1, remove 1.0C
|
||||||
convert2unsignedint16((LB_AverageTemperature * 10) - 10); //add sign if negative and increase range
|
system_temperature_max_dC = (LB_AverageTemperature * 10); //Increase range from C to C+1
|
||||||
temperature_max = convert2unsignedint16((LB_AverageTemperature * 10));
|
|
||||||
} else if (LEAF_Battery_Type == AZE0_BATTERY) {
|
} else if (LEAF_Battery_Type == AZE0_BATTERY) {
|
||||||
//Use the value sent constantly via CAN in 5C0 (only available on AZE0)
|
//Use the value sent constantly via CAN in 5C0 (only available on AZE0)
|
||||||
temperature_min =
|
system_temperature_min_dC = (LB_HistData_Temperature_MIN * 10); //Increase range from C to C+1
|
||||||
convert2unsignedint16((LB_HistData_Temperature_MIN * 10)); //add sign if negative and increase range
|
system_temperature_max_dC = (LB_HistData_Temperature_MAX * 10); //Increase range from C to C+1
|
||||||
temperature_max = convert2unsignedint16((LB_HistData_Temperature_MAX * 10));
|
|
||||||
} else { // ZE1 (TODO: Once the muxed value in 5C0 becomes known, switch to using that instead of this complicated polled value)
|
} else { // ZE1 (TODO: Once the muxed value in 5C0 becomes known, switch to using that instead of this complicated polled value)
|
||||||
if (temp_raw_min != 0) //We have a polled value available
|
if (temp_raw_min != 0) //We have a polled value available
|
||||||
{
|
{
|
||||||
temp_polled_min = ((Temp_fromRAW_to_F(temp_raw_min) - 320) * 5) / 9; //Convert from F to C
|
temp_polled_min = ((Temp_fromRAW_to_F(temp_raw_min) - 320) * 5) / 9; //Convert from F to C
|
||||||
temp_polled_max = ((Temp_fromRAW_to_F(temp_raw_max) - 320) * 5) / 9; //Convert from F to C
|
temp_polled_max = ((Temp_fromRAW_to_F(temp_raw_max) - 320) * 5) / 9; //Convert from F to C
|
||||||
if (temp_polled_min < temp_polled_max) { //Catch any edge cases from Temp_fromRAW_to_F function
|
if (temp_polled_min < temp_polled_max) { //Catch any edge cases from Temp_fromRAW_to_F function
|
||||||
temperature_min = convert2unsignedint16((temp_polled_min));
|
system_temperature_min_dC = temp_polled_min;
|
||||||
temperature_max = convert2unsignedint16((temp_polled_max));
|
system_temperature_max_dC = temp_polled_max;
|
||||||
} else {
|
} else {
|
||||||
temperature_min = convert2unsignedint16((temp_polled_max));
|
system_temperature_min_dC = temp_polled_max;
|
||||||
temperature_max = convert2unsignedint16((temp_polled_min));
|
system_temperature_max_dC = temp_polled_min;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define power able to be discharged from battery
|
// Define power able to be discharged from battery
|
||||||
if (LB_Discharge_Power_Limit > 30) { //if >30kW can be pulled from battery
|
if (LB_Discharge_Power_Limit > 60) { //if >60kW can be pulled from battery
|
||||||
max_target_discharge_power = 30000; //cap value so we don't go over the Fronius limits
|
system_max_discharge_power_W = 60000; //cap value so we don't go over uint16 value
|
||||||
} else {
|
} else {
|
||||||
max_target_discharge_power = (LB_Discharge_Power_Limit * 1000); //kW to W
|
system_max_discharge_power_W = (LB_Discharge_Power_Limit * 1000); //kW to W
|
||||||
}
|
}
|
||||||
if (SOC == 0) { //Scaled SOC% value is 0.00%, we should not discharge battery further
|
if (system_scaled_SOC_pptt == 0) { //Scaled SOC% value is 0.00%, we should not discharge battery further
|
||||||
max_target_discharge_power = 0;
|
system_max_discharge_power_W = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define power able to be put into the battery
|
// Define power able to be put into the battery
|
||||||
if (LB_Charge_Power_Limit > 30) { //if >30kW can be put into the battery
|
if (LB_Charge_Power_Limit > 60) { //if >60kW can be put into the battery
|
||||||
max_target_charge_power = 30000; //cap value so we don't go over the Fronius limits
|
system_max_charge_power_W = 60000; //cap value so we don't go over uint16 value
|
||||||
} else {
|
} else {
|
||||||
max_target_charge_power = (LB_Charge_Power_Limit * 1000); //kW to W
|
system_max_charge_power_W = (LB_Charge_Power_Limit * 1000); //kW to W
|
||||||
}
|
}
|
||||||
if (SOC == 10000) //Scaled SOC% value is 100.00%
|
if (system_scaled_SOC_pptt == 10000) //Scaled SOC% value is 100.00%
|
||||||
{
|
{
|
||||||
max_target_charge_power = 0; //No need to charge further, set max power to 0
|
system_max_charge_power_W = 0; //No need to charge further, set max power to 0
|
||||||
}
|
}
|
||||||
|
|
||||||
//Map all cell voltages to the global array
|
//Map all cell voltages to the global array
|
||||||
for (int i = 0; i < 96; ++i) {
|
for (int i = 0; i < 96; ++i) {
|
||||||
cellvoltages[i] = cell_voltages[i];
|
system_cellvoltages_mV[i] = cell_voltages[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
|
||||||
|
|
||||||
/*Extra safety functions below*/
|
/*Extra safety functions below*/
|
||||||
if (LB_GIDS < 10) //800Wh left in battery
|
if (LB_GIDS < 10) //700Wh left in battery!
|
||||||
{ //Battery is running abnormally low, some discharge logic might have failed. Zero it all out.
|
{ //Battery is running abnormally low, some discharge logic might have failed. Zero it all out.
|
||||||
SOC = 0;
|
set_event(EVENT_BATTERY_EMPTY, 0);
|
||||||
max_target_discharge_power = 0;
|
system_real_SOC_pptt = 0;
|
||||||
|
system_max_discharge_power_W = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if SOC% is plausible
|
//Check if SOC% is plausible
|
||||||
if (battery_voltage >
|
if (system_battery_voltage_dV >
|
||||||
(ABSOLUTE_MAX_VOLTAGE - 100)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
|
(system_max_design_voltage_dV - 100)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
|
||||||
if (LB_SOC < 650) {
|
if (LB_SOC < 650) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, LB_SOC / 10); // Set event with the SOC as data
|
||||||
#ifdef DEBUG_VIA_USB
|
} else {
|
||||||
Serial.println("ERROR: SOC% reported by battery not plausible. Restart battery!");
|
clear_event(EVENT_SOC_PLAUSIBILITY_ERROR);
|
||||||
#endif
|
|
||||||
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, LB_SOC / 10);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LB_Full_CHARGE_flag) { //Battery reports that it is fully charged stop all further charging incase it hasn't already
|
if (LB_Full_CHARGE_flag) { //Battery reports that it is fully charged stop all further charging incase it hasn't already
|
||||||
max_target_charge_power = 0;
|
set_event(EVENT_BATTERY_FULL, 0);
|
||||||
|
system_max_charge_power_W = 0;
|
||||||
|
} else {
|
||||||
|
clear_event(EVENT_BATTERY_FULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LB_Capacity_Empty) { //Battery reports that it is fully discharged. Stop all further discharging incase it hasn't already
|
if (LB_Capacity_Empty) { //Battery reports that it is fully discharged. Stop all further discharging incase it hasn't already
|
||||||
max_target_discharge_power = 0;
|
set_event(EVENT_BATTERY_EMPTY, 0);
|
||||||
|
system_max_discharge_power_W = 0;
|
||||||
|
} else {
|
||||||
|
clear_event(EVENT_BATTERY_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LB_Relay_Cut_Request) { //LB_FAIL, BMS requesting shutdown and contactors to be opened
|
if (LB_Relay_Cut_Request) { //LB_FAIL, BMS requesting shutdown and contactors to be opened
|
||||||
|
@ -281,8 +272,8 @@ void update_values_leaf_battery() { /* This function maps all the values fetched
|
||||||
#endif
|
#endif
|
||||||
//Note, this is sometimes triggered during the night while idle, and the BMS recovers after a while. Removed latching from this scenario
|
//Note, this is sometimes triggered during the night while idle, and the BMS recovers after a while. Removed latching from this scenario
|
||||||
errorCode = 1;
|
errorCode = 1;
|
||||||
max_target_discharge_power = 0;
|
system_max_discharge_power_W = 0;
|
||||||
max_target_charge_power = 0;
|
system_max_charge_power_W = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LB_Failsafe_Status > 0) // 0 is normal, start charging/discharging
|
if (LB_Failsafe_Status > 0) // 0 is normal, start charging/discharging
|
||||||
|
@ -308,87 +299,64 @@ void update_values_leaf_battery() { /* This function maps all the values fetched
|
||||||
break;
|
break;
|
||||||
case (5):
|
case (5):
|
||||||
//Caution Lamp Request & Normal Stop Request
|
//Caution Lamp Request & Normal Stop Request
|
||||||
bms_status = FAULT;
|
|
||||||
errorCode = 2;
|
errorCode = 2;
|
||||||
#ifdef DEBUG_VIA_USB
|
|
||||||
Serial.println("ERROR: Battery raised caution indicator AND requested discharge stop. Inspect battery status!");
|
|
||||||
#endif
|
|
||||||
set_event(EVENT_BATTERY_DISCHG_STOP_REQ, 0);
|
set_event(EVENT_BATTERY_DISCHG_STOP_REQ, 0);
|
||||||
break;
|
break;
|
||||||
case (6):
|
case (6):
|
||||||
//Caution Lamp Request & Charging Mode Stop Request
|
//Caution Lamp Request & Charging Mode Stop Request
|
||||||
bms_status = FAULT;
|
|
||||||
errorCode = 3;
|
errorCode = 3;
|
||||||
#ifdef DEBUG_VIA_USB
|
|
||||||
Serial.println("ERROR: Battery raised caution indicator AND requested charge stop. Inspect battery status!");
|
|
||||||
#endif
|
|
||||||
set_event(EVENT_BATTERY_CHG_STOP_REQ, 0);
|
set_event(EVENT_BATTERY_CHG_STOP_REQ, 0);
|
||||||
break;
|
break;
|
||||||
case (7):
|
case (7):
|
||||||
//Caution Lamp Request & Charging Mode Stop Request & Normal Stop Request
|
//Caution Lamp Request & Charging Mode Stop Request & Normal Stop Request
|
||||||
bms_status = FAULT;
|
|
||||||
errorCode = 4;
|
errorCode = 4;
|
||||||
#ifdef DEBUG_VIA_USB
|
|
||||||
Serial.println(
|
|
||||||
"ERROR: Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!");
|
|
||||||
#endif
|
|
||||||
set_event(EVENT_BATTERY_CHG_DISCHG_STOP_REQ, 0);
|
set_event(EVENT_BATTERY_CHG_DISCHG_STOP_REQ, 0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else { //LB_Failsafe_Status == 0
|
||||||
|
clear_event(EVENT_BATTERY_DISCHG_STOP_REQ);
|
||||||
|
clear_event(EVENT_BATTERY_CHG_STOP_REQ);
|
||||||
|
clear_event(EVENT_BATTERY_CHG_DISCHG_STOP_REQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LB_StateOfHealth < 25) { //Battery is extremely degraded, not fit for secondlifestorage. Zero it all out.
|
if (LB_StateOfHealth < 25) { //Battery is extremely degraded, not fit for secondlifestorage. Zero it all out.
|
||||||
if (LB_StateOfHealth != 0) { //Extra check to see that we actually have a SOH Value available
|
if (LB_StateOfHealth != 0) { //Extra check to see that we actually have a SOH Value available
|
||||||
#ifdef DEBUG_VIA_USB
|
|
||||||
Serial.println(
|
|
||||||
"ERROR: State of health critically low. Battery internal resistance too high to continue. Recycle battery.");
|
|
||||||
#endif
|
|
||||||
bms_status = FAULT;
|
|
||||||
errorCode = 5;
|
errorCode = 5;
|
||||||
set_event(EVENT_LOW_SOH, LB_StateOfHealth);
|
set_event(EVENT_LOW_SOH, LB_StateOfHealth);
|
||||||
max_target_discharge_power = 0;
|
} else {
|
||||||
max_target_charge_power = 0;
|
clear_event(EVENT_LOW_SOH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef INTERLOCK_REQUIRED
|
#ifdef INTERLOCK_REQUIRED
|
||||||
if (!LB_Interlock) {
|
if (!LB_Interlock) {
|
||||||
#ifdef DEBUG_VIA_USB
|
|
||||||
Serial.println(
|
|
||||||
"ERROR: Battery interlock loop broken. Check that high voltage connectors are seated. Battery will be "
|
|
||||||
"disabled!");
|
|
||||||
#endif
|
|
||||||
bms_status = FAULT;
|
|
||||||
set_event(EVENT_HVIL_FAILURE, 0);
|
set_event(EVENT_HVIL_FAILURE, 0);
|
||||||
errorCode = 6;
|
errorCode = 6;
|
||||||
SOC = 0;
|
} else {
|
||||||
max_target_discharge_power = 0;
|
clear_event(EVENT_HVIL_FAILURE);
|
||||||
max_target_charge_power = 0;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if (!CANstillAlive) {
|
if (!CANstillAlive) {
|
||||||
bms_status = FAULT;
|
|
||||||
errorCode = 7;
|
errorCode = 7;
|
||||||
#ifdef DEBUG_VIA_USB
|
set_event(EVENT_CAN_RX_FAILURE, 0);
|
||||||
Serial.println("ERROR: No CAN communication detected for 60s. Shutting down battery control.");
|
|
||||||
#endif
|
|
||||||
set_event(EVENT_CAN_FAILURE, 0);
|
|
||||||
} else {
|
} else {
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
|
clear_event(EVENT_CAN_RX_FAILURE);
|
||||||
}
|
}
|
||||||
if (CANerror >
|
if (CANerror >
|
||||||
MAX_CAN_FAILURES) //Also check if we have recieved too many malformed CAN messages. If so, signal via LED
|
MAX_CAN_FAILURES) //Also check if we have recieved too many malformed CAN messages. If so, signal via LED
|
||||||
{
|
{
|
||||||
errorCode = 10;
|
errorCode = 10;
|
||||||
LEDcolor = YELLOW;
|
set_event(EVENT_CAN_RX_WARNING, 0);
|
||||||
#ifdef DEBUG_VIA_USB
|
}
|
||||||
Serial.println("ERROR: High amount of corrupted CAN messages detected. Check CAN wire shielding!");
|
|
||||||
#endif
|
if (system_bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
|
||||||
set_event(EVENT_CAN_WARNING, 0);
|
system_max_charge_power_W = 0;
|
||||||
|
system_max_discharge_power_W = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Finally print out values to serial if configured to do so*/
|
/*Finally print out values to serial if configured to do so*/
|
||||||
|
@ -398,33 +366,20 @@ void update_values_leaf_battery() { /* This function maps all the values fetched
|
||||||
Serial.println(errorCode);
|
Serial.println(errorCode);
|
||||||
}
|
}
|
||||||
Serial.println("Values going to inverter");
|
Serial.println("Values going to inverter");
|
||||||
print_with_units("SOH%: ", (StateOfHealth * 0.01), "% ");
|
print_with_units("SOH%: ", (system_SOH_pptt * 0.01), "% ");
|
||||||
print_with_units(", SOC% scaled: ", (SOC * 0.01), "% ");
|
print_with_units(", SOC% scaled: ", (system_scaled_SOC_pptt * 0.01), "% ");
|
||||||
print_with_units(", Voltage: ", (battery_voltage * 0.1), "V ");
|
print_with_units(", Voltage: ", (system_battery_voltage_dV * 0.1), "V ");
|
||||||
print_with_units(", Max discharge power: ", max_target_discharge_power, "W ");
|
print_with_units(", Max discharge power: ", system_max_discharge_power_W, "W ");
|
||||||
print_with_units(", Max charge power: ", max_target_charge_power, "W ");
|
print_with_units(", Max charge power: ", system_max_charge_power_W, "W ");
|
||||||
print_with_units(", Max temp: ", ((int16_t)temperature_max * 0.1), "°C ");
|
print_with_units(", Max temp: ", (system_temperature_max_dC * 0.1), "°C ");
|
||||||
print_with_units(", Min temp: ", ((int16_t)temperature_min * 0.1), "°C ");
|
print_with_units(", Min temp: ", (system_temperature_min_dC * 0.1), "°C ");
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
Serial.print("BMS Status: ");
|
Serial.print("BMS Status: ");
|
||||||
if (bms_status == 3) {
|
if (system_bms_status == 3) {
|
||||||
Serial.print("Active, ");
|
Serial.print("Active, ");
|
||||||
} else {
|
} else {
|
||||||
Serial.print("FAULT, ");
|
Serial.print("FAULT, ");
|
||||||
}
|
}
|
||||||
switch (bms_char_dis_status) {
|
|
||||||
case 0:
|
|
||||||
Serial.print("Idle");
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
Serial.print("Discharging");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
Serial.print("Charging");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
print_with_units(", Power: ", LB_Power, "W ");
|
print_with_units(", Power: ", LB_Power, "W ");
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
Serial.println("Values from battery");
|
Serial.println("Values from battery");
|
||||||
|
@ -445,7 +400,7 @@ void update_values_leaf_battery() { /* This function maps all the values fetched
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_leaf_battery(CAN_frame_t rx_frame) {
|
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
switch (rx_frame.MsgID) {
|
switch (rx_frame.MsgID) {
|
||||||
case 0x1DB:
|
case 0x1DB:
|
||||||
if (is_message_corrupt(rx_frame)) {
|
if (is_message_corrupt(rx_frame)) {
|
||||||
|
@ -616,31 +571,19 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame) {
|
||||||
|
|
||||||
cell_deviation_mV = (min_max_voltage[1] - min_max_voltage[0]);
|
cell_deviation_mV = (min_max_voltage[1] - min_max_voltage[0]);
|
||||||
|
|
||||||
cell_max_voltage = min_max_voltage[1];
|
system_cell_max_voltage_mV = min_max_voltage[1];
|
||||||
cell_min_voltage = min_max_voltage[0];
|
system_cell_min_voltage_mV = min_max_voltage[0];
|
||||||
|
|
||||||
if (cell_deviation_mV > MAX_CELL_DEVIATION) {
|
if (cell_deviation_mV > MAX_CELL_DEVIATION) {
|
||||||
LEDcolor = YELLOW;
|
|
||||||
#ifdef DEBUG_VIA_USB
|
|
||||||
Serial.println("HIGH CELL DEVIATION!!! Inspect battery!");
|
|
||||||
#endif
|
|
||||||
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
|
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (min_max_voltage[1] >= MAX_CELL_VOLTAGE) {
|
if (min_max_voltage[1] >= MAX_CELL_VOLTAGE) {
|
||||||
bms_status = FAULT;
|
|
||||||
errorCode = 8;
|
errorCode = 8;
|
||||||
#ifdef DEBUG_VIA_USB
|
|
||||||
Serial.println("CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
#endif
|
|
||||||
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
||||||
}
|
}
|
||||||
if (min_max_voltage[0] <= MIN_CELL_VOLTAGE) {
|
if (min_max_voltage[0] <= MIN_CELL_VOLTAGE) {
|
||||||
bms_status = FAULT;
|
|
||||||
errorCode = 9;
|
errorCode = 9;
|
||||||
#ifdef DEBUG_VIA_USB
|
|
||||||
Serial.println("CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
#endif
|
|
||||||
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -721,7 +664,7 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_leaf_battery() {
|
void send_can_battery() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100) {
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
|
@ -905,14 +848,6 @@ void send_can_leaf_battery() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t convert2unsignedint16(int16_t signed_value) {
|
|
||||||
if (signed_value < 0) {
|
|
||||||
return (65535 + signed_value);
|
|
||||||
} else {
|
|
||||||
return (uint16_t)signed_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_message_corrupt(CAN_frame_t rx_frame) {
|
bool is_message_corrupt(CAN_frame_t rx_frame) {
|
||||||
uint8_t crc = 0;
|
uint8_t crc = 0;
|
||||||
for (uint8_t j = 0; j < 7; j++) {
|
for (uint8_t j = 0; j < 7; j++) {
|
||||||
|
@ -952,6 +887,12 @@ uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrib
|
||||||
return static_cast<uint16_t>(1094 + (309 - temperature) * 2.5714285714285715);
|
return static_cast<uint16_t>(1094 + (309 - temperature) * 2.5714285714285715);
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_battery(void) {
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
nof_cellvoltages = 96;
|
Serial.println("Nissan LEAF battery selected");
|
||||||
|
|
||||||
|
system_number_of_cells = 96;
|
||||||
|
system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
|
||||||
|
system_min_design_voltage_dV = 2600; // 260.0V under this, discharging further is disabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -5,38 +5,32 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE \
|
#define BATTERY_SELECTED
|
||||||
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
|
||||||
|
|
||||||
// These parameters need to be mapped for the inverter
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh; //Wh, 0-60000
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power; //W, 0-60000
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power; //W, 0-60000
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status; //Enum, 0-5
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status; //Enum, 0-2
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint8_t LEDcolor; //Enum, 0-10
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery.
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
void update_values_leaf_battery();
|
|
||||||
void receive_can_leaf_battery(CAN_frame_t rx_frame);
|
|
||||||
void send_can_leaf_battery();
|
|
||||||
uint16_t convert2unsignedint16(int16_t signed_value);
|
|
||||||
uint16_t Temp_fromRAW_to_F(uint16_t temperature);
|
uint16_t Temp_fromRAW_to_F(uint16_t temperature);
|
||||||
bool is_message_corrupt(CAN_frame_t rx_frame);
|
bool is_message_corrupt(CAN_frame_t rx_frame);
|
||||||
|
void setup_battery(void);
|
||||||
void init_battery(void);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
#include "RENAULT-KANGOO-BATTERY.h"
|
#include "BATTERIES.h"
|
||||||
|
#ifdef RENAULT_KANGOO_BATTERY
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
#include "RENAULT-KANGOO-BATTERY.h"
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
#define LB_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Fronius
|
|
||||||
#define LB_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Fronius
|
|
||||||
|
|
||||||
static uint32_t LB_Battery_Voltage = 3700;
|
static uint32_t LB_Battery_Voltage = 3700;
|
||||||
static uint32_t LB_Charge_Power_Limit_Watts = 0;
|
static uint32_t LB_Charge_Power_Limit_Watts = 0;
|
||||||
static uint32_t LB_Discharge_Power_Limit_Watts = 0;
|
static uint32_t LB_Discharge_Power_Limit_Watts = 0;
|
||||||
static int32_t LB_Current = 0;
|
static int32_t LB_Current = 0;
|
||||||
static int16_t LB_MAX_TEMPERATURE = 0;
|
static int16_t LB_MAX_TEMPERATURE = 0;
|
||||||
static int16_t LB_MIN_TEMPERATURE = 0;
|
static int16_t LB_MIN_TEMPERATURE = 0;
|
||||||
static uint16_t soc_calculated = 0;
|
|
||||||
static uint16_t LB_SOC = 0;
|
static uint16_t LB_SOC = 0;
|
||||||
static uint16_t LB_SOH = 0;
|
static uint16_t LB_SOH = 0;
|
||||||
static uint16_t LB_Discharge_Power_Limit = 0;
|
static uint16_t LB_Discharge_Power_Limit = 0;
|
||||||
|
@ -57,115 +55,96 @@ static const int interval10 = 10; // interval (ms) at which send CAN Messag
|
||||||
static const int interval100 = 100; // interval (ms) at which send CAN Messages
|
static const int interval100 = 100; // interval (ms) at which send CAN Messages
|
||||||
static const int interval1000 = 1000; // interval (ms) at which send CAN Messages
|
static const int interval1000 = 1000; // interval (ms) at which send CAN Messages
|
||||||
|
|
||||||
void update_values_kangoo_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
|
||||||
|
|
||||||
StateOfHealth = (LB_SOH * 100); //Increase range from 99% -> 99.00%
|
system_real_SOC_pptt = (LB_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
|
||||||
|
|
||||||
//Calculate the SOC% value to send to Fronius
|
system_SOH_pptt = (LB_SOH * 100); //Increase range from 99% -> 99.00%
|
||||||
soc_calculated = LB_SOC;
|
|
||||||
soc_calculated =
|
|
||||||
LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (soc_calculated - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
|
||||||
if (soc_calculated < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Inverter as 0%
|
|
||||||
soc_calculated = 0;
|
|
||||||
}
|
|
||||||
if (soc_calculated > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Inverter as 100%
|
|
||||||
soc_calculated = 1000;
|
|
||||||
}
|
|
||||||
SOC = (soc_calculated * 10); //increase LB_SOC range from 0-100.0 -> 100.00
|
|
||||||
|
|
||||||
battery_voltage = LB_Battery_Voltage;
|
system_battery_voltage_dV = LB_Battery_Voltage;
|
||||||
|
|
||||||
battery_current = LB_Current;
|
system_battery_current_dA = LB_Current;
|
||||||
|
|
||||||
capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value
|
system_capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value
|
||||||
|
|
||||||
remaining_capacity_Wh = (uint16_t)((SOC / 10000) * capacity_Wh);
|
system_remaining_capacity_Wh = (uint16_t)((system_real_SOC_pptt / 10000) * system_capacity_Wh);
|
||||||
|
|
||||||
LB_Discharge_Power_Limit_Watts = (LB_Discharge_Power_Limit * 500); //Convert value fetched from battery to watts
|
LB_Discharge_Power_Limit_Watts = (LB_Discharge_Power_Limit * 500); //Convert value fetched from battery to watts
|
||||||
/* Define power able to be discharged from battery */
|
/* Define power able to be discharged from battery */
|
||||||
if (LB_Discharge_Power_Limit_Watts > 30000) //if >30kW can be pulled from battery
|
if (LB_Discharge_Power_Limit_Watts > 60000) //if >60kW can be pulled from battery
|
||||||
{
|
{
|
||||||
max_target_discharge_power = 30000; //cap value so we don't go over the Fronius limits
|
system_max_discharge_power_W = 60000; //cap value so we don't go over the uint16 limit
|
||||||
} else {
|
} else {
|
||||||
max_target_discharge_power = LB_Discharge_Power_Limit_Watts;
|
system_max_discharge_power_W = LB_Discharge_Power_Limit_Watts;
|
||||||
}
|
}
|
||||||
if (SOC == 0) //Scaled SOC% value is 0.00%, we should not discharge battery further
|
if (system_scaled_SOC_pptt == 0) //Scaled SOC% value is 0.00%, we should not discharge battery further
|
||||||
{
|
{
|
||||||
max_target_discharge_power = 0;
|
system_max_discharge_power_W = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LB_Charge_Power_Limit_Watts = (LB_Charge_Power_Limit * 500); //Convert value fetched from battery to watts
|
LB_Charge_Power_Limit_Watts = (LB_Charge_Power_Limit * 500); //Convert value fetched from battery to watts
|
||||||
/* Define power able to be put into the battery */
|
/* Define power able to be put into the battery */
|
||||||
if (LB_Charge_Power_Limit_Watts > 30000) //if >30kW can be put into the battery
|
if (LB_Charge_Power_Limit_Watts > 60000) //if >60kW can be put into the battery
|
||||||
{
|
{
|
||||||
max_target_charge_power = 30000; //cap value so we don't go over the Fronius limits
|
system_max_charge_power_W = 60000; //cap value so we don't go over the uint16 limit
|
||||||
}
|
|
||||||
if (LB_Charge_Power_Limit_Watts < 0) {
|
|
||||||
max_target_charge_power = 0; //cap calue so we dont do under the Fronius limits
|
|
||||||
} else {
|
} else {
|
||||||
max_target_charge_power = LB_Charge_Power_Limit_Watts;
|
system_max_charge_power_W = LB_Charge_Power_Limit_Watts;
|
||||||
}
|
}
|
||||||
if (SOC == 10000) //Scaled SOC% value is 100.00%
|
if (system_scaled_SOC_pptt == 10000) //Scaled SOC% value is 100.00%
|
||||||
{
|
{
|
||||||
max_target_charge_power = 0; //No need to charge further, set max power to 0
|
system_max_charge_power_W = 0; //No need to charge further, set max power to 0
|
||||||
}
|
}
|
||||||
|
|
||||||
stat_batt_power = (battery_voltage * LB_Current); //TODO: check if scaling is OK
|
system_active_power_W = (system_battery_voltage_dV * LB_Current); //TODO: check if scaling is OK
|
||||||
|
|
||||||
temperature_min = convert2uint16(LB_MIN_TEMPERATURE * 10);
|
system_temperature_min_dC = (LB_MIN_TEMPERATURE * 10);
|
||||||
|
|
||||||
temperature_max = convert2uint16(LB_MAX_TEMPERATURE * 10);
|
system_temperature_max_dC = (LB_MAX_TEMPERATURE * 10);
|
||||||
|
|
||||||
cell_min_voltage = LB_Cell_Min_Voltage;
|
system_cell_min_voltage_mV = LB_Cell_Min_Voltage;
|
||||||
|
|
||||||
cell_max_voltage = LB_Cell_Max_Voltage;
|
system_cell_max_voltage_mV = LB_Cell_Max_Voltage;
|
||||||
|
|
||||||
cell_deviation_mV = (cell_max_voltage - cell_min_voltage);
|
cell_deviation_mV = (system_temperature_max_dC - system_temperature_min_dC);
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if (!CANstillAlive) {
|
if (!CANstillAlive) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_CAN_RX_FAILURE, 0);
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
|
||||||
set_event(EVENT_CAN_FAILURE, 0);
|
|
||||||
} else {
|
} else {
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
|
clear_event(EVENT_CAN_RX_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LB_Cell_Max_Voltage >= ABSOLUTE_CELL_MAX_VOLTAGE) {
|
if (LB_Cell_Max_Voltage >= ABSOLUTE_CELL_MAX_VOLTAGE) {
|
||||||
bms_status = FAULT;
|
|
||||||
Serial.println("ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
||||||
}
|
}
|
||||||
if (LB_Cell_Min_Voltage <= ABSOLUTE_CELL_MIN_VOLTAGE) {
|
if (LB_Cell_Min_Voltage <= ABSOLUTE_CELL_MIN_VOLTAGE) {
|
||||||
bms_status = FAULT;
|
|
||||||
Serial.println("ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
||||||
}
|
}
|
||||||
if (cell_deviation_mV > MAX_CELL_DEVIATION_MV) {
|
if (cell_deviation_mV > MAX_CELL_DEVIATION_MV) {
|
||||||
LEDcolor = YELLOW;
|
|
||||||
Serial.println("ERROR: HIGH CELL mV DEVIATION!!! Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
|
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
|
||||||
|
} else {
|
||||||
|
clear_event(EVENT_CELL_DEVIATION_HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.println("Values going to inverter:");
|
Serial.println("Values going to inverter:");
|
||||||
Serial.print("SOH%: ");
|
Serial.print("SOH%: ");
|
||||||
Serial.print(StateOfHealth);
|
Serial.print(system_SOH_pptt);
|
||||||
Serial.print(", SOC% scaled: ");
|
Serial.print(", SOC% scaled: ");
|
||||||
Serial.print(SOC);
|
Serial.print(system_scaled_SOC_pptt);
|
||||||
Serial.print(", Voltage: ");
|
Serial.print(", Voltage: ");
|
||||||
Serial.print(battery_voltage);
|
Serial.print(system_battery_voltage_dV);
|
||||||
Serial.print(", Max discharge power: ");
|
Serial.print(", Max discharge power: ");
|
||||||
Serial.print(max_target_discharge_power);
|
Serial.print(system_max_discharge_power_W);
|
||||||
Serial.print(", Max charge power: ");
|
Serial.print(", Max charge power: ");
|
||||||
Serial.print(max_target_charge_power);
|
Serial.print(system_max_charge_power_W);
|
||||||
Serial.print(", Max temp: ");
|
Serial.print(", Max temp: ");
|
||||||
Serial.print(temperature_max);
|
Serial.print(system_temperature_max_dC);
|
||||||
Serial.print(", Min temp: ");
|
Serial.print(", Min temp: ");
|
||||||
Serial.print(temperature_min);
|
Serial.print(system_temperature_min_dC);
|
||||||
Serial.print(", BMS Status (3=OK): ");
|
Serial.print(", BMS Status (3=OK): ");
|
||||||
Serial.print(bms_status);
|
Serial.print(system_bms_status);
|
||||||
|
|
||||||
Serial.println("Battery values: ");
|
Serial.println("Battery values: ");
|
||||||
Serial.print("Real SOC: ");
|
Serial.print("Real SOC: ");
|
||||||
|
@ -182,7 +161,7 @@ void update_values_kangoo_battery() { //This function maps all the values fetch
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_kangoo_battery(CAN_frame_t rx_frame) //GKOE reworked
|
void receive_can_battery(CAN_frame_t rx_frame) //GKOE reworked
|
||||||
{
|
{
|
||||||
|
|
||||||
switch (rx_frame.MsgID) {
|
switch (rx_frame.MsgID) {
|
||||||
|
@ -235,7 +214,7 @@ void receive_can_kangoo_battery(CAN_frame_t rx_frame) //GKOE reworked
|
||||||
}
|
}
|
||||||
if (rx_frame.data.u8[0] == 0x24) { //5th response Bytes 24-31
|
if (rx_frame.data.u8[0] == 0x24) { //5th response Bytes 24-31
|
||||||
LB_Discharge_Power_Limit = word(LB_Discharge_Power_Limit_Byte1, rx_frame.data.u8[1]) * 100; //OK!
|
LB_Discharge_Power_Limit = word(LB_Discharge_Power_Limit_Byte1, rx_frame.data.u8[1]) * 100; //OK!
|
||||||
LB_Battery_Voltage = word(rx_frame.data.u8[2], rx_frame.data.u8[3]) * 10; //OK!
|
LB_Battery_Voltage = word(rx_frame.data.u8[2], rx_frame.data.u8[3]) / 10; //OK!
|
||||||
GVB_79B_Continue = false;
|
GVB_79B_Continue = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -244,7 +223,7 @@ void receive_can_kangoo_battery(CAN_frame_t rx_frame) //GKOE reworked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_can_kangoo_battery() {
|
void send_can_battery() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message (for 2.4s, then pause 10s)
|
// Send 100ms CAN Message (for 2.4s, then pause 10s)
|
||||||
if ((currentMillis - previousMillis100) >= (interval100 + GVL_pause)) {
|
if ((currentMillis - previousMillis100) >= (interval100 + GVL_pause)) {
|
||||||
|
@ -267,10 +246,11 @@ void send_can_kangoo_battery() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t convert2uint16(int16_t signed_value) {
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
if (signed_value < 0) {
|
Serial.println("Renault Kangoo battery selected");
|
||||||
return (65535 + signed_value);
|
|
||||||
} else {
|
system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
|
||||||
return (uint16_t)signed_value;
|
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -5,39 +5,37 @@
|
||||||
#include "../devboard/config.h" // Needed for defines
|
#include "../devboard/config.h" // Needed for defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE \
|
#define BATTERY_SELECTED
|
||||||
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
|
||||||
#define ABSOLUTE_CELL_MAX_VOLTAGE \
|
#define ABSOLUTE_CELL_MAX_VOLTAGE \
|
||||||
4100 // Max Cell Voltage mV! if voltage goes over this, charging is not possible (goes into forced discharge)
|
4100 // Max Cell Voltage mV! if voltage goes over this, charging is not possible (goes into forced discharge)
|
||||||
#define ABSOLUTE_CELL_MIN_VOLTAGE \
|
#define ABSOLUTE_CELL_MIN_VOLTAGE \
|
||||||
3000 // Min Cell Voltage mV! if voltage goes under this, discharging further is disabled
|
3000 // Min Cell Voltage mV! if voltage goes under this, discharging further is disabled
|
||||||
#define MAX_CELL_DEVIATION_MV 500 //LED turns yellow on the board if mv delta exceeds this value
|
#define MAX_CELL_DEVIATION_MV 500 //LED turns yellow on the board if mv delta exceeds this value
|
||||||
|
|
||||||
// These parameters need to be mapped for the Gen24
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh; //Wh, 0-60000
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power; //W, 0-60000
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power; //W, 0-60000
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status; //Enum, 0-5
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status; //Enum, 0-2
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t CANerror;
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern uint8_t LEDcolor; //Enum, 0-10
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
extern bool inverterAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
void update_values_kangoo_battery();
|
void setup_battery(void);
|
||||||
void receive_can_kangoo_battery(CAN_frame_t rx_frame);
|
|
||||||
void send_can_kangoo_battery();
|
|
||||||
uint16_t convert2uint16(int16_t signed_value);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
#include "RENAULT-ZOE-BATTERY.h"
|
#include "BATTERIES.h"
|
||||||
|
#ifdef RENAULT_ZOE_BATTERY
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
#include "RENAULT-ZOE-BATTERY.h"
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
#define LB_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Fronius
|
|
||||||
#define LB_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Fronius
|
|
||||||
|
|
||||||
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
||||||
static uint8_t errorCode = 0; //stores if we have an error code active from battery control logic
|
static uint8_t errorCode = 0; //stores if we have an error code active from battery control logic
|
||||||
static uint16_t LB_SOC = 50;
|
static uint16_t LB_SOC = 50;
|
||||||
static uint16_t soc_calculated = 0;
|
|
||||||
static uint16_t LB_SOH = 99;
|
static uint16_t LB_SOH = 99;
|
||||||
static int16_t LB_MIN_TEMPERATURE = 0;
|
static int16_t LB_MIN_TEMPERATURE = 0;
|
||||||
static int16_t LB_MAX_TEMPERATURE = 0;
|
static int16_t LB_MAX_TEMPERATURE = 0;
|
||||||
|
@ -42,91 +40,74 @@ static const int interval10 = 10; // interval (ms) at which send CAN Messag
|
||||||
static const int interval100 = 100; // interval (ms) at which send CAN Messages
|
static const int interval100 = 100; // interval (ms) at which send CAN Messages
|
||||||
static const int interval1000 = 1000; // interval (ms) at which send CAN Messages
|
static const int interval1000 = 1000; // interval (ms) at which send CAN Messages
|
||||||
|
|
||||||
void update_values_zoe_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
system_SOH_pptt = (LB_SOH * 100); //Increase range from 99% -> 99.00%
|
||||||
|
|
||||||
StateOfHealth = (LB_SOH * 100); //Increase range from 99% -> 99.00%
|
system_real_SOC_pptt = (LB_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
|
||||||
|
|
||||||
//Calculate the SOC% value to send to Fronius
|
system_battery_voltage_dV = LB_Battery_Voltage;
|
||||||
soc_calculated = LB_SOC;
|
|
||||||
soc_calculated =
|
|
||||||
LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (soc_calculated - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
|
||||||
if (soc_calculated < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Inverter as 0%
|
|
||||||
soc_calculated = 0;
|
|
||||||
}
|
|
||||||
if (soc_calculated > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Inverter as 100%
|
|
||||||
soc_calculated = 1000;
|
|
||||||
}
|
|
||||||
SOC = (soc_calculated * 10); //increase LB_SOC range from 0-100.0 -> 100.00
|
|
||||||
|
|
||||||
battery_voltage = LB_Battery_Voltage;
|
system_battery_current_dA = LB_Current;
|
||||||
|
|
||||||
battery_current = LB_Current;
|
system_capacity_Wh = BATTERY_WH_MAX; //Use the configured value to avoid overflows
|
||||||
|
|
||||||
capacity_Wh = BATTERY_WH_MAX; //Use the configured value to avoid overflows
|
|
||||||
|
|
||||||
//Calculate the remaining Wh amount from SOC% and max Wh value.
|
//Calculate the remaining Wh amount from SOC% and max Wh value.
|
||||||
remaining_capacity_Wh = static_cast<int>((static_cast<double>(SOC) / 10000) * BATTERY_WH_MAX);
|
system_remaining_capacity_Wh = static_cast<int>((static_cast<double>(system_real_SOC_pptt) / 10000) * BATTERY_WH_MAX);
|
||||||
|
|
||||||
max_target_discharge_power;
|
system_max_discharge_power_W;
|
||||||
|
|
||||||
max_target_charge_power;
|
system_max_charge_power_W;
|
||||||
|
|
||||||
stat_batt_power;
|
system_active_power_W;
|
||||||
|
|
||||||
temperature_min;
|
system_temperature_min_dC;
|
||||||
|
|
||||||
temperature_max;
|
system_temperature_max_dC;
|
||||||
|
|
||||||
cell_min_voltage;
|
system_cell_min_voltage_mV;
|
||||||
|
|
||||||
cell_max_voltage;
|
system_cell_max_voltage_mV;
|
||||||
|
|
||||||
cell_deviation_mV = (cell_max_voltage - cell_min_voltage);
|
cell_deviation_mV = (system_cell_max_voltage_mV - system_cell_min_voltage_mV);
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if (!CANstillAlive) {
|
if (!CANstillAlive) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_CAN_RX_FAILURE, 0);
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
|
||||||
set_event(EVENT_CAN_FAILURE, 0);
|
|
||||||
} else {
|
} else {
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
|
clear_event(EVENT_CAN_RX_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LB_Cell_Max_Voltage >= ABSOLUTE_CELL_MAX_VOLTAGE) {
|
if (LB_Cell_Max_Voltage >= ABSOLUTE_CELL_MAX_VOLTAGE) {
|
||||||
bms_status = FAULT;
|
|
||||||
Serial.println("ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
||||||
}
|
}
|
||||||
if (LB_Cell_Min_Voltage <= ABSOLUTE_CELL_MIN_VOLTAGE) {
|
if (LB_Cell_Min_Voltage <= ABSOLUTE_CELL_MIN_VOLTAGE) {
|
||||||
bms_status = FAULT;
|
|
||||||
Serial.println("ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
||||||
}
|
}
|
||||||
if (cell_deviation_mV > MAX_CELL_DEVIATION_MV) {
|
if (cell_deviation_mV > MAX_CELL_DEVIATION_MV) {
|
||||||
LEDcolor = YELLOW;
|
|
||||||
Serial.println("ERROR: HIGH CELL mV DEVIATION!!! Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
|
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
|
||||||
|
} else {
|
||||||
|
clear_event(EVENT_CELL_DEVIATION_HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.println("Values going to inverter:");
|
Serial.println("Values going to inverter:");
|
||||||
Serial.print("SOH%: ");
|
Serial.print("SOH%: ");
|
||||||
Serial.print(StateOfHealth);
|
Serial.print(system_SOH_pptt);
|
||||||
Serial.print(", SOC% scaled: ");
|
Serial.print(", SOC% scaled: ");
|
||||||
Serial.print(SOC);
|
Serial.print(system_scaled_SOC_pptt);
|
||||||
Serial.print(", Voltage: ");
|
Serial.print(", Voltage: ");
|
||||||
Serial.print(battery_voltage);
|
Serial.print(system_battery_voltage_dV);
|
||||||
Serial.print(", Max discharge power: ");
|
Serial.print(", Max discharge power: ");
|
||||||
Serial.print(max_target_discharge_power);
|
Serial.print(system_max_discharge_power_W);
|
||||||
Serial.print(", Max charge power: ");
|
Serial.print(", Max charge power: ");
|
||||||
Serial.print(max_target_charge_power);
|
Serial.print(system_max_charge_power_W);
|
||||||
Serial.print(", Max temp: ");
|
Serial.print(", Max temp: ");
|
||||||
Serial.print(temperature_max);
|
Serial.print(system_temperature_max_dC);
|
||||||
Serial.print(", Min temp: ");
|
Serial.print(", Min temp: ");
|
||||||
Serial.print(temperature_min);
|
Serial.print(system_temperature_min_dC);
|
||||||
Serial.print(", BMS Status (3=OK): ");
|
Serial.print(", BMS Status (3=OK): ");
|
||||||
Serial.print(bms_status);
|
Serial.print(system_bms_status);
|
||||||
|
|
||||||
Serial.println("Battery values: ");
|
Serial.println("Battery values: ");
|
||||||
Serial.print("Real SOC: ");
|
Serial.print("Real SOC: ");
|
||||||
|
@ -143,7 +124,7 @@ void update_values_zoe_battery() { //This function maps all the values fetched
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_zoe_battery(CAN_frame_t rx_frame) {
|
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
|
|
||||||
switch (rx_frame.MsgID) {
|
switch (rx_frame.MsgID) {
|
||||||
case 0x42E: //HV SOC & Battery Temp & Charging Power
|
case 0x42E: //HV SOC & Battery Temp & Charging Power
|
||||||
|
@ -157,7 +138,7 @@ void receive_can_zoe_battery(CAN_frame_t rx_frame) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_can_zoe_battery() {
|
void send_can_battery() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100) {
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
|
@ -170,3 +151,12 @@ void send_can_zoe_battery() {
|
||||||
//ESP32Can.CANWriteFrame(&ZOE_423);
|
//ESP32Can.CANWriteFrame(&ZOE_423);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
|
Serial.println("Renault Zoe battery selected");
|
||||||
|
|
||||||
|
system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
|
||||||
|
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -5,38 +5,37 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE \
|
#define BATTERY_SELECTED
|
||||||
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
|
||||||
#define ABSOLUTE_CELL_MAX_VOLTAGE \
|
#define ABSOLUTE_CELL_MAX_VOLTAGE \
|
||||||
4100 // Max Cell Voltage mV! if voltage goes over this, charging is not possible (goes into forced discharge)
|
4100 // Max Cell Voltage mV! if voltage goes over this, charging is not possible (goes into forced discharge)
|
||||||
#define ABSOLUTE_CELL_MIN_VOLTAGE \
|
#define ABSOLUTE_CELL_MIN_VOLTAGE \
|
||||||
3000 // Min Cell Voltage mV! if voltage goes under this, discharging further is disabled
|
3000 // Min Cell Voltage mV! if voltage goes under this, discharging further is disabled
|
||||||
#define MAX_CELL_DEVIATION_MV 500 //LED turns yellow on the board if mv delta exceeds this value
|
#define MAX_CELL_DEVIATION_MV 500 //LED turns yellow on the board if mv delta exceeds this value
|
||||||
|
|
||||||
// These parameters need to be mapped for the Gen24
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh; //Wh, 0-60000
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power; //W, 0-60000
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power; //W, 0-60000
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status; //Enum, 0-5
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status; //Enum, 0-2
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t CANerror;
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern uint8_t LEDcolor; //Enum, 0-10
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
||||||
|
|
||||||
void update_values_zoe_battery();
|
void setup_battery(void);
|
||||||
void receive_can_zoe_battery(CAN_frame_t rx_frame);
|
|
||||||
void send_can_zoe_battery();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#include "SANTA-FE-PHEV-BATTERY.h"
|
#include "BATTERIES.h"
|
||||||
|
#ifdef SANTA_FE_PHEV_BATTERY
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
#include "SANTA-FE-PHEV-BATTERY.h"
|
||||||
|
|
||||||
/* Credits go to maciek16c for these findings!
|
/* Credits go to maciek16c for these findings!
|
||||||
https://github.com/maciek16c/hyundai-santa-fe-phev-battery
|
https://github.com/maciek16c/hyundai-santa-fe-phev-battery
|
||||||
|
@ -19,9 +21,6 @@ static const int interval10 = 10; // interval (ms) at which send CAN
|
||||||
static const int interval100 = 100; // interval (ms) at which send CAN Messages
|
static const int interval100 = 100; // interval (ms) at which send CAN Messages
|
||||||
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
||||||
|
|
||||||
#define LB_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Inverter
|
|
||||||
#define LB_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Inverter
|
|
||||||
|
|
||||||
static int SOC_1 = 0;
|
static int SOC_1 = 0;
|
||||||
static int SOC_2 = 0;
|
static int SOC_2 = 0;
|
||||||
static int SOC_3 = 0;
|
static int SOC_3 = 0;
|
||||||
|
@ -57,37 +56,34 @@ CAN_frame_t SANTAFE_523 = {.FIR = {.B =
|
||||||
.MsgID = 0x523,
|
.MsgID = 0x523,
|
||||||
.data = {0x60, 0x00, 0x60, 0, 0, 0, 0, 0}};
|
.data = {0x60, 0x00, 0x60, 0, 0, 0, 0, 0}};
|
||||||
|
|
||||||
void update_values_santafe_phev_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
|
|
||||||
SOC;
|
system_real_SOC_pptt;
|
||||||
|
|
||||||
battery_voltage;
|
system_battery_voltage_dV;
|
||||||
|
|
||||||
battery_current;
|
system_battery_current_dA;
|
||||||
|
|
||||||
capacity_Wh = BATTERY_WH_MAX;
|
system_capacity_Wh = BATTERY_WH_MAX;
|
||||||
|
|
||||||
remaining_capacity_Wh;
|
system_remaining_capacity_Wh;
|
||||||
|
|
||||||
max_target_discharge_power;
|
system_max_discharge_power_W;
|
||||||
|
|
||||||
max_target_charge_power;
|
system_max_charge_power_W;
|
||||||
|
|
||||||
stat_batt_power;
|
system_active_power_W;
|
||||||
|
|
||||||
temperature_min;
|
system_temperature_min_dC;
|
||||||
|
|
||||||
temperature_max;
|
system_temperature_max_dC;
|
||||||
|
|
||||||
bms_status = ACTIVE; //Startout in active mode, then check safeties
|
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if (!CANstillAlive) {
|
if (!CANstillAlive) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_CAN_RX_FAILURE, 0);
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
|
||||||
set_event(EVENT_CAN_FAILURE, 0);
|
|
||||||
} else {
|
} else {
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
|
clear_event(EVENT_CAN_RX_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
|
@ -95,7 +91,7 @@ void update_values_santafe_phev_battery() { //This function maps all the values
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_santafe_phev_battery(CAN_frame_t rx_frame) {
|
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
CANstillAlive = 12;
|
CANstillAlive = 12;
|
||||||
switch (rx_frame.MsgID) {
|
switch (rx_frame.MsgID) {
|
||||||
case 0x200:
|
case 0x200:
|
||||||
|
@ -130,7 +126,7 @@ void receive_can_santafe_phev_battery(CAN_frame_t rx_frame) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_santafe_phev_battery() {
|
void send_can_battery() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
//Send 10ms message
|
//Send 10ms message
|
||||||
if (currentMillis - previousMillis10 >= interval10) {
|
if (currentMillis - previousMillis10 >= interval10) {
|
||||||
|
@ -177,3 +173,12 @@ uint8_t CalculateCRC8(CAN_frame_t rx_frame) {
|
||||||
}
|
}
|
||||||
return (uint8_t)crc;
|
return (uint8_t)crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
|
Serial.println("Hyundai Santa Fe PHEV battery selected");
|
||||||
|
|
||||||
|
system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
|
||||||
|
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -5,31 +5,31 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE \
|
#define BATTERY_SELECTED
|
||||||
4030 // 403.0V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
|
||||||
|
|
||||||
// These parameters need to be mapped for the Gen24
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC;
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth;
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage;
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current;
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh;
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh;
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power;
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power;
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status;
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status;
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power;
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min;
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max;
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t CANerror;
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
void update_values_santafe_phev_battery();
|
|
||||||
void receive_can_santafe_phev_battery(CAN_frame_t rx_frame);
|
|
||||||
void send_can_santafe_phev_battery();
|
|
||||||
uint8_t CalculateCRC8(CAN_frame_t rx_frame);
|
uint8_t CalculateCRC8(CAN_frame_t rx_frame);
|
||||||
|
void setup_battery(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp
|
#include "BATTERIES.h"
|
||||||
|
#ifdef SERIAL_LINK_RECEIVER
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "../devboard/utils/events.h"
|
||||||
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h"
|
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h"
|
||||||
|
|
||||||
#define INVERTER_SEND_NUM_VARIABLES 1
|
#define INVERTER_SEND_NUM_VARIABLES 1
|
||||||
|
@ -26,28 +28,28 @@ SerialDataLink dataLinkReceive(SerialReceiver, 0, 0x01, sendingNumVariables,
|
||||||
static bool batteryFault = false; // used locally - mainly to indicate Battery CAN failure
|
static bool batteryFault = false; // used locally - mainly to indicate Battery CAN failure
|
||||||
|
|
||||||
void __getData() {
|
void __getData() {
|
||||||
SOC = (uint16_t)dataLinkReceive.getReceivedData(0);
|
system_real_SOC_pptt = (uint16_t)dataLinkReceive.getReceivedData(0);
|
||||||
StateOfHealth = (uint16_t)dataLinkReceive.getReceivedData(1);
|
system_SOH_pptt = (uint16_t)dataLinkReceive.getReceivedData(1);
|
||||||
battery_voltage = (uint16_t)dataLinkReceive.getReceivedData(2);
|
system_battery_voltage_dV = (uint16_t)dataLinkReceive.getReceivedData(2);
|
||||||
battery_current = (uint16_t)dataLinkReceive.getReceivedData(3);
|
system_battery_current_dA = (int16_t)dataLinkReceive.getReceivedData(3);
|
||||||
capacity_Wh = (uint16_t)dataLinkReceive.getReceivedData(4);
|
system_capacity_Wh = (uint32_t)dataLinkReceive.getReceivedData(4);
|
||||||
remaining_capacity_Wh = (uint16_t)dataLinkReceive.getReceivedData(5);
|
system_remaining_capacity_Wh = (uint32_t)dataLinkReceive.getReceivedData(5);
|
||||||
max_target_discharge_power = (uint16_t)dataLinkReceive.getReceivedData(6);
|
system_max_discharge_power_W = (uint16_t)dataLinkReceive.getReceivedData(6);
|
||||||
max_target_charge_power = (uint16_t)dataLinkReceive.getReceivedData(7);
|
system_max_charge_power_W = (uint16_t)dataLinkReceive.getReceivedData(7);
|
||||||
uint16_t _bms_status = (uint16_t)dataLinkReceive.getReceivedData(8);
|
uint16_t _system_bms_status = (uint16_t)dataLinkReceive.getReceivedData(8);
|
||||||
bms_status = _bms_status;
|
system_active_power_W = (uint16_t)dataLinkReceive.getReceivedData(9);
|
||||||
bms_char_dis_status = (uint16_t)dataLinkReceive.getReceivedData(9);
|
system_temperature_min_dC = (int16_t)dataLinkReceive.getReceivedData(10);
|
||||||
stat_batt_power = (uint16_t)dataLinkReceive.getReceivedData(10);
|
system_temperature_max_dC = (int16_t)dataLinkReceive.getReceivedData(11);
|
||||||
temperature_min = (uint16_t)dataLinkReceive.getReceivedData(11);
|
system_cell_max_voltage_mV = (uint16_t)dataLinkReceive.getReceivedData(12);
|
||||||
temperature_max = (uint16_t)dataLinkReceive.getReceivedData(12);
|
system_cell_min_voltage_mV = (uint16_t)dataLinkReceive.getReceivedData(13);
|
||||||
cell_max_voltage = (uint16_t)dataLinkReceive.getReceivedData(13);
|
system_LFP_Chemistry = (bool)dataLinkReceive.getReceivedData(14);
|
||||||
cell_min_voltage = (uint16_t)dataLinkReceive.getReceivedData(14);
|
batteryAllowsContactorClosing = (bool)dataLinkReceive.getReceivedData(15);
|
||||||
LFP_Chemistry = (bool)dataLinkReceive.getReceivedData(15);
|
|
||||||
batteryAllowsContactorClosing = (uint16_t)dataLinkReceive.getReceivedData(16);
|
|
||||||
|
|
||||||
batteryFault = false;
|
batteryFault = false;
|
||||||
if (_bms_status == FAULT)
|
if (_system_bms_status == FAULT) {
|
||||||
batteryFault = true;
|
batteryFault = true;
|
||||||
|
set_event(EVENT_SERIAL_TRANSMITTER_FAILURE, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateData() {
|
void updateData() {
|
||||||
|
@ -97,8 +99,8 @@ void manageSerialLinkReceiver() {
|
||||||
{
|
{
|
||||||
__getData();
|
__getData();
|
||||||
reads++;
|
reads++;
|
||||||
lastGoodMaxCharge = max_target_charge_power;
|
lastGoodMaxCharge = system_max_charge_power_W;
|
||||||
lastGoodMaxDischarge = max_target_discharge_power;
|
lastGoodMaxDischarge = system_max_discharge_power_W;
|
||||||
//--- if BatteryFault then assume Data is stale
|
//--- if BatteryFault then assume Data is stale
|
||||||
if (!batteryFault)
|
if (!batteryFault)
|
||||||
lastGood = currentTime;
|
lastGood = currentTime;
|
||||||
|
@ -114,14 +116,14 @@ void manageSerialLinkReceiver() {
|
||||||
if (minutesLost > 0 && lastGood > 0) {
|
if (minutesLost > 0 && lastGood > 0) {
|
||||||
// lose 25% each minute of data loss
|
// lose 25% each minute of data loss
|
||||||
if (minutesLost < 4) {
|
if (minutesLost < 4) {
|
||||||
max_target_charge_power = (lastGoodMaxCharge * (4 - minutesLost)) / 4;
|
system_max_charge_power_W = (lastGoodMaxCharge * (4 - minutesLost)) / 4;
|
||||||
max_target_discharge_power = (lastGoodMaxDischarge * (4 - minutesLost)) / 4;
|
system_max_discharge_power_W = (lastGoodMaxDischarge * (4 - minutesLost)) / 4;
|
||||||
|
set_event(EVENT_SERIAL_RX_WARNING, minutesLost);
|
||||||
} else {
|
} else {
|
||||||
// Times Up -
|
// Times Up -
|
||||||
max_target_charge_power = 0;
|
system_max_charge_power_W = 0;
|
||||||
max_target_discharge_power = 0;
|
system_max_discharge_power_W = 0;
|
||||||
bms_status = 4; //Fault state
|
set_event(EVENT_SERIAL_RX_FAILURE, uint8_t(min(minutesLost, 255uL)));
|
||||||
LEDcolor = RED;
|
|
||||||
//----- Throw Error
|
//----- Throw Error
|
||||||
}
|
}
|
||||||
// report Lost data & Max charge / Discharge reductions
|
// report Lost data & Max charge / Discharge reductions
|
||||||
|
@ -135,9 +137,9 @@ void manageSerialLinkReceiver() {
|
||||||
}
|
}
|
||||||
Serial.print(minutesLost);
|
Serial.print(minutesLost);
|
||||||
Serial.print(", max Charge = ");
|
Serial.print(", max Charge = ");
|
||||||
Serial.print(max_target_charge_power);
|
Serial.print(system_max_charge_power_W);
|
||||||
Serial.print(", max Discharge = ");
|
Serial.print(", max Discharge = ");
|
||||||
Serial.println(max_target_discharge_power);
|
Serial.println(system_max_discharge_power_W);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,37 +176,35 @@ void manageSerialLinkReceiver() {
|
||||||
void update_values_serial_link() {
|
void update_values_serial_link() {
|
||||||
Serial.println("Values from battery: ");
|
Serial.println("Values from battery: ");
|
||||||
Serial.print("SOC: ");
|
Serial.print("SOC: ");
|
||||||
Serial.print(SOC);
|
Serial.print(system_real_SOC_pptt);
|
||||||
Serial.print(" SOH: ");
|
Serial.print(" SOH: ");
|
||||||
Serial.print(StateOfHealth);
|
Serial.print(system_SOH_pptt);
|
||||||
Serial.print(" Voltage: ");
|
Serial.print(" Voltage: ");
|
||||||
Serial.print(battery_voltage);
|
Serial.print(system_battery_voltage_dV);
|
||||||
Serial.print(" Current: ");
|
Serial.print(" Current: ");
|
||||||
Serial.print(battery_current);
|
Serial.print(system_battery_current_dA);
|
||||||
Serial.print(" Capacity: ");
|
Serial.print(" Capacity: ");
|
||||||
Serial.print(capacity_Wh);
|
Serial.print(system_capacity_Wh);
|
||||||
Serial.print(" Remain cap: ");
|
Serial.print(" Remain cap: ");
|
||||||
Serial.print(remaining_capacity_Wh);
|
Serial.print(system_remaining_capacity_Wh);
|
||||||
Serial.print(" Max discharge W: ");
|
Serial.print(" Max discharge W: ");
|
||||||
Serial.print(max_target_discharge_power);
|
Serial.print(system_max_discharge_power_W);
|
||||||
Serial.print(" Max charge W: ");
|
Serial.print(" Max charge W: ");
|
||||||
Serial.print(max_target_charge_power);
|
Serial.print(system_max_charge_power_W);
|
||||||
Serial.print(" BMS status: ");
|
Serial.print(" BMS status: ");
|
||||||
Serial.print(bms_status);
|
Serial.print(system_bms_status);
|
||||||
Serial.print(" BMS status dis/cha: ");
|
|
||||||
Serial.print(bms_char_dis_status);
|
|
||||||
Serial.print(" Power: ");
|
Serial.print(" Power: ");
|
||||||
Serial.print(stat_batt_power);
|
Serial.print(system_active_power_W);
|
||||||
Serial.print(" Temp min: ");
|
Serial.print(" Temp min: ");
|
||||||
Serial.print(temperature_min);
|
Serial.print(system_temperature_min_dC);
|
||||||
Serial.print(" Temp max: ");
|
Serial.print(" Temp max: ");
|
||||||
Serial.print(temperature_max);
|
Serial.print(system_temperature_max_dC);
|
||||||
Serial.print(" Cell max: ");
|
Serial.print(" Cell max: ");
|
||||||
Serial.print(cell_max_voltage);
|
Serial.print(system_cell_max_voltage_mV);
|
||||||
Serial.print(" Cell min: ");
|
Serial.print(" Cell min: ");
|
||||||
Serial.print(cell_min_voltage);
|
Serial.print(system_cell_min_voltage_mV);
|
||||||
Serial.print(" LFP : ");
|
Serial.print(" LFP : ");
|
||||||
Serial.print(LFP_Chemistry);
|
Serial.print(system_LFP_Chemistry);
|
||||||
Serial.print(" batteryAllowsContactorClosing: ");
|
Serial.print(" batteryAllowsContactorClosing: ");
|
||||||
Serial.print(batteryAllowsContactorClosing);
|
Serial.print(batteryAllowsContactorClosing);
|
||||||
Serial.print(" inverterAllowsContactorClosing: ");
|
Serial.print(" inverterAllowsContactorClosing: ");
|
||||||
|
@ -212,3 +212,11 @@ void update_values_serial_link() {
|
||||||
|
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup_battery(void) {
|
||||||
|
Serial.println("SERIAL_DATA_LINK_RECEIVER selected");
|
||||||
|
}
|
||||||
|
void update_values_battery() {}
|
||||||
|
void send_can_battery() {}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#ifndef SERIAL_LINK_RECEIVER_FROM_BATTERY_H
|
#ifndef SERIAL_LINK_RECEIVER_FROM_BATTERY_H
|
||||||
#define SERIAL_LINK_RECEIVER_FROM_BATTERY_H
|
#define SERIAL_LINK_RECEIVER_FROM_BATTERY_H
|
||||||
|
|
||||||
|
#define BATTERY_SELECTED
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
|
@ -10,31 +12,24 @@
|
||||||
|
|
||||||
// https://github.com/mackelec/SerialDataLink
|
// https://github.com/mackelec/SerialDataLink
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE \
|
// These parameters need to be mapped on the battery side
|
||||||
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
// These parameters need to be mapped for the inverter
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t capacity_Wh; //Wh, 0-60000
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t max_target_discharge_power; //W, 0-60000
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
extern uint16_t max_target_charge_power; //W, 0-60000
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint8_t bms_status; //Enum, 0-5
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint8_t bms_char_dis_status; //Enum, 0-2
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern bool system_LFP_Chemistry; //Set to true or false depending on cell chemistry
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
|
||||||
extern uint8_t LEDcolor; //Enum, 0-10
|
|
||||||
|
|
||||||
extern bool LFP_Chemistry;
|
|
||||||
extern uint16_t CANerror;
|
|
||||||
|
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
||||||
|
|
||||||
// Parameters to send to the transmitter
|
// Parameters to send to the transmitter
|
||||||
|
@ -42,5 +37,6 @@ extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
||||||
|
|
||||||
void manageSerialLinkReceiver();
|
void manageSerialLinkReceiver();
|
||||||
void update_values_serial_link();
|
void update_values_serial_link();
|
||||||
|
void setup_battery(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#include "TESLA-MODEL-3-BATTERY.h"
|
#include "BATTERIES.h"
|
||||||
|
#ifdef TESLA_MODEL_3_BATTERY
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
#include "TESLA-MODEL-3-BATTERY.h"
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
/* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */
|
/* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */
|
||||||
|
@ -48,17 +50,16 @@ static uint16_t regenerative_limit = 0;
|
||||||
static uint16_t discharge_limit = 0;
|
static uint16_t discharge_limit = 0;
|
||||||
static uint16_t max_heat_park = 0;
|
static uint16_t max_heat_park = 0;
|
||||||
static uint16_t hvac_max_power = 0;
|
static uint16_t hvac_max_power = 0;
|
||||||
static uint16_t min_voltage = 0;
|
|
||||||
static uint16_t max_discharge_current = 0;
|
static uint16_t max_discharge_current = 0;
|
||||||
static uint16_t max_charge_current = 0;
|
static uint16_t max_charge_current = 0;
|
||||||
static uint16_t max_voltage = 0;
|
static uint16_t bms_max_voltage = 0;
|
||||||
|
static uint16_t bms_min_voltage = 0;
|
||||||
static uint16_t high_voltage = 0;
|
static uint16_t high_voltage = 0;
|
||||||
static uint16_t low_voltage = 0;
|
static uint16_t low_voltage = 0;
|
||||||
static uint16_t output_current = 0;
|
static uint16_t output_current = 0;
|
||||||
static uint16_t soc_min = 0;
|
static uint16_t soc_min = 0;
|
||||||
static uint16_t soc_max = 0;
|
static uint16_t soc_max = 0;
|
||||||
static uint16_t soc_vi = 0;
|
static uint16_t soc_vi = 0;
|
||||||
static uint16_t soc_calculated = 0;
|
|
||||||
static uint16_t soc_ave = 0;
|
static uint16_t soc_ave = 0;
|
||||||
static uint16_t cell_max_v = 3700;
|
static uint16_t cell_max_v = 3700;
|
||||||
static uint16_t cell_min_v = 3700;
|
static uint16_t cell_min_v = 3700;
|
||||||
|
@ -151,117 +152,119 @@ static const char* hvilStatusState[] = {"NOT OK",
|
||||||
"UNKNOWN(14)",
|
"UNKNOWN(14)",
|
||||||
"UNKNOWN(15)"};
|
"UNKNOWN(15)"};
|
||||||
|
|
||||||
#define MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to inverter
|
|
||||||
#define MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to inverter
|
|
||||||
#define MAX_CELL_VOLTAGE_NCA_NCM 4250 //Battery is put into emergency stop if one cell goes over this value
|
#define MAX_CELL_VOLTAGE_NCA_NCM 4250 //Battery is put into emergency stop if one cell goes over this value
|
||||||
#define MIN_CELL_VOLTAGE_NCA_NCM 2950 //Battery is put into emergency stop if one cell goes below this value
|
#define MIN_CELL_VOLTAGE_NCA_NCM 2950 //Battery is put into emergency stop if one cell goes below this value
|
||||||
#define MAX_CELL_DEVIATION_NCA_NCM 500 //LED turns yellow on the board if mv delta exceeds this value
|
#define MAX_CELL_DEVIATION_NCA_NCM 500 //LED turns yellow on the board if mv delta exceeds this value
|
||||||
|
|
||||||
#define MAX_CELL_VOLTAGE_LFP 3500 //Battery is put into emergency stop if one cell goes over this value
|
#define MAX_CELL_VOLTAGE_LFP 3520 //Battery is put into emergency stop if one cell goes over this value
|
||||||
#define MIN_CELL_VOLTAGE_LFP 2800 //Battery is put into emergency stop if one cell goes below this value
|
#define MIN_CELL_VOLTAGE_LFP 2800 //Battery is put into emergency stop if one cell goes below this value
|
||||||
#define MAX_CELL_DEVIATION_LFP 150 //LED turns yellow on the board if mv delta exceeds this value
|
#define MAX_CELL_DEVIATION_LFP 150 //LED turns yellow on the board if mv delta exceeds this value
|
||||||
|
|
||||||
#define REASONABLE_ENERGYAMOUNT 20 //When the BMS stops making sense on some values, they are always <20
|
#define REASONABLE_ENERGYAMOUNT 20 //When the BMS stops making sense on some values, they are always <20
|
||||||
|
|
||||||
void update_values_tesla_model_3_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
//After values are mapped, we perform some safety checks, and do some serial printouts
|
//After values are mapped, we perform some safety checks, and do some serial printouts
|
||||||
//Calculate the SOH% to send to inverter
|
//Calculate the SOH% to send to inverter
|
||||||
if (bat_beginning_of_life != 0) { //div/0 safeguard
|
if (bat_beginning_of_life != 0) { //div/0 safeguard
|
||||||
StateOfHealth =
|
system_SOH_pptt =
|
||||||
static_cast<uint16_t>((static_cast<double>(nominal_full_pack_energy) / bat_beginning_of_life) * 10000.0);
|
static_cast<uint16_t>((static_cast<double>(nominal_full_pack_energy) / bat_beginning_of_life) * 10000.0);
|
||||||
}
|
}
|
||||||
|
//If the calculation went wrong, set SOH to 100%
|
||||||
|
if (system_SOH_pptt > 10000) {
|
||||||
|
system_SOH_pptt = 10000;
|
||||||
|
}
|
||||||
//If the value is unavailable, set SOH to 99%
|
//If the value is unavailable, set SOH to 99%
|
||||||
if (nominal_full_pack_energy < REASONABLE_ENERGYAMOUNT) {
|
if (nominal_full_pack_energy < REASONABLE_ENERGYAMOUNT) {
|
||||||
StateOfHealth = 9900;
|
system_SOH_pptt = 9900;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Calculate the SOC% value to send to inverter
|
system_real_SOC_pptt = (soc_vi * 10); //increase SOC range from 0-100.0 -> 100.00
|
||||||
soc_calculated = soc_vi;
|
|
||||||
soc_calculated = MIN_SOC + (MAX_SOC - MIN_SOC) * (soc_calculated - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
|
||||||
if (soc_calculated < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Inverter as 0%
|
|
||||||
soc_calculated = 0;
|
|
||||||
}
|
|
||||||
if (soc_calculated > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Inverter as 100%
|
|
||||||
soc_calculated = 1000;
|
|
||||||
}
|
|
||||||
SOC = (soc_calculated * 10); //increase SOC range from 0-100.0 -> 100.00
|
|
||||||
|
|
||||||
battery_voltage = (volts * 10); //One more decimal needed (370 -> 3700)
|
system_battery_voltage_dV = (volts * 10); //One more decimal needed (370 -> 3700)
|
||||||
|
|
||||||
battery_current = convert2unsignedInt16(amps); //13.0A
|
system_battery_current_dA = amps; //13.0A
|
||||||
|
|
||||||
capacity_Wh = BATTERY_WH_MAX; //Use the configured value to avoid overflows
|
system_capacity_Wh = BATTERY_WH_MAX; //Use the configured value to avoid overflows
|
||||||
|
|
||||||
//Calculate the remaining Wh amount from SOC% and max Wh value.
|
//Calculate the remaining Wh amount from SOC% and max Wh value.
|
||||||
remaining_capacity_Wh = static_cast<uint16_t>((static_cast<double>(SOC) / 10000) * BATTERY_WH_MAX);
|
system_remaining_capacity_Wh =
|
||||||
|
static_cast<uint32_t>((static_cast<double>(system_real_SOC_pptt) / 10000) * BATTERY_WH_MAX);
|
||||||
|
|
||||||
// Define the allowed discharge power
|
// Define the allowed discharge power
|
||||||
max_target_discharge_power = (max_discharge_current * volts);
|
system_max_discharge_power_W = (max_discharge_current * volts);
|
||||||
// Cap the allowed discharge power if battery is empty, or discharge power is higher than the maximum discharge power allowed
|
// Cap the allowed discharge power if battery is empty, or discharge power is higher than the maximum discharge power allowed
|
||||||
if (SOC == 0) {
|
if (system_scaled_SOC_pptt == 0) {
|
||||||
max_target_discharge_power = 0;
|
system_max_discharge_power_W = 0;
|
||||||
} else if (max_target_discharge_power > MAXDISCHARGEPOWERALLOWED) {
|
} else if (system_max_discharge_power_W > MAXDISCHARGEPOWERALLOWED) {
|
||||||
max_target_discharge_power = MAXDISCHARGEPOWERALLOWED;
|
system_max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
//The allowed charge power behaves strangely. We instead estimate this value
|
//The allowed charge power behaves strangely. We instead estimate this value
|
||||||
if (SOC == 10000) { // When scaled SOC is 100%, set allowed charge power to 0
|
if (system_scaled_SOC_pptt == 10000) { // When scaled SOC is 100%, set allowed charge power to 0
|
||||||
max_target_charge_power = 0;
|
system_max_charge_power_W = 0;
|
||||||
} else if (soc_vi > 950) { // When real SOC is between 95-99.99%, ramp the value between Max<->0
|
} else if (soc_vi > 950) { // When real SOC is between 95-99.99%, ramp the value between Max<->0
|
||||||
max_target_charge_power = MAXCHARGEPOWERALLOWED * (1 - (soc_vi - 950) / 50.0);
|
system_max_charge_power_W = MAXCHARGEPOWERALLOWED * (1 - (soc_vi - 950) / 50.0);
|
||||||
} else { // No limits, max charging power allowed
|
} else { // No limits, max charging power allowed
|
||||||
max_target_charge_power = MAXCHARGEPOWERALLOWED;
|
system_max_charge_power_W = MAXCHARGEPOWERALLOWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
power = ((volts / 10) * amps);
|
power = ((volts / 10) * amps);
|
||||||
stat_batt_power = convert2unsignedInt16(power);
|
system_active_power_W = power;
|
||||||
|
|
||||||
temperature_min = convert2unsignedInt16(min_temp);
|
system_temperature_min_dC = min_temp;
|
||||||
|
|
||||||
temperature_max = convert2unsignedInt16(max_temp);
|
system_temperature_max_dC = max_temp;
|
||||||
|
|
||||||
cell_max_voltage = cell_max_v;
|
system_cell_max_voltage_mV = cell_max_v;
|
||||||
|
|
||||||
cell_min_voltage = cell_min_v;
|
system_cell_min_voltage_mV = cell_min_v;
|
||||||
|
|
||||||
/* Value mapping is completed. Start to check all safeties */
|
/* Value mapping is completed. Start to check all safeties */
|
||||||
|
|
||||||
bms_status = ACTIVE; //Startout in active mode before checking if we have any faults
|
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if (!stillAliveCAN) {
|
if (!stillAliveCAN) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_CAN_RX_FAILURE, 0);
|
||||||
Serial.println("ERROR: No CAN communication detected for 60s. Shutting down battery control.");
|
|
||||||
set_event(EVENT_CAN_FAILURE, 0);
|
|
||||||
} else {
|
} else {
|
||||||
stillAliveCAN--;
|
stillAliveCAN--;
|
||||||
|
clear_event(EVENT_CAN_RX_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hvil_status == 3) { //INTERNAL_OPEN_FAULT - Someone disconnected a high voltage cable while battery was in use
|
if (hvil_status == 3) { //INTERNAL_OPEN_FAULT - Someone disconnected a high voltage cable while battery was in use
|
||||||
bms_status = FAULT;
|
|
||||||
Serial.println("ERROR: High voltage cable removed while battery running. Opening contactors!");
|
|
||||||
set_event(EVENT_INTERNAL_OPEN_FAULT, 0);
|
set_event(EVENT_INTERNAL_OPEN_FAULT, 0);
|
||||||
|
} else {
|
||||||
|
clear_event(EVENT_INTERNAL_OPEN_FAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
cell_deviation_mV = (cell_max_v - cell_min_v);
|
cell_deviation_mV = (cell_max_v - cell_min_v);
|
||||||
|
|
||||||
//Determine which chemistry battery pack is using (crude method, TODO: replace with real CAN data later)
|
//Determine which chemistry battery pack is using (crude method, TODO: replace with real CAN identifier later)
|
||||||
if (soc_vi > 900) { //When SOC% is over 90.0%, we can use max cell voltage to estimate what chemistry is used
|
if (soc_vi > 900) { //When SOC% is over 90.0%, we can use max cell voltage to estimate what chemistry is used
|
||||||
if (cell_max_v < 3450) {
|
if (cell_max_v < 3450) {
|
||||||
LFP_Chemistry = true;
|
system_LFP_Chemistry = true;
|
||||||
}
|
}
|
||||||
if (cell_max_v > 3700) {
|
if (cell_max_v > 3700) {
|
||||||
LFP_Chemistry = false;
|
system_LFP_Chemistry = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// An even better way is to check how many cells are in the pack. NCM/A batteries have 96s, LFP has 102-106s
|
||||||
|
if (system_number_of_cells > 101) {
|
||||||
|
system_LFP_Chemistry = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Once cell chemistry is determined, set maximum and minimum total pack voltage safety limits
|
||||||
|
if (system_LFP_Chemistry) {
|
||||||
|
system_max_design_voltage_dV = 3880;
|
||||||
|
system_min_design_voltage_dV = 2968;
|
||||||
|
} else { // NCM/A chemistry
|
||||||
|
system_max_design_voltage_dV = 4030;
|
||||||
|
system_min_design_voltage_dV = 3100;
|
||||||
|
}
|
||||||
|
|
||||||
//Check if SOC% is plausible
|
//Check if SOC% is plausible
|
||||||
if (battery_voltage >
|
if (system_battery_voltage_dV >
|
||||||
(ABSOLUTE_MAX_VOLTAGE - 100)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
|
(system_max_design_voltage_dV - 20)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
|
||||||
if (SOC < 6500) { //When SOC is less than 65.00% when approaching max voltage
|
if (system_real_SOC_pptt < 5000) { //When SOC is less than 50.00% when approaching max voltage
|
||||||
bms_status = FAULT;
|
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, system_real_SOC_pptt / 100);
|
||||||
Serial.println("ERROR: SOC% reported by battery not plausible. Restart battery!");
|
|
||||||
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, SOC / 100);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,49 +273,40 @@ void update_values_tesla_model_3_battery() { //This function maps all the value
|
||||||
Serial.println("Warning: kWh remaining " + String(nominal_full_pack_energy) +
|
Serial.println("Warning: kWh remaining " + String(nominal_full_pack_energy) +
|
||||||
" reported by battery not plausible. Battery needs cycling.");
|
" reported by battery not plausible. Battery needs cycling.");
|
||||||
set_event(EVENT_KWH_PLAUSIBILITY_ERROR, nominal_full_pack_energy);
|
set_event(EVENT_KWH_PLAUSIBILITY_ERROR, nominal_full_pack_energy);
|
||||||
LEDcolor = YELLOW;
|
|
||||||
} else if (nominal_full_pack_energy <= 1) {
|
} else if (nominal_full_pack_energy <= 1) {
|
||||||
Serial.println("Info: kWh remaining battery is not reporting kWh remaining.");
|
Serial.println("Info: kWh remaining battery is not reporting kWh remaining.");
|
||||||
set_event(EVENT_KWH_PLAUSIBILITY_ERROR, nominal_full_pack_energy);
|
set_event(EVENT_KWH_PLAUSIBILITY_ERROR, nominal_full_pack_energy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LFP_Chemistry) { //LFP limits used for voltage safeties
|
if (system_LFP_Chemistry) { //LFP limits used for voltage safeties
|
||||||
if (cell_max_v >= MAX_CELL_VOLTAGE_LFP) {
|
if (cell_max_v >= MAX_CELL_VOLTAGE_LFP) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_CELL_OVER_VOLTAGE, (cell_max_v - MAX_CELL_VOLTAGE_LFP));
|
||||||
Serial.println("ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
|
||||||
}
|
}
|
||||||
if (cell_min_v <= MIN_CELL_VOLTAGE_LFP) {
|
if (cell_min_v <= MIN_CELL_VOLTAGE_LFP) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_CELL_UNDER_VOLTAGE, (MIN_CELL_VOLTAGE_LFP - cell_min_v));
|
||||||
Serial.println("ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
|
||||||
}
|
}
|
||||||
if (cell_deviation_mV > MAX_CELL_DEVIATION_LFP) {
|
if (cell_deviation_mV > MAX_CELL_DEVIATION_LFP) {
|
||||||
LEDcolor = YELLOW;
|
set_event(EVENT_CELL_DEVIATION_HIGH, cell_deviation_mV);
|
||||||
Serial.println("ERROR: HIGH CELL DEVIATION!!! Inspect battery!");
|
} else {
|
||||||
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
|
clear_event(EVENT_CELL_DEVIATION_HIGH);
|
||||||
}
|
}
|
||||||
} else { //NCA/NCM limits used
|
} else { //NCA/NCM limits used
|
||||||
if (cell_max_v >= MAX_CELL_VOLTAGE_NCA_NCM) {
|
if (cell_max_v >= MAX_CELL_VOLTAGE_NCA_NCM) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_CELL_OVER_VOLTAGE, (cell_max_v - MAX_CELL_VOLTAGE_NCA_NCM));
|
||||||
Serial.println("ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
|
||||||
}
|
}
|
||||||
if (cell_min_v <= MIN_CELL_VOLTAGE_NCA_NCM) {
|
if (cell_min_v <= MIN_CELL_VOLTAGE_NCA_NCM) {
|
||||||
bms_status = FAULT;
|
set_event(EVENT_CELL_UNDER_VOLTAGE, (MIN_CELL_VOLTAGE_NCA_NCM - cell_min_v));
|
||||||
Serial.println("ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
|
||||||
}
|
}
|
||||||
if (cell_deviation_mV > MAX_CELL_DEVIATION_NCA_NCM) {
|
if (cell_deviation_mV > MAX_CELL_DEVIATION_NCA_NCM) {
|
||||||
LEDcolor = YELLOW;
|
set_event(EVENT_CELL_DEVIATION_HIGH, cell_deviation_mV);
|
||||||
Serial.println("ERROR: HIGH CELL DEVIATION!!! Inspect battery!");
|
} else {
|
||||||
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
|
clear_event(EVENT_CELL_DEVIATION_HIGH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
|
if (system_bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
|
||||||
max_target_charge_power = 0;
|
system_max_charge_power_W = 0;
|
||||||
max_target_discharge_power = 0;
|
system_max_discharge_power_W = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Safeties verified. Perform USB serial printout if configured to do so */
|
/* Safeties verified. Perform USB serial printout if configured to do so */
|
||||||
|
@ -346,7 +340,7 @@ void update_values_tesla_model_3_battery() { //This function maps all the value
|
||||||
Serial.print("YES, ");
|
Serial.print("YES, ");
|
||||||
else
|
else
|
||||||
Serial.print("NO, ");
|
Serial.print("NO, ");
|
||||||
if (LFP_Chemistry) {
|
if (system_LFP_Chemistry) {
|
||||||
Serial.print("LFP chemistry detected!");
|
Serial.print("LFP chemistry detected!");
|
||||||
}
|
}
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
|
@ -370,19 +364,19 @@ void update_values_tesla_model_3_battery() { //This function maps all the value
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
|
|
||||||
Serial.println("Values passed to the inverter: ");
|
Serial.println("Values passed to the inverter: ");
|
||||||
print_SOC(" SOC: ", SOC);
|
print_SOC(" SOC: ", system_scaled_SOC_pptt);
|
||||||
print_int_with_units(" Max discharge power: ", max_target_discharge_power, "W");
|
print_int_with_units(" Max discharge power: ", system_max_discharge_power_W, "W");
|
||||||
Serial.print(", ");
|
Serial.print(", ");
|
||||||
print_int_with_units(" Max charge power: ", max_target_charge_power, "W");
|
print_int_with_units(" Max charge power: ", system_max_charge_power_W, "W");
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
print_int_with_units(" Max temperature: ", ((int16_t)temperature_max * 0.1), "°C");
|
print_int_with_units(" Max temperature: ", ((int16_t)system_temperature_min_dC * 0.1), "°C");
|
||||||
Serial.print(", ");
|
Serial.print(", ");
|
||||||
print_int_with_units(" Min temperature: ", ((int16_t)temperature_min * 0.1), "°C");
|
print_int_with_units(" Min temperature: ", ((int16_t)system_temperature_max_dC * 0.1), "°C");
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame) {
|
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
static int mux = 0;
|
static int mux = 0;
|
||||||
static int temp = 0;
|
static int temp = 0;
|
||||||
|
|
||||||
|
@ -473,11 +467,11 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame) {
|
||||||
{
|
{
|
||||||
// Example, frame3=0x89,frame2=0x1D = 35101 / 10 = 3510mV
|
// Example, frame3=0x89,frame2=0x1D = 35101 / 10 = 3510mV
|
||||||
volts = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) / 10;
|
volts = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) / 10;
|
||||||
cellvoltages[mux * 3] = volts;
|
system_cellvoltages_mV[mux * 3] = volts;
|
||||||
volts = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) / 10;
|
volts = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) / 10;
|
||||||
cellvoltages[1 + mux * 3] = volts;
|
system_cellvoltages_mV[1 + mux * 3] = volts;
|
||||||
volts = ((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) / 10;
|
volts = ((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) / 10;
|
||||||
cellvoltages[2 + mux * 3] = volts;
|
system_cellvoltages_mV[2 + mux * 3] = volts;
|
||||||
|
|
||||||
// Track the max value of mux. If we've seen two 0 values for mux, we've probably gathered all
|
// Track the max value of mux. If we've seen two 0 values for mux, we've probably gathered all
|
||||||
// cell voltages. Then, 2 + mux_max * 3 + 1 is the number of cell voltages.
|
// cell voltages. Then, 2 + mux_max * 3 + 1 is the number of cell voltages.
|
||||||
|
@ -486,7 +480,7 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame) {
|
||||||
mux_zero_counter++;
|
mux_zero_counter++;
|
||||||
if (mux_zero_counter == 2u) {
|
if (mux_zero_counter == 2u) {
|
||||||
// The max index will be 2 + mux_max * 3 (see above), so "+ 1" for the number of cells
|
// The max index will be 2 + mux_max * 3 (see above), so "+ 1" for the number of cells
|
||||||
nof_cellvoltages = 2 + 3 * mux_max + 1;
|
system_number_of_cells = 2 + 3 * mux_max + 1;
|
||||||
// Increase the counter arbitrarily another time to make the initial if-statement evaluate to false
|
// Increase the counter arbitrarily another time to make the initial if-statement evaluate to false
|
||||||
mux_zero_counter++;
|
mux_zero_counter++;
|
||||||
}
|
}
|
||||||
|
@ -495,8 +489,10 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame) {
|
||||||
break;
|
break;
|
||||||
case 0x2d2:
|
case 0x2d2:
|
||||||
//Min / max limits
|
//Min / max limits
|
||||||
min_voltage = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.01 * 2; //Example 24148mv * 0.01 = 241.48 V
|
bms_min_voltage =
|
||||||
max_voltage = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) * 0.01 * 2; //Example 40282mv * 0.01 = 402.82 V
|
((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.01 * 2; //Example 24148mv * 0.01 = 241.48 V
|
||||||
|
bms_max_voltage =
|
||||||
|
((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) * 0.01 * 2; //Example 40282mv * 0.01 = 402.82 V
|
||||||
max_charge_current =
|
max_charge_current =
|
||||||
(((rx_frame.data.u8[5] & 0x3F) << 8) | rx_frame.data.u8[4]) * 0.1; //Example 1301? * 0.1 = 130.1?
|
(((rx_frame.data.u8[5] & 0x3F) << 8) | rx_frame.data.u8[4]) * 0.1; //Example 1301? * 0.1 = 130.1?
|
||||||
max_discharge_current =
|
max_discharge_current =
|
||||||
|
@ -572,7 +568,7 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_tesla_model_3_battery() {
|
void send_can_battery() {
|
||||||
/*From bielec: My fist 221 message, to close the contactors is 0x41, 0x11, 0x01, 0x00, 0x00, 0x00, 0x20, 0x96 and then,
|
/*From bielec: My fist 221 message, to close the contactors is 0x41, 0x11, 0x01, 0x00, 0x00, 0x00, 0x20, 0x96 and then,
|
||||||
to cause "hv_up_for_drive" I send an additional 221 message 0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA so
|
to cause "hv_up_for_drive" I send an additional 221 message 0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA so
|
||||||
two 221 messages are being continuously transmitted. When I want to shut down, I stop the second message and only send
|
two 221 messages are being continuously transmitted. When I want to shut down, I stop the second message and only send
|
||||||
|
@ -584,12 +580,12 @@ the first, for a few cycles, then stop all messages which causes the contactor
|
||||||
previousMillis30 = currentMillis;
|
previousMillis30 = currentMillis;
|
||||||
|
|
||||||
if (inverterAllowsContactorClosing == 1) {
|
if (inverterAllowsContactorClosing == 1) {
|
||||||
if (bms_status == ACTIVE) {
|
if (system_bms_status == ACTIVE) {
|
||||||
send221still = 50;
|
send221still = 50;
|
||||||
batteryAllowsContactorClosing = true;
|
batteryAllowsContactorClosing = true;
|
||||||
ESP32Can.CANWriteFrame(&TESLA_221_1);
|
ESP32Can.CANWriteFrame(&TESLA_221_1);
|
||||||
ESP32Can.CANWriteFrame(&TESLA_221_2);
|
ESP32Can.CANWriteFrame(&TESLA_221_2);
|
||||||
} else { //bms_status == FAULT or inverter requested opening contactors
|
} else { //system_bms_status == FAULT or inverter requested opening contactors
|
||||||
if (send221still > 0) {
|
if (send221still > 0) {
|
||||||
batteryAllowsContactorClosing = false;
|
batteryAllowsContactorClosing = false;
|
||||||
ESP32Can.CANWriteFrame(&TESLA_221_1);
|
ESP32Can.CANWriteFrame(&TESLA_221_1);
|
||||||
|
@ -599,13 +595,6 @@ the first, for a few cycles, then stop all messages which causes the contactor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint16_t convert2unsignedInt16(int16_t signed_value) {
|
|
||||||
if (signed_value < 0) {
|
|
||||||
return (65535 + signed_value);
|
|
||||||
} else {
|
|
||||||
return (uint16_t)signed_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_int_with_units(char* header, int value, char* units) {
|
void print_int_with_units(char* header, int value, char* units) {
|
||||||
Serial.print(header);
|
Serial.print(header);
|
||||||
|
@ -697,3 +686,12 @@ void printDebugIfActive(uint8_t symbol, const char* message) {
|
||||||
Serial.println(message);
|
Serial.println(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
|
Serial.println("Tesla Model 3 battery selected");
|
||||||
|
|
||||||
|
system_max_design_voltage_dV = 4030; // 403.0V, over this, charging is not possible (goes into forced discharge)
|
||||||
|
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -5,43 +5,40 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE \
|
#define BATTERY_SELECTED
|
||||||
4030 // 403.0V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 2450 // 245.0V if battery voltage goes under this, discharging further is disabled
|
|
||||||
#define MAXCHARGEPOWERALLOWED 15000 // 15000W we use a define since the value supplied by Tesla is always 0
|
#define MAXCHARGEPOWERALLOWED 15000 // 15000W we use a define since the value supplied by Tesla is always 0
|
||||||
#define MAXDISCHARGEPOWERALLOWED \
|
#define MAXDISCHARGEPOWERALLOWED \
|
||||||
60000 // 60000W we need to cap this value to max 60kW, most inverters overflow otherwise
|
60000 // 60000W we need to cap this value to max 60kW, most inverters overflow otherwise
|
||||||
|
|
||||||
// These parameters need to be mapped for the Inverter
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh; //Wh, 0-60000
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power; //W, 0-60000
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power; //W, 0-60000
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status; //Enum, 0-5
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status; //Enum, 0-2
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery.
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
extern uint8_t LEDcolor; //Enum, 0-10
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
||||||
extern bool LFP_Chemistry;
|
extern bool system_LFP_Chemistry; //Bool, 1=true, 0=false
|
||||||
|
|
||||||
void update_values_tesla_model_3_battery();
|
|
||||||
void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame);
|
|
||||||
void send_can_tesla_model_3_battery();
|
|
||||||
void printFaultCodesIfActive();
|
void printFaultCodesIfActive();
|
||||||
void printDebugIfActive(uint8_t symbol, const char* message);
|
void printDebugIfActive(uint8_t symbol, const char* message);
|
||||||
void print_int_with_units(char* header, int value, char* units);
|
void print_int_with_units(char* header, int value, char* units);
|
||||||
void print_SOC(char* header, int SOC);
|
void print_SOC(char* header, int SOC);
|
||||||
uint16_t convert2unsignedInt16(int16_t signed_value);
|
void setup_battery(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "TEST-FAKE-BATTERY.h"
|
#include "BATTERIES.h"
|
||||||
|
#ifdef TEST_FAKE_BATTERY
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
#include "TEST-FAKE-BATTERY.h"
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send
|
static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send
|
||||||
|
@ -16,58 +18,54 @@ void print_units(char* header, int value, char* units) {
|
||||||
Serial.print(units);
|
Serial.print(units);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_values_test_battery() { /* This function puts fake values onto the parameters sent towards the inverter */
|
void update_values_battery() { /* This function puts fake values onto the parameters sent towards the inverter */
|
||||||
bms_status = ACTIVE; //Always be in Active mode
|
system_real_SOC_pptt = 5000; // 50.00%
|
||||||
|
|
||||||
LEDcolor = TEST_ALL_COLORS; // Cycle the LED thru all available colors
|
system_SOH_pptt = 9900; // 99.00%
|
||||||
|
|
||||||
SOC = 5000; // 50.00%
|
//system_battery_voltage_dV = 3700; // 370.0V , value set in startup in .ino file, editable via webUI
|
||||||
|
|
||||||
StateOfHealth = 9900; // 99.00%
|
system_battery_current_dA = 0; // 0 A
|
||||||
|
|
||||||
//battery_voltage = 3700; // 370.0V , value set in startup in .ino file, editable via webUI
|
system_capacity_Wh = 30000; // 30kWh
|
||||||
|
|
||||||
battery_current = 0; // 0 A
|
system_remaining_capacity_Wh = 15000; // 15kWh
|
||||||
|
|
||||||
capacity_Wh = 30000; // 30kWh
|
system_cell_max_voltage_mV = 3596;
|
||||||
|
|
||||||
remaining_capacity_Wh = 15000; // 15kWh
|
system_cell_min_voltage_mV = 3500;
|
||||||
|
|
||||||
cell_max_voltage = 3596;
|
system_active_power_W = 0; // 0W
|
||||||
|
|
||||||
cell_min_voltage = 3500;
|
system_temperature_min_dC = 50; // 5.0*C
|
||||||
|
|
||||||
stat_batt_power = 0; // 0W
|
system_temperature_max_dC = 60; // 6.0*C
|
||||||
|
|
||||||
temperature_min = 50; // 5.0*C
|
system_max_discharge_power_W = 5000; // 5kW
|
||||||
|
|
||||||
temperature_max = 60; // 6.0*C
|
system_max_charge_power_W = 5000; // 5kW
|
||||||
|
|
||||||
max_target_discharge_power = 5000; // 5kW
|
|
||||||
|
|
||||||
max_target_charge_power = 5000; // 5kW
|
|
||||||
|
|
||||||
for (int i = 0; i < 97; ++i) {
|
for (int i = 0; i < 97; ++i) {
|
||||||
cellvoltages[i] = 3500 + i;
|
system_cellvoltages_mV[i] = 3500 + i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Finally print out values to serial if configured to do so*/
|
/*Finally print out values to serial if configured to do so*/
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.println("FAKE Values going to inverter");
|
Serial.println("FAKE Values going to inverter");
|
||||||
print_units("SOH%: ", (StateOfHealth * 0.01), "% ");
|
print_units("SOH%: ", (system_SOH_pptt * 0.01), "% ");
|
||||||
print_units(", SOC%: ", (SOC * 0.01), "% ");
|
print_units(", SOC%: ", (system_scaled_SOC_pptt * 0.01), "% ");
|
||||||
print_units(", Voltage: ", (battery_voltage * 0.1), "V ");
|
print_units(", Voltage: ", (system_battery_voltage_dV * 0.1), "V ");
|
||||||
print_units(", Max discharge power: ", max_target_discharge_power, "W ");
|
print_units(", Max discharge power: ", system_max_discharge_power_W, "W ");
|
||||||
print_units(", Max charge power: ", max_target_charge_power, "W ");
|
print_units(", Max charge power: ", system_max_charge_power_W, "W ");
|
||||||
print_units(", Max temp: ", (temperature_max * 0.1), "°C ");
|
print_units(", Max temp: ", (system_temperature_max_dC * 0.1), "°C ");
|
||||||
print_units(", Min temp: ", (temperature_min * 0.1), "°C ");
|
print_units(", Min temp: ", (system_temperature_min_dC * 0.1), "°C ");
|
||||||
print_units(", Max cell voltage: ", cell_max_voltage, "mV ");
|
print_units(", Max cell voltage: ", system_cell_max_voltage_mV, "mV ");
|
||||||
print_units(", Min cell voltage: ", cell_min_voltage, "mV ");
|
print_units(", Min cell voltage: ", system_cell_min_voltage_mV, "mV ");
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_test_battery(CAN_frame_t rx_frame) {
|
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
switch (rx_frame.MsgID) {
|
switch (rx_frame.MsgID) {
|
||||||
case 0xABC:
|
case 0xABC:
|
||||||
break;
|
break;
|
||||||
|
@ -75,7 +73,7 @@ void receive_can_test_battery(CAN_frame_t rx_frame) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_test_battery() {
|
void send_can_battery() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100) {
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
|
@ -83,3 +81,12 @@ void send_can_test_battery() {
|
||||||
// Put fake messages here incase you want to test sending CAN
|
// Put fake messages here incase you want to test sending CAN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
|
Serial.println("Test mode with fake battery selected");
|
||||||
|
|
||||||
|
system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
|
||||||
|
system_min_design_voltage_dV = 2450; // 245.0V under this, discharging further is disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -5,33 +5,31 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE \
|
#define BATTERY_SELECTED
|
||||||
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
|
||||||
|
|
||||||
// These parameters need to be mapped for the inverter
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh; //Wh, 0-60000
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power; //W, 0-60000
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power; //W, 0-60000
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status; //Enum, 0-5
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status; //Enum, 0-2
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t cellvoltages[120]; //mV 0-5000 per cell
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
extern uint8_t LEDcolor; //Enum, 0-10
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
extern bool inverterAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
void update_values_test_battery();
|
void setup_battery(void);
|
||||||
void receive_can_test_battery(CAN_frame_t rx_frame);
|
|
||||||
void send_can_test_battery();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -227,12 +227,12 @@ void send_can_nissanleaf_charger() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if actual battery_voltage is less than setpoint got to max power set from web ui
|
// if actual battery_voltage is less than setpoint got to max power set from web ui
|
||||||
if (battery_voltage < (CHARGER_SET_HV * 10)) { //battery_voltage = V+1, 0-500.0 (0-5000)
|
if (system_battery_voltage_dV < (CHARGER_SET_HV * 10)) { //system_battery_voltage_dV = V+1, 0-500.0 (0-5000)
|
||||||
OBCpower = OBCpowerSetpoint;
|
OBCpower = OBCpowerSetpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrement charger power if volt setpoint is reached
|
// decrement charger power if volt setpoint is reached
|
||||||
if (battery_voltage >= (CHARGER_SET_HV * 10)) {
|
if (system_battery_voltage_dV >= (CHARGER_SET_HV * 10)) {
|
||||||
if (OBCpower > 0x64) {
|
if (OBCpower > 0x64) {
|
||||||
OBCpower--;
|
OBCpower--;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
|
|
||||||
void send_can_nissanleaf_charger();
|
void send_can_nissanleaf_charger();
|
||||||
void receive_can_nissanleaf_charger(CAN_frame_t rx_frame);
|
void receive_can_nissanleaf_charger(CAN_frame_t rx_frame);
|
||||||
|
|
|
@ -36,11 +36,11 @@
|
||||||
#define SD_CS_PIN 13
|
#define SD_CS_PIN 13
|
||||||
#define WS2812_PIN 4
|
#define WS2812_PIN 4
|
||||||
|
|
||||||
// LED definitions for the board
|
// LED definitions for the board, in order of "priority", DONT CHANGE!
|
||||||
#define GREEN 0
|
#define GREEN 0
|
||||||
#define YELLOW 1
|
#define YELLOW 1
|
||||||
#define RED 2
|
#define BLUE 2
|
||||||
#define BLUE 3
|
#define RED 3
|
||||||
#define TEST_ALL_COLORS 10
|
#define TEST_ALL_COLORS 10
|
||||||
|
|
||||||
// Inverter definitions
|
// Inverter definitions
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include "../../../USER_SETTINGS.h"
|
#include "../../../USER_SETTINGS.h"
|
||||||
#include "../../battery/BATTERIES.h"
|
#include "../../battery/BATTERIES.h"
|
||||||
|
#include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h"
|
||||||
#include "../../lib/knolleary-pubsubclient/PubSubClient.h"
|
#include "../../lib/knolleary-pubsubclient/PubSubClient.h"
|
||||||
#include "../utils/timer.h"
|
#include "../utils/timer.h"
|
||||||
|
|
||||||
|
@ -26,84 +27,73 @@ static void publish_values(void) {
|
||||||
publish_cell_voltages();
|
publish_cell_voltages();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String generateCellVoltageAutoConfigTopic(int cell_number, const char* hostname) {
|
||||||
|
return String("homeassistant/sensor/battery-emulator_") + String(hostname) + "/cell_voltage" + String(cell_number) +
|
||||||
|
"/config";
|
||||||
|
}
|
||||||
|
|
||||||
static void publish_cell_voltages(void) {
|
static void publish_cell_voltages(void) {
|
||||||
static bool mqtt_first_transmission = true;
|
static bool mqtt_first_transmission = true;
|
||||||
|
static JsonDocument doc;
|
||||||
|
static const char* hostname = WiFi.getHostname();
|
||||||
|
static String state_topic = String("battery-emulator_") + String(hostname) + "/spec_data";
|
||||||
|
|
||||||
// If the cell voltage number isn't initialized...
|
// If the cell voltage number isn't initialized...
|
||||||
if (nof_cellvoltages == 0u) {
|
if (system_number_of_cells == 0u) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// At startup, re-post the discovery message for home assistant
|
|
||||||
if (mqtt_first_transmission == true) {
|
if (mqtt_first_transmission == true) {
|
||||||
mqtt_first_transmission = false;
|
mqtt_first_transmission = false;
|
||||||
|
|
||||||
// Base topic for any cell voltage "sensor"
|
|
||||||
String topic = "homeassistant/sensor/battery-emulator/cell_voltage";
|
String topic = "homeassistant/sensor/battery-emulator/cell_voltage";
|
||||||
for (int i = 0; i < nof_cellvoltages; i++) {
|
|
||||||
// Build JSON message with device configuration for each cell voltage
|
|
||||||
// Probably shouldn't be BatteryEmulator here, instead "LeafBattery"
|
|
||||||
// or similar but hey, it works.
|
|
||||||
// mqtt_msg is a global buffer, should be fine since we run too much
|
|
||||||
// in a single thread :)
|
|
||||||
snprintf(mqtt_msg, sizeof(mqtt_msg),
|
|
||||||
"{"
|
|
||||||
"\"device\": {"
|
|
||||||
"\"identifiers\": ["
|
|
||||||
"\"battery-emulator\""
|
|
||||||
"],"
|
|
||||||
"\"manufacturer\": \"DalaTech\","
|
|
||||||
"\"model\": \"BatteryEmulator\","
|
|
||||||
"\"name\": \"BatteryEmulator\""
|
|
||||||
"},"
|
|
||||||
"\"device_class\": \"voltage\","
|
|
||||||
"\"enabled_by_default\": true,"
|
|
||||||
"\"object_id\": \"sensor_battery_voltage_cell%d\","
|
|
||||||
"\"origin\": {"
|
|
||||||
"\"name\": \"BatteryEmulator\","
|
|
||||||
"\"sw\": \"%s-mqtt\","
|
|
||||||
"\"url\": \"https://github.com/dalathegreat/Battery-Emulator\""
|
|
||||||
"},"
|
|
||||||
"\"state_class\": \"measurement\","
|
|
||||||
"\"name\": \"Battery Cell Voltage %d\","
|
|
||||||
"\"state_topic\": \"battery-emulator/spec_data\","
|
|
||||||
"\"unique_id\": \"battery-emulator_battery_voltage_cell%d\","
|
|
||||||
"\"unit_of_measurement\": \"V\","
|
|
||||||
"\"value_template\": \"{{ value_json.cell_voltages[%d] }}\""
|
|
||||||
"}",
|
|
||||||
i + 1, version_number, i + 1, i + 1, i);
|
|
||||||
// End each discovery topic with cell number and '/config'
|
|
||||||
String cell_topic = topic + String(i + 1) + "/config";
|
|
||||||
mqtt_publish_retain(cell_topic.c_str());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Every 5-ish seconds, build the JSON payload for the state topic. This requires
|
|
||||||
// some annoying formatting due to C++ not having nice Python-like string formatting.
|
|
||||||
// msg_length is a cumulative variable to track start position (param 1) and for
|
|
||||||
// modifying the maxiumum amount of characters to write (param 2). The third parameter
|
|
||||||
// is the string content
|
|
||||||
|
|
||||||
|
for (int i = 0; i < system_number_of_cells; i++) {
|
||||||
|
int cellNumber = i + 1;
|
||||||
|
doc["name"] = "Battery Cell Voltage " + String(cellNumber);
|
||||||
|
doc["object_id"] = "battery_voltage_cell" + String(cellNumber);
|
||||||
|
doc["unique_id"] = "battery-emulator_" + String(hostname) + "_battery_voltage_cell" +
|
||||||
|
String(cellNumber); //"battery-emulator_" + String(hostname) + "_" +
|
||||||
|
doc["device_class"] = "voltage";
|
||||||
|
doc["state_class"] = "measurement";
|
||||||
|
doc["state_topic"] = state_topic;
|
||||||
|
doc["unit_of_measurement"] = "V";
|
||||||
|
doc["enabled_by_default"] = true;
|
||||||
|
doc["expire_after"] = 240;
|
||||||
|
doc["value_template"] = "{{ value_json.cell_voltages[" + String(i) + "] }}";
|
||||||
|
doc["device"]["identifiers"][0] = "battery-emulator";
|
||||||
|
doc["device"]["manufacturer"] = "DalaTech";
|
||||||
|
doc["device"]["model"] = "BatteryEmulator";
|
||||||
|
doc["device"]["name"] = "BatteryEmulator_" + String(hostname);
|
||||||
|
doc["origin"]["name"] = "BatteryEmulator";
|
||||||
|
doc["origin"]["sw"] = String(version_number) + "-mqtt";
|
||||||
|
doc["origin"]["url"] = "https://github.com/dalathegreat/Battery-Emulator";
|
||||||
|
|
||||||
|
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
|
||||||
|
mqtt_publish(generateCellVoltageAutoConfigTopic(cellNumber, hostname).c_str(), mqtt_msg, true);
|
||||||
|
}
|
||||||
|
doc.clear(); // clear after sending autoconfig
|
||||||
|
} else {
|
||||||
// If cell voltages haven't been populated...
|
// If cell voltages haven't been populated...
|
||||||
if (nof_cellvoltages == 0u) {
|
if (system_number_of_cells == 0u) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonArray cell_voltages = doc["cell_voltages"].to<JsonArray>();
|
||||||
|
for (size_t i = 0; i < system_number_of_cells; ++i) {
|
||||||
|
cell_voltages.add(((float)system_cellvoltages_mV[i]) / 1000.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t msg_length = snprintf(mqtt_msg, sizeof(mqtt_msg), "{\n\"cell_voltages\":[");
|
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
|
||||||
for (size_t i = 0; i < nof_cellvoltages; ++i) {
|
|
||||||
msg_length += snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "%s%.3f", (i == 0) ? "" : ", ",
|
|
||||||
((float)cellvoltages[i]) / 1000);
|
|
||||||
}
|
|
||||||
snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "]\n}\n");
|
|
||||||
|
|
||||||
// Publish and print error if not OK
|
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||||
if (mqtt_publish_retain("battery-emulator/spec_data") == false) {
|
|
||||||
Serial.println("Cell voltage MQTT msg could not be sent");
|
Serial.println("Cell voltage MQTT msg could not be sent");
|
||||||
}
|
}
|
||||||
|
doc.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SensorConfig {
|
struct SensorConfig {
|
||||||
const char* object_id;
|
const char* object_id;
|
||||||
const char* topic;
|
|
||||||
const char* name;
|
const char* name;
|
||||||
const char* value_template;
|
const char* value_template;
|
||||||
const char* unit;
|
const char* unit;
|
||||||
|
@ -111,83 +101,69 @@ struct SensorConfig {
|
||||||
};
|
};
|
||||||
|
|
||||||
SensorConfig sensorConfigs[] = {
|
SensorConfig sensorConfigs[] = {
|
||||||
{"SOC", "homeassistant/sensor/battery-emulator/SOC/config", "Battery Emulator SOC", "{{ value_json.SOC }}", "%",
|
{"SOC", "Battery Emulator SOC", "{{ value_json.SOC }}", "%", "battery"},
|
||||||
"battery"},
|
{"state_of_health", "Battery Emulator State Of Health", "{{ value_json.state_of_health }}", "%", "battery"},
|
||||||
{"state_of_health", "homeassistant/sensor/battery-emulator/state_of_health/config",
|
{"temperature_min", "Battery Emulator Temperature Min", "{{ value_json.temperature_min }}", "°C", "temperature"},
|
||||||
"Battery Emulator State Of Health", "{{ value_json.state_of_health }}", "%", "battery"},
|
{"temperature_max", "Battery Emulator Temperature Max", "{{ value_json.temperature_max }}", "°C", "temperature"},
|
||||||
{"temperature_min", "homeassistant/sensor/battery-emulator/temperature_min/config",
|
{"stat_batt_power", "Battery Emulator Stat Batt Power", "{{ value_json.stat_batt_power }}", "W", "power"},
|
||||||
"Battery Emulator Temperature Min", "{{ value_json.temperature_min }}", "°C", "temperature"},
|
{"battery_current", "Battery Emulator Battery Current", "{{ value_json.battery_current }}", "A", "current"},
|
||||||
{"temperature_max", "homeassistant/sensor/battery-emulator/temperature_max/config",
|
{"cell_max_voltage", "Battery Emulator Cell Max Voltage", "{{ value_json.cell_max_voltage }}", "V", "voltage"},
|
||||||
"Battery Emulator Temperature Max", "{{ value_json.temperature_max }}", "°C", "temperature"},
|
{"cell_min_voltage", "Battery Emulator Cell Min Voltage", "{{ value_json.cell_min_voltage }}", "V", "voltage"},
|
||||||
{"stat_batt_power", "homeassistant/sensor/battery-emulator/stat_batt_power/config",
|
{"battery_voltage", "Battery Emulator Battery Voltage", "{{ value_json.battery_voltage }}", "V", "voltage"},
|
||||||
"Battery Emulator Stat Batt Power", "{{ value_json.stat_batt_power }}", "W", "power"},
|
|
||||||
{"battery_current", "homeassistant/sensor/battery-emulator/battery_current/config",
|
|
||||||
"Battery Emulator Battery Current", "{{ value_json.battery_current }}", "A", "current"},
|
|
||||||
{"cell_max_voltage", "homeassistant/sensor/battery-emulator/cell_max_voltage/config",
|
|
||||||
"Battery Emulator Cell Max Voltage", "{{ value_json.cell_max_voltage }}", "V", "voltage"},
|
|
||||||
{"cell_min_voltage", "homeassistant/sensor/battery-emulator/cell_min_voltage/config",
|
|
||||||
"Battery Emulator Cell Min Voltage", "{{ value_json.cell_min_voltage }}", "V", "voltage"},
|
|
||||||
{"battery_voltage", "homeassistant/sensor/battery-emulator/battery_voltage/config",
|
|
||||||
"Battery Emulator Battery Voltage", "{{ value_json.battery_voltage }}", "V", "voltage"},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static String generateCommonInfoAutoConfigTopic(const char* object_id, const char* hostname) {
|
||||||
|
return String("homeassistant/sensor/battery-emulator_") + String(hostname) + "/" + String(object_id) + "/config";
|
||||||
|
}
|
||||||
|
|
||||||
static void publish_common_info(void) {
|
static void publish_common_info(void) {
|
||||||
|
static JsonDocument doc;
|
||||||
static bool mqtt_first_transmission = true;
|
static bool mqtt_first_transmission = true;
|
||||||
static char* state_topic = "battery-emulator/info";
|
static const char* hostname = WiFi.getHostname();
|
||||||
|
static String state_topic = String("battery-emulator_") + String(hostname) + "/info";
|
||||||
if (mqtt_first_transmission == true) {
|
if (mqtt_first_transmission == true) {
|
||||||
mqtt_first_transmission = false;
|
mqtt_first_transmission = false;
|
||||||
for (int i = 0; i < sizeof(sensorConfigs) / sizeof(sensorConfigs[0]); i++) {
|
for (int i = 0; i < sizeof(sensorConfigs) / sizeof(sensorConfigs[0]); i++) {
|
||||||
SensorConfig& config = sensorConfigs[i];
|
SensorConfig& config = sensorConfigs[i];
|
||||||
snprintf(mqtt_msg, sizeof(mqtt_msg),
|
doc["name"] = config.name;
|
||||||
"{"
|
doc["state_topic"] = state_topic;
|
||||||
"\"name\": \"%s\","
|
doc["unique_id"] = "battery-emulator_" + String(hostname) + "_" + String(config.object_id);
|
||||||
"\"state_topic\": \"%s\","
|
doc["object_id"] = String(hostname) + "_" + String(config.object_id);
|
||||||
"\"unique_id\": \"battery-emulator_%s\","
|
doc["value_template"] = config.value_template;
|
||||||
"\"object_id\": \"sensor_battery_%s\","
|
doc["unit_of_measurement"] = config.unit;
|
||||||
"\"device\": {"
|
doc["device_class"] = config.device_class;
|
||||||
"\"identifiers\": ["
|
doc["enabled_by_default"] = true;
|
||||||
"\"battery-emulator\""
|
doc["state_class"] = "measurement";
|
||||||
"],"
|
doc["expire_after"] = 240;
|
||||||
"\"manufacturer\": \"DalaTech\","
|
doc["device"]["identifiers"][0] = "battery-emulator";
|
||||||
"\"model\": \"BatteryEmulator\","
|
doc["device"]["manufacturer"] = "DalaTech";
|
||||||
"\"name\": \"BatteryEmulator\""
|
doc["device"]["model"] = "BatteryEmulator";
|
||||||
"},"
|
doc["device"]["name"] = "BatteryEmulator_" + String(hostname);
|
||||||
"\"origin\": {"
|
doc["origin"]["name"] = "BatteryEmulator";
|
||||||
"\"name\": \"BatteryEmulator\","
|
doc["origin"]["sw"] = String(version_number) + "-mqtt";
|
||||||
"\"sw\": \"%s-mqtt\","
|
doc["origin"]["url"] = "https://github.com/dalathegreat/Battery-Emulator";
|
||||||
"\"url\": \"https://github.com/dalathegreat/Battery-Emulator\""
|
serializeJson(doc, mqtt_msg);
|
||||||
"},"
|
mqtt_publish(generateCommonInfoAutoConfigTopic(config.object_id, hostname).c_str(), mqtt_msg, true);
|
||||||
"\"value_template\": \"%s\","
|
|
||||||
"\"unit_of_measurement\": \"%s\","
|
|
||||||
"\"device_class\": \"%s\","
|
|
||||||
"\"enabled_by_default\": true,"
|
|
||||||
"\"state_class\": \"measurement\""
|
|
||||||
"}",
|
|
||||||
config.name, state_topic, config.object_id, config.object_id, version_number, config.value_template,
|
|
||||||
config.unit, config.device_class);
|
|
||||||
mqtt_publish_retain(config.topic);
|
|
||||||
}
|
}
|
||||||
|
doc.clear();
|
||||||
} else {
|
} else {
|
||||||
snprintf(mqtt_msg, sizeof(mqtt_msg),
|
doc["SOC"] = ((float)system_scaled_SOC_pptt) / 100.0;
|
||||||
"{\n"
|
doc["SOC_real"] = ((float)system_real_SOC_pptt) / 100.0;
|
||||||
" \"SOC\": %.3f,\n"
|
doc["state_of_health"] = ((float)system_SOH_pptt) / 100.0;
|
||||||
" \"state_of_health\": %.3f,\n"
|
doc["temperature_min"] = ((float)((int16_t)system_temperature_min_dC)) / 10.0;
|
||||||
" \"temperature_min\": %.3f,\n"
|
doc["temperature_max"] = ((float)((int16_t)system_temperature_max_dC)) / 10.0;
|
||||||
" \"temperature_max\": %.3f,\n"
|
doc["stat_batt_power"] = ((float)((int16_t)system_active_power_W));
|
||||||
" \"stat_batt_power\": %.3f,\n"
|
doc["battery_current"] = ((float)((int16_t)system_battery_current_dA)) / 10.0;
|
||||||
" \"battery_current\": %.3f,\n"
|
doc["cell_max_voltage"] = ((float)system_cell_max_voltage_mV) / 1000.0;
|
||||||
" \"cell_max_voltage\": %.3f,\n"
|
doc["cell_min_voltage"] = ((float)system_cell_min_voltage_mV) / 1000.0;
|
||||||
" \"cell_min_voltage\": %.3f,\n"
|
doc["battery_voltage"] = ((float)system_battery_voltage_dV) / 10.0;
|
||||||
" \"battery_voltage\": %d\n"
|
|
||||||
"}\n",
|
|
||||||
((float)SOC) / 100.0, ((float)StateOfHealth) / 100.0, ((float)((int16_t)temperature_min)) / 10.0,
|
|
||||||
((float)((int16_t)temperature_max)) / 10.0, ((float)((int16_t)stat_batt_power)),
|
|
||||||
((float)((int16_t)battery_current)) / 10.0, ((float)cell_max_voltage) / 1000,
|
|
||||||
((float)cell_min_voltage) / 1000, battery_voltage / 10.0);
|
|
||||||
bool result = client.publish(state_topic, mqtt_msg, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Serial.println(mqtt_msg); // Uncomment to print the payload on serial
|
serializeJson(doc, mqtt_msg);
|
||||||
|
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||||
|
Serial.println("Common info MQTT msg could not be sent");
|
||||||
|
}
|
||||||
|
doc.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is called whenever a subscribed topic changes (hopefully) */
|
/* This is called whenever a subscribed topic changes (hopefully) */
|
||||||
|
@ -206,9 +182,10 @@ static void reconnect() {
|
||||||
// attempt one reconnection
|
// attempt one reconnection
|
||||||
Serial.print("Attempting MQTT connection... ");
|
Serial.print("Attempting MQTT connection... ");
|
||||||
const char* hostname = WiFi.getHostname();
|
const char* hostname = WiFi.getHostname();
|
||||||
String clientId = "LilyGoClient-" + String(hostname);
|
char clientId[64]; // Adjust the size as needed
|
||||||
|
snprintf(clientId, sizeof(clientId), "LilyGoClient-%s", hostname);
|
||||||
// Attempt to connect
|
// Attempt to connect
|
||||||
if (client.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
|
if (client.connect(clientId, mqtt_user, mqtt_password)) {
|
||||||
Serial.println("connected");
|
Serial.println("connected");
|
||||||
|
|
||||||
for (int i = 0; i < mqtt_nof_subscriptions; i++) {
|
for (int i = 0; i < mqtt_nof_subscriptions; i++) {
|
||||||
|
@ -238,20 +215,20 @@ void mqtt_loop(void) {
|
||||||
client.loop();
|
client.loop();
|
||||||
if (publish_global_timer.elapsed() == true) // Every 5s
|
if (publish_global_timer.elapsed() == true) // Every 5s
|
||||||
{
|
{
|
||||||
publish_values(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus.
|
publish_values();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (millis() - previousMillisUpdateVal >= 5000) // Every 5s
|
if (millis() - previousMillisUpdateVal >= 5000) // Every 5s
|
||||||
{
|
{
|
||||||
previousMillisUpdateVal = millis();
|
previousMillisUpdateVal = millis();
|
||||||
reconnect(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus.
|
reconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mqtt_publish_retain(const char* topic) {
|
bool mqtt_publish(const char* topic, const char* mqtt_msg, bool retain) {
|
||||||
if (client.connected() == true) {
|
if (client.connected() == true) {
|
||||||
return client.publish(topic, mqtt_msg, true);
|
return client.publish(topic, mqtt_msg, retain);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,16 +41,18 @@
|
||||||
|
|
||||||
extern const char* version_number; // The current software version, used for mqtt
|
extern const char* version_number; // The current software version, used for mqtt
|
||||||
|
|
||||||
extern uint16_t SOC;
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t StateOfHealth;
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery.
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000 , Stores the highest cell millivolt value
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
|
||||||
extern const char* mqtt_user;
|
extern const char* mqtt_user;
|
||||||
extern const char* mqtt_password;
|
extern const char* mqtt_password;
|
||||||
|
@ -59,6 +61,6 @@ extern char mqtt_msg[MQTT_MSG_BUFFER_SIZE];
|
||||||
|
|
||||||
void init_mqtt(void);
|
void init_mqtt(void);
|
||||||
void mqtt_loop(void);
|
void mqtt_loop(void);
|
||||||
bool mqtt_publish_retain(const char* topic);
|
bool mqtt_publish(const char* topic, const char* mqtt_msg, bool retain);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,82 +1,190 @@
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
|
|
||||||
|
#ifndef UNIT_TEST
|
||||||
|
#include <EEPROM.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "../../../USER_SETTINGS.h"
|
#include "../../../USER_SETTINGS.h"
|
||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
unsigned long previous_millis = 0;
|
#define EE_MAGIC_HEADER_VALUE 0xA5A5
|
||||||
uint32_t time_seconds = 0;
|
#define EE_NOF_EVENT_ENTRIES 30
|
||||||
static uint8_t total_led_color = GREEN;
|
#define EE_EVENT_ENTRY_SIZE sizeof(EVENT_LOG_ENTRY_TYPE)
|
||||||
static char event_message[256];
|
#define EE_WRITE_PERIOD_MINUTES 10
|
||||||
EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS];
|
|
||||||
|
/** EVENT LOG STRUCTURE
|
||||||
|
*
|
||||||
|
* The event log is stored in a simple header-block structure. The
|
||||||
|
* header contains a magic number to identify it as an event log,
|
||||||
|
* a head index and a tail index. The head index points to the last
|
||||||
|
* recorded event, the tail index points to the "oldest" event in the
|
||||||
|
* log. The event log is set up like a circular buffer, so we only
|
||||||
|
* store the set amount of events. The head continuously overwrites
|
||||||
|
* the oldest events, and both the head and tail indices wrap around
|
||||||
|
* to 0 at the end of the event log:
|
||||||
|
*
|
||||||
|
* [ HEADER ]
|
||||||
|
* [ MAGIC NUMBER ][ HEAD INDEX ][ TAIL INDEX ][ EVENT BLOCK 0 ][ EVENT BLOCK 1]...
|
||||||
|
* [ 2 bytes ][ 2 bytes ][ 2 bytes ][ 6 bytes ][ 6 bytes ]
|
||||||
|
*
|
||||||
|
* 1024 bytes are allocated to the event log in flash emulated EEPROM,
|
||||||
|
* giving room for (1024 - (2 + 2 + 2)) / 6 ~= 169 events
|
||||||
|
*
|
||||||
|
* For now, we store 30 to make it easier to handle initial debugging.
|
||||||
|
*/
|
||||||
|
#define EE_EVENT_LOG_START_ADDRESS 0
|
||||||
|
#define EE_EVENT_LOG_HEAD_INDEX_ADDRESS EE_EVENT_LOG_START_ADDRESS + 2
|
||||||
|
#define EE_EVENT_LOG_TAIL_INDEX_ADDRESS EE_EVENT_LOG_HEAD_INDEX_ADDRESS + 2
|
||||||
|
#define EE_EVENT_ENTRY_START_ADDRESS EE_EVENT_LOG_TAIL_INDEX_ADDRESS + 2
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
EVENTS_ENUM_TYPE event;
|
||||||
|
uint32_t timestamp;
|
||||||
|
uint8_t data;
|
||||||
|
} EVENT_LOG_ENTRY_TYPE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS];
|
||||||
|
uint32_t time_seconds;
|
||||||
|
MyTimer second_timer;
|
||||||
|
MyTimer ee_timer;
|
||||||
|
EVENTS_LEVEL_TYPE level;
|
||||||
|
uint16_t event_log_head_index;
|
||||||
|
uint16_t event_log_tail_index;
|
||||||
|
uint8_t nof_logged_events;
|
||||||
|
uint16_t nof_eeprom_writes;
|
||||||
|
} EVENT_TYPE;
|
||||||
|
|
||||||
|
/* Local variables */
|
||||||
|
static EVENT_TYPE events;
|
||||||
|
static const char* EVENTS_ENUM_TYPE_STRING[] = {EVENTS_ENUM_TYPE(GENERATE_STRING)};
|
||||||
|
static const char* EVENTS_LEVEL_TYPE_STRING[] = {EVENTS_LEVEL_TYPE(GENERATE_STRING)};
|
||||||
|
|
||||||
/* Local function prototypes */
|
/* Local function prototypes */
|
||||||
static void set_event_message(EVENTS_ENUM_TYPE event);
|
static void update_event_time(void);
|
||||||
static void update_led_color(EVENTS_ENUM_TYPE event);
|
static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched);
|
||||||
|
static void update_event_level(void);
|
||||||
|
static void update_bms_status(void);
|
||||||
|
|
||||||
|
static void log_event(EVENTS_ENUM_TYPE event, uint8_t data);
|
||||||
|
static void print_event_log(void);
|
||||||
|
static void check_ee_write(void);
|
||||||
|
|
||||||
/* Exported functions */
|
/* Exported functions */
|
||||||
|
|
||||||
|
/* Main execution function, should handle various continuous functionality */
|
||||||
|
void run_event_handling(void) {
|
||||||
|
update_event_time();
|
||||||
|
run_sequence_on_target();
|
||||||
|
check_ee_write();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialization function */
|
||||||
void init_events(void) {
|
void init_events(void) {
|
||||||
for (uint8_t i = 0; i < EVENT_NOF_EVENTS; i++) {
|
|
||||||
entries[i].timestamp = 0;
|
EEPROM.begin(1024);
|
||||||
entries[i].data = 0;
|
events.nof_logged_events = 0;
|
||||||
entries[i].occurences = 0;
|
|
||||||
entries[i].led_color = RED; // Most events are RED, critical errors
|
uint16_t header = EEPROM.readUShort(EE_EVENT_LOG_START_ADDRESS);
|
||||||
|
if (header != EE_MAGIC_HEADER_VALUE) {
|
||||||
|
// The header doesn't appear to be a compatible event log, clear it and initialize
|
||||||
|
EEPROM.writeUShort(EE_EVENT_LOG_START_ADDRESS, EE_MAGIC_HEADER_VALUE);
|
||||||
|
EEPROM.writeUShort(EE_EVENT_LOG_HEAD_INDEX_ADDRESS, 0);
|
||||||
|
EEPROM.writeUShort(EE_EVENT_LOG_TAIL_INDEX_ADDRESS, 0);
|
||||||
|
|
||||||
|
// Prepare an empty event block to write
|
||||||
|
EVENT_LOG_ENTRY_TYPE entry = {.event = EVENT_NOF_EVENTS, .timestamp = 0, .data = 0};
|
||||||
|
|
||||||
|
// Put the event in (what I guess is) the RAM EEPROM mirror, or write buffer
|
||||||
|
|
||||||
|
for (int i = 0; i < EE_NOF_EVENT_ENTRIES; i++) {
|
||||||
|
// Start at the oldest event, work through the log all the way the the head
|
||||||
|
int address = EE_EVENT_ENTRY_START_ADDRESS + EE_EVENT_ENTRY_SIZE * i;
|
||||||
|
EEPROM.put(address, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// YELLOW warning events below
|
// Push changes to eeprom
|
||||||
entries[EVENT_12V_LOW].led_color = YELLOW;
|
EEPROM.commit();
|
||||||
entries[EVENT_CAN_WARNING].led_color = YELLOW;
|
Serial.println("EEPROM wasn't ready");
|
||||||
entries[EVENT_CELL_DEVIATION_HIGH].led_color = YELLOW;
|
} else {
|
||||||
entries[EVENT_KWH_PLAUSIBILITY_ERROR].led_color = YELLOW;
|
events.event_log_head_index = EEPROM.readUShort(EE_EVENT_LOG_HEAD_INDEX_ADDRESS);
|
||||||
|
events.event_log_tail_index = EEPROM.readUShort(EE_EVENT_LOG_TAIL_INDEX_ADDRESS);
|
||||||
|
Serial.println("EEPROM was initialized for event logging");
|
||||||
|
Serial.println("head: " + String(events.event_log_head_index) + ", tail: " + String(events.event_log_tail_index));
|
||||||
|
print_event_log();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < EVENT_NOF_EVENTS; i++) {
|
||||||
|
events.entries[i].data = 0;
|
||||||
|
events.entries[i].timestamp = 0;
|
||||||
|
events.entries[i].occurences = 0;
|
||||||
|
events.entries[i].log = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
events.entries[EVENT_CAN_RX_FAILURE].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_CAN_RX_WARNING].level = EVENT_LEVEL_WARNING;
|
||||||
|
events.entries[EVENT_CAN_TX_FAILURE].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_WATER_INGRESS].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_12V_LOW].level = EVENT_LEVEL_WARNING;
|
||||||
|
events.entries[EVENT_SOC_PLAUSIBILITY_ERROR].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_KWH_PLAUSIBILITY_ERROR].level = EVENT_LEVEL_INFO;
|
||||||
|
events.entries[EVENT_BATTERY_EMPTY].level = EVENT_LEVEL_INFO;
|
||||||
|
events.entries[EVENT_BATTERY_FULL].level = EVENT_LEVEL_INFO;
|
||||||
|
events.entries[EVENT_BATTERY_CHG_STOP_REQ].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_BATTERY_DISCHG_STOP_REQ].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_BATTERY_CHG_DISCHG_STOP_REQ].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_LOW_SOH].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_HVIL_FAILURE].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_INTERNAL_OPEN_FAULT].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_INVERTER_OPEN_CONTACTOR].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_CELL_UNDER_VOLTAGE].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_CELL_OVER_VOLTAGE].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_CELL_DEVIATION_HIGH].level = EVENT_LEVEL_WARNING;
|
||||||
|
events.entries[EVENT_UNKNOWN_EVENT_SET].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_OTA_UPDATE].level = EVENT_LEVEL_UPDATE;
|
||||||
|
events.entries[EVENT_OTA_UPDATE_TIMEOUT].level = EVENT_LEVEL_INFO;
|
||||||
|
events.entries[EVENT_DUMMY_INFO].level = EVENT_LEVEL_INFO;
|
||||||
|
events.entries[EVENT_DUMMY_DEBUG].level = EVENT_LEVEL_DEBUG;
|
||||||
|
events.entries[EVENT_DUMMY_WARNING].level = EVENT_LEVEL_WARNING;
|
||||||
|
events.entries[EVENT_DUMMY_ERROR].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_SERIAL_RX_WARNING].level = EVENT_LEVEL_WARNING;
|
||||||
|
events.entries[EVENT_SERIAL_RX_FAILURE].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_SERIAL_TX_FAILURE].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_SERIAL_TRANSMITTER_FAILURE].level = EVENT_LEVEL_ERROR;
|
||||||
|
events.entries[EVENT_EEPROM_WRITE].level = EVENT_LEVEL_INFO;
|
||||||
|
|
||||||
|
events.entries[EVENT_EEPROM_WRITE].log = false; // Don't log the logger...
|
||||||
|
|
||||||
|
events.second_timer.set_interval(1000);
|
||||||
|
// Write to EEPROM every X minutes (if an event has been set)
|
||||||
|
events.ee_timer.set_interval(EE_WRITE_PERIOD_MINUTES * 60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_event(EVENTS_ENUM_TYPE event, uint8_t data) {
|
void set_event(EVENTS_ENUM_TYPE event, uint8_t data) {
|
||||||
if (event >= EVENT_NOF_EVENTS) {
|
set_event(event, data, false);
|
||||||
event = EVENT_UNKNOWN_EVENT_SET;
|
|
||||||
}
|
|
||||||
entries[event].timestamp = time_seconds;
|
|
||||||
entries[event].data = data;
|
|
||||||
++entries[event].occurences;
|
|
||||||
set_event_message(event);
|
|
||||||
#ifdef DEBUG_VIA_USB
|
|
||||||
Serial.println("Set event: " + String(get_event_enum_string(event)) + ". Has occured " +
|
|
||||||
String(entries[event].occurences) + " times");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_event_timestamps(void) {
|
void set_event_latched(EVENTS_ENUM_TYPE event, uint8_t data) {
|
||||||
unsigned long new_millis = millis();
|
set_event(event, data, true);
|
||||||
if (new_millis - previous_millis >= 1000) {
|
}
|
||||||
time_seconds++;
|
|
||||||
previous_millis = new_millis;
|
void clear_event(EVENTS_ENUM_TYPE event) {
|
||||||
|
if (events.entries[event].state == EVENT_STATE_ACTIVE) {
|
||||||
|
events.entries[event].state = EVENT_STATE_INACTIVE;
|
||||||
|
update_event_level();
|
||||||
|
update_bms_status();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Local functions */
|
const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
|
||||||
static void update_led_color(EVENTS_ENUM_TYPE event) {
|
|
||||||
total_led_color = (total_led_color == RED) ? RED : entries[event].led_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* get_led_color_display_text(u_int8_t led_color) {
|
|
||||||
switch (led_color) {
|
|
||||||
case RED:
|
|
||||||
return "Error";
|
|
||||||
case YELLOW:
|
|
||||||
return "Warning";
|
|
||||||
case GREEN:
|
|
||||||
return "Info";
|
|
||||||
case BLUE:
|
|
||||||
return "Debug";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* get_event_message(EVENTS_ENUM_TYPE event) {
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case EVENT_CAN_FAILURE:
|
case EVENT_CAN_RX_FAILURE:
|
||||||
return "No CAN communication detected for 60s. Shutting down battery control.";
|
return "No CAN communication detected for 60s. Shutting down battery control.";
|
||||||
case EVENT_CAN_WARNING:
|
case EVENT_CAN_RX_WARNING:
|
||||||
return "ERROR: High amount of corrupted CAN messages detected. Check CAN wire shielding!";
|
return "ERROR: High amount of corrupted CAN messages detected. Check CAN wire shielding!";
|
||||||
|
case EVENT_CAN_TX_FAILURE:
|
||||||
|
return "ERROR: CAN messages failed to transmit, or no one on the bus to ACK the message!";
|
||||||
case EVENT_WATER_INGRESS:
|
case EVENT_WATER_INGRESS:
|
||||||
return "Water leakage inside battery detected. Operation halted. Inspect battery!";
|
return "Water leakage inside battery detected. Operation halted. Inspect battery!";
|
||||||
case EVENT_12V_LOW:
|
case EVENT_12V_LOW:
|
||||||
|
@ -84,7 +192,11 @@ const char* get_event_message(EVENTS_ENUM_TYPE event) {
|
||||||
case EVENT_SOC_PLAUSIBILITY_ERROR:
|
case EVENT_SOC_PLAUSIBILITY_ERROR:
|
||||||
return "ERROR: SOC% reported by battery not plausible. Restart battery!";
|
return "ERROR: SOC% reported by battery not plausible. Restart battery!";
|
||||||
case EVENT_KWH_PLAUSIBILITY_ERROR:
|
case EVENT_KWH_PLAUSIBILITY_ERROR:
|
||||||
return "Warning: kWh remaining reported by battery not plausible. Battery needs cycling.";
|
return "Info: kWh remaining reported by battery not plausible. Battery needs cycling.";
|
||||||
|
case EVENT_BATTERY_EMPTY:
|
||||||
|
return "Info: Battery is completely discharged";
|
||||||
|
case EVENT_BATTERY_FULL:
|
||||||
|
return "Info: Battery is fully charged";
|
||||||
case EVENT_BATTERY_CHG_STOP_REQ:
|
case EVENT_BATTERY_CHG_STOP_REQ:
|
||||||
return "ERROR: Battery raised caution indicator AND requested charge stop. Inspect battery status!";
|
return "ERROR: Battery raised caution indicator AND requested charge stop. Inspect battery status!";
|
||||||
case EVENT_BATTERY_DISCHG_STOP_REQ:
|
case EVENT_BATTERY_DISCHG_STOP_REQ:
|
||||||
|
@ -99,6 +211,8 @@ const char* get_event_message(EVENTS_ENUM_TYPE event) {
|
||||||
"disabled!";
|
"disabled!";
|
||||||
case EVENT_INTERNAL_OPEN_FAULT:
|
case EVENT_INTERNAL_OPEN_FAULT:
|
||||||
return "ERROR: High voltage cable removed while battery running. Opening contactors!";
|
return "ERROR: High voltage cable removed while battery running. Opening contactors!";
|
||||||
|
case EVENT_INVERTER_OPEN_CONTACTOR:
|
||||||
|
return "ERROR: Inverter requested contactors to open. Opening contactors!";
|
||||||
case EVENT_CELL_UNDER_VOLTAGE:
|
case EVENT_CELL_UNDER_VOLTAGE:
|
||||||
return "ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!";
|
return "ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!";
|
||||||
case EVENT_CELL_OVER_VOLTAGE:
|
case EVENT_CELL_OVER_VOLTAGE:
|
||||||
|
@ -107,22 +221,187 @@ const char* get_event_message(EVENTS_ENUM_TYPE event) {
|
||||||
return "ERROR: HIGH CELL DEVIATION!!! Inspect battery!";
|
return "ERROR: HIGH CELL DEVIATION!!! Inspect battery!";
|
||||||
case EVENT_UNKNOWN_EVENT_SET:
|
case EVENT_UNKNOWN_EVENT_SET:
|
||||||
return "An unknown event was set! Review your code!";
|
return "An unknown event was set! Review your code!";
|
||||||
case EVENT_DUMMY:
|
case EVENT_DUMMY_INFO:
|
||||||
return "The dummy event was set!"; // Don't change this event message!
|
return "The dummy info event was set!"; // Don't change this event message!
|
||||||
|
case EVENT_DUMMY_DEBUG:
|
||||||
|
return "The dummy debug event was set!"; // Don't change this event message!
|
||||||
|
case EVENT_DUMMY_WARNING:
|
||||||
|
return "The dummy warning event was set!"; // Don't change this event message!
|
||||||
|
case EVENT_DUMMY_ERROR:
|
||||||
|
return "The dummy error event was set!"; // Don't change this event message!
|
||||||
|
case EVENT_SERIAL_RX_WARNING:
|
||||||
|
return "Error in serial function: No data received for some time, see data for minutes";
|
||||||
|
case EVENT_SERIAL_RX_FAILURE:
|
||||||
|
return "Error in serial function: No data for a long time!";
|
||||||
|
case EVENT_SERIAL_TX_FAILURE:
|
||||||
|
return "Error in serial function: No ACK from receiver!";
|
||||||
|
case EVENT_SERIAL_TRANSMITTER_FAILURE:
|
||||||
|
return "Error in serial function: Some ERROR level fault in transmitter, received by receiver";
|
||||||
|
case EVENT_OTA_UPDATE:
|
||||||
|
return "OTA update started!";
|
||||||
|
case EVENT_OTA_UPDATE_TIMEOUT:
|
||||||
|
return "OTA update timed out!";
|
||||||
|
case EVENT_EEPROM_WRITE:
|
||||||
|
return "Info: The EEPROM was written";
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* get_event_enum_string(EVENTS_ENUM_TYPE event) {
|
const char* get_event_enum_string(EVENTS_ENUM_TYPE event) {
|
||||||
const char* fullString = EVENTS_ENUM_TYPE_STRING[event];
|
// Return the event name but skip "EVENT_" that should always be first
|
||||||
if (strncmp(fullString, "EVENT_", 6) == 0) {
|
return EVENTS_ENUM_TYPE_STRING[event] + 6;
|
||||||
return fullString + 6; // Skip the first 6 characters
|
|
||||||
}
|
|
||||||
return fullString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_event_message(EVENTS_ENUM_TYPE event) {
|
const char* get_event_level_string(EVENTS_ENUM_TYPE event) {
|
||||||
const char* message = get_event_message(event);
|
// Return the event level but skip "EVENT_LEVEL_" that should always be first
|
||||||
snprintf(event_message, sizeof(event_message), "%s", message);
|
return EVENTS_LEVEL_TYPE_STRING[events.entries[event].level] + 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EVENTS_STRUCT_TYPE* get_event_pointer(EVENTS_ENUM_TYPE event) {
|
||||||
|
return &events.entries[event];
|
||||||
|
}
|
||||||
|
|
||||||
|
EVENTS_LEVEL_TYPE get_event_level(void) {
|
||||||
|
return events.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Local functions */
|
||||||
|
|
||||||
|
static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) {
|
||||||
|
// Just some defensive stuff if someone sets an unknown event
|
||||||
|
if (event >= EVENT_NOF_EVENTS) {
|
||||||
|
event = EVENT_UNKNOWN_EVENT_SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the event is already set, no reason to continue
|
||||||
|
if ((events.entries[event].state != EVENT_STATE_ACTIVE) &&
|
||||||
|
(events.entries[event].state != EVENT_STATE_ACTIVE_LATCHED)) {
|
||||||
|
events.entries[event].occurences++;
|
||||||
|
if (events.entries[event].log) {
|
||||||
|
log_event(event, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should set the event, update event info
|
||||||
|
events.entries[event].timestamp = events.time_seconds;
|
||||||
|
events.entries[event].data = data;
|
||||||
|
// Check if the event is latching
|
||||||
|
events.entries[event].state = latched ? EVENT_STATE_ACTIVE_LATCHED : EVENT_STATE_ACTIVE;
|
||||||
|
|
||||||
|
update_event_level();
|
||||||
|
update_bms_status();
|
||||||
|
|
||||||
|
#ifdef DEBUG_VIA_USB
|
||||||
|
Serial.println(get_event_message_string(event));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_bms_status(void) {
|
||||||
|
switch (events.level) {
|
||||||
|
case EVENT_LEVEL_INFO:
|
||||||
|
case EVENT_LEVEL_WARNING:
|
||||||
|
case EVENT_LEVEL_DEBUG:
|
||||||
|
system_bms_status = ACTIVE;
|
||||||
|
break;
|
||||||
|
case EVENT_LEVEL_UPDATE:
|
||||||
|
system_bms_status = UPDATING;
|
||||||
|
break;
|
||||||
|
case EVENT_LEVEL_ERROR:
|
||||||
|
system_bms_status = FAULT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_event_level(void) {
|
||||||
|
events.level = EVENT_LEVEL_INFO;
|
||||||
|
for (uint8_t i = 0u; i < EVENT_NOF_EVENTS; i++) {
|
||||||
|
if ((events.entries[i].state == EVENT_STATE_ACTIVE) || (events.entries[i].state == EVENT_STATE_ACTIVE_LATCHED)) {
|
||||||
|
events.level = max(events.entries[i].level, events.level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_event_time(void) {
|
||||||
|
if (events.second_timer.elapsed() == true) {
|
||||||
|
events.time_seconds++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_event(EVENTS_ENUM_TYPE event, uint8_t data) {
|
||||||
|
// Update head with wrap to 0
|
||||||
|
if (++events.event_log_head_index == EE_NOF_EVENT_ENTRIES) {
|
||||||
|
events.event_log_head_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the head now points to the tail, move the tail, with wrap to 0
|
||||||
|
if (events.event_log_head_index == events.event_log_tail_index) {
|
||||||
|
if (++events.event_log_tail_index == EE_NOF_EVENT_ENTRIES) {
|
||||||
|
events.event_log_tail_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The head now holds the index to the oldest event, the one we want to overwrite,
|
||||||
|
// so calculate the absolute address
|
||||||
|
int entry_address = EE_EVENT_ENTRY_START_ADDRESS + EE_EVENT_ENTRY_SIZE * events.event_log_head_index;
|
||||||
|
|
||||||
|
// Prepare an event block to write
|
||||||
|
EVENT_LOG_ENTRY_TYPE entry = {.event = event, .timestamp = events.time_seconds, .data = data};
|
||||||
|
|
||||||
|
// Put the event in (what I guess is) the RAM EEPROM mirror, or write buffer
|
||||||
|
EEPROM.put(entry_address, entry);
|
||||||
|
|
||||||
|
// Store the new indices
|
||||||
|
EEPROM.writeUShort(EE_EVENT_LOG_HEAD_INDEX_ADDRESS, events.event_log_head_index);
|
||||||
|
EEPROM.writeUShort(EE_EVENT_LOG_TAIL_INDEX_ADDRESS, events.event_log_tail_index);
|
||||||
|
//Serial.println("Wrote event " + String(event) + " to " + String(entry_address));
|
||||||
|
//Serial.println("head: " + String(events.event_log_head_index) + ", tail: " + String(events.event_log_tail_index));
|
||||||
|
|
||||||
|
// We don't need the exact number, it's just for deciding to store or not
|
||||||
|
events.nof_logged_events += (events.nof_logged_events < 255) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_event_log(void) {
|
||||||
|
// If the head actually points to the tail, the log is probably blank
|
||||||
|
if (events.event_log_head_index == events.event_log_tail_index) {
|
||||||
|
Serial.println("No events in log");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EVENT_LOG_ENTRY_TYPE entry;
|
||||||
|
|
||||||
|
for (int i = 0; i < EE_NOF_EVENT_ENTRIES; i++) {
|
||||||
|
// Start at the oldest event, work through the log all the way the the head
|
||||||
|
int index = ((events.event_log_tail_index + i) % EE_NOF_EVENT_ENTRIES);
|
||||||
|
int address = EE_EVENT_ENTRY_START_ADDRESS + EE_EVENT_ENTRY_SIZE * index;
|
||||||
|
|
||||||
|
EEPROM.get(address, entry);
|
||||||
|
if (entry.event == EVENT_NOF_EVENTS) {
|
||||||
|
// The entry is a blank that has been left behind somehow
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Serial.println("Event: " + String(get_event_enum_string(entry.event)) + ", data: " + String(entry.data) +
|
||||||
|
", time: " + String(entry.timestamp));
|
||||||
|
|
||||||
|
if (index == events.event_log_head_index) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_ee_write(void) {
|
||||||
|
// Only actually write to flash emulated EEPROM every EE_WRITE_PERIOD_MINUTES minutes,
|
||||||
|
// and only if we've logged any events
|
||||||
|
if (events.ee_timer.elapsed() && (events.nof_logged_events > 0)) {
|
||||||
|
EEPROM.commit();
|
||||||
|
events.nof_eeprom_writes += (events.nof_eeprom_writes < 65535) ? 1 : 0;
|
||||||
|
events.nof_logged_events = 0;
|
||||||
|
|
||||||
|
// We want to know how many writes we have, and to increment the occurrence counter
|
||||||
|
// we need to clear it first. It's just the way life is. Using events is a smooth
|
||||||
|
// way to visualize it in the web UI
|
||||||
|
clear_event(EVENT_EEPROM_WRITE);
|
||||||
|
set_event(EVENT_EEPROM_WRITE, events.nof_eeprom_writes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,52 +4,102 @@
|
||||||
|
|
||||||
#ifndef UNIT_TEST
|
#ifndef UNIT_TEST
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
extern unsigned long previous_millis;
|
|
||||||
extern uint32_t time_seconds;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// #define INCLUDE_EVENTS_TEST // Enable to run an event test loop, see events_test_on_target.cpp
|
||||||
|
|
||||||
|
#define GENERATE_ENUM(ENUM) ENUM,
|
||||||
|
#define GENERATE_STRING(STRING) #STRING,
|
||||||
|
|
||||||
|
/** EVENT ENUMERATION
|
||||||
|
*
|
||||||
|
* Do not change the order!
|
||||||
|
* When adding events, add them RIGHT BEFORE the EVENT_NOF_EVENTS enum.
|
||||||
|
* In addition, the event name must start with "EVENT_"
|
||||||
|
*
|
||||||
|
* After adding an event, assign the proper event level in events.cpp:init_events()
|
||||||
|
*/
|
||||||
|
|
||||||
#define EVENTS_ENUM_TYPE(XX) \
|
#define EVENTS_ENUM_TYPE(XX) \
|
||||||
XX(EVENT_CAN_FAILURE) \
|
XX(EVENT_CAN_RX_FAILURE) \
|
||||||
XX(EVENT_CAN_WARNING) \
|
XX(EVENT_CAN_RX_WARNING) \
|
||||||
|
XX(EVENT_CAN_TX_FAILURE) \
|
||||||
XX(EVENT_WATER_INGRESS) \
|
XX(EVENT_WATER_INGRESS) \
|
||||||
XX(EVENT_12V_LOW) \
|
XX(EVENT_12V_LOW) \
|
||||||
XX(EVENT_SOC_PLAUSIBILITY_ERROR) \
|
XX(EVENT_SOC_PLAUSIBILITY_ERROR) \
|
||||||
XX(EVENT_KWH_PLAUSIBILITY_ERROR) \
|
XX(EVENT_KWH_PLAUSIBILITY_ERROR) \
|
||||||
|
XX(EVENT_BATTERY_EMPTY) \
|
||||||
|
XX(EVENT_BATTERY_FULL) \
|
||||||
XX(EVENT_BATTERY_CHG_STOP_REQ) \
|
XX(EVENT_BATTERY_CHG_STOP_REQ) \
|
||||||
XX(EVENT_BATTERY_DISCHG_STOP_REQ) \
|
XX(EVENT_BATTERY_DISCHG_STOP_REQ) \
|
||||||
XX(EVENT_BATTERY_CHG_DISCHG_STOP_REQ) \
|
XX(EVENT_BATTERY_CHG_DISCHG_STOP_REQ) \
|
||||||
XX(EVENT_LOW_SOH) \
|
XX(EVENT_LOW_SOH) \
|
||||||
XX(EVENT_HVIL_FAILURE) \
|
XX(EVENT_HVIL_FAILURE) \
|
||||||
XX(EVENT_INTERNAL_OPEN_FAULT) \
|
XX(EVENT_INTERNAL_OPEN_FAULT) \
|
||||||
|
XX(EVENT_INVERTER_OPEN_CONTACTOR) \
|
||||||
XX(EVENT_CELL_UNDER_VOLTAGE) \
|
XX(EVENT_CELL_UNDER_VOLTAGE) \
|
||||||
XX(EVENT_CELL_OVER_VOLTAGE) \
|
XX(EVENT_CELL_OVER_VOLTAGE) \
|
||||||
XX(EVENT_CELL_DEVIATION_HIGH) \
|
XX(EVENT_CELL_DEVIATION_HIGH) \
|
||||||
XX(EVENT_UNKNOWN_EVENT_SET) \
|
XX(EVENT_UNKNOWN_EVENT_SET) \
|
||||||
XX(EVENT_DUMMY) \
|
XX(EVENT_OTA_UPDATE) \
|
||||||
|
XX(EVENT_OTA_UPDATE_TIMEOUT) \
|
||||||
|
XX(EVENT_DUMMY_INFO) \
|
||||||
|
XX(EVENT_DUMMY_DEBUG) \
|
||||||
|
XX(EVENT_DUMMY_WARNING) \
|
||||||
|
XX(EVENT_DUMMY_ERROR) \
|
||||||
|
XX(EVENT_SERIAL_RX_WARNING) \
|
||||||
|
XX(EVENT_SERIAL_RX_FAILURE) \
|
||||||
|
XX(EVENT_SERIAL_TX_FAILURE) \
|
||||||
|
XX(EVENT_SERIAL_TRANSMITTER_FAILURE) \
|
||||||
|
XX(EVENT_EEPROM_WRITE) \
|
||||||
XX(EVENT_NOF_EVENTS)
|
XX(EVENT_NOF_EVENTS)
|
||||||
|
|
||||||
#define GENERATE_ENUM(ENUM) ENUM,
|
|
||||||
#define GENERATE_STRING(STRING) #STRING,
|
|
||||||
|
|
||||||
typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE;
|
typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE;
|
||||||
|
|
||||||
static const char* EVENTS_ENUM_TYPE_STRING[] = {EVENTS_ENUM_TYPE(GENERATE_STRING)};
|
/* Event type enumeration, keep in order of priority! */
|
||||||
|
#define EVENTS_LEVEL_TYPE(XX) \
|
||||||
|
XX(EVENT_LEVEL_INFO) \
|
||||||
|
XX(EVENT_LEVEL_DEBUG) \
|
||||||
|
XX(EVENT_LEVEL_WARNING) \
|
||||||
|
XX(EVENT_LEVEL_ERROR) \
|
||||||
|
XX(EVENT_LEVEL_UPDATE)
|
||||||
|
|
||||||
const char* get_event_enum_string(EVENTS_ENUM_TYPE event);
|
typedef enum { EVENTS_LEVEL_TYPE(GENERATE_ENUM) } EVENTS_LEVEL_TYPE;
|
||||||
|
|
||||||
const char* get_event_message(EVENTS_ENUM_TYPE event);
|
typedef enum {
|
||||||
|
EVENT_STATE_PENDING = 0,
|
||||||
|
EVENT_STATE_INACTIVE,
|
||||||
|
EVENT_STATE_ACTIVE,
|
||||||
|
EVENT_STATE_ACTIVE_LATCHED
|
||||||
|
} EVENTS_STATE_TYPE;
|
||||||
|
|
||||||
const char* get_led_color_display_text(u_int8_t led_color);
|
|
||||||
|
|
||||||
void init_events(void);
|
|
||||||
void set_event(EVENTS_ENUM_TYPE event, uint8_t data);
|
|
||||||
void update_event_timestamps(void);
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t timestamp; // Time in seconds since startup when the event occurred
|
uint32_t timestamp; // Time in seconds since startup when the event occurred
|
||||||
uint8_t data; // Custom data passed when setting the event, for example cell number for under voltage
|
uint8_t data; // Custom data passed when setting the event, for example cell number for under voltage
|
||||||
uint8_t occurences; // Number of occurrences since startup
|
uint8_t occurences; // Number of occurrences since startup
|
||||||
uint8_t led_color; // Weirdly indented comment
|
EVENTS_LEVEL_TYPE level; // Event level, i.e. ERROR/WARNING...
|
||||||
|
EVENTS_STATE_TYPE state; // Event state, i.e. ACTIVE/INACTIVE...
|
||||||
|
bool log;
|
||||||
} EVENTS_STRUCT_TYPE;
|
} EVENTS_STRUCT_TYPE;
|
||||||
extern EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS];
|
|
||||||
|
const char* get_event_enum_string(EVENTS_ENUM_TYPE event);
|
||||||
|
const char* get_event_message_string(EVENTS_ENUM_TYPE event);
|
||||||
|
const char* get_event_level_string(EVENTS_ENUM_TYPE event);
|
||||||
|
const char* get_event_type(EVENTS_ENUM_TYPE event);
|
||||||
|
|
||||||
|
EVENTS_LEVEL_TYPE get_event_level(void);
|
||||||
|
|
||||||
|
void init_events(void);
|
||||||
|
void set_event_latched(EVENTS_ENUM_TYPE event, uint8_t data);
|
||||||
|
void set_event(EVENTS_ENUM_TYPE event, uint8_t data);
|
||||||
|
void clear_event(EVENTS_ENUM_TYPE event);
|
||||||
|
|
||||||
|
const EVENTS_STRUCT_TYPE* get_event_pointer(EVENTS_ENUM_TYPE event);
|
||||||
|
|
||||||
|
void run_event_handling(void);
|
||||||
|
|
||||||
|
void run_sequence_on_target(void);
|
||||||
|
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
|
||||||
#endif // __MYTIMER_H__
|
#endif // __MYTIMER_H__
|
||||||
|
|
142
Software/src/devboard/utils/events_test_on_target.cpp
Normal file
142
Software/src/devboard/utils/events_test_on_target.cpp
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
#include "events.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ETOT_INIT,
|
||||||
|
ETOT_FIRST_WAIT,
|
||||||
|
ETOT_INFO,
|
||||||
|
ETOT_INFO_CLEAR,
|
||||||
|
ETOT_DEBUG,
|
||||||
|
ETOT_DEBUG_CLEAR,
|
||||||
|
ETOT_WARNING,
|
||||||
|
ETOT_WARNING_CLEAR,
|
||||||
|
ETOT_ERROR,
|
||||||
|
ETOT_ERROR_CLEAR,
|
||||||
|
ETOT_ERROR_LATCHED,
|
||||||
|
ETOT_DONE
|
||||||
|
} ETOT_TYPE;
|
||||||
|
|
||||||
|
MyTimer timer(5000);
|
||||||
|
ETOT_TYPE events_test_state = ETOT_INIT;
|
||||||
|
|
||||||
|
void run_sequence_on_target(void) {
|
||||||
|
#ifdef INCLUDE_EVENTS_TEST
|
||||||
|
switch (events_test_state) {
|
||||||
|
case ETOT_INIT:
|
||||||
|
timer.set_interval(10000);
|
||||||
|
events_test_state = ETOT_FIRST_WAIT;
|
||||||
|
Serial.println("Events test: initialized");
|
||||||
|
Serial.print("system_bms_status: ");
|
||||||
|
Serial.println(system_bms_status);
|
||||||
|
break;
|
||||||
|
case ETOT_FIRST_WAIT:
|
||||||
|
if (timer.elapsed()) {
|
||||||
|
timer.set_interval(8000);
|
||||||
|
events_test_state = ETOT_INFO;
|
||||||
|
set_event(EVENT_DUMMY_INFO, 123);
|
||||||
|
set_event(EVENT_DUMMY_INFO, 234); // 234 should show, occurrence 1
|
||||||
|
Serial.println("Events test: info event set, data: 234");
|
||||||
|
Serial.print("system_bms_status: ");
|
||||||
|
Serial.println(system_bms_status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ETOT_INFO:
|
||||||
|
if (timer.elapsed()) {
|
||||||
|
timer.set_interval(8000);
|
||||||
|
clear_event(EVENT_DUMMY_INFO);
|
||||||
|
events_test_state = ETOT_INFO_CLEAR;
|
||||||
|
Serial.println("Events test : info event cleared");
|
||||||
|
Serial.print("system_bms_status: ");
|
||||||
|
Serial.println(system_bms_status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ETOT_INFO_CLEAR:
|
||||||
|
if (timer.elapsed()) {
|
||||||
|
timer.set_interval(8000);
|
||||||
|
events_test_state = ETOT_DEBUG;
|
||||||
|
set_event(EVENT_DUMMY_DEBUG, 111);
|
||||||
|
set_event(EVENT_DUMMY_DEBUG, 222); // 222 should show, occurrence 1
|
||||||
|
Serial.println("Events test : debug event set, data: 222");
|
||||||
|
Serial.print("system_bms_status: ");
|
||||||
|
Serial.println(system_bms_status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ETOT_DEBUG:
|
||||||
|
if (timer.elapsed()) {
|
||||||
|
timer.set_interval(8000);
|
||||||
|
clear_event(EVENT_DUMMY_DEBUG);
|
||||||
|
events_test_state = ETOT_DEBUG_CLEAR;
|
||||||
|
Serial.println("Events test : info event cleared");
|
||||||
|
Serial.print("system_bms_status: ");
|
||||||
|
Serial.println(system_bms_status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ETOT_DEBUG_CLEAR:
|
||||||
|
if (timer.elapsed()) {
|
||||||
|
timer.set_interval(8000);
|
||||||
|
events_test_state = ETOT_WARNING;
|
||||||
|
set_event(EVENT_DUMMY_WARNING, 234);
|
||||||
|
set_event(EVENT_DUMMY_WARNING, 121); // 121 should show, occurrence 1
|
||||||
|
Serial.println("Events test : warning event set, data: 121");
|
||||||
|
Serial.print("system_bms_status: ");
|
||||||
|
Serial.println(system_bms_status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ETOT_WARNING:
|
||||||
|
if (timer.elapsed()) {
|
||||||
|
timer.set_interval(8000);
|
||||||
|
clear_event(EVENT_DUMMY_WARNING);
|
||||||
|
events_test_state = ETOT_WARNING_CLEAR;
|
||||||
|
Serial.println("Events test : warning event cleared");
|
||||||
|
Serial.print("system_bms_status: ");
|
||||||
|
Serial.println(system_bms_status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ETOT_WARNING_CLEAR:
|
||||||
|
if (timer.elapsed()) {
|
||||||
|
timer.set_interval(8000);
|
||||||
|
events_test_state = ETOT_ERROR;
|
||||||
|
set_event(EVENT_DUMMY_ERROR, 221);
|
||||||
|
set_event(EVENT_DUMMY_ERROR, 133); // 133 should show, occurrence 1
|
||||||
|
Serial.println("Events test : error event set, data: 133");
|
||||||
|
Serial.print("system_bms_status: ");
|
||||||
|
Serial.println(system_bms_status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ETOT_ERROR:
|
||||||
|
if (timer.elapsed()) {
|
||||||
|
timer.set_interval(8000);
|
||||||
|
clear_event(EVENT_DUMMY_ERROR);
|
||||||
|
events_test_state = ETOT_ERROR_CLEAR;
|
||||||
|
Serial.println("Events test : error event cleared");
|
||||||
|
Serial.print("system_bms_status: ");
|
||||||
|
Serial.println(system_bms_status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ETOT_ERROR_CLEAR:
|
||||||
|
if (timer.elapsed()) {
|
||||||
|
timer.set_interval(8000);
|
||||||
|
events_test_state = ETOT_ERROR_LATCHED;
|
||||||
|
set_event_latched(EVENT_DUMMY_ERROR, 221);
|
||||||
|
set_event_latched(EVENT_DUMMY_ERROR, 133); // 133 should show, occurrence 1
|
||||||
|
Serial.println("Events test : latched error event set, data: 133");
|
||||||
|
Serial.print("system_bms_status: ");
|
||||||
|
Serial.println(system_bms_status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ETOT_ERROR_LATCHED:
|
||||||
|
if (timer.elapsed()) {
|
||||||
|
timer.set_interval(8000);
|
||||||
|
clear_event(EVENT_DUMMY_ERROR);
|
||||||
|
events_test_state = ETOT_DONE;
|
||||||
|
Serial.println("Events test : latched error event cleared?");
|
||||||
|
Serial.print("system_bms_status: ");
|
||||||
|
Serial.println(system_bms_status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ETOT_DONE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ MyTimer::MyTimer(unsigned long interval) : interval(interval) {
|
||||||
previous_millis = millis();
|
previous_millis = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MyTimer::elapsed() {
|
bool MyTimer::elapsed(void) {
|
||||||
unsigned long current_millis = millis();
|
unsigned long current_millis = millis();
|
||||||
if (current_millis - previous_millis >= interval) {
|
if (current_millis - previous_millis >= interval) {
|
||||||
previous_millis = current_millis;
|
previous_millis = current_millis;
|
||||||
|
@ -12,3 +12,12 @@ bool MyTimer::elapsed() {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyTimer::reset(void) {
|
||||||
|
previous_millis = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyTimer::set_interval(unsigned long interval) {
|
||||||
|
this->interval = interval;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
|
@ -7,14 +7,20 @@
|
||||||
|
|
||||||
class MyTimer {
|
class MyTimer {
|
||||||
public:
|
public:
|
||||||
|
/** Default constructor */
|
||||||
|
MyTimer() : interval(0), previous_millis(0) {}
|
||||||
|
|
||||||
/** interval in ms */
|
/** interval in ms */
|
||||||
MyTimer(unsigned long interval);
|
MyTimer(unsigned long interval);
|
||||||
/** Returns true and resets the timer if it has elapsed */
|
/** Returns true and resets the timer if it has elapsed */
|
||||||
bool elapsed();
|
bool elapsed(void);
|
||||||
|
void reset(void);
|
||||||
|
void set_interval(unsigned long interval);
|
||||||
|
|
||||||
private:
|
|
||||||
unsigned long interval;
|
unsigned long interval;
|
||||||
unsigned long previous_millis;
|
unsigned long previous_millis;
|
||||||
|
|
||||||
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __MYTIMER_H__
|
#endif // __MYTIMER_H__
|
||||||
|
|
|
@ -18,9 +18,9 @@ String cellmonitor_processor(const String& var) {
|
||||||
|
|
||||||
// Display max, min, and deviation voltage values
|
// Display max, min, and deviation voltage values
|
||||||
content += "<div class='voltage-values'>";
|
content += "<div class='voltage-values'>";
|
||||||
content += "Max Voltage: " + String(cell_max_voltage) + " mV<br>";
|
content += "Max Voltage: " + String(system_cell_max_voltage_mV) + " mV<br>";
|
||||||
content += "Min Voltage: " + String(cell_min_voltage) + " mV<br>";
|
content += "Min Voltage: " + String(system_cell_min_voltage_mV) + " mV<br>";
|
||||||
int deviation = cell_max_voltage - cell_min_voltage;
|
int deviation = system_cell_max_voltage_mV - system_cell_min_voltage_mV;
|
||||||
content += "Voltage Deviation: " + String(deviation) + " mV";
|
content += "Voltage Deviation: " + String(deviation) + " mV";
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
|
|
||||||
|
@ -28,14 +28,14 @@ String cellmonitor_processor(const String& var) {
|
||||||
content += "<div class='container'>";
|
content += "<div class='container'>";
|
||||||
for (int i = 0; i < 120; ++i) {
|
for (int i = 0; i < 120; ++i) {
|
||||||
// Skip empty values
|
// Skip empty values
|
||||||
if (cellvoltages[i] == 0) {
|
if (system_cellvoltages_mV[i] == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String cellContent = "Cell " + String(i + 1) + "<br>" + String(cellvoltages[i]) + " mV";
|
String cellContent = "Cell " + String(i + 1) + "<br>" + String(system_cellvoltages_mV[i]) + " mV";
|
||||||
|
|
||||||
// Check if the cell voltage is below 3000, apply red color
|
// Check if the cell voltage is below 3000, apply red color
|
||||||
if (cellvoltages[i] < 3000) {
|
if (system_cellvoltages_mV[i] < 3000) {
|
||||||
cellContent = "<span class='low-voltage'>" + cellContent + "</span>";
|
cellContent = "<span class='low-voltage'>" + cellContent + "</span>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Replaces placeholder with content section in web page
|
* @brief Replaces placeholder with content section in web page
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "events_html.h"
|
#include "events_html.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "../utils/events.h"
|
||||||
|
|
||||||
const char EVENTS_HTML_START[] = R"=====(
|
const char EVENTS_HTML_START[] = R"=====(
|
||||||
<style>body{background-color:#000;color:#fff}.event-log{display:flex;flex-direction:column}.event{display:flex;flex-wrap:wrap;border:1px solid #fff;padding:10px}.event>div{flex:1;min-width:100px;max-width:90%;word-break:break-word}</style><div style="background-color:#303e47;padding:10px;margin-bottom:10px;border-radius:25px"><div class="event-log"><div class="event" style="background-color:#1e2c33;font-weight:700"><div>Event Type</div><div>Severity</div><div>Last Event</div><div>Count</div><div>Data</div><div>Message</div></div>
|
<style>body{background-color:#000;color:#fff}.event-log{display:flex;flex-direction:column}.event{display:flex;flex-wrap:wrap;border:1px solid #fff;padding:10px}.event>div{flex:1;min-width:100px;max-width:90%;word-break:break-word}</style><div style="background-color:#303e47;padding:10px;margin-bottom:10px;border-radius:25px"><div class="event-log"><div class="event" style="background-color:#1e2c33;font-weight:700"><div>Event Type</div><div>Severity</div><div>Last Event</div><div>Count</div><div>Data</div><div>Message</div></div>
|
||||||
|
@ -17,18 +18,22 @@ String events_processor(const String& var) {
|
||||||
content.reserve(5000);
|
content.reserve(5000);
|
||||||
// Page format
|
// Page format
|
||||||
content.concat(FPSTR(EVENTS_HTML_START));
|
content.concat(FPSTR(EVENTS_HTML_START));
|
||||||
|
const EVENTS_STRUCT_TYPE* event_pointer;
|
||||||
for (int i = 0; i < EVENT_NOF_EVENTS; i++) {
|
for (int i = 0; i < EVENT_NOF_EVENTS; i++) {
|
||||||
Serial.println("Event: " + String(get_event_enum_string(static_cast<EVENTS_ENUM_TYPE>(i))) +
|
event_pointer = get_event_pointer((EVENTS_ENUM_TYPE)i);
|
||||||
" count: " + String(entries[i].occurences) + " seconds: " + String(entries[i].timestamp) +
|
EVENTS_ENUM_TYPE event_handle = static_cast<EVENTS_ENUM_TYPE>(i);
|
||||||
" data: " + String(entries[i].data));
|
Serial.println("Event: " + String(get_event_enum_string(event_handle)) +
|
||||||
if (entries[i].occurences > 0) {
|
" count: " + String(event_pointer->occurences) + " seconds: " + String(event_pointer->timestamp) +
|
||||||
|
" data: " + String(event_pointer->data) +
|
||||||
|
" level: " + String(get_event_level_string(event_handle)));
|
||||||
|
if (event_pointer->occurences > 0) {
|
||||||
content.concat("<div class='event'>");
|
content.concat("<div class='event'>");
|
||||||
content.concat("<div>" + String(get_event_enum_string(static_cast<EVENTS_ENUM_TYPE>(i))) + "</div>");
|
content.concat("<div>" + String(get_event_enum_string(event_handle)) + "</div>");
|
||||||
content.concat("<div>" + String(get_led_color_display_text(entries[i].led_color)) + "</div>");
|
content.concat("<div>" + String(get_event_level_string(event_handle)) + "</div>");
|
||||||
content.concat("<div class='last-event-seconds-ago'>" + String(entries[i].timestamp) + "</div>");
|
content.concat("<div class='last-event-seconds-ago'>" + String(event_pointer->timestamp) + "</div>");
|
||||||
content.concat("<div>" + String(entries[i].occurences) + "</div>");
|
content.concat("<div>" + String(event_pointer->occurences) + "</div>");
|
||||||
content.concat("<div>" + String(entries[i].data) + "</div>");
|
content.concat("<div>" + String(event_pointer->data) + "</div>");
|
||||||
content.concat("<div>" + String(get_event_message(static_cast<EVENTS_ENUM_TYPE>(i))) + "</div>");
|
content.concat("<div>" + String(get_event_message_string(event_handle)) + "</div>");
|
||||||
content.concat("<div class='timestamp' style='display:none;'>" + String(millis() / 1000) + "</div>");
|
content.concat("<div class='timestamp' style='display:none;'>" + String(millis() / 1000) + "</div>");
|
||||||
content.concat("</div>"); // End of event row
|
content.concat("</div>"); // End of event row
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
#ifndef EVENTS_H
|
#ifndef EVENTS_H
|
||||||
#define EVENTS_H
|
#define EVENTS_H
|
||||||
|
|
||||||
#include "../utils/events.h"
|
#include <Arduino.h>
|
||||||
|
|
||||||
extern EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Replaces placeholder with content section in web page
|
* @brief Replaces placeholder with content section in web page
|
||||||
|
|
|
@ -15,6 +15,8 @@ String settings_processor(const String& var) {
|
||||||
// Show current settings with edit buttons and input fields
|
// Show current settings with edit buttons and input fields
|
||||||
content += "<h4 style='color: white;'>Battery capacity: <span id='BATTERY_WH_MAX'>" + String(BATTERY_WH_MAX) +
|
content += "<h4 style='color: white;'>Battery capacity: <span id='BATTERY_WH_MAX'>" + String(BATTERY_WH_MAX) +
|
||||||
" Wh </span> <button onclick='editWh()'>Edit</button></h4>";
|
" Wh </span> <button onclick='editWh()'>Edit</button></h4>";
|
||||||
|
content += "<h4 style='color: white;'>Rescale SOC: <span id='USE_SCALED_SOC'>" + String(USE_SCALED_SOC) +
|
||||||
|
"</span> <button onclick='editUseScaledSOC()'>Edit</button></h4>";
|
||||||
content += "<h4 style='color: white;'>SOC max percentage: " + String(MAXPERCENTAGE / 10.0, 1) +
|
content += "<h4 style='color: white;'>SOC max percentage: " + String(MAXPERCENTAGE / 10.0, 1) +
|
||||||
" </span> <button onclick='editSocMax()'>Edit</button></h4>";
|
" </span> <button onclick='editSocMax()'>Edit</button></h4>";
|
||||||
content += "<h4 style='color: white;'>SOC min percentage: " + String(MINPERCENTAGE / 10.0, 1) +
|
content += "<h4 style='color: white;'>SOC min percentage: " + String(MINPERCENTAGE / 10.0, 1) +
|
||||||
|
@ -29,7 +31,7 @@ String settings_processor(const String& var) {
|
||||||
#ifdef TEST_FAKE_BATTERY
|
#ifdef TEST_FAKE_BATTERY
|
||||||
// Start a new block with blue background color
|
// Start a new block with blue background color
|
||||||
content += "<div style='background-color: #2E37AD; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
content += "<div style='background-color: #2E37AD; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
||||||
float voltageFloat = static_cast<float>(battery_voltage) / 10.0; // Convert to float and divide by 10
|
float voltageFloat = static_cast<float>(system_battery_voltage_dV) / 10.0; // Convert to float and divide by 10
|
||||||
content += "<h4 style='color: white;'>Fake battery voltage: " + String(voltageFloat, 1) +
|
content += "<h4 style='color: white;'>Fake battery voltage: " + String(voltageFloat, 1) +
|
||||||
" V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>";
|
" V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>";
|
||||||
|
|
||||||
|
@ -69,14 +71,26 @@ String settings_processor(const String& var) {
|
||||||
|
|
||||||
content += "<script>";
|
content += "<script>";
|
||||||
content += "function editWh() {";
|
content += "function editWh() {";
|
||||||
content += "var value = prompt('How much energy the battery can store. Enter new Wh value (1-65000):');";
|
content += "var value = prompt('How much energy the battery can store. Enter new Wh value (1-120000):');";
|
||||||
content += "if (value !== null) {";
|
content += "if (value !== null) {";
|
||||||
content += " if (value >= 1 && value <= 65000) {";
|
content += " if (value >= 1 && value <= 120000) {";
|
||||||
content += " var xhr = new XMLHttpRequest();";
|
content += " var xhr = new XMLHttpRequest();";
|
||||||
content += " xhr.open('GET', '/updateBatterySize?value=' + value, true);";
|
content += " xhr.open('GET', '/updateBatterySize?value=' + value, true);";
|
||||||
content += " xhr.send();";
|
content += " xhr.send();";
|
||||||
content += " } else {";
|
content += " } else {";
|
||||||
content += " alert('Invalid value. Please enter a value between 1 and 65000.');";
|
content += " alert('Invalid value. Please enter a value between 1 and 120000.');";
|
||||||
|
content += " }";
|
||||||
|
content += "}";
|
||||||
|
content += "}";
|
||||||
|
content += "function editUseScaledSOC() {";
|
||||||
|
content += "var value = prompt('Should SOC% be scaled? (0 = No, 1 = Yes):');";
|
||||||
|
content += "if (value !== null) {";
|
||||||
|
content += " if (value == 0 || value == 1) {";
|
||||||
|
content += " var xhr = new XMLHttpRequest();";
|
||||||
|
content += " xhr.open('GET', '/updateUseScaledSOC?value=' + value, true);";
|
||||||
|
content += " xhr.send();";
|
||||||
|
content += " } else {";
|
||||||
|
content += " alert('Invalid value. Please enter a value between 0 and 1.');";
|
||||||
content += " }";
|
content += " }";
|
||||||
content += "}";
|
content += "}";
|
||||||
content += "}";
|
content += "}";
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#include "../../../USER_SETTINGS.h" // Needed for WiFi ssid and password
|
#include "../../../USER_SETTINGS.h" // Needed for WiFi ssid and password
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Replaces placeholder with content section in web page
|
* @brief Replaces placeholder with content section in web page
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "webserver.h"
|
#include "webserver.h"
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
|
#include "../utils/events.h"
|
||||||
|
#include "../utils/timer.h"
|
||||||
|
|
||||||
// Create AsyncWebServer object on port 80
|
// Create AsyncWebServer object on port 80
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
|
@ -20,6 +22,9 @@ enum WifiState {
|
||||||
|
|
||||||
WifiState wifi_state = INIT;
|
WifiState wifi_state = INIT;
|
||||||
|
|
||||||
|
MyTimer ota_timeout_timer = MyTimer(5000);
|
||||||
|
bool ota_active = false;
|
||||||
|
|
||||||
unsigned const long WIFI_MONITOR_INTERVAL_TIME = 15000;
|
unsigned const long WIFI_MONITOR_INTERVAL_TIME = 15000;
|
||||||
unsigned const long INIT_WIFI_CONNECT_TIMEOUT = 8000; // Timeout for initial WiFi connect in milliseconds
|
unsigned const long INIT_WIFI_CONNECT_TIMEOUT = 8000; // Timeout for initial WiFi connect in milliseconds
|
||||||
unsigned const long DEFAULT_WIFI_RECONNECT_INTERVAL = 1000; // Default WiFi reconnect interval in ms
|
unsigned const long DEFAULT_WIFI_RECONNECT_INTERVAL = 1000; // Default WiFi reconnect interval in ms
|
||||||
|
@ -69,6 +74,18 @@ void init_webserver() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Route for editing USE_SCALED_SOC
|
||||||
|
server.on("/updateUseScaledSOC", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
|
if (request->hasParam("value")) {
|
||||||
|
String value = request->getParam("value")->value();
|
||||||
|
USE_SCALED_SOC = value.toInt();
|
||||||
|
storeSettings();
|
||||||
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
|
} else {
|
||||||
|
request->send(400, "text/plain", "Bad Request");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Route for editing SOCMax
|
// Route for editing SOCMax
|
||||||
server.on("/updateSocMax", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/updateSocMax", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (request->hasParam("value")) {
|
if (request->hasParam("value")) {
|
||||||
|
@ -127,7 +144,7 @@ void init_webserver() {
|
||||||
String value = request->getParam("value")->value();
|
String value = request->getParam("value")->value();
|
||||||
float val = value.toFloat();
|
float val = value.toFloat();
|
||||||
|
|
||||||
battery_voltage = val * 10;
|
system_battery_voltage_dV = val * 10;
|
||||||
|
|
||||||
request->send(200, "text/plain", "Updated successfully");
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
});
|
});
|
||||||
|
@ -298,6 +315,14 @@ void wifi_monitor() {
|
||||||
Serial.println(" Hostname: " + String(WiFi.getHostname()));
|
Serial.println(" Hostname: " + String(WiFi.getHostname()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ota_active && ota_timeout_timer.elapsed()) {
|
||||||
|
// OTA timeout, try to restore can and clear the update event
|
||||||
|
ESP32Can.CANInit();
|
||||||
|
clear_event(EVENT_OTA_UPDATE);
|
||||||
|
set_event(EVENT_OTA_UPDATE_TIMEOUT, 0);
|
||||||
|
ota_active = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_WiFi_STA(const char* ssid, const char* password, const uint8_t wifi_channel) {
|
void init_WiFi_STA(const char* ssid, const char* password, const uint8_t wifi_channel) {
|
||||||
|
@ -467,57 +492,42 @@ String processor(const String& var) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display battery statistics within this block
|
// Display battery statistics within this block
|
||||||
float socFloat = static_cast<float>(SOC) / 100.0; // Convert to float and divide by 100
|
float socRealFloat = static_cast<float>(system_real_SOC_pptt) / 100.0; // Convert to float and divide by 100
|
||||||
float sohFloat = static_cast<float>(StateOfHealth) / 100.0; // Convert to float and divide by 100
|
float socScaledFloat = static_cast<float>(system_scaled_SOC_pptt) / 100.0; // Convert to float and divide by 100
|
||||||
float voltageFloat = static_cast<float>(battery_voltage) / 10.0; // Convert to float and divide by 10
|
float sohFloat = static_cast<float>(system_SOH_pptt) / 100.0; // Convert to float and divide by 100
|
||||||
float currentFloat = 0;
|
float voltageFloat = static_cast<float>(system_battery_voltage_dV) / 10.0; // Convert to float and divide by 10
|
||||||
if (battery_current > 32767) { //Handle negative values on this unsigned value
|
float currentFloat = static_cast<float>(system_battery_current_dA) / 10.0; // Convert to float and divide by 10
|
||||||
currentFloat = static_cast<float>(-(65535 - battery_current)) / 10.0; // Convert to float and divide by 10
|
float powerFloat = static_cast<float>(system_active_power_W); // Convert to float
|
||||||
} else {
|
float tempMaxFloat = static_cast<float>(system_temperature_max_dC) / 10.0; // Convert to float
|
||||||
currentFloat = static_cast<float>(battery_current) / 10.0; // Convert to float and divide by 10
|
float tempMinFloat = static_cast<float>(system_temperature_min_dC) / 10.0; // Convert to float
|
||||||
}
|
|
||||||
float powerFloat = 0;
|
content += "<h4 style='color: white;'>Real SOC: " + String(socRealFloat, 2) + "</h4>";
|
||||||
if (stat_batt_power > 32767) { //Handle negative values on this unsigned value
|
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) + "</h4>";
|
||||||
powerFloat = static_cast<float>(-(65535 - stat_batt_power));
|
|
||||||
} else {
|
|
||||||
powerFloat = static_cast<float>(stat_batt_power);
|
|
||||||
}
|
|
||||||
float tempMaxFloat = 0;
|
|
||||||
float tempMinFloat = 0;
|
|
||||||
if (temperature_max > 32767) { //Handle negative values on this unsigned value
|
|
||||||
tempMaxFloat = static_cast<float>(-(65536 - temperature_max)) / 10.0; // Convert to float and divide by 10
|
|
||||||
} else {
|
|
||||||
tempMaxFloat = static_cast<float>(temperature_max) / 10.0; // Convert to float and divide by 10
|
|
||||||
}
|
|
||||||
if (temperature_min > 32767) { //Handle negative values on this unsigned value
|
|
||||||
tempMinFloat = static_cast<float>(-(65536 - temperature_min)) / 10.0; // Convert to float and divide by 10
|
|
||||||
} else {
|
|
||||||
tempMinFloat = static_cast<float>(temperature_min) / 10.0; // Convert to float and divide by 10
|
|
||||||
}
|
|
||||||
content += "<h4 style='color: white;'>SOC: " + String(socFloat, 2) + "</h4>";
|
|
||||||
content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "</h4>";
|
content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "</h4>";
|
||||||
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) + " V</h4>";
|
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) + " V</h4>";
|
||||||
content += "<h4 style='color: white;'>Current: " + String(currentFloat, 1) + " A</h4>";
|
content += "<h4 style='color: white;'>Current: " + String(currentFloat, 1) + " A</h4>";
|
||||||
content += formatPowerValue("Power", powerFloat, "", 1);
|
content += formatPowerValue("Power", powerFloat, "", 1);
|
||||||
content += formatPowerValue("Total capacity", capacity_Wh, "h", 0);
|
content += formatPowerValue("Total capacity", system_capacity_Wh, "h", 0);
|
||||||
content += formatPowerValue("Remaining capacity", remaining_capacity_Wh, "h", 1);
|
content += formatPowerValue("Remaining capacity", system_remaining_capacity_Wh, "h", 1);
|
||||||
content += formatPowerValue("Max discharge power", max_target_discharge_power, "", 1);
|
content += formatPowerValue("Max discharge power", system_max_discharge_power_W, "", 1);
|
||||||
content += formatPowerValue("Max charge power", max_target_charge_power, "", 1);
|
content += formatPowerValue("Max charge power", system_max_charge_power_W, "", 1);
|
||||||
content += "<h4>Cell max: " + String(cell_max_voltage) + " mV</h4>";
|
content += "<h4>Cell max: " + String(system_cell_max_voltage_mV) + " mV</h4>";
|
||||||
content += "<h4>Cell min: " + String(cell_min_voltage) + " mV</h4>";
|
content += "<h4>Cell min: " + String(system_cell_min_voltage_mV) + " mV</h4>";
|
||||||
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " C</h4>";
|
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " C</h4>";
|
||||||
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " C</h4>";
|
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " C</h4>";
|
||||||
if (bms_status == 3) {
|
if (system_bms_status == ACTIVE) {
|
||||||
content += "<h4>BMS Status: OK </h4>";
|
content += "<h4>BMS Status: OK </h4>";
|
||||||
|
} else if (system_bms_status == UPDATING) {
|
||||||
|
content += "<h4>BMS Status: UPDATING </h4>";
|
||||||
} else {
|
} else {
|
||||||
content += "<h4>BMS Status: FAULT </h4>";
|
content += "<h4>BMS Status: FAULT </h4>";
|
||||||
}
|
}
|
||||||
if (bms_char_dis_status == 2) {
|
if (system_battery_current_dA == 0) {
|
||||||
content += "<h4>Battery charging!</h4>";
|
|
||||||
} else if (bms_char_dis_status == 1) {
|
|
||||||
content += "<h4>Battery discharging!</h4>";
|
|
||||||
} else { //0 idle
|
|
||||||
content += "<h4>Battery idle</h4>";
|
content += "<h4>Battery idle</h4>";
|
||||||
|
} else if (system_battery_current_dA < 0) {
|
||||||
|
content += "<h4>Battery discharging!</h4>";
|
||||||
|
} else { // > 0
|
||||||
|
content += "<h4>Battery charging!</h4>";
|
||||||
}
|
}
|
||||||
content += "<h4>Automatic contactor closing allowed:</h4>";
|
content += "<h4>Automatic contactor closing allowed:</h4>";
|
||||||
content += "<h4>Battery: ";
|
content += "<h4>Battery: ";
|
||||||
|
@ -630,19 +640,23 @@ String processor(const String& var) {
|
||||||
|
|
||||||
void onOTAStart() {
|
void onOTAStart() {
|
||||||
// Log when OTA has started
|
// Log when OTA has started
|
||||||
Serial.println("OTA update started!");
|
|
||||||
ESP32Can.CANStop();
|
ESP32Can.CANStop();
|
||||||
bms_status = UPDATING; //Inform inverter that we are updating
|
set_event(EVENT_OTA_UPDATE, 0);
|
||||||
LEDcolor = BLUE;
|
|
||||||
|
// If already set, make a new attempt
|
||||||
|
clear_event(EVENT_OTA_UPDATE_TIMEOUT);
|
||||||
|
ota_active = true;
|
||||||
|
ota_timeout_timer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onOTAProgress(size_t current, size_t final) {
|
void onOTAProgress(size_t current, size_t final) {
|
||||||
bms_status = UPDATING; //Inform inverter that we are updating
|
|
||||||
LEDcolor = BLUE;
|
|
||||||
// Log every 1 second
|
// Log every 1 second
|
||||||
if (millis() - ota_progress_millis > 1000) {
|
if (millis() - ota_progress_millis > 1000) {
|
||||||
ota_progress_millis = millis();
|
ota_progress_millis = millis();
|
||||||
Serial.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
|
Serial.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
|
||||||
|
|
||||||
|
// Reset the "watchdog"
|
||||||
|
ota_timeout_timer.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -652,16 +666,19 @@ void onOTAEnd(bool success) {
|
||||||
Serial.println("OTA update finished successfully!");
|
Serial.println("OTA update finished successfully!");
|
||||||
} else {
|
} else {
|
||||||
Serial.println("There was an error during OTA update!");
|
Serial.println("There was an error during OTA update!");
|
||||||
|
|
||||||
|
// If we fail without a timeout, try to restore CAN
|
||||||
|
ESP32Can.CANInit();
|
||||||
}
|
}
|
||||||
bms_status = UPDATING; //Inform inverter that we are updating
|
ota_active = false;
|
||||||
LEDcolor = BLUE;
|
clear_event(EVENT_OTA_UPDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> // This function makes power values appear as W when under 1000, and kW when over
|
template <typename T> // This function makes power values appear as W when under 1000, and kW when over
|
||||||
String formatPowerValue(String label, T value, String unit, int precision) {
|
String formatPowerValue(String label, T value, String unit, int precision) {
|
||||||
String result = "<h4 style='color: white;'>" + label + ": ";
|
String result = "<h4 style='color: white;'>" + label + ": ";
|
||||||
|
|
||||||
if (std::is_same<T, float>::value || std::is_same<T, uint16_t>::value) {
|
if (std::is_same<T, float>::value || std::is_same<T, uint16_t>::value || std::is_same<T, uint32_t>::value) {
|
||||||
float convertedValue = static_cast<float>(value);
|
float convertedValue = static_cast<float>(value);
|
||||||
|
|
||||||
if (convertedValue >= 1000.0 || convertedValue <= -1000.0) {
|
if (convertedValue >= 1000.0 || convertedValue <= -1000.0) {
|
||||||
|
|
|
@ -17,22 +17,25 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern const char* version_number; // The current software version, shown on webserver
|
extern const char* version_number; // The current software version, shown on webserver
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh; //Wh, 0-60000
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t max_target_discharge_power; //W, 0-60000
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power; //W, 0-60000
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status; //Enum, 0-5
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint8_t bms_char_dis_status; //Enum, 0-2
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000 , Stores the highest cell millivolt value
|
||||||
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
extern uint8_t LEDcolor; //Enum, 0-10
|
extern uint8_t LEDcolor; //Enum, 0-10
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
||||||
|
|
|
@ -106,28 +106,26 @@ CAN_frame_t BYD_210 = {.FIR = {.B =
|
||||||
.MsgID = 0x210,
|
.MsgID = 0x210,
|
||||||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
|
||||||
static int discharge_current = 0;
|
static uint16_t discharge_current = 0;
|
||||||
static int charge_current = 0;
|
static uint16_t charge_current = 0;
|
||||||
static int initialDataSent = 0;
|
|
||||||
static int16_t temperature_average = 0;
|
static int16_t temperature_average = 0;
|
||||||
static int16_t temp_min = 0;
|
static uint16_t inverter_voltage = 0;
|
||||||
static int16_t temp_max = 0;
|
static uint16_t inverter_SOC = 0;
|
||||||
static int inverter_voltage = 0;
|
|
||||||
static int inverter_SOC = 0;
|
|
||||||
static long inverter_timestamp = 0;
|
static long inverter_timestamp = 0;
|
||||||
|
static bool initialDataSent = 0;
|
||||||
|
|
||||||
void update_values_can_byd() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
void update_values_can_byd() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
||||||
//Calculate values
|
//Calculate values
|
||||||
charge_current =
|
charge_current = ((system_max_charge_power_W * 10) /
|
||||||
((max_target_charge_power * 10) / max_voltage); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
||||||
//The above calculation results in (30 000*10)/3700=81A
|
//The above calculation results in (30 000*10)/3700=81A
|
||||||
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
|
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
|
||||||
if (charge_current > MAXCHARGEAMP) {
|
if (charge_current > MAXCHARGEAMP) {
|
||||||
charge_current = MAXCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
charge_current = MAXCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
||||||
}
|
}
|
||||||
|
|
||||||
discharge_current = ((max_target_discharge_power * 10) /
|
discharge_current = ((system_max_discharge_power_W * 10) /
|
||||||
max_voltage); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
||||||
//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)
|
||||||
if (discharge_current > MAXDISCHARGEAMP) {
|
if (discharge_current > MAXDISCHARGEAMP) {
|
||||||
|
@ -135,17 +133,15 @@ void update_values_can_byd() { //This function maps all the values fetched from
|
||||||
MAXDISCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
MAXDISCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
||||||
}
|
}
|
||||||
|
|
||||||
temp_min = temperature_min; //Convert from unsigned to signed
|
temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
|
||||||
temp_max = temperature_max;
|
|
||||||
temperature_average = ((temp_max + temp_min) / 2);
|
|
||||||
|
|
||||||
//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] = (max_voltage >> 8);
|
BYD_110.data.u8[0] = (system_max_design_voltage_dV >> 8);
|
||||||
BYD_110.data.u8[1] = (max_voltage & 0x00FF);
|
BYD_110.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
|
||||||
//Minvoltage (eg 300.0V = 3000 , 16bits long)
|
//Minvoltage (eg 300.0V = 3000 , 16bits long)
|
||||||
BYD_110.data.u8[2] = (min_voltage >> 8);
|
BYD_110.data.u8[2] = (system_min_design_voltage_dV >> 8);
|
||||||
BYD_110.data.u8[3] = (min_voltage & 0x00FF);
|
BYD_110.data.u8[3] = (system_min_design_voltage_dV & 0x00FF);
|
||||||
//Maximum discharge power allowed (Unit: A+1)
|
//Maximum discharge power allowed (Unit: A+1)
|
||||||
BYD_110.data.u8[4] = (discharge_current >> 8);
|
BYD_110.data.u8[4] = (discharge_current >> 8);
|
||||||
BYD_110.data.u8[5] = (discharge_current & 0x00FF);
|
BYD_110.data.u8[5] = (discharge_current & 0x00FF);
|
||||||
|
@ -154,11 +150,11 @@ void update_values_can_byd() { //This function maps all the values fetched from
|
||||||
BYD_110.data.u8[7] = (charge_current & 0x00FF);
|
BYD_110.data.u8[7] = (charge_current & 0x00FF);
|
||||||
|
|
||||||
//SOC (100.00%)
|
//SOC (100.00%)
|
||||||
BYD_150.data.u8[0] = (SOC >> 8);
|
BYD_150.data.u8[0] = (system_scaled_SOC_pptt >> 8);
|
||||||
BYD_150.data.u8[1] = (SOC & 0x00FF);
|
BYD_150.data.u8[1] = (system_scaled_SOC_pptt & 0x00FF);
|
||||||
//StateOfHealth (100.00%)
|
//StateOfHealth (100.00%)
|
||||||
BYD_150.data.u8[2] = (StateOfHealth >> 8);
|
BYD_150.data.u8[2] = (system_SOH_pptt >> 8);
|
||||||
BYD_150.data.u8[3] = (StateOfHealth & 0x00FF);
|
BYD_150.data.u8[3] = (system_SOH_pptt & 0x00FF);
|
||||||
//Maximum charge power allowed (Unit: A+1)
|
//Maximum charge power allowed (Unit: A+1)
|
||||||
BYD_150.data.u8[4] = (charge_current >> 8);
|
BYD_150.data.u8[4] = (charge_current >> 8);
|
||||||
BYD_150.data.u8[5] = (charge_current & 0x00FF);
|
BYD_150.data.u8[5] = (charge_current & 0x00FF);
|
||||||
|
@ -167,21 +163,21 @@ void update_values_can_byd() { //This function maps all the values fetched from
|
||||||
BYD_150.data.u8[7] = (discharge_current & 0x00FF);
|
BYD_150.data.u8[7] = (discharge_current & 0x00FF);
|
||||||
|
|
||||||
//Voltage (ex 370.0)
|
//Voltage (ex 370.0)
|
||||||
BYD_1D0.data.u8[0] = (battery_voltage >> 8);
|
BYD_1D0.data.u8[0] = (system_battery_voltage_dV >> 8);
|
||||||
BYD_1D0.data.u8[1] = (battery_voltage & 0x00FF);
|
BYD_1D0.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
|
||||||
//Current (ex 81.0A)
|
//Current (ex 81.0A)
|
||||||
BYD_1D0.data.u8[2] = (battery_current >> 8);
|
BYD_1D0.data.u8[2] = (system_battery_current_dA >> 8);
|
||||||
BYD_1D0.data.u8[3] = (battery_current & 0x00FF);
|
BYD_1D0.data.u8[3] = (system_battery_current_dA & 0x00FF);
|
||||||
//Temperature average
|
//Temperature average
|
||||||
BYD_1D0.data.u8[4] = (temperature_average >> 8);
|
BYD_1D0.data.u8[4] = (temperature_average >> 8);
|
||||||
BYD_1D0.data.u8[5] = (temperature_average & 0x00FF);
|
BYD_1D0.data.u8[5] = (temperature_average & 0x00FF);
|
||||||
|
|
||||||
//Temperature max
|
//Temperature max
|
||||||
BYD_210.data.u8[0] = (temperature_max >> 8);
|
BYD_210.data.u8[0] = (system_temperature_max_dC >> 8);
|
||||||
BYD_210.data.u8[1] = (temperature_max & 0x00FF);
|
BYD_210.data.u8[1] = (system_temperature_max_dC & 0x00FF);
|
||||||
//Temperature min
|
//Temperature min
|
||||||
BYD_210.data.u8[2] = (temperature_min >> 8);
|
BYD_210.data.u8[2] = (system_temperature_min_dC >> 8);
|
||||||
BYD_210.data.u8[3] = (temperature_min & 0x00FF);
|
BYD_210.data.u8[3] = (system_temperature_min_dC & 0x00FF);
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
if (char1_151 != 0) {
|
if (char1_151 != 0) {
|
||||||
|
|
|
@ -5,24 +5,28 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
extern uint16_t SOC;
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t StateOfHealth;
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage;
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_current;
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh;
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t remaining_capacity_Wh;
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t max_target_discharge_power;
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_charge_power;
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status;
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status;
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t stat_batt_power;
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min;
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max;
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t CANerror;
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t min_voltage;
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t max_voltage;
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
extern bool inverterAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
void update_values_can_byd();
|
void update_values_can_byd();
|
||||||
void send_can_byd();
|
void send_can_byd();
|
||||||
|
|
|
@ -32,15 +32,19 @@ void handle_update_data_modbusp201_byd() {
|
||||||
static uint16_t system_data[13];
|
static uint16_t system_data[13];
|
||||||
system_data[0] = 0; // Id.: p201 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
system_data[0] = 0; // Id.: p201 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
||||||
system_data[1] = 0; // Id.: p202 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
system_data[1] = 0; // Id.: p202 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
||||||
|
if (system_capacity_Wh > 60000) {
|
||||||
|
system_data[2] = 60000;
|
||||||
|
} else {
|
||||||
system_data[2] =
|
system_data[2] =
|
||||||
(capacity_Wh); // Id.: p203 Value.: 32000 Scaled value.: 32kWh Comment.: Capacity rated, maximum value is 60000 (60kWh)
|
(system_capacity_Wh); // Id.: p203 Value.: 32000 Scaled value.: 32kWh Comment.: Capacity rated, maximum value is 60000 (60kWh)
|
||||||
|
}
|
||||||
system_data[3] = MAX_POWER; // Id.: p204 Value.: 32000 Scaled value.: 32kWh Comment.: Nominal capacity
|
system_data[3] = MAX_POWER; // Id.: p204 Value.: 32000 Scaled value.: 32kWh Comment.: Nominal capacity
|
||||||
system_data[4] =
|
system_data[4] =
|
||||||
MAX_POWER; // Id.: p205 Value.: 14500 Scaled value.: 30,42kW Comment.: Max Charge/Discharge Power=10.24kW (lowest value of 204 and 205 will be enforced by Gen24)
|
MAX_POWER; // Id.: p205 Value.: 14500 Scaled value.: 30,42kW Comment.: Max Charge/Discharge Power=10.24kW (lowest value of 204 and 205 will be enforced by Gen24)
|
||||||
system_data[5] =
|
system_data[5] =
|
||||||
(max_voltage); // Id.: p206 Value.: 3667 Scaled value.: 362,7VDC Comment.: Max Voltage, if higher charging is not possible (goes into forced discharge)
|
(system_max_design_voltage_dV); // Id.: p206 Value.: 3667 Scaled value.: 362,7VDC Comment.: Max Voltage, if higher charging is not possible (goes into forced discharge)
|
||||||
system_data[6] =
|
system_data[6] =
|
||||||
(min_voltage); // Id.: p207 Value.: 2776 Scaled value.: 277,6VDC Comment.: Min Voltage, if lower Gen24 disables battery
|
(system_min_design_voltage_dV); // Id.: p207 Value.: 2776 Scaled value.: 277,6VDC Comment.: Min Voltage, if lower Gen24 disables battery
|
||||||
system_data[7] =
|
system_data[7] =
|
||||||
53248; // Id.: p208 Value.: 53248 Scaled value.: 53248 Comment.: Always 53248 for this BYD, Peak Charge power?
|
53248; // Id.: p208 Value.: 53248 Scaled value.: 53248 Comment.: Always 53248 for this BYD, Peak Charge power?
|
||||||
system_data[8] = 10; // Id.: p209 Value.: 10 Scaled value.: 10 Comment.: Always 10
|
system_data[8] = 10; // Id.: p209 Value.: 10 Scaled value.: 10 Comment.: Always 10
|
||||||
|
@ -56,42 +60,62 @@ void handle_update_data_modbusp201_byd() {
|
||||||
void handle_update_data_modbusp301_byd() {
|
void handle_update_data_modbusp301_byd() {
|
||||||
// Store the data into the array
|
// Store the data into the array
|
||||||
static uint16_t battery_data[24];
|
static uint16_t battery_data[24];
|
||||||
if (battery_current == 0) {
|
|
||||||
|
static uint8_t bms_char_dis_status = STANDBY;
|
||||||
|
if (system_battery_current_dA == 0) {
|
||||||
bms_char_dis_status = STANDBY;
|
bms_char_dis_status = STANDBY;
|
||||||
} else if (battery_current > 32767) { //Negative value = Discharging
|
} else if (system_battery_current_dA < 0) { //Negative value = Discharging
|
||||||
bms_char_dis_status = DISCHARGING;
|
bms_char_dis_status = DISCHARGING;
|
||||||
} else { //Positive value = Charging
|
} else { //Positive value = Charging
|
||||||
bms_char_dis_status = CHARGING;
|
bms_char_dis_status = CHARGING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bms_status == ACTIVE) {
|
if (system_bms_status == ACTIVE) {
|
||||||
battery_data[8] =
|
battery_data[8] =
|
||||||
battery_voltage; // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V
|
system_battery_voltage_dV; // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V
|
||||||
} else {
|
} else {
|
||||||
battery_data[8] = 0;
|
battery_data[8] = 0;
|
||||||
}
|
}
|
||||||
battery_data[0] =
|
battery_data[0] =
|
||||||
bms_status; // Id.: p301 Value.: 3 Scaled value.: 3 Comment.: status(*): ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING]
|
system_bms_status; // Id.: p301 Value.: 3 Scaled value.: 3 Comment.: status(*): ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING]
|
||||||
battery_data[1] = 0; // Id.: p302 Value.: 0 Scaled value.: 0 Comment.: always 0
|
battery_data[1] = 0; // Id.: p302 Value.: 0 Scaled value.: 0 Comment.: always 0
|
||||||
battery_data[2] = 128 + bms_char_dis_status; // Id.: p303 Value.: 130 Scaled value.: 130 Comment.: mode(*): normal
|
battery_data[2] = 128 + bms_char_dis_status; // Id.: p303 Value.: 130 Scaled value.: 130 Comment.: mode(*): normal
|
||||||
battery_data[3] = SOC; // Id.: p304 Value.: 1700 Scaled value.: 50% Comment.: SOC: (50% would equal 5000)
|
battery_data[3] =
|
||||||
battery_data[4] = capacity_Wh; // Id.: p305 Value.: 32000 Scaled value.: 32kWh Comment.: tot cap:
|
system_scaled_SOC_pptt; // Id.: p304 Value.: 1700 Scaled value.: 50% Comment.: SOC: (50% would equal 5000)
|
||||||
|
if (system_capacity_Wh > 60000) {
|
||||||
|
battery_data[4] = 60000;
|
||||||
|
} else {
|
||||||
|
battery_data[4] = system_capacity_Wh; // Id.: p305 Value.: 32000 Scaled value.: 32kWh Comment.: tot cap:
|
||||||
|
}
|
||||||
|
if (system_remaining_capacity_Wh > 60000) {
|
||||||
|
battery_data[5] = 60000;
|
||||||
|
} else {
|
||||||
battery_data[5] =
|
battery_data[5] =
|
||||||
remaining_capacity_Wh; // Id.: p306 Value.: 13260 Scaled value.: 13,26kWh Comment.: remaining cap: 7.68kWh
|
system_remaining_capacity_Wh; // Id.: p306 Value.: 13260 Scaled value.: 13,26kWh Comment.: remaining cap: 7.68kWh
|
||||||
|
}
|
||||||
|
if (system_max_discharge_power_W > 30000) {
|
||||||
|
battery_data[6] = 30000;
|
||||||
|
} else {
|
||||||
battery_data[6] =
|
battery_data[6] =
|
||||||
max_target_discharge_power; // Id.: p307 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target discharge power: 0W (0W > restricts to no discharge)
|
system_max_discharge_power_W; // Id.: p307 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target discharge power: 0W (0W > restricts to no discharge)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (system_max_charge_power_W > 30000) {
|
||||||
|
battery_data[7] = 30000;
|
||||||
|
} else {
|
||||||
battery_data[7] =
|
battery_data[7] =
|
||||||
max_target_charge_power; // Id.: p308 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target charge power: 4.3kW (during charge), both 307&308 can be set (>0) at the same time
|
system_max_charge_power_W; // Id.: p308 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target charge power: 4.3kW (during charge), both 307&308 can be set (>0) at the same time
|
||||||
|
}
|
||||||
//Battery_data[8] set previously in function // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V
|
//Battery_data[8] set previously in function // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V
|
||||||
battery_data[9] =
|
battery_data[9] =
|
||||||
2000; // Id.: p310 Value.: 64121 Scaled value.: 6412,1W Comment.: Current Power to API: if>32768... -(65535-61760)=3775W
|
2000; // Id.: p310 Value.: 64121 Scaled value.: 6412,1W Comment.: Current Power to API: if>32768... -(65535-61760)=3775W
|
||||||
battery_data[10] =
|
battery_data[10] =
|
||||||
battery_voltage; // Id.: p311 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage inner: 173.2V (LEAF voltage is in whole volts, need to add a decimal)
|
system_battery_voltage_dV; // Id.: p311 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage inner: 173.2V
|
||||||
battery_data[11] = 2000; // Id.: p312 Value.: 64121 Scaled value.: 6412,1W Comment.: p310
|
battery_data[11] = 2000; // Id.: p312 Value.: 64121 Scaled value.: 6412,1W Comment.: p310
|
||||||
battery_data[12] =
|
battery_data[12] =
|
||||||
temperature_min; // Id.: p313 Value.: 75 Scaled value.: 7,5 Comment.: temp min: 7 degrees (if below 0....65535-t)
|
system_temperature_min_dC; // Id.: p313 Value.: 75 Scaled value.: 7,5 Comment.: temp min: 7 degrees (if below 0....65535-t)
|
||||||
battery_data[13] =
|
battery_data[13] =
|
||||||
temperature_max; // Id.: p314 Value.: 95 Scaled value.: 9,5 Comment.: temp max: 9 degrees (if below 0....65535-t)
|
system_temperature_max_dC; // Id.: p314 Value.: 95 Scaled value.: 9,5 Comment.: temp max: 9 degrees (if below 0....65535-t)
|
||||||
battery_data[14] = 0; // Id.: p315 Value.: 0 Scaled value.: 0 Comment.: always 0
|
battery_data[14] = 0; // Id.: p315 Value.: 0 Scaled value.: 0 Comment.: always 0
|
||||||
battery_data[15] = 0; // Id.: p316 Value.: 0 Scaled value.: 0 Comment.: always 0
|
battery_data[15] = 0; // Id.: p316 Value.: 0 Scaled value.: 0 Comment.: always 0
|
||||||
battery_data[16] = 16; // Id.: p317 Value.: 0 Scaled value.: 0 Comment.: counter charge hi
|
battery_data[16] = 16; // Id.: p317 Value.: 0 Scaled value.: 0 Comment.: counter charge hi
|
||||||
|
@ -103,27 +127,27 @@ void handle_update_data_modbusp301_byd() {
|
||||||
battery_data[21] =
|
battery_data[21] =
|
||||||
52064; // Id.: p322 Value.: 0 Scaled value.: 0 Comment.: counter discharge lo....65536*92+7448 = 6036760 Wh?
|
52064; // Id.: p322 Value.: 0 Scaled value.: 0 Comment.: counter discharge lo....65536*92+7448 = 6036760 Wh?
|
||||||
battery_data[22] = 230; // Id.: p323 Value.: 0 Scaled value.: 0 Comment.: device temperature (23 degrees)
|
battery_data[22] = 230; // Id.: p323 Value.: 0 Scaled value.: 0 Comment.: device temperature (23 degrees)
|
||||||
battery_data[23] = StateOfHealth; // Id.: p324 Value.: 9900 Scaled value.: 99% Comment.: SOH
|
battery_data[23] = system_SOH_pptt; // Id.: p324 Value.: 9900 Scaled value.: 99% Comment.: SOH
|
||||||
static uint16_t i = 300;
|
static uint16_t i = 300;
|
||||||
memcpy(&mbPV[i], battery_data, sizeof(battery_data));
|
memcpy(&mbPV[i], battery_data, sizeof(battery_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void verify_temperature_modbus() {
|
void verify_temperature_modbus() {
|
||||||
if (LFP_Chemistry) {
|
if (system_LFP_Chemistry) {
|
||||||
return; // Skip the following section
|
return; // Skip the following section
|
||||||
}
|
}
|
||||||
// This section checks if the battery temperature is negative, and incase it falls between -9.0 and -20.0C degrees
|
// This section checks if the battery temperature is negative, and incase it falls between -9.0 and -20.0C degrees
|
||||||
// The Fronius Gen24 (and other Fronius inverters also affected), will stop charge/discharge if the battery gets colder than -10°C.
|
// The Fronius Gen24 (and other Fronius inverters also affected), will stop charge/discharge if the battery gets colder than -10°C.
|
||||||
// This is due to the original battery pack (BYD HVM), is a lithium iron phosphate battery, that cannot be charged in cold weather.
|
// This is due to the original battery pack (BYD HVM), is a lithium iron phosphate battery, that cannot be charged in cold weather.
|
||||||
// When using EV packs with NCM/LMO/NCA chemsitry, this is not a problem, since these chemistries are OK for outdoor cold use.
|
// When using EV packs with NCM/LMO/NCA chemsitry, this is not a problem, since these chemistries are OK for outdoor cold use.
|
||||||
if (temperature_min > 32768) { // Signed value on negative side
|
if (system_temperature_min_dC < 0) {
|
||||||
if (temperature_min < 65445 && temperature_min > 65335) { // Between -9.0 and -20.0C degrees
|
if (system_temperature_min_dC < -90 && system_temperature_min_dC > -200) { // Between -9.0 and -20.0C degrees
|
||||||
temperature_min = 65445; //Cap value to -9.0C
|
system_temperature_min_dC = -90; //Cap value to -9.0C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (temperature_max > 32768) { // Signed value on negative side
|
if (system_temperature_max_dC < 0) { // Signed value on negative side
|
||||||
if (temperature_max < 65445 && temperature_max > 65335) { // Between -9.0 and -20.0C degrees
|
if (system_temperature_max_dC < -90 && system_temperature_max_dC > -200) { // Between -9.0 and -20.0C degrees
|
||||||
temperature_max = 65445; //Cap value to -9.0C
|
system_temperature_max_dC = -90; //Cap value to -9.0C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,25 +7,24 @@
|
||||||
#define MAX_POWER 40960 //BYD Modbus specific value
|
#define MAX_POWER 40960 //BYD Modbus specific value
|
||||||
|
|
||||||
extern uint16_t mbPV[MB_RTU_NUM_VALUES];
|
extern uint16_t mbPV[MB_RTU_NUM_VALUES];
|
||||||
extern uint16_t SOC;
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth;
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage;
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t battery_current;
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh;
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t remaining_capacity_Wh;
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t max_target_discharge_power;
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power;
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status;
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint8_t bms_char_dis_status;
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power;
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min;
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t temperature_max;
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t max_power;
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t max_voltage;
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
extern uint16_t min_voltage;
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool inverterAllowsContactorClosing; //Bool, true/false
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool system_LFP_Chemistry; //Bool, true/false
|
||||||
extern bool LFP_Chemistry;
|
|
||||||
|
|
||||||
void handle_static_data_modbus_byd();
|
void handle_static_data_modbus_byd();
|
||||||
void verify_temperature_modbus();
|
void verify_temperature_modbus();
|
||||||
|
|
|
@ -21,6 +21,10 @@
|
||||||
#include "SMA-CAN.h"
|
#include "SMA-CAN.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SMA_TRIPOWER_CAN
|
||||||
|
#include "SMA-TRIPOWER-CAN.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef SOFAR_CAN
|
#ifdef SOFAR_CAN
|
||||||
#include "SOFAR-CAN.h"
|
#include "SOFAR-CAN.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -14,9 +14,9 @@ void handle_update_data_modbus32051() {
|
||||||
system_data[2] = 110; //Goes between 110- -107 [NOTE, SIGNED VALUE]
|
system_data[2] = 110; //Goes between 110- -107 [NOTE, SIGNED VALUE]
|
||||||
system_data[3] = 0; //Goes between 0 and -1 [NOTE, SIGNED VALUE]
|
system_data[3] = 0; //Goes between 0 and -1 [NOTE, SIGNED VALUE]
|
||||||
system_data[4] = 616; //Goes between 616- -520 [NOTE, SIGNED VALUE]
|
system_data[4] = 616; //Goes between 616- -520 [NOTE, SIGNED VALUE]
|
||||||
system_data[5] = temperature_max; //Temperature max?
|
system_data[5] = system_temperature_max_dC; //Temperature max?
|
||||||
system_data[6] = temperature_min; //Temperature min?
|
system_data[6] = system_temperature_min_dC; //Temperature min?
|
||||||
system_data[7] = (SOC / 100); //SOC 0-100%, no decimals
|
system_data[7] = (system_scaled_SOC_pptt / 100); //SOC 0-100%, no decimals
|
||||||
system_data[8] = 98; //Always 98 in logs
|
system_data[8] = 98; //Always 98 in logs
|
||||||
static uint16_t i = 2051;
|
static uint16_t i = 2051;
|
||||||
memcpy(&mbPV[i], system_data, sizeof(system_data));
|
memcpy(&mbPV[i], system_data, sizeof(system_data));
|
||||||
|
@ -26,17 +26,17 @@ void handle_update_data_modbus39500() {
|
||||||
// Store the data into the array
|
// Store the data into the array
|
||||||
static uint16_t system_data[26];
|
static uint16_t system_data[26];
|
||||||
system_data[0] = 0;
|
system_data[0] = 0;
|
||||||
system_data[1] = capacity_Wh; //Capacity? 5000 with 5kWh battery
|
system_data[1] = system_capacity_Wh; //Capacity? 5000 with 5kWh battery
|
||||||
system_data[2] = 0;
|
system_data[2] = 0;
|
||||||
system_data[3] = capacity_Wh; //Capacity? 5000 with 5kWh battery
|
system_data[3] = system_capacity_Wh; //Capacity? 5000 with 5kWh battery
|
||||||
system_data[4] = 0;
|
system_data[4] = 0;
|
||||||
system_data[5] = 2500; //???
|
system_data[5] = 2500; //???
|
||||||
system_data[6] = 0;
|
system_data[6] = 0;
|
||||||
system_data[7] = 2500; //???
|
system_data[7] = 2500; //???
|
||||||
system_data[8] = (SOC / 100); //SOC 0-100%, no decimals
|
system_data[8] = (system_scaled_SOC_pptt / 100); //SOC 0-100%, no decimals
|
||||||
system_data[9] =
|
system_data[9] =
|
||||||
1; //Running status, equiv to register 37762, 0 = Offline, 1 = Standby,2 = Running, 3 = Fault, 4 = sleep mode
|
1; //Running status, equiv to register 37762, 0 = Offline, 1 = Standby,2 = Running, 3 = Fault, 4 = sleep mode
|
||||||
system_data[10] = battery_voltage; //Battery bus voltage (766.5V = 7665)
|
system_data[10] = system_battery_voltage_dV; //Battery bus voltage (766.5V = 7665)
|
||||||
system_data[11] = 9; //TODO: GOES LOWER WITH LOW SOC
|
system_data[11] = 9; //TODO: GOES LOWER WITH LOW SOC
|
||||||
system_data[12] = 0;
|
system_data[12] = 0;
|
||||||
system_data[13] = 699; //TODO: GOES LOWER WITH LOW SOC
|
system_data[13] = 699; //TODO: GOES LOWER WITH LOW SOC
|
||||||
|
@ -50,7 +50,7 @@ void handle_update_data_modbus39500() {
|
||||||
system_data[21] = 0;
|
system_data[21] = 0;
|
||||||
system_data[22] = 0;
|
system_data[22] = 0;
|
||||||
system_data[23] = 0;
|
system_data[23] = 0;
|
||||||
system_data[24] = (SOC / 10); //SOC 0-100.0%, 1x decimal
|
system_data[24] = (system_scaled_SOC_pptt / 10); //SOC 0-100.0%, 1x decimal
|
||||||
system_data[25] = 0;
|
system_data[25] = 0;
|
||||||
system_data[26] = 1; //Always 1 in logs
|
system_data[26] = 1; //Always 1 in logs
|
||||||
static uint16_t i = 9500;
|
static uint16_t i = 9500;
|
||||||
|
|
|
@ -6,24 +6,27 @@
|
||||||
#define MB_RTU_NUM_VALUES 30000
|
#define MB_RTU_NUM_VALUES 30000
|
||||||
|
|
||||||
extern uint16_t mbPV[MB_RTU_NUM_VALUES];
|
extern uint16_t mbPV[MB_RTU_NUM_VALUES];
|
||||||
extern uint16_t SOC;
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth;
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage;
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current;
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh;
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh;
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power;
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power;
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status;
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status;
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power;
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min;
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max;
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t max_power;
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t max_voltage;
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t min_voltage;
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
extern bool inverterAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
void update_modbus_registers_luna2000();
|
void update_modbus_registers_luna2000();
|
||||||
void handle_update_data_modbus32051();
|
void handle_update_data_modbus32051();
|
||||||
|
|
|
@ -182,53 +182,53 @@ void update_values_can_pylon() { //This function maps all the values fetched fr
|
||||||
PYLON_4281.data.u8[3] = 0;
|
PYLON_4281.data.u8[3] = 0;
|
||||||
|
|
||||||
//Voltage (370.0)
|
//Voltage (370.0)
|
||||||
PYLON_4210.data.u8[0] = (battery_voltage >> 8);
|
PYLON_4210.data.u8[0] = (system_battery_voltage_dV >> 8);
|
||||||
PYLON_4210.data.u8[1] = (battery_voltage & 0x00FF);
|
PYLON_4210.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
|
||||||
PYLON_4211.data.u8[0] = (battery_voltage >> 8);
|
PYLON_4211.data.u8[0] = (system_battery_voltage_dV >> 8);
|
||||||
PYLON_4211.data.u8[1] = (battery_voltage & 0x00FF);
|
PYLON_4211.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
|
||||||
|
|
||||||
//Current (TODO: SIGNED? Or looks like it could be just offset, in that case the below line wont work)
|
//Current (15.0)
|
||||||
PYLON_4210.data.u8[2] = (battery_current >> 8);
|
PYLON_4210.data.u8[2] = (system_battery_current_dA >> 8);
|
||||||
PYLON_4210.data.u8[3] = (battery_current & 0x00FF);
|
PYLON_4210.data.u8[3] = (system_battery_current_dA & 0x00FF);
|
||||||
PYLON_4211.data.u8[2] = (battery_current >> 8);
|
PYLON_4211.data.u8[2] = (system_battery_current_dA >> 8);
|
||||||
PYLON_4211.data.u8[3] = (battery_current & 0x00FF);
|
PYLON_4211.data.u8[3] = (system_battery_current_dA & 0x00FF);
|
||||||
|
|
||||||
//SOC (100.00%)
|
//SOC (100.00%)
|
||||||
PYLON_4210.data.u8[6] = (SOC * 0.01); //Remove decimals
|
PYLON_4210.data.u8[6] = (system_scaled_SOC_pptt * 0.01); //Remove decimals
|
||||||
PYLON_4211.data.u8[6] = (SOC * 0.01); //Remove decimals
|
PYLON_4211.data.u8[6] = (system_scaled_SOC_pptt * 0.01); //Remove decimals
|
||||||
|
|
||||||
//StateOfHealth (100.00%)
|
//StateOfHealth (100.00%)
|
||||||
PYLON_4210.data.u8[7] = (StateOfHealth * 0.01);
|
PYLON_4210.data.u8[7] = (system_SOH_pptt * 0.01);
|
||||||
PYLON_4211.data.u8[7] = (StateOfHealth * 0.01);
|
PYLON_4211.data.u8[7] = (system_SOH_pptt * 0.01);
|
||||||
|
|
||||||
#ifdef INVERT_VOLTAGE //Useful for Sofar inverters \
|
#ifdef INVERT_VOLTAGE //Useful for Sofar inverters \
|
||||||
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Discharge Cutoff Voltage
|
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Discharge Cutoff Voltage
|
||||||
PYLON_4220.data.u8[0] = (max_voltage & 0x00FF);
|
PYLON_4220.data.u8[0] = (system_max_design_voltage_dV & 0x00FF);
|
||||||
PYLON_4220.data.u8[1] = (max_voltage >> 8);
|
PYLON_4220.data.u8[1] = (system_max_design_voltage_dV >> 8);
|
||||||
PYLON_4221.data.u8[0] = (max_voltage & 0x00FF);
|
PYLON_4221.data.u8[0] = (system_max_design_voltage_dV & 0x00FF);
|
||||||
PYLON_4221.data.u8[1] = (max_voltage >> 8);
|
PYLON_4221.data.u8[1] = (system_max_design_voltage_dV >> 8);
|
||||||
|
|
||||||
//Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage
|
//Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage
|
||||||
PYLON_4220.data.u8[2] = (min_voltage & 0x00FF);
|
PYLON_4220.data.u8[2] = (system_min_design_voltage_dV & 0x00FF);
|
||||||
PYLON_4220.data.u8[3] = (min_voltage >> 8);
|
PYLON_4220.data.u8[3] = (system_min_design_voltage_dV >> 8);
|
||||||
PYLON_4221.data.u8[2] = (min_voltage & 0x00FF);
|
PYLON_4221.data.u8[2] = (system_min_design_voltage_dV & 0x00FF);
|
||||||
PYLON_4221.data.u8[3] = (min_voltage >> 8);
|
PYLON_4221.data.u8[3] = (system_min_design_voltage_dV >> 8);
|
||||||
#else
|
#else
|
||||||
//Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage
|
//Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage
|
||||||
PYLON_4220.data.u8[0] = (min_voltage >> 8);
|
PYLON_4220.data.u8[0] = (system_min_design_voltage_dV >> 8);
|
||||||
PYLON_4220.data.u8[1] = (min_voltage & 0x00FF);
|
PYLON_4220.data.u8[1] = (system_min_design_voltage_dV & 0x00FF);
|
||||||
PYLON_4221.data.u8[0] = (min_voltage >> 8);
|
PYLON_4221.data.u8[0] = (system_min_design_voltage_dV >> 8);
|
||||||
PYLON_4221.data.u8[1] = (min_voltage & 0x00FF);
|
PYLON_4221.data.u8[1] = (system_min_design_voltage_dV & 0x00FF);
|
||||||
|
|
||||||
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Discharge Cutoff Voltage
|
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Discharge Cutoff Voltage
|
||||||
PYLON_4220.data.u8[2] = (max_voltage >> 8);
|
PYLON_4220.data.u8[2] = (system_max_design_voltage_dV >> 8);
|
||||||
PYLON_4220.data.u8[3] = (max_voltage & 0x00FF);
|
PYLON_4220.data.u8[3] = (system_max_design_voltage_dV & 0x00FF);
|
||||||
PYLON_4221.data.u8[2] = (max_voltage >> 8);
|
PYLON_4221.data.u8[2] = (system_max_design_voltage_dV >> 8);
|
||||||
PYLON_4221.data.u8[3] = (max_voltage & 0x00FF);
|
PYLON_4221.data.u8[3] = (system_max_design_voltage_dV & 0x00FF);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//In case we run into any errors/faults, we can set charge / discharge forbidden
|
//In case we run into any errors/faults, we can set charge / discharge forbidden
|
||||||
if (bms_status == FAULT) {
|
if (system_bms_status == FAULT) {
|
||||||
PYLON_4280.data.u8[0] = 0xAA;
|
PYLON_4280.data.u8[0] = 0xAA;
|
||||||
PYLON_4280.data.u8[1] = 0xAA;
|
PYLON_4280.data.u8[1] = 0xAA;
|
||||||
PYLON_4280.data.u8[2] = 0xAA;
|
PYLON_4280.data.u8[2] = 0xAA;
|
||||||
|
|
|
@ -5,24 +5,27 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
extern uint16_t SOC;
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth;
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage;
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current;
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh;
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh;
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power;
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power;
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status;
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status;
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power;
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min;
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max;
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t CANerror;
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t min_voltage;
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t max_voltage;
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
extern bool inverterAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
void update_values_can_pylon();
|
void update_values_can_pylon();
|
||||||
void receive_can_pylon(CAN_frame_t rx_frame);
|
void receive_can_pylon(CAN_frame_t rx_frame);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//SERIAL-LINK-TRANSMITTER-INVERTER.cpp
|
//SERIAL-LINK-TRANSMITTER-INVERTER.cpp
|
||||||
|
|
||||||
#include "SERIAL-LINK-TRANSMITTER-INVERTER.h"
|
#include "SERIAL-LINK-TRANSMITTER-INVERTER.h"
|
||||||
|
#include "../devboard/utils/events.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SerialDataLink
|
* SerialDataLink
|
||||||
|
@ -8,7 +9,7 @@
|
||||||
* Will transmit max 16 int variable - receive none
|
* Will transmit max 16 int variable - receive none
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define BATTERY_SEND_NUM_VARIABLES 17
|
#define BATTERY_SEND_NUM_VARIABLES 16
|
||||||
#define BATTERY_RECV_NUM_VARIABLES 1
|
#define BATTERY_RECV_NUM_VARIABLES 1
|
||||||
|
|
||||||
#ifdef BATTERY_RECV_NUM_VARIABLES
|
#ifdef BATTERY_RECV_NUM_VARIABLES
|
||||||
|
@ -107,10 +108,9 @@ void manageSerialLinkTransmitter() {
|
||||||
Serial.println("SerialDataLink : max_target_discharge_power = 0");
|
Serial.println("SerialDataLink : max_target_discharge_power = 0");
|
||||||
Serial.println("SerialDataLink : max_target_charge_power = 0");
|
Serial.println("SerialDataLink : max_target_charge_power = 0");
|
||||||
|
|
||||||
bms_status = 4; //FAULT
|
system_max_discharge_power_W = 0;
|
||||||
max_target_discharge_power = 0;
|
system_max_charge_power_W = 0;
|
||||||
max_target_charge_power = 0;
|
set_event(EVENT_SERIAL_TX_FAILURE, 0);
|
||||||
LEDcolor = RED;
|
|
||||||
// throw error
|
// throw error
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -129,23 +129,22 @@ void manageSerialLinkTransmitter() {
|
||||||
|
|
||||||
if (currentTime - updateDataTime > 999) {
|
if (currentTime - updateDataTime > 999) {
|
||||||
updateDataTime = currentTime;
|
updateDataTime = currentTime;
|
||||||
dataLinkTransmit.updateData(0, SOC);
|
dataLinkTransmit.updateData(0, system_real_SOC_pptt);
|
||||||
dataLinkTransmit.updateData(1, StateOfHealth);
|
dataLinkTransmit.updateData(1, system_SOH_pptt);
|
||||||
dataLinkTransmit.updateData(2, battery_voltage);
|
dataLinkTransmit.updateData(2, system_battery_voltage_dV);
|
||||||
dataLinkTransmit.updateData(3, battery_current);
|
dataLinkTransmit.updateData(3, system_battery_current_dA);
|
||||||
dataLinkTransmit.updateData(4, capacity_Wh);
|
dataLinkTransmit.updateData(4, system_capacity_Wh);
|
||||||
dataLinkTransmit.updateData(5, remaining_capacity_Wh);
|
dataLinkTransmit.updateData(5, system_remaining_capacity_Wh);
|
||||||
dataLinkTransmit.updateData(6, max_target_discharge_power);
|
dataLinkTransmit.updateData(6, system_max_discharge_power_W);
|
||||||
dataLinkTransmit.updateData(7, max_target_charge_power);
|
dataLinkTransmit.updateData(7, system_max_charge_power_W);
|
||||||
dataLinkTransmit.updateData(8, bms_status);
|
dataLinkTransmit.updateData(8, system_bms_status);
|
||||||
dataLinkTransmit.updateData(9, bms_char_dis_status);
|
dataLinkTransmit.updateData(9, system_active_power_W);
|
||||||
dataLinkTransmit.updateData(10, stat_batt_power);
|
dataLinkTransmit.updateData(10, system_temperature_min_dC);
|
||||||
dataLinkTransmit.updateData(11, temperature_min);
|
dataLinkTransmit.updateData(11, system_temperature_max_dC);
|
||||||
dataLinkTransmit.updateData(12, temperature_max);
|
dataLinkTransmit.updateData(12, system_cell_max_voltage_mV);
|
||||||
dataLinkTransmit.updateData(13, cell_max_voltage);
|
dataLinkTransmit.updateData(13, system_cell_min_voltage_mV);
|
||||||
dataLinkTransmit.updateData(14, cell_min_voltage);
|
dataLinkTransmit.updateData(14, (int16_t)system_LFP_Chemistry);
|
||||||
dataLinkTransmit.updateData(15, (int16_t)LFP_Chemistry);
|
dataLinkTransmit.updateData(15, batteryAllowsContactorClosing);
|
||||||
dataLinkTransmit.updateData(16, batteryAllowsContactorClosing);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,37 +152,35 @@ void manageSerialLinkTransmitter() {
|
||||||
void printSendingValues() {
|
void printSendingValues() {
|
||||||
Serial.println("Values from battery: ");
|
Serial.println("Values from battery: ");
|
||||||
Serial.print("SOC: ");
|
Serial.print("SOC: ");
|
||||||
Serial.print(SOC);
|
Serial.print(system_real_SOC_pptt);
|
||||||
Serial.print(" SOH: ");
|
Serial.print(" SOH: ");
|
||||||
Serial.print(StateOfHealth);
|
Serial.print(system_SOH_pptt);
|
||||||
Serial.print(" Voltage: ");
|
Serial.print(" Voltage: ");
|
||||||
Serial.print(battery_voltage);
|
Serial.print(system_battery_voltage_dV);
|
||||||
Serial.print(" Current: ");
|
Serial.print(" Current: ");
|
||||||
Serial.print(battery_current);
|
Serial.print(system_battery_current_dA);
|
||||||
Serial.print(" Capacity: ");
|
Serial.print(" Capacity: ");
|
||||||
Serial.print(capacity_Wh);
|
Serial.print(system_capacity_Wh);
|
||||||
Serial.print(" Remain cap: ");
|
Serial.print(" Remain cap: ");
|
||||||
Serial.print(remaining_capacity_Wh);
|
Serial.print(system_remaining_capacity_Wh);
|
||||||
Serial.print(" Max discharge W: ");
|
Serial.print(" Max discharge W: ");
|
||||||
Serial.print(max_target_discharge_power);
|
Serial.print(system_max_discharge_power_W);
|
||||||
Serial.print(" Max charge W: ");
|
Serial.print(" Max charge W: ");
|
||||||
Serial.print(max_target_charge_power);
|
Serial.print(system_max_charge_power_W);
|
||||||
Serial.print(" BMS status: ");
|
Serial.print(" BMS status: ");
|
||||||
Serial.print(bms_status);
|
Serial.print(system_bms_status);
|
||||||
Serial.print(" BMS status dis/cha: ");
|
|
||||||
Serial.print(bms_char_dis_status);
|
|
||||||
Serial.print(" Power: ");
|
Serial.print(" Power: ");
|
||||||
Serial.print(stat_batt_power);
|
Serial.print(system_active_power_W);
|
||||||
Serial.print(" Temp min: ");
|
Serial.print(" Temp min: ");
|
||||||
Serial.print(temperature_min);
|
Serial.print(system_temperature_min_dC);
|
||||||
Serial.print(" Temp max: ");
|
Serial.print(" Temp max: ");
|
||||||
Serial.print(temperature_max);
|
Serial.print(system_temperature_max_dC);
|
||||||
Serial.print(" Cell max: ");
|
Serial.print(" Cell max: ");
|
||||||
Serial.print(cell_max_voltage);
|
Serial.print(system_cell_max_voltage_mV);
|
||||||
Serial.print(" Cell min: ");
|
Serial.print(" Cell min: ");
|
||||||
Serial.print(cell_min_voltage);
|
Serial.print(system_cell_min_voltage_mV);
|
||||||
Serial.print(" LFP : ");
|
Serial.print(" LFP : ");
|
||||||
Serial.print(LFP_Chemistry);
|
Serial.print(system_LFP_Chemistry);
|
||||||
Serial.print(" batteryAllowsContactorClosing: ");
|
Serial.print(" batteryAllowsContactorClosing: ");
|
||||||
Serial.print(batteryAllowsContactorClosing);
|
Serial.print(batteryAllowsContactorClosing);
|
||||||
Serial.print(" inverterAllowsContactorClosing: ");
|
Serial.print(" inverterAllowsContactorClosing: ");
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
//SERIAL-LINK-TRANSMITTER-INVERTER.h
|
|
||||||
|
|
||||||
#ifndef SERIAL_LINK_TRANSMITTER_INVERTER_H
|
#ifndef SERIAL_LINK_TRANSMITTER_INVERTER_H
|
||||||
#define SERIAL_LINK_TRANSMITTER_INVERTER_H
|
#define SERIAL_LINK_TRANSMITTER_INVERTER_H
|
||||||
|
|
||||||
|
@ -9,29 +7,25 @@
|
||||||
#include "../lib/mackelec-SerialDataLink/SerialDataLink.h"
|
#include "../lib/mackelec-SerialDataLink/SerialDataLink.h"
|
||||||
|
|
||||||
// These parameters need to be mapped for the inverter
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t capacity_Wh; //Wh, 0-60000
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t max_target_discharge_power; //W, 0-60000
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t max_target_charge_power; //W, 0-60000
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint8_t bms_status; //Enum, 0-5
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
extern uint8_t bms_char_dis_status; //Enum, 0-2
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
extern uint8_t LEDcolor; //Enum, 0-10
|
extern bool system_LFP_Chemistry; //Bool, true/false
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
|
||||||
|
|
||||||
extern bool LFP_Chemistry;
|
|
||||||
extern uint16_t CANerror;
|
|
||||||
|
|
||||||
// parameters received from receiver
|
// parameters received from receiver
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool inverterAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
void manageSerialLinkTransmitter();
|
void manageSerialLinkTransmitter();
|
||||||
|
|
||||||
|
|
|
@ -2,33 +2,11 @@
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
//TODO: change CAN sending routine once confirmed that 500ms interval is OK for this battery type
|
/* TODO: Map error bits in 0x158 */
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
static unsigned long previousMillis1s = 0; // will store last time a Xs CAN Message was send
|
static unsigned long previousMillis100ms = 0; // will store last time a 100ms CAN Message was send
|
||||||
static unsigned long previousMillis2s = 0; // will store last time a Xs CAN Message was send
|
static const int interval100ms = 100; // interval (ms) at which send CAN Messages
|
||||||
static unsigned long previousMillis3s = 0; // will store last time a Xs CAN Message was send
|
|
||||||
static unsigned long previousMillis4s = 0; // will store last time a Xs CAN Message was send
|
|
||||||
static unsigned long previousMillis5s = 0; // will store last time a Xs CAN Message was send
|
|
||||||
static unsigned long previousMillis6s = 0; // will store last time a Xs CAN Message was send
|
|
||||||
static unsigned long previousMillis7s = 0; // will store last time a Xs CAN Message was send
|
|
||||||
static unsigned long previousMillis8s = 0; // will store last time a Xs CAN Message was send
|
|
||||||
static unsigned long previousMillis9s = 0; // will store last time a Xs CAN Message was send
|
|
||||||
static unsigned long previousMillis10s = 0; // will store last time a Xs CAN Message was send
|
|
||||||
static unsigned long previousMillis11s = 0; // will store last time a Xs CAN Message was send
|
|
||||||
static unsigned long previousMillis12s = 0; // will store last time a Xs CAN Message was send
|
|
||||||
static const int interval1s = 100; // interval (ms) at which send CAN Messages
|
|
||||||
static const int interval2s = 102; // interval (ms) at which send CAN Messages
|
|
||||||
static const int interval3s = 104; // interval (ms) at which send CAN Messages
|
|
||||||
static const int interval4s = 106; // interval (ms) at which send CAN Messages
|
|
||||||
static const int interval5s = 108; // interval (ms) at which send CAN Messages
|
|
||||||
static const int interval6s = 110; // interval (ms) at which send CAN Messages
|
|
||||||
static const int interval7s = 112; // interval (ms) at which send CAN Messages
|
|
||||||
static const int interval8s = 114; // interval (ms) at which send CAN Messages
|
|
||||||
static const int interval9s = 116; // interval (ms) at which send CAN Messages
|
|
||||||
static const int interval10s = 118; // interval (ms) at which send CAN Messages
|
|
||||||
static const int interval11s = 120; // interval (ms) at which send CAN Messages
|
|
||||||
static const int interval12s = 122; // interval (ms) at which send CAN Messages
|
|
||||||
|
|
||||||
//Actual content messages
|
//Actual content messages
|
||||||
static const CAN_frame_t SMA_558 = {
|
static const CAN_frame_t SMA_558 = {
|
||||||
|
@ -118,35 +96,36 @@ CAN_frame_t SMA_158 = {.FIR = {.B =
|
||||||
.MsgID = 0x158,
|
.MsgID = 0x158,
|
||||||
.data = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xAA, 0xAA}};
|
.data = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xAA, 0xAA}};
|
||||||
|
|
||||||
static int discharge_current = 0;
|
static int16_t discharge_current = 0;
|
||||||
static int charge_current = 0;
|
static int16_t charge_current = 0;
|
||||||
static int temperature_average = 0;
|
static int16_t temperature_average = 0;
|
||||||
static int ampere_hours_remaining = 0;
|
static uint16_t ampere_hours_remaining = 0;
|
||||||
|
|
||||||
void update_values_can_sma() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
void update_values_can_sma() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
||||||
//Calculate values
|
//Calculate values
|
||||||
charge_current =
|
charge_current = ((system_max_charge_power_W * 10) /
|
||||||
((max_target_charge_power * 10) / max_voltage); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
||||||
//The above calculation results in (30 000*10)/3700=81A
|
//The above calculation results in (30 000*10)/3700=81A
|
||||||
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
|
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
|
||||||
|
|
||||||
discharge_current = ((max_target_discharge_power * 10) /
|
discharge_current = ((system_max_discharge_power_W * 10) /
|
||||||
max_voltage); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
||||||
//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)
|
||||||
|
|
||||||
temperature_average = ((temperature_max + temperature_min) / 2);
|
temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
|
||||||
|
|
||||||
ampere_hours_remaining =
|
ampere_hours_remaining =
|
||||||
((remaining_capacity_Wh / battery_voltage) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
|
((system_remaining_capacity_Wh / system_battery_voltage_dV) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
|
||||||
|
|
||||||
//Map values to CAN messages
|
//Map values to CAN messages
|
||||||
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
|
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
|
||||||
SMA_358.data.u8[0] = (max_voltage >> 8);
|
SMA_358.data.u8[0] = (system_max_design_voltage_dV >> 8);
|
||||||
SMA_358.data.u8[1] = (max_voltage & 0x00FF);
|
SMA_358.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
|
||||||
//Minvoltage (eg 300.0V = 3000 , 16bits long)
|
//Minvoltage (eg 300.0V = 3000 , 16bits long)
|
||||||
SMA_358.data.u8[2] = (min_voltage >> 8); //Minvoltage behaves strange on SMA, cuts out at 56% of the set value?
|
SMA_358.data.u8[2] =
|
||||||
SMA_358.data.u8[3] = (min_voltage & 0x00FF);
|
(system_min_design_voltage_dV >> 8); //Minvoltage behaves strange on SMA, cuts out at 56% of the set value?
|
||||||
|
SMA_358.data.u8[3] = (system_min_design_voltage_dV & 0x00FF);
|
||||||
//Discharge limited current, 500 = 50A, (0.1, A)
|
//Discharge limited current, 500 = 50A, (0.1, A)
|
||||||
SMA_358.data.u8[4] = (discharge_current >> 8);
|
SMA_358.data.u8[4] = (discharge_current >> 8);
|
||||||
SMA_358.data.u8[5] = (discharge_current & 0x00FF);
|
SMA_358.data.u8[5] = (discharge_current & 0x00FF);
|
||||||
|
@ -155,37 +134,97 @@ void update_values_can_sma() { //This function maps all the values fetched from
|
||||||
SMA_358.data.u8[7] = (charge_current & 0x00FF);
|
SMA_358.data.u8[7] = (charge_current & 0x00FF);
|
||||||
|
|
||||||
//SOC (100.00%)
|
//SOC (100.00%)
|
||||||
SMA_3D8.data.u8[0] = (SOC >> 8);
|
SMA_3D8.data.u8[0] = (system_scaled_SOC_pptt >> 8);
|
||||||
SMA_3D8.data.u8[1] = (SOC & 0x00FF);
|
SMA_3D8.data.u8[1] = (system_scaled_SOC_pptt & 0x00FF);
|
||||||
//StateOfHealth (100.00%)
|
//StateOfHealth (100.00%)
|
||||||
SMA_3D8.data.u8[2] = (StateOfHealth >> 8);
|
SMA_3D8.data.u8[2] = (system_SOH_pptt >> 8);
|
||||||
SMA_3D8.data.u8[3] = (StateOfHealth & 0x00FF);
|
SMA_3D8.data.u8[3] = (system_SOH_pptt & 0x00FF);
|
||||||
//State of charge (AH, 0.1)
|
//State of charge (AH, 0.1)
|
||||||
SMA_3D8.data.u8[4] = (ampere_hours_remaining >> 8);
|
SMA_3D8.data.u8[4] = (ampere_hours_remaining >> 8);
|
||||||
SMA_3D8.data.u8[5] = (ampere_hours_remaining & 0x00FF);
|
SMA_3D8.data.u8[5] = (ampere_hours_remaining & 0x00FF);
|
||||||
|
|
||||||
//Voltage (370.0)
|
//Voltage (370.0)
|
||||||
SMA_4D8.data.u8[0] = (battery_voltage >> 8);
|
SMA_4D8.data.u8[0] = (system_battery_voltage_dV >> 8);
|
||||||
SMA_4D8.data.u8[1] = (battery_voltage & 0x00FF);
|
SMA_4D8.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
|
||||||
//Current (TODO: signed OK?)
|
//Current (TODO: signed OK?)
|
||||||
SMA_4D8.data.u8[2] = (battery_current >> 8);
|
SMA_4D8.data.u8[2] = (system_battery_current_dA >> 8);
|
||||||
SMA_4D8.data.u8[3] = (battery_current & 0x00FF);
|
SMA_4D8.data.u8[3] = (system_battery_current_dA & 0x00FF);
|
||||||
//Temperature average
|
//Temperature average
|
||||||
SMA_4D8.data.u8[4] = (temperature_average >> 8);
|
SMA_4D8.data.u8[4] = (temperature_average >> 8);
|
||||||
SMA_4D8.data.u8[5] = (temperature_average & 0x00FF);
|
SMA_4D8.data.u8[5] = (temperature_average & 0x00FF);
|
||||||
|
//Battery ready
|
||||||
|
if (system_bms_status == ACTIVE) {
|
||||||
|
SMA_4D8.data.u8[6] = READY_STATE;
|
||||||
|
} else {
|
||||||
|
SMA_4D8.data.u8[6] = STOP_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
//Error bits
|
//Error bits
|
||||||
|
/*
|
||||||
//SMA_158.data.u8[0] = //bit12 Fault high temperature, bit34Battery cellundervoltage, bit56 Battery cell overvoltage, bit78 batterysystemdefect
|
//SMA_158.data.u8[0] = //bit12 Fault high temperature, bit34Battery cellundervoltage, bit56 Battery cell overvoltage, bit78 batterysystemdefect
|
||||||
//TODO: add all error bits
|
//TODO: add all error bits. Sending message with all 0xAA until that.
|
||||||
|
|
||||||
|
0x158 can be used to send error messages or warnings.
|
||||||
|
|
||||||
|
Each message is defined of two bits:
|
||||||
|
01=message triggered
|
||||||
|
10=no message triggered
|
||||||
|
0xA9=10101001, triggers first message
|
||||||
|
0xA6=10100110, triggers second message
|
||||||
|
0x9A=10011010, triggers third message
|
||||||
|
0x6A=01101010, triggers forth message
|
||||||
|
bX defines the byte
|
||||||
|
|
||||||
|
b0 A9 Battery system defect
|
||||||
|
b0 A6 Battery cell overvoltage fault
|
||||||
|
b0 9A Battery cell undervoltage fault
|
||||||
|
b0 6A Battery high temperature fault
|
||||||
|
b1 A9 Battery low temperature fault
|
||||||
|
b1 A6 Battery high temperature fault
|
||||||
|
b1 9A Battery low temperature fault
|
||||||
|
b1 6A Overload (reboot required)
|
||||||
|
b2 A9 Overload (reboot required)
|
||||||
|
b2 A6 Incorrect switch position for the battery disconnection point
|
||||||
|
b2 9A Battery system short circuit
|
||||||
|
b2 6A Internal battery hardware fault
|
||||||
|
b3 A9 Battery imbalancing fault
|
||||||
|
b3 A6 Battery service life expiry
|
||||||
|
b3 9A Battery system thermal management defective
|
||||||
|
b3 6A Internal battery hardware fault
|
||||||
|
b4 A9 Battery system defect (warning)
|
||||||
|
b4 A6 Battery cell overvoltage fault (warning)
|
||||||
|
b4 9A Battery cell undervoltage fault (warning)
|
||||||
|
b4 6A Battery high temperature fault (warning)
|
||||||
|
b5 A9 Battery low temperature fault (warning)
|
||||||
|
b5 A6 Battery high temperature fault (warning)
|
||||||
|
b5 9A Battery low temperature fault (warning)
|
||||||
|
b5 6A Self-diagnosis (warning)
|
||||||
|
b6 A9 Self-diagnosis (warning)
|
||||||
|
b6 A6 Incorrect switch position for the battery disconnection point (warning)
|
||||||
|
b6 9A Battery system short circuit (warning)
|
||||||
|
b6 6A Internal battery hardware fault (warning)
|
||||||
|
b7 A9 Battery imbalancing fault (warning)
|
||||||
|
b7 A6 Battery service life expiry (warning)
|
||||||
|
b7 9A Battery system thermal management defective (warning)
|
||||||
|
b7 6A Internal battery hardware fault (warning)
|
||||||
|
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_sma(CAN_frame_t rx_frame) {
|
void receive_can_sma(CAN_frame_t rx_frame) {
|
||||||
switch (rx_frame.MsgID) {
|
switch (rx_frame.MsgID) {
|
||||||
case 0x660: //Message originating from SMA inverter
|
case 0x360: //Message originating from SMA inverter - Voltage and current
|
||||||
|
//Frame0-1 Voltage
|
||||||
|
//Frame2-3 Current
|
||||||
break;
|
break;
|
||||||
case 0x5E0: //Message originating from SMA inverter
|
case 0x420: //Message originating from SMA inverter - Timestamp
|
||||||
|
//Frame0-3 Timestamp
|
||||||
break;
|
break;
|
||||||
case 0x560: //Message originating from SMA inverter
|
case 0x3E0: //Message originating from SMA inverter - ?
|
||||||
|
break;
|
||||||
|
case 0x5E0: //Message originating from SMA inverter - String
|
||||||
|
break;
|
||||||
|
case 0x560: //Message originating from SMA inverter - Init
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -195,65 +234,21 @@ void receive_can_sma(CAN_frame_t rx_frame) {
|
||||||
void send_can_sma() {
|
void send_can_sma() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
|
|
||||||
// Send CAN Message every X ms, 1000 for testing
|
// Send CAN Message every 100ms
|
||||||
if (currentMillis - previousMillis1s >= interval1s) {
|
if (currentMillis - previousMillis100ms >= interval100ms) {
|
||||||
previousMillis1s = currentMillis;
|
previousMillis100ms = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_558);
|
ESP32Can.CANWriteFrame(&SMA_558);
|
||||||
}
|
|
||||||
if (currentMillis - previousMillis2s >= interval2s) {
|
|
||||||
previousMillis2s = currentMillis;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_598);
|
ESP32Can.CANWriteFrame(&SMA_598);
|
||||||
}
|
|
||||||
if (currentMillis - previousMillis3s >= interval3s) {
|
|
||||||
previousMillis3s = currentMillis;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_5D8);
|
ESP32Can.CANWriteFrame(&SMA_5D8);
|
||||||
}
|
ESP32Can.CANWriteFrame(&SMA_618_1); // TODO, should these 3x
|
||||||
if (currentMillis - previousMillis4s >= interval4s) {
|
ESP32Can.CANWriteFrame(&SMA_618_2); // be sent as batch?
|
||||||
previousMillis4s = currentMillis;
|
ESP32Can.CANWriteFrame(&SMA_618_3); // or alternate on each send?
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_618_1);
|
|
||||||
}
|
|
||||||
if (currentMillis - previousMillis5s >= interval5s) {
|
|
||||||
previousMillis5s = currentMillis;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_618_2);
|
|
||||||
}
|
|
||||||
if (currentMillis - previousMillis6s >= interval6s) {
|
|
||||||
previousMillis6s = currentMillis;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_618_3);
|
|
||||||
}
|
|
||||||
if (currentMillis - previousMillis7s >= interval7s) {
|
|
||||||
previousMillis7s = currentMillis;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_358);
|
ESP32Can.CANWriteFrame(&SMA_358);
|
||||||
}
|
|
||||||
if (currentMillis - previousMillis8s >= interval8s) {
|
|
||||||
previousMillis8s = currentMillis;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_3D8);
|
ESP32Can.CANWriteFrame(&SMA_3D8);
|
||||||
}
|
|
||||||
if (currentMillis - previousMillis9s >= interval9s) {
|
|
||||||
previousMillis9s = currentMillis;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_458);
|
ESP32Can.CANWriteFrame(&SMA_458);
|
||||||
}
|
|
||||||
if (currentMillis - previousMillis10s >= interval10s) {
|
|
||||||
previousMillis10s = currentMillis;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_518);
|
ESP32Can.CANWriteFrame(&SMA_518);
|
||||||
}
|
|
||||||
if (currentMillis - previousMillis11s >= interval11s) {
|
|
||||||
previousMillis11s = currentMillis;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_4D8);
|
ESP32Can.CANWriteFrame(&SMA_4D8);
|
||||||
}
|
|
||||||
if (currentMillis - previousMillis12s >= interval12s) {
|
|
||||||
previousMillis12s = currentMillis;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_158);
|
ESP32Can.CANWriteFrame(&SMA_158);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,26 +5,30 @@
|
||||||
#include "../devboard/config.h" // Needed for all defines
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh; //Wh, 0-60000
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power; //W, 0-60000
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power; //W, 0-60000
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status; //Enum, 0-5
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status; //Enum, 0-2
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t min_voltage;
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern uint16_t max_voltage;
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
extern uint8_t LEDcolor; //Enum, 0-10
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
extern bool inverterAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
|
#define READY_STATE 0x03
|
||||||
|
#define STOP_STATE 0x02
|
||||||
|
|
||||||
void update_values_can_sma();
|
void update_values_can_sma();
|
||||||
void send_can_sma();
|
void send_can_sma();
|
||||||
|
|
365
Software/src/inverter/SMA-TRIPOWER-CAN.cpp
Normal file
365
Software/src/inverter/SMA-TRIPOWER-CAN.cpp
Normal file
|
@ -0,0 +1,365 @@
|
||||||
|
#include "SMA-TRIPOWER-CAN.h"
|
||||||
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
- Figure out the manufacturer info needed in send_tripower_init() CAN messages
|
||||||
|
- CAN logs from real system might be needed
|
||||||
|
- Figure out how cellvoltages need to be displayed
|
||||||
|
- Figure out if sending send_tripower_init() like we do now is OK
|
||||||
|
- Figure out how to send the non-cyclic messages when needed
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
|
static unsigned long previousMillis500ms = 0; // will store last time a 100ms CAN Message was send
|
||||||
|
static const int interval500ms = 100; // interval (ms) at which send CAN Messages
|
||||||
|
|
||||||
|
//Actual content messages
|
||||||
|
static CAN_frame_t SMA_00D = { // Battery Limits
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x00D,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_00F = { // Battery State
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x00F,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_011 = { // Battery Energy
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x011,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_013 = { // Battery Measurements
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x013,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_014 = { // Battery Tempeartures and Cellvoltages
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x014,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_005 = { // Battery Alarms 1
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x005,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_007 = { // Battery Alarms 2
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x007,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_006 = { // Battery Error Codes
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x006,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_008 = { // Battery Events
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x008,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_015 = { // Battery Data 1
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x015,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_016 = { // Battery Data 2
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x016,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_017 = { // Battery Manufacturer
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x017,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
static CAN_frame_t SMA_018 = { // Battery Name
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x018,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
|
||||||
|
static int discharge_current = 0;
|
||||||
|
static int charge_current = 0;
|
||||||
|
static int temperature_average = 0;
|
||||||
|
static int ampere_hours_remaining = 0;
|
||||||
|
static int ampere_hours_max = 0;
|
||||||
|
static bool batteryAlarm = false;
|
||||||
|
static bool BMSevent = false;
|
||||||
|
|
||||||
|
enum BatteryState { NA, INIT, BAT_STANDBY, OPERATE, WARNING, FAULTED, UPDATE, BAT_UPDATE };
|
||||||
|
BatteryState batteryState = OPERATE;
|
||||||
|
enum InverterControlFlags {
|
||||||
|
EMG_CHARGE_REQUEST,
|
||||||
|
EMG_DISCHARGE_REQUEST,
|
||||||
|
NOT_ENOUGH_ENERGY_FOR_START,
|
||||||
|
INVERTER_STAY_ON,
|
||||||
|
FORCED_BATTERY_SHUTDOWN,
|
||||||
|
RESERVED,
|
||||||
|
BATTERY_UPDATE_AVAILABLE,
|
||||||
|
NO_BATTERY_UPDATED_BY_INV
|
||||||
|
};
|
||||||
|
InverterControlFlags inverterControlFlags = BATTERY_UPDATE_AVAILABLE;
|
||||||
|
enum Events0 {
|
||||||
|
START_SOC_CALIBRATE,
|
||||||
|
STOP_SOC_CALIBRATE,
|
||||||
|
START_POWERLIMIT,
|
||||||
|
STOP_POWERLIMIT,
|
||||||
|
PREVENTATIVE_BAT_SHUTDOWN,
|
||||||
|
THERMAL_MANAGEMENT,
|
||||||
|
START_BALANCING,
|
||||||
|
STOP_BALANCING
|
||||||
|
};
|
||||||
|
Events0 events0 = START_BALANCING;
|
||||||
|
enum Events1 { START_BATTERY_SELFTEST, STOP_BATTERY_SELFTEST };
|
||||||
|
Events1 events1 = START_BATTERY_SELFTEST;
|
||||||
|
enum Command2Battery { IDLE, RUN, NOT_USED1, NOT_USED2, SHUTDOWN, FIRMWARE_UPDATE, BATSELFUPDATE, NOT_USED3 };
|
||||||
|
Command2Battery command2Battery = RUN;
|
||||||
|
enum InvInitState { SYSTEM_FREQUENCY, XPHASE_SYSTEM, BLACKSTART_OPERATION };
|
||||||
|
InvInitState invInitState = SYSTEM_FREQUENCY;
|
||||||
|
|
||||||
|
void update_values_can_sma_tripower() { //This function maps all the values fetched from battery CAN to the inverter CAN
|
||||||
|
//Calculate values
|
||||||
|
charge_current = ((system_max_charge_power_W * 10) /
|
||||||
|
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
||||||
|
//The above calculation results in (30 000*10)/3700=81A
|
||||||
|
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
|
||||||
|
|
||||||
|
discharge_current = ((system_max_discharge_power_W * 10) /
|
||||||
|
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
||||||
|
//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)
|
||||||
|
|
||||||
|
temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
|
||||||
|
|
||||||
|
ampere_hours_remaining =
|
||||||
|
((system_remaining_capacity_Wh / system_battery_voltage_dV) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
|
||||||
|
ampere_hours_max =
|
||||||
|
((system_capacity_Wh / system_battery_voltage_dV) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
|
||||||
|
|
||||||
|
batteryState = OPERATE;
|
||||||
|
inverterControlFlags = INVERTER_STAY_ON;
|
||||||
|
|
||||||
|
//Map values to CAN messages
|
||||||
|
// Battery Limits
|
||||||
|
//Battery Max Charge Voltage (eg 400.0V = 4000 , 16bits long)
|
||||||
|
SMA_00D.data.u8[0] = (system_max_design_voltage_dV >> 8);
|
||||||
|
SMA_00D.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
|
||||||
|
//Battery Min Discharge Voltage (eg 300.0V = 3000 , 16bits long)
|
||||||
|
SMA_00D.data.u8[2] = (system_min_design_voltage_dV >> 8);
|
||||||
|
SMA_00D.data.u8[3] = (system_min_design_voltage_dV & 0x00FF);
|
||||||
|
//Discharge limited current, 500 = 50A, (0.1, A)
|
||||||
|
SMA_00D.data.u8[4] = (discharge_current >> 8);
|
||||||
|
SMA_00D.data.u8[5] = (discharge_current & 0x00FF);
|
||||||
|
//Charge limited current, 125 =12.5A (0.1, A)
|
||||||
|
SMA_00D.data.u8[6] = (charge_current >> 8);
|
||||||
|
SMA_00D.data.u8[7] = (charge_current & 0x00FF);
|
||||||
|
|
||||||
|
// Battery State
|
||||||
|
//SOC (100.00%)
|
||||||
|
SMA_00F.data.u8[0] = (system_scaled_SOC_pptt >> 8);
|
||||||
|
SMA_00F.data.u8[1] = (system_scaled_SOC_pptt & 0x00FF);
|
||||||
|
//StateOfHealth (100.00%)
|
||||||
|
SMA_00F.data.u8[2] = (system_SOH_pptt >> 8);
|
||||||
|
SMA_00F.data.u8[3] = (system_SOH_pptt & 0x00FF);
|
||||||
|
//State of charge (AH, 0.1)
|
||||||
|
SMA_00F.data.u8[4] = (ampere_hours_remaining >> 8);
|
||||||
|
SMA_00F.data.u8[5] = (ampere_hours_remaining & 0x00FF);
|
||||||
|
//Fully charged (AH, 0.1)
|
||||||
|
SMA_00F.data.u8[6] = (ampere_hours_max >> 8);
|
||||||
|
SMA_00F.data.u8[7] = (ampere_hours_max & 0x00FF);
|
||||||
|
|
||||||
|
// Battery Energy
|
||||||
|
//Charged Energy Counter TODO: are these needed?
|
||||||
|
//SMA_011.data.u8[0] = (X >> 8);
|
||||||
|
//SMA_011.data.u8[1] = (X & 0x00FF);
|
||||||
|
//SMA_011.data.u8[2] = (X >> 8);
|
||||||
|
//SMA_011.data.u8[3] = (X & 0x00FF);
|
||||||
|
//Discharged Energy Counter TODO: are these needed?
|
||||||
|
//SMA_011.data.u8[4] = (X >> 8);
|
||||||
|
//SMA_011.data.u8[5] = (X & 0x00FF);
|
||||||
|
//SMA_011.data.u8[6] = (X >> 8);
|
||||||
|
//SMA_011.data.u8[7] = (X & 0x00FF);
|
||||||
|
|
||||||
|
// Battery Measurements
|
||||||
|
//Voltage (370.0)
|
||||||
|
SMA_013.data.u8[0] = (system_battery_voltage_dV >> 8);
|
||||||
|
SMA_013.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
|
||||||
|
//Current (TODO: signed OK?)
|
||||||
|
SMA_013.data.u8[2] = (system_battery_current_dA >> 8);
|
||||||
|
SMA_013.data.u8[3] = (system_battery_current_dA & 0x00FF);
|
||||||
|
//Temperature average
|
||||||
|
SMA_013.data.u8[4] = (temperature_average >> 8);
|
||||||
|
SMA_013.data.u8[5] = (temperature_average & 0x00FF);
|
||||||
|
//Battery state
|
||||||
|
SMA_013.data.u8[6] = batteryState;
|
||||||
|
SMA_013.data.u8[6] = inverterControlFlags;
|
||||||
|
|
||||||
|
// Battery Temperature and Cellvoltages
|
||||||
|
// Battery max temperature
|
||||||
|
SMA_014.data.u8[0] = (system_temperature_max_dC >> 8);
|
||||||
|
SMA_014.data.u8[1] = (system_temperature_max_dC & 0x00FF);
|
||||||
|
// Battery min temperature
|
||||||
|
SMA_014.data.u8[2] = (system_temperature_min_dC >> 8);
|
||||||
|
SMA_014.data.u8[3] = (system_temperature_min_dC & 0x00FF);
|
||||||
|
// Battery Cell Voltage (sum)
|
||||||
|
//SMA_014.data.u8[4] = (??? >> 8); //TODO scaling?
|
||||||
|
//SMA_014.data.u8[5] = (??? & 0x00FF); //TODO scaling?
|
||||||
|
// Cell voltage min
|
||||||
|
//SMA_014.data.u8[6] = (??? >> 8); //TODO scaling? 0-255
|
||||||
|
// Cell voltage max
|
||||||
|
//SMA_014.data.u8[7] = (??? >> 8); //TODO scaling? 0-255
|
||||||
|
|
||||||
|
//SMA_006.data.u8[0] = (ErrorCode >> 8);
|
||||||
|
//SMA_006.data.u8[1] = (ErrorCode & 0x00FF);
|
||||||
|
//SMA_006.data.u8[2] = ModuleNumber;
|
||||||
|
//SMA_006.data.u8[3] = ErrorLevel;
|
||||||
|
//SMA_008.data.u8[0] = Events0;
|
||||||
|
//SMA_008.data.u8[1] = Events1;
|
||||||
|
|
||||||
|
//SMA_005.data.u8[0] = BMSalarms0;
|
||||||
|
//SMA_005.data.u8[1] = BMSalarms1;
|
||||||
|
//SMA_005.data.u8[2] = BMSalarms2;
|
||||||
|
//SMA_005.data.u8[3] = BMSalarms3;
|
||||||
|
//SMA_005.data.u8[4] = BMSalarms4;
|
||||||
|
//SMA_005.data.u8[5] = BMSalarms5;
|
||||||
|
//SMA_005.data.u8[6] = BMSalarms6;
|
||||||
|
//SMA_005.data.u8[7] = BMSalarms7;
|
||||||
|
|
||||||
|
//SMA_007.data.u8[0] = DCDCalarms0;
|
||||||
|
//SMA_007.data.u8[1] = DCDCalarms1;
|
||||||
|
//SMA_007.data.u8[2] = DCDCalarms2;
|
||||||
|
//SMA_007.data.u8[3] = DCDCalarms3;
|
||||||
|
//SMA_007.data.u8[4] = DCDCwarnings0;
|
||||||
|
//SMA_007.data.u8[5] = DCDCwarnings1;
|
||||||
|
//SMA_007.data.u8[6] = DCDCwarnings2;
|
||||||
|
//SMA_007.data.u8[7] = DCDCwarnings3;
|
||||||
|
|
||||||
|
//SMA_015.data.u8[0] = BatterySystemVersion;
|
||||||
|
//SMA_015.data.u8[1] = BatterySystemVersion;
|
||||||
|
//SMA_015.data.u8[2] = BatterySystemVersion;
|
||||||
|
//SMA_015.data.u8[3] = BatterySystemVersion;
|
||||||
|
//SMA_015.data.u8[4] = BatteryCapacity;
|
||||||
|
//SMA_015.data.u8[5] = BatteryCapacity;
|
||||||
|
//SMA_015.data.u8[6] = NumberOfModules;
|
||||||
|
//SMA_015.data.u8[7] = BatteryManufacturerID;
|
||||||
|
|
||||||
|
//SMA_016.data.u8[0] = SerialNumber;
|
||||||
|
//SMA_016.data.u8[1] = SerialNumber;
|
||||||
|
//SMA_016.data.u8[2] = SerialNumber;
|
||||||
|
//SMA_016.data.u8[3] = SerialNumber;
|
||||||
|
//SMA_016.data.u8[4] = ManufacturingDate;
|
||||||
|
//SMA_016.data.u8[5] = ManufacturingDate;
|
||||||
|
//SMA_016.data.u8[6] = ManufacturingDate;
|
||||||
|
//SMA_016.data.u8[7] = ManufacturingDate;
|
||||||
|
|
||||||
|
//SMA_017.data.u8[0] = Multiplex;
|
||||||
|
//SMA_017.data.u8[1] = ManufacturerName;
|
||||||
|
//SMA_017.data.u8[2] = ManufacturerName;
|
||||||
|
//SMA_017.data.u8[3] = ManufacturerName;
|
||||||
|
//SMA_017.data.u8[4] = ManufacturerName;
|
||||||
|
//SMA_017.data.u8[5] = ManufacturerName;
|
||||||
|
//SMA_017.data.u8[6] = ManufacturerName;
|
||||||
|
//SMA_017.data.u8[7] = ManufacturerName;
|
||||||
|
|
||||||
|
//SMA_018.data.u8[0] = Multiplex;
|
||||||
|
//SMA_018.data.u8[1] = BatteryName;
|
||||||
|
//SMA_018.data.u8[2] = BatteryName;
|
||||||
|
//SMA_018.data.u8[3] = BatteryName;
|
||||||
|
//SMA_018.data.u8[4] = BatteryName;
|
||||||
|
//SMA_018.data.u8[5] = BatteryName;
|
||||||
|
//SMA_018.data.u8[6] = BatteryName;
|
||||||
|
//SMA_018.data.u8[7] = BatteryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive_can_sma_tripower(CAN_frame_t rx_frame) {
|
||||||
|
switch (rx_frame.MsgID) {
|
||||||
|
case 0x00D: //Inverter Measurements
|
||||||
|
break;
|
||||||
|
case 0x00F: //Inverter Feedback
|
||||||
|
break;
|
||||||
|
case 0x010: //Time from inverter
|
||||||
|
break;
|
||||||
|
case 0x015: //Initialization message from inverter
|
||||||
|
send_tripower_init();
|
||||||
|
break;
|
||||||
|
case 0x017: //Initialization message from inverter 2
|
||||||
|
//send_tripower_init();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_can_sma_tripower() {
|
||||||
|
unsigned long currentMillis = millis();
|
||||||
|
|
||||||
|
// Send CAN Message every 500ms
|
||||||
|
if (currentMillis - previousMillis500ms >= interval500ms) {
|
||||||
|
previousMillis500ms = currentMillis;
|
||||||
|
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_00D); //Battery limits
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_00F); // Battery state
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_011); // Battery Energy
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_013); // Battery Measurements
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_014); // Battery Temperatures and cellvoltages
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batteryAlarm) { //Non-cyclic
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_005); // Battery Alarms 1
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_007); // Battery Alarms 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BMSevent) { //Non-cyclic
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_006); // Battery Errorcode
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_008); // Battery Events
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_tripower_init() {
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_015); // Battery Data 1
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_016); // Battery Data 2
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_017); // Battery Manufacturer
|
||||||
|
ESP32Can.CANWriteFrame(&SMA_018); // Battery Name
|
||||||
|
}
|
35
Software/src/inverter/SMA-TRIPOWER-CAN.h
Normal file
35
Software/src/inverter/SMA-TRIPOWER-CAN.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef SMA_CAN_TRIPOWER_H
|
||||||
|
#define SMA_CAN_TRIPOWER_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/config.h" // Needed for all defines
|
||||||
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
extern bool inverterAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
|
void update_values_can_sma_tripower();
|
||||||
|
void send_can_sma_tripower();
|
||||||
|
void receive_can_sma_tripower(CAN_frame_t rx_frame);
|
||||||
|
void send_tripower_init();
|
||||||
|
|
||||||
|
#endif
|
|
@ -281,29 +281,29 @@ CAN_frame_t SOFAR_7C0 = {.FIR = {.B =
|
||||||
void update_values_can_sofar() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
void update_values_can_sofar() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
||||||
|
|
||||||
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Charge Cutoff Voltage
|
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Charge Cutoff Voltage
|
||||||
SOFAR_351.data.u8[0] = (max_voltage >> 8);
|
SOFAR_351.data.u8[0] = (system_max_design_voltage_dV >> 8);
|
||||||
SOFAR_351.data.u8[1] = (max_voltage & 0x00FF);
|
SOFAR_351.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
|
||||||
//SOFAR_351.data.u8[2] = DC charge current limitation (Default 25.0A)
|
//SOFAR_351.data.u8[2] = DC charge current limitation (Default 25.0A)
|
||||||
//SOFAR_351.data.u8[3] = DC charge current limitation
|
//SOFAR_351.data.u8[3] = DC charge current limitation
|
||||||
//SOFAR_351.data.u8[4] = DC discharge current limitation (Default 25.0A)
|
//SOFAR_351.data.u8[4] = DC discharge current limitation (Default 25.0A)
|
||||||
//SOFAR_351.data.u8[5] = DC discharge current limitation
|
//SOFAR_351.data.u8[5] = DC discharge current limitation
|
||||||
//Minvoltage (eg 300.0V = 3000 , 16bits long) Discharge Cutoff Voltage
|
//Minvoltage (eg 300.0V = 3000 , 16bits long) Discharge Cutoff Voltage
|
||||||
SOFAR_351.data.u8[6] = (min_voltage >> 8);
|
SOFAR_351.data.u8[6] = (system_min_design_voltage_dV >> 8);
|
||||||
SOFAR_351.data.u8[7] = (min_voltage & 0x00FF);
|
SOFAR_351.data.u8[7] = (system_min_design_voltage_dV & 0x00FF);
|
||||||
|
|
||||||
//SOC
|
//SOC
|
||||||
SOFAR_355.data.u8[0] = (SOC / 100);
|
SOFAR_355.data.u8[0] = (system_scaled_SOC_pptt / 100);
|
||||||
SOFAR_355.data.u8[2] = (StateOfHealth / 100);
|
SOFAR_355.data.u8[2] = (system_SOH_pptt / 100);
|
||||||
//SOFAR_355.data.u8[6] = (AH_remaining >> 8);
|
//SOFAR_355.data.u8[6] = (AH_remaining >> 8);
|
||||||
//SOFAR_355.data.u8[7] = (AH_remaining & 0x00FF);
|
//SOFAR_355.data.u8[7] = (AH_remaining & 0x00FF);
|
||||||
|
|
||||||
//Voltage (370.0)
|
//Voltage (370.0)
|
||||||
SOFAR_356.data.u8[0] = (battery_voltage >> 8);
|
SOFAR_356.data.u8[0] = (system_battery_voltage_dV >> 8);
|
||||||
SOFAR_356.data.u8[1] = (battery_voltage & 0x00FF);
|
SOFAR_356.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
|
||||||
SOFAR_356.data.u8[2] = (battery_current >> 8);
|
SOFAR_356.data.u8[2] = (system_battery_current_dA >> 8);
|
||||||
SOFAR_356.data.u8[3] = (battery_current & 0x00FF);
|
SOFAR_356.data.u8[3] = (system_battery_current_dA & 0x00FF);
|
||||||
SOFAR_356.data.u8[2] = (temperature_max >> 8);
|
SOFAR_356.data.u8[2] = (system_temperature_max_dC >> 8);
|
||||||
SOFAR_356.data.u8[3] = (temperature_max & 0x00FF);
|
SOFAR_356.data.u8[3] = (system_temperature_max_dC & 0x00FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_sofar(CAN_frame_t rx_frame) {
|
void receive_can_sofar(CAN_frame_t rx_frame) {
|
||||||
|
|
|
@ -6,24 +6,27 @@
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
// These parameters need to be mapped for the inverter
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh; //Wh, 0-60000
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power; //W, 0-60000
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power; //W, 0-60000
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status; //Enum, 0-5
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status; //Enum, 0-2
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_max_voltage; //mV, 0-4350
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint8_t LEDcolor; //Enum, 0-10
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
extern bool inverterAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
extern uint16_t min_voltage;
|
extern uint16_t min_voltage;
|
||||||
extern uint16_t max_voltage;
|
extern uint16_t max_voltage;
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
#include "SOLAX-CAN.h"
|
#include "SOLAX-CAN.h"
|
||||||
|
#include "../devboard/utils/events.h"
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
static uint16_t max_charge_rate_amp = 0;
|
static uint16_t max_charge_rate_amp = 0;
|
||||||
static uint16_t max_discharge_rate_amp = 0;
|
static uint16_t max_discharge_rate_amp = 0;
|
||||||
static uint16_t temperature_average = 0;
|
static int16_t temperature_average = 0;
|
||||||
static int STATE = BATTERY_ANNOUNCE;
|
static uint8_t STATE = BATTERY_ANNOUNCE;
|
||||||
static unsigned long LastFrameTime = 0;
|
static unsigned long LastFrameTime = 0;
|
||||||
static int number_of_batteries = 1;
|
static uint8_t number_of_batteries = 1;
|
||||||
|
static uint16_t capped_capacity_Wh;
|
||||||
|
static uint16_t capped_remaining_capacity_Wh;
|
||||||
|
|
||||||
//CAN message translations from this amazing repository: https://github.com/rand12345/solax_can_bus
|
//CAN message translations from this amazing repository: https://github.com/rand12345/solax_can_bus
|
||||||
|
|
||||||
|
@ -122,64 +125,77 @@ void update_values_can_solax() { //This function maps all the values fetched fr
|
||||||
STATE = BATTERY_ANNOUNCE;
|
STATE = BATTERY_ANNOUNCE;
|
||||||
}
|
}
|
||||||
//Calculate the required values
|
//Calculate the required values
|
||||||
temperature_average = ((temperature_max + temperature_min) / 2);
|
temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
|
||||||
|
|
||||||
//max_target_charge_power (30000W max)
|
//system_max_charge_power_W (30000W max)
|
||||||
if (SOC > 9999) //99.99%
|
if (system_scaled_SOC_pptt > 9999) //99.99%
|
||||||
{ //Additional safety incase SOC% is 100, then do not charge battery further
|
{ //Additional safety incase SOC% is 100, then do not charge battery further
|
||||||
max_charge_rate_amp = 0;
|
max_charge_rate_amp = 0;
|
||||||
} else { //We can pass on the battery charge rate (in W) to the inverter (that takes A)
|
} else { //We can pass on the battery charge rate (in W) to the inverter (that takes A)
|
||||||
if (max_target_charge_power >= 30000) {
|
if (system_max_charge_power_W >= 30000) {
|
||||||
max_charge_rate_amp = 75; //Incase battery can take over 30kW, cap value to 75A
|
max_charge_rate_amp = 75; //Incase battery can take over 30kW, cap value to 75A
|
||||||
} else { //Calculate the W value into A
|
} else { //Calculate the W value into A
|
||||||
max_charge_rate_amp = (max_target_charge_power / (battery_voltage * 0.1)); // P/U = I
|
max_charge_rate_amp = (system_max_charge_power_W / (system_battery_voltage_dV * 0.1)); // P/U = I
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//max_target_discharge_power (30000W max)
|
//system_max_discharge_power_W (30000W max)
|
||||||
if (SOC < 100) //1.00%
|
if (system_scaled_SOC_pptt < 100) //1.00%
|
||||||
{ //Additional safety incase SOC% is below 1, then do not charge battery further
|
{ //Additional safety incase SOC% is below 1, then do not charge battery further
|
||||||
max_discharge_rate_amp = 0;
|
max_discharge_rate_amp = 0;
|
||||||
} else { //We can pass on the battery discharge rate to the inverter
|
} else { //We can pass on the battery discharge rate to the inverter
|
||||||
if (max_target_discharge_power >= 30000) {
|
if (system_max_discharge_power_W >= 30000) {
|
||||||
max_discharge_rate_amp = 75; //Incase battery can be charged with over 30kW, cap value to 75A
|
max_discharge_rate_amp = 75; //Incase battery can be charged with over 30kW, cap value to 75A
|
||||||
} else { //Calculate the W value into A
|
} else { //Calculate the W value into A
|
||||||
max_discharge_rate_amp = (max_target_discharge_power / (battery_voltage * 0.1)); // P/U = I
|
max_discharge_rate_amp = (system_max_discharge_power_W / (system_battery_voltage_dV * 0.1)); // P/U = I
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Batteries might be larger than uint16_t value can take
|
||||||
|
if (system_capacity_Wh > 65000) {
|
||||||
|
capped_capacity_Wh = 65000;
|
||||||
|
} else {
|
||||||
|
capped_capacity_Wh = system_capacity_Wh;
|
||||||
|
}
|
||||||
|
// Batteries might be larger than uint16_t value can take
|
||||||
|
if (system_remaining_capacity_Wh > 65000) {
|
||||||
|
capped_remaining_capacity_Wh = 65000;
|
||||||
|
} else {
|
||||||
|
capped_remaining_capacity_Wh = system_remaining_capacity_Wh;
|
||||||
|
}
|
||||||
|
|
||||||
//Put the values into the CAN messages
|
//Put the values into the CAN messages
|
||||||
//BMS_Limits
|
//BMS_Limits
|
||||||
SOLAX_1872.data.u8[0] = (uint8_t)max_voltage; //TODO: scaling OK?
|
SOLAX_1872.data.u8[0] = (uint8_t)system_max_design_voltage_dV;
|
||||||
SOLAX_1872.data.u8[1] = (max_voltage >> 8);
|
SOLAX_1872.data.u8[1] = (system_max_design_voltage_dV >> 8);
|
||||||
SOLAX_1872.data.u8[2] = (uint8_t)min_voltage; //TODO: scaling OK?
|
SOLAX_1872.data.u8[2] = (uint8_t)system_min_design_voltage_dV;
|
||||||
SOLAX_1872.data.u8[3] = (min_voltage >> 8);
|
SOLAX_1872.data.u8[3] = (system_min_design_voltage_dV >> 8);
|
||||||
SOLAX_1872.data.u8[4] = (uint8_t)(max_charge_rate_amp * 10); //TODO: scaling OK?
|
SOLAX_1872.data.u8[4] = (uint8_t)(max_charge_rate_amp * 10);
|
||||||
SOLAX_1872.data.u8[5] = ((max_charge_rate_amp * 10) >> 8);
|
SOLAX_1872.data.u8[5] = ((max_charge_rate_amp * 10) >> 8);
|
||||||
SOLAX_1872.data.u8[6] = (uint8_t)(max_discharge_rate_amp * 10); //TODO: scaling OK?
|
SOLAX_1872.data.u8[6] = (uint8_t)(max_discharge_rate_amp * 10);
|
||||||
SOLAX_1872.data.u8[7] = ((max_discharge_rate_amp * 10) >> 8);
|
SOLAX_1872.data.u8[7] = ((max_discharge_rate_amp * 10) >> 8);
|
||||||
|
|
||||||
//BMS_PackData
|
//BMS_PackData
|
||||||
SOLAX_1873.data.u8[0] = (uint8_t)battery_voltage; // OK
|
SOLAX_1873.data.u8[0] = (uint8_t)system_battery_voltage_dV; // OK
|
||||||
SOLAX_1873.data.u8[1] = (battery_voltage >> 8);
|
SOLAX_1873.data.u8[1] = (system_battery_voltage_dV >> 8);
|
||||||
SOLAX_1873.data.u8[2] = (int8_t)battery_current; // OK, Signed (Active current in Amps x 10)
|
SOLAX_1873.data.u8[2] = (int8_t)system_battery_current_dA; // OK, Signed (Active current in Amps x 10)
|
||||||
SOLAX_1873.data.u8[3] = (battery_current >> 8);
|
SOLAX_1873.data.u8[3] = (system_battery_current_dA >> 8);
|
||||||
SOLAX_1873.data.u8[4] = (uint8_t)(SOC / 100); //SOC (100.00%)
|
SOLAX_1873.data.u8[4] = (uint8_t)(system_scaled_SOC_pptt / 100); //SOC (100.00%)
|
||||||
//SOLAX_1873.data.u8[5] = //Seems like this is not required? Or shall we put SOC decimals here?
|
//SOLAX_1873.data.u8[5] = //Seems like this is not required? Or shall we put SOC decimals here?
|
||||||
SOLAX_1873.data.u8[6] = (uint8_t)(remaining_capacity_Wh / 100); //TODO: scaling OK?
|
SOLAX_1873.data.u8[6] = (uint8_t)(capped_remaining_capacity_Wh / 100); //TODO: scaling OK?
|
||||||
SOLAX_1873.data.u8[7] = ((remaining_capacity_Wh / 100) >> 8);
|
SOLAX_1873.data.u8[7] = ((capped_remaining_capacity_Wh / 100) >> 8);
|
||||||
|
|
||||||
//BMS_CellData
|
//BMS_CellData
|
||||||
SOLAX_1874.data.u8[0] = (uint8_t)temperature_max;
|
SOLAX_1874.data.u8[0] = (int8_t)system_temperature_max_dC;
|
||||||
SOLAX_1874.data.u8[1] = (temperature_max >> 8);
|
SOLAX_1874.data.u8[1] = (system_temperature_max_dC >> 8);
|
||||||
SOLAX_1874.data.u8[2] = (uint8_t)temperature_min;
|
SOLAX_1874.data.u8[2] = (int8_t)system_temperature_min_dC;
|
||||||
SOLAX_1874.data.u8[3] = (temperature_min >> 8);
|
SOLAX_1874.data.u8[3] = (system_temperature_min_dC >> 8);
|
||||||
SOLAX_1874.data.u8[4] =
|
SOLAX_1874.data.u8[4] =
|
||||||
(uint8_t)(cell_max_voltage); //TODO: scaling OK? Supposed to be alarm trigger absolute cell max?
|
(uint8_t)(system_cell_max_voltage_mV); //TODO: scaling OK? Supposed to be alarm trigger absolute cell max?
|
||||||
SOLAX_1874.data.u8[5] = (cell_max_voltage >> 8);
|
SOLAX_1874.data.u8[5] = (system_cell_max_voltage_mV >> 8);
|
||||||
SOLAX_1874.data.u8[6] =
|
SOLAX_1874.data.u8[6] =
|
||||||
(uint8_t)(cell_min_voltage); //TODO: scaling OK? Supposed to be alarm trigger absolute cell min?
|
(uint8_t)(system_cell_min_voltage_mV); //TODO: scaling OK? Supposed to be alarm trigger absolute cell min?
|
||||||
SOLAX_1874.data.u8[7] = (cell_min_voltage >> 8);
|
SOLAX_1874.data.u8[7] = (system_cell_min_voltage_mV >> 8);
|
||||||
|
|
||||||
//BMS_Status
|
//BMS_Status
|
||||||
SOLAX_1875.data.u8[0] = (uint8_t)temperature_average;
|
SOLAX_1875.data.u8[0] = (uint8_t)temperature_average;
|
||||||
|
@ -188,11 +204,11 @@ void update_values_can_solax() { //This function maps all the values fetched fr
|
||||||
SOLAX_1875.data.u8[4] = (uint8_t)0; // Contactor Status 0=off, 1=on.
|
SOLAX_1875.data.u8[4] = (uint8_t)0; // Contactor Status 0=off, 1=on.
|
||||||
|
|
||||||
//BMS_PackTemps (strange name, since it has voltages?)
|
//BMS_PackTemps (strange name, since it has voltages?)
|
||||||
SOLAX_1876.data.u8[2] = (uint8_t)cell_max_voltage; //TODO: scaling OK?
|
SOLAX_1876.data.u8[2] = (uint8_t)system_cell_max_voltage_mV; //TODO: scaling OK?
|
||||||
SOLAX_1876.data.u8[3] = (cell_max_voltage >> 8);
|
SOLAX_1876.data.u8[3] = (system_cell_max_voltage_mV >> 8);
|
||||||
|
|
||||||
SOLAX_1876.data.u8[6] = (uint8_t)cell_min_voltage; //TODO: scaling OK?
|
SOLAX_1876.data.u8[6] = (uint8_t)system_cell_min_voltage_mV; //TODO: scaling OK?
|
||||||
SOLAX_1876.data.u8[7] = (cell_min_voltage >> 8);
|
SOLAX_1876.data.u8[7] = (system_cell_min_voltage_mV >> 8);
|
||||||
|
|
||||||
//Unknown
|
//Unknown
|
||||||
SOLAX_1877.data.u8[4] = (uint8_t)0x50; // Battery type
|
SOLAX_1877.data.u8[4] = (uint8_t)0x50; // Battery type
|
||||||
|
@ -201,11 +217,11 @@ void update_values_can_solax() { //This function maps all the values fetched fr
|
||||||
(uint8_t)0x02; // The above firmware version applies to:02 = Master BMS, 10 = S1, 20 = S2, 30 = S3, 40 = S4
|
(uint8_t)0x02; // The above firmware version applies to:02 = Master BMS, 10 = S1, 20 = S2, 30 = S3, 40 = S4
|
||||||
|
|
||||||
//BMS_PackStats
|
//BMS_PackStats
|
||||||
SOLAX_1878.data.u8[0] = (uint8_t)(battery_voltage); //TODO: should this be max or current voltage?
|
SOLAX_1878.data.u8[0] = (uint8_t)(system_battery_voltage_dV); //TODO: should this be max or current voltage?
|
||||||
SOLAX_1878.data.u8[1] = ((battery_voltage) >> 8);
|
SOLAX_1878.data.u8[1] = ((system_battery_voltage_dV) >> 8);
|
||||||
|
|
||||||
SOLAX_1878.data.u8[4] = (uint8_t)capacity_Wh; //TODO: scaling OK?
|
SOLAX_1878.data.u8[4] = (uint8_t)capped_capacity_Wh; //TODO: scaling OK?
|
||||||
SOLAX_1878.data.u8[5] = (capacity_Wh >> 8);
|
SOLAX_1878.data.u8[5] = (capped_capacity_Wh >> 8);
|
||||||
|
|
||||||
// BMS_Answer
|
// BMS_Answer
|
||||||
SOLAX_1801.data.u8[0] = 2;
|
SOLAX_1801.data.u8[0] = 2;
|
||||||
|
@ -264,8 +280,10 @@ void receive_can_solax(CAN_frame_t rx_frame) {
|
||||||
CAN_WriteFrame(&SOLAX_1878);
|
CAN_WriteFrame(&SOLAX_1878);
|
||||||
// Message from the inverter to open contactor
|
// Message from the inverter to open contactor
|
||||||
// Byte 4 changes from 1 to 0
|
// Byte 4 changes from 1 to 0
|
||||||
if (rx_frame.data.u64 == Contactor_Open_Payload)
|
if (rx_frame.data.u64 == Contactor_Open_Payload) {
|
||||||
|
set_event(EVENT_INVERTER_OPEN_CONTACTOR, 0);
|
||||||
STATE = BATTERY_ANNOUNCE;
|
STATE = BATTERY_ANNOUNCE;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,26 +8,27 @@
|
||||||
|
|
||||||
extern ACAN2515 can;
|
extern ACAN2515 can;
|
||||||
|
|
||||||
extern uint16_t SOC;
|
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t StateOfHealth;
|
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
|
||||||
extern uint16_t battery_voltage;
|
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t battery_current;
|
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
|
||||||
extern uint16_t capacity_Wh;
|
extern int16_t system_active_power_W; //W, -32000 to 32000
|
||||||
extern uint16_t remaining_capacity_Wh;
|
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
|
||||||
extern uint16_t max_target_discharge_power;
|
extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint16_t max_target_charge_power;
|
extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_status;
|
extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
|
||||||
extern uint8_t bms_char_dis_status;
|
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t stat_batt_power;
|
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_min;
|
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
|
||||||
extern uint16_t temperature_max;
|
extern uint16_t system_max_discharge_power_W; //W, 0-65000
|
||||||
extern uint16_t CANerror;
|
extern uint16_t system_max_charge_power_W; //W, 0-65000
|
||||||
extern uint16_t min_voltage;
|
extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
|
||||||
extern uint16_t max_voltage;
|
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
|
||||||
extern uint16_t cell_max_voltage;
|
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
|
||||||
extern uint16_t cell_min_voltage;
|
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
|
||||||
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern uint8_t system_bms_status; //Enum 0-5
|
||||||
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
|
extern bool batteryAllowsContactorClosing; //Bool, true/false
|
||||||
|
extern bool inverterAllowsContactorClosing; //Bool, true/false
|
||||||
|
|
||||||
// Timeout in milliseconds
|
// Timeout in milliseconds
|
||||||
#define SolaxTimeout 2000
|
#define SolaxTimeout 2000
|
||||||
|
|
7578
Software/src/lib/bblanchon-ArduinoJson/ArduinoJson.h
Normal file
7578
Software/src/lib/bblanchon-ArduinoJson/ArduinoJson.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,6 @@
|
||||||
#include "ESP32CAN.h"
|
#include "ESP32CAN.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "../../devboard/utils/events.h"
|
||||||
|
|
||||||
int ESP32CAN::CANInit() {
|
int ESP32CAN::CANInit() {
|
||||||
return CAN_init();
|
return CAN_init();
|
||||||
|
@ -12,13 +13,14 @@ int ESP32CAN::CANWriteFrame(const CAN_frame_t* p_frame) {
|
||||||
tx_ok = (result == 0) ? true : false;
|
tx_ok = (result == 0) ? true : false;
|
||||||
if (tx_ok == false) {
|
if (tx_ok == false) {
|
||||||
Serial.println("CAN failure! Check wires");
|
Serial.println("CAN failure! Check wires");
|
||||||
LEDcolor = 3;
|
set_event(EVENT_CAN_TX_FAILURE, 0);
|
||||||
start_time = millis();
|
start_time = millis();
|
||||||
|
} else {
|
||||||
|
clear_event(EVENT_CAN_TX_FAILURE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((millis() - start_time) >= 2000) {
|
if ((millis() - start_time) >= 2000) {
|
||||||
tx_ok = true;
|
tx_ok = true;
|
||||||
LEDcolor = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include "../../lib/miwagner-ESP32-Arduino-CAN/CAN.h"
|
#include "../../lib/miwagner-ESP32-Arduino-CAN/CAN.h"
|
||||||
#include "../../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
extern uint8_t LEDcolor;
|
|
||||||
|
|
||||||
class ESP32CAN {
|
class ESP32CAN {
|
||||||
public:
|
public:
|
||||||
|
|
15
cmake_clean.bat
Normal file
15
cmake_clean.bat
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
@echo off
|
||||||
|
echo Cleaning up
|
||||||
|
rmdir /Q/S build
|
||||||
|
echo Creating new CMake build folder
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
echo Building CMake project
|
||||||
|
call cmake ..
|
||||||
|
call cmake --build .
|
||||||
|
echo Executing tests
|
||||||
|
for %%i in ("test\Debug\*.exe") do (
|
||||||
|
echo Running %%i
|
||||||
|
%%i
|
||||||
|
)
|
||||||
|
cd..
|
18
test/CMakeLists.txt
Normal file
18
test/CMakeLists.txt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Include the directory with your source files
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/Software/src/devboard ${CMAKE_SOURCE_DIR}/Software/src/devboard/utils . )
|
||||||
|
|
||||||
|
# Create a variable to store the list of test files
|
||||||
|
file(GLOB TEST_SOURCES utils/*.cpp)
|
||||||
|
|
||||||
|
# Loop through each test source file and create an executable
|
||||||
|
foreach(TEST_SOURCE ${TEST_SOURCES})
|
||||||
|
# Extract the test name without extension
|
||||||
|
get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE)
|
||||||
|
|
||||||
|
# Create an executable for the test
|
||||||
|
add_executable(${TEST_NAME} ${TEST_SOURCE} test_lib.cpp)
|
||||||
|
|
||||||
|
# Apply the target_compile_definitions for the test
|
||||||
|
target_compile_definitions(${TEST_NAME} PRIVATE UNIT_TEST)
|
||||||
|
|
||||||
|
endforeach()
|
209
test/microtest.h
Normal file
209
test/microtest.h
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
//
|
||||||
|
// microtest.h
|
||||||
|
//
|
||||||
|
// URL: https://github.com/torpedro/microtest.h
|
||||||
|
// Author: Pedro Flemming (http://torpedro.com/)
|
||||||
|
// License: MIT License (https://github.com/torpedro/microtest.h/blob/master/LICENSE)
|
||||||
|
// Copyright (c) 2017 Pedro Flemming
|
||||||
|
//
|
||||||
|
// This is a small header-only C++ unit testing framework.
|
||||||
|
// It allows to define small unit tests with set of assertions available.
|
||||||
|
//
|
||||||
|
#ifndef __MICROTEST_H__
|
||||||
|
#define __MICROTEST_H__
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// Assertions //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
#define ASSERT(cond) ASSERT_TRUE(cond);
|
||||||
|
|
||||||
|
#define ASSERT_TRUE(cond) \
|
||||||
|
if (!(cond)) \
|
||||||
|
throw mt::AssertFailedException(#cond, __FILE__, __LINE__);
|
||||||
|
|
||||||
|
#define ASSERT_FALSE(cond) \
|
||||||
|
if (cond) \
|
||||||
|
throw mt::AssertFailedException(#cond, __FILE__, __LINE__);
|
||||||
|
|
||||||
|
#define ASSERT_NULL(value) ASSERT_TRUE(value == NULL);
|
||||||
|
|
||||||
|
#define ASSERT_NOTNULL(value) ASSERT_TRUE(value != NULL);
|
||||||
|
|
||||||
|
#define ASSERT_STREQ(a, b) \
|
||||||
|
if (std::string(a).compare(std::string(b)) != 0) { \
|
||||||
|
printf("%s{ info} %s", mt::yellow(), mt::def()); \
|
||||||
|
std::cout << "Actual values: " << a << " != " << b << std::endl; \
|
||||||
|
throw mt::AssertFailedException(#a " == " #b, __FILE__, __LINE__); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ASSERT_STRNEQ(a, b) \
|
||||||
|
if (std::string(a).compare(std::string(b)) != = 0) { \
|
||||||
|
printf("%s{ info} %s", mt::yellow(), mt::def()); \
|
||||||
|
std::cout << "Actual values: " << a << " == " << b << std::endl; \
|
||||||
|
throw mt::AssertFailedException(#a " != " #b, __FILE__, __LINE__); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ASSERT_EQ(a, b) \
|
||||||
|
if (a != b) { \
|
||||||
|
printf("%s{ info} %s", mt::yellow(), mt::def()); \
|
||||||
|
std::cout << "Actual values: " << a << " != " << b << std::endl; \
|
||||||
|
} \
|
||||||
|
ASSERT(a == b);
|
||||||
|
|
||||||
|
#define ASSERT_NEQ(a, b) \
|
||||||
|
if (a == b) { \
|
||||||
|
printf("%s{ info} %s", mt::yellow(), mt::def()); \
|
||||||
|
std::cout << "Actual values: " << a << " == " << b << std::endl; \
|
||||||
|
} \
|
||||||
|
ASSERT(a != b);
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// Unit Tests //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
#define TEST(name) \
|
||||||
|
void name(); \
|
||||||
|
namespace { \
|
||||||
|
bool __##name = mt::TestsManager::AddTest(name, #name); \
|
||||||
|
} \
|
||||||
|
void name()
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
// Framework //
|
||||||
|
///////////////
|
||||||
|
|
||||||
|
namespace mt {
|
||||||
|
|
||||||
|
inline const char* red() {
|
||||||
|
return "\033[1;31m";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char* green() {
|
||||||
|
return "\033[0;32m";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char* yellow() {
|
||||||
|
return "\033[0;33m";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char* def() {
|
||||||
|
return "\033[0m";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void printRunning(const char* message, FILE* file = stdout) {
|
||||||
|
fprintf(file, "%s{ running}%s %s\n", green(), def(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void printOk(const char* message, FILE* file = stdout) {
|
||||||
|
fprintf(file, "%s{ ok}%s %s\n", green(), def(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void printFailed(const char* message, FILE* file = stdout) {
|
||||||
|
fprintf(file, "%s{ failed} %s%s\n", red(), message, def());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exception that is thrown when an assertion fails.
|
||||||
|
class AssertFailedException : public std::exception {
|
||||||
|
public:
|
||||||
|
AssertFailedException(std::string description, std::string filepath, int line)
|
||||||
|
: std::exception(), description_(description), filepath_(filepath), line_(line){};
|
||||||
|
|
||||||
|
virtual const char* what() const throw() { return description_.c_str(); }
|
||||||
|
|
||||||
|
inline const char* getFilepath() { return filepath_.c_str(); }
|
||||||
|
|
||||||
|
inline int getLine() { return line_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string description_;
|
||||||
|
std::string filepath_;
|
||||||
|
int line_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestsManager {
|
||||||
|
// Note: static initialization fiasco
|
||||||
|
// http://www.parashift.com/c++-faq-lite/static-init-order.html
|
||||||
|
// http://www.parashift.com/c++-faq-lite/static-init-order-on-first-use.html
|
||||||
|
public:
|
||||||
|
struct Test {
|
||||||
|
const char* name;
|
||||||
|
void (*fn)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::vector<Test>& tests() {
|
||||||
|
static std::vector<Test> tests_;
|
||||||
|
return tests_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a new test to the current set of tests.
|
||||||
|
// Returns false if a test with the same name already exists.
|
||||||
|
inline static bool AddTest(void (*fn)(void), const char* name) {
|
||||||
|
tests().push_back({name, fn});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all tests that are registered.
|
||||||
|
// Returns the number of tests that failed.
|
||||||
|
inline static size_t RunAllTests(FILE* file = stdout) {
|
||||||
|
size_t num_failed = 0;
|
||||||
|
|
||||||
|
for (const Test& test : tests()) {
|
||||||
|
// Run the test.
|
||||||
|
// If an AsserFailedException is thrown, the test has failed.
|
||||||
|
try {
|
||||||
|
printRunning(test.name, file);
|
||||||
|
|
||||||
|
(*test.fn)();
|
||||||
|
|
||||||
|
printOk(test.name, file);
|
||||||
|
|
||||||
|
} catch (AssertFailedException& e) {
|
||||||
|
printFailed(test.name, file);
|
||||||
|
fprintf(file, " %sAssertion failed: %s%s\n", red(), e.what(), def());
|
||||||
|
fprintf(file, " %s%s:%d%s\n", red(), e.getFilepath(), e.getLine(), def());
|
||||||
|
++num_failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int return_code = (num_failed > 0) ? 1 : 0;
|
||||||
|
return return_code;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Class that will capture the arguments passed to the program.
|
||||||
|
class Runtime {
|
||||||
|
public:
|
||||||
|
static const std::vector<std::string>& args(int argc = -1, char** argv = NULL) {
|
||||||
|
static std::vector<std::string> args_;
|
||||||
|
if (argc >= 0) {
|
||||||
|
for (int i = 0; i < argc; ++i) {
|
||||||
|
args_.push_back(argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace mt
|
||||||
|
|
||||||
|
#define TEST_MAIN() \
|
||||||
|
int main(int argc, char* argv[]) { \
|
||||||
|
mt::Runtime::args(argc, argv); \
|
||||||
|
\
|
||||||
|
size_t num_failed = mt::TestsManager::RunAllTests(stdout); \
|
||||||
|
if (num_failed == 0) { \
|
||||||
|
fprintf(stdout, "%s{ summary} All tests succeeded!%s\n", mt::green(), mt::def()); \
|
||||||
|
return 0; \
|
||||||
|
} else { \
|
||||||
|
double percentage = 100.0 * num_failed / mt::TestsManager::tests().size(); \
|
||||||
|
fprintf(stderr, "%s{ summary} %zu tests failed (%.2f%%)%s\n", mt::red(), num_failed, percentage, mt::def()); \
|
||||||
|
return -1; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __MICROTEST_H__
|
13
test/test_lib.cpp
Normal file
13
test/test_lib.cpp
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include "test_lib.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "../Software/src/devboard/config.h"
|
||||||
|
|
||||||
|
MySerial Serial;
|
||||||
|
|
||||||
|
unsigned long testlib_millis = 0;
|
||||||
|
|
||||||
|
uint8_t bms_status = ACTIVE;
|
||||||
|
|
||||||
|
uint8_t LEDcolor = GREEN;
|
48
test/test_lib.h
Normal file
48
test/test_lib.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef __TEST_LIB_H__
|
||||||
|
#define __TEST_LIB_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "microtest.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class MySerial;
|
||||||
|
|
||||||
|
extern unsigned long testlib_millis;
|
||||||
|
extern MySerial Serial;
|
||||||
|
extern uint8_t bms_status;
|
||||||
|
extern uint8_t LEDcolor;
|
||||||
|
|
||||||
|
/* Mock millis() */
|
||||||
|
static inline unsigned long millis(void) {
|
||||||
|
return testlib_millis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mock Serial class */
|
||||||
|
class MySerial {
|
||||||
|
public:
|
||||||
|
size_t println(const char* s) {
|
||||||
|
return print(s, true); // Call print with newline argument true
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t print(const char* s) {
|
||||||
|
return print(s, false); // Call print with newline argument false
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t print(const char* s, bool newline) {
|
||||||
|
size_t length = printf("%s", s); // Print the string without newline
|
||||||
|
if (newline) {
|
||||||
|
printf("\n"); // Add a newline if specified
|
||||||
|
length++; // Increment length to account for the added newline character
|
||||||
|
}
|
||||||
|
return length; // Return the total length printed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
105
test/utils/events_test.cpp_
Normal file
105
test/utils/events_test.cpp_
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// The test library must be included first!
|
||||||
|
#include "../test_lib.h"
|
||||||
|
|
||||||
|
#include "../../Software/src/devboard/config.h"
|
||||||
|
#include "../../Software/src/devboard/utils/timer.cpp"
|
||||||
|
|
||||||
|
|
||||||
|
class EEPROMClass {
|
||||||
|
public:
|
||||||
|
void begin(int s) {}
|
||||||
|
void writeUShort(int a, uint16_t d) {}
|
||||||
|
void commit(void) {}
|
||||||
|
uint16_t readUShort(int a) {}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void get(int address, T &t) {}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void put(int address, const T &t) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
EEPROMClass EEPROM;
|
||||||
|
|
||||||
|
#include "../../Software/src/devboard/utils/events.cpp"
|
||||||
|
/* Local test variables */
|
||||||
|
bool elapsed = false;
|
||||||
|
|
||||||
|
/* Stubs */
|
||||||
|
void run_sequence_on_target(void) {}
|
||||||
|
|
||||||
|
/* Helper functions */
|
||||||
|
/* Test functions */
|
||||||
|
|
||||||
|
TEST(init_events_test) {
|
||||||
|
init_events();
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < EVENT_NOF_EVENTS; i++) {
|
||||||
|
ASSERT_EQ(events.entries[i].occurences, 0);
|
||||||
|
ASSERT_EQ(events.entries[i].data, 0);
|
||||||
|
ASSERT_EQ(events.entries[i].timestamp, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(update_event_time_test) {
|
||||||
|
// Reset
|
||||||
|
testlib_millis = 0;
|
||||||
|
events.time_seconds = 0;
|
||||||
|
init_events();
|
||||||
|
|
||||||
|
// No delta, so time shouldn't increase
|
||||||
|
update_event_time();
|
||||||
|
ASSERT_EQ(events.time_seconds, 0);
|
||||||
|
|
||||||
|
// Almost time to bump the seconds
|
||||||
|
testlib_millis = 999;
|
||||||
|
update_event_time();
|
||||||
|
ASSERT_EQ(events.time_seconds, 0);
|
||||||
|
|
||||||
|
// millis == 1000, so we should add a second
|
||||||
|
testlib_millis = 1000;
|
||||||
|
update_event_time();
|
||||||
|
ASSERT_EQ(events.time_seconds, 1);
|
||||||
|
|
||||||
|
// We shouldn't add more seconds until 2000 now
|
||||||
|
testlib_millis = 1999;
|
||||||
|
update_event_time();
|
||||||
|
ASSERT_EQ(events.time_seconds, 1);
|
||||||
|
testlib_millis = 2000;
|
||||||
|
update_event_time();
|
||||||
|
ASSERT_EQ(events.time_seconds, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(set_event_test) {
|
||||||
|
// Reset
|
||||||
|
init_events();
|
||||||
|
events.time_seconds = 0;
|
||||||
|
|
||||||
|
// Initially, the event should not have any data or occurences
|
||||||
|
ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].data, 0);
|
||||||
|
ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].occurences, 0);
|
||||||
|
ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].timestamp, 0);
|
||||||
|
// Set current time and overvoltage event for cell 23 (RED color, bms_status == FAULT)
|
||||||
|
events.time_seconds = 345;
|
||||||
|
set_event(EVENT_CELL_OVER_VOLTAGE, 123);
|
||||||
|
// Ensure proper event data
|
||||||
|
ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].data, 123);
|
||||||
|
ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].occurences, 1);
|
||||||
|
ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].timestamp, 345);
|
||||||
|
ASSERT_EQ(bms_status, FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(events_message_test) {
|
||||||
|
set_event(EVENT_DUMMY_ERROR, 0); // Set dummy event with no data
|
||||||
|
|
||||||
|
ASSERT_STREQ("The dummy error event was set!", get_event_message_string(EVENT_DUMMY_ERROR));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(events_level_test) {
|
||||||
|
init_events();
|
||||||
|
set_event(EVENT_DUMMY_ERROR, 0); // Set dummy event with no data
|
||||||
|
|
||||||
|
ASSERT_STREQ("ERROR", get_event_level_string(EVENT_DUMMY_ERROR));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_MAIN();
|
Loading…
Add table
Add a link
Reference in a new issue