Skip to content

Commit

Permalink
Merge pull request #103 from dalathegreat/feature/double-lilygo
Browse files Browse the repository at this point in the history
New feature: Double LilyGo!
  • Loading branch information
rjsc authored Nov 22, 2023
2 parents df07076 + 6fd2ca5 commit f1ac772
Show file tree
Hide file tree
Showing 11 changed files with 1,067 additions and 1 deletion.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ This video explains all the above mentioned steps:
https://youtu.be/_mH2AjnAjDk

## Dependencies 📖
This code uses two libraries, ESP32-Arduino-CAN (https://github.com/miwagner/ESP32-Arduino-CAN/) slightly modified for this usecase, and the eModbus library (https://github.com/eModbus/eModbus). Both these are already located in the Software folder for an easy start.
This code uses the following libraries, already located in the lib folder for an easy start:
- ESP32-Arduino-CAN (https://github.com/miwagner/ESP32-Arduino-CAN/) slightly modified for this usecase
- eModbus library (https://github.com/eModbus/eModbus)
- Adafruit Neopixel (https://github.com/adafruit/Adafruit_NeoPixel)
- mackelec SerialDataLink (https://github.com/mackelec/SerialDataLink)
- pierremolinaro acan2515 (https://github.com/pierremolinaro/acan2515)

It is also based on the info found in the following excellent repositories/websites:
- https://gitlab.com/pelle8/gen24
Expand Down
36 changes: 36 additions & 0 deletions Software/Software.ino
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
#include "src/lib/eModbus-eModbus/Logging.h"
#include "src/lib/eModbus-eModbus/ModbusServerRTU.h"
#include "src/lib/eModbus-eModbus/scripts/mbServerFCs.h"
#include "src/lib/mackelec-SerialDataLink/SerialDataLink.h"
#include "src/lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"

// Interval settings
int intervalUpdateValues = 4800; // Interval at which to update inverter values / Modbus registers
const int interval1 = 1; // Interval for 1ms tasks
const int interval10 = 10; // Interval for 10ms tasks
unsigned long previousMillis1ms = 0;
unsigned long previousMillis10ms = 50;
unsigned long previousMillisUpdateVal = 0;

Expand Down Expand Up @@ -128,6 +131,9 @@ void loop() {
#ifdef DUAL_CAN
receive_can2();
#endif
#ifdef SERIAL_LINK_TRANSMITTER_INVERTER
receive_serial();
#endif

// Process
if (millis() - previousMillis10ms >= interval10) // Every 10ms
Expand All @@ -150,6 +156,9 @@ void loop() {
#ifdef DUAL_CAN
send_can2();
#endif
#ifdef SERIAL_LINK_RECEIVER_FROM_BATTERY
send_serial();
#endif
}

// Initialization functions
Expand Down Expand Up @@ -217,6 +226,13 @@ void init_modbus() {
pinMode(PIN_5V_EN, OUTPUT);
digitalWrite(PIN_5V_EN, HIGH);

#if defined(SERIAL_LINK_RECEIVER_FROM_BATTERY) || defined(SERIAL_LINK_TRANSMITTER_INVERTER)
Serial2.begin(9600); // If the Modbus RTU port will be used for serial link
#if defined(BYD_MODBUS) || defined(LUNA2000_MODBUS)
#error Modbus pins cannot be used for Serial and Modbus at the same time!
#endif
#endif

#ifdef BYD_MODBUS
// Init Static data to the RTU Modbus
handle_static_data_modbus_byd();
Expand Down Expand Up @@ -386,6 +402,26 @@ void send_can() {
#endif
}

#ifdef SERIAL_LINK_RECEIVER_FROM_BATTERY
void send_serial() {
static unsigned long currentMillis = millis();
if (currentMillis - previousMillis1ms >= interval1) {
previousMillis1ms = currentMillis;
manageSerialLinkReceiver();
}
}
#endif

#ifdef SERIAL_LINK_TRANSMITTER_INVERTER
void receive_serial() {
static unsigned long currentMillis = millis();
if (currentMillis - previousMillis1ms >= interval1) {
previousMillis1ms = currentMillis;
manageSerialLinkTransmitter();
}
}
#endif

#ifdef DUAL_CAN
void receive_can2() { // This function is similar to receive_can, but just takes care of inverters in the 2nd bus.
// Depending on which inverter is selected, we forward this to their respective CAN routines
Expand Down
2 changes: 2 additions & 0 deletions Software/USER_SETTINGS.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,7 @@
//#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence
//#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM logic for contactors, which lower power consumption and heat generation
//#define DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 controller (Needed for FoxESS inverters)
//#define SERIAL_LINK_RECEIVER_FROM_BATTERY //Enable this line to send battery data over Modbus pins to another Lilygo (This LilyGo interfaces with battery)
//#define SERIAL_LINK_TRANSMITTER_INVERTER //Enable this line to receive battery data over Modbus pins from another Lilygo (This LilyGo interfaces with inverter)

#endif
5 changes: 5 additions & 0 deletions Software/src/battery/BATTERIES.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,9 @@
#ifdef TEST_FAKE_BATTERY
#include "TEST-FAKE-BATTERY.h" //See this file for more Fake battery settings
#endif

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

#endif
115 changes: 115 additions & 0 deletions Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp

#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h"

//#define INVERTER_SEND_NUM_VARIABLES 3 //--- comment out if nothing to send
#define INVERTER_RECV_NUM_VARIABLES 16

#ifdef INVERTER_SEND_NUM_VARIABLES
const uint8_t sendingNumVariables = INVERTER_SEND_NUM_VARIABLES;
#else
const uint8_t sendingNumVariables = 0;
#endif

// txid,rxid, num_send,num_recv
SerialDataLink dataLinkReceive(Serial2, 0, 0x01, sendingNumVariables,
INVERTER_RECV_NUM_VARIABLES); // ...

void __getData() {
SOC = (uint16_t)dataLinkReceive.getReceivedData(0);
StateOfHealth = (uint16_t)dataLinkReceive.getReceivedData(1);
battery_voltage = (uint16_t)dataLinkReceive.getReceivedData(2);
battery_current = (uint16_t)dataLinkReceive.getReceivedData(3);
capacity_Wh = (uint16_t)dataLinkReceive.getReceivedData(4);
remaining_capacity_Wh = (uint16_t)dataLinkReceive.getReceivedData(5);
max_target_discharge_power = (uint16_t)dataLinkReceive.getReceivedData(6);
max_target_charge_power = (uint16_t)dataLinkReceive.getReceivedData(7);
bms_status = (uint16_t)dataLinkReceive.getReceivedData(8);
bms_char_dis_status = (uint16_t)dataLinkReceive.getReceivedData(9);
stat_batt_power = (uint16_t)dataLinkReceive.getReceivedData(10);
temperature_min = (uint16_t)dataLinkReceive.getReceivedData(11);
temperature_max = (uint16_t)dataLinkReceive.getReceivedData(12);
cell_max_voltage = (uint16_t)dataLinkReceive.getReceivedData(13);
cell_min_voltage = (uint16_t)dataLinkReceive.getReceivedData(14);
batteryAllowsContactorClosing = (uint16_t)dataLinkReceive.getReceivedData(15);
}

void updateData() {
// --- update with fresh data
/*
dataLinkReceive.updateData(0,var1);
dataLinkReceive.updateData(1,var2);
dataLinkReceive.updateData(2,var3);
*/
}

/*
* @ 9600bps, assume void manageSerialLinkReceiver()
* is called every 1mS
*/

void manageSerialLinkReceiver() {
static bool lasterror = false;
static unsigned long lastGood;
static uint16_t lastGoodMaxCharge;
static uint16_t lastGoodMaxDischarge;
static bool initLink = false;

unsigned long currentTime = millis();

if (!initLink) {
initLink = true;
// sends variables every 5000mS even if no change
dataLinkReceive.setUpdateInterval(5000);
#ifdef SERIALDATALINK_MUTEACK
dataLinkReceive.muteACK(true);
#endif
}
dataLinkReceive.run();
bool readError = dataLinkReceive.checkReadError(true); // check for error & clear error flag
LEDcolor = GREEN;
if (readError) {
LEDcolor = RED;
bms_status = 4; //FAULT
Serial.print(currentTime);
Serial.println(" - ERROR: Serial Data Link - Read Error");
lasterror = true;
} else {
if (lasterror) {
lasterror = false;
Serial.print(currentTime);
Serial.println(" - RECOVERY: Serial Data Link - Read GOOD");
}
lastGood = currentTime;
}
if (dataLinkReceive.checkNewData(true)) // true = clear Flag
{
__getData();
lastGoodMaxCharge = max_target_charge_power;
lastGoodMaxDischarge = max_target_discharge_power;
}

unsigned long minutesLost = (currentTime - lastGood) / 60000UL;
;
if (minutesLost > 0 && lastGood > 0) {
// lose 25% each minute of data loss
if (minutesLost < 4) {
max_target_charge_power = (lastGoodMaxCharge * (4 - minutesLost)) / 4;
max_target_discharge_power = (lastGoodMaxDischarge * (4 - minutesLost)) / 4;
} else {
max_target_charge_power = 0;
max_target_discharge_power = 0;
}
}

static unsigned long updateTime = 0;

#ifdef INVERTER_SEND_NUM_VARIABLES
if (currentTime - updateTime > 100) {
updateTime = currentTime;
dataLinkReceive.run();
bool sendError = dataLinkReceive.checkTransmissionError(true); // check for error & clear error flag
updateData();
}
#endif
}
38 changes: 38 additions & 0 deletions Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SERIAL-LINK-RECEIVER-FROM-BATTERY.h

#ifndef SERIAL_LINK_RECEIVER_FROM_BATTERY_H
#define SERIAL_LINK_RECEIVER_FROM_BATTERY_H

#include <Arduino.h>
#include "../../USER_SETTINGS.h"
#include "../devboard/config.h" // Needed for LED defines
#include "../lib/mackelec-SerialDataLink/SerialDataLink.h"

// https://github.com/mackelec/SerialDataLink

#define ABSOLUTE_MAX_VOLTAGE \
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled

// These parameters need to be mapped for the inverter
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
extern uint16_t capacity_Wh; //Wh, 0-60000
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
extern uint16_t max_target_discharge_power; //W, 0-60000
extern uint16_t max_target_charge_power; //W, 0-60000
extern uint16_t bms_status; //Enum, 0-5
extern uint16_t bms_char_dis_status; //Enum, 0-2
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
extern uint16_t cell_max_voltage; //mV, 0-4350
extern uint16_t cell_min_voltage; //mV, 0-4350
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
extern uint8_t LEDcolor; //Enum, 0-10

void manageSerialLinkReceiver();

#endif
4 changes: 4 additions & 0 deletions Software/src/inverter/INVERTERS.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@
#include "SOLAX-CAN.h"
#endif

#ifdef SERIAL_LINK_TRANSMITTER_INVERTER
#include "SERIAL-LINK-TRANSMITTER-INVERTER.h"
#endif

#endif
85 changes: 85 additions & 0 deletions Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//SERIAL-LINK-TRANSMITTER-INVERTER.cpp

#include "SERIAL-LINK-TRANSMITTER-INVERTER.h"

/*
* SerialDataLink
* txid=1, rxid=0 gives this the startup transmit priority_queue
* Will transmit max 16 int variable - receive none
*/

#define BATTERY_SEND_NUM_VARIABLES 16
//#define BATTERY_RECV_NUM_VARIABLES 3 //--- comment out if nothing to receive

#ifdef BATTERY_RECV_NUM_VARIABLES
const uint8_t receivingNumVariables = BATTERY_RECV_NUM_VARIABLES;
#else
const uint8_t receivingNumVariables = 0;
#endif

// txid,rxid,num_tx,num_rx
SerialDataLink dataLinkTransmit(Serial2, 0x01, 0, BATTERY_SEND_NUM_VARIABLES, receivingNumVariables);

void _getData() {
/*
var1 = dataLinkTransmit.getReceivedData(0);
var2 = dataLinkTransmit.getReceivedData(1);
var3 = dataLinkTransmit.getReceivedData(2);
*/
}

void manageSerialLinkTransmitter() {
static bool initLink = false;
static unsigned long updateTime = 0;
static bool lasterror = false;

dataLinkTransmit.run();

#ifdef BATTERY_RECV_NUM_VARIABLES
bool readError = dataLinkTransmit.checkReadError(true); // check for error & clear error flag
if (dataLinkTransmit.checkNewData(true)) // true = clear Flag
{
_getData();
}
#endif

if (millis() - updateTime > 100) {
updateTime = millis();
if (!initLink) {
initLink = true;
// sends variables every 5000mS even if no change
dataLinkTransmit.setUpdateInterval(5000);
}
bool sendError = dataLinkTransmit.checkTransmissionError(true);
LEDcolor = GREEN;
if (sendError) {
LEDcolor = RED;
Serial.print(millis());
Serial.println(" - ERROR: Serial Data Link - SEND Error");
lasterror = true;
} else {
if (lasterror) {
lasterror = false;
Serial.print(millis());
Serial.println(" - RECOVERY: Serial Data Link - Send GOOD");
}
}

dataLinkTransmit.updateData(0, SOC);
dataLinkTransmit.updateData(1, StateOfHealth);
dataLinkTransmit.updateData(2, battery_voltage);
dataLinkTransmit.updateData(3, battery_current);
dataLinkTransmit.updateData(4, capacity_Wh);
dataLinkTransmit.updateData(5, remaining_capacity_Wh);
dataLinkTransmit.updateData(6, max_target_discharge_power);
dataLinkTransmit.updateData(7, max_target_charge_power);
dataLinkTransmit.updateData(8, bms_status);
dataLinkTransmit.updateData(9, bms_char_dis_status);
dataLinkTransmit.updateData(10, stat_batt_power);
dataLinkTransmit.updateData(11, temperature_min);
dataLinkTransmit.updateData(12, temperature_max);
dataLinkTransmit.updateData(13, cell_max_voltage);
dataLinkTransmit.updateData(14, cell_min_voltage);
dataLinkTransmit.updateData(15, batteryAllowsContactorClosing);
}
}
32 changes: 32 additions & 0 deletions Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//SERIAL-LINK-TRANSMITTER-INVERTER.h

#ifndef SERIAL_LINK_TRANSMITTER_INVERTER_H
#define SERIAL_LINK_TRANSMITTER_INVERTER_H

#include <Arduino.h>
#include "../../USER_SETTINGS.h"
#include "../devboard/config.h" // Needed for LED defines
#include "../lib/mackelec-SerialDataLink/SerialDataLink.h"

// These parameters need to be mapped for the inverter
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
extern uint16_t capacity_Wh; //Wh, 0-60000
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
extern uint16_t max_target_discharge_power; //W, 0-60000
extern uint16_t max_target_charge_power; //W, 0-60000
extern uint16_t bms_status; //Enum, 0-5
extern uint16_t bms_char_dis_status; //Enum, 0-2
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
extern uint16_t cell_max_voltage; //mV, 0-4350
extern uint16_t cell_min_voltage; //mV, 0-4350
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
extern uint8_t LEDcolor; //Enum, 0-10

void manageSerialLinkTransmitter();

#endif
Loading

0 comments on commit f1ac772

Please sign in to comment.