Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Double LilyGo! #109

Merged
merged 16 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions Software/Software.ino
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

// 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 +130,9 @@ void loop() {
#ifdef DUAL_CAN
receive_can2();
#endif
#ifdef SERIAL_LINK_RECEIVER
receive_serial();
#endif

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

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

#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
Serial2.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN); // 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 +401,30 @@ void send_can() {
#endif
}

#ifdef SERIAL_LINK_RECEIVER
//---- Receives serial data and transfers to the Inverter
void receive_serial() {
static unsigned long currentMillis = millis();
if (currentMillis - previousMillis1ms > interval1) { //--- try 2 second
previousMillis1ms = currentMillis;
manageSerialLinkReceiver();
}
}
#endif

#ifdef SERIAL_LINK_TRANSMITTER
//---- Gets data from Battery and serial Transmits the data to the Receiver
void send_serial() {
static unsigned long currentMillis = millis();
if (bms_status == ACTIVE) {
if (currentMillis - previousMillis1ms > interval1) { //--- try 2 second
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 //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)

#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
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h" //See this file for more Serial-battery settings
#endif

#endif
128 changes: 128 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,128 @@
// 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 last_minutesLost = 0;
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

if (readError) {
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");
}
}
if (dataLinkReceive.checkNewData(true)) // true = clear Flag
{
__getData();
lastGoodMaxCharge = max_target_charge_power;
lastGoodMaxDischarge = max_target_discharge_power;
lastGood = currentTime;
}

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;
bms_status = 4; //Fault state
LEDcolor = RED;
//----- Throw Error
}
// report Lost data & Max charge / Discharge reductions
if (minutesLost != last_minutesLost) {
last_minutesLost = minutesLost;
Serial.print(currentTime);
Serial.print(" - Minutes without data : ");
Serial.print(minutesLost);
Serial.print(", max Charge = ");
Serial.print(max_target_charge_power);
Serial.print(", max Discharge = ");
Serial.println(max_target_discharge_power);
}
}

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 uint8_t LEDcolor; //Enum, 0-10
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false

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
#include "SERIAL-LINK-TRANSMITTER-INVERTER.h"
#endif

#endif
119 changes: 119 additions & 0 deletions Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//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;
static unsigned long lastNoError = 0;
static unsigned long transmitGoodSince = 0;

unsigned long currentTime = millis();

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 (currentTime - updateTime > 100) {
updateTime = currentTime;
if (!initLink) {
initLink = true;
// sends variables every 5000mS even if no change
dataLinkTransmit.setUpdateInterval(5000);
}
bool sendError = dataLinkTransmit.checkTransmissionError(true);
if (sendError) {
Serial.print(currentTime);
Serial.println(" - ERROR: Serial Data Link - SEND Error");
lasterror = true;
transmitGoodSince = currentTime;
} else {
if (lasterror) {
lasterror = false;
Serial.print(currentTime);
Serial.println(" - RECOVERY: Serial Data Link - Send GOOD");
}
lastNoError = currentTime;
}

//--- reporting every 60 seconds that transmission is good
if (currentTime - transmitGoodSince > 60000) {
transmitGoodSince = currentTime;
Serial.print(currentTime);
Serial.println(" - Transmit Good");
}

//--- report that Errors been ocurring for > 60 seconds
if (currentTime - lastNoError > 60000) // 60 seconds
{
Serial.print(currentTime);
Serial.println(" - Transmit Failed : 60 seconds");
bms_status = 4; //FAULT
max_target_discharge_power = 0;
max_target_charge_power = 0;
LEDcolor = RED;
// throw error
}
/*
// lastMessageReceived from CAN bus (Battery)
if (currentTime - lastMessageReceived > (5 * 60000) ) // 5 minutes
{
Serial.print(millis());
Serial.println(" - Data Stale : 5 minutes");
// throw error

// stop transmitting until fresh
}
*/

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);
}
}
Loading