Skip to content

Commit

Permalink
Merge branch 'main' into SIMPBMS
Browse files Browse the repository at this point in the history
  • Loading branch information
jamiejones85 authored Feb 25, 2025
2 parents e256df4 + cd9f7df commit 10b85f3
Show file tree
Hide file tree
Showing 80 changed files with 4,694 additions and 2,348 deletions.
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: Triage
assignees: ''

---

**Describe the bug**
A clear and concise description of what the encountered bug is

**To Reproduce**
Please try to explain the steps required to reproduce the issue

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Version and settings (please complete the following information):**
- Please attach your USER_SETTINGS files for easier debugging
- Software version: X.Y.Z
- Battery used: [NISSAN_LEAF]
- Inverter communication protocol: [BYD_MODBUS]
- Hardware used for Battery-Emulator: [HW_LILYGO/HW_STARK/HW_DEVKIT]
- CONTACTOR_CONTROL: [yes/no]
- MQTT: [yes/no]
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: Discord
url: https://www.patreon.com/dala
about: Get direct support and hang out with us! Join via Patreon
- name: Wiki
url: /~https://github.com/dalathegreat/Battery-Emulator/wiki
about: Highly recommended to read before asking questions!
20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature request] "
labels: Feature request
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
1 change: 1 addition & 0 deletions .github/workflows/compile-all-batteries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ jobs:
- MEB_BATTERY
- MG_5_BATTERY
- NISSAN_LEAF_BATTERY
- ORION_BMS
- PYLON_BATTERY
- RJXZS_BMS
- RANGE_ROVER_PHEV_BATTERY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ jobs:
# These are the batteries for which the code will be compiled.
battery:
- NISSAN_LEAF_BATTERY
- ORION_BMS
- PYLON_BATTERY
- RJXZS_BMS
- RANGE_ROVER_PHEV_BATTERY
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/compile-all-inverters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ jobs:
- BYD_CAN
- BYD_KOSTAL_RS485
- BYD_MODBUS
- FERROAMP_CAN
- FOXESS_CAN
- GROWATT_HV_CAN
- GROWATT_LV_CAN
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ This code uses the following excellent libraries:
- [bblanchon/ArduinoJson](/~https://github.com/bblanchon/ArduinoJson) MIT-License
- [eModbus/eModbus](/~https://github.com/eModbus/eModbus) MIT-License
- [mackelec/SerialDataLink](/~https://github.com/mackelec/SerialDataLink)
- [mathieucarbou/AsyncTCPsock](/~https://github.com/mathieucarbou/AsyncTCPSock) LGPL-3.0 license
- [ESP32Async/AsyncTCP](/~https://github.com/ESP32Async/AsyncTCP) LGPL-3.0 license
- [ESP32Async/ESPAsyncWebServer](/~https://github.com/ESP32Async/ESPAsyncWebServer) LGPL-3.0 license
- [miwagner/ESP32-Arduino-CAN](/~https://github.com/miwagner/ESP32-Arduino-CAN/) MIT-License
- [pierremolinaro/acan2515](/~https://github.com/pierremolinaro/acan2515) MIT-License
Expand Down
68 changes: 60 additions & 8 deletions Software/Software.ino
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
#endif // WIFI

// The current software version, shown on webserver
const char* version_number = "8.5.dev";
const char* version_number = "8.7.dev";

// Interval settings
uint16_t intervalUpdateValues = INTERVAL_1_S; // Interval at which to update inverter values / Modbus registers
Expand All @@ -73,13 +73,17 @@ TaskHandle_t logging_loop_task;

Logging logging;

#define WDT_TIMEOUT_SECONDS 5 // If code hangs for longer than this, it will be rebooted by the watchdog

// Initialization
void setup() {
init_serial();

// We print this after setting up serial, such that is also printed to serial with DEBUG_VIA_USB set.
logging.printf("Battery emulator %s build " __DATE__ " " __TIME__ "\n", version_number);

init_events();

init_stored_settings();

#ifdef WIFI
Expand All @@ -92,8 +96,6 @@ void setup() {
TASK_CONNECTIVITY_PRIO, &logging_loop_task, WIFI_CORE);
#endif

init_events();

init_CAN();

init_contactors();
Expand All @@ -118,10 +120,19 @@ void setup() {
// BOOT button at runtime is used as an input for various things
pinMode(0, INPUT_PULLUP);

esp_task_wdt_deinit(); // Disable watchdog

check_reset_reason();

// Initialize Task Watchdog for subscribed tasks
esp_task_wdt_config_t wdt_config = {
.timeout_ms = WDT_TIMEOUT_SECONDS * 1000, // Convert seconds to milliseconds
.idle_core_mask = (1 << CORE_FUNCTION_CORE) | (1 << WIFI_CORE), // Watch both cores
.trigger_panic = true // Enable panic reset on timeout
};

// Initialize Task Watchdog
esp_task_wdt_init(&wdt_config);

// Start tasks
xTaskCreatePinnedToCore((TaskFunction_t)&core_loop, "core_loop", 4096, &core_task_time_us, TASK_CORE_PRIO,
&main_loop_task, CORE_FUNCTION_CORE);
}
Expand Down Expand Up @@ -157,7 +168,7 @@ void logging_loop(void* task_time_us) {

#ifdef WIFI
void connectivity_loop(void* task_time_us) {

esp_task_wdt_add(NULL); // Register this task with WDT
// Init wifi
init_WiFi();

Expand Down Expand Up @@ -191,12 +202,14 @@ void connectivity_loop(void* task_time_us) {
datalayer.system.status.wifi_task_10s_max_us = 0;
}
#endif
esp_task_wdt_reset(); // Reset watchdog
delay(1);
}
}
#endif

void core_loop(void* task_time_us) {
esp_task_wdt_add(NULL); // Register this task with WDT
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = pdMS_TO_TICKS(1); // Convert 1ms to ticks
led_init();
Expand All @@ -210,7 +223,7 @@ void core_loop(void* task_time_us) {

// Input, Runs as fast as possible
receive_can(); // Receive CAN messages
#ifdef RS485_INVERTER_SELECTED
#if defined(RS485_INVERTER_SELECTED) || defined(RS485_BATTERY_SELECTED)
receive_RS485(); // Process serial2 RS485 interface
#endif // RS485_INVERTER_SELECTED
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
Expand Down Expand Up @@ -255,6 +268,10 @@ void core_loop(void* task_time_us) {
// Output
transmit_can(); // Send CAN messages to all components

#ifdef RS485_BATTERY_SELECTED
transmit_rs485();
#endif // RS485_BATTERY_SELECTED

END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us);
END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us);
#ifdef FUNCTION_TIME_MEASUREMENT
Expand Down Expand Up @@ -286,7 +303,7 @@ void core_loop(void* task_time_us) {
#ifdef DEBUG_LOG
logging.log_bms_status(datalayer.battery.status.real_bms_status, 1);
#endif

esp_task_wdt_reset(); // Reset watchdog to prevent reset
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
}
Expand Down Expand Up @@ -335,13 +352,48 @@ void update_calculated_values() {
/* Restrict values from user settings if needed*/
if (datalayer.battery.status.max_charge_current_dA > datalayer.battery.settings.max_user_set_charge_dA) {
datalayer.battery.status.max_charge_current_dA = datalayer.battery.settings.max_user_set_charge_dA;
datalayer.battery.settings.user_settings_limit_charge = true;
} else {
datalayer.battery.settings.user_settings_limit_charge = false;
}
if (datalayer.battery.status.max_discharge_current_dA > datalayer.battery.settings.max_user_set_discharge_dA) {
datalayer.battery.status.max_discharge_current_dA = datalayer.battery.settings.max_user_set_discharge_dA;
datalayer.battery.settings.user_settings_limit_discharge = true;
} else {
datalayer.battery.settings.user_settings_limit_discharge = false;
}
/* Calculate active power based on voltage and current*/
datalayer.battery.status.active_power_W =
(datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100));
/* Calculate if battery or inverter is limiting factor*/

if (datalayer.battery.status.current_dA == 0) { //Battery idle
if (datalayer.battery.status.max_discharge_current_dA > 0) {
//We allow discharge, but inverter does nothing. Inverter is limiting
datalayer.battery.settings.inverter_limits_discharge = true;
} else {
datalayer.battery.settings.inverter_limits_discharge = false;
}
if (datalayer.battery.status.max_charge_current_dA > 0) {
//We allow charge, but inverter does nothing. Inverter is limiting
datalayer.battery.settings.inverter_limits_charge = true;
} else {
datalayer.battery.settings.inverter_limits_charge = false;
}
} else if (datalayer.battery.status.current_dA < 0) { //Battery discharging
if (-datalayer.battery.status.current_dA < datalayer.battery.status.max_discharge_current_dA) {
datalayer.battery.settings.inverter_limits_discharge = true;
} else {
datalayer.battery.settings.inverter_limits_discharge = false;
}
} else { // > 0 Battery charging
//If actual current is smaller than max we allow, inverter is limiting factor
if (datalayer.battery.status.current_dA < datalayer.battery.status.max_charge_current_dA) {
datalayer.battery.settings.inverter_limits_charge = true;
} else {
datalayer.battery.settings.inverter_limits_charge = false;
}
}

#ifdef DOUBLE_BATTERY
/* Calculate active power based on voltage and current for battery 2*/
Expand Down
12 changes: 9 additions & 3 deletions Software/USER_SETTINGS.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
//#define MEB_BATTERY
//#define MG_5_BATTERY
//#define NISSAN_LEAF_BATTERY
//#define ORION_BMS
//#define PYLON_BATTERY
//#define DALY_BMS
//#define RJXZS_BMS
//#define RANGE_ROVER_PHEV_BATTERY
//#define RENAULT_KANGOO_BATTERY
Expand All @@ -39,15 +41,17 @@
//#define TESLA_MODEL_3Y_BATTERY
//#define TESLA_MODEL_SX_BATTERY
//#define VOLVO_SPA_BATTERY
//#define VOLVO_SPA_HYBRID_BATTERY
//#define TEST_FAKE_BATTERY
//#define DOUBLE_BATTERY //Enable this line if you use two identical batteries at the same time (requires CAN_ADDON setup)
//#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery)

/* Select inverter communication protocol. See Wiki for which to use with your inverter: /~https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki */
//#define AFORE_CAN //Enable this line to emulate an "Afore battery" over CAN bus
//#define BYD_CAN //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus
//#define BYD_CAN //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus
//#define BYD_KOSTAL_RS485 //Enable this line to emulate a "BYD 11kWh HVM battery" over Kostal RS485
//#define BYD_MODBUS //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU
//#define FERROAMP_CAN //Enable this line to emulate a "Pylon 4x96V Force H2" over CAN Bus
//#define FOXESS_CAN //Enable this line to emulate a "HV2600/ECS4100 battery" over CAN bus
//#define GROWATT_HV_CAN //Enable this line to emulate a "Growatt High Voltage v1.10 battery" over CAN bus
//#define GROWATT_LV_CAN //Enable this line to emulate a "48V Growatt Low Voltage battery" over CAN bus
Expand Down Expand Up @@ -92,8 +96,8 @@
//#define EQUIPMENT_STOP_BUTTON // Enable this to allow an equipment stop button connected to the Battery-Emulator to disengage the battery
//#define LFP_CHEMISTRY //Tesla specific setting, enable this line to startup in LFP mode
//#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting
//#define LOG_TO_SD //Enable this line to log diagnostic data to SD card
//#define LOG_CAN_TO_SD //Enable this line to log incoming/outgoing CAN & CAN-FD messages to SD card
//#define LOG_TO_SD //Enable this line to log diagnostic data to SD card (WARNING, raises CPU load, do not use for production)
//#define LOG_CAN_TO_SD //Enable this line to log incoming/outgoing CAN & CAN-FD messages to SD card (WARNING, raises CPU load, do not use for production)
//#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs (WARNING, raises CPU load, do not use for production)
//#define DEBUG_VIA_WEB //Enable this line to log diagnostic data while program runs, which can be viewed via webpage (WARNING, slightly raises CPU load, do not use for production)
//#define DEBUG_CAN_DATA //Enable this line to print incoming/outgoing CAN & CAN-FD messages to USB serial (WARNING, raises CPU load, do not use for production)
Expand Down Expand Up @@ -140,6 +144,8 @@
#define BATTERY_MAXTEMPERATURE 500
// -250 = -25.0 °C , Min temperature (Will produce a battery frozen event if below)
#define BATTERY_MINTEMPERATURE -250
// 150 = 15.0 °C , Max difference between min and max temperature (Will produce a battery temperature deviation event if greater)
#define BATTERY_MAX_TEMPERATURE_DEVIATION 150
// 300 = 30.0A , Max charge in Amp (Some inverters needs to be limited)
#define BATTERY_MAX_CHARGE_AMP 300
// 300 = 30.0A , Max discharge in Amp (Some inverters needs to be limited)
Expand Down
22 changes: 20 additions & 2 deletions Software/src/battery/BATTERIES.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ void setup_can_shunt();
#include "FOXESS-BATTERY.h"
#endif

#ifdef ORION_BMS
#include "ORION-BMS.h"
#endif

#ifdef SONO_BATTERY
#include "SONO-BATTERY.h"
#endif
Expand Down Expand Up @@ -86,6 +90,10 @@ void setup_can_shunt();
#include "PYLON-BATTERY.h"
#endif

#ifdef DALY_BMS
#include "DALY-BMS.h"
#endif

#ifdef RJXZS_BMS
#include "RJXZS-BMS.h"
#endif
Expand Down Expand Up @@ -131,14 +139,24 @@ void setup_can_shunt();
#include "VOLVO-SPA-BATTERY.h"
#endif

#ifdef VOLVO_SPA_HYBRID_BATTERY
#include "VOLVO-SPA-HYBRID-BATTERY.h"
#endif

#ifdef SERIAL_LINK_RECEIVER
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h"
#endif

void handle_incoming_can_frame_battery(CAN_frame rx_frame);
void setup_battery(void);
void update_values_battery();

#ifdef RS485_BATTERY_SELECTED
void transmit_rs485();
void receive_RS485();
#else
void handle_incoming_can_frame_battery(CAN_frame rx_frame);
void transmit_can_battery();
void setup_battery(void);
#endif

#ifdef DOUBLE_BATTERY
void update_values_battery2();
Expand Down
3 changes: 3 additions & 0 deletions Software/src/battery/BMW-IX-BATTERY.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,9 @@ void setup_battery(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "BMW iX and i4-7 platform", 63);
datalayer.system.info.battery_protocol[63] = '\0';

//Reset Battery at bootup
transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery);

//Before we have started up and detected which battery is in use, use 108S values
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
Expand Down
2 changes: 1 addition & 1 deletion Software/src/battery/BMW-IX-BATTERY.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500
#define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
#define STALE_PERIOD_CONFIG \
300000; //Number of milliseconds before critical values are classed as stale/stuck 300000 = 300 seconds
400000; //Number of milliseconds before critical values are classed as stale/stuck 300000 = 400 seconds
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);

Expand Down
2 changes: 1 addition & 1 deletion Software/src/battery/BOLT-AMPERA-BATTERY.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#define MAX_PACK_VOLTAGE_DV 4150 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 2500
#define MAX_CELL_DEVIATION_MV 500
#define MAX_CELL_DEVIATION_MV 150
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value

Expand Down
Loading

0 comments on commit 10b85f3

Please sign in to comment.