mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 17:59:27 +02:00
Remove serial-link (double LilyGo) as an option
This commit is contained in:
parent
83fc6e34ae
commit
2c0e5be5da
19 changed files with 1 additions and 1335 deletions
1
.github/workflows/compile-all-batteries.yml
vendored
1
.github/workflows/compile-all-batteries.yml
vendored
|
@ -78,7 +78,6 @@ jobs:
|
||||||
- TESLA_MODEL_SX_BATTERY
|
- TESLA_MODEL_SX_BATTERY
|
||||||
- VOLVO_SPA_BATTERY
|
- VOLVO_SPA_BATTERY
|
||||||
- TEST_FAKE_BATTERY
|
- TEST_FAKE_BATTERY
|
||||||
- SERIAL_LINK_RECEIVER
|
|
||||||
# These are the emulated inverter communication protocols for which the code will be compiled.
|
# These are the emulated inverter communication protocols for which the code will be compiled.
|
||||||
inverter:
|
inverter:
|
||||||
- BYD_CAN
|
- BYD_CAN
|
||||||
|
|
|
@ -86,7 +86,6 @@ jobs:
|
||||||
- SMA_TRIPOWER_CAN
|
- SMA_TRIPOWER_CAN
|
||||||
- SOFAR_CAN
|
- SOFAR_CAN
|
||||||
- SOLAX_CAN
|
- SOLAX_CAN
|
||||||
- SERIAL_LINK_TRANSMITTER
|
|
||||||
# These are the supported hardware platforms for which the code will be compiled.
|
# These are the supported hardware platforms for which the code will be compiled.
|
||||||
hardware:
|
hardware:
|
||||||
- HW_LILYGO
|
- HW_LILYGO
|
||||||
|
|
|
@ -86,7 +86,6 @@ jobs:
|
||||||
- SMA_TRIPOWER_CAN
|
- SMA_TRIPOWER_CAN
|
||||||
- SOFAR_CAN
|
- SOFAR_CAN
|
||||||
- SOLAX_CAN
|
- SOLAX_CAN
|
||||||
- SERIAL_LINK_TRANSMITTER
|
|
||||||
# These are the supported hardware platforms for which the code will be compiled.
|
# These are the supported hardware platforms for which the code will be compiled.
|
||||||
hardware:
|
hardware:
|
||||||
- HW_LILYGO
|
- HW_LILYGO
|
||||||
|
|
1
.github/workflows/compile-all-inverters.yml
vendored
1
.github/workflows/compile-all-inverters.yml
vendored
|
@ -72,7 +72,6 @@ jobs:
|
||||||
- SOFAR_CAN
|
- SOFAR_CAN
|
||||||
- SOLAX_CAN
|
- SOLAX_CAN
|
||||||
- SUNGROW_CAN
|
- SUNGROW_CAN
|
||||||
- SERIAL_LINK_TRANSMITTER
|
|
||||||
- NISSANLEAF_CHARGER # Last element is not an inverter, but good to also test if the charger compiles
|
- NISSANLEAF_CHARGER # Last element is not an inverter, but good to also test if the charger compiles
|
||||||
# These are the supported hardware platforms for which the code will be compiled.
|
# These are the supported hardware platforms for which the code will be compiled.
|
||||||
hardware:
|
hardware:
|
||||||
|
|
|
@ -92,7 +92,6 @@ This code uses the following excellent libraries:
|
||||||
- [ayushsharma82/ElegantOTA](https://github.com/ayushsharma82/ElegantOTA) AGPL-3.0 license
|
- [ayushsharma82/ElegantOTA](https://github.com/ayushsharma82/ElegantOTA) AGPL-3.0 license
|
||||||
- [bblanchon/ArduinoJson](https://github.com/bblanchon/ArduinoJson) MIT-License
|
- [bblanchon/ArduinoJson](https://github.com/bblanchon/ArduinoJson) MIT-License
|
||||||
- [eModbus/eModbus](https://github.com/eModbus/eModbus) MIT-License
|
- [eModbus/eModbus](https://github.com/eModbus/eModbus) MIT-License
|
||||||
- [mackelec/SerialDataLink](https://github.com/mackelec/SerialDataLink)
|
|
||||||
- [ESP32Async/AsyncTCP](https://github.com/ESP32Async/AsyncTCP) LGPL-3.0 license
|
- [ESP32Async/AsyncTCP](https://github.com/ESP32Async/AsyncTCP) LGPL-3.0 license
|
||||||
- [ESP32Async/ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) LGPL-3.0 license
|
- [ESP32Async/ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) LGPL-3.0 license
|
||||||
- [miwagner/ESP32-Arduino-CAN](https://github.com/miwagner/ESP32-Arduino-CAN/) MIT-License
|
- [miwagner/ESP32-Arduino-CAN](https://github.com/miwagner/ESP32-Arduino-CAN/) MIT-License
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include "src/communication/nvm/comm_nvm.h"
|
#include "src/communication/nvm/comm_nvm.h"
|
||||||
#include "src/communication/precharge_control/precharge_control.h"
|
#include "src/communication/precharge_control/precharge_control.h"
|
||||||
#include "src/communication/rs485/comm_rs485.h"
|
#include "src/communication/rs485/comm_rs485.h"
|
||||||
#include "src/communication/seriallink/comm_seriallink.h"
|
|
||||||
#include "src/datalayer/datalayer.h"
|
#include "src/datalayer/datalayer.h"
|
||||||
#include "src/devboard/sdcard/sdcard.h"
|
#include "src/devboard/sdcard/sdcard.h"
|
||||||
#include "src/devboard/utils/events.h"
|
#include "src/devboard/utils/events.h"
|
||||||
|
@ -109,7 +108,6 @@ void setup() {
|
||||||
|
|
||||||
init_rs485();
|
init_rs485();
|
||||||
|
|
||||||
init_serialDataLink();
|
|
||||||
#if defined(CAN_INVERTER_SELECTED) || defined(MODBUS_INVERTER_SELECTED) || defined(RS485_INVERTER_SELECTED)
|
#if defined(CAN_INVERTER_SELECTED) || defined(MODBUS_INVERTER_SELECTED) || defined(RS485_INVERTER_SELECTED)
|
||||||
setup_inverter();
|
setup_inverter();
|
||||||
#endif
|
#endif
|
||||||
|
@ -237,9 +235,6 @@ void core_loop(void* task_time_us) {
|
||||||
#if defined(RS485_INVERTER_SELECTED) || defined(RS485_BATTERY_SELECTED)
|
#if defined(RS485_INVERTER_SELECTED) || defined(RS485_BATTERY_SELECTED)
|
||||||
receive_RS485(); // Process serial2 RS485 interface
|
receive_RS485(); // Process serial2 RS485 interface
|
||||||
#endif // RS485_INVERTER_SELECTED
|
#endif // RS485_INVERTER_SELECTED
|
||||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
|
||||||
run_serialDataLink();
|
|
||||||
#endif // SERIAL_LINK_RECEIVER || SERIAL_LINK_TRANSMITTER
|
|
||||||
END_TIME_MEASUREMENT_MAX(comm, datalayer.system.status.time_comm_us);
|
END_TIME_MEASUREMENT_MAX(comm, datalayer.system.status.time_comm_us);
|
||||||
#ifdef WEBSERVER
|
#ifdef WEBSERVER
|
||||||
START_TIME_MEASUREMENT(ota);
|
START_TIME_MEASUREMENT(ota);
|
||||||
|
@ -268,9 +263,7 @@ void core_loop(void* task_time_us) {
|
||||||
check_interconnect_available();
|
check_interconnect_available();
|
||||||
#endif // DOUBLE_BATTERY
|
#endif // DOUBLE_BATTERY
|
||||||
update_calculated_values();
|
update_calculated_values();
|
||||||
#ifndef SERIAL_LINK_RECEIVER
|
update_machineryprotection(); // Check safeties
|
||||||
update_machineryprotection(); // Check safeties (Not on serial link reciever board)
|
|
||||||
#endif // SERIAL_LINK_RECEIVER
|
|
||||||
update_values_inverter(); // Update values heading towards inverter
|
update_values_inverter(); // Update values heading towards inverter
|
||||||
}
|
}
|
||||||
END_TIME_MEASUREMENT_MAX(time_values, datalayer.system.status.time_values_us);
|
END_TIME_MEASUREMENT_MAX(time_values, datalayer.system.status.time_values_us);
|
||||||
|
|
|
@ -44,7 +44,6 @@
|
||||||
//#define VOLVO_SPA_HYBRID_BATTERY
|
//#define VOLVO_SPA_HYBRID_BATTERY
|
||||||
//#define TEST_FAKE_BATTERY
|
//#define TEST_FAKE_BATTERY
|
||||||
//#define DOUBLE_BATTERY //Enable this line if you use two identical batteries at the same time (requires CAN_ADDON setup)
|
//#define DOUBLE_BATTERY //Enable this line if you use two identical batteries at the same time (requires CAN_ADDON setup)
|
||||||
//#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery)
|
|
||||||
|
|
||||||
/* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki */
|
/* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki */
|
||||||
//#define AFORE_CAN //Enable this line to emulate an "Afore battery" over CAN bus
|
//#define AFORE_CAN //Enable this line to emulate an "Afore battery" over CAN bus
|
||||||
|
@ -65,7 +64,6 @@
|
||||||
//#define SOFAR_CAN //Enable this line to emulate a "Sofar Energy Storage Inverter High Voltage BMS General Protocol (Extended Frame)" over CAN bus
|
//#define SOFAR_CAN //Enable this line to emulate a "Sofar Energy Storage Inverter High Voltage BMS General Protocol (Extended Frame)" over CAN bus
|
||||||
//#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus
|
//#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus
|
||||||
//#define SUNGROW_CAN //Enable this line to emulate a "Sungrow SBR064" over CAN bus
|
//#define SUNGROW_CAN //Enable this line to emulate a "Sungrow SBR064" over CAN bus
|
||||||
//#define SERIAL_LINK_RECEIVER //Enable this line to receive battery data over RS485 pins from another Lilygo (This LilyGo interfaces with inverter)
|
|
||||||
|
|
||||||
/* Select hardware used for Battery-Emulator */
|
/* Select hardware used for Battery-Emulator */
|
||||||
//#define HW_LILYGO
|
//#define HW_LILYGO
|
||||||
|
|
|
@ -143,10 +143,6 @@ void setup_can_shunt();
|
||||||
#include "VOLVO-SPA-HYBRID-BATTERY.h"
|
#include "VOLVO-SPA-HYBRID-BATTERY.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SERIAL_LINK_RECEIVER
|
|
||||||
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setup_battery(void);
|
void setup_battery(void);
|
||||||
void update_values_battery();
|
void update_values_battery();
|
||||||
|
|
||||||
|
|
|
@ -1,230 +0,0 @@
|
||||||
#include "../include.h"
|
|
||||||
#ifdef SERIAL_LINK_RECEIVER
|
|
||||||
#include "../datalayer/datalayer.h"
|
|
||||||
#include "../devboard/utils/events.h"
|
|
||||||
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h"
|
|
||||||
|
|
||||||
#define INVERTER_SEND_NUM_VARIABLES 1
|
|
||||||
#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
|
|
||||||
|
|
||||||
#ifdef TESTBENCH
|
|
||||||
// In the testbench environment, the receiver uses Serial3
|
|
||||||
#define SerialReceiver Serial3
|
|
||||||
#else
|
|
||||||
// In the production environment, the receiver uses Serial2
|
|
||||||
#define SerialReceiver Serial2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// txid,rxid, num_send,num_recv
|
|
||||||
SerialDataLink dataLinkReceive(SerialReceiver, 0, 0x01, sendingNumVariables,
|
|
||||||
INVERTER_RECV_NUM_VARIABLES); // ...
|
|
||||||
|
|
||||||
static bool batteryFault = false; // used locally - mainly to indicate Battery CAN failure
|
|
||||||
|
|
||||||
void __getData() {
|
|
||||||
datalayer.battery.status.real_soc = (uint16_t)dataLinkReceive.getReceivedData(0);
|
|
||||||
datalayer.battery.status.soh_pptt = (uint16_t)dataLinkReceive.getReceivedData(1);
|
|
||||||
datalayer.battery.status.voltage_dV = (uint16_t)dataLinkReceive.getReceivedData(2);
|
|
||||||
datalayer.battery.status.current_dA = (int16_t)dataLinkReceive.getReceivedData(3);
|
|
||||||
datalayer.battery.info.total_capacity_Wh =
|
|
||||||
(uint32_t)(dataLinkReceive.getReceivedData(4) * 10); //add back missing decimal
|
|
||||||
datalayer.battery.status.remaining_capacity_Wh =
|
|
||||||
(uint32_t)(dataLinkReceive.getReceivedData(5) * 10); //add back missing decimal
|
|
||||||
datalayer.battery.status.max_discharge_power_W =
|
|
||||||
(uint32_t)(dataLinkReceive.getReceivedData(6) * 10); //add back missing decimal
|
|
||||||
datalayer.battery.status.max_charge_power_W =
|
|
||||||
(uint32_t)(dataLinkReceive.getReceivedData(7) * 10); //add back missing decimal
|
|
||||||
uint16_t _system_bms_status = (uint16_t)dataLinkReceive.getReceivedData(8);
|
|
||||||
datalayer.battery.status.active_power_W =
|
|
||||||
(uint32_t)(dataLinkReceive.getReceivedData(9) * 10); //add back missing decimal
|
|
||||||
datalayer.battery.status.temperature_min_dC = (int16_t)dataLinkReceive.getReceivedData(10);
|
|
||||||
datalayer.battery.status.temperature_max_dC = (int16_t)dataLinkReceive.getReceivedData(11);
|
|
||||||
datalayer.battery.status.cell_max_voltage_mV = (uint16_t)dataLinkReceive.getReceivedData(12);
|
|
||||||
datalayer.battery.status.cell_min_voltage_mV = (uint16_t)dataLinkReceive.getReceivedData(13);
|
|
||||||
datalayer.battery.info.chemistry = (battery_chemistry_enum)dataLinkReceive.getReceivedData(14);
|
|
||||||
datalayer.system.status.battery_allows_contactor_closing = (bool)dataLinkReceive.getReceivedData(15);
|
|
||||||
|
|
||||||
batteryFault = false;
|
|
||||||
if (_system_bms_status == FAULT) {
|
|
||||||
batteryFault = true;
|
|
||||||
set_event(EVENT_SERIAL_TRANSMITTER_FAILURE, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateData() {
|
|
||||||
// --- update with fresh data
|
|
||||||
dataLinkReceive.updateData(0, datalayer.system.status.inverter_allows_contactor_closing);
|
|
||||||
//dataLinkReceive.updateData(1,var2); // For future expansion,
|
|
||||||
//dataLinkReceive.updateData(2,var3); // if inverter needs to send data to battery
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @ 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;
|
|
||||||
static unsigned long reportTime = 0;
|
|
||||||
static uint16_t reads = 0;
|
|
||||||
static uint16_t errors = 0;
|
|
||||||
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) {
|
|
||||||
logging.print(currentTime);
|
|
||||||
logging.println(" - ERROR: SerialDataLink - Read Error");
|
|
||||||
lasterror = true;
|
|
||||||
errors++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataLinkReceive.checkNewData(true)) // true = clear Flag
|
|
||||||
{
|
|
||||||
__getData();
|
|
||||||
reads++;
|
|
||||||
lastGoodMaxCharge = datalayer.battery.status.max_charge_power_W;
|
|
||||||
lastGoodMaxDischarge = datalayer.battery.status.max_discharge_power_W;
|
|
||||||
//--- if BatteryFault then assume Data is stale
|
|
||||||
if (!batteryFault)
|
|
||||||
lastGood = currentTime;
|
|
||||||
//bms_status = ACTIVE; // just testing
|
|
||||||
if (lasterror) {
|
|
||||||
lasterror = false;
|
|
||||||
logging.print(currentTime);
|
|
||||||
logging.println(" - RECOVERY: SerialDataLink - Read GOOD");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long minutesLost = (currentTime - lastGood) / 60000UL;
|
|
||||||
if (minutesLost > 0 && lastGood > 0) {
|
|
||||||
// lose 25% each minute of data loss
|
|
||||||
if (minutesLost < 4) {
|
|
||||||
datalayer.battery.status.max_charge_power_W = (lastGoodMaxCharge * (4 - minutesLost)) / 4;
|
|
||||||
datalayer.battery.status.max_discharge_power_W = (lastGoodMaxDischarge * (4 - minutesLost)) / 4;
|
|
||||||
set_event(EVENT_SERIAL_RX_WARNING, minutesLost);
|
|
||||||
} else {
|
|
||||||
// Times Up -
|
|
||||||
datalayer.battery.status.max_charge_power_W = 0;
|
|
||||||
datalayer.battery.status.max_discharge_power_W = 0;
|
|
||||||
set_event(EVENT_SERIAL_RX_FAILURE, uint8_t(min(minutesLost, 255uL)));
|
|
||||||
//----- Throw Error
|
|
||||||
}
|
|
||||||
// report Lost data & Max charge / Discharge reductions
|
|
||||||
if (minutesLost != last_minutesLost) {
|
|
||||||
last_minutesLost = minutesLost;
|
|
||||||
logging.print(currentTime);
|
|
||||||
if (batteryFault) {
|
|
||||||
logging.print("Battery Fault (minutes) : ");
|
|
||||||
} else {
|
|
||||||
logging.print(" - Minutes without data : ");
|
|
||||||
}
|
|
||||||
logging.print(minutesLost);
|
|
||||||
logging.print(", max Charge = ");
|
|
||||||
logging.print(datalayer.battery.status.max_charge_power_W);
|
|
||||||
logging.print(", max Discharge = ");
|
|
||||||
logging.println(datalayer.battery.status.max_discharge_power_W);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentTime - reportTime > 59999) {
|
|
||||||
reportTime = currentTime;
|
|
||||||
logging.print(currentTime);
|
|
||||||
logging.print(" SerialDataLink-Receiver - NewData :");
|
|
||||||
logging.print(reads);
|
|
||||||
logging.print(" Errors : ");
|
|
||||||
logging.println(errors);
|
|
||||||
reads = 0;
|
|
||||||
errors = 0;
|
|
||||||
|
|
||||||
// --- printUsefullData();
|
|
||||||
//logging.print("SOC = ");
|
|
||||||
//logging.println(SOC);
|
|
||||||
#ifdef DEBUG_LOG
|
|
||||||
update_values_serial_link();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_values_serial_link() {
|
|
||||||
logging.println("Values from battery: ");
|
|
||||||
logging.print("SOC: ");
|
|
||||||
logging.print(datalayer.battery.status.real_soc);
|
|
||||||
logging.print(" SOH: ");
|
|
||||||
logging.print(datalayer.battery.status.soh_pptt);
|
|
||||||
logging.print(" Voltage: ");
|
|
||||||
logging.print(datalayer.battery.status.voltage_dV);
|
|
||||||
logging.print(" Current: ");
|
|
||||||
logging.print(datalayer.battery.status.current_dA);
|
|
||||||
logging.print(" Capacity: ");
|
|
||||||
logging.print(datalayer.battery.info.total_capacity_Wh);
|
|
||||||
logging.print(" Remain cap: ");
|
|
||||||
logging.print(datalayer.battery.status.remaining_capacity_Wh);
|
|
||||||
logging.print(" Max discharge W: ");
|
|
||||||
logging.print(datalayer.battery.status.max_discharge_power_W);
|
|
||||||
logging.print(" Max charge W: ");
|
|
||||||
logging.print(datalayer.battery.status.max_charge_power_W);
|
|
||||||
logging.print(" BMS status: ");
|
|
||||||
logging.print(datalayer.battery.status.bms_status);
|
|
||||||
logging.print(" Power: ");
|
|
||||||
logging.print(datalayer.battery.status.active_power_W);
|
|
||||||
logging.print(" Temp min: ");
|
|
||||||
logging.print(datalayer.battery.status.temperature_min_dC);
|
|
||||||
logging.print(" Temp max: ");
|
|
||||||
logging.print(datalayer.battery.status.temperature_max_dC);
|
|
||||||
logging.print(" Cell max: ");
|
|
||||||
logging.print(datalayer.battery.status.cell_max_voltage_mV);
|
|
||||||
logging.print(" Cell min: ");
|
|
||||||
logging.print(datalayer.battery.status.cell_min_voltage_mV);
|
|
||||||
logging.print(" LFP : ");
|
|
||||||
logging.print(datalayer.battery.info.chemistry);
|
|
||||||
logging.print(" Battery Allows Contactor Closing: ");
|
|
||||||
logging.print(datalayer.system.status.battery_allows_contactor_closing);
|
|
||||||
logging.print(" Inverter Allows Contactor Closing: ");
|
|
||||||
logging.print(datalayer.system.status.inverter_allows_contactor_closing);
|
|
||||||
|
|
||||||
logging.println("");
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_battery(void) {
|
|
||||||
strncpy(datalayer.system.info.battery_protocol, "Serial link to another LilyGo board", 63);
|
|
||||||
datalayer.system.info.battery_protocol[63] = '\0';
|
|
||||||
}
|
|
||||||
// Needed to make the compiler happy
|
|
||||||
void update_values_battery() {}
|
|
||||||
void transmit_can_battery() {}
|
|
||||||
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,15 +0,0 @@
|
||||||
// SERIAL-LINK-RECEIVER-FROM-BATTERY.h
|
|
||||||
|
|
||||||
#ifndef SERIAL_LINK_RECEIVER_FROM_BATTERY_H
|
|
||||||
#define SERIAL_LINK_RECEIVER_FROM_BATTERY_H
|
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
|
||||||
|
|
||||||
#include "../include.h"
|
|
||||||
#include "../lib/mackelec-SerialDataLink/SerialDataLink.h"
|
|
||||||
|
|
||||||
void manageSerialLinkReceiver();
|
|
||||||
void update_values_serial_link();
|
|
||||||
void setup_battery(void);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -9,9 +9,6 @@ uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory
|
||||||
// Create a ModbusRTU server instance listening on Serial2 with 2000ms timeout
|
// Create a ModbusRTU server instance listening on Serial2 with 2000ms timeout
|
||||||
ModbusServerRTU MBserver(Serial2, 2000);
|
ModbusServerRTU MBserver(Serial2, 2000);
|
||||||
#endif
|
#endif
|
||||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
|
||||||
#define SERIAL_LINK_BAUDRATE 112500
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Initialization functions
|
// Initialization functions
|
||||||
|
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
#include "comm_seriallink.h"
|
|
||||||
#include "../../include.h"
|
|
||||||
|
|
||||||
// Parameters
|
|
||||||
|
|
||||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
|
||||||
#define SERIAL_LINK_BAUDRATE 112500
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Initialization functions
|
|
||||||
|
|
||||||
void init_serialDataLink() {
|
|
||||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
|
||||||
Serial2.begin(SERIAL_LINK_BAUDRATE, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
|
||||||
#endif // SERIAL_LINK_RECEIVER || SERIAL_LINK_TRANSMITTER
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main functions
|
|
||||||
|
|
||||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
|
||||||
void run_serialDataLink() {
|
|
||||||
static unsigned long updateTime = 0;
|
|
||||||
unsigned long currentMillis = millis();
|
|
||||||
|
|
||||||
if ((currentMillis - updateTime) > 1) { //Every 2ms
|
|
||||||
updateTime = currentMillis;
|
|
||||||
#ifdef SERIAL_LINK_RECEIVER
|
|
||||||
manageSerialLinkReceiver();
|
|
||||||
#endif
|
|
||||||
#ifdef SERIAL_LINK_TRANSMITTER
|
|
||||||
manageSerialLinkTransmitter();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // SERIAL_LINK_RECEIVER || SERIAL_LINK_TRANSMITTER
|
|
|
@ -1,17 +0,0 @@
|
||||||
#ifndef _COMM_SERIALLINK_H_
|
|
||||||
#define _COMM_SERIALLINK_H_
|
|
||||||
|
|
||||||
#include "../../include.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialization of serial data link
|
|
||||||
*
|
|
||||||
* @param[in] void
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
void init_serialDataLink();
|
|
||||||
|
|
||||||
void run_serialDataLink();
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -30,20 +30,6 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MODBUS_INVERTER_SELECTED
|
|
||||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
|
||||||
// Check that Dual LilyGo via RS485 option isn't enabled, this collides with Modbus!
|
|
||||||
#error MODBUS CANNOT BE USED IN DOUBLE LILYGO SETUPS! CHECK USER SETTINGS!
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef RS485_INVERTER_SELECTED
|
|
||||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
|
||||||
// Check that Dual LilyGo via RS485 option isn't enabled, this collides with Modbus!
|
|
||||||
#error RS485 CANNOT BE USED IN DOUBLE LILYGO SETUPS! CHECK USER SETTINGS!
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HW_LILYGO
|
#ifdef HW_LILYGO
|
||||||
#if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET)
|
#if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET)
|
||||||
#if defined(CAN_ADDON) || defined(CANFD_ADDON) || defined(CHADEMO_BATTERY)
|
#if defined(CAN_ADDON) || defined(CANFD_ADDON) || defined(CHADEMO_BATTERY)
|
||||||
|
|
|
@ -75,10 +75,6 @@
|
||||||
#include "SUNGROW-CAN.h"
|
#include "SUNGROW-CAN.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SERIAL_LINK_TRANSMITTER
|
|
||||||
#include "SERIAL-LINK-TRANSMITTER-INVERTER.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CAN_INVERTER_SELECTED
|
#ifdef CAN_INVERTER_SELECTED
|
||||||
void update_values_can_inverter();
|
void update_values_can_inverter();
|
||||||
void map_can_frame_to_variable_inverter(CAN_frame rx_frame);
|
void map_can_frame_to_variable_inverter(CAN_frame rx_frame);
|
||||||
|
|
|
@ -1,195 +0,0 @@
|
||||||
#include "../include.h"
|
|
||||||
#ifdef SERIAL_LINK_TRANSMITTER
|
|
||||||
|
|
||||||
#include "../datalayer/datalayer.h"
|
|
||||||
#include "../devboard/utils/events.h"
|
|
||||||
#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 1
|
|
||||||
|
|
||||||
#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 printSendingValues();
|
|
||||||
|
|
||||||
void _getData() {
|
|
||||||
datalayer.system.status.inverter_allows_contactor_closing = dataLinkTransmit.getReceivedData(0);
|
|
||||||
//var2 = dataLinkTransmit.getReceivedData(1); // For future expansion,
|
|
||||||
//var3 = dataLinkTransmit.getReceivedData(2); // if inverter needs to send data to battery
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
static unsigned long lastGood = 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 > INTERVAL_100_MS) {
|
|
||||||
updateTime = currentTime;
|
|
||||||
if (!initLink) {
|
|
||||||
initLink = true;
|
|
||||||
transmitGoodSince = currentTime;
|
|
||||||
dataLinkTransmit.setUpdateInterval(INTERVAL_10_S);
|
|
||||||
}
|
|
||||||
bool sendError = dataLinkTransmit.checkTransmissionError(true);
|
|
||||||
if (sendError) {
|
|
||||||
logging.print(currentTime);
|
|
||||||
logging.println(" - ERROR: Serial Data Link - SEND Error");
|
|
||||||
lasterror = true;
|
|
||||||
transmitGoodSince = currentTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* new feature */
|
|
||||||
/* @getLastAcknowledge(bool resetFlag)
|
|
||||||
* - returns:
|
|
||||||
* -2 NACK received from receiver
|
|
||||||
* -1 no ACK received
|
|
||||||
* 0 no activity
|
|
||||||
* 1 ACK received
|
|
||||||
* resetFlag = true will clear to 0
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ackReceived = dataLinkTransmit.getLastAcknowledge(true);
|
|
||||||
if (ackReceived > 0)
|
|
||||||
lastGood = currentTime;
|
|
||||||
|
|
||||||
if (lasterror && (ackReceived > 0)) {
|
|
||||||
lasterror = false;
|
|
||||||
logging.print(currentTime);
|
|
||||||
logging.println(" - RECOVERY: Serial Data Link - Send GOOD");
|
|
||||||
}
|
|
||||||
|
|
||||||
//--- reporting every 60 seconds that transmission is good
|
|
||||||
if (currentTime - transmitGoodSince > INTERVAL_60_S) {
|
|
||||||
transmitGoodSince = currentTime;
|
|
||||||
logging.print(currentTime);
|
|
||||||
logging.println(" - Transmit Good");
|
|
||||||
// printUsefullData();
|
|
||||||
#ifdef DEBUG_LOG
|
|
||||||
void printSendingValues();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
//--- report that Errors been ocurring for > 60 seconds
|
|
||||||
if (currentTime - lastGood > INTERVAL_60_S) {
|
|
||||||
lastGood = currentTime;
|
|
||||||
logging.print(currentTime);
|
|
||||||
logging.println(" - Transmit Failed : 60 seconds");
|
|
||||||
// print the max_ data
|
|
||||||
logging.println("SerialDataLink : bms_status=4");
|
|
||||||
logging.println("SerialDataLink : LEDcolor = RED");
|
|
||||||
logging.println("SerialDataLink : max_target_discharge_power = 0");
|
|
||||||
logging.println("SerialDataLink : max_target_charge_power = 0");
|
|
||||||
|
|
||||||
datalayer.battery.status.max_discharge_power_W = 0;
|
|
||||||
datalayer.battery.status.max_charge_power_W = 0;
|
|
||||||
set_event(EVENT_SERIAL_TX_FAILURE, 0);
|
|
||||||
// throw error
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
// lastMessageReceived from CAN bus (Battery)
|
|
||||||
if (currentTime - lastMessageReceived > (5 * 60000) ) // 5 minutes
|
|
||||||
{
|
|
||||||
logging.print(millis());
|
|
||||||
logging.println(" - Data Stale : 5 minutes");
|
|
||||||
// throw error
|
|
||||||
|
|
||||||
// stop transmitting until fresh
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
static unsigned long updateDataTime = 0;
|
|
||||||
|
|
||||||
if (currentTime - updateDataTime > INTERVAL_1_S) {
|
|
||||||
strncpy(datalayer.system.info.inverter_protocol, "Serial link to another LilyGo board", 63);
|
|
||||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
|
||||||
updateDataTime = currentTime;
|
|
||||||
dataLinkTransmit.updateData(0, datalayer.battery.status.real_soc);
|
|
||||||
dataLinkTransmit.updateData(1, datalayer.battery.status.soh_pptt);
|
|
||||||
dataLinkTransmit.updateData(2, datalayer.battery.status.voltage_dV);
|
|
||||||
dataLinkTransmit.updateData(3, datalayer.battery.status.current_dA);
|
|
||||||
dataLinkTransmit.updateData(4, datalayer.battery.info.total_capacity_Wh / 10); //u32, remove .0 to fit 16bit
|
|
||||||
dataLinkTransmit.updateData(5,
|
|
||||||
datalayer.battery.status.remaining_capacity_Wh / 10); //u32, remove .0 to fit 16bit
|
|
||||||
dataLinkTransmit.updateData(6,
|
|
||||||
datalayer.battery.status.max_discharge_power_W / 10); //u32, remove .0 to fit 16bit
|
|
||||||
dataLinkTransmit.updateData(7, datalayer.battery.status.max_charge_power_W / 10); //u32, remove .0 to fit 16bit
|
|
||||||
dataLinkTransmit.updateData(8, datalayer.battery.status.bms_status);
|
|
||||||
dataLinkTransmit.updateData(9, datalayer.battery.status.active_power_W / 10); //u32, remove .0 to fit 16bit
|
|
||||||
dataLinkTransmit.updateData(10, datalayer.battery.status.temperature_min_dC);
|
|
||||||
dataLinkTransmit.updateData(11, datalayer.battery.status.temperature_max_dC);
|
|
||||||
dataLinkTransmit.updateData(12, datalayer.battery.status.cell_max_voltage_mV);
|
|
||||||
dataLinkTransmit.updateData(13, datalayer.battery.status.cell_min_voltage_mV);
|
|
||||||
dataLinkTransmit.updateData(14, (int16_t)datalayer.battery.info.chemistry);
|
|
||||||
dataLinkTransmit.updateData(15, datalayer.system.status.battery_allows_contactor_closing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void printSendingValues() {
|
|
||||||
logging.println("Values from battery: ");
|
|
||||||
logging.print("SOC: ");
|
|
||||||
logging.print(datalayer.battery.status.real_soc);
|
|
||||||
logging.print(" SOH: ");
|
|
||||||
logging.print(datalayer.battery.status.soh_pptt);
|
|
||||||
logging.print(" Voltage: ");
|
|
||||||
logging.print(datalayer.battery.status.voltage_dV);
|
|
||||||
logging.print(" Current: ");
|
|
||||||
logging.print(datalayer.battery.status.current_dA);
|
|
||||||
logging.print(" Capacity: ");
|
|
||||||
logging.print(datalayer.battery.info.total_capacity_Wh);
|
|
||||||
logging.print(" Remain cap: ");
|
|
||||||
logging.print(datalayer.battery.status.remaining_capacity_Wh);
|
|
||||||
logging.print(" Max discharge W: ");
|
|
||||||
logging.print(datalayer.battery.status.max_discharge_power_W);
|
|
||||||
logging.print(" Max charge W: ");
|
|
||||||
logging.print(datalayer.battery.status.max_charge_power_W);
|
|
||||||
logging.print(" BMS status: ");
|
|
||||||
logging.print(datalayer.battery.status.bms_status);
|
|
||||||
logging.print(" Power: ");
|
|
||||||
logging.print(datalayer.battery.status.active_power_W);
|
|
||||||
logging.print(" Temp min: ");
|
|
||||||
logging.print(datalayer.battery.status.temperature_min_dC);
|
|
||||||
logging.print(" Temp max: ");
|
|
||||||
logging.print(datalayer.battery.status.temperature_max_dC);
|
|
||||||
logging.print(" Cell max: ");
|
|
||||||
logging.print(datalayer.battery.status.cell_max_voltage_mV);
|
|
||||||
logging.print(" Cell min: ");
|
|
||||||
logging.print(datalayer.battery.status.cell_min_voltage_mV);
|
|
||||||
logging.print(" LFP : ");
|
|
||||||
logging.print(datalayer.battery.info.chemistry);
|
|
||||||
logging.print(" Battery Allows Contactor Closing: ");
|
|
||||||
logging.print(datalayer.system.status.battery_allows_contactor_closing);
|
|
||||||
logging.print(" Inverter Allows Contactor Closing: ");
|
|
||||||
logging.print(datalayer.system.status.inverter_allows_contactor_closing);
|
|
||||||
|
|
||||||
logging.println("");
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,11 +0,0 @@
|
||||||
#ifndef SERIAL_LINK_TRANSMITTER_INVERTER_H
|
|
||||||
#define SERIAL_LINK_TRANSMITTER_INVERTER_H
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "../include.h"
|
|
||||||
#include "../lib/mackelec-SerialDataLink/SerialDataLink.h"
|
|
||||||
|
|
||||||
void manageSerialLinkTransmitter();
|
|
||||||
void setup_inverter(void);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,607 +0,0 @@
|
||||||
// SerialDataLink.cpp
|
|
||||||
|
|
||||||
#include "SerialDataLink.h"
|
|
||||||
|
|
||||||
|
|
||||||
const uint16_t crcTable[256] = {
|
|
||||||
0, 32773, 32783, 10, 32795, 30, 20, 32785,
|
|
||||||
32819, 54, 60, 32825, 40, 32813, 32807, 34,
|
|
||||||
32867, 102, 108, 32873, 120, 32893, 32887, 114,
|
|
||||||
80, 32853, 32863, 90, 32843, 78, 68, 32833,
|
|
||||||
32963, 198, 204, 32969, 216, 32989, 32983, 210,
|
|
||||||
240, 33013, 33023, 250, 33003, 238, 228, 32993,
|
|
||||||
160, 32933, 32943, 170, 32955, 190, 180, 32945,
|
|
||||||
32915, 150, 156, 32921, 136, 32909, 32903, 130,
|
|
||||||
33155, 390, 396, 33161, 408, 33181, 33175, 402,
|
|
||||||
432, 33205, 33215, 442, 33195, 430, 420, 33185,
|
|
||||||
480, 33253, 33263, 490, 33275, 510, 500, 33265,
|
|
||||||
33235, 470, 476, 33241, 456, 33229, 33223, 450,
|
|
||||||
320, 33093, 33103, 330, 33115, 350, 340, 33105,
|
|
||||||
33139, 374, 380, 33145, 360, 33133, 33127, 354,
|
|
||||||
33059, 294, 300, 33065, 312, 33085, 33079, 306,
|
|
||||||
272, 33045, 33055, 282, 33035, 270, 260, 33025,
|
|
||||||
33539, 774, 780, 33545, 792, 33565, 33559, 786,
|
|
||||||
816, 33589, 33599, 826, 33579, 814, 804, 33569,
|
|
||||||
864, 33637, 33647, 874, 33659, 894, 884, 33649,
|
|
||||||
33619, 854, 860, 33625, 840, 33613, 33607, 834,
|
|
||||||
960, 33733, 33743, 970, 33755, 990, 980, 33745,
|
|
||||||
33779, 1014, 1020, 33785, 1000, 33773, 33767, 994,
|
|
||||||
33699, 934, 940, 33705, 952, 33725, 33719, 946,
|
|
||||||
912, 33685, 33695, 922, 33675, 910, 900, 33665,
|
|
||||||
640, 33413, 33423, 650, 33435, 670, 660, 33425,
|
|
||||||
33459, 694, 700, 33465, 680, 33453, 33447, 674,
|
|
||||||
33507, 742, 748, 33513, 760, 33533, 33527, 754,
|
|
||||||
720, 33493, 33503, 730, 33483, 718, 708, 33473,
|
|
||||||
33347, 582, 588, 33353, 600, 33373, 33367, 594,
|
|
||||||
624, 33397, 33407, 634, 33387, 622, 612, 33377,
|
|
||||||
544, 33317, 33327, 554, 33339, 574, 564, 33329,
|
|
||||||
33299, 534, 540, 33305, 520, 33293, 33287, 514
|
|
||||||
};
|
|
||||||
|
|
||||||
union Convert
|
|
||||||
{
|
|
||||||
uint16_t u16;
|
|
||||||
int16_t i16;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
byte low;
|
|
||||||
byte high;
|
|
||||||
};
|
|
||||||
}convert;
|
|
||||||
|
|
||||||
/*
|
|
||||||
#define SET_PA6() (GPIOA->BSRR = GPIO_BSRR_BS6)
|
|
||||||
#define CLEAR_PA6() (GPIOA->BSRR = GPIO_BSRR_BR6)
|
|
||||||
// Macro to toggle PA6
|
|
||||||
#define TOGGLE_PA6() (GPIOA->ODR ^= GPIO_ODR_ODR6)
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
SerialDataLink::SerialDataLink(Stream &serial, uint8_t transmitID, uint8_t receiveID, uint8_t maxIndexTX, uint8_t maxIndexRX, bool enableRetransmit)
|
|
||||||
: serial(serial), transmitID(transmitID), receiveID(receiveID), maxIndexTX(maxIndexTX), maxIndexRX(maxIndexRX), retransmitEnabled(enableRetransmit) {
|
|
||||||
// Initialize buffers and state variables
|
|
||||||
txBufferIndex = 0;
|
|
||||||
isTransmitting = false;
|
|
||||||
isReceiving = false;
|
|
||||||
transmissionError = false;
|
|
||||||
readError = false;
|
|
||||||
newData = false;
|
|
||||||
|
|
||||||
// Initialize data arrays and update flags
|
|
||||||
|
|
||||||
memset(dataArrayTX, 0, sizeof(dataArrayTX));
|
|
||||||
memset(dataArrayRX, 0, sizeof(dataArrayRX));
|
|
||||||
memset(dataUpdated, 0, sizeof(dataUpdated));
|
|
||||||
memset(lastSent , 0, sizeof(lastSent ));
|
|
||||||
|
|
||||||
// Additional initialization as required
|
|
||||||
}
|
|
||||||
|
|
||||||
void SerialDataLink::updateData(uint8_t index, int16_t value)
|
|
||||||
{
|
|
||||||
if (index < maxIndexTX)
|
|
||||||
{
|
|
||||||
if (dataArrayTX[index] != value)
|
|
||||||
{
|
|
||||||
dataArrayTX[index] = value;
|
|
||||||
dataUpdated[index] = true;
|
|
||||||
lastSent[index] = millis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SerialDataLink::getReceivedData(uint8_t index)
|
|
||||||
{
|
|
||||||
if (index < dataArraySizeRX) {
|
|
||||||
return dataArrayRX[index];
|
|
||||||
} else {
|
|
||||||
// Handle the case where the index is out of bounds
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SerialDataLink::checkTransmissionError(bool resetFlag)
|
|
||||||
{
|
|
||||||
bool currentStatus = transmissionError;
|
|
||||||
if (resetFlag && transmissionError) {
|
|
||||||
transmissionError = false;
|
|
||||||
}
|
|
||||||
return currentStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SerialDataLink::getLastAcknowledge(bool resetFlag)
|
|
||||||
{
|
|
||||||
int result = lastAcknowledgeStatus;
|
|
||||||
if (resetFlag)
|
|
||||||
{
|
|
||||||
lastAcknowledgeStatus = 0; // Reset to default state
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SerialDataLink::checkReadError(bool reset)
|
|
||||||
{
|
|
||||||
bool error = readError;
|
|
||||||
if (reset) {
|
|
||||||
readError = false;
|
|
||||||
}
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool SerialDataLink::checkNewData(bool resetFlag) {
|
|
||||||
bool currentStatus = newData;
|
|
||||||
if (resetFlag && newData) {
|
|
||||||
newData = false;
|
|
||||||
}
|
|
||||||
return currentStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SerialDataLink::muteACK(bool mute)
|
|
||||||
{
|
|
||||||
muteAcknowledgement = mute;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SerialDataLink::run()
|
|
||||||
{
|
|
||||||
unsigned long currentTime = millis();
|
|
||||||
static DataLinkState oldstate;
|
|
||||||
|
|
||||||
|
|
||||||
// Check if state has not changed for a prolonged period
|
|
||||||
if (oldstate != currentState)
|
|
||||||
{
|
|
||||||
lastStateChangeTime = currentTime;
|
|
||||||
oldstate = currentState;
|
|
||||||
}
|
|
||||||
if ((currentTime - lastStateChangeTime) > stateChangeTimeout) {
|
|
||||||
// Reset the state to Idle and perform necessary cleanup
|
|
||||||
currentState = DataLinkState::Idle;
|
|
||||||
// Perform any additional cleanup or reinitialization here
|
|
||||||
// ...
|
|
||||||
|
|
||||||
lastStateChangeTime = currentTime; // Reset the last state change time
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (currentState)
|
|
||||||
{
|
|
||||||
case DataLinkState::Idle:
|
|
||||||
// Decide if the device should start transmitting
|
|
||||||
currentState = DataLinkState::Receiving;
|
|
||||||
if (shouldTransmit())
|
|
||||||
{
|
|
||||||
currentState = DataLinkState::WaitTobuildPacket;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DataLinkState::WaitTobuildPacket:
|
|
||||||
constructPacket();
|
|
||||||
if (isTransmitting)
|
|
||||||
{
|
|
||||||
currentState = DataLinkState::Transmitting;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DataLinkState::Transmitting:
|
|
||||||
sendNextByte();
|
|
||||||
|
|
||||||
// Check if the transmission is complete
|
|
||||||
if (transmissionComplete)
|
|
||||||
{
|
|
||||||
transmissionComplete = false;
|
|
||||||
isTransmitting = false;
|
|
||||||
currentState = DataLinkState::WaitingForAck; // Move to WaitingForAck state
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DataLinkState::WaitingForAck:
|
|
||||||
|
|
||||||
if (ackTimeout())
|
|
||||||
{
|
|
||||||
// Handle ACK timeout scenario
|
|
||||||
transmissionError = true;
|
|
||||||
lastAcknowledgeStatus = -1;
|
|
||||||
//--- if no ACK's etc received may as well move to Transmitting
|
|
||||||
currentState = DataLinkState::Idle;
|
|
||||||
}
|
|
||||||
if (ackReceived())
|
|
||||||
{
|
|
||||||
// No data to send from the other device
|
|
||||||
currentState = DataLinkState::Idle;
|
|
||||||
}
|
|
||||||
if (requestToSend)
|
|
||||||
{
|
|
||||||
// The other device has data to send (indicated by ACK+RTT)
|
|
||||||
currentState = DataLinkState::Receiving;
|
|
||||||
requestToSend = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
case DataLinkState::Receiving:
|
|
||||||
read();
|
|
||||||
if (readComplete)
|
|
||||||
{
|
|
||||||
readComplete = false;
|
|
||||||
currentState = DataLinkState::SendingAck;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DataLinkState::SendingAck:
|
|
||||||
|
|
||||||
constructPacket();
|
|
||||||
|
|
||||||
if (muteAcknowledgement && (needToACK || needToNACK))
|
|
||||||
{
|
|
||||||
needToACK = false;
|
|
||||||
needToNACK = false;
|
|
||||||
}
|
|
||||||
uint8_t ack;
|
|
||||||
// now it is known which acknoledge need sending since last Reception
|
|
||||||
if (needToACK)
|
|
||||||
{
|
|
||||||
needToACK = false;
|
|
||||||
ack = (txBufferIndex > 5) ? ACK_RTT_CODE : ACK_CODE;
|
|
||||||
serial.write(ack);
|
|
||||||
}
|
|
||||||
if (needToNACK)
|
|
||||||
{
|
|
||||||
needToNACK = false;
|
|
||||||
ack = (txBufferIndex > 5) ? NACK_RTT_CODE : NACK_CODE;
|
|
||||||
serial.write(ack);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentState = DataLinkState::Idle;
|
|
||||||
if (isTransmitting)
|
|
||||||
{
|
|
||||||
currentState = DataLinkState::Wait;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DataLinkState::Wait:
|
|
||||||
{
|
|
||||||
static unsigned long waitTimer=0;
|
|
||||||
if (waitTimer == 0) waitTimer = currentTime;
|
|
||||||
if (currentTime - waitTimer > 20)
|
|
||||||
{
|
|
||||||
waitTimer=0;
|
|
||||||
currentState = DataLinkState::Transmitting;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
currentState = DataLinkState::Idle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SerialDataLink::updateState(DataLinkState newState)
|
|
||||||
{
|
|
||||||
if (currentState != newState)
|
|
||||||
{
|
|
||||||
currentState = newState;
|
|
||||||
lastStateChangeTime = millis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SerialDataLink::shouldTransmit()
|
|
||||||
{
|
|
||||||
// Priority condition: Device with transmitID = 1 and receiveID = 0 has the highest priority
|
|
||||||
if (transmitID == 1 && receiveID == 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SerialDataLink::constructPacket()
|
|
||||||
{
|
|
||||||
if (maxIndexTX <1) return;
|
|
||||||
if (!isTransmitting)
|
|
||||||
{
|
|
||||||
lastTransmissionTime = millis();
|
|
||||||
txBufferIndex = 0; // Reset the TX buffer index
|
|
||||||
addToTxBuffer(headerChar);
|
|
||||||
addToTxBuffer(transmitID);
|
|
||||||
addToTxBuffer(0); // EOT position - place holder
|
|
||||||
unsigned long currentTime = millis();
|
|
||||||
int count = txBufferIndex;
|
|
||||||
for (uint8_t i = 0; i < maxIndexTX; i++)
|
|
||||||
{
|
|
||||||
if (dataUpdated[i] || (currentTime - lastSent[i] >= updateInterval))
|
|
||||||
{
|
|
||||||
addToTxBuffer(i);
|
|
||||||
convert.i16 = dataArrayTX[i];
|
|
||||||
addToTxBuffer(convert.high);
|
|
||||||
addToTxBuffer(convert.low);
|
|
||||||
|
|
||||||
dataUpdated[i] = false;
|
|
||||||
lastSent[i] = currentTime; // Update the last sent time for this index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == txBufferIndex)
|
|
||||||
{
|
|
||||||
// No data was added to the buffer, so no need to send a packet
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
addToTxBuffer(eotChar);
|
|
||||||
//----- assign EOT position
|
|
||||||
txBuffer[2] = txBufferIndex - 1;
|
|
||||||
uint16_t crc = calculateCRC16(txBuffer, txBufferIndex);
|
|
||||||
convert.u16 = crc;
|
|
||||||
addToTxBuffer(convert.high);
|
|
||||||
addToTxBuffer(convert.low);
|
|
||||||
isTransmitting = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SerialDataLink::addToTxBuffer(uint8_t byte)
|
|
||||||
{
|
|
||||||
if (txBufferIndex < txBufferSize)
|
|
||||||
{
|
|
||||||
txBuffer[txBufferIndex] = byte;
|
|
||||||
txBufferIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SerialDataLink::sendNextByte()
|
|
||||||
{
|
|
||||||
if (!isTransmitting) return false;
|
|
||||||
|
|
||||||
if (txBufferIndex >= txBufferSize)
|
|
||||||
{
|
|
||||||
txBufferIndex = 0; // Reset the TX buffer index
|
|
||||||
isTransmitting = false;
|
|
||||||
return false; // Buffer was fully sent, end transmission
|
|
||||||
}
|
|
||||||
serial.write(txBuffer[sendBufferIndex]);
|
|
||||||
sendBufferIndex++;
|
|
||||||
|
|
||||||
if (sendBufferIndex >= txBufferIndex)
|
|
||||||
{
|
|
||||||
isTransmitting = false;
|
|
||||||
txBufferIndex = 0; // Reset the TX buffer index for the next packet
|
|
||||||
sendBufferIndex = 0;
|
|
||||||
transmissionComplete = true;
|
|
||||||
return true; // Packet was fully sent
|
|
||||||
}
|
|
||||||
return false; // More bytes remain to be sent
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SerialDataLink::ackReceived()
|
|
||||||
{
|
|
||||||
|
|
||||||
// Check if there is data available to read
|
|
||||||
int count = 0;
|
|
||||||
if (serial.available() )
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
// Peek at the next byte without removing it from the buffer
|
|
||||||
uint8_t nextByte = serial.peek();
|
|
||||||
|
|
||||||
if (nextByte == headerChar)
|
|
||||||
{
|
|
||||||
requestToSend = true;
|
|
||||||
transmissionError = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t receivedByte = serial.read();
|
|
||||||
|
|
||||||
switch (receivedByte)
|
|
||||||
{
|
|
||||||
case ACK_CODE:
|
|
||||||
// Handle standard ACK
|
|
||||||
lastAcknowledgeStatus = 1;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case ACK_RTT_CODE:
|
|
||||||
// Handle ACK with request to transmit
|
|
||||||
requestToSend = true;
|
|
||||||
lastAcknowledgeStatus = 1;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case NACK_RTT_CODE:
|
|
||||||
requestToSend = true;
|
|
||||||
case NACK_CODE:
|
|
||||||
transmissionError = true;
|
|
||||||
lastAcknowledgeStatus = -2;
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false; // No ACK, NACK, or new packet received
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SerialDataLink::ackTimeout()
|
|
||||||
{
|
|
||||||
// Check if the current time has exceeded the last transmission time by the ACK timeout period
|
|
||||||
|
|
||||||
if (millis() - lastTransmissionTime > ACK_TIMEOUT)
|
|
||||||
{
|
|
||||||
return true; // Timeout occurred
|
|
||||||
}
|
|
||||||
return false; // No timeout
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void SerialDataLink::read()
|
|
||||||
{
|
|
||||||
if (maxIndexRX < 1) return;
|
|
||||||
int count = 0;
|
|
||||||
while (serial.available() && count < 10)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
if (millis() - lastHeaderTime > PACKET_TIMEOUT && rxBufferIndex > 0)
|
|
||||||
{
|
|
||||||
// Timeout occurred, reset buffer and pointer
|
|
||||||
rxBufferIndex = 0;
|
|
||||||
eotPosition = 0;
|
|
||||||
readError = true;
|
|
||||||
}
|
|
||||||
uint8_t incomingByte = serial.read();
|
|
||||||
switch (rxBufferIndex) {
|
|
||||||
case 0: // Looking for the header
|
|
||||||
if (incomingByte == headerChar)
|
|
||||||
{
|
|
||||||
lastHeaderTime = millis();
|
|
||||||
rxBuffer[rxBufferIndex] = incomingByte;
|
|
||||||
rxBufferIndex++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // Looking for the address
|
|
||||||
if (incomingByte == receiveID) {
|
|
||||||
rxBuffer[rxBufferIndex] = incomingByte;
|
|
||||||
rxBufferIndex++;
|
|
||||||
} else {
|
|
||||||
// Address mismatch, reset to look for a new packet
|
|
||||||
rxBufferIndex = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // EOT position
|
|
||||||
eotPosition = incomingByte;
|
|
||||||
rxBuffer[rxBufferIndex] = incomingByte;
|
|
||||||
rxBufferIndex++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Normal data handling
|
|
||||||
rxBuffer[rxBufferIndex] = incomingByte;
|
|
||||||
rxBufferIndex++;
|
|
||||||
|
|
||||||
if (isCompletePacket())
|
|
||||||
{
|
|
||||||
processPacket();
|
|
||||||
rxBufferIndex = 0; // Reset for the next packet
|
|
||||||
readComplete = true; // Indicate that read operation is complete
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for buffer overflow
|
|
||||||
if (rxBufferIndex >= rxBufferSize)
|
|
||||||
{
|
|
||||||
rxBufferIndex = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SerialDataLink::isCompletePacket() {
|
|
||||||
if (rxBufferIndex - 3 < eotPosition) return false;
|
|
||||||
// Ensure there are enough bytes for EOT + 2-byte CRC
|
|
||||||
|
|
||||||
// Check if the third-last byte is the EOT character
|
|
||||||
if (rxBuffer[eotPosition] == eotChar)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SerialDataLink::checkCRC()
|
|
||||||
{
|
|
||||||
uint16_t receivedCrc;
|
|
||||||
if (rxBufferIndex < 3)
|
|
||||||
{
|
|
||||||
// Not enough data for CRC check
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
convert.high = rxBuffer[rxBufferIndex - 2];
|
|
||||||
convert.low = rxBuffer[rxBufferIndex - 1];
|
|
||||||
receivedCrc = convert.u16;
|
|
||||||
|
|
||||||
// Calculate CRC for the received data (excluding the CRC bytes themselves)
|
|
||||||
uint16_t calculatedCrc = calculateCRC16(rxBuffer, rxBufferIndex - 2);
|
|
||||||
return receivedCrc == calculatedCrc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SerialDataLink::processPacket()
|
|
||||||
{
|
|
||||||
|
|
||||||
if (!checkCRC()) {
|
|
||||||
// CRC check failed, handle the error
|
|
||||||
readError = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start from index 3 to skip the SOT and ADDRESS and EOT Position characters
|
|
||||||
uint8_t i = 3;
|
|
||||||
while (i < eotPosition)
|
|
||||||
{
|
|
||||||
uint8_t arrayID = rxBuffer[i++];
|
|
||||||
|
|
||||||
// Make sure there's enough data for a complete int16 (2 bytes)
|
|
||||||
if (i + 1 >= rxBufferIndex) {
|
|
||||||
readError = true;
|
|
||||||
needToNACK = true;
|
|
||||||
return; // Incomplete packet or buffer overflow
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combine the next two bytes into an int16 value
|
|
||||||
int16_t value = (int16_t(rxBuffer[i]) << 8) | int16_t(rxBuffer[i + 1]);
|
|
||||||
i += 2;
|
|
||||||
|
|
||||||
// Handle the array ID and value here
|
|
||||||
if (arrayID < dataArraySizeRX) {
|
|
||||||
dataArrayRX[arrayID] = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Handle invalid array ID
|
|
||||||
readError = true;
|
|
||||||
needToNACK = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
newData = true;
|
|
||||||
needToACK = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void SerialDataLink::setUpdateInterval(unsigned long interval) {
|
|
||||||
updateInterval = interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SerialDataLink::setAckTimeout(unsigned long timeout) {
|
|
||||||
ACK_TIMEOUT = timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SerialDataLink::setPacketTimeout(unsigned long timeout) {
|
|
||||||
PACKET_TIMEOUT = timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SerialDataLink::setHeaderChar(char header)
|
|
||||||
{
|
|
||||||
headerChar = header;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SerialDataLink::setEOTChar(char eot)
|
|
||||||
{
|
|
||||||
eotChar = eot;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint16_t SerialDataLink::calculateCRC16(const uint8_t* data, size_t length)
|
|
||||||
{
|
|
||||||
uint16_t crc = 0xFFFF; // Start value for CRC
|
|
||||||
for (size_t i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
uint8_t index = (crc >> 8) ^ data[i];
|
|
||||||
crc = (crc << 8) ^ crcTable[index];
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
|
@ -1,185 +0,0 @@
|
||||||
/**
|
|
||||||
* @file SerialDataLink.h
|
|
||||||
* @brief Half-Duplex Serial Data Link for Arduino
|
|
||||||
*
|
|
||||||
* This file contains the definition of the SerialDataLink class, designed to facilitate
|
|
||||||
* half-duplex communication between Arduino controllers. The class employs a non-blocking,
|
|
||||||
* poll-based approach to transmit and receive data, making it suitable for applications
|
|
||||||
* where continuous monitoring and variable transfer between controllers are required.
|
|
||||||
*
|
|
||||||
* The half-duplex nature of this implementation allows for data transfer in both directions,
|
|
||||||
* but not simultaneously, ensuring a controlled communication flow and reducing the likelihood
|
|
||||||
* of data collision.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author MackElec
|
|
||||||
* @web https://github.com/mackelec/SerialDataLink
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ... Class definition ...
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class SerialDataLink
|
|
||||||
* @brief Class for managing half-duplex serial communication.
|
|
||||||
*
|
|
||||||
* Provides functions to send and receive data in a half-duplex manner over a serial link.
|
|
||||||
* It supports non-blocking operation with a polling approach to check for new data and
|
|
||||||
* transmission errors.
|
|
||||||
*
|
|
||||||
* Public Methods:
|
|
||||||
* - SerialDataLink(): Constructor to initialize the communication parameters.
|
|
||||||
* - run(): Main method to be called frequently to handle data transmission and reception.
|
|
||||||
* - updateData(): Method to update data to be transmitted.
|
|
||||||
* - getReceivedData(): Retrieves data received from the serial link.
|
|
||||||
* - checkNewData(): Checks if new data has been received.
|
|
||||||
* - checkTransmissionError(): Checks for transmission errors.
|
|
||||||
* - checkReadError(): Checks for read errors.
|
|
||||||
* - setUpdateInterval(): Sets the interval for data updates.
|
|
||||||
* - setAckTimeout(): Sets the timeout for acknowledgments.
|
|
||||||
* - setPacketTimeout(): Sets the timeout for packet reception.
|
|
||||||
* - setHeaderChar(): Sets the character used to denote the start of a packet.
|
|
||||||
* - setEOTChar(): Sets the character used to denote the end of a packet.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef SERIALDATALINK_H
|
|
||||||
#define SERIALDATALINK_H
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SerialDataLink {
|
|
||||||
public:
|
|
||||||
// Constructor
|
|
||||||
SerialDataLink(Stream &serial, uint8_t transmitID, uint8_t receiveID, uint8_t maxIndexTX, uint8_t maxIndexRX, bool enableRetransmit = false);
|
|
||||||
|
|
||||||
// Method to handle data transmission and reception
|
|
||||||
void run();
|
|
||||||
|
|
||||||
void updateData(uint8_t index, int16_t value);
|
|
||||||
|
|
||||||
// Check if new data has been received
|
|
||||||
bool checkNewData(bool resetFlag);
|
|
||||||
int16_t getReceivedData(uint8_t index);
|
|
||||||
|
|
||||||
// Check for errors
|
|
||||||
bool checkTransmissionError(bool resetFlag);
|
|
||||||
int getLastAcknowledge(bool resetFlag);
|
|
||||||
bool checkReadError(bool resetFlag);
|
|
||||||
|
|
||||||
// Setter methods for various parameters and special characters
|
|
||||||
|
|
||||||
void setUpdateInterval(unsigned long interval);
|
|
||||||
void setAckTimeout(unsigned long timeout);
|
|
||||||
void setPacketTimeout(unsigned long timeout);
|
|
||||||
|
|
||||||
void setHeaderChar(char header);
|
|
||||||
void setEOTChar(char eot);
|
|
||||||
void muteACK(bool mute);
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum class DataLinkState
|
|
||||||
{
|
|
||||||
Idle,
|
|
||||||
WaitTobuildPacket,
|
|
||||||
Transmitting,
|
|
||||||
WaitingForAck,
|
|
||||||
Receiving,
|
|
||||||
SendingAck,
|
|
||||||
Wait,
|
|
||||||
Error
|
|
||||||
};
|
|
||||||
|
|
||||||
DataLinkState currentState;
|
|
||||||
Stream &serial;
|
|
||||||
uint8_t transmitID;
|
|
||||||
uint8_t receiveID;
|
|
||||||
|
|
||||||
// Separate max indices for TX and RX
|
|
||||||
const uint8_t maxIndexTX;
|
|
||||||
const uint8_t maxIndexRX;
|
|
||||||
|
|
||||||
|
|
||||||
// Buffer and state management
|
|
||||||
static const uint8_t txBufferSize = 128; // Adjust size as needed
|
|
||||||
static const uint8_t rxBufferSize = 128; // Adjust size as needed
|
|
||||||
|
|
||||||
uint8_t txBuffer[txBufferSize];
|
|
||||||
uint8_t rxBuffer[rxBufferSize];
|
|
||||||
|
|
||||||
uint8_t txBufferIndex;
|
|
||||||
uint8_t rxBufferIndex;
|
|
||||||
uint8_t sendBufferIndex = 0;
|
|
||||||
|
|
||||||
bool isTransmitting;
|
|
||||||
bool transmissionComplete = false;
|
|
||||||
bool isReceiving;
|
|
||||||
bool readComplete = false;
|
|
||||||
bool retransmitEnabled;
|
|
||||||
bool transmissionError = false;
|
|
||||||
int lastAcknowledgeStatus = 0;
|
|
||||||
bool readError = false;
|
|
||||||
bool muteAcknowledgement = false;
|
|
||||||
|
|
||||||
// Data arrays and update management
|
|
||||||
|
|
||||||
static const uint8_t dataArraySizeTX = 20; // Adjust size as needed for TX
|
|
||||||
static const uint8_t dataArraySizeRX = 20; // Adjust size as needed for RX
|
|
||||||
|
|
||||||
int16_t dataArrayTX[dataArraySizeTX];
|
|
||||||
int16_t dataArrayRX[dataArraySizeRX];
|
|
||||||
bool dataUpdated[dataArraySizeTX];
|
|
||||||
unsigned long lastSent[dataArraySizeTX];
|
|
||||||
|
|
||||||
// times in milliseconds
|
|
||||||
unsigned long updateInterval = 1000;
|
|
||||||
unsigned long ACK_TIMEOUT = 200;
|
|
||||||
unsigned long PACKET_TIMEOUT = 200;
|
|
||||||
unsigned long stateChangeTimeout = 300;
|
|
||||||
|
|
||||||
unsigned long lastStateChangeTime = 0;
|
|
||||||
|
|
||||||
|
|
||||||
// Special characters for packet framing
|
|
||||||
char headerChar = '<';
|
|
||||||
char eotChar = '>';
|
|
||||||
|
|
||||||
static const uint8_t ACK_CODE = 0x06; // Standard acknowledgment
|
|
||||||
static const uint8_t ACK_RTT_CODE = 0x07; // Acknowledgment with request to transmit
|
|
||||||
static const uint8_t NACK_CODE = 0x08; // Negative acknowledgment
|
|
||||||
static const uint8_t NACK_RTT_CODE = 0x09; // Negative acknowledgment with request to transmit
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Internal methods for packet construction, transmission, and reception
|
|
||||||
bool shouldTransmit();
|
|
||||||
void constructPacket();
|
|
||||||
void addToTxBuffer(uint8_t byte);
|
|
||||||
bool sendNextByte();
|
|
||||||
bool ackReceived();
|
|
||||||
bool ackTimeout();
|
|
||||||
void updateState(DataLinkState newState);
|
|
||||||
|
|
||||||
// Internal methods for reception
|
|
||||||
void read();
|
|
||||||
void handleResendRequest();
|
|
||||||
bool isCompletePacket();
|
|
||||||
void processPacket();
|
|
||||||
void sendACK();
|
|
||||||
bool checkCRC();
|
|
||||||
uint16_t calculateCRC16(const uint8_t* data, size_t length);
|
|
||||||
|
|
||||||
unsigned long lastTransmissionTime;
|
|
||||||
bool requestToSend = false;
|
|
||||||
unsigned long lastHeaderTime = 0;
|
|
||||||
bool newData = false;
|
|
||||||
bool needToACK = false;
|
|
||||||
bool needToNACK = false;
|
|
||||||
uint8_t eotPosition = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SERIALDATALINK_H
|
|
Loading…
Add table
Add a link
Reference in a new issue