From a1aea67d306c894336b67f8efa17e0176f6b914c Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 19 Nov 2023 22:23:35 +0200 Subject: [PATCH 1/6] Add libraries --- README.md | 7 +- Software/Software.ino | 1 + .../SerialDataLink.cpp | 531 ++++++++++++++++++ .../mackelec-SerialDataLink/SerialDataLink.h | 170 ++++++ 4 files changed, 708 insertions(+), 1 deletion(-) create mode 100644 Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp create mode 100644 Software/src/lib/mackelec-SerialDataLink/SerialDataLink.h diff --git a/README.md b/README.md index 3e34d254..0ee4b3e6 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,12 @@ This video explains all the above mentioned steps: https://youtu.be/_mH2AjnAjDk ## Dependencies 📖 -This code uses two libraries, ESP32-Arduino-CAN (https://github.com/miwagner/ESP32-Arduino-CAN/) slightly modified for this usecase, and the eModbus library (https://github.com/eModbus/eModbus). Both these are already located in the Software folder for an easy start. +This code uses the following libraries, already located in the lib folder for an easy start: +- ESP32-Arduino-CAN (https://github.com/miwagner/ESP32-Arduino-CAN/) slightly modified for this usecase +- eModbus library (https://github.com/eModbus/eModbus) +- Adafruit Neopixel (https://github.com/adafruit/Adafruit_NeoPixel) +- mackelec SerialDataLink (https://github.com/mackelec/SerialDataLink) +- pierremolinaro acan2515 (https://github.com/pierremolinaro/acan2515) It is also based on the info found in the following excellent repositories/websites: - https://gitlab.com/pelle8/gen24 diff --git a/Software/Software.ino b/Software/Software.ino index 9042202f..358711df 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -11,6 +11,7 @@ #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" diff --git a/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp b/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp new file mode 100644 index 00000000..522b8811 --- /dev/null +++ b/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp @@ -0,0 +1,531 @@ +// 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::run() +{ + 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 + + 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; + } +} + +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 new file mode 100644 index 00000000..163d9441 --- /dev/null +++ b/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.h @@ -0,0 +1,170 @@ +/** + * @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); + +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; + + // 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 + + // 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(); + + // 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 154cd7f001cc521b1569dee741574a9ac71b3ef6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 19 Nov 2023 22:50:10 +0200 Subject: [PATCH 2/6] Add skeleton for SerialLink --- Software/Software.ino | 31 +++++++++++++++++++++++++++++++ Software/USER_SETTINGS.h | 2 ++ 2 files changed, 33 insertions(+) diff --git a/Software/Software.ino b/Software/Software.ino index 358711df..f6403570 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -17,7 +17,9 @@ // Interval settings int intervalUpdateValues = 4800; // Interval at which to update inverter values / Modbus registers +const int interval1 = 1; // Interval for 1ms tasks const int interval10 = 10; // Interval for 10ms tasks +unsigned long previousMillis1ms = 0; unsigned long previousMillis10ms = 50; unsigned long previousMillisUpdateVal = 0; @@ -129,6 +131,9 @@ void loop() { #ifdef DUAL_CAN receive_can2(); #endif +#ifdef SERIAL_LINK_TRANSMITTER_INVERTER + receive_serial(); +#endif // Process if (millis() - previousMillis10ms >= interval10) // Every 10ms @@ -151,6 +156,9 @@ void loop() { #ifdef DUAL_CAN send_can2(); #endif +#ifdef SERIAL_LINK_RECEIVER_FROM_BATTERY + send_serial(); +#endif } // Initialization functions @@ -218,6 +226,13 @@ void init_modbus() { pinMode(PIN_5V_EN, OUTPUT); digitalWrite(PIN_5V_EN, HIGH); +#if defined(SERIAL_LINK_RECEIVER_FROM_BATTERY) || defined(SERIAL_LINK_TRANSMITTER_INVERTER) + Serial2.begin(9600); // If the Modbus RTU port will be used for serial link +#if defined(BYD_MODBUS) || defined(LUNA2000_MODBUS) +#error Modbus pins cannot be used for Serial and Modbus at the same time! +#endif +#endif + #ifdef BYD_MODBUS // Init Static data to the RTU Modbus handle_static_data_modbus_byd(); @@ -384,6 +399,22 @@ void send_can() { #endif } +void send_serial() { + static unsigned long currentMillis = millis(); + if (currentMillis - previousMillis1ms >= interval1) { + previousMillis1ms = currentMillis; + manageSerialLinkReceiver(); + } +} + +void receive_serial() { + static unsigned long currentMillis = millis(); + if (currentMillis - previousMillis1ms >= interval1) { + previousMillis1ms = currentMillis; + manageSerialLinkTransmitter(); + } +} + #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 c05068c6..83c5e617 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -42,5 +42,7 @@ //#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence //#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM logic for contactors, which lower power consumption and heat generation //#define DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 controller (Needed for FoxESS inverters) +#define SERIAL_LINK_RECEIVER_FROM_BATTERY //Enable this line to send battery data over Modbus pins to another Lilygo +#define SERIAL_LINK_TRANSMITTER_INVERTER //Enable this line to receive battery data over Modbus pins from another Lilygo #endif From 42f79ca0b7da1f7f96edd6e32a18de7e83ea5559 Mon Sep 17 00:00:00 2001 From: mackelec Date: Tue, 21 Nov 2023 06:59:12 +1000 Subject: [PATCH 3/6] Add files via upload --- Software/src/battery/BATTERIES.h | 5 ++ .../SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp | 86 +++++++++++++++++++ .../SERIAL-LINK-RECEIVER-FROM-BATTERY.h | 51 +++++++++++ 3 files changed, 142 insertions(+) create mode 100644 Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp create mode 100644 Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.h diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index d68a892f..1880d8e8 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -32,4 +32,9 @@ #ifdef TEST_FAKE_BATTERY #include "TEST-FAKE-BATTERY.h" //See this file for more Fake battery settings #endif + +#ifdef SERIAL_LINK_RECEIVER_FROM_BATTERY +#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h" +#endif + #endif diff --git a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp new file mode 100644 index 00000000..55641393 --- /dev/null +++ b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp @@ -0,0 +1,86 @@ +// 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() +{ + dataLinkReceive.run(); + bool readError = dataLinkReceive.checkReadError(true); // check for error & clear error flag + if (dataLinkReceive.checkNewData(true)) // true = clear Flag + { + __getData(); + } + + + + #ifdef INVERTER_SEND_NUM_VARIABLES + static bool initLink=false; + static unsigned long updateTime = 0; + if (! initLink) + { + initLink = true; + // sends variables every 5000mS even if no change + dataLinkReceive.setUpdateInterval(5000); + } + unsigned long currentTime = millis(); + 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 new file mode 100644 index 00000000..04ebe799 --- /dev/null +++ b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.h @@ -0,0 +1,51 @@ +// 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 "../lib/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 + + + + + + +void manageSerialLinkReceiver(); + + + + + + + + + + +#endif \ No newline at end of file From 3379cba2372a1da1c433f8fd8889dc7de036e669 Mon Sep 17 00:00:00 2001 From: mackelec Date: Tue, 21 Nov 2023 07:00:38 +1000 Subject: [PATCH 4/6] Add files via upload --- Software/src/inverter/INVERTERS.h | 4 + .../SERIAL-LINK-TRANSMITTER-INVERTER.cpp | 81 +++++++++++++++++++ .../SERIAL-LINK-TRANSMITTER-INVERTER.h | 41 ++++++++++ 3 files changed, 126 insertions(+) create mode 100644 Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp create mode 100644 Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h index 97527c2b..9d8825f7 100644 --- a/Software/src/inverter/INVERTERS.h +++ b/Software/src/inverter/INVERTERS.h @@ -29,4 +29,8 @@ #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 new file mode 100644 index 00000000..f25479cd --- /dev/null +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp @@ -0,0 +1,81 @@ +//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; + + 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); + // todo some error management - LEDS etc + + 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); + } + + +} \ No newline at end of file diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h new file mode 100644 index 00000000..8a54741e --- /dev/null +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h @@ -0,0 +1,41 @@ +//SERIAL-LINK-TRANSMITTER-INVERTER.h + +#ifndef SERIAL_LINK_TRANSMITTER_INVERTER_H +#define SERIAL_LINK_TRANSMITTER_INVERTER_H + + +#include +#include "../../USER_SETTINGS.h" +#include "../lib/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 + + + +void manageSerialLinkTransmitter(); + + + + + + + + +#endif \ No newline at end of file From 0bfb757dffda995c507f8ee7bad2773715c2ed87 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:46:49 +0200 Subject: [PATCH 5/6] Add fixes from AndyMack-e- --- Software/Software.ino | 4 + Software/USER_SETTINGS.h | 4 +- .../SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp | 114 +++++++++--------- .../SERIAL-LINK-RECEIVER-FROM-BATTERY.h | 21 +--- .../SERIAL-LINK-TRANSMITTER-INVERTER.cpp | 110 ++++++++--------- .../SERIAL-LINK-TRANSMITTER-INVERTER.h | 19 +-- 6 files changed, 122 insertions(+), 150 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index f6403570..8041e190 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -399,6 +399,7 @@ void send_can() { #endif } +#ifdef SERIAL_LINK_RECEIVER_FROM_BATTERY void send_serial() { static unsigned long currentMillis = millis(); if (currentMillis - previousMillis1ms >= interval1) { @@ -406,7 +407,9 @@ void send_serial() { manageSerialLinkReceiver(); } } +#endif +#ifdef SERIAL_LINK_TRANSMITTER_INVERTER void receive_serial() { static unsigned long currentMillis = millis(); if (currentMillis - previousMillis1ms >= interval1) { @@ -414,6 +417,7 @@ void receive_serial() { 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. diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 83c5e617..24d2491e 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -42,7 +42,7 @@ //#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence //#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM logic for contactors, which lower power consumption and heat generation //#define DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 controller (Needed for FoxESS inverters) -#define SERIAL_LINK_RECEIVER_FROM_BATTERY //Enable this line to send battery data over Modbus pins to another Lilygo -#define SERIAL_LINK_TRANSMITTER_INVERTER //Enable this line to receive battery data over Modbus pins from another Lilygo +//#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/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp index 55641393..b189eedb 100644 --- a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp +++ b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp @@ -2,85 +2,79 @@ #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 +//#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; +const uint8_t sendingNumVariables = INVERTER_SEND_NUM_VARIABLES; #else - const uint8_t sendingNumVariables = 0; +const uint8_t sendingNumVariables = 0; #endif // txid,rxid, num_send,num_recv SerialDataLink dataLinkReceive(Serial2, 0, 0x01, sendingNumVariables, - INVERTER_RECV_NUM_VARIABLES); // ... + 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 __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 - /* +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() -{ - dataLinkReceive.run(); - bool readError = dataLinkReceive.checkReadError(true); // check for error & clear error flag - if (dataLinkReceive.checkNewData(true)) // true = clear Flag - { - __getData(); - } - - - - #ifdef INVERTER_SEND_NUM_VARIABLES - static bool initLink=false; - static unsigned long updateTime = 0; - if (! initLink) - { - initLink = true; - // sends variables every 5000mS even if no change - dataLinkReceive.setUpdateInterval(5000); - } - unsigned long currentTime = millis(); - if (currentTime - updateTime > 100) - { - updateTime = currentTime; - dataLinkReceive.run(); - bool sendError = dataLinkReceive.checkTransmissionError(true); // check for error & clear error flag - updateData(); - } - #endif +void manageSerialLinkReceiver() { + dataLinkReceive.run(); + bool readError = dataLinkReceive.checkReadError(true); // check for error & clear error flag + LEDcolor = GREEN; + if (readError) { + LEDcolor = RED; + Serial.println("ERROR: Serial Data Link - Read Error"); + } + if (dataLinkReceive.checkNewData(true)) // true = clear Flag + { + __getData(); + } + +#ifdef INVERTER_SEND_NUM_VARIABLES + static bool initLink = false; + static unsigned long updateTime = 0; + if (!initLink) { + initLink = true; + // sends variables every 5000mS even if no change + dataLinkReceive.setUpdateInterval(5000); + } + unsigned long currentTime = millis(); + 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 index 04ebe799..65dc04fd 100644 --- a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.h +++ b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.h @@ -5,7 +5,8 @@ #include #include "../../USER_SETTINGS.h" -#include "../lib/SerialDataLink/SerialDataLink.h" +#include "../devboard/config.h" // Needed for LED defines +#include "../lib/mackelec-SerialDataLink/SerialDataLink.h" // https://github.com/mackelec/SerialDataLink @@ -13,7 +14,6 @@ 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) @@ -31,21 +31,8 @@ extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 funct 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 \ No newline at end of file +#endif diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp index f25479cd..e0f62c62 100644 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp @@ -8,74 +8,70 @@ * 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 +#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; +const uint8_t receivingNumVariables = BATTERY_RECV_NUM_VARIABLES; #else - const uint8_t receivingNumVariables = 0; +const uint8_t receivingNumVariables = 0; #endif // txid,rxid,num_tx,num_rx -SerialDataLink dataLinkTransmit(Serial2, 0x01, 0, BATTERY_SEND_NUM_VARIABLES, - receivingNumVariables ); +SerialDataLink dataLinkTransmit(Serial2, 0x01, 0, BATTERY_SEND_NUM_VARIABLES, receivingNumVariables); - -void _getData() -{ - /* +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; + + dataLinkTransmit.run(); -void manageSerialLinkTransmitter() -{ - static bool initLink=false; - static unsigned long updateTime = 0; - - 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); - // todo some error management - LEDS etc - - 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); - } - - -} \ No newline at end of file +#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.println("ERROR: Serial Data Link - SEND Error"); + } + // todo some error management - LEDS etc + + 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 index 8a54741e..68625b89 100644 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.h @@ -1,13 +1,12 @@ //SERIAL-LINK-TRANSMITTER-INVERTER.h #ifndef SERIAL_LINK_TRANSMITTER_INVERTER_H -#define SERIAL_LINK_TRANSMITTER_INVERTER_H - +#define SERIAL_LINK_TRANSMITTER_INVERTER_H #include #include "../../USER_SETTINGS.h" -#include "../lib/SerialDataLink/SerialDataLink.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) @@ -26,16 +25,8 @@ extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 funct 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 \ No newline at end of file +#endif From 6fd2ca52db59a5811f11d8038883bd015e1e49e3 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 10:45:15 +0200 Subject: [PATCH 6/6] More fixes from Andy --- .../SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp | 53 +++++++++++++++---- .../SERIAL-LINK-TRANSMITTER-INVERTER.cpp | 12 ++++- .../SerialDataLink.cpp | 39 +++++++++++++- .../mackelec-SerialDataLink/SerialDataLink.h | 8 ++- 4 files changed, 99 insertions(+), 13 deletions(-) diff --git a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp index b189eedb..9d286324 100644 --- a/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp +++ b/Software/src/battery/SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp @@ -49,27 +49,62 @@ void updateData() { */ 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; - Serial.println("ERROR: Serial Data Link - Read Error"); + 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; } -#ifdef INVERTER_SEND_NUM_VARIABLES - static bool initLink = false; - static unsigned long updateTime = 0; - if (!initLink) { - initLink = true; - // sends variables every 5000mS even if no change - dataLinkReceive.setUpdateInterval(5000); + 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; + } } - unsigned long currentTime = millis(); + + static unsigned long updateTime = 0; + +#ifdef INVERTER_SEND_NUM_VARIABLES if (currentTime - updateTime > 100) { updateTime = currentTime; dataLinkReceive.run(); diff --git a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp index e0f62c62..229d0bd5 100644 --- a/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp +++ b/Software/src/inverter/SERIAL-LINK-TRANSMITTER-INVERTER.cpp @@ -31,6 +31,7 @@ void _getData() { void manageSerialLinkTransmitter() { static bool initLink = false; static unsigned long updateTime = 0; + static bool lasterror = false; dataLinkTransmit.run(); @@ -53,9 +54,16 @@ void manageSerialLinkTransmitter() { LEDcolor = GREEN; if (sendError) { LEDcolor = RED; - Serial.println("ERROR: Serial Data Link - SEND Error"); + 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"); + } } - // todo some error management - LEDS etc dataLinkTransmit.updateData(0, SOC); dataLinkTransmit.updateData(1, StateOfHealth); diff --git a/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp b/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp index 522b8811..31a6902c 100644 --- a/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp +++ b/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.cpp @@ -124,8 +124,31 @@ bool SerialDataLink::checkNewData(bool resetFlag) { 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: @@ -146,7 +169,12 @@ void SerialDataLink::run() { 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) @@ -215,6 +243,15 @@ void SerialDataLink::run() } } +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 diff --git a/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.h b/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.h index 163d9441..258939c4 100644 --- a/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.h +++ b/Software/src/lib/mackelec-SerialDataLink/SerialDataLink.h @@ -76,6 +76,7 @@ class SerialDataLink { void setHeaderChar(char header); void setEOTChar(char eot); + void muteACK(bool mute); private: enum class DataLinkState @@ -115,6 +116,7 @@ class SerialDataLink { bool retransmitEnabled; bool transmissionError = false; bool readError = false; + bool muteAcknowledgement = false; // Data arrays and update management @@ -130,6 +132,9 @@ class SerialDataLink { 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 = '>'; @@ -147,7 +152,8 @@ class SerialDataLink { void addToTxBuffer(uint8_t byte); bool sendNextByte(); bool ackReceived(); - bool ackTimeout(); + bool ackTimeout(); + void updateState(DataLinkState newState); // Internal methods for reception void read();