From 59da9eac2ba72095e3e130b7e10cb73d3810f41a Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 10 Nov 2023 23:29:25 +0200 Subject: [PATCH 01/15] Add SOC% plausibility detection --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 32dd0d4a..4e5c1b2e 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -241,6 +241,14 @@ void update_values_leaf_battery() { /* This function maps all the values fetched max_target_discharge_power = 0; } + //Check if SOC% is plausible + if (battery_voltage > (ABSOLUTE_MAX_VOLTAGE - 100)) { + if (LB_SOC < 650) { + bms_status = FAULT; + Serial.println("ERROR: SOC% reported by battery not plausible. Restart battery!"); + } + } + if (LB_Full_CHARGE_flag) { //Battery reports that it is fully charged stop all further charging incase it hasn't already max_target_charge_power = 0; } From 3aa3d8ead9772ecf1f87399ddbcaa8f81ac285f4 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 19 Nov 2023 20:31:18 +0200 Subject: [PATCH 02/15] Add ramped charge value for Tesla --- Software/src/battery/TESLA-MODEL-3-BATTERY.cpp | 8 +++++--- Software/src/battery/TESLA-MODEL-3-BATTERY.h | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp index e29f1c55..23760e20 100644 --- a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp +++ b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp @@ -196,9 +196,11 @@ void update_values_tesla_model_3_battery() { //This function maps all the value //The allowed charge power behaves strangely. We instead estimate this value if (SOC == 10000) { - max_target_charge_power = 0; //When battery is 100% full, set allowed charge W to 0 - } else { - max_target_charge_power = 15000; //Otherwise we can push 15kW into the pack! + max_target_charge_power = 0; // When battery is 100% full, set allowed charge W to 0 + } else if (SOC >= 9500 && SOC <= 9999) { // When battery is between 95-99.99%, ramp the value between Max<->0 + max_target_charge_power = MAXCHARGEPOWERALLOWED * (1 - (SOC - 9500) / 500); + } else { // Outside the specified SOC range, set the charge power to the maximum + max_target_charge_power = MAXCHARGEPOWERALLOWED; } stat_batt_power = (volts * amps); //TODO: check if scaling is OK diff --git a/Software/src/battery/TESLA-MODEL-3-BATTERY.h b/Software/src/battery/TESLA-MODEL-3-BATTERY.h index 1d9081d0..15d207c8 100644 --- a/Software/src/battery/TESLA-MODEL-3-BATTERY.h +++ b/Software/src/battery/TESLA-MODEL-3-BATTERY.h @@ -7,7 +7,8 @@ #define ABSOLUTE_MAX_VOLTAGE \ 4030 // 403.0V,if battery voltage goes over this, charging is not possible (goes into forced discharge) -#define ABSOLUTE_MIN_VOLTAGE 2450 // 245.0V if battery voltage goes under this, discharging further is disabled +#define ABSOLUTE_MIN_VOLTAGE 2450 // 245.0V if battery voltage goes under this, discharging further is disabled +#define MAXCHARGEPOWERALLOWED 15000 // 15000W we use a define since the value supplied by Tesla is always 0 // These parameters need to be mapped for the Inverter extern uint16_t SOC; From 19e33727f54ab4fff2af782c71f22064e4b5a70e Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 20 Nov 2023 18:58:13 +0200 Subject: [PATCH 03/15] Raise max voltage per cell LFP --- Software/src/battery/TESLA-MODEL-3-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp index 23760e20..9d83bda7 100644 --- a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp +++ b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp @@ -156,7 +156,7 @@ static const char* hvilStatusState[] = {"NOT OK", #define MIN_CELL_VOLTAGE_NCA_NCM 2950 //Battery is put into emergency stop if one cell goes below this value #define MAX_CELL_DEVIATION_NCA_NCM 500 //LED turns yellow on the board if mv delta exceeds this value -#define MAX_CELL_VOLTAGE_LFP 3450 //Battery is put into emergency stop if one cell goes over this value +#define MAX_CELL_VOLTAGE_LFP 3500 //Battery is put into emergency stop if one cell goes over this value #define MIN_CELL_VOLTAGE_LFP 2800 //Battery is put into emergency stop if one cell goes over this value #define MAX_CELL_DEVIATION_LFP 150 //LED turns yellow on the board if mv delta exceeds this value From 1e91bd69aa2b98a622aefc3687c640edb481d6f3 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 20 Nov 2023 19:10:31 +0200 Subject: [PATCH 04/15] Tweak ramp if-statement --- Software/src/battery/TESLA-MODEL-3-BATTERY.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp index 9d83bda7..41f9cf8c 100644 --- a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp +++ b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp @@ -196,8 +196,8 @@ void update_values_tesla_model_3_battery() { //This function maps all the value //The allowed charge power behaves strangely. We instead estimate this value if (SOC == 10000) { - max_target_charge_power = 0; // When battery is 100% full, set allowed charge W to 0 - } else if (SOC >= 9500 && SOC <= 9999) { // When battery is between 95-99.99%, ramp the value between Max<->0 + max_target_charge_power = 0; // When battery is 100% full, set allowed charge W to 0 + } else if (SOC > 9500) { // When battery is between 95-99.99%, ramp the value between Max<->0 max_target_charge_power = MAXCHARGEPOWERALLOWED * (1 - (SOC - 9500) / 500); } else { // Outside the specified SOC range, set the charge power to the maximum max_target_charge_power = MAXCHARGEPOWERALLOWED; From d2c58108631ff3654983cd09a512cb6033a67c86 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 20 Nov 2023 21:48:33 +0200 Subject: [PATCH 05/15] Fix cell max volt mapping --- Software/src/inverter/SOLAX-CAN.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/inverter/SOLAX-CAN.cpp b/Software/src/inverter/SOLAX-CAN.cpp index f192ae5f..d4169a93 100644 --- a/Software/src/inverter/SOLAX-CAN.cpp +++ b/Software/src/inverter/SOLAX-CAN.cpp @@ -189,7 +189,7 @@ void update_values_can_solax() { //This function maps all the values fetched fr //BMS_PackTemps (strange name, since it has voltages?) SOLAX_1876.data.u8[2] = (uint8_t)cell_max_voltage; //TODO: scaling OK? - SOLAX_1876.data.u8[3] = (cell_min_voltage >> 8); + SOLAX_1876.data.u8[3] = (cell_max_voltage >> 8); SOLAX_1876.data.u8[6] = (uint8_t)cell_min_voltage; //TODO: scaling OK? SOLAX_1876.data.u8[7] = (cell_min_voltage >> 8); From 2f6d6cfae846da65a61e3729087fcf3f6b23e041 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 20 Nov 2023 23:14:36 +0200 Subject: [PATCH 06/15] Fix division error --- Software/src/battery/TESLA-MODEL-3-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp index 41f9cf8c..b28c8a18 100644 --- a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp +++ b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp @@ -198,7 +198,7 @@ void update_values_tesla_model_3_battery() { //This function maps all the value if (SOC == 10000) { max_target_charge_power = 0; // When battery is 100% full, set allowed charge W to 0 } else if (SOC > 9500) { // When battery is between 95-99.99%, ramp the value between Max<->0 - max_target_charge_power = MAXCHARGEPOWERALLOWED * (1 - (SOC - 9500) / 500); + max_target_charge_power = MAXCHARGEPOWERALLOWED * (1 - (SOC - 9500) / 500.0); } else { // Outside the specified SOC range, set the charge power to the maximum max_target_charge_power = MAXCHARGEPOWERALLOWED; } From 3545c3d8ba7c43578e626de5ecc34471461368de Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 20:17:07 +0200 Subject: [PATCH 07/15] Improve comment on plausability check --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 4e5c1b2e..c66c81e1 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -242,7 +242,7 @@ void update_values_leaf_battery() { /* This function maps all the values fetched } //Check if SOC% is plausible - if (battery_voltage > (ABSOLUTE_MAX_VOLTAGE - 100)) { + if (battery_voltage > (ABSOLUTE_MAX_VOLTAGE - 100)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT if (LB_SOC < 650) { bms_status = FAULT; Serial.println("ERROR: SOC% reported by battery not plausible. Restart battery!"); From e89717f1676aba2d512f9115006ce53e1c89fe30 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 20:18:59 +0200 Subject: [PATCH 08/15] Fix pre-commit error --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index c66c81e1..42cf263f 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -242,7 +242,8 @@ void update_values_leaf_battery() { /* This function maps all the values fetched } //Check if SOC% is plausible - if (battery_voltage > (ABSOLUTE_MAX_VOLTAGE - 100)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT + if (battery_voltage > + (ABSOLUTE_MAX_VOLTAGE - 100)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT if (LB_SOC < 650) { bms_status = FAULT; Serial.println("ERROR: SOC% reported by battery not plausible. Restart battery!"); From 6801e8392985de5d4410de0bd1bebd853d8dce25 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:13:30 +0200 Subject: [PATCH 09/15] Rework ramp logic --- Software/src/battery/TESLA-MODEL-3-BATTERY.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp index b28c8a18..f4d619f3 100644 --- a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp +++ b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp @@ -157,7 +157,7 @@ static const char* hvilStatusState[] = {"NOT OK", #define MAX_CELL_DEVIATION_NCA_NCM 500 //LED turns yellow on the board if mv delta exceeds this value #define MAX_CELL_VOLTAGE_LFP 3500 //Battery is put into emergency stop if one cell goes over this value -#define MIN_CELL_VOLTAGE_LFP 2800 //Battery is put into emergency stop if one cell goes over this value +#define MIN_CELL_VOLTAGE_LFP 2800 //Battery is put into emergency stop if one cell goes below this value #define MAX_CELL_DEVIATION_LFP 150 //LED turns yellow on the board if mv delta exceeds this value void update_values_tesla_model_3_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus @@ -195,12 +195,12 @@ void update_values_tesla_model_3_battery() { //This function maps all the value } //The allowed charge power behaves strangely. We instead estimate this value - if (SOC == 10000) { - max_target_charge_power = 0; // When battery is 100% full, set allowed charge W to 0 - } else if (SOC > 9500) { // When battery is between 95-99.99%, ramp the value between Max<->0 - max_target_charge_power = MAXCHARGEPOWERALLOWED * (1 - (SOC - 9500) / 500.0); - } else { // Outside the specified SOC range, set the charge power to the maximum - max_target_charge_power = MAXCHARGEPOWERALLOWED; + max_target_charge_power = MAXCHARGEPOWERALLOWED; + if (soc_vi > 950) { // When battery is between 95-99.99% real SOC, ramp the value between Max<->0 + max_target_charge_power = MAXCHARGEPOWERALLOWED * (1 - (soc_vi - 950) / 50.0); + } + if (SOC == 10000) { // When battery is defined as 100% full, set allowed charge W to 0 + max_target_charge_power = 0; } stat_batt_power = (volts * amps); //TODO: check if scaling is OK From bf292a14573b493882c4fbf0a8c118c12764481a Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:15:57 +0200 Subject: [PATCH 10/15] Add discharge SOC% safety --- Software/src/battery/TESLA-MODEL-3-BATTERY.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp index f4d619f3..4a1eaccc 100644 --- a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp +++ b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp @@ -193,6 +193,9 @@ void update_values_tesla_model_3_battery() { //This function maps all the value } else { max_target_discharge_power = temporaryvariable; } + if (SOC < 20) { // When battery is lower than 0.20% , set allowed discharge W to 0 + max_target_discharge_power = 0; + } //The allowed charge power behaves strangely. We instead estimate this value max_target_charge_power = MAXCHARGEPOWERALLOWED; From 36deffd9e930cfd3c80a3c07d515b55f4c904e72 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 22:13:09 +0200 Subject: [PATCH 11/15] Rework ramp logic --- Software/src/battery/TESLA-MODEL-3-BATTERY.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp index 4a1eaccc..325f5ac2 100644 --- a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp +++ b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp @@ -198,12 +198,12 @@ void update_values_tesla_model_3_battery() { //This function maps all the value } //The allowed charge power behaves strangely. We instead estimate this value - max_target_charge_power = MAXCHARGEPOWERALLOWED; - if (soc_vi > 950) { // When battery is between 95-99.99% real SOC, ramp the value between Max<->0 - max_target_charge_power = MAXCHARGEPOWERALLOWED * (1 - (soc_vi - 950) / 50.0); - } - if (SOC == 10000) { // When battery is defined as 100% full, set allowed charge W to 0 + if (SOC == 10000) { // When scaled SOC is 100%, set allowed charge power to 0 max_target_charge_power = 0; + } else if (soc_vi > 950) { // When real SOC is between 95-99.99%, ramp the value between Max<->0 + max_target_charge_power = MAXCHARGEPOWERALLOWED * (1 - (soc_vi - 950) / 50.0); + } else { // No limits, max charging power allowed + max_target_charge_power = MAXCHARGEPOWERALLOWED; } stat_batt_power = (volts * amps); //TODO: check if scaling is OK From e2d3c4f023e36e2f98f351f7faf16cb3d4549a7c Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 22:23:54 +0200 Subject: [PATCH 12/15] Rework discharge calculation --- Software/src/battery/TESLA-MODEL-3-BATTERY.cpp | 17 +++++++---------- Software/src/battery/TESLA-MODEL-3-BATTERY.h | 2 ++ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp index 325f5ac2..28e5a72f 100644 --- a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp +++ b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp @@ -26,7 +26,6 @@ CAN_frame_t TESLA_221_2 = { .MsgID = 0x221, .data = {0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA}}; //Contactor Frame 221 - hv_up_for_drive -static uint32_t temporaryvariable = 0; static uint32_t total_discharge = 0; static uint32_t total_charge = 0; static uint16_t volts = 0; // V @@ -186,15 +185,13 @@ void update_values_tesla_model_3_battery() { //This function maps all the value remaining_capacity_Wh = (expected_energy_remaining * 100); //Scale up 60.3kWh -> 60300Wh - //Calculate the allowed discharge power, cap it if it gets too large - temporaryvariable = (max_discharge_current * volts); - if (temporaryvariable > 60000) { - max_target_discharge_power = 60000; - } else { - max_target_discharge_power = temporaryvariable; - } - if (SOC < 20) { // When battery is lower than 0.20% , set allowed discharge W to 0 + // Define the allowed discharge power + max_target_discharge_power = (max_discharge_current * volts); + // Cap the allowed discharge power if battery is empty, or discharge power is higher than the maximum discharge power allowed + if (SOC == 0) { max_target_discharge_power = 0; + } else if (max_target_discharge_power > MAXDISCHARGEPOWERALLOWED) { + max_target_discharge_power = MAXDISCHARGEPOWERALLOWED; } //The allowed charge power behaves strangely. We instead estimate this value @@ -202,7 +199,7 @@ void update_values_tesla_model_3_battery() { //This function maps all the value max_target_charge_power = 0; } else if (soc_vi > 950) { // When real SOC is between 95-99.99%, ramp the value between Max<->0 max_target_charge_power = MAXCHARGEPOWERALLOWED * (1 - (soc_vi - 950) / 50.0); - } else { // No limits, max charging power allowed + } else { // No limits, max charging power allowed max_target_charge_power = MAXCHARGEPOWERALLOWED; } diff --git a/Software/src/battery/TESLA-MODEL-3-BATTERY.h b/Software/src/battery/TESLA-MODEL-3-BATTERY.h index 15d207c8..2775b56c 100644 --- a/Software/src/battery/TESLA-MODEL-3-BATTERY.h +++ b/Software/src/battery/TESLA-MODEL-3-BATTERY.h @@ -9,6 +9,8 @@ 4030 // 403.0V,if battery voltage goes over this, charging is not possible (goes into forced discharge) #define ABSOLUTE_MIN_VOLTAGE 2450 // 245.0V if battery voltage goes under this, discharging further is disabled #define MAXCHARGEPOWERALLOWED 15000 // 15000W we use a define since the value supplied by Tesla is always 0 +#define MAXDISCHARGEPOWERALLOWED \ + 60000 // 60000W we need to cap this value to max 60kW, most inverters overflow otherwise // These parameters need to be mapped for the Inverter extern uint16_t SOC; From 79709ea7df84d4e38b42540ce39dc1a7db5e6683 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 22:56:20 +0200 Subject: [PATCH 13/15] Add compilation error for missing battery --- Software/Software.ino | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Software/Software.ino b/Software/Software.ino index 9042202f..e02a1979 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -288,6 +288,9 @@ void inform_user_on_battery() { #ifdef TEST_FAKE_BATTERY Serial.println("Test mode with fake battery selected"); #endif +#if !defined(ABSOLUTE_MAX_VOLTAGE) +#error No battery selected! Choose one from the USER_SETTINGS.h file +#endif } // Functions From 6e183f506b74255f780789a723422baaf368cfb4 Mon Sep 17 00:00:00 2001 From: rjsc Date: Wed, 22 Nov 2023 22:47:48 +0000 Subject: [PATCH 14/15] Revert "New feature: Double LilyGo!" --- README.md | 7 +- Software/Software.ino | 36 -- Software/USER_SETTINGS.h | 2 - Software/src/battery/BATTERIES.h | 5 - .../SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp | 115 ---- .../SERIAL-LINK-RECEIVER-FROM-BATTERY.h | 38 -- Software/src/inverter/INVERTERS.h | 4 - .../SERIAL-LINK-TRANSMITTER-INVERTER.cpp | 85 --- .../SERIAL-LINK-TRANSMITTER-INVERTER.h | 32 - .../SerialDataLink.cpp | 568 ------------------ .../mackelec-SerialDataLink/SerialDataLink.h | 176 ------ 11 files changed, 1 insertion(+), 1067 deletions(-) delete mode 100644 Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp delete mode 100644 Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.h delete mode 100644 Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp delete mode 100644 Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h delete mode 100644 Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp delete mode 100644 Software/src/lib/mackelec-SerialDataLink/SerialDataLink.h diff --git a/README.md b/README.md index 0ee4b3e6..3e34d254 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,7 @@ This video explains all the above mentioned steps: https://youtu.be/_mH2AjnAjDk ## Dependencies 📖 -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) +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. It is also based on the info found in the following excellent repositories/websites: - https://gitlab.com/pelle8/gen24 diff --git a/Software/Software.ino b/Software/Software.ino index d01a10c9..e02a1979 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -11,15 +11,12 @@ #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; @@ -131,9 +128,6 @@ void loop() { #ifdef DUAL_CAN receive_can2(); #endif -#ifdef SERIAL_LINK_TRANSMITTER_INVERTER - receive_serial(); -#endif // Process if (millis() - previousMillis10ms >= interval10) // Every 10ms @@ -156,9 +150,6 @@ void loop() { #ifdef DUAL_CAN send_can2(); #endif -#ifdef SERIAL_LINK_RECEIVER_FROM_BATTERY - send_serial(); -#endif } // Initialization functions @@ -226,13 +217,6 @@ 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(); @@ -402,26 +386,6 @@ 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 diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 24d2491e..c05068c6 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -42,7 +42,5 @@ //#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 diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 1880d8e8..d68a892f 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -32,9 +32,4 @@ #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 diff --git a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp deleted file mode 100644 index 9d286324..00000000 --- a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// 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 -} diff --git a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.h b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.h deleted file mode 100644 index 65dc04fd..00000000 --- a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.h +++ /dev/null @@ -1,38 +0,0 @@ -// SERIAL-LINK-RECEIVER-FROM-BATTERY.h - -#ifndef SERIAL_LINK_RECEIVER_FROM_BATTERY_H -#define SERIAL_LINK_RECEIVER_FROM_BATTERY_H - -#include -#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 diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h index 9d8825f7..97527c2b 100644 --- a/Software/src/inverter/INVERTERS.h +++ b/Software/src/inverter/INVERTERS.h @@ -29,8 +29,4 @@ #include "SOLAX-CAN.h" #endif -#ifdef SERIAL_LINK_TRANSMITTER_INVERTER -#include "SERIAL-LINK-TRANSMITTER-INVERTER.h" -#endif - #endif diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp deleted file mode 100644 index 229d0bd5..00000000 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp +++ /dev/null @@ -1,85 +0,0 @@ -//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); - } -} diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h deleted file mode 100644 index 68625b89..00000000 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h +++ /dev/null @@ -1,32 +0,0 @@ -//SERIAL-LINK-TRANSMITTER-INVERTER.h - -#ifndef SERIAL_LINK_TRANSMITTER_INVERTER_H -#define SERIAL_LINK_TRANSMITTER_INVERTER_H - -#include -#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 diff --git a/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp b/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp deleted file mode 100644 index 31a6902c..00000000 --- a/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp +++ /dev/null @@ -1,568 +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; - - - - - -// 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; -} - -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::Transmitting; - } - break; - - case DataLinkState::Transmitting: - if (isTransmitting) - { - sendNextByte(); // Continue sending the current data - } - else - { - - constructPacket(); // Construct a new packet if not currently transmitting - - if (muteAcknowledgement) - { - 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); - } - } - - if (maxIndexTX < 1) - { - currentState = DataLinkState::Receiving; - } - // 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; - isTransmitting = false; - //handleAckTimeout(); - //--- if no ACK's etc received may as well move to Transmitting - currentState = DataLinkState::Transmitting; - } - if (ackReceived()) - { - // No data to send from the other device - currentState = DataLinkState::Transmitting; - } - if (requestToSend) - { - // The other device has data to send (indicated by ACK+RTT) - currentState = DataLinkState::Receiving; - } - break; - - - case DataLinkState::Receiving: - read(); - if (readComplete) - { - readComplete = false; - // transition to transmit mode - 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 - if (serial.available() > 0) - { - // Peek at the next byte without removing it from the buffer - uint8_t nextByte = serial.peek(); - - if (nextByte == headerChar) - { - requestToSend = true; - return false; - } - - uint8_t receivedByte = serial.read(); - - switch (receivedByte) - { - case ACK_CODE: - // Handle standard ACK - return true; - - case ACK_RTT_CODE: - // Handle ACK with request to transmit - requestToSend = true; - return true; - - case NACK_RTT_CODE: - requestToSend = true; - case NACK_CODE: - transmissionError = true; - return false; - - 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; - if (serial.available()) - { - //Serial.print("."); - 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; -} diff --git a/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.h b/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.h deleted file mode 100644 index 258939c4..00000000 --- a/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.h +++ /dev/null @@ -1,176 +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 - -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); - 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, - Transmitting, - WaitingForAck, - Receiving, - 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; - 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]; - - unsigned long updateInterval = 500; - unsigned long ACK_TIMEOUT = 100; - unsigned long PACKET_TIMEOUT = 100; // Timeout in milliseconds - - unsigned long lastStateChangeTime = 0; - unsigned long stateChangeTimeout = 200; - - // 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 From 6810c66dc847c59499e88945e35bacfbf630db91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 23 Nov 2023 21:04:45 +0200 Subject: [PATCH 15/15] Create CODE_OF_CONDUCT.md Add CODE_OF_CONDUCT --- CODE_OF_CONDUCT.md | 128 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..166b6a85 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +dalathegreat@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations.