mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 10:49:42 +02:00
Merge branch 'main' into feature/kostal-rs485
This commit is contained in:
commit
5a0f9ee8e6
15 changed files with 669 additions and 259 deletions
1
.github/workflows/compile-all-batteries.yml
vendored
1
.github/workflows/compile-all-batteries.yml
vendored
|
@ -53,7 +53,6 @@ jobs:
|
|||
inverter:
|
||||
- BYD_CAN
|
||||
# - BYD_MODBUS
|
||||
# - LUNA2000_MODBUS
|
||||
# - PYLON_CAN
|
||||
# - SMA_CAN
|
||||
# - SMA_TRIPOWER_CAN
|
||||
|
|
|
@ -55,7 +55,6 @@ jobs:
|
|||
- BYD_CAN
|
||||
- BYD_SMA
|
||||
- BYD_MODBUS
|
||||
- LUNA2000_MODBUS
|
||||
- PYLON_CAN
|
||||
- SMA_CAN
|
||||
- SMA_TRIPOWER_CAN
|
||||
|
|
1
.github/workflows/compile-all-inverters.yml
vendored
1
.github/workflows/compile-all-inverters.yml
vendored
|
@ -45,7 +45,6 @@ jobs:
|
|||
- BYD_CAN
|
||||
- BYD_SMA
|
||||
- BYD_MODBUS
|
||||
- LUNA2000_MODBUS
|
||||
- PYLON_CAN
|
||||
- SMA_CAN
|
||||
- SMA_TRIPOWER_CAN
|
||||
|
|
|
@ -26,13 +26,19 @@
|
|||
#include "src/datalayer/datalayer.h"
|
||||
|
||||
#ifdef WEBSERVER
|
||||
#include <ESPmDNS.h>
|
||||
#include "src/devboard/webserver/webserver.h"
|
||||
#endif
|
||||
#ifdef MDNSRESPONDER
|
||||
#include <ESPmDNS.h>
|
||||
#endif // MDNSRESONDER
|
||||
#else // WEBSERVER
|
||||
#ifdef MDNSRESPONDER
|
||||
#error WEBSERVER needs to be enabled for MDNSRESPONDER!
|
||||
#endif // MDNSRSPONDER
|
||||
#endif // WEBSERVER
|
||||
|
||||
Preferences settings; // Store user settings
|
||||
// The current software version, shown on webserver
|
||||
const char* version_number = "7.1.dev";
|
||||
const char* version_number = "7.2.dev";
|
||||
|
||||
// Interval settings
|
||||
uint16_t intervalUpdateValues = INTERVAL_5_S; // Interval at which to update inverter values / Modbus registers
|
||||
|
@ -167,7 +173,9 @@ void loop() {
|
|||
void connectivity_loop(void* task_time_us) {
|
||||
// Init
|
||||
init_webserver();
|
||||
#ifdef MDNSRESPONDER
|
||||
init_mDNS();
|
||||
#endif
|
||||
#ifdef MQTT
|
||||
init_mqtt();
|
||||
#endif
|
||||
|
@ -287,7 +295,7 @@ void core_loop(void* task_time_us) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef WEBSERVER
|
||||
#ifdef MDNSRESPONDER
|
||||
// Initialise mDNS
|
||||
void init_mDNS() {
|
||||
|
||||
|
@ -306,7 +314,7 @@ void init_mDNS() {
|
|||
MDNS.addService("battery_emulator", "tcp", 80);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // MDNSRESPONDER
|
||||
|
||||
// Initialization functions
|
||||
void init_serial() {
|
||||
|
|
|
@ -22,15 +22,15 @@ volatile CAN_Configuration can_config = {
|
|||
volatile uint8_t AccessPointEnabled = true; //Set to either true/false to enable direct wifi access point
|
||||
std::string ssid = "REPLACE_WITH_YOUR_SSID"; // Maximum of 63 characters;
|
||||
std::string password = "REPLACE_WITH_YOUR_PASSWORD"; // Minimum of 8 characters;
|
||||
const char* ssidAP = "Battery Emulator"; // Maximum of 63 characters;
|
||||
const char* ssidAP = "Battery Emulator"; // Maximum of 63 characters, also used for device name on web interface
|
||||
const char* passwordAP = "123456789"; // Minimum of 8 characters; set to NULL if you want the access point to be open
|
||||
const uint8_t wifi_channel = 0; // Set to 0 for automatic channel selection
|
||||
// MQTT
|
||||
#ifdef MQTT
|
||||
const char* mqtt_user = "REDACTED";
|
||||
const char* mqtt_password = "REDACTED";
|
||||
#endif // USE_MQTT
|
||||
#endif // WEBSERVER
|
||||
const char* mqtt_user = "REDACTED"; // Set NULL for no username
|
||||
const char* mqtt_password = "REDACTED"; // Set NULL for no password
|
||||
#endif // USE_MQTT
|
||||
#endif // WEBSERVER
|
||||
|
||||
/* Charger settings (Optional, when using generator charging) */
|
||||
volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
//#define KIA_E_GMP_BATTERY
|
||||
//#define KIA_HYUNDAI_HYBRID_BATTERY
|
||||
//#define MG_5_BATTERY
|
||||
//#define NISSAN_LEAF_BATTERY
|
||||
#define NISSAN_LEAF_BATTERY
|
||||
//#define PYLON_BATTERY
|
||||
//#define RENAULT_KANGOO_BATTERY
|
||||
//#define RENAULT_ZOE_GEN1_BATTERY
|
||||
|
@ -32,8 +32,7 @@
|
|||
//#define BYD_CAN //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus
|
||||
//#define BYD_SMA //Enable this line to emulate a SMA compatible "BYD Battery-Box HVS 10.2KW battery" over CAN bus
|
||||
//#define BYD_MODBUS //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU
|
||||
//#define BYD_KOSTAL_RS485 //Enable this line to emulate a "BYD 11kWh HVM battery" over Kostal RS485
|
||||
//#define LUNA2000_MODBUS //Enable this line to emulate a "Luna2000 battery" over Modbus RTU
|
||||
#define BYD_KOSTAL_RS485 //Enable this line to emulate a "BYD 11kWh HVM battery" over Kostal RS485
|
||||
//#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_TRIPOWER_CAN //Enable this line to emulate a "SMA Home Storage battery" over CAN bus
|
||||
|
@ -56,6 +55,8 @@
|
|||
//#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 WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.
|
||||
#define WIFIAP //Disable this line to permanently disable WIFI AP mode (make sure to hardcode ssid and password of you home wifi network). When enabled WIFI AP can still be disabled by a setting in the future.
|
||||
#define MDNSRESPONDER //Enable this line to enable MDNS, allows battery monitor te be found by .local address. Requires WEBSERVER to be enabled.
|
||||
#define LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot (overrides Wifi/battery settings set below)
|
||||
//#define FUNCTION_TIME_MEASUREMENT // Enable this to record execution times and present them in the web UI (WARNING, raises CPU load, do not use for production)
|
||||
|
||||
|
@ -64,6 +65,9 @@
|
|||
#define MQTT_SERVER "192.168.xxx.yyy"
|
||||
#define MQTT_PORT 1883
|
||||
|
||||
/* Home Assistant options */
|
||||
#define HA_AUTODISCOVERY // Enable this line to send Home Assistant autodiscovery messages. If not enabled manual configuration of Home Assitant is required
|
||||
|
||||
/* Event options*/
|
||||
#define DUMMY_EVENT_ENABLED false //Enable this line to have a dummy event that gets logged to test the interface
|
||||
|
||||
|
|
|
@ -6,11 +6,35 @@
|
|||
#include "KIA-E-GMP-BATTERY.h"
|
||||
|
||||
/* Do not change code below unless you are sure what you are doing */
|
||||
static unsigned long previousMillis20ms = 0; // will store last time a 20ms CAN Message was send
|
||||
static unsigned long previousMillis30ms = 0; // will store last time a 30ms CAN Message was send
|
||||
static unsigned long previousMillis100ms = 0; // will store last time a 100ms CAN Message was send
|
||||
static unsigned long previousMillis200ms = 0; // will store last time a 200ms CAN Message was send
|
||||
static unsigned long previousMillis500ms = 0; // will store last time a 500ms CAN Message was send
|
||||
static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send
|
||||
static unsigned long previousMillis2s = 0; // will store last time a 2s CAN Message was send
|
||||
|
||||
#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
|
||||
|
||||
const unsigned char crc8_table[256] =
|
||||
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
|
||||
0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0,
|
||||
0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E, 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0,
|
||||
0xF3, 0xEE, 0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23,
|
||||
0x04, 0x19, 0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
|
||||
0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B,
|
||||
0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65, 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B,
|
||||
0x08, 0x15, 0x32, 0x2F, 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8,
|
||||
0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
|
||||
0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC,
|
||||
0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, 0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B,
|
||||
0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7, 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C,
|
||||
0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
|
||||
0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47,
|
||||
0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C, 0x97, 0x8A, 0xAD, 0xB0,
|
||||
0xE3, 0xFE, 0xD9, 0xC4};
|
||||
|
||||
static uint16_t inverterVoltageFrameHigh = 0;
|
||||
static uint16_t inverterVoltage = 0;
|
||||
static uint16_t soc_calculated = 0;
|
||||
|
@ -19,7 +43,7 @@ static uint16_t SOC_Display = 0;
|
|||
static uint16_t batterySOH = 1000;
|
||||
static uint16_t CellVoltMax_mV = 3700;
|
||||
static uint16_t CellVoltMin_mV = 3700;
|
||||
static uint16_t batteryVoltage = 0;
|
||||
static uint16_t batteryVoltage = 6700;
|
||||
static int16_t leadAcidBatteryVoltage = 120;
|
||||
static int16_t batteryAmps = 0;
|
||||
static int16_t powerWatt = 0;
|
||||
|
@ -41,6 +65,210 @@ static int8_t temperature_water_inlet = 0;
|
|||
static int8_t powerRelayTemperature = 0;
|
||||
static int8_t heatertemp = 0;
|
||||
|
||||
static uint8_t ticks_200ms_counter = 0;
|
||||
static uint8_t EGMP_1CF_counter = 0;
|
||||
static uint8_t EGMP_3XF_counter = 0;
|
||||
|
||||
CAN_frame EGMP_1CF = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x1CF,
|
||||
.data = {0x56, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_3AA = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3AA,
|
||||
.data = {0xFF, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_3E0 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3E0,
|
||||
.data = {0xC3, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_3E1 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3E1,
|
||||
.data = {0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_36F = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x36F,
|
||||
.data = {0x28, 0x31, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_37F = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x37F,
|
||||
.data = {0x9B, 0x30, 0x52, 0x24, 0x41, 0x02, 0x00, 0x00}};
|
||||
CAN_frame EGMP_4B4 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4B4,
|
||||
.data = {0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_4B5 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4B5,
|
||||
.data = {0x08, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_4B7 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4B7,
|
||||
.data = {0x08, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_4CC = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4CC,
|
||||
.data = {0x08, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_4CE = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4CE,
|
||||
.data = {0x16, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
|
||||
CAN_frame EGMP_4D8 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4D8,
|
||||
.data = {0x40, 0x10, 0xF0, 0xF0, 0x40, 0xF2, 0x1E, 0xCC}};
|
||||
CAN_frame EGMP_4DD = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4DD,
|
||||
.data = {0x3F, 0xFC, 0xFF, 0x00, 0x38, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_4E7 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4E7,
|
||||
.data = {0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00}};
|
||||
CAN_frame EGMP_4E9 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4E9,
|
||||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_4EA = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4EA,
|
||||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_4EB = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4EB,
|
||||
.data = {0x01, 0x50, 0x0B, 0x26, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_4EC = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4EC,
|
||||
.data = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F}};
|
||||
CAN_frame EGMP_4ED = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4ED,
|
||||
.data = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F}};
|
||||
CAN_frame EGMP_4EE = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4EE,
|
||||
.data = {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_4EF = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4EF,
|
||||
.data = {0x2B, 0xFE, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_405 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x405,
|
||||
.data = {0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_410 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x410,
|
||||
.data = {0xA6, 0x10, 0xFF, 0x3C, 0xFF, 0x7F, 0xFF, 0xFF}};
|
||||
CAN_frame EGMP_411 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x411,
|
||||
.data = {0xEA, 0x22, 0x50, 0x51, 0x00, 0x00, 0x00, 0x40}};
|
||||
CAN_frame EGMP_412 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x412,
|
||||
.data = {0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_413 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x413,
|
||||
.data = {0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_414 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x414,
|
||||
.data = {0xF0, 0x10, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_416 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x416,
|
||||
.data = {0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_417 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x417,
|
||||
.data = {0xC7, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_418 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x418,
|
||||
.data = {0x17, 0x20, 0x00, 0x00, 0x14, 0x0C, 0x00, 0x00}};
|
||||
CAN_frame EGMP_3C1 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3C1,
|
||||
.data = {0x59, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_3C2 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3C2,
|
||||
.data = {0x07, 0x00, 0x11, 0x40, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_4F0 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4F0,
|
||||
.data = {0x8A, 0x0A, 0x0D, 0x34, 0x60, 0x18, 0x12, 0xFC}};
|
||||
CAN_frame EGMP_4F2 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4F2,
|
||||
.data = {0x0A, 0xC3, 0xD5, 0xFF, 0x0F, 0x21, 0x80, 0x2B}};
|
||||
CAN_frame EGMP_4FE = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x4FE,
|
||||
.data = {0x69, 0x3F, 0x00, 0x04, 0xDF, 0x01, 0x4C, 0xA8}};
|
||||
CAN_frame EGMP_48F = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x48F,
|
||||
.data = {0xAD, 0x10, 0x41, 0x00, 0x05, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_419 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x419,
|
||||
.data = {0xC7, 0x90, 0xB9, 0xD2, 0x0D, 0x62, 0x7A, 0x00}};
|
||||
CAN_frame EGMP_422 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x422,
|
||||
.data = {0x15, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_444 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x444,
|
||||
.data = {0x96, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame EGMP_641 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x641,
|
||||
.data = {0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF}};
|
||||
CAN_frame EGMP_7E4 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
|
@ -53,14 +281,22 @@ CAN_frame EGMP_7E4_ack = {
|
|||
.ID = 0x7E4,
|
||||
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned
|
||||
|
||||
void set_cell_voltages(CANFDMessage frame, int start, int length, int startCell) {
|
||||
void set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if ((frame.data[start + i] * 20) > 1000) {
|
||||
datalayer.battery.status.cell_voltages_mV[startCell + i] = (frame.data[start + i] * 20);
|
||||
if ((rx_frame.data.u8[start + i] * 20) > 1000) {
|
||||
datalayer.battery.status.cell_voltages_mV[startCell + i] = (rx_frame.data.u8[start + i] * 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value) {
|
||||
uint8_t crc = initial_value;
|
||||
for (uint8_t j = 1; j < length; j++) { //start at 1, since 0 is the CRC
|
||||
crc = crc8_table[(crc ^ static_cast<uint8_t>(rx_frame.data.u8[j])) % 256];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||
|
||||
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
|
||||
|
@ -180,170 +416,220 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
#endif
|
||||
}
|
||||
|
||||
void receive_canfd_battery(CANFDMessage frame) {
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
switch (frame.id) {
|
||||
void receive_can_battery(CAN_frame rx_frame) {
|
||||
switch (rx_frame.ID) {
|
||||
case 0x055:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x150:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x1F5:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x215:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x21A:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x235:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x245:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x25A:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x275:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x2FA:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x325:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x330:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x335:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x360:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x365:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x3BA:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x3F5:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x7EC:
|
||||
// print_canfd_frame(frame);
|
||||
switch (frame.data[0]) {
|
||||
switch (rx_frame.data.u8[0]) {
|
||||
case 0x10: //"PID Header"
|
||||
// Serial.println ("Send ack");
|
||||
poll_data_pid = frame.data[4];
|
||||
// if (frame.data[4] == poll_data_pid) {
|
||||
poll_data_pid = rx_frame.data.u8[4];
|
||||
// if (rx_frame.data.u8[4] == poll_data_pid) {
|
||||
transmit_can(&EGMP_7E4_ack, can_config.battery); //Send ack to BMS if the same frame is sent as polled
|
||||
// }
|
||||
break;
|
||||
case 0x21: //First frame in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
allowedChargePower = ((frame.data[3] << 8) + frame.data[4]);
|
||||
allowedDischargePower = ((frame.data[5] << 8) + frame.data[6]);
|
||||
SOC_BMS = frame.data[2] * 5; //100% = 200 ( 200 * 5 = 1000 )
|
||||
allowedChargePower = ((rx_frame.data.u8[3] << 8) + rx_frame.data.u8[4]);
|
||||
allowedDischargePower = ((rx_frame.data.u8[5] << 8) + rx_frame.data.u8[6]);
|
||||
SOC_BMS = rx_frame.data.u8[2] * 5; //100% = 200 ( 200 * 5 = 1000 )
|
||||
|
||||
} else if (poll_data_pid == 2) {
|
||||
// set cell voltages data, start bite, data length from start, start cell
|
||||
set_cell_voltages(frame, 2, 6, 0);
|
||||
set_cell_voltages(rx_frame, 2, 6, 0);
|
||||
} else if (poll_data_pid == 3) {
|
||||
set_cell_voltages(frame, 2, 6, 32);
|
||||
set_cell_voltages(rx_frame, 2, 6, 32);
|
||||
} else if (poll_data_pid == 4) {
|
||||
set_cell_voltages(frame, 2, 6, 64);
|
||||
set_cell_voltages(rx_frame, 2, 6, 64);
|
||||
} else if (poll_data_pid == 0x0A) {
|
||||
set_cell_voltages(frame, 2, 6, 96);
|
||||
set_cell_voltages(rx_frame, 2, 6, 96);
|
||||
} else if (poll_data_pid == 0x0B) {
|
||||
set_cell_voltages(frame, 2, 6, 128);
|
||||
set_cell_voltages(rx_frame, 2, 6, 128);
|
||||
} else if (poll_data_pid == 0x0C) {
|
||||
set_cell_voltages(frame, 2, 6, 160);
|
||||
set_cell_voltages(rx_frame, 2, 6, 160);
|
||||
}
|
||||
break;
|
||||
case 0x22: //Second datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
batteryVoltage = (frame.data[3] << 8) + frame.data[4];
|
||||
batteryAmps = (frame.data[1] << 8) + frame.data[2];
|
||||
temperatureMax = frame.data[5];
|
||||
temperatureMin = frame.data[6];
|
||||
// temp1 = frame.data[7];
|
||||
batteryVoltage = (rx_frame.data.u8[3] << 8) + rx_frame.data.u8[4];
|
||||
batteryAmps = (rx_frame.data.u8[1] << 8) + rx_frame.data.u8[2];
|
||||
temperatureMax = rx_frame.data.u8[5];
|
||||
temperatureMin = rx_frame.data.u8[6];
|
||||
// temp1 = rx_frame.data.u8[7];
|
||||
} else if (poll_data_pid == 2) {
|
||||
set_cell_voltages(frame, 1, 7, 6);
|
||||
set_cell_voltages(rx_frame, 1, 7, 6);
|
||||
} else if (poll_data_pid == 3) {
|
||||
set_cell_voltages(frame, 1, 7, 38);
|
||||
set_cell_voltages(rx_frame, 1, 7, 38);
|
||||
} else if (poll_data_pid == 4) {
|
||||
set_cell_voltages(frame, 1, 7, 70);
|
||||
set_cell_voltages(rx_frame, 1, 7, 70);
|
||||
} else if (poll_data_pid == 0x0A) {
|
||||
set_cell_voltages(frame, 1, 7, 102);
|
||||
set_cell_voltages(rx_frame, 1, 7, 102);
|
||||
} else if (poll_data_pid == 0x0B) {
|
||||
set_cell_voltages(frame, 1, 7, 134);
|
||||
set_cell_voltages(rx_frame, 1, 7, 134);
|
||||
} else if (poll_data_pid == 0x0C) {
|
||||
set_cell_voltages(frame, 1, 7, 166);
|
||||
set_cell_voltages(rx_frame, 1, 7, 166);
|
||||
} else if (poll_data_pid == 6) {
|
||||
batteryManagementMode = frame.data[5];
|
||||
batteryManagementMode = rx_frame.data.u8[5];
|
||||
}
|
||||
break;
|
||||
case 0x23: //Third datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
temperature_water_inlet = frame.data[6];
|
||||
CellVoltMax_mV = (frame.data[7] * 20); //(volts *50) *20 =mV
|
||||
// temp2 = frame.data[1];
|
||||
// temp3 = frame.data[2];
|
||||
// temp4 = frame.data[3];
|
||||
temperature_water_inlet = rx_frame.data.u8[6];
|
||||
CellVoltMax_mV = (rx_frame.data.u8[7] * 20); //(volts *50) *20 =mV
|
||||
// temp2 = rx_frame.data.u8[1];
|
||||
// temp3 = rx_frame.data.u8[2];
|
||||
// temp4 = rx_frame.data.u8[3];
|
||||
} else if (poll_data_pid == 2) {
|
||||
set_cell_voltages(frame, 1, 7, 13);
|
||||
set_cell_voltages(rx_frame, 1, 7, 13);
|
||||
} else if (poll_data_pid == 3) {
|
||||
set_cell_voltages(frame, 1, 7, 45);
|
||||
set_cell_voltages(rx_frame, 1, 7, 45);
|
||||
} else if (poll_data_pid == 4) {
|
||||
set_cell_voltages(frame, 1, 7, 77);
|
||||
set_cell_voltages(rx_frame, 1, 7, 77);
|
||||
} else if (poll_data_pid == 0x0A) {
|
||||
set_cell_voltages(frame, 1, 7, 109);
|
||||
set_cell_voltages(rx_frame, 1, 7, 109);
|
||||
} else if (poll_data_pid == 0x0B) {
|
||||
set_cell_voltages(frame, 1, 7, 141);
|
||||
set_cell_voltages(rx_frame, 1, 7, 141);
|
||||
} else if (poll_data_pid == 0x0C) {
|
||||
set_cell_voltages(frame, 1, 7, 173);
|
||||
set_cell_voltages(rx_frame, 1, 7, 173);
|
||||
} else if (poll_data_pid == 5) {
|
||||
// ac = frame.data[3];
|
||||
// Vdiff = frame.data[4];
|
||||
// ac = rx_frame.data.u8[3];
|
||||
// Vdiff = rx_frame.data.u8[4];
|
||||
|
||||
// airbag = frame.data[6];
|
||||
heatertemp = frame.data[7];
|
||||
// airbag = rx_frame.data.u8[6];
|
||||
heatertemp = rx_frame.data.u8[7];
|
||||
}
|
||||
break;
|
||||
case 0x24: //Fourth datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
CellVmaxNo = frame.data[1];
|
||||
CellVoltMin_mV = (frame.data[2] * 20); //(volts *50) *20 =mV
|
||||
CellVminNo = frame.data[3];
|
||||
// fanMod = frame.data[4];
|
||||
// fanSpeed = frame.data[5];
|
||||
leadAcidBatteryVoltage = frame.data[6]; //12v Battery Volts
|
||||
//cumulative_charge_current[0] = frame.data[7];
|
||||
CellVmaxNo = rx_frame.data.u8[1];
|
||||
CellVoltMin_mV = (rx_frame.data.u8[2] * 20); //(volts *50) *20 =mV
|
||||
CellVminNo = rx_frame.data.u8[3];
|
||||
// fanMod = rx_frame.data.u8[4];
|
||||
// fanSpeed = rx_frame.data.u8[5];
|
||||
leadAcidBatteryVoltage = rx_frame.data.u8[6]; //12v Battery Volts
|
||||
//cumulative_charge_current[0] = rx_frame.data.u8[7];
|
||||
} else if (poll_data_pid == 2) {
|
||||
set_cell_voltages(frame, 1, 7, 20);
|
||||
set_cell_voltages(rx_frame, 1, 7, 20);
|
||||
} else if (poll_data_pid == 3) {
|
||||
set_cell_voltages(frame, 1, 7, 52);
|
||||
set_cell_voltages(rx_frame, 1, 7, 52);
|
||||
} else if (poll_data_pid == 4) {
|
||||
set_cell_voltages(frame, 1, 7, 84);
|
||||
set_cell_voltages(rx_frame, 1, 7, 84);
|
||||
} else if (poll_data_pid == 0x0A) {
|
||||
set_cell_voltages(frame, 1, 7, 116);
|
||||
set_cell_voltages(rx_frame, 1, 7, 116);
|
||||
} else if (poll_data_pid == 0x0B) {
|
||||
set_cell_voltages(frame, 1, 7, 148);
|
||||
set_cell_voltages(rx_frame, 1, 7, 148);
|
||||
} else if (poll_data_pid == 0x0C) {
|
||||
set_cell_voltages(frame, 1, 7, 180);
|
||||
set_cell_voltages(rx_frame, 1, 7, 180);
|
||||
} else if (poll_data_pid == 5) {
|
||||
batterySOH = ((frame.data[2] << 8) + frame.data[3]);
|
||||
// maxDetCell = frame.data[4];
|
||||
// minDet = (frame.data[5] << 8) + frame.data[6];
|
||||
// minDetCell = frame.data[7];
|
||||
batterySOH = ((rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3]);
|
||||
// maxDetCell = rx_frame.data.u8[4];
|
||||
// minDet = (rx_frame.data.u8[5] << 8) + rx_frame.data.u8[6];
|
||||
// minDetCell = rx_frame.data.u8[7];
|
||||
}
|
||||
break;
|
||||
case 0x25: //Fifth datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
//cumulative_charge_current[1] = frame.data[1];
|
||||
//cumulative_charge_current[2] = frame.data[2];
|
||||
//cumulative_charge_current[3] = frame.data[3];
|
||||
//cumulative_discharge_current[0] = frame.data[4];
|
||||
//cumulative_discharge_current[1] = frame.data[5];
|
||||
//cumulative_discharge_current[2] = frame.data[6];
|
||||
//cumulative_discharge_current[3] = frame.data[7];
|
||||
//cumulative_charge_current[1] = rx_frame.data.u8[1];
|
||||
//cumulative_charge_current[2] = rx_frame.data.u8[2];
|
||||
//cumulative_charge_current[3] = rx_frame.data.u8[3];
|
||||
//cumulative_discharge_current[0] = rx_frame.data.u8[4];
|
||||
//cumulative_discharge_current[1] = rx_frame.data.u8[5];
|
||||
//cumulative_discharge_current[2] = rx_frame.data.u8[6];
|
||||
//cumulative_discharge_current[3] = rx_frame.data.u8[7];
|
||||
//set_cumulative_charge_current();
|
||||
//set_cumulative_discharge_current();
|
||||
} else if (poll_data_pid == 2) {
|
||||
set_cell_voltages(frame, 1, 5, 27);
|
||||
set_cell_voltages(rx_frame, 1, 5, 27);
|
||||
} else if (poll_data_pid == 3) {
|
||||
set_cell_voltages(frame, 1, 5, 59);
|
||||
set_cell_voltages(rx_frame, 1, 5, 59);
|
||||
} else if (poll_data_pid == 4) {
|
||||
set_cell_voltages(frame, 1, 5, 91);
|
||||
set_cell_voltages(rx_frame, 1, 5, 91);
|
||||
} else if (poll_data_pid == 0x0A) {
|
||||
set_cell_voltages(frame, 1, 5, 123);
|
||||
set_cell_voltages(rx_frame, 1, 5, 123);
|
||||
} else if (poll_data_pid == 0x0B) {
|
||||
set_cell_voltages(frame, 1, 5, 155);
|
||||
set_cell_voltages(rx_frame, 1, 5, 155);
|
||||
} else if (poll_data_pid == 0x0C) {
|
||||
set_cell_voltages(frame, 1, 5, 187);
|
||||
set_cell_voltages(rx_frame, 1, 5, 187);
|
||||
//set_cell_count();
|
||||
} else if (poll_data_pid == 5) {
|
||||
// datalayer.battery.info.number_of_cells = 98;
|
||||
SOC_Display = frame.data[1] * 5;
|
||||
SOC_Display = rx_frame.data.u8[1] * 5;
|
||||
}
|
||||
break;
|
||||
case 0x26: //Sixth datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
//cumulative_energy_charged[0] = frame.data[1];
|
||||
// cumulative_energy_charged[1] = frame.data[2];
|
||||
//cumulative_energy_charged[2] = frame.data[3];
|
||||
//cumulative_energy_charged[3] = frame.data[4];
|
||||
//cumulative_energy_discharged[0] = frame.data[5];
|
||||
//cumulative_energy_discharged[1] = frame.data[6];
|
||||
//cumulative_energy_discharged[2] = frame.data[7];
|
||||
//cumulative_energy_charged[0] = rx_frame.data.u8[1];
|
||||
// cumulative_energy_charged[1] = rx_frame.data.u8[2];
|
||||
//cumulative_energy_charged[2] = rx_frame.data.u8[3];
|
||||
//cumulative_energy_charged[3] = rx_frame.data.u8[4];
|
||||
//cumulative_energy_discharged[0] = rx_frame.data.u8[5];
|
||||
//cumulative_energy_discharged[1] = rx_frame.data.u8[6];
|
||||
//cumulative_energy_discharged[2] = rx_frame.data.u8[7];
|
||||
// set_cumulative_energy_charged();
|
||||
}
|
||||
break;
|
||||
case 0x27: //Seventh datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
//cumulative_energy_discharged[3] = frame.data[1];
|
||||
//cumulative_energy_discharged[3] = rx_frame.data.u8[1];
|
||||
|
||||
//opTimeBytes[0] = frame.data[2];
|
||||
//opTimeBytes[1] = frame.data[3];
|
||||
//opTimeBytes[2] = frame.data[4];
|
||||
//opTimeBytes[3] = frame.data[5];
|
||||
//opTimeBytes[0] = rx_frame.data.u8[2];
|
||||
//opTimeBytes[1] = rx_frame.data.u8[3];
|
||||
//opTimeBytes[2] = rx_frame.data.u8[4];
|
||||
//opTimeBytes[3] = rx_frame.data.u8[5];
|
||||
|
||||
BMS_ign = frame.data[6];
|
||||
inverterVoltageFrameHigh = frame.data[7]; // BMS Capacitoir
|
||||
BMS_ign = rx_frame.data.u8[6];
|
||||
inverterVoltageFrameHigh = rx_frame.data.u8[7]; // BMS Capacitoir
|
||||
|
||||
// set_cumulative_energy_discharged();
|
||||
// set_opTime();
|
||||
|
@ -351,7 +637,7 @@ void receive_canfd_battery(CANFDMessage frame) {
|
|||
break;
|
||||
case 0x28: //Eighth datarow in PID group
|
||||
if (poll_data_pid == 1) {
|
||||
inverterVoltage = (inverterVoltageFrameHigh << 8) + frame.data[1]; // BMS Capacitoir
|
||||
inverterVoltage = (inverterVoltageFrameHigh << 8) + rx_frame.data.u8[1]; // BMS Capacitoir
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -361,11 +647,178 @@ void receive_canfd_battery(CANFDMessage frame) {
|
|||
}
|
||||
}
|
||||
|
||||
void receive_can_battery(CAN_frame frame) {} // Not used on CAN-FD battery, just included to compile
|
||||
|
||||
void send_can_battery() {
|
||||
|
||||
unsigned long currentMillis = millis();
|
||||
|
||||
//Send 20ms CANFD message
|
||||
if (currentMillis - previousMillis20ms >= INTERVAL_20_MS) {
|
||||
previousMillis20ms = currentMillis;
|
||||
|
||||
EGMP_1CF.data.u8[1] = (EGMP_1CF_counter % 15) * 0x10;
|
||||
EGMP_1CF_counter++;
|
||||
if (EGMP_1CF_counter > 0xE) {
|
||||
EGMP_1CF_counter = 0;
|
||||
}
|
||||
EGMP_1CF.data.u8[0] = calculateCRC(EGMP_1CF, EGMP_1CF.DLC, 0x0A); // Set CRC bit, initial Value 0x0A
|
||||
|
||||
transmit_can(&EGMP_1CF, can_config.battery);
|
||||
}
|
||||
|
||||
//Send 30ms CANFD message
|
||||
if (currentMillis - previousMillis30ms >= INTERVAL_30_MS) {
|
||||
previousMillis30ms = currentMillis;
|
||||
|
||||
transmit_can(&EGMP_419, can_config.battery); // TODO: Handle variations better
|
||||
}
|
||||
|
||||
//Send 100ms CANFD message
|
||||
if (currentMillis - previousMillis100ms >= INTERVAL_100_MS) {
|
||||
previousMillis100ms = currentMillis;
|
||||
|
||||
EGMP_36F.data.u8[1] = ((EGMP_3XF_counter % 15) << 4) + 0x01;
|
||||
EGMP_37F.data.u8[1] = ((EGMP_3XF_counter % 15) << 4);
|
||||
EGMP_3XF_counter++;
|
||||
if (EGMP_3XF_counter > 0xE) {
|
||||
EGMP_3XF_counter = 0;
|
||||
}
|
||||
EGMP_36F.data.u8[0] = calculateCRC(EGMP_36F, EGMP_36F.DLC, 0x8A); // Set CRC bit, initial Value 0x8A
|
||||
EGMP_37F.data.u8[0] = calculateCRC(EGMP_37F, EGMP_37F.DLC, 0x38); // Set CRC bit, initial Value 0x38
|
||||
|
||||
transmit_can(&EGMP_36F, can_config.battery);
|
||||
transmit_can(&EGMP_37F, can_config.battery);
|
||||
}
|
||||
|
||||
//Send 200ms CANFD message
|
||||
if (currentMillis - previousMillis200ms >= INTERVAL_200_MS) {
|
||||
previousMillis200ms = currentMillis;
|
||||
|
||||
transmit_can(&EGMP_4B4, can_config.battery);
|
||||
transmit_can(&EGMP_4B5, can_config.battery);
|
||||
transmit_can(&EGMP_4B7, can_config.battery);
|
||||
transmit_can(&EGMP_4CC, can_config.battery);
|
||||
transmit_can(&EGMP_4CE, can_config.battery);
|
||||
transmit_can(&EGMP_4D8, can_config.battery);
|
||||
transmit_can(&EGMP_4DD, can_config.battery);
|
||||
transmit_can(&EGMP_4E7, can_config.battery);
|
||||
transmit_can(&EGMP_4E9, can_config.battery);
|
||||
transmit_can(&EGMP_4EA, can_config.battery);
|
||||
transmit_can(&EGMP_4EB, can_config.battery);
|
||||
transmit_can(&EGMP_4EC, can_config.battery);
|
||||
transmit_can(&EGMP_4ED, can_config.battery);
|
||||
transmit_can(&EGMP_4EE, can_config.battery);
|
||||
transmit_can(&EGMP_4EF, can_config.battery);
|
||||
transmit_can(&EGMP_641, can_config.battery);
|
||||
transmit_can(&EGMP_3AA, can_config.battery);
|
||||
transmit_can(&EGMP_3E0, can_config.battery);
|
||||
transmit_can(&EGMP_3E1, can_config.battery);
|
||||
transmit_can(&EGMP_422, can_config.battery);
|
||||
transmit_can(&EGMP_444, can_config.battery);
|
||||
transmit_can(&EGMP_405, can_config.battery);
|
||||
transmit_can(&EGMP_410, can_config.battery);
|
||||
transmit_can(&EGMP_411, can_config.battery);
|
||||
transmit_can(&EGMP_412, can_config.battery);
|
||||
transmit_can(&EGMP_412, can_config.battery);
|
||||
transmit_can(&EGMP_413, can_config.battery);
|
||||
transmit_can(&EGMP_414, can_config.battery);
|
||||
transmit_can(&EGMP_416, can_config.battery);
|
||||
transmit_can(&EGMP_417, can_config.battery);
|
||||
transmit_can(&EGMP_418, can_config.battery);
|
||||
transmit_can(&EGMP_3C1, can_config.battery);
|
||||
transmit_can(&EGMP_3C2, can_config.battery);
|
||||
transmit_can(&EGMP_4F0, can_config.battery); //TODO: could be handled better
|
||||
transmit_can(&EGMP_4F2, can_config.battery); //TODO: could be handled better
|
||||
|
||||
if (ticks_200ms_counter < 254) {
|
||||
ticks_200ms_counter++;
|
||||
}
|
||||
if (ticks_200ms_counter > 11) {
|
||||
EGMP_412.data.u8[0] = 0x48;
|
||||
EGMP_412.data.u8[1] = 0x10;
|
||||
EGMP_412.data.u8[6] = 0x04;
|
||||
|
||||
EGMP_418.data.u8[0] = 0xCE;
|
||||
EGMP_418.data.u8[1] = 0x30;
|
||||
EGMP_418.data.u8[4] = 0x14;
|
||||
EGMP_418.data.u8[5] = 0x4C;
|
||||
if (ticks_200ms_counter > 39) {
|
||||
EGMP_412.data.u8[0] = 0xB3;
|
||||
EGMP_412.data.u8[1] = 0x20;
|
||||
EGMP_412.data.u8[6] = 0x00;
|
||||
|
||||
EGMP_418.data.u8[0] = 0xA6;
|
||||
EGMP_418.data.u8[1] = 0x40;
|
||||
EGMP_418.data.u8[5] = 0x0C;
|
||||
}
|
||||
}
|
||||
if (ticks_200ms_counter > 20) {
|
||||
EGMP_413.data.u8[0] = 0xF5;
|
||||
EGMP_413.data.u8[1] = 0x10;
|
||||
EGMP_413.data.u8[3] = 0x41;
|
||||
}
|
||||
if (ticks_200ms_counter > 28) {
|
||||
EGMP_4B4.data.u8[2] = 0;
|
||||
EGMP_4B4.data.u8[3] = 0;
|
||||
}
|
||||
if (ticks_200ms_counter > 26) {
|
||||
EGMP_411.data.u8[0] = 0x9E;
|
||||
EGMP_411.data.u8[1] = 0x32;
|
||||
EGMP_411.data.u8[7] = 0x50;
|
||||
|
||||
EGMP_417.data.u8[0] = 0x9E;
|
||||
EGMP_417.data.u8[1] = 0x20;
|
||||
EGMP_417.data.u8[4] = 0x04;
|
||||
EGMP_417.data.u8[5] = 0x01;
|
||||
}
|
||||
if (ticks_200ms_counter > 32) {
|
||||
EGMP_4CE.data.u8[0] = 0x22;
|
||||
EGMP_4CE.data.u8[1] = 0x41;
|
||||
EGMP_4CE.data.u8[6] = 0x47;
|
||||
EGMP_4CE.data.u8[7] = 0x1F;
|
||||
}
|
||||
if (ticks_200ms_counter > 43) {
|
||||
EGMP_4EB.data.u8[2] = 0x0D;
|
||||
EGMP_4EB.data.u8[3] = 0x3B;
|
||||
}
|
||||
if (ticks_200ms_counter > 46) {
|
||||
EGMP_4EB.data.u8[2] = 0x0E;
|
||||
EGMP_4EB.data.u8[3] = 0x00;
|
||||
}
|
||||
if (ticks_200ms_counter > 24) {
|
||||
EGMP_4ED.data.u8[1] = 0x00;
|
||||
EGMP_4ED.data.u8[2] = 0x00;
|
||||
EGMP_4ED.data.u8[3] = 0x00;
|
||||
EGMP_4ED.data.u8[4] = 0x00;
|
||||
}
|
||||
if (ticks_200ms_counter > 21) {
|
||||
EGMP_3E1.data.u8[0] = 0x49;
|
||||
EGMP_3E1.data.u8[1] = 0x10;
|
||||
EGMP_3E1.data.u8[2] = 0x12;
|
||||
EGMP_3E1.data.u8[3] = 0x15;
|
||||
|
||||
EGMP_422.data.u8[0] = 0xEE;
|
||||
EGMP_422.data.u8[1] = 0x20;
|
||||
EGMP_422.data.u8[2] = 0x11;
|
||||
EGMP_422.data.u8[6] = 0x04;
|
||||
|
||||
EGMP_405.data.u8[0] = 0xD2;
|
||||
EGMP_405.data.u8[1] = 0x10;
|
||||
EGMP_405.data.u8[5] = 0x01;
|
||||
}
|
||||
if (ticks_200ms_counter > 12) {
|
||||
EGMP_444.data.u8[0] = 0xEE;
|
||||
EGMP_444.data.u8[1] = 0x30;
|
||||
EGMP_444.data.u8[3] = 0x20;
|
||||
if (ticks_200ms_counter > 23) { // TODO: Could be handled better
|
||||
EGMP_444.data.u8[0] = 0xE4;
|
||||
EGMP_444.data.u8[1] = 0x60;
|
||||
EGMP_444.data.u8[2] = 0x25;
|
||||
EGMP_444.data.u8[3] = 0x4E;
|
||||
EGMP_444.data.u8[4] = 0x04;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Send 500ms CANFD message
|
||||
if (currentMillis - previousMillis500ms >= INTERVAL_500_MS) {
|
||||
|
||||
|
@ -376,13 +829,7 @@ void send_can_battery() {
|
|||
clear_event(EVENT_CAN_OVERRUN);
|
||||
}
|
||||
previousMillis500ms = currentMillis;
|
||||
// Section added to close contractor
|
||||
if (datalayer.battery.status.bms_status == ACTIVE) {
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
} else { //datalayer.battery.status.bms_status == FAULT or inverter requested opening contactors
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
}
|
||||
// Section end
|
||||
|
||||
EGMP_7E4.data.u8[3] = KIA_7E4_COUNTER;
|
||||
transmit_can(&EGMP_7E4, can_config.battery);
|
||||
|
||||
|
@ -391,6 +838,18 @@ void send_can_battery() {
|
|||
KIA_7E4_COUNTER = 0x01;
|
||||
}
|
||||
}
|
||||
//Send 1s CANFD message
|
||||
if (currentMillis - previousMillis1s >= INTERVAL_1_S) {
|
||||
previousMillis1s = currentMillis;
|
||||
|
||||
transmit_can(&EGMP_48F, can_config.battery);
|
||||
}
|
||||
//Send 2s CANFD message
|
||||
if (currentMillis - previousMillis2s >= INTERVAL_2_S) {
|
||||
previousMillis2s = currentMillis;
|
||||
|
||||
transmit_can(&EGMP_4FE, can_config.battery);
|
||||
}
|
||||
}
|
||||
|
||||
void setup_battery(void) { // Performs one time setup at startup
|
||||
|
@ -398,6 +857,8 @@ void setup_battery(void) { // Performs one time setup at startup
|
|||
Serial.println("Hyundai E-GMP (Electric Global Modular Platform) battery selected");
|
||||
#endif
|
||||
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
|
||||
datalayer.battery.info.number_of_cells = 192; // TODO: will vary depending on battery
|
||||
|
||||
datalayer.battery.info.max_design_voltage_dV =
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
WiFiClient espClient;
|
||||
PubSubClient client(espClient);
|
||||
char mqtt_msg[MQTT_MSG_BUFFER_SIZE];
|
||||
int value = 0;
|
||||
static unsigned long previousMillisUpdateVal;
|
||||
MyTimer publish_global_timer(5000);
|
||||
|
||||
|
@ -25,13 +24,17 @@ static void publish_values(void) {
|
|||
publish_cell_voltages();
|
||||
}
|
||||
|
||||
#ifdef HA_AUTODISCOVERY
|
||||
static String generateCellVoltageAutoConfigTopic(int cell_number, const char* hostname) {
|
||||
return String("homeassistant/sensor/battery-emulator_") + String(hostname) + "/cell_voltage" + String(cell_number) +
|
||||
"/config";
|
||||
}
|
||||
#endif // HA_AUTODISCOVERY
|
||||
|
||||
static void publish_cell_voltages(void) {
|
||||
#ifdef HA_AUTODISCOVERY
|
||||
static bool mqtt_first_transmission = true;
|
||||
#endif // HA_AUTODISCOVERY
|
||||
static JsonDocument doc;
|
||||
static const char* hostname = WiFi.getHostname();
|
||||
static String state_topic = String("battery-emulator_") + String(hostname) + "/spec_data";
|
||||
|
@ -40,7 +43,7 @@ static void publish_cell_voltages(void) {
|
|||
if (datalayer.battery.info.number_of_cells == 0u) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HA_AUTODISCOVERY
|
||||
if (mqtt_first_transmission == true) {
|
||||
mqtt_first_transmission = false;
|
||||
String topic = "homeassistant/sensor/battery-emulator/cell_voltage";
|
||||
|
@ -71,6 +74,7 @@ static void publish_cell_voltages(void) {
|
|||
}
|
||||
doc.clear(); // clear after sending autoconfig
|
||||
} else {
|
||||
#endif // HA_AUTODISCOVERY
|
||||
// If cell voltages haven't been populated...
|
||||
if (datalayer.battery.info.number_of_cells == 0u ||
|
||||
datalayer.battery.status.cell_voltages_mV[datalayer.battery.info.number_of_cells - 1] == 0u) {
|
||||
|
@ -87,12 +91,15 @@ static void publish_cell_voltages(void) {
|
|||
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Cell voltage MQTT msg could not be sent");
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
}
|
||||
doc.clear();
|
||||
#ifdef HA_AUTODISCOVERY
|
||||
}
|
||||
#endif // HA_AUTODISCOVERY
|
||||
}
|
||||
|
||||
#ifdef HA_AUTODISCOVERY
|
||||
struct SensorConfig {
|
||||
const char* object_id;
|
||||
const char* name;
|
||||
|
@ -117,12 +124,16 @@ SensorConfig sensorConfigs[] = {
|
|||
static String generateCommonInfoAutoConfigTopic(const char* object_id, const char* hostname) {
|
||||
return String("homeassistant/sensor/battery-emulator_") + String(hostname) + "/" + String(object_id) + "/config";
|
||||
}
|
||||
#endif // HA_AUTODISCOVERY
|
||||
|
||||
static void publish_common_info(void) {
|
||||
static JsonDocument doc;
|
||||
#ifdef HA_AUTODISCOVERY
|
||||
static bool mqtt_first_transmission = true;
|
||||
#endif // HA_AUTODISCOVERY
|
||||
static const char* hostname = WiFi.getHostname();
|
||||
static String state_topic = String("battery-emulator_") + String(hostname) + "/info";
|
||||
#ifdef HA_AUTODISCOVERY
|
||||
if (mqtt_first_transmission == true) {
|
||||
mqtt_first_transmission = false;
|
||||
for (int i = 0; i < sizeof(sensorConfigs) / sizeof(sensorConfigs[0]); i++) {
|
||||
|
@ -149,6 +160,7 @@ static void publish_common_info(void) {
|
|||
}
|
||||
doc.clear();
|
||||
} else {
|
||||
#endif // HA_AUTODISCOVERY
|
||||
doc["SOC"] = ((float)datalayer.battery.status.reported_soc) / 100.0;
|
||||
doc["SOC_real"] = ((float)datalayer.battery.status.real_soc) / 100.0;
|
||||
doc["state_of_health"] = ((float)datalayer.battery.status.soh_pptt) / 100.0;
|
||||
|
@ -167,10 +179,12 @@ static void publish_common_info(void) {
|
|||
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Common info MQTT msg could not be sent");
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
}
|
||||
doc.clear();
|
||||
#ifdef HA_AUTODISCOVERY
|
||||
}
|
||||
#endif // HA_AUTODISCOVERY
|
||||
}
|
||||
|
||||
/* If we lose the connection, get it back */
|
||||
|
@ -178,7 +192,7 @@ static void reconnect() {
|
|||
// attempt one reconnection
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.print("Attempting MQTT connection... ");
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
const char* hostname = WiFi.getHostname();
|
||||
char clientId[64]; // Adjust the size as needed
|
||||
snprintf(clientId, sizeof(clientId), "LilyGoClient-%s", hostname);
|
||||
|
@ -186,13 +200,13 @@ static void reconnect() {
|
|||
if (client.connect(clientId, mqtt_user, mqtt_password)) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("connected");
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
} else {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.print("failed, rc=");
|
||||
Serial.print(client.state());
|
||||
Serial.println(" try again in 5 seconds");
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
// Wait 5 seconds before retrying
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +215,7 @@ void init_mqtt(void) {
|
|||
client.setServer(MQTT_SERVER, MQTT_PORT);
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("MQTT initialized");
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
|
||||
previousMillisUpdateVal = millis();
|
||||
reconnect();
|
||||
|
|
|
@ -155,7 +155,6 @@ String settings_processor(const String& var) {
|
|||
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
||||
"updateMaxDischargeA?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between 0 "
|
||||
"and 1000.0');}}}";
|
||||
content += "</script>";
|
||||
|
||||
#ifdef TEST_FAKE_BATTERY
|
||||
content +=
|
||||
|
|
|
@ -37,12 +37,16 @@ unsigned long last_wifi_attempt_time = millis(); //init millis so wifi monitor
|
|||
|
||||
void init_webserver() {
|
||||
// Configure WiFi
|
||||
#ifdef WIFIAP
|
||||
if (AccessPointEnabled) {
|
||||
WiFi.mode(WIFI_AP_STA); // Simultaneous WiFi AP and Router connection
|
||||
init_WiFi_AP();
|
||||
} else {
|
||||
WiFi.mode(WIFI_STA); // Only Router connection
|
||||
}
|
||||
#else
|
||||
WiFi.mode(WIFI_STA); // Only Router connection
|
||||
#endif // WIFIAP
|
||||
init_WiFi_STA(ssid.c_str(), password.c_str(), wifi_channel);
|
||||
|
||||
String content = index_html;
|
||||
|
@ -181,7 +185,7 @@ void init_webserver() {
|
|||
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
#endif
|
||||
#endif // TEST_FAKE_BATTERY
|
||||
|
||||
#if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
|
||||
// Route for editing ChargerTargetV
|
||||
|
@ -260,7 +264,7 @@ void init_webserver() {
|
|||
request->send(400, "text/plain", "Bad Request");
|
||||
}
|
||||
});
|
||||
#endif
|
||||
#endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
|
||||
|
||||
// Send a GET request to <ESP_IP>/update
|
||||
server.on("/debug", HTTP_GET,
|
||||
|
@ -283,22 +287,24 @@ void init_webserver() {
|
|||
#ifdef MQTT
|
||||
// Init MQTT
|
||||
init_mqtt();
|
||||
#endif
|
||||
#endif // MQTT
|
||||
}
|
||||
|
||||
#ifdef WIFIAP
|
||||
void init_WiFi_AP() {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Creating Access Point: " + String(ssidAP));
|
||||
Serial.println("With password: " + String(passwordAP));
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
WiFi.softAP(ssidAP, passwordAP);
|
||||
IPAddress IP = WiFi.softAPIP();
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("Access Point created.");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(IP);
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
}
|
||||
#endif // WIFIAP
|
||||
|
||||
String getConnectResultString(wl_status_t status) {
|
||||
switch (status) {
|
||||
|
@ -331,7 +337,7 @@ void wifi_monitor() {
|
|||
if (status != WL_CONNECTED && status != WL_IDLE_STATUS) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println(getConnectResultString(status));
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
if (wifi_state == INIT) { //we haven't been connected yet, try the init logic
|
||||
init_WiFi_STA(ssid.c_str(), password.c_str(), wifi_channel);
|
||||
} else { //we were connected before, try the reconnect logic
|
||||
|
@ -339,7 +345,7 @@ void wifi_monitor() {
|
|||
last_wifi_attempt_time = currentMillis;
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("WiFi not connected, trying to reconnect...");
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
wifi_state = RECONNECTING;
|
||||
WiFi.reconnect();
|
||||
wifi_reconnect_interval = min(wifi_reconnect_interval * 2, MAX_WIFI_RETRY_INTERVAL);
|
||||
|
@ -355,7 +361,7 @@ void wifi_monitor() {
|
|||
Serial.print(" Signal Strength: " + String(WiFi.RSSI()) + " dBm");
|
||||
Serial.println(" Channel: " + String(WiFi.channel()));
|
||||
Serial.println(" Hostname: " + String(WiFi.getHostname()));
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -373,7 +379,7 @@ void init_WiFi_STA(const char* ssid, const char* password, const uint8_t wifi_ch
|
|||
#ifdef DEBUG_VIA_USB
|
||||
Serial.print("Connecting to ");
|
||||
Serial.println(ssid);
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
WiFi.begin(ssid, password, wifi_channel);
|
||||
WiFi.setAutoReconnect(true); // Enable auto reconnect
|
||||
wl_status_t result = static_cast<wl_status_t>(WiFi.waitForConnectResult(INIT_WIFI_CONNECT_TIMEOUT));
|
||||
|
@ -408,10 +414,10 @@ String processor(const String& var) {
|
|||
// Show hardware used:
|
||||
#ifdef HW_LILYGO
|
||||
content += "<h4>Hardware: LilyGo T-CAN485</h4>";
|
||||
#endif
|
||||
#endif // HW_LILYGO
|
||||
#ifdef HW_STARK
|
||||
content += "<h4>Hardware: Stark CMR Module</h4>";
|
||||
#endif
|
||||
#endif // HW_STARK
|
||||
content += "<h4>Uptime: " + uptime_formatter::getUptime() + "</h4>";
|
||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||
// Load information
|
||||
|
@ -431,7 +437,7 @@ String processor(const String& var) {
|
|||
content += "<h4>CAN/serial RX function timing: " + String(datalayer.system.status.time_snap_comm_us) + " us</h4>";
|
||||
content += "<h4>CAN TX function timing: " + String(datalayer.system.status.time_snap_cantx_us) + " us</h4>";
|
||||
content += "<h4>OTA function timing: " + String(datalayer.system.status.time_snap_ota_us) + " us</h4>";
|
||||
#endif
|
||||
#endif // FUNCTION_TIME_MEASUREMENT
|
||||
|
||||
wl_status_t status = WiFi.status();
|
||||
// Display ssid of network connected to and, if connected to the WiFi, its own IP
|
||||
|
@ -453,103 +459,100 @@ String processor(const String& var) {
|
|||
content += "<h4 style='color: white;'>Inverter protocol: ";
|
||||
#ifdef BYD_CAN
|
||||
content += "BYD Battery-Box Premium HVS over CAN Bus";
|
||||
#endif
|
||||
#endif // BYD_CAN
|
||||
#ifdef BYD_MODBUS
|
||||
content += "BYD 11kWh HVM battery over Modbus RTU";
|
||||
#endif
|
||||
#endif// BYD_MODBUS
|
||||
#ifdef BYD_KOSTAL_RS485
|
||||
content += "BYD 11kWh HVM battery over Kostal RS485";
|
||||
#endif
|
||||
#ifdef LUNA2000_MODBUS
|
||||
content += "Luna2000 battery over Modbus RTU";
|
||||
#endif
|
||||
#endif //BYD_KOSTAL_RS485
|
||||
#ifdef PYLON_CAN
|
||||
content += "Pylontech battery over CAN bus";
|
||||
#endif
|
||||
#endif // PYLON_CAN
|
||||
#ifdef SERIAL_LINK_TRANSMITTER
|
||||
content += "Serial link to another LilyGo board";
|
||||
#endif
|
||||
#endif // SERIAL_LINK_TRANSMITTER
|
||||
#ifdef SMA_CAN
|
||||
content += "BYD Battery-Box H 8.9kWh, 7 mod over CAN bus";
|
||||
#endif
|
||||
#endif // SMA_CAN
|
||||
#ifdef SOFAR_CAN
|
||||
content += "Sofar Energy Storage Inverter High Voltage BMS General Protocol (Extended Frame) over CAN bus";
|
||||
#endif
|
||||
#endif // SOFAR_CAN
|
||||
#ifdef SOLAX_CAN
|
||||
content += "SolaX Triple Power LFP over CAN bus";
|
||||
#endif
|
||||
#endif // SOLAX_CAN
|
||||
content += "</h4>";
|
||||
|
||||
content += "<h4 style='color: white;'>Battery protocol: ";
|
||||
#ifdef BMW_I3_BATTERY
|
||||
content += "BMW i3";
|
||||
#endif
|
||||
#endif // BMW_I3_BATTERY
|
||||
#ifdef BYD_ATTO_3_BATTERY
|
||||
content += "BYD Atto 3";
|
||||
#endif
|
||||
#endif // BYD_ATTO_3_BATTERY
|
||||
#ifdef CHADEMO_BATTERY
|
||||
content += "Chademo V2X mode";
|
||||
#endif
|
||||
#endif // CHADEMO_BATTERY
|
||||
#ifdef IMIEV_CZERO_ION_BATTERY
|
||||
content += "I-Miev / C-Zero / Ion Triplet";
|
||||
#endif
|
||||
#endif // IMIEV_CZERO_ION_BATTERY
|
||||
#ifdef JAGUAR_IPACE_BATTERY
|
||||
content += "Jaguar I-PACE";
|
||||
#endif
|
||||
#endif // JAGUAR_IPACE_BATTERY
|
||||
#ifdef KIA_HYUNDAI_64_BATTERY
|
||||
content += "Kia/Hyundai 64kWh";
|
||||
#endif
|
||||
#endif // KIA_HYUNDAI_64_BATTERY
|
||||
#ifdef KIA_E_GMP_BATTERY
|
||||
content += "Kia/Hyundai EGMP platform";
|
||||
#endif
|
||||
#endif // KIA_E_GMP_BATTERY
|
||||
#ifdef KIA_HYUNDAI_HYBRID_BATTERY
|
||||
content += "Kia/Hyundai Hybrid";
|
||||
#endif
|
||||
#endif // KIA_HYUNDAI_HYBRID_BATTERY
|
||||
#ifdef MG_5_BATTERY
|
||||
content += "MG 5";
|
||||
#endif
|
||||
#endif // MG_5_BATTERY
|
||||
#ifdef NISSAN_LEAF_BATTERY
|
||||
content += "Nissan LEAF";
|
||||
#endif
|
||||
#endif // NISSAN_LEAF_BATTERY
|
||||
#ifdef RENAULT_KANGOO_BATTERY
|
||||
content += "Renault Kangoo";
|
||||
#endif
|
||||
#endif // RENAULT_KANGOO_BATTERY
|
||||
#ifdef RENAULT_ZOE_GEN1_BATTERY
|
||||
content += "Renault Zoe Gen1 22/40";
|
||||
#endif
|
||||
#endif // RENAULT_ZOE_GEN1_BATTERY
|
||||
#ifdef RENAULT_ZOE_GEN2_BATTERY
|
||||
content += "Renault Zoe Gen2 50";
|
||||
#endif
|
||||
#endif // RENAULT_ZOE_GEN2_BATTERY
|
||||
#ifdef SANTA_FE_PHEV_BATTERY
|
||||
content += "Santa Fe PHEV";
|
||||
#endif
|
||||
#endif // SANTA_FE_PHEV_BATTERY
|
||||
#ifdef SERIAL_LINK_RECEIVER
|
||||
content += "Serial link to another LilyGo board";
|
||||
#endif
|
||||
#endif // SERIAL_LINK_RECEIVER
|
||||
#ifdef TESLA_MODEL_3_BATTERY
|
||||
content += "Tesla Model S/3/X/Y";
|
||||
#endif
|
||||
#endif // TESLA_MODEL_3_BATTERY
|
||||
#ifdef VOLVO_SPA_BATTERY
|
||||
content += "Volvo / Polestar 78kWh battery";
|
||||
#endif
|
||||
#endif // VOLVO_SPA_BATTERY
|
||||
#ifdef TEST_FAKE_BATTERY
|
||||
content += "Fake battery for testing purposes";
|
||||
#endif
|
||||
#endif // TEST_FAKE_BATTERY
|
||||
#ifdef DOUBLE_BATTERY
|
||||
content += " (Double battery)";
|
||||
#endif
|
||||
#endif // DOUBLE_BATTERY
|
||||
content += "</h4>";
|
||||
|
||||
#if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
|
||||
content += "<h4 style='color: white;'>Charger protocol: ";
|
||||
#ifdef CHEVYVOLT_CHARGER
|
||||
content += "Chevy Volt Gen1 Charger";
|
||||
#endif
|
||||
#endif // CHEVYVOLT_CHARGER
|
||||
#ifdef NISSANLEAF_CHARGER
|
||||
content += "Nissan LEAF 2013-2024 PDM charger";
|
||||
#endif
|
||||
#endif // NISSANLEAF_CHARGER
|
||||
content += "</h4>";
|
||||
#endif
|
||||
#endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
|
||||
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
|
@ -561,7 +564,7 @@ String processor(const String& var) {
|
|||
#else
|
||||
// Start a new block with a specific background color. Color changes depending on system status
|
||||
content += "<div style='background-color: ";
|
||||
#endif
|
||||
#endif // DOUBLE_BATTERY
|
||||
|
||||
switch (led_get_color()) {
|
||||
case led_color::GREEN:
|
||||
|
@ -722,7 +725,7 @@ String processor(const String& var) {
|
|||
|
||||
content += "</div>";
|
||||
content += "</div>";
|
||||
#endif
|
||||
#endif // DOUBLE_BATTERY
|
||||
|
||||
#if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
|
||||
// Start a new block with orange background color
|
||||
|
@ -762,7 +765,7 @@ String processor(const String& var) {
|
|||
content += "<h4 style='color: white;'>Charger LVDC Output V: " + String(LVvol, 2) + "</h4>";
|
||||
content += "<h4 style='color: white;'>Charger AC Input V: " + String(ACvol, 2) + " VAC</h4>";
|
||||
content += "<h4 style='color: white;'>Charger AC Input I: " + String(ACcur, 2) + " A</h4>";
|
||||
#endif
|
||||
#endif // CHEVYVOLT_CHARGER
|
||||
#ifdef NISSANLEAF_CHARGER
|
||||
float chgPwrDC = static_cast<float>(charger_stat_HVcur * 100);
|
||||
charger_stat_HVcur = chgPwrDC / (datalayer.battery.status.voltage_dV / 10); // P/U=I
|
||||
|
@ -775,10 +778,10 @@ String processor(const String& var) {
|
|||
content += "<h4 style='color: white;'>Charger HVDC Output V: " + String(HVvol, 2) + " V</h4>";
|
||||
content += "<h4 style='color: white;'>Charger HVDC Output I: " + String(HVcur, 2) + " A</h4>";
|
||||
content += "<h4 style='color: white;'>Charger AC Input V: " + String(ACvol, 2) + " VAC</h4>";
|
||||
#endif
|
||||
#endif // NISSANLEAF_CHARGER
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
#endif
|
||||
#endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
|
||||
|
||||
content += "<button onclick='OTA()'>Perform OTA update</button>";
|
||||
content += " ";
|
||||
|
@ -832,7 +835,7 @@ void onOTAProgress(size_t current, size_t final) {
|
|||
ota_progress_millis = millis();
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
// Reset the "watchdog"
|
||||
ota_timeout_timer.reset();
|
||||
}
|
||||
|
@ -843,11 +846,11 @@ void onOTAEnd(bool success) {
|
|||
if (success) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("OTA update finished successfully!");
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
} else {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.println("There was an error during OTA update!");
|
||||
#endif
|
||||
#endif // DEBUG_VIA_USB
|
||||
|
||||
// If we fail without a timeout, try to restore CAN
|
||||
ESP32Can.CANInit();
|
||||
|
|
|
@ -54,6 +54,7 @@ void init_webserver();
|
|||
*/
|
||||
void wifi_monitor();
|
||||
|
||||
#ifdef WIFIAP
|
||||
/**
|
||||
* @brief Initialization function that creates a WiFi Access Point.
|
||||
*
|
||||
|
@ -62,6 +63,7 @@ void wifi_monitor();
|
|||
* @return void
|
||||
*/
|
||||
void init_WiFi_AP();
|
||||
#endif // WIFIAP
|
||||
|
||||
/**
|
||||
* @brief Initialization function that connects to an existing network.
|
||||
|
|
|
@ -19,10 +19,6 @@
|
|||
#include "KOSTAL-RS485.h"
|
||||
#endif
|
||||
|
||||
#ifdef LUNA2000_MODBUS
|
||||
#include "LUNA2000-MODBUS.h"
|
||||
#endif
|
||||
|
||||
#ifdef PYLON_CAN
|
||||
#include "PYLON-CAN.h"
|
||||
#endif
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
#include "../include.h"
|
||||
#ifdef LUNA2000_MODBUS
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "LUNA2000-MODBUS.h"
|
||||
|
||||
void update_modbus_registers_inverter() {
|
||||
handle_update_data_modbus32051();
|
||||
handle_update_data_modbus39500();
|
||||
}
|
||||
|
||||
void handle_update_data_modbus32051() {
|
||||
// Store the data into the array
|
||||
static uint16_t system_data[9];
|
||||
system_data[0] = 1;
|
||||
system_data[1] = 534; //Goes between 534-498 depending on SOC
|
||||
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[4] = 616; //Goes between 616- -520 [NOTE, SIGNED VALUE]
|
||||
system_data[5] = datalayer.battery.status.temperature_max_dC; //Temperature max?
|
||||
system_data[6] = datalayer.battery.status.temperature_min_dC; //Temperature min?
|
||||
system_data[7] = (datalayer.battery.status.reported_soc / 100); //SOC 0-100%, no decimals
|
||||
system_data[8] = 98; //Always 98 in logs
|
||||
static uint16_t i = 2051;
|
||||
memcpy(&mbPV[i], system_data, sizeof(system_data));
|
||||
}
|
||||
|
||||
void handle_update_data_modbus39500() {
|
||||
// Store the data into the array
|
||||
static uint16_t system_data[26];
|
||||
system_data[0] = 0;
|
||||
system_data[1] = datalayer.battery.info.total_capacity_Wh; //Capacity? 5000 with 5kWh battery
|
||||
system_data[2] = 0;
|
||||
system_data[3] = datalayer.battery.info.total_capacity_Wh; //Capacity? 5000 with 5kWh battery
|
||||
system_data[4] = 0;
|
||||
system_data[5] = 2500; //???
|
||||
system_data[6] = 0;
|
||||
system_data[7] = 2500; //???
|
||||
system_data[8] = (datalayer.battery.status.reported_soc / 100); //SOC 0-100%, no decimals
|
||||
system_data[9] =
|
||||
1; //Running status, equiv to register 37762, 0 = Offline, 1 = Standby,2 = Running, 3 = Fault, 4 = sleep mode
|
||||
system_data[10] = datalayer.battery.status.voltage_dV; //Battery bus voltage (766.5V = 7665)
|
||||
system_data[11] = 9; //TODO: GOES LOWER WITH LOW SOC
|
||||
system_data[12] = 0;
|
||||
system_data[13] = 699; //TODO: GOES LOWER WITH LOW SOC
|
||||
system_data[14] = 1; //Always 1 in logs
|
||||
system_data[15] = 18; //Always 18 in logs
|
||||
system_data[16] = 8066; //TODO: GOES HIGHER WITH LOW SOC (max allowed charge W?)
|
||||
system_data[17] = 17;
|
||||
system_data[18] = 44027; //TODO: GOES LOWER WITH LOW SOC
|
||||
system_data[19] = 0;
|
||||
system_data[20] = 435; //Always 435 in logs
|
||||
system_data[21] = 0;
|
||||
system_data[22] = 0;
|
||||
system_data[23] = 0;
|
||||
system_data[24] = (datalayer.battery.status.reported_soc / 10); //SOC 0-100.0%, 1x decimal
|
||||
system_data[25] = 0;
|
||||
system_data[26] = 1; //Always 1 in logs
|
||||
static uint16_t i = 9500;
|
||||
memcpy(&mbPV[i], system_data, sizeof(system_data));
|
||||
}
|
||||
#endif
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef LUNA2000_MODBUS_H
|
||||
#define LUNA2000_MODBUS_H
|
||||
#include "../include.h"
|
||||
|
||||
#define MODBUS_INVERTER_SELECTED
|
||||
|
||||
#define MB_RTU_NUM_VALUES 30000
|
||||
|
||||
extern uint16_t mbPV[MB_RTU_NUM_VALUES];
|
||||
|
||||
void handle_update_data_modbus32051();
|
||||
void handle_update_data_modbus39500();
|
||||
#endif
|
|
@ -14,8 +14,8 @@ src_dir = ./Software
|
|||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
platform_packages=
|
||||
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.2
|
||||
framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.2/esp32-arduino-libs-3.0.2.zip
|
||||
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.4
|
||||
framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.4/esp32-arduino-libs-3.0.4.zip
|
||||
board = esp32dev
|
||||
monitor_speed = 115200
|
||||
monitor_filters = default, time, log2file
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue