mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 02:39:57 +02:00
Merge branch 'main' into bugfix/i3-power-watt
This commit is contained in:
commit
9876d5b75b
20 changed files with 427 additions and 202 deletions
|
@ -39,12 +39,12 @@ For more examples showing wiring, see each battery types own Wiki page. For inst
|
||||||
1. Download the Arduino IDE: https://www.arduino.cc/en/software
|
1. Download the Arduino IDE: https://www.arduino.cc/en/software
|
||||||
2. When the Arduino IDE has been started;
|
2. When the Arduino IDE has been started;
|
||||||
Click "File" in the upper left corner -> Preferences -> Additional Development >Board Manager URL -> Enter the URL in the input box https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
|
Click "File" in the upper left corner -> Preferences -> Additional Development >Board Manager URL -> Enter the URL in the input box https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
|
||||||
3. Go to "Boards Manager", and install the ESP32 package by Espressif Systems. **The latest confirmed compatible version is 2.0.11**
|
3. Go to "Boards Manager", and install the ESP32 package by Espressif Systems. **NOTE: The version depends on which release of Battery-Emulator you are running!**
|
||||||
|
|
||||||
⚠️ Make sure to use a 2.x.x version - preferably 2.0.11 - and not a 3.x.x version, as it is not yet supported by the libraries we include with the SW!
|
- ⚠️ Make sure to use a 2.x.x version if you are on a release **older** than 6.0.0 (For instance ESP32 v2.0.11 when using Battery-Emulator v5.4.0)
|
||||||

|
- ⚠️ Make sure to use a 3.x.x version if you are on a release **newer** than 6.0.0 (For instance ESP32 v3.0.0 when using Battery-Emulator v6.0.0)
|
||||||
|
|
||||||
For future migration, see this link: https://docs.espressif.com/projects/arduino-esp32/en/latest/migration_guides/2.x_to_3.0.html
|

|
||||||
|
|
||||||
4. The arduino settings should be set to "ESP32 Dev Module" with the following settings;
|
4. The arduino settings should be set to "ESP32 Dev Module" with the following settings;
|
||||||

|

|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
Preferences settings; // Store user settings
|
Preferences settings; // Store user settings
|
||||||
// The current software version, shown on webserver
|
// The current software version, shown on webserver
|
||||||
const char* version_number = "5.11.dev";
|
const char* version_number = "6.0.RC";
|
||||||
|
|
||||||
// Interval settings
|
// Interval settings
|
||||||
uint16_t intervalUpdateValues = INTERVAL_5_S; // Interval at which to update inverter values / Modbus registers
|
uint16_t intervalUpdateValues = INTERVAL_5_S; // Interval at which to update inverter values / Modbus registers
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
//#define IMIEV_CZERO_ION_BATTERY
|
//#define IMIEV_CZERO_ION_BATTERY
|
||||||
//#define KIA_HYUNDAI_64_BATTERY
|
//#define KIA_HYUNDAI_64_BATTERY
|
||||||
//#define KIA_E_GMP_BATTERY
|
//#define KIA_E_GMP_BATTERY
|
||||||
|
//#define MG_5_BATTERY
|
||||||
//#define NISSAN_LEAF_BATTERY
|
//#define NISSAN_LEAF_BATTERY
|
||||||
//#define PYLON_BATTERY
|
//#define PYLON_BATTERY
|
||||||
//#define RENAULT_KANGOO_BATTERY
|
//#define RENAULT_KANGOO_BATTERY
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
#include "KIA-HYUNDAI-64-BATTERY.h"
|
#include "KIA-HYUNDAI-64-BATTERY.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef MG_5_BATTERY
|
||||||
|
#include "MG-5-BATTERY.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
#ifdef NISSAN_LEAF_BATTERY
|
||||||
#include "NISSAN-LEAF-BATTERY.h"
|
#include "NISSAN-LEAF-BATTERY.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
150
Software/src/battery/MG-5-BATTERY.cpp
Normal file
150
Software/src/battery/MG-5-BATTERY.cpp
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
#include "../include.h"
|
||||||
|
#ifdef MG_5_BATTERY_H
|
||||||
|
#include "../datalayer/datalayer.h"
|
||||||
|
#include "../devboard/utils/events.h"
|
||||||
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
#include "MG-5-BATTERY.h"
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
- Get contactor closing working
|
||||||
|
- Figure out which CAN messages need to be sent towards the battery to keep it alive
|
||||||
|
- Map all values from battery CAN messages
|
||||||
|
- Most important ones
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 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 previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
||||||
|
|
||||||
|
static int BMS_SOC = 0;
|
||||||
|
|
||||||
|
CAN_frame_t MG_5_100 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x100,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00}};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
datalayer.battery.status.voltage_dV;
|
||||||
|
|
||||||
|
datalayer.battery.status.current_dA;
|
||||||
|
|
||||||
|
datalayer.battery.info.total_capacity_Wh;
|
||||||
|
|
||||||
|
datalayer.battery.status.remaining_capacity_Wh;
|
||||||
|
|
||||||
|
datalayer.battery.status.max_discharge_power_W;
|
||||||
|
|
||||||
|
datalayer.battery.status.max_charge_power_W;
|
||||||
|
|
||||||
|
datalayer.battery.status.active_power_W;
|
||||||
|
|
||||||
|
datalayer.battery.status.temperature_min_dC;
|
||||||
|
|
||||||
|
datalayer.battery.status.temperature_max_dC;
|
||||||
|
|
||||||
|
#ifdef DEBUG_VIA_USB
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
switch (rx_frame.MsgID) {
|
||||||
|
case 0x171: //Following messages were detected on a MG5 battery BMS
|
||||||
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; // Let system know battery is sending CAN
|
||||||
|
break;
|
||||||
|
case 0x172:
|
||||||
|
break;
|
||||||
|
case 0x173:
|
||||||
|
break;
|
||||||
|
case 0x293:
|
||||||
|
break;
|
||||||
|
case 0x295:
|
||||||
|
break;
|
||||||
|
case 0x297:
|
||||||
|
break;
|
||||||
|
case 0x29B:
|
||||||
|
break;
|
||||||
|
case 0x29C:
|
||||||
|
break;
|
||||||
|
case 0x2A0:
|
||||||
|
break;
|
||||||
|
case 0x2A2:
|
||||||
|
break;
|
||||||
|
case 0x322:
|
||||||
|
break;
|
||||||
|
case 0x334:
|
||||||
|
break;
|
||||||
|
case 0x33F:
|
||||||
|
break;
|
||||||
|
case 0x391:
|
||||||
|
break;
|
||||||
|
case 0x393:
|
||||||
|
break;
|
||||||
|
case 0x3AB:
|
||||||
|
break;
|
||||||
|
case 0x3AC:
|
||||||
|
break;
|
||||||
|
case 0x3B8:
|
||||||
|
break;
|
||||||
|
case 0x3BA:
|
||||||
|
break;
|
||||||
|
case 0x3BC:
|
||||||
|
break;
|
||||||
|
case 0x3BE:
|
||||||
|
break;
|
||||||
|
case 0x3C0:
|
||||||
|
break;
|
||||||
|
case 0x3C2:
|
||||||
|
break;
|
||||||
|
case 0x400:
|
||||||
|
break;
|
||||||
|
case 0x402:
|
||||||
|
break;
|
||||||
|
case 0x418:
|
||||||
|
break;
|
||||||
|
case 0x44C:
|
||||||
|
break;
|
||||||
|
case 0x620:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void send_can_battery() {
|
||||||
|
unsigned long currentMillis = millis();
|
||||||
|
//Send 10ms message
|
||||||
|
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
|
||||||
|
// Check if sending of CAN messages has been delayed too much.
|
||||||
|
if ((currentMillis - previousMillis10 >= INTERVAL_10_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) {
|
||||||
|
set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis10));
|
||||||
|
}
|
||||||
|
previousMillis10 = currentMillis;
|
||||||
|
|
||||||
|
ESP32Can.CANWriteFrame(&MG_5_100);
|
||||||
|
}
|
||||||
|
// Send 100ms CAN Message
|
||||||
|
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
|
||||||
|
previousMillis100 = currentMillis;
|
||||||
|
|
||||||
|
//ESP32Can.CANWriteFrame(&MG_5_100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
|
#ifdef DEBUG_VIA_USB
|
||||||
|
Serial.println("MG 5 battery selected");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
datalayer.battery.info.max_design_voltage_dV = 4040; // Over this charging is not possible
|
||||||
|
datalayer.battery.info.min_design_voltage_dV = 3100; // Under this discharging is disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
12
Software/src/battery/MG-5-BATTERY.h
Normal file
12
Software/src/battery/MG-5-BATTERY.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef MG_5_BATTERY_H
|
||||||
|
#define MG_5_BATTERY_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "../include.h"
|
||||||
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
|
||||||
|
#define BATTERY_SELECTED
|
||||||
|
#define MAX_CELL_DEVIATION_MV 150
|
||||||
|
|
||||||
|
void setup_battery(void);
|
||||||
|
|
||||||
|
#endif
|
|
@ -16,6 +16,7 @@ static unsigned long previousMillis10s = 0; // will store last time a 1s CAN Me
|
||||||
static uint8_t mprun10r = 0; //counter 0-20 for 0x1F2 message
|
static uint8_t mprun10r = 0; //counter 0-20 for 0x1F2 message
|
||||||
static uint8_t mprun10 = 0; //counter 0-3
|
static uint8_t mprun10 = 0; //counter 0-3
|
||||||
static uint8_t mprun100 = 0; //counter 0-3
|
static uint8_t mprun100 = 0; //counter 0-3
|
||||||
|
static bool can_bus_alive = false;
|
||||||
|
|
||||||
CAN_frame_t LEAF_1F2 = {.FIR = {.B =
|
CAN_frame_t LEAF_1F2 = {.FIR = {.B =
|
||||||
{
|
{
|
||||||
|
@ -202,11 +203,6 @@ void update_values_battery() { /* This function maps all the values fetched via
|
||||||
|
|
||||||
datalayer.battery.status.max_charge_power_W = (LB_Charge_Power_Limit * 1000); //kW to W
|
datalayer.battery.status.max_charge_power_W = (LB_Charge_Power_Limit * 1000); //kW to W
|
||||||
|
|
||||||
//Map all cell voltages to the global array
|
|
||||||
for (int i = 0; i < 96; ++i) {
|
|
||||||
datalayer.battery.status.cell_voltages_mV[i] = cell_voltages[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Extra safety functions below*/
|
/*Extra safety functions below*/
|
||||||
if (LB_GIDS < 10) //700Wh 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.
|
||||||
|
@ -352,6 +348,7 @@ void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
LB_Capacity_Empty = (bool)((rx_frame.data.u8[6] & 0x80) >> 7);
|
LB_Capacity_Empty = (bool)((rx_frame.data.u8[6] & 0x80) >> 7);
|
||||||
break;
|
break;
|
||||||
case 0x5BC:
|
case 0x5BC:
|
||||||
|
can_bus_alive = true;
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; // Let system know battery is sending CAN
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; // Let system know battery is sending CAN
|
||||||
|
|
||||||
LB_MAX = ((rx_frame.data.u8[5] & 0x10) >> 4);
|
LB_MAX = ((rx_frame.data.u8[5] & 0x10) >> 4);
|
||||||
|
@ -451,6 +448,13 @@ void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
}
|
}
|
||||||
if (rx_frame.data.u8[6] == 0xFF && rx_frame.data.u8[0] == 0x2C) { //Last frame
|
if (rx_frame.data.u8[6] == 0xFF && rx_frame.data.u8[0] == 0x2C) { //Last frame
|
||||||
//Last frame does not contain any cell data, calculate the result
|
//Last frame does not contain any cell data, calculate the result
|
||||||
|
|
||||||
|
//Map all cell voltages to the global array
|
||||||
|
for (int i = 0; i < 96; ++i) {
|
||||||
|
datalayer.battery.status.cell_voltages_mV[i] = cell_voltages[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
//calculate min/max voltages
|
||||||
min_max_voltage[0] = 9999;
|
min_max_voltage[0] = 9999;
|
||||||
min_max_voltage[1] = 0;
|
min_max_voltage[1] = 0;
|
||||||
for (cellcounter = 0; cellcounter < 96; cellcounter++) {
|
for (cellcounter = 0; cellcounter < 96; cellcounter++) {
|
||||||
|
@ -548,189 +552,192 @@ void receive_can_battery(CAN_frame_t rx_frame) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_battery() {
|
void send_can_battery() {
|
||||||
unsigned long currentMillis = millis();
|
if (can_bus_alive) {
|
||||||
|
|
||||||
//Send 10ms message
|
unsigned long currentMillis = millis();
|
||||||
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
|
|
||||||
// Check if sending of CAN messages has been delayed too much.
|
|
||||||
if ((currentMillis - previousMillis10 >= INTERVAL_10_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) {
|
|
||||||
set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis10));
|
|
||||||
}
|
|
||||||
previousMillis10 = currentMillis;
|
|
||||||
|
|
||||||
switch (mprun10) {
|
//Send 10ms message
|
||||||
case 0:
|
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
|
||||||
LEAF_1D4.data.u8[4] = 0x07;
|
// Check if sending of CAN messages has been delayed too much.
|
||||||
LEAF_1D4.data.u8[7] = 0x12;
|
if ((currentMillis - previousMillis10 >= INTERVAL_10_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) {
|
||||||
break;
|
set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis10));
|
||||||
case 1:
|
}
|
||||||
LEAF_1D4.data.u8[4] = 0x47;
|
previousMillis10 = currentMillis;
|
||||||
LEAF_1D4.data.u8[7] = 0xD5;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
LEAF_1D4.data.u8[4] = 0x87;
|
|
||||||
LEAF_1D4.data.u8[7] = 0x19;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
LEAF_1D4.data.u8[4] = 0xC7;
|
|
||||||
LEAF_1D4.data.u8[7] = 0xDE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ESP32Can.CANWriteFrame(&LEAF_1D4);
|
|
||||||
|
|
||||||
switch (mprun10r) {
|
switch (mprun10) {
|
||||||
case (0):
|
case 0:
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1D4.data.u8[4] = 0x07;
|
||||||
LEAF_1F2.data.u8[6] = 0x00;
|
LEAF_1D4.data.u8[7] = 0x12;
|
||||||
LEAF_1F2.data.u8[7] = 0x8F;
|
break;
|
||||||
break;
|
case 1:
|
||||||
case (1):
|
LEAF_1D4.data.u8[4] = 0x47;
|
||||||
LEAF_1F2.data.u8[6] = 0x01;
|
LEAF_1D4.data.u8[7] = 0xD5;
|
||||||
LEAF_1F2.data.u8[7] = 0x80;
|
break;
|
||||||
break;
|
case 2:
|
||||||
case (2):
|
LEAF_1D4.data.u8[4] = 0x87;
|
||||||
LEAF_1F2.data.u8[6] = 0x02;
|
LEAF_1D4.data.u8[7] = 0x19;
|
||||||
LEAF_1F2.data.u8[7] = 0x81;
|
break;
|
||||||
break;
|
case 3:
|
||||||
case (3):
|
LEAF_1D4.data.u8[4] = 0xC7;
|
||||||
LEAF_1F2.data.u8[6] = 0x03;
|
LEAF_1D4.data.u8[7] = 0xDE;
|
||||||
LEAF_1F2.data.u8[7] = 0x82;
|
break;
|
||||||
break;
|
}
|
||||||
case (4):
|
ESP32Can.CANWriteFrame(&LEAF_1D4);
|
||||||
LEAF_1F2.data.u8[6] = 0x00;
|
|
||||||
LEAF_1F2.data.u8[7] = 0x8F;
|
switch (mprun10r) {
|
||||||
break;
|
case (0):
|
||||||
case (5): // Set 2
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[6] = 0x00;
|
||||||
LEAF_1F2.data.u8[6] = 0x01;
|
LEAF_1F2.data.u8[7] = 0x8F;
|
||||||
LEAF_1F2.data.u8[7] = 0x84;
|
break;
|
||||||
break;
|
case (1):
|
||||||
case (6):
|
LEAF_1F2.data.u8[6] = 0x01;
|
||||||
LEAF_1F2.data.u8[6] = 0x02;
|
LEAF_1F2.data.u8[7] = 0x80;
|
||||||
LEAF_1F2.data.u8[7] = 0x85;
|
break;
|
||||||
break;
|
case (2):
|
||||||
case (7):
|
LEAF_1F2.data.u8[6] = 0x02;
|
||||||
LEAF_1F2.data.u8[6] = 0x03;
|
LEAF_1F2.data.u8[7] = 0x81;
|
||||||
LEAF_1F2.data.u8[7] = 0x86;
|
break;
|
||||||
break;
|
case (3):
|
||||||
case (8):
|
LEAF_1F2.data.u8[6] = 0x03;
|
||||||
LEAF_1F2.data.u8[6] = 0x00;
|
LEAF_1F2.data.u8[7] = 0x82;
|
||||||
LEAF_1F2.data.u8[7] = 0x83;
|
break;
|
||||||
break;
|
case (4):
|
||||||
case (9):
|
LEAF_1F2.data.u8[6] = 0x00;
|
||||||
LEAF_1F2.data.u8[6] = 0x01;
|
LEAF_1F2.data.u8[7] = 0x8F;
|
||||||
LEAF_1F2.data.u8[7] = 0x84;
|
break;
|
||||||
break;
|
case (5): // Set 2
|
||||||
case (10): // Set 3
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1F2.data.u8[6] = 0x01;
|
||||||
LEAF_1F2.data.u8[6] = 0x02;
|
LEAF_1F2.data.u8[7] = 0x84;
|
||||||
LEAF_1F2.data.u8[7] = 0x81;
|
break;
|
||||||
break;
|
case (6):
|
||||||
case (11):
|
LEAF_1F2.data.u8[6] = 0x02;
|
||||||
LEAF_1F2.data.u8[6] = 0x03;
|
LEAF_1F2.data.u8[7] = 0x85;
|
||||||
LEAF_1F2.data.u8[7] = 0x82;
|
break;
|
||||||
break;
|
case (7):
|
||||||
case (12):
|
LEAF_1F2.data.u8[6] = 0x03;
|
||||||
LEAF_1F2.data.u8[6] = 0x00;
|
LEAF_1F2.data.u8[7] = 0x86;
|
||||||
LEAF_1F2.data.u8[7] = 0x8F;
|
break;
|
||||||
break;
|
case (8):
|
||||||
case (13):
|
LEAF_1F2.data.u8[6] = 0x00;
|
||||||
LEAF_1F2.data.u8[6] = 0x01;
|
LEAF_1F2.data.u8[7] = 0x83;
|
||||||
LEAF_1F2.data.u8[7] = 0x80;
|
break;
|
||||||
break;
|
case (9):
|
||||||
case (14):
|
LEAF_1F2.data.u8[6] = 0x01;
|
||||||
LEAF_1F2.data.u8[6] = 0x02;
|
LEAF_1F2.data.u8[7] = 0x84;
|
||||||
LEAF_1F2.data.u8[7] = 0x81;
|
break;
|
||||||
break;
|
case (10): // Set 3
|
||||||
case (15): // Set 4
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[6] = 0x02;
|
||||||
LEAF_1F2.data.u8[6] = 0x03;
|
LEAF_1F2.data.u8[7] = 0x81;
|
||||||
LEAF_1F2.data.u8[7] = 0x86;
|
break;
|
||||||
break;
|
case (11):
|
||||||
case (16):
|
LEAF_1F2.data.u8[6] = 0x03;
|
||||||
LEAF_1F2.data.u8[6] = 0x00;
|
LEAF_1F2.data.u8[7] = 0x82;
|
||||||
LEAF_1F2.data.u8[7] = 0x83;
|
break;
|
||||||
break;
|
case (12):
|
||||||
case (17):
|
LEAF_1F2.data.u8[6] = 0x00;
|
||||||
LEAF_1F2.data.u8[6] = 0x01;
|
LEAF_1F2.data.u8[7] = 0x8F;
|
||||||
LEAF_1F2.data.u8[7] = 0x84;
|
break;
|
||||||
break;
|
case (13):
|
||||||
case (18):
|
LEAF_1F2.data.u8[6] = 0x01;
|
||||||
LEAF_1F2.data.u8[6] = 0x02;
|
LEAF_1F2.data.u8[7] = 0x80;
|
||||||
LEAF_1F2.data.u8[7] = 0x85;
|
break;
|
||||||
break;
|
case (14):
|
||||||
case (19):
|
LEAF_1F2.data.u8[6] = 0x02;
|
||||||
LEAF_1F2.data.u8[6] = 0x03;
|
LEAF_1F2.data.u8[7] = 0x81;
|
||||||
LEAF_1F2.data.u8[7] = 0x86;
|
break;
|
||||||
break;
|
case (15): // Set 4
|
||||||
default:
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
break;
|
LEAF_1F2.data.u8[6] = 0x03;
|
||||||
}
|
LEAF_1F2.data.u8[7] = 0x86;
|
||||||
|
break;
|
||||||
|
case (16):
|
||||||
|
LEAF_1F2.data.u8[6] = 0x00;
|
||||||
|
LEAF_1F2.data.u8[7] = 0x83;
|
||||||
|
break;
|
||||||
|
case (17):
|
||||||
|
LEAF_1F2.data.u8[6] = 0x01;
|
||||||
|
LEAF_1F2.data.u8[7] = 0x84;
|
||||||
|
break;
|
||||||
|
case (18):
|
||||||
|
LEAF_1F2.data.u8[6] = 0x02;
|
||||||
|
LEAF_1F2.data.u8[7] = 0x85;
|
||||||
|
break;
|
||||||
|
case (19):
|
||||||
|
LEAF_1F2.data.u8[6] = 0x03;
|
||||||
|
LEAF_1F2.data.u8[7] = 0x86;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
//Only send this message when NISSANLEAF_CHARGER is not defined (otherwise it will collide!)
|
//Only send this message when NISSANLEAF_CHARGER is not defined (otherwise it will collide!)
|
||||||
#ifndef NISSANLEAF_CHARGER
|
#ifndef NISSANLEAF_CHARGER
|
||||||
ESP32Can.CANWriteFrame(&LEAF_1F2); //Contains (CHG_STA_RQ == 1 == Normal Charge)
|
ESP32Can.CANWriteFrame(&LEAF_1F2); //Contains (CHG_STA_RQ == 1 == Normal Charge)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mprun10r = (mprun10r + 1) % 20; // 0x1F2 patter repeats after 20 messages. 0-1..19-0
|
mprun10r = (mprun10r + 1) % 20; // 0x1F2 patter repeats after 20 messages. 0-1..19-0
|
||||||
|
|
||||||
mprun10 = (mprun10 + 1) % 4; // mprun10 cycles between 0-1-2-3-0-1...
|
mprun10 = (mprun10 + 1) % 4; // mprun10 cycles between 0-1-2-3-0-1...
|
||||||
}
|
|
||||||
|
|
||||||
// Send 100ms CAN Message
|
|
||||||
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
|
|
||||||
previousMillis100 = currentMillis;
|
|
||||||
|
|
||||||
//When battery requests heating pack status change, ack this
|
|
||||||
if (Batt_Heater_Mail_Send_Request) {
|
|
||||||
LEAF_50B.data.u8[6] = 0x20; //Batt_Heater_Mail_Send_OK
|
|
||||||
} else {
|
|
||||||
LEAF_50B.data.u8[6] = 0x00; //Batt_Heater_Mail_Send_NG
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VCM message, containing info if battery should sleep or stay awake
|
// Send 100ms CAN Message
|
||||||
ESP32Can.CANWriteFrame(&LEAF_50B); // HCM_WakeUpSleepCommand == 11b == WakeUp, and CANMASK = 1
|
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
|
||||||
|
previousMillis100 = currentMillis;
|
||||||
|
|
||||||
LEAF_50C.data.u8[3] = mprun100;
|
//When battery requests heating pack status change, ack this
|
||||||
switch (mprun100) {
|
if (Batt_Heater_Mail_Send_Request) {
|
||||||
case 0:
|
LEAF_50B.data.u8[6] = 0x20; //Batt_Heater_Mail_Send_OK
|
||||||
LEAF_50C.data.u8[4] = 0x5D;
|
} else {
|
||||||
LEAF_50C.data.u8[5] = 0xC8;
|
LEAF_50B.data.u8[6] = 0x00; //Batt_Heater_Mail_Send_NG
|
||||||
break;
|
}
|
||||||
case 1:
|
|
||||||
LEAF_50C.data.u8[4] = 0xB2;
|
|
||||||
LEAF_50C.data.u8[5] = 0x31;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
LEAF_50C.data.u8[4] = 0x5D;
|
|
||||||
LEAF_50C.data.u8[5] = 0x63;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
LEAF_50C.data.u8[4] = 0xB2;
|
|
||||||
LEAF_50C.data.u8[5] = 0x9A;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ESP32Can.CANWriteFrame(&LEAF_50C);
|
|
||||||
|
|
||||||
mprun100 = (mprun100 + 1) % 4; // mprun100 cycles between 0-1-2-3-0-1...
|
// VCM message, containing info if battery should sleep or stay awake
|
||||||
}
|
ESP32Can.CANWriteFrame(&LEAF_50B); // HCM_WakeUpSleepCommand == 11b == WakeUp, and CANMASK = 1
|
||||||
|
|
||||||
//Send 10s CAN messages
|
LEAF_50C.data.u8[3] = mprun100;
|
||||||
if (currentMillis - previousMillis10s >= INTERVAL_10_S) {
|
switch (mprun100) {
|
||||||
previousMillis10s = currentMillis;
|
case 0:
|
||||||
|
LEAF_50C.data.u8[4] = 0x5D;
|
||||||
|
LEAF_50C.data.u8[5] = 0xC8;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
LEAF_50C.data.u8[4] = 0xB2;
|
||||||
|
LEAF_50C.data.u8[5] = 0x31;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
LEAF_50C.data.u8[4] = 0x5D;
|
||||||
|
LEAF_50C.data.u8[5] = 0x63;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
LEAF_50C.data.u8[4] = 0xB2;
|
||||||
|
LEAF_50C.data.u8[5] = 0x9A;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ESP32Can.CANWriteFrame(&LEAF_50C);
|
||||||
|
|
||||||
//Every 10s, ask diagnostic data from the battery. Don't ask if someone is already polling on the bus (Leafspy?)
|
mprun100 = (mprun100 + 1) % 4; // mprun100 cycles between 0-1-2-3-0-1...
|
||||||
if (!stop_battery_query) {
|
|
||||||
group = (group == 1) ? 2 : (group == 2) ? 4 : 1;
|
|
||||||
// Cycle between group 1, 2, and 4 using ternary operation
|
|
||||||
LEAF_GROUP_REQUEST.data.u8[2] = group;
|
|
||||||
ESP32Can.CANWriteFrame(&LEAF_GROUP_REQUEST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hold_off_with_polling_10seconds > 0) {
|
//Send 10s CAN messages
|
||||||
hold_off_with_polling_10seconds--;
|
if (currentMillis - previousMillis10s >= INTERVAL_10_S) {
|
||||||
} else {
|
previousMillis10s = currentMillis;
|
||||||
stop_battery_query = false;
|
|
||||||
|
//Every 10s, ask diagnostic data from the battery. Don't ask if someone is already polling on the bus (Leafspy?)
|
||||||
|
if (!stop_battery_query) {
|
||||||
|
group = (group == 1) ? 2 : (group == 2) ? 4 : 1;
|
||||||
|
// Cycle between group 1, 2, and 4 using ternary operation
|
||||||
|
LEAF_GROUP_REQUEST.data.u8[2] = group;
|
||||||
|
ESP32Can.CANWriteFrame(&LEAF_GROUP_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hold_off_with_polling_10seconds > 0) {
|
||||||
|
hold_off_with_polling_10seconds--;
|
||||||
|
} else {
|
||||||
|
stop_battery_query = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,8 @@ static void publish_cell_voltages(void) {
|
||||||
doc.clear(); // clear after sending autoconfig
|
doc.clear(); // clear after sending autoconfig
|
||||||
} else {
|
} else {
|
||||||
// If cell voltages haven't been populated...
|
// If cell voltages haven't been populated...
|
||||||
if (datalayer.battery.info.number_of_cells == 0u) {
|
if (datalayer.battery.info.number_of_cells == 0u ||
|
||||||
|
datalayer.battery.status.cell_voltages_mV[datalayer.battery.info.number_of_cells - 1] == 0u) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,10 +159,13 @@ static void publish_common_info(void) {
|
||||||
doc["temperature_max"] = ((float)((int16_t)datalayer.battery.status.temperature_max_dC)) / 10.0;
|
doc["temperature_max"] = ((float)((int16_t)datalayer.battery.status.temperature_max_dC)) / 10.0;
|
||||||
doc["stat_batt_power"] = ((float)((int32_t)datalayer.battery.status.active_power_W));
|
doc["stat_batt_power"] = ((float)((int32_t)datalayer.battery.status.active_power_W));
|
||||||
doc["battery_current"] = ((float)((int16_t)datalayer.battery.status.current_dA)) / 10.0;
|
doc["battery_current"] = ((float)((int16_t)datalayer.battery.status.current_dA)) / 10.0;
|
||||||
doc["cell_max_voltage"] = ((float)datalayer.battery.status.cell_max_voltage_mV) / 1000.0;
|
|
||||||
doc["cell_min_voltage"] = ((float)datalayer.battery.status.cell_min_voltage_mV) / 1000.0;
|
|
||||||
doc["battery_voltage"] = ((float)datalayer.battery.status.voltage_dV) / 10.0;
|
doc["battery_voltage"] = ((float)datalayer.battery.status.voltage_dV) / 10.0;
|
||||||
|
// publish only if cell voltages have been populated...
|
||||||
|
if (datalayer.battery.info.number_of_cells != 0u &&
|
||||||
|
datalayer.battery.status.cell_voltages_mV[datalayer.battery.info.number_of_cells - 1] != 0u) {
|
||||||
|
doc["cell_max_voltage"] = ((float)datalayer.battery.status.cell_max_voltage_mV) / 1000.0;
|
||||||
|
doc["cell_min_voltage"] = ((float)datalayer.battery.status.cell_min_voltage_mV) / 1000.0;
|
||||||
|
}
|
||||||
serializeJson(doc, mqtt_msg);
|
serializeJson(doc, mqtt_msg);
|
||||||
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
|
|
|
@ -159,6 +159,7 @@ void init_events(void) {
|
||||||
events.entries[EVENT_PRECHARGE_FAILURE].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_PRECHARGE_FAILURE].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_INTERNAL_OPEN_FAULT].level = EVENT_LEVEL_ERROR;
|
events.entries[EVENT_INTERNAL_OPEN_FAULT].level = EVENT_LEVEL_ERROR;
|
||||||
events.entries[EVENT_INVERTER_OPEN_CONTACTOR].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_INVERTER_OPEN_CONTACTOR].level = EVENT_LEVEL_INFO;
|
||||||
|
events.entries[EVENT_MODBUS_INVERTER_MISSING].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_ERROR_OPEN_CONTACTOR].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_ERROR_OPEN_CONTACTOR].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_CELL_UNDER_VOLTAGE].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_OVER_VOLTAGE].level = EVENT_LEVEL_ERROR;
|
||||||
|
@ -265,6 +266,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
|
||||||
case EVENT_ERROR_OPEN_CONTACTOR:
|
case EVENT_ERROR_OPEN_CONTACTOR:
|
||||||
return "Info: Too much time spent in error state. Opening contactors, not safe to continue charging. "
|
return "Info: Too much time spent in error state. Opening contactors, not safe to continue charging. "
|
||||||
"Check other error code for reason!";
|
"Check other error code for reason!";
|
||||||
|
case EVENT_MODBUS_INVERTER_MISSING:
|
||||||
|
return "Info: Modbus inverter has not sent any data. Inspect communication wiring!";
|
||||||
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:
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
XX(EVENT_PRECHARGE_FAILURE) \
|
XX(EVENT_PRECHARGE_FAILURE) \
|
||||||
XX(EVENT_INTERNAL_OPEN_FAULT) \
|
XX(EVENT_INTERNAL_OPEN_FAULT) \
|
||||||
XX(EVENT_INVERTER_OPEN_CONTACTOR) \
|
XX(EVENT_INVERTER_OPEN_CONTACTOR) \
|
||||||
|
XX(EVENT_MODBUS_INVERTER_MISSING) \
|
||||||
XX(EVENT_ERROR_OPEN_CONTACTOR) \
|
XX(EVENT_ERROR_OPEN_CONTACTOR) \
|
||||||
XX(EVENT_CELL_UNDER_VOLTAGE) \
|
XX(EVENT_CELL_UNDER_VOLTAGE) \
|
||||||
XX(EVENT_CELL_OVER_VOLTAGE) \
|
XX(EVENT_CELL_OVER_VOLTAGE) \
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include "../../datalayer/datalayer.h"
|
#include "../../datalayer/datalayer.h"
|
||||||
|
|
||||||
String cellmonitor_processor(const String& var) {
|
String cellmonitor_processor(const String& var) {
|
||||||
if (var == "ABC") {
|
if (var == "X") {
|
||||||
String content = "";
|
String content = "";
|
||||||
// Page format
|
// Page format
|
||||||
content += "<style>";
|
content += "<style>";
|
||||||
|
@ -37,7 +37,7 @@ String cellmonitor_processor(const String& var) {
|
||||||
content += "<script>";
|
content += "<script>";
|
||||||
// Populate cell data
|
// Populate cell data
|
||||||
content += "const data = [";
|
content += "const data = [";
|
||||||
for (uint8_t i = 0u; i < MAX_AMOUNT_CELLS; i++) {
|
for (uint8_t i = 0u; i < datalayer.battery.info.number_of_cells; i++) {
|
||||||
if (datalayer.battery.status.cell_voltages_mV[i] == 0) {
|
if (datalayer.battery.status.cell_voltages_mV[i] == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ const char EVENTS_HTML_END[] = R"=====(
|
||||||
)=====";
|
)=====";
|
||||||
|
|
||||||
String events_processor(const String& var) {
|
String events_processor(const String& var) {
|
||||||
if (var == "ABC") {
|
if (var == "X") {
|
||||||
String content = "";
|
String content = "";
|
||||||
content.reserve(5000);
|
content.reserve(5000);
|
||||||
// Page format
|
// Page format
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include "../../datalayer/datalayer.h"
|
#include "../../datalayer/datalayer.h"
|
||||||
|
|
||||||
String settings_processor(const String& var) {
|
String settings_processor(const String& var) {
|
||||||
if (var == "ABC") {
|
if (var == "X") {
|
||||||
String content = "";
|
String content = "";
|
||||||
//Page format
|
//Page format
|
||||||
content += "<style>";
|
content += "<style>";
|
||||||
|
|
|
@ -448,6 +448,9 @@ String processor(const String& var) {
|
||||||
#ifdef KIA_E_GMP_BATTERY
|
#ifdef KIA_E_GMP_BATTERY
|
||||||
content += "Kia/Hyundai EGMP platform";
|
content += "Kia/Hyundai EGMP platform";
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef MG_5_BATTERY
|
||||||
|
content += "MG 5";
|
||||||
|
#endif
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
#ifdef NISSAN_LEAF_BATTERY
|
||||||
content += "Nissan LEAF";
|
content += "Nissan LEAF";
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
#ifdef BYD_MODBUS
|
#ifdef BYD_MODBUS
|
||||||
#include "../datalayer/datalayer.h"
|
#include "../datalayer/datalayer.h"
|
||||||
|
#include "../devboard/utils/events.h"
|
||||||
#include "BYD-MODBUS.h"
|
#include "BYD-MODBUS.h"
|
||||||
|
|
||||||
// For modbus register definitions, see https://gitlab.com/pelle8/inverter_resources/-/blob/main/byd_registers_modbus_rtu.md
|
// For modbus register definitions, see https://gitlab.com/pelle8/inverter_resources/-/blob/main/byd_registers_modbus_rtu.md
|
||||||
|
|
||||||
static uint8_t bms_char_dis_status = STANDBY;
|
#define HISTORY_LENGTH 3 // Amount of samples(minutes) that needs to match for register to be considered stale
|
||||||
|
static unsigned long previousMillis60s = 0; // will store last time a 60s event occured
|
||||||
static uint32_t user_configured_max_discharge_W = 0;
|
static uint32_t user_configured_max_discharge_W = 0;
|
||||||
static uint32_t user_configured_max_charge_W = 0;
|
static uint32_t user_configured_max_charge_W = 0;
|
||||||
static uint32_t max_discharge_W = 0;
|
static uint32_t max_discharge_W = 0;
|
||||||
static uint32_t max_charge_W = 0;
|
static uint32_t max_charge_W = 0;
|
||||||
|
static uint16_t register_401_history[HISTORY_LENGTH] = {0};
|
||||||
|
static uint8_t history_index = 0;
|
||||||
|
static uint8_t bms_char_dis_status = STANDBY;
|
||||||
|
static bool all_401_values_equal = false;
|
||||||
|
|
||||||
void update_modbus_registers_inverter() {
|
void update_modbus_registers_inverter() {
|
||||||
verify_temperature_modbus();
|
verify_temperature_modbus();
|
||||||
|
verify_inverter_modbus();
|
||||||
handle_update_data_modbusp201_byd();
|
handle_update_data_modbusp201_byd();
|
||||||
handle_update_data_modbusp301_byd();
|
handle_update_data_modbusp301_byd();
|
||||||
}
|
}
|
||||||
|
@ -43,7 +50,7 @@ void handle_static_data_modbus_byd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_update_data_modbusp201_byd() {
|
void handle_update_data_modbusp201_byd() {
|
||||||
mbPV[202] = std::min(datalayer.battery.info.total_capacity_Wh, 60000u); //Cap capacity to 60kWh if needed
|
mbPV[202] = std::min(datalayer.battery.info.total_capacity_Wh, static_cast<uint32_t>(60000u)); //Cap to 60kWh
|
||||||
mbPV[205] = (datalayer.battery.info.max_design_voltage_dV); // Max Voltage, if higher Gen24 forces discharge
|
mbPV[205] = (datalayer.battery.info.max_design_voltage_dV); // Max Voltage, if higher Gen24 forces discharge
|
||||||
mbPV[206] = (datalayer.battery.info.min_design_voltage_dV); // Min Voltage, if lower Gen24 disables battery
|
mbPV[206] = (datalayer.battery.info.min_design_voltage_dV); // Min Voltage, if lower Gen24 disables battery
|
||||||
}
|
}
|
||||||
|
@ -76,10 +83,10 @@ void handle_update_data_modbusp301_byd() {
|
||||||
mbPV[300] = datalayer.battery.status.bms_status;
|
mbPV[300] = datalayer.battery.status.bms_status;
|
||||||
mbPV[302] = 128 + bms_char_dis_status;
|
mbPV[302] = 128 + bms_char_dis_status;
|
||||||
mbPV[303] = datalayer.battery.status.reported_soc;
|
mbPV[303] = datalayer.battery.status.reported_soc;
|
||||||
mbPV[304] = std::min(datalayer.battery.info.total_capacity_Wh, 60000u); //Cap capacity to 60kWh if needed
|
mbPV[304] = std::min(datalayer.battery.info.total_capacity_Wh, static_cast<uint32_t>(60000u)); //Cap to 60kWh
|
||||||
mbPV[305] = std::min(datalayer.battery.status.remaining_capacity_Wh, 60000u); //Cap capacity to 60kWh if needed
|
mbPV[305] = std::min(datalayer.battery.status.remaining_capacity_Wh, static_cast<uint32_t>(60000u)); //Cap to 60kWh
|
||||||
mbPV[306] = std::min(max_discharge_W, 30000u); //Cap to 30000 if exceeding
|
mbPV[306] = std::min(max_discharge_W, static_cast<uint32_t>(30000u)); //Cap to 30000 if exceeding
|
||||||
mbPV[307] = std::min(max_charge_W, 30000u); //Cap to 30000 if exceeding
|
mbPV[307] = std::min(max_charge_W, static_cast<uint32_t>(30000u)); //Cap to 30000 if exceeding
|
||||||
mbPV[310] = datalayer.battery.status.voltage_dV;
|
mbPV[310] = datalayer.battery.status.voltage_dV;
|
||||||
mbPV[312] = datalayer.battery.status.temperature_min_dC;
|
mbPV[312] = datalayer.battery.status.temperature_min_dC;
|
||||||
mbPV[313] = datalayer.battery.status.temperature_max_dC;
|
mbPV[313] = datalayer.battery.status.temperature_max_dC;
|
||||||
|
@ -107,4 +114,32 @@ void verify_temperature_modbus() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void verify_inverter_modbus() {
|
||||||
|
// Every 60 seconds, the Gen24 writes to this 401 register, alternating between 00FF and FF00.
|
||||||
|
// We sample the register every 60 seconds. Incase the value has not changed for 3 minutes, we raise an event
|
||||||
|
unsigned long currentMillis = millis();
|
||||||
|
|
||||||
|
if (currentMillis - previousMillis60s >= INTERVAL_60_S) {
|
||||||
|
previousMillis60s = currentMillis;
|
||||||
|
|
||||||
|
all_401_values_equal = true;
|
||||||
|
for (int i = 0; i < HISTORY_LENGTH; ++i) {
|
||||||
|
if (register_401_history[i] != mbPV[401]) {
|
||||||
|
all_401_values_equal = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (all_401_values_equal) {
|
||||||
|
set_event(EVENT_MODBUS_INVERTER_MISSING, 0);
|
||||||
|
} else {
|
||||||
|
clear_event(EVENT_MODBUS_INVERTER_MISSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update history
|
||||||
|
register_401_history[history_index] = mbPV[401];
|
||||||
|
history_index = (history_index + 1) % HISTORY_LENGTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -11,6 +11,7 @@ extern uint16_t mbPV[MB_RTU_NUM_VALUES];
|
||||||
|
|
||||||
void handle_static_data_modbus_byd();
|
void handle_static_data_modbus_byd();
|
||||||
void verify_temperature_modbus();
|
void verify_temperature_modbus();
|
||||||
|
void verify_inverter_modbus();
|
||||||
void handle_update_data_modbusp201_byd();
|
void handle_update_data_modbusp201_byd();
|
||||||
void handle_update_data_modbusp301_byd();
|
void handle_update_data_modbusp301_byd();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -185,7 +185,7 @@ void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
|
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
|
||||||
ets_printf("ERROR: Too many messages queued\n");
|
//ets_printf("ERROR: Too many messages queued\n");
|
||||||
delete dataMessage;
|
delete dataMessage;
|
||||||
} else {
|
} else {
|
||||||
_messageQueue.add(dataMessage);
|
_messageQueue.add(dataMessage);
|
||||||
|
|
|
@ -548,7 +548,7 @@ void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES){
|
if(_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES){
|
||||||
ets_printf("ERROR: Too many messages queued\n");
|
//ets_printf("ERROR: Too many messages queued\n");
|
||||||
delete dataMessage;
|
delete dataMessage;
|
||||||
} else {
|
} else {
|
||||||
_messageQueue.add(dataMessage);
|
_messageQueue.add(dataMessage);
|
||||||
|
@ -829,7 +829,7 @@ void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer)
|
||||||
|
|
||||||
IPAddress AsyncWebSocketClient::remoteIP() {
|
IPAddress AsyncWebSocketClient::remoteIP() {
|
||||||
if(!_client) {
|
if(!_client) {
|
||||||
return IPAddress(0U);
|
return IPAddress(static_cast<uint32_t>(0U));
|
||||||
}
|
}
|
||||||
return _client->remoteIP();
|
return _client->remoteIP();
|
||||||
}
|
}
|
||||||
|
@ -1259,9 +1259,9 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket
|
||||||
(String&)key += WS_STR_UUID;
|
(String&)key += WS_STR_UUID;
|
||||||
mbedtls_sha1_context ctx;
|
mbedtls_sha1_context ctx;
|
||||||
mbedtls_sha1_init(&ctx);
|
mbedtls_sha1_init(&ctx);
|
||||||
mbedtls_sha1_starts_ret(&ctx);
|
mbedtls_sha1_starts(&ctx);
|
||||||
mbedtls_sha1_update_ret(&ctx, (const unsigned char*)key.c_str(), key.length());
|
mbedtls_sha1_update(&ctx, (const unsigned char*)key.c_str(), key.length());
|
||||||
mbedtls_sha1_finish_ret(&ctx, hash);
|
mbedtls_sha1_finish(&ctx, hash);
|
||||||
mbedtls_sha1_free(&ctx);
|
mbedtls_sha1_free(&ctx);
|
||||||
#endif
|
#endif
|
||||||
base64_encodestate _state;
|
base64_encodestate _state;
|
||||||
|
|
|
@ -71,9 +71,9 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo
|
||||||
memset(_buf, 0x00, 16);
|
memset(_buf, 0x00, 16);
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
mbedtls_md5_init(&_ctx);
|
mbedtls_md5_init(&_ctx);
|
||||||
mbedtls_md5_starts_ret(&_ctx);
|
mbedtls_md5_starts(&_ctx);
|
||||||
mbedtls_md5_update_ret(&_ctx, data, len);
|
mbedtls_md5_update(&_ctx, data, len);
|
||||||
mbedtls_md5_finish_ret(&_ctx, _buf);
|
mbedtls_md5_finish(&_ctx, _buf);
|
||||||
#else
|
#else
|
||||||
MD5Init(&_ctx);
|
MD5Init(&_ctx);
|
||||||
MD5Update(&_ctx, data, len);
|
MD5Update(&_ctx, data, len);
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
|
|
||||||
#include "esp_intr.h"
|
#include "esp_intr_alloc.h" // Renamed when migrating ESP32 2.x -> 3.x
|
||||||
#include "soc/dport_reg.h"
|
#include "soc/dport_reg.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
@ -43,6 +43,10 @@
|
||||||
#include "can_regdef.h"
|
#include "can_regdef.h"
|
||||||
#include "CAN_config.h"
|
#include "CAN_config.h"
|
||||||
|
|
||||||
|
#define TWAI_TX_IDX 123 // TODO: Are these OK?
|
||||||
|
// not sure what file is needed now, maybe "soc/gpio_sig_map.h" but using hard coded values for now
|
||||||
|
#define TWAI_RX_IDX 94 // TODO: Are these OK?
|
||||||
|
|
||||||
// CAN Filter - no acceptance filter
|
// CAN Filter - no acceptance filter
|
||||||
static CAN_filter_t __filter = { Dual_Mode, 0, 0, 0, 0, 0Xff, 0Xff, 0Xff, 0Xff };
|
static CAN_filter_t __filter = { Dual_Mode, 0, 0, 0, 0, 0Xff, 0Xff, 0Xff, 0Xff };
|
||||||
|
|
||||||
|
@ -177,12 +181,12 @@ int CAN_init() {
|
||||||
// configure TX pin
|
// configure TX pin
|
||||||
gpio_set_level(CAN_cfg.tx_pin_id, 1);
|
gpio_set_level(CAN_cfg.tx_pin_id, 1);
|
||||||
gpio_set_direction(CAN_cfg.tx_pin_id, GPIO_MODE_OUTPUT);
|
gpio_set_direction(CAN_cfg.tx_pin_id, GPIO_MODE_OUTPUT);
|
||||||
gpio_matrix_out(CAN_cfg.tx_pin_id, CAN_TX_IDX, 0, 0);
|
gpio_matrix_out(CAN_cfg.tx_pin_id, TWAI_TX_IDX, 0, 0);
|
||||||
gpio_pad_select_gpio(CAN_cfg.tx_pin_id);
|
gpio_pad_select_gpio(CAN_cfg.tx_pin_id);
|
||||||
|
|
||||||
// configure RX pin
|
// configure RX pin
|
||||||
gpio_set_direction(CAN_cfg.rx_pin_id, GPIO_MODE_INPUT);
|
gpio_set_direction(CAN_cfg.rx_pin_id, GPIO_MODE_INPUT);
|
||||||
gpio_matrix_in(CAN_cfg.rx_pin_id, CAN_RX_IDX, 0);
|
gpio_matrix_in(CAN_cfg.rx_pin_id, TWAI_RX_IDX, 0);
|
||||||
gpio_pad_select_gpio(CAN_cfg.rx_pin_id);
|
gpio_pad_select_gpio(CAN_cfg.rx_pin_id);
|
||||||
|
|
||||||
// set to PELICAN mode
|
// set to PELICAN mode
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue