From 4b70ee863df0b6711da561c4694f97c2e765716e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Fri, 27 Jun 2025 19:55:17 +0100 Subject: [PATCH 01/21] Serial bridge implementation --- examples/simple_repeater/main.cpp | 18 +++- platformio.ini | 1 + src/Dispatcher.cpp | 24 ++++- src/Dispatcher.h | 5 ++ src/bridge/serial/Packet.h | 58 +++++++++++++ src/bridge/serial/PacketQueue.h | 113 ++++++++++++++++++++++++ src/bridge/serial/SerialBridge.cpp | 135 +++++++++++++++++++++++++++++ src/bridge/serial/SerialBridge.h | 73 ++++++++++++++++ 8 files changed, 422 insertions(+), 5 deletions(-) create mode 100644 src/bridge/serial/Packet.h create mode 100644 src/bridge/serial/PacketQueue.h create mode 100644 src/bridge/serial/SerialBridge.cpp create mode 100644 src/bridge/serial/SerialBridge.h diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index c33cadda..4edca23d 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -72,6 +72,11 @@ static UITask ui_task(display); #endif +#ifdef BRIDGE_OVER_SERIAL +#include "bridge/serial/SerialBridge.h" +bridge::SerialBridge *bridge_interface; +#endif + #define FIRMWARE_ROLE "repeater" #define PACKET_LOG_FILE "/packet_log" @@ -752,7 +757,14 @@ void setup() { } #endif - if (!radio_init()) { halt(); } +#ifdef BRIDGE_OVER_SERIAL + bridge_interface = new bridge::SerialBridge(); + bridge_interface->setup(); +#endif + + if (!radio_init()) { + halt(); + } fast_rng.begin(radio_get_rng_seed()); @@ -825,6 +837,10 @@ void loop() { command[0] = 0; // reset command buffer } +#ifdef BRIDGE_OVER_SERIAL + bridge_interface->loop(); +#endif + the_mesh.loop(); sensors.loop(); } diff --git a/platformio.ini b/platformio.ini index 90e7cfb0..36cdf760 100644 --- a/platformio.ini +++ b/platformio.ini @@ -32,6 +32,7 @@ build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1 build_src_filter = +<*.cpp> + + + ; ----------------- ESP32 --------------------- diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 7f39dc49..08cca61e 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -1,7 +1,7 @@ #include "Dispatcher.h" #if MESH_PACKET_LOGGING - #include +#include #endif #include @@ -104,6 +104,7 @@ void Dispatcher::loop() { processRecvPacket(pkt); } } + checkRecv(); checkSend(); } @@ -115,6 +116,18 @@ void Dispatcher::checkRecv() { { uint8_t raw[MAX_TRANS_UNIT+1]; int len = _radio->recvRaw(raw, MAX_TRANS_UNIT); + +#ifdef BRIDGE_OVER_SERIAL + // We are basically stamping metadata from the last RF packet into something that came from serial, + // it works for now. But long term the code on checkRecv() should be refactored to be able to deal + // with both use cases without dupeing the existing code. I've chosen [for now] not to dupe and just + // fake the metadata. + + if (len == 0) { + len = bridge_interface->getPacket(raw); + } +#endif + if (len > 0) { logRxRaw(_radio->getLastSNR(), _radio->getLastRSSI(), raw, len); @@ -280,7 +293,11 @@ void Dispatcher::checkSend() { } outbound_expiry = futureMillis(max_airtime); - #if MESH_PACKET_LOGGING +#ifdef BRIDGE_OVER_SERIAL + bridge_interface->sendPacket(outbound); +#endif + +#if MESH_PACKET_LOGGING Serial.print(getLogDateTime()); Serial.printf(": TX, len=%d (type=%d, route=%s, payload_len=%d)", len, outbound->getPayloadType(), outbound->isRouteDirect() ? "D" : "F", outbound->payload_len); @@ -290,7 +307,7 @@ void Dispatcher::checkSend() { } else { Serial.printf("\n"); } - #endif +#endif } } } @@ -328,5 +345,4 @@ bool Dispatcher::millisHasNowPassed(unsigned long timestamp) const { unsigned long Dispatcher::futureMillis(int millis_from_now) const { return _ms->getMillis() + millis_from_now; } - } \ No newline at end of file diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 2200f81b..98184b50 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -6,6 +6,11 @@ #include #include +#ifdef BRIDGE_OVER_SERIAL +#include "bridge/serial/SerialBridge.h" +extern bridge::SerialBridge *bridge_interface; +#endif + namespace mesh { /** diff --git a/src/bridge/serial/Packet.h b/src/bridge/serial/Packet.h new file mode 100644 index 00000000..fbb4155a --- /dev/null +++ b/src/bridge/serial/Packet.h @@ -0,0 +1,58 @@ +/** + * MeshCore - A new lightweight, hybrid routing mesh protocol for packet radios + * Copyright (c) 2025 Scott Powell / rippleradios.com + * + * This project is maintained by the contributors listed in + * https://github.com/ripplebiz/MeshCore/graphs/contributors + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#pragma once + +#include "MeshCore.h" + +#include + +namespace bridge { + +/* + * +----------------------------------------------------+ + * | SERIAL PACKET SPEC | + * +-----------+---------+------------------------------+ + * | TYPE | NAME | DESCRIPTION | + * +-----------+---------+------------------------------+ + * | uint16_t | MAGIC | The packet start marker | + * | uint16_t | LEN | Length of the payload | + * | uint16_t | CRC | Checksum for error checking | + * | uint8_t[] | PAYLOAD | Actual rf packet data | + * +-----------+---------+------------------------------+ + */ +#define SERIAL_PKT_MAGIC 0xdead + +struct Packet { + uint16_t magic, len, crc; + uint8_t payload[MAX_TRANS_UNIT]; + + Packet() : magic(SERIAL_PKT_MAGIC), len(0), crc(0) {} +}; + +} // namespace bridge \ No newline at end of file diff --git a/src/bridge/serial/PacketQueue.h b/src/bridge/serial/PacketQueue.h new file mode 100644 index 00000000..a83d8f2e --- /dev/null +++ b/src/bridge/serial/PacketQueue.h @@ -0,0 +1,113 @@ +/** + * MeshCore - A new lightweight, hybrid routing mesh protocol for packet radios + * Copyright (c) 2025 Scott Powell / rippleradios.com + * + * This project is maintained by the contributors listed in + * https://github.com/ripplebiz/MeshCore/graphs/contributors + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#pragma once + +#include "MeshCore.h" + +#include +#include + +#if MESH_PACKET_LOGGING +#include +#endif + +#ifndef MAX_QUEUE_SIZE +#define MAX_QUEUE_SIZE 8 +#endif + +namespace bridge { + +class PacketQueue { +private: + struct { + size_t len; + uint8_t bytes[MAX_TRANS_UNIT]; + } buffer[MAX_QUEUE_SIZE]; + +protected: + uint16_t head = 0, tail = 0; + +public: + size_t available() const { return (tail - head + MAX_QUEUE_SIZE) % MAX_QUEUE_SIZE; } + + size_t enqueue(const uint8_t *bytes, const uint8_t len) { + if (len == 0 || len > MAX_TRANS_UNIT) { +#if BRIDGE_DEBUG + Serial.printf("BRIDGE: enqueue() failed len=%d\n", len); +#endif + return 0; + } + + if ((tail + 1) % MAX_QUEUE_SIZE == head) { // Buffer full + head = (head + 1) % MAX_QUEUE_SIZE; // Overwrite oldest packet + } + + buffer[tail].len = len; + memcpy(buffer[tail].bytes, bytes, len); + +#if MESH_PACKET_LOGGING + Serial.printf("BRIDGE: enqueue() len=%d payload[5]=", len); + for (size_t i = 0; i < len && i < 5; ++i) { + Serial.printf("0x%02x ", buffer[tail].bytes[i]); + } + Serial.printf("\n"); +#endif + + tail = (tail + 1) % MAX_QUEUE_SIZE; + return len; + } + + size_t enqueue(const mesh::Packet *pkt) { + uint8_t bytes[MAX_TRANS_UNIT]; + const size_t len = pkt->writeTo(bytes); + return enqueue(bytes, len); + } + + size_t dequeue(uint8_t *bytes) { + if (head == tail) { // Buffer empty + return 0; + } + + const size_t len = buffer[head].len; + memcpy(bytes, buffer[head].bytes, len); + head = (head + 1) % MAX_QUEUE_SIZE; + +#if MESH_PACKET_LOGGING + Serial.printf("BRIDGE: dequeue() payload[5]="); + for (size_t i = 0; i < len && i < 5; ++i) { + Serial.printf("0x%02x ", bytes[i]); + } + Serial.printf("\n"); +#endif + + return len; + } +}; + +} // namespace bridge diff --git a/src/bridge/serial/SerialBridge.cpp b/src/bridge/serial/SerialBridge.cpp new file mode 100644 index 00000000..1e83f9e6 --- /dev/null +++ b/src/bridge/serial/SerialBridge.cpp @@ -0,0 +1,135 @@ +/** + * MeshCore - A new lightweight, hybrid routing mesh protocol for packet radios + * Copyright (c) 2025 Scott Powell / rippleradios.com + * + * This project is maintained by the contributors listed in + * https://github.com/ripplebiz/MeshCore/graphs/contributors + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include "SerialBridge.h" + +#include "MeshCore.h" +#include "Packet.h" + +// Alternative for ESP32 +// Alternative for RP2040 +#include + +namespace bridge { +#ifdef BRIDGE_OVER_SERIAL + +#if !defined(BRIDGE_OVER_SERIAL_RX) || !defined(BRIDGE_OVER_SERIAL_TX) +#error You must define RX and TX pins +#endif + +void SerialBridge::setup() { +#if defined(ESP32) + BRIDGE_OVER_SERIAL.setPins(BRIDGE_OVER_SERIAL_RX, BRIDGE_OVER_SERIAL_TX); +#elif defined(RP2040_PLATFORM) + BRIDGE_OVER_SERIAL.setPinout(BRIDGE_OVER_SERIAL_TX, BRIDGE_OVER_SERIAL_RX); +#else +#error SerialBridge was not tested on the current platform +#endif + BRIDGE_OVER_SERIAL.begin(115200); + Serial.printf("Bridge over serial: enabled\n"); +} + +void SerialBridge::loop() { + readFromSerial(); + writeToSerial(); +} + +bool SerialBridge::shouldRetransmit(const mesh::Packet *pkt) { + if (pkt->isMarkedDoNotRetransmit()) { + return false; + } +} + +size_t SerialBridge::getPacket(uint8_t *bytes) { + return rx_queue.dequeue(bytes); +} + +size_t SerialBridge::sendPacket(const mesh::Packet *pkt) { + if (shouldRetransmit(pkt)) return 0; + const size_t len = tx_queue.enqueue(pkt); + return len; +} + +void SerialBridge::readFromSerial() { + static constexpr uint16_t size = sizeof(bridge::Packet) + 1; + static uint8_t buffer[size]; + static uint16_t tail = 0; + + while (BRIDGE_OVER_SERIAL.available()) { + buffer[tail] = (uint8_t)BRIDGE_OVER_SERIAL.read(); + tail = (tail + 1) % size; + +#if BRIDGE_OVER_SERIAL_DEBUG + Serial.printf("%02x ", buffer[(tail - 1 + size) % size]); +#endif + + // Check for complete packet by looking back to where the magic number should be + uint16_t head = (tail - sizeof(bridge::Packet) + size) % size; + const uint16_t magic = buffer[head] | (buffer[(head + 1) % size] << 8); + + if (magic == SERIAL_PKT_MAGIC) { + const uint16_t len = buffer[(head + 2) % size] | (buffer[(head + 3) % size] << 8); + const uint16_t crc = buffer[(head + 4) % size] | (buffer[(head + 5) % size] << 8); + + uint8_t payload[MAX_TRANS_UNIT]; + for (size_t i = 0; i < len; i++) { + payload[i] = buffer[(head + 6 + i) % size]; + } + + const bool valid = verifyCRC(payload, len, crc); + +#if MESH_PACKET_LOGGING + Serial.printf("BRIDGE: Read from serial len=%d crc=0x%04x\n", len, crc); +#endif + + if (verifyCRC(payload, len, crc)) { +#if MESH_PACKET_LOGGING + Serial.printf("BRIDGE: Received packet was validated\n"); +#endif + rx_queue.enqueue(payload, len); + } + } + } +} + +void SerialBridge::writeToSerial() { + bridge::Packet pkt; + if (!tx_queue.available()) return; + pkt.len = tx_queue.dequeue(pkt.payload); + + if (pkt.len > 0) { + pkt.crc = SerialBridge::calculateCRC(pkt.payload, pkt.len); + BRIDGE_OVER_SERIAL.write((uint8_t *)&pkt, sizeof(bridge::Packet)); +#if MESH_PACKET_LOGGING + Serial.printf("BRIDGE: Write to serial len=%d crc=0x%04x\n", pkt.len, pkt.crc); +#endif + } +} + +#endif +} // namespace bridge diff --git a/src/bridge/serial/SerialBridge.h b/src/bridge/serial/SerialBridge.h new file mode 100644 index 00000000..80bb2360 --- /dev/null +++ b/src/bridge/serial/SerialBridge.h @@ -0,0 +1,73 @@ +/** + * MeshCore - A new lightweight, hybrid routing mesh protocol for packet radios + * Copyright (c) 2025 Scott Powell / rippleradios.com + * + * This project is maintained by the contributors listed in + * https://github.com/ripplebiz/MeshCore/graphs/contributors + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#pragma once + +#include "Packet.h" +#include "PacketQueue.h" + +#include + +namespace bridge { + +class SerialBridge { +private: + PacketQueue rx_queue, tx_queue; + +protected: + bool shouldRetransmit(const mesh::Packet *); + +public: + void loop(); + void setup(); + void readFromSerial(); + void writeToSerial(); + + size_t getPacket(uint8_t *); + size_t sendPacket(const mesh::Packet *); + + static uint16_t calculateCRC(const uint8_t *bytes, size_t len) { + // Fletcher-16 + // https://en.wikipedia.org/wiki/Fletcher%27s_checksum + uint8_t sum1 = 0, sum2 = 0; + + for (size_t i = 0; i < len; i++) { + sum1 = (sum1 + bytes[i]) % 255; + sum2 = (sum2 + sum1) % 255; + } + + return (sum2 << 8) | sum1; + }; + + static bool verifyCRC(const uint8_t *bytes, size_t len, uint16_t crc) { + uint16_t computedChecksum = calculateCRC(bytes, len); + return (computedChecksum == crc); + } +}; + +} // namespace bridge From 2f77cef04b65f1d76a01e3b8c74086732d43a8e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Sun, 29 Jun 2025 16:28:11 +0100 Subject: [PATCH 02/21] Add config flags to variants --- variants/heltec_v3/platformio.ini | 3 +++ variants/lilygo_tlora_v2_1/platformio.ini | 3 +++ variants/waveshare_rp2040_lora/platformio.ini | 3 +++ 3 files changed, 9 insertions(+) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 8f9b1a27..173be80c 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -47,6 +47,9 @@ build_flags = -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 +; -D BRIDGE_OVER_SERIAL=Serial2 +; -D BRIDGE_OVER_SERIAL_RX=5 +; -D BRIDGE_OVER_SERIAL_TX=6 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index d9cecfc2..bd351c5a 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -44,6 +44,9 @@ build_flags = -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 +; -D BRIDGE_OVER_SERIAL=Serial2 +; -D BRIDGE_OVER_SERIAL_RX=34 +; -D BRIDGE_OVER_SERIAL_TX=25 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 ; -D CORE_DEBUG_LEVEL=3 diff --git a/variants/waveshare_rp2040_lora/platformio.ini b/variants/waveshare_rp2040_lora/platformio.ini index 2730734d..b1e4714a 100644 --- a/variants/waveshare_rp2040_lora/platformio.ini +++ b/variants/waveshare_rp2040_lora/platformio.ini @@ -35,6 +35,9 @@ build_flags = ${waveshare_rp2040_lora.build_flags} -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 +; -D BRIDGE_OVER_SERIAL=Serial2 +; -D BRIDGE_OVER_SERIAL_RX=9 +; -D BRIDGE_OVER_SERIAL_TX=8 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${waveshare_rp2040_lora.build_src_filter} From ac056fb0b98ecd9ce0da6cda8693f1cdffb97cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Tue, 8 Jul 2025 14:04:21 +0100 Subject: [PATCH 03/21] Remove serial bridge implementation and implement simplified version directly in the repeater source code. --- examples/simple_repeater/main.cpp | 121 +++++++++++++++++++++----- src/Dispatcher.cpp | 15 ---- src/Dispatcher.h | 5 -- src/bridge/serial/Packet.h | 58 ------------- src/bridge/serial/PacketQueue.h | 113 ------------------------ src/bridge/serial/SerialBridge.cpp | 135 ----------------------------- src/bridge/serial/SerialBridge.h | 73 ---------------- 7 files changed, 100 insertions(+), 420 deletions(-) delete mode 100644 src/bridge/serial/Packet.h delete mode 100644 src/bridge/serial/PacketQueue.h delete mode 100644 src/bridge/serial/SerialBridge.cpp delete mode 100644 src/bridge/serial/SerialBridge.h diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 131dc496..96969f96 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -72,17 +72,35 @@ static UITask ui_task(display); #endif -#ifdef BRIDGE_OVER_SERIAL -#include "bridge/serial/SerialBridge.h" -bridge::SerialBridge *bridge_interface; -#endif - #define FIRMWARE_ROLE "repeater" #define PACKET_LOG_FILE "/packet_log" /* ------------------------------ Code -------------------------------- */ +#ifdef BRIDGE_OVER_SERIAL +#define SERIAL_PKT_MAGIC 0xbeef + +struct SerialPacket { + uint16_t magic, len, crc; + uint8_t payload[MAX_TRANS_UNIT]; + SerialPacket() : magic(SERIAL_PKT_MAGIC), len(0), crc(0) {} +}; + +// Fletcher-16 +// https://en.wikipedia.org/wiki/Fletcher%27s_checksum +static uint16_t fletcher16(const uint8_t *bytes, const size_t len) { + uint8_t sum1 = 0, sum2 = 0; + + for (size_t i = 0; i < len; i++) { + sum1 = (sum1 + bytes[i]) % 255; + sum2 = (sum2 + sum1) % 255; + } + + return (sum2 << 8) | sum1; +}; +#endif + #define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS #define REQ_TYPE_KEEP_ALIVE 0x02 #define REQ_TYPE_GET_TELEMETRY_DATA 0x03 @@ -297,6 +315,20 @@ protected: } } void logTx(mesh::Packet* pkt, int len) override { +#ifdef BRIDGE_OVER_SERIAL + SerialPacket spkt; + spkt.len = pkt->writeTo(spkt.payload); + + if (spkt.len > 0) { + spkt.crc = fletcher16(spkt.payload, spkt.len); + BRIDGE_OVER_SERIAL.write((uint8_t *)&spkt, sizeof(SerialPacket)); + +#if MESH_PACKET_LOGGING + Serial.printf("BRIDGE: Write to serial len=%d crc=0x%04x\n", spkt.len, spkt.crc); +#endif + } +#endif + if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { @@ -358,9 +390,9 @@ protected: } else if (strcmp((char *) &data[4], _prefs.guest_password) == 0) { // check guest password is_admin = false; } else { - #if MESH_DEBUG +#if MESH_DEBUG MESH_DEBUG_PRINTLN("Invalid password: %s", &data[4]); - #endif +#endif return; } @@ -377,15 +409,15 @@ protected: uint32_t now = getRTCClock()->getCurrentTimeUnique(); memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp - #if 0 +#if 0 memcpy(&reply_data[4], "OK", 2); // legacy response - #else +#else reply_data[4] = RESP_SERVER_LOGIN_OK; reply_data[5] = 0; // NEW: recommended keep-alive interval (secs / 16) reply_data[6] = is_admin ? 1 : 0; reply_data[7] = 0; // FUTURE: reserved getRNG()->random(&reply_data[8], 4); // random blob to help packet-hash uniqueness - #endif +#endif if (packet->isRouteFlood()) { // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response @@ -562,9 +594,9 @@ public: next_local_advert = next_flood_advert = 0; _logging = false; - #if MAX_NEIGHBOURS +#if MAX_NEIGHBOURS memset(neighbours, 0, sizeof(neighbours)); - #endif +#endif // defaults memset(&_prefs, 0, sizeof(_prefs)); @@ -712,6 +744,44 @@ public: } void loop() { +#ifdef BRIDGE_OVER_SERIAL + static constexpr uint16_t size = sizeof(SerialPacket) + 1; + static uint8_t buffer[size]; + static uint16_t tail = 0; + + while (BRIDGE_OVER_SERIAL.available()) { + buffer[tail] = (uint8_t)BRIDGE_OVER_SERIAL.read(); + MESH_DEBUG_PRINT("%02x ", buffer[tail]); + tail = (tail + 1) % size; + + // Check for complete packet by looking back to where the magic number should be + uint16_t head = (tail - sizeof(SerialPacket) + size) % size; + const uint16_t magic = buffer[head] | (buffer[(head + 1) % size] << 8); + + if (magic == SERIAL_PKT_MAGIC) { + uint8_t bytes[MAX_TRANS_UNIT]; + const uint16_t len = buffer[(head + 2) % size] | (buffer[(head + 3) % size] << 8); + const uint16_t crc = buffer[(head + 4) % size] | (buffer[(head + 5) % size] << 8); + + for (size_t i = 0; i < len; i++) { + bytes[i] = buffer[(head + 6 + i) % size]; + } + + uint16_t f16 = fletcher16(bytes, len); + +#if MESH_PACKET_LOGGING + Serial.printf("BRIDGE: Read from serial len=%d crc=0x%04x valid=%s\n", len, crc, (f16 == crc) ? "true" : "false"); +#endif + + if (f16 == crc) { + mesh::Packet *pkt = _mgr->allocNew(); + pkt->readFrom(bytes, len); + _mgr->queueInbound(pkt, millis()); + } + } + } +#endif + mesh::Mesh::loop(); if (next_flood_advert && millisHasNowPassed(next_flood_advert)) { @@ -745,6 +815,24 @@ static char command[80]; void setup() { Serial.begin(115200); + +#ifdef BRIDGE_OVER_SERIAL +#if defined(ESP32) + BRIDGE_OVER_SERIAL.setPins(BRIDGE_OVER_SERIAL_RX, BRIDGE_OVER_SERIAL_TX); +#elif defined(RP2040_PLATFORM) + BRIDGE_OVER_SERIAL.setRX(BRIDGE_OVER_SERIAL_RX); + BRIDGE_OVER_SERIAL.setTX(BRIDGE_OVER_SERIAL_TX); +#elif defined(STM32_PLATFORM) + BRIDGE_OVER_SERIAL.setRx(BRIDGE_OVER_SERIAL_RX); + BRIDGE_OVER_SERIAL.setTx(BRIDGE_OVER_SERIAL_TX); +#else +#error SerialBridge was not tested on the current platform +#endif + + BRIDGE_OVER_SERIAL.begin(115200); + MESH_DEBUG_PRINTLN("Bridge over serial: enabled"); +#endif + delay(1000); board.begin(); @@ -757,11 +845,6 @@ void setup() { } #endif -#ifdef BRIDGE_OVER_SERIAL - bridge_interface = new bridge::SerialBridge(); - bridge_interface->setup(); -#endif - if (!radio_init()) { halt(); } @@ -837,10 +920,6 @@ void loop() { command[0] = 0; // reset command buffer } -#ifdef BRIDGE_OVER_SERIAL - bridge_interface->loop(); -#endif - the_mesh.loop(); sensors.loop(); } diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 08cca61e..1a6e833b 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -117,17 +117,6 @@ void Dispatcher::checkRecv() { uint8_t raw[MAX_TRANS_UNIT+1]; int len = _radio->recvRaw(raw, MAX_TRANS_UNIT); -#ifdef BRIDGE_OVER_SERIAL - // We are basically stamping metadata from the last RF packet into something that came from serial, - // it works for now. But long term the code on checkRecv() should be refactored to be able to deal - // with both use cases without dupeing the existing code. I've chosen [for now] not to dupe and just - // fake the metadata. - - if (len == 0) { - len = bridge_interface->getPacket(raw); - } -#endif - if (len > 0) { logRxRaw(_radio->getLastSNR(), _radio->getLastRSSI(), raw, len); @@ -293,10 +282,6 @@ void Dispatcher::checkSend() { } outbound_expiry = futureMillis(max_airtime); -#ifdef BRIDGE_OVER_SERIAL - bridge_interface->sendPacket(outbound); -#endif - #if MESH_PACKET_LOGGING Serial.print(getLogDateTime()); Serial.printf(": TX, len=%d (type=%d, route=%s, payload_len=%d)", diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 98184b50..2200f81b 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -6,11 +6,6 @@ #include #include -#ifdef BRIDGE_OVER_SERIAL -#include "bridge/serial/SerialBridge.h" -extern bridge::SerialBridge *bridge_interface; -#endif - namespace mesh { /** diff --git a/src/bridge/serial/Packet.h b/src/bridge/serial/Packet.h deleted file mode 100644 index fbb4155a..00000000 --- a/src/bridge/serial/Packet.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * MeshCore - A new lightweight, hybrid routing mesh protocol for packet radios - * Copyright (c) 2025 Scott Powell / rippleradios.com - * - * This project is maintained by the contributors listed in - * https://github.com/ripplebiz/MeshCore/graphs/contributors - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ -#pragma once - -#include "MeshCore.h" - -#include - -namespace bridge { - -/* - * +----------------------------------------------------+ - * | SERIAL PACKET SPEC | - * +-----------+---------+------------------------------+ - * | TYPE | NAME | DESCRIPTION | - * +-----------+---------+------------------------------+ - * | uint16_t | MAGIC | The packet start marker | - * | uint16_t | LEN | Length of the payload | - * | uint16_t | CRC | Checksum for error checking | - * | uint8_t[] | PAYLOAD | Actual rf packet data | - * +-----------+---------+------------------------------+ - */ -#define SERIAL_PKT_MAGIC 0xdead - -struct Packet { - uint16_t magic, len, crc; - uint8_t payload[MAX_TRANS_UNIT]; - - Packet() : magic(SERIAL_PKT_MAGIC), len(0), crc(0) {} -}; - -} // namespace bridge \ No newline at end of file diff --git a/src/bridge/serial/PacketQueue.h b/src/bridge/serial/PacketQueue.h deleted file mode 100644 index a83d8f2e..00000000 --- a/src/bridge/serial/PacketQueue.h +++ /dev/null @@ -1,113 +0,0 @@ -/** - * MeshCore - A new lightweight, hybrid routing mesh protocol for packet radios - * Copyright (c) 2025 Scott Powell / rippleradios.com - * - * This project is maintained by the contributors listed in - * https://github.com/ripplebiz/MeshCore/graphs/contributors - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ -#pragma once - -#include "MeshCore.h" - -#include -#include - -#if MESH_PACKET_LOGGING -#include -#endif - -#ifndef MAX_QUEUE_SIZE -#define MAX_QUEUE_SIZE 8 -#endif - -namespace bridge { - -class PacketQueue { -private: - struct { - size_t len; - uint8_t bytes[MAX_TRANS_UNIT]; - } buffer[MAX_QUEUE_SIZE]; - -protected: - uint16_t head = 0, tail = 0; - -public: - size_t available() const { return (tail - head + MAX_QUEUE_SIZE) % MAX_QUEUE_SIZE; } - - size_t enqueue(const uint8_t *bytes, const uint8_t len) { - if (len == 0 || len > MAX_TRANS_UNIT) { -#if BRIDGE_DEBUG - Serial.printf("BRIDGE: enqueue() failed len=%d\n", len); -#endif - return 0; - } - - if ((tail + 1) % MAX_QUEUE_SIZE == head) { // Buffer full - head = (head + 1) % MAX_QUEUE_SIZE; // Overwrite oldest packet - } - - buffer[tail].len = len; - memcpy(buffer[tail].bytes, bytes, len); - -#if MESH_PACKET_LOGGING - Serial.printf("BRIDGE: enqueue() len=%d payload[5]=", len); - for (size_t i = 0; i < len && i < 5; ++i) { - Serial.printf("0x%02x ", buffer[tail].bytes[i]); - } - Serial.printf("\n"); -#endif - - tail = (tail + 1) % MAX_QUEUE_SIZE; - return len; - } - - size_t enqueue(const mesh::Packet *pkt) { - uint8_t bytes[MAX_TRANS_UNIT]; - const size_t len = pkt->writeTo(bytes); - return enqueue(bytes, len); - } - - size_t dequeue(uint8_t *bytes) { - if (head == tail) { // Buffer empty - return 0; - } - - const size_t len = buffer[head].len; - memcpy(bytes, buffer[head].bytes, len); - head = (head + 1) % MAX_QUEUE_SIZE; - -#if MESH_PACKET_LOGGING - Serial.printf("BRIDGE: dequeue() payload[5]="); - for (size_t i = 0; i < len && i < 5; ++i) { - Serial.printf("0x%02x ", bytes[i]); - } - Serial.printf("\n"); -#endif - - return len; - } -}; - -} // namespace bridge diff --git a/src/bridge/serial/SerialBridge.cpp b/src/bridge/serial/SerialBridge.cpp deleted file mode 100644 index 1e83f9e6..00000000 --- a/src/bridge/serial/SerialBridge.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/** - * MeshCore - A new lightweight, hybrid routing mesh protocol for packet radios - * Copyright (c) 2025 Scott Powell / rippleradios.com - * - * This project is maintained by the contributors listed in - * https://github.com/ripplebiz/MeshCore/graphs/contributors - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ -#include "SerialBridge.h" - -#include "MeshCore.h" -#include "Packet.h" - -// Alternative for ESP32 -// Alternative for RP2040 -#include - -namespace bridge { -#ifdef BRIDGE_OVER_SERIAL - -#if !defined(BRIDGE_OVER_SERIAL_RX) || !defined(BRIDGE_OVER_SERIAL_TX) -#error You must define RX and TX pins -#endif - -void SerialBridge::setup() { -#if defined(ESP32) - BRIDGE_OVER_SERIAL.setPins(BRIDGE_OVER_SERIAL_RX, BRIDGE_OVER_SERIAL_TX); -#elif defined(RP2040_PLATFORM) - BRIDGE_OVER_SERIAL.setPinout(BRIDGE_OVER_SERIAL_TX, BRIDGE_OVER_SERIAL_RX); -#else -#error SerialBridge was not tested on the current platform -#endif - BRIDGE_OVER_SERIAL.begin(115200); - Serial.printf("Bridge over serial: enabled\n"); -} - -void SerialBridge::loop() { - readFromSerial(); - writeToSerial(); -} - -bool SerialBridge::shouldRetransmit(const mesh::Packet *pkt) { - if (pkt->isMarkedDoNotRetransmit()) { - return false; - } -} - -size_t SerialBridge::getPacket(uint8_t *bytes) { - return rx_queue.dequeue(bytes); -} - -size_t SerialBridge::sendPacket(const mesh::Packet *pkt) { - if (shouldRetransmit(pkt)) return 0; - const size_t len = tx_queue.enqueue(pkt); - return len; -} - -void SerialBridge::readFromSerial() { - static constexpr uint16_t size = sizeof(bridge::Packet) + 1; - static uint8_t buffer[size]; - static uint16_t tail = 0; - - while (BRIDGE_OVER_SERIAL.available()) { - buffer[tail] = (uint8_t)BRIDGE_OVER_SERIAL.read(); - tail = (tail + 1) % size; - -#if BRIDGE_OVER_SERIAL_DEBUG - Serial.printf("%02x ", buffer[(tail - 1 + size) % size]); -#endif - - // Check for complete packet by looking back to where the magic number should be - uint16_t head = (tail - sizeof(bridge::Packet) + size) % size; - const uint16_t magic = buffer[head] | (buffer[(head + 1) % size] << 8); - - if (magic == SERIAL_PKT_MAGIC) { - const uint16_t len = buffer[(head + 2) % size] | (buffer[(head + 3) % size] << 8); - const uint16_t crc = buffer[(head + 4) % size] | (buffer[(head + 5) % size] << 8); - - uint8_t payload[MAX_TRANS_UNIT]; - for (size_t i = 0; i < len; i++) { - payload[i] = buffer[(head + 6 + i) % size]; - } - - const bool valid = verifyCRC(payload, len, crc); - -#if MESH_PACKET_LOGGING - Serial.printf("BRIDGE: Read from serial len=%d crc=0x%04x\n", len, crc); -#endif - - if (verifyCRC(payload, len, crc)) { -#if MESH_PACKET_LOGGING - Serial.printf("BRIDGE: Received packet was validated\n"); -#endif - rx_queue.enqueue(payload, len); - } - } - } -} - -void SerialBridge::writeToSerial() { - bridge::Packet pkt; - if (!tx_queue.available()) return; - pkt.len = tx_queue.dequeue(pkt.payload); - - if (pkt.len > 0) { - pkt.crc = SerialBridge::calculateCRC(pkt.payload, pkt.len); - BRIDGE_OVER_SERIAL.write((uint8_t *)&pkt, sizeof(bridge::Packet)); -#if MESH_PACKET_LOGGING - Serial.printf("BRIDGE: Write to serial len=%d crc=0x%04x\n", pkt.len, pkt.crc); -#endif - } -} - -#endif -} // namespace bridge diff --git a/src/bridge/serial/SerialBridge.h b/src/bridge/serial/SerialBridge.h deleted file mode 100644 index 80bb2360..00000000 --- a/src/bridge/serial/SerialBridge.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * MeshCore - A new lightweight, hybrid routing mesh protocol for packet radios - * Copyright (c) 2025 Scott Powell / rippleradios.com - * - * This project is maintained by the contributors listed in - * https://github.com/ripplebiz/MeshCore/graphs/contributors - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ -#pragma once - -#include "Packet.h" -#include "PacketQueue.h" - -#include - -namespace bridge { - -class SerialBridge { -private: - PacketQueue rx_queue, tx_queue; - -protected: - bool shouldRetransmit(const mesh::Packet *); - -public: - void loop(); - void setup(); - void readFromSerial(); - void writeToSerial(); - - size_t getPacket(uint8_t *); - size_t sendPacket(const mesh::Packet *); - - static uint16_t calculateCRC(const uint8_t *bytes, size_t len) { - // Fletcher-16 - // https://en.wikipedia.org/wiki/Fletcher%27s_checksum - uint8_t sum1 = 0, sum2 = 0; - - for (size_t i = 0; i < len; i++) { - sum1 = (sum1 + bytes[i]) % 255; - sum2 = (sum2 + sum1) % 255; - } - - return (sum2 << 8) | sum1; - }; - - static bool verifyCRC(const uint8_t *bytes, size_t len, uint16_t crc) { - uint16_t computedChecksum = calculateCRC(bytes, len); - return (computedChecksum == crc); - } -}; - -} // namespace bridge From 92ee1820c42041dce922b8e5917eb46533f9e217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Tue, 8 Jul 2025 16:02:10 +0100 Subject: [PATCH 04/21] Add null check for packet allocation and clean up Dispatcher --- examples/simple_repeater/main.cpp | 13 ++++++++++--- src/Dispatcher.cpp | 9 ++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 96969f96..428e932f 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -774,9 +774,16 @@ public: #endif if (f16 == crc) { - mesh::Packet *pkt = _mgr->allocNew(); - pkt->readFrom(bytes, len); - _mgr->queueInbound(pkt, millis()); + Packet *pkt = _mgr->allocNew(); + + if (pkt != NULL) { + pkt->readFrom(bytes, len); + _mgr->queueInbound(pkt, millis()); + } else { +#if MESH_PACKET_LOGGING + Serial.printf("BRIDGE: Unable to allocate new Packet *pkt"); +#endif + } } } } diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 1a6e833b..7f39dc49 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -1,7 +1,7 @@ #include "Dispatcher.h" #if MESH_PACKET_LOGGING -#include + #include #endif #include @@ -104,7 +104,6 @@ void Dispatcher::loop() { processRecvPacket(pkt); } } - checkRecv(); checkSend(); } @@ -116,7 +115,6 @@ void Dispatcher::checkRecv() { { uint8_t raw[MAX_TRANS_UNIT+1]; int len = _radio->recvRaw(raw, MAX_TRANS_UNIT); - if (len > 0) { logRxRaw(_radio->getLastSNR(), _radio->getLastRSSI(), raw, len); @@ -282,7 +280,7 @@ void Dispatcher::checkSend() { } outbound_expiry = futureMillis(max_airtime); -#if MESH_PACKET_LOGGING + #if MESH_PACKET_LOGGING Serial.print(getLogDateTime()); Serial.printf(": TX, len=%d (type=%d, route=%s, payload_len=%d)", len, outbound->getPayloadType(), outbound->isRouteDirect() ? "D" : "F", outbound->payload_len); @@ -292,7 +290,7 @@ void Dispatcher::checkSend() { } else { Serial.printf("\n"); } -#endif + #endif } } } @@ -330,4 +328,5 @@ bool Dispatcher::millisHasNowPassed(unsigned long timestamp) const { unsigned long Dispatcher::futureMillis(int millis_from_now) const { return _ms->getMillis() + millis_from_now; } + } \ No newline at end of file From 97b51900f8667515f4f1a6f9064f5d280a0b480f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Tue, 8 Jul 2025 21:45:49 +0100 Subject: [PATCH 05/21] More robust handling of pkt len --- examples/simple_repeater/main.cpp | 45 ++++++++++++++++++------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 428e932f..fe065a33 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -79,7 +79,7 @@ /* ------------------------------ Code -------------------------------- */ #ifdef BRIDGE_OVER_SERIAL -#define SERIAL_PKT_MAGIC 0xbeef +#define SERIAL_PKT_MAGIC 0xcafe struct SerialPacket { uint16_t magic, len, crc; @@ -316,15 +316,17 @@ protected: } void logTx(mesh::Packet* pkt, int len) override { #ifdef BRIDGE_OVER_SERIAL - SerialPacket spkt; - spkt.len = pkt->writeTo(spkt.payload); + SerialPacket serialpkt; + size_t seriallen = pkt->writeTo(serialpkt.payload); - if (spkt.len > 0) { - spkt.crc = fletcher16(spkt.payload, spkt.len); - BRIDGE_OVER_SERIAL.write((uint8_t *)&spkt, sizeof(SerialPacket)); + if (seriallen - 1 < MAX_TRANS_UNIT - 1) { + serialpkt.len = seriallen; + serialpkt.crc = fletcher16(serialpkt.payload, serialpkt.len); + BRIDGE_OVER_SERIAL.write((uint8_t *)&serialpkt, sizeof(SerialPacket)); #if MESH_PACKET_LOGGING - Serial.printf("BRIDGE: Write to serial len=%d crc=0x%04x\n", spkt.len, spkt.crc); + Serial.print(getLogDateTime()); + Serial.printf(": BRIDGE: Write to serial len=%d crc=0x%04x\n", serialpkt.len, serialpkt.crc); #endif } #endif @@ -763,26 +765,31 @@ public: const uint16_t len = buffer[(head + 2) % size] | (buffer[(head + 3) % size] << 8); const uint16_t crc = buffer[(head + 4) % size] | (buffer[(head + 5) % size] << 8); - for (size_t i = 0; i < len; i++) { - bytes[i] = buffer[(head + 6 + i) % size]; - } + if (len - 1 < MAX_TRANS_UNIT - 1) { + for (size_t i = 0; i < len; i++) { + bytes[i] = buffer[(head + 6 + i) % size]; + } - uint16_t f16 = fletcher16(bytes, len); + uint16_t f16 = fletcher16(bytes, len); #if MESH_PACKET_LOGGING - Serial.printf("BRIDGE: Read from serial len=%d crc=0x%04x valid=%s\n", len, crc, (f16 == crc) ? "true" : "false"); + Serial.print(getLogDateTime()); + Serial.printf(": BRIDGE: Read from serial len=%d crc=0x%04x valid=%s\n", len, crc, + (f16 == crc) ? "true" : "false"); #endif - if (f16 == crc) { - Packet *pkt = _mgr->allocNew(); + if (f16 == crc) { + mesh::Packet *pkt = _mgr->allocNew(); - if (pkt != NULL) { - pkt->readFrom(bytes, len); - _mgr->queueInbound(pkt, millis()); - } else { + if (pkt == NULL) { #if MESH_PACKET_LOGGING - Serial.printf("BRIDGE: Unable to allocate new Packet *pkt"); + Serial.print(getLogDateTime()); + Serial.printf(": BRIDGE: Unable to allocate new Packet *pkt\n"); #endif + } else { + pkt->readFrom(bytes, len); + _mgr->queueInbound(pkt, millis()); + } } } } From 04042e3ca0c1f0c52ddd110d589bdb446d5de4eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Wed, 9 Jul 2025 11:03:35 +0100 Subject: [PATCH 06/21] Refactor serial bridge handling --- examples/simple_repeater/main.cpp | 170 +++++++++--------- platformio.ini | 1 - variants/heltec_v3/platformio.ini | 25 ++- variants/lilygo_tlora_v2_1/platformio.ini | 25 ++- variants/waveshare_rp2040_lora/platformio.ini | 19 +- 5 files changed, 147 insertions(+), 93 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index fe065a33..e9683fd3 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -80,25 +80,6 @@ #ifdef BRIDGE_OVER_SERIAL #define SERIAL_PKT_MAGIC 0xcafe - -struct SerialPacket { - uint16_t magic, len, crc; - uint8_t payload[MAX_TRANS_UNIT]; - SerialPacket() : magic(SERIAL_PKT_MAGIC), len(0), crc(0) {} -}; - -// Fletcher-16 -// https://en.wikipedia.org/wiki/Fletcher%27s_checksum -static uint16_t fletcher16(const uint8_t *bytes, const size_t len) { - uint8_t sum1 = 0, sum2 = 0; - - for (size_t i = 0; i < len; i++) { - sum1 = (sum1 + bytes[i]) % 255; - sum2 = (sum2 + sum1) % 255; - } - - return (sum2 << 8) | sum1; -}; #endif #define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS @@ -267,6 +248,89 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { #endif } +#ifdef BRIDGE_OVER_SERIAL + struct SerialPacket { + uint16_t magic, len, crc; + uint8_t payload[MAX_TRANS_UNIT]; + SerialPacket() : magic(SERIAL_PKT_MAGIC), len(0), crc(0) {} + }; + + // Fletcher-16 + // https://en.wikipedia.org/wiki/Fletcher%27s_checksum + inline static uint16_t fletcher16(const uint8_t *bytes, const size_t len) { + uint8_t sum1 = 0, sum2 = 0; + + for (size_t i = 0; i < len; i++) { + sum1 = (sum1 + bytes[i]) % 255; + sum2 = (sum2 + sum1) % 255; + } + + return (sum2 << 8) | sum1; + }; + + inline void serialBridgeSendPkt(const mesh::Packet *pkt) { + SerialPacket spkt; + spkt.len = pkt->writeTo(spkt.payload); + spkt.crc = fletcher16(spkt.payload, spkt.len); + BRIDGE_OVER_SERIAL.write((uint8_t *)&spkt, sizeof(SerialPacket)); + +#if MESH_PACKET_LOGGING + Serial.printf("%s: BRIDGE: TX, len=%d crc=0x%04x\n", getLogDateTime(), spkt.len, spkt.crc); +#endif + } + + inline void serialBridgeReceivePkt() { + static constexpr uint16_t size = sizeof(SerialPacket) + 1; + static uint8_t buffer[size]; + static uint16_t tail = 0; + + while (BRIDGE_OVER_SERIAL.available()) { + buffer[tail] = (uint8_t)BRIDGE_OVER_SERIAL.read(); + MESH_DEBUG_PRINT("%02x ", buffer[tail]); + tail = (tail + 1) % size; + + // Check for complete packet by looking back to where the magic number should be + const uint16_t head = (tail - sizeof(SerialPacket) + size) % size; + if ((buffer[head] | (buffer[(head + 1) % size] << 8)) != SERIAL_PKT_MAGIC) { + return; + } + + uint8_t bytes[MAX_TRANS_UNIT]; + const uint16_t len = buffer[(head + 2) % size] | (buffer[(head + 3) % size] << 8); + + if (len == 0 || len > sizeof(bytes)) { + MESH_DEBUG_PRINTLN("%s: BRIDGE: RX, invalid packet len", getLogDateTime()); + return; + } + + for (size_t i = 0; i < len; i++) { + bytes[i] = buffer[(head + 6 + i) % size]; + } + + const uint16_t crc = buffer[(head + 4) % size] | (buffer[(head + 5) % size] << 8); + const uint16_t f16 = fletcher16(bytes, len); + +#if MESH_PACKET_LOGGING + Serial.printf("%s: BRIDGE: RX, len=%d crc=0x%04x\n", getLogDateTime(), len, crc); +#endif + + if ((f16 != crc)) { + MESH_DEBUG_PRINTLN("%s: BRIDGE: RX, invalid packet checksum", getLogDateTime()); + return; + } + + mesh::Packet *pkt = _mgr->allocNew(); + if (pkt == NULL) { + MESH_DEBUG_PRINTLN("%s: BRIDGE: RX, no unused packets available", getLogDateTime()); + return; + } + + pkt->readFrom(bytes, len); + _mgr->queueInbound(pkt, futureMillis(0)); + } + } +#endif + protected: float getAirtimeBudgetFactor() const override { return _prefs.airtime_factor; @@ -316,21 +380,10 @@ protected: } void logTx(mesh::Packet* pkt, int len) override { #ifdef BRIDGE_OVER_SERIAL - SerialPacket serialpkt; - size_t seriallen = pkt->writeTo(serialpkt.payload); - - if (seriallen - 1 < MAX_TRANS_UNIT - 1) { - serialpkt.len = seriallen; - serialpkt.crc = fletcher16(serialpkt.payload, serialpkt.len); - BRIDGE_OVER_SERIAL.write((uint8_t *)&serialpkt, sizeof(SerialPacket)); - -#if MESH_PACKET_LOGGING - Serial.print(getLogDateTime()); - Serial.printf(": BRIDGE: Write to serial len=%d crc=0x%04x\n", serialpkt.len, serialpkt.crc); -#endif + if (!pkt->isMarkedDoNotRetransmit()) { + serialBridgeSendPkt(pkt); } #endif - if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { @@ -747,53 +800,7 @@ public: void loop() { #ifdef BRIDGE_OVER_SERIAL - static constexpr uint16_t size = sizeof(SerialPacket) + 1; - static uint8_t buffer[size]; - static uint16_t tail = 0; - - while (BRIDGE_OVER_SERIAL.available()) { - buffer[tail] = (uint8_t)BRIDGE_OVER_SERIAL.read(); - MESH_DEBUG_PRINT("%02x ", buffer[tail]); - tail = (tail + 1) % size; - - // Check for complete packet by looking back to where the magic number should be - uint16_t head = (tail - sizeof(SerialPacket) + size) % size; - const uint16_t magic = buffer[head] | (buffer[(head + 1) % size] << 8); - - if (magic == SERIAL_PKT_MAGIC) { - uint8_t bytes[MAX_TRANS_UNIT]; - const uint16_t len = buffer[(head + 2) % size] | (buffer[(head + 3) % size] << 8); - const uint16_t crc = buffer[(head + 4) % size] | (buffer[(head + 5) % size] << 8); - - if (len - 1 < MAX_TRANS_UNIT - 1) { - for (size_t i = 0; i < len; i++) { - bytes[i] = buffer[(head + 6 + i) % size]; - } - - uint16_t f16 = fletcher16(bytes, len); - -#if MESH_PACKET_LOGGING - Serial.print(getLogDateTime()); - Serial.printf(": BRIDGE: Read from serial len=%d crc=0x%04x valid=%s\n", len, crc, - (f16 == crc) ? "true" : "false"); -#endif - - if (f16 == crc) { - mesh::Packet *pkt = _mgr->allocNew(); - - if (pkt == NULL) { -#if MESH_PACKET_LOGGING - Serial.print(getLogDateTime()); - Serial.printf(": BRIDGE: Unable to allocate new Packet *pkt\n"); -#endif - } else { - pkt->readFrom(bytes, len); - _mgr->queueInbound(pkt, millis()); - } - } - } - } - } + serialBridgeReceivePkt(); #endif mesh::Mesh::loop(); @@ -829,6 +836,7 @@ static char command[80]; void setup() { Serial.begin(115200); + delay(1000); #ifdef BRIDGE_OVER_SERIAL #if defined(ESP32) @@ -842,13 +850,9 @@ void setup() { #else #error SerialBridge was not tested on the current platform #endif - BRIDGE_OVER_SERIAL.begin(115200); - MESH_DEBUG_PRINTLN("Bridge over serial: enabled"); #endif - delay(1000); - board.begin(); #ifdef DISPLAY_CLASS diff --git a/platformio.ini b/platformio.ini index 36cdf760..90e7cfb0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -32,7 +32,6 @@ build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1 build_src_filter = +<*.cpp> + - + ; ----------------- ESP32 --------------------- diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 704958d1..16e65bf2 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -49,9 +49,6 @@ build_flags = -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -; -D BRIDGE_OVER_SERIAL=Serial2 -; -D BRIDGE_OVER_SERIAL_RX=5 -; -D BRIDGE_OVER_SERIAL_TX=6 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} @@ -61,6 +58,28 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} +[env:Heltec_v3_Bridge] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"Heltec Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D BRIDGE_OVER_SERIAL=Serial2 + -D BRIDGE_OVER_SERIAL_RX=5 + -D BRIDGE_OVER_SERIAL_TX=6 + -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + ${esp32_ota.lib_deps} + [env:Heltec_v3_room_server] extends = Heltec_lora32_v3 build_flags = diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index a2169f1f..adf4a7e4 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -57,9 +57,6 @@ build_flags = -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -; -D BRIDGE_OVER_SERIAL=Serial2 -; -D BRIDGE_OVER_SERIAL_RX=34 -; -D BRIDGE_OVER_SERIAL_TX=25 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 ; -D CORE_DEBUG_LEVEL=3 @@ -67,6 +64,28 @@ lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} ${esp32_ota.lib_deps} +[env:LilyGo_TLora_V2_1_1_6_Bridge] +extends = LilyGo_TLora_V2_1_1_6 +build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + +<../examples/simple_repeater> +build_flags = + ${LilyGo_TLora_V2_1_1_6.build_flags} + -D ADVERT_NAME='"TLora-V2.1-1.6 Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D BRIDGE_OVER_SERIAL=Serial2 + -D BRIDGE_OVER_SERIAL_RX=34 + -D BRIDGE_OVER_SERIAL_TX=25 + -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +; -D CORE_DEBUG_LEVEL=3 +lib_deps = + ${LilyGo_TLora_V2_1_1_6.lib_deps} + ${esp32_ota.lib_deps} + [env:LilyGo_TLora_V2_1_1_6_terminal_chat] extends = LilyGo_TLora_V2_1_1_6 build_flags = diff --git a/variants/waveshare_rp2040_lora/platformio.ini b/variants/waveshare_rp2040_lora/platformio.ini index b1e4714a..515f7020 100644 --- a/variants/waveshare_rp2040_lora/platformio.ini +++ b/variants/waveshare_rp2040_lora/platformio.ini @@ -35,14 +35,27 @@ build_flags = ${waveshare_rp2040_lora.build_flags} -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 -; -D BRIDGE_OVER_SERIAL=Serial2 -; -D BRIDGE_OVER_SERIAL_RX=9 -; -D BRIDGE_OVER_SERIAL_TX=8 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${waveshare_rp2040_lora.build_src_filter} +<../examples/simple_repeater> +[env:waveshare_rp2040_lora_Bridge] +extends = waveshare_rp2040_lora +build_flags = ${waveshare_rp2040_lora.build_flags} + -D ADVERT_NAME='"RP2040-LoRa Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D BRIDGE_OVER_SERIAL=Serial2 + -D BRIDGE_OVER_SERIAL_RX=9 + -D BRIDGE_OVER_SERIAL_TX=8 + -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${waveshare_rp2040_lora.build_src_filter} + +<../examples/simple_repeater> + [env:waveshare_rp2040_lora_room_server] extends = waveshare_rp2040_lora build_flags = ${waveshare_rp2040_lora.build_flags} From 9fd7e9427a528db1cca00cbad1b1d854f2dea6de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Mon, 1 Sep 2025 10:53:51 +0100 Subject: [PATCH 07/21] Add bridge support for WSL3 board --- variants/heltec_v3/platformio.ini | 23 ++++++++++++++++++- variants/lilygo_tlora_v2_1/platformio.ini | 2 +- variants/waveshare_rp2040_lora/platformio.ini | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 1d2baa2b..3e7524c9 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -49,7 +49,7 @@ lib_deps = ${esp32_ota.lib_deps} bakercp/CRC32 @ ^2.0.0 -[env:Heltec_v3_Bridge] +[env:Heltec_v3_repeater_bridge] extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} @@ -208,6 +208,27 @@ lib_deps = ${esp32_ota.lib_deps} bakercp/CRC32 @ ^2.0.0 +[env:Heltec_WSL3_repeater_bridge] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D ADVERT_NAME='"Heltec WSL3 Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D BRIDGE_OVER_SERIAL=Serial2 + -D BRIDGE_OVER_SERIAL_RX=5 + -D BRIDGE_OVER_SERIAL_TX=6 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_repeater> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 + [env:Heltec_WSL3_room_server] extends = Heltec_lora32_v3 build_src_filter = ${Heltec_lora32_v3.build_src_filter} diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 4e2078c1..4e146cf6 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -64,7 +64,7 @@ lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} ${esp32_ota.lib_deps} -[env:LilyGo_TLora_V2_1_1_6_Bridge] +[env:LilyGo_TLora_V2_1_1_6_repeater_bridge] extends = LilyGo_TLora_V2_1_1_6 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + diff --git a/variants/waveshare_rp2040_lora/platformio.ini b/variants/waveshare_rp2040_lora/platformio.ini index 60b39ad6..0ec745ff 100644 --- a/variants/waveshare_rp2040_lora/platformio.ini +++ b/variants/waveshare_rp2040_lora/platformio.ini @@ -40,7 +40,7 @@ build_flags = ${waveshare_rp2040_lora.build_flags} build_src_filter = ${waveshare_rp2040_lora.build_src_filter} +<../examples/simple_repeater> -[env:waveshare_rp2040_lora_Bridge] +[env:waveshare_rp2040_lora_repeater_bridge] extends = waveshare_rp2040_lora build_flags = ${waveshare_rp2040_lora.build_flags} -D ADVERT_NAME='"RP2040-LoRa Bridge"' From 1948d284a022b3951e5b6b607b13ca0eed71bc1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Thu, 4 Sep 2025 23:43:05 +0100 Subject: [PATCH 08/21] Extract serial bridge into dedicated classes This commit refactors the serial bridge functionality out of the `simple_repeater` example and into a more reusable, object-oriented structure. An `AbstractBridge` interface has been introduced, along with a concrete `SerialBridge` implementation. This encapsulates all the logic for packet framing, checksum calculation, and serial communication, cleaning up the main example file significantly. The `simple_repeater` example now instantiates and uses the `SerialBridge` class, resulting in better separation of concerns and improved code organization. --- examples/simple_repeater/main.cpp | 109 +++--------------------------- src/helpers/AbstractBridge.h | 32 +++++++++ src/helpers/SerialBridge.cpp | 98 +++++++++++++++++++++++++++ src/helpers/SerialBridge.h | 30 ++++++++ 4 files changed, 171 insertions(+), 98 deletions(-) create mode 100644 src/helpers/AbstractBridge.h create mode 100644 src/helpers/SerialBridge.cpp create mode 100644 src/helpers/SerialBridge.h diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 7e44d40b..7150f0b2 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -79,7 +79,7 @@ /* ------------------------------ Code -------------------------------- */ #ifdef BRIDGE_OVER_SERIAL -#define SERIAL_PKT_MAGIC 0xcafe +#include "helpers/SerialBridge.h" #endif #define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS @@ -118,6 +118,10 @@ struct ClientInfo { #define MAX_CLIENTS 32 #endif +#ifdef BRIDGE_OVER_SERIAL +AbstractBridge* bridge; +#endif + struct NeighbourInfo { mesh::Identity id; uint32_t advert_timestamp; @@ -256,89 +260,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { #endif } -#ifdef BRIDGE_OVER_SERIAL - struct SerialPacket { - uint16_t magic, len, crc; - uint8_t payload[MAX_TRANS_UNIT]; - SerialPacket() : magic(SERIAL_PKT_MAGIC), len(0), crc(0) {} - }; - - // Fletcher-16 - // https://en.wikipedia.org/wiki/Fletcher%27s_checksum - inline static uint16_t fletcher16(const uint8_t *bytes, const size_t len) { - uint8_t sum1 = 0, sum2 = 0; - - for (size_t i = 0; i < len; i++) { - sum1 = (sum1 + bytes[i]) % 255; - sum2 = (sum2 + sum1) % 255; - } - - return (sum2 << 8) | sum1; - }; - - inline void serialBridgeSendPkt(const mesh::Packet *pkt) { - SerialPacket spkt; - spkt.len = pkt->writeTo(spkt.payload); - spkt.crc = fletcher16(spkt.payload, spkt.len); - BRIDGE_OVER_SERIAL.write((uint8_t *)&spkt, sizeof(SerialPacket)); - -#if MESH_PACKET_LOGGING - Serial.printf("%s: BRIDGE: TX, len=%d crc=0x%04x\n", getLogDateTime(), spkt.len, spkt.crc); -#endif - } - - inline void serialBridgeReceivePkt() { - static constexpr uint16_t size = sizeof(SerialPacket) + 1; - static uint8_t buffer[size]; - static uint16_t tail = 0; - - while (BRIDGE_OVER_SERIAL.available()) { - buffer[tail] = (uint8_t)BRIDGE_OVER_SERIAL.read(); - MESH_DEBUG_PRINT("%02x ", buffer[tail]); - tail = (tail + 1) % size; - - // Check for complete packet by looking back to where the magic number should be - const uint16_t head = (tail - sizeof(SerialPacket) + size) % size; - if ((buffer[head] | (buffer[(head + 1) % size] << 8)) != SERIAL_PKT_MAGIC) { - return; - } - - uint8_t bytes[MAX_TRANS_UNIT]; - const uint16_t len = buffer[(head + 2) % size] | (buffer[(head + 3) % size] << 8); - - if (len == 0 || len > sizeof(bytes)) { - MESH_DEBUG_PRINTLN("%s: BRIDGE: RX, invalid packet len", getLogDateTime()); - return; - } - - for (size_t i = 0; i < len; i++) { - bytes[i] = buffer[(head + 6 + i) % size]; - } - - const uint16_t crc = buffer[(head + 4) % size] | (buffer[(head + 5) % size] << 8); - const uint16_t f16 = fletcher16(bytes, len); - -#if MESH_PACKET_LOGGING - Serial.printf("%s: BRIDGE: RX, len=%d crc=0x%04x\n", getLogDateTime(), len, crc); -#endif - - if ((f16 != crc)) { - MESH_DEBUG_PRINTLN("%s: BRIDGE: RX, invalid packet checksum", getLogDateTime()); - return; - } - - mesh::Packet *pkt = _mgr->allocNew(); - if (pkt == NULL) { - MESH_DEBUG_PRINTLN("%s: BRIDGE: RX, no unused packets available", getLogDateTime()); - return; - } - - pkt->readFrom(bytes, len); - _mgr->queueInbound(pkt, futureMillis(0)); - } - } -#endif - protected: float getAirtimeBudgetFactor() const override { return _prefs.airtime_factor; @@ -389,7 +310,7 @@ protected: void logTx(mesh::Packet* pkt, int len) override { #ifdef BRIDGE_OVER_SERIAL if (!pkt->isMarkedDoNotRetransmit()) { - serialBridgeSendPkt(pkt); + bridge->onPacketTransmitted(pkt); } #endif if (_logging) { @@ -657,6 +578,9 @@ public: : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), _cli(board, rtc, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) { +#ifdef BRIDGE_OVER_SERIAL + bridge = new SerialBridge(BRIDGE_OVER_SERIAL, _mgr); +#endif memset(known_clients, 0, sizeof(known_clients)); next_local_advert = next_flood_advert = 0; set_radio_at = revert_radio_at = 0; @@ -858,7 +782,7 @@ public: void loop() { #ifdef BRIDGE_OVER_SERIAL - serialBridgeReceivePkt(); + bridge->loop(); #endif mesh::Mesh::loop(); @@ -910,18 +834,7 @@ void setup() { delay(1000); #ifdef BRIDGE_OVER_SERIAL -#if defined(ESP32) - BRIDGE_OVER_SERIAL.setPins(BRIDGE_OVER_SERIAL_RX, BRIDGE_OVER_SERIAL_TX); -#elif defined(RP2040_PLATFORM) - BRIDGE_OVER_SERIAL.setRX(BRIDGE_OVER_SERIAL_RX); - BRIDGE_OVER_SERIAL.setTX(BRIDGE_OVER_SERIAL_TX); -#elif defined(STM32_PLATFORM) - BRIDGE_OVER_SERIAL.setRx(BRIDGE_OVER_SERIAL_RX); - BRIDGE_OVER_SERIAL.setTx(BRIDGE_OVER_SERIAL_TX); -#else -#error SerialBridge was not tested on the current platform -#endif - BRIDGE_OVER_SERIAL.begin(115200); + bridge->begin(); #endif board.begin(); diff --git a/src/helpers/AbstractBridge.h b/src/helpers/AbstractBridge.h new file mode 100644 index 00000000..930eea65 --- /dev/null +++ b/src/helpers/AbstractBridge.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +class AbstractBridge { +public: + virtual ~AbstractBridge() {} + + /** + * @brief Initializes the bridge. + */ + virtual void begin() = 0; + + /** + * @brief A method to be called on every main loop iteration. + * Used for tasks like checking for incoming data. + */ + virtual void loop() = 0; + + /** + * @brief A callback that is triggered when the mesh transmits a packet. + * The bridge can use this to forward the packet. + * + * @param packet The packet that was transmitted. + */ + virtual void onPacketTransmitted(mesh::Packet* packet) = 0; + + /** + * @brief Processes a received packet from the bridge's medium. + */ + virtual void onPacketReceived() = 0; +}; diff --git a/src/helpers/SerialBridge.cpp b/src/helpers/SerialBridge.cpp new file mode 100644 index 00000000..235661aa --- /dev/null +++ b/src/helpers/SerialBridge.cpp @@ -0,0 +1,98 @@ +#include "SerialBridge.h" +#include + +#ifdef BRIDGE_OVER_SERIAL + +#define SERIAL_PKT_MAGIC 0xcafe + +struct SerialPacket { + uint16_t magic, len, crc; + uint8_t payload[MAX_TRANS_UNIT]; + SerialPacket() : magic(SERIAL_PKT_MAGIC), len(0), crc(0) {} +}; + +// Fletcher-16 +// https://en.wikipedia.org/wiki/Fletcher%27s_checksum +inline static uint16_t fletcher16(const uint8_t *bytes, const size_t len) { + uint8_t sum1 = 0, sum2 = 0; + + for (size_t i = 0; i < len; i++) { + sum1 = (sum1 + bytes[i]) % 255; + sum2 = (sum2 + sum1) % 255; + } + + return (sum2 << 8) | sum1; +}; + +SerialBridge::SerialBridge(Stream& serial, mesh::PacketManager* mgr) : _serial(&serial), _mgr(mgr) {} + +void SerialBridge::begin() { +#if defined(ESP32) + ((HardwareSerial*)_serial)->setPins(BRIDGE_OVER_SERIAL_RX, BRIDGE_OVER_SERIAL_TX); +#elif defined(RP2040_PLATFORM) + ((HardwareSerial*)_serial)->setRX(BRIDGE_OVER_SERIAL_RX); + ((HardwareSerial*)_serial)->setTX(BRIDGE_OVER_SERIAL_TX); +#elif defined(STM32_PLATFORM) + ((HardwareSerial*)_serial)->setRx(BRIDGE_OVER_SERIAL_RX); + ((HardwareSerial*)_serial)->setTx(BRIDGE_OVER_SERIAL_TX); +#else +#error SerialBridge was not tested on the current platform +#endif + ((HardwareSerial*)_serial)->begin(115200); +} + +void SerialBridge::onPacketTransmitted(mesh::Packet* packet) { + SerialPacket spkt; + spkt.len = packet->writeTo(spkt.payload); + spkt.crc = fletcher16(spkt.payload, spkt.len); + _serial->write((uint8_t *)&spkt, sizeof(SerialPacket)); +} + +void SerialBridge::loop() { + while (_serial->available()) { + onPacketReceived(); + } +} + +void SerialBridge::onPacketReceived() { + static constexpr uint16_t size = sizeof(SerialPacket) + 1; + static uint8_t buffer[size]; + static uint16_t tail = 0; + + buffer[tail] = (uint8_t)_serial->read(); + tail = (tail + 1) % size; + + // Check for complete packet by looking back to where the magic number should be + const uint16_t head = (tail - sizeof(SerialPacket) + size) % size; + if ((buffer[head] | (buffer[(head + 1) % size] << 8)) != SERIAL_PKT_MAGIC) { + return; + } + + uint8_t bytes[MAX_TRANS_UNIT]; + const uint16_t len = buffer[(head + 2) % size] | (buffer[(head + 3) % size] << 8); + + if (len == 0 || len > sizeof(bytes)) { + return; + } + + for (size_t i = 0; i < len; i++) { + bytes[i] = buffer[(head + 6 + i) % size]; + } + + const uint16_t crc = buffer[(head + 4) % size] | (buffer[(head + 5) % size] << 8); + const uint16_t f16 = fletcher16(bytes, len); + + if ((f16 != crc)) { + return; + } + + mesh::Packet *new_pkt = _mgr->allocNew(); + if (new_pkt == NULL) { + return; + } + + new_pkt->readFrom(bytes, len); + _mgr->queueInbound(new_pkt, 0); +} + +#endif diff --git a/src/helpers/SerialBridge.h b/src/helpers/SerialBridge.h new file mode 100644 index 00000000..3a4f0776 --- /dev/null +++ b/src/helpers/SerialBridge.h @@ -0,0 +1,30 @@ +#pragma once + +#include "helpers/AbstractBridge.h" +#include + +#ifdef BRIDGE_OVER_SERIAL + +/** + * @brief A bridge implementation that uses a serial port to connect two mesh networks. + */ +class SerialBridge : public AbstractBridge { +public: + /** + * @brief Construct a new Serial Bridge object + * + * @param serial The serial port to use for the bridge. + * @param mgr A pointer to the packet manager. + */ + SerialBridge(Stream& serial, mesh::PacketManager* mgr); + void begin() override; + void loop() override; + void onPacketTransmitted(mesh::Packet* packet) override; + void onPacketReceived() override; + +private: + Stream* _serial; + mesh::PacketManager* _mgr; +}; + +#endif From ee3c4baea5e420f8798ad313861fb1ed5bb2f730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Thu, 4 Sep 2025 23:50:13 +0100 Subject: [PATCH 09/21] Prevent packet loops and duplicates Implement a "seen packets" table to track packets that have already been processed by the serial bridge. This prevents packets from being re-transmitted over the serial link if they have already been seen, and it stops inbound packets from serial from being re-injected into the mesh if they are duplicates. Duplicate inbound packets are now freed to prevent memory leaks. --- src/helpers/SerialBridge.cpp | 16 +++++++++++----- src/helpers/SerialBridge.h | 2 ++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/helpers/SerialBridge.cpp b/src/helpers/SerialBridge.cpp index 235661aa..bbd2e0dd 100644 --- a/src/helpers/SerialBridge.cpp +++ b/src/helpers/SerialBridge.cpp @@ -42,10 +42,12 @@ void SerialBridge::begin() { } void SerialBridge::onPacketTransmitted(mesh::Packet* packet) { - SerialPacket spkt; - spkt.len = packet->writeTo(spkt.payload); - spkt.crc = fletcher16(spkt.payload, spkt.len); - _serial->write((uint8_t *)&spkt, sizeof(SerialPacket)); + if (!_seen_packets.hasSeen(packet)) { + SerialPacket spkt; + spkt.len = packet->writeTo(spkt.payload); + spkt.crc = fletcher16(spkt.payload, spkt.len); + _serial->write((uint8_t *)&spkt, sizeof(SerialPacket)); + } } void SerialBridge::loop() { @@ -92,7 +94,11 @@ void SerialBridge::onPacketReceived() { } new_pkt->readFrom(bytes, len); - _mgr->queueInbound(new_pkt, 0); + if (!_seen_packets.hasSeen(new_pkt)) { + _mgr->queueInbound(new_pkt, 0); + } else { + _mgr->free(new_pkt); + } } #endif diff --git a/src/helpers/SerialBridge.h b/src/helpers/SerialBridge.h index 3a4f0776..fe3c176f 100644 --- a/src/helpers/SerialBridge.h +++ b/src/helpers/SerialBridge.h @@ -1,6 +1,7 @@ #pragma once #include "helpers/AbstractBridge.h" +#include "helpers/SimpleMeshTables.h" #include #ifdef BRIDGE_OVER_SERIAL @@ -25,6 +26,7 @@ public: private: Stream* _serial; mesh::PacketManager* _mgr; + SimpleMeshTables _seen_packets; }; #endif From 2b920dfed32ae548812bba1383d3b2a734bfc8d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Fri, 5 Sep 2025 01:50:50 +0100 Subject: [PATCH 10/21] Rework packet serialization and parsing --- src/helpers/AbstractBridge.h | 4 +- src/helpers/SerialBridge.cpp | 106 +++++++++++++++++------------------ src/helpers/SerialBridge.h | 28 ++++++++- 3 files changed, 82 insertions(+), 56 deletions(-) diff --git a/src/helpers/AbstractBridge.h b/src/helpers/AbstractBridge.h index 930eea65..a348e933 100644 --- a/src/helpers/AbstractBridge.h +++ b/src/helpers/AbstractBridge.h @@ -27,6 +27,8 @@ public: /** * @brief Processes a received packet from the bridge's medium. + * + * @param packet The packet that was received. */ - virtual void onPacketReceived() = 0; + virtual void onPacketReceived(mesh::Packet* packet) = 0; }; diff --git a/src/helpers/SerialBridge.cpp b/src/helpers/SerialBridge.cpp index bbd2e0dd..c56645b6 100644 --- a/src/helpers/SerialBridge.cpp +++ b/src/helpers/SerialBridge.cpp @@ -3,14 +3,6 @@ #ifdef BRIDGE_OVER_SERIAL -#define SERIAL_PKT_MAGIC 0xcafe - -struct SerialPacket { - uint16_t magic, len, crc; - uint8_t payload[MAX_TRANS_UNIT]; - SerialPacket() : magic(SERIAL_PKT_MAGIC), len(0), crc(0) {} -}; - // Fletcher-16 // https://en.wikipedia.org/wiki/Fletcher%27s_checksum inline static uint16_t fletcher16(const uint8_t *bytes, const size_t len) { @@ -43,61 +35,67 @@ void SerialBridge::begin() { void SerialBridge::onPacketTransmitted(mesh::Packet* packet) { if (!_seen_packets.hasSeen(packet)) { - SerialPacket spkt; - spkt.len = packet->writeTo(spkt.payload); - spkt.crc = fletcher16(spkt.payload, spkt.len); - _serial->write((uint8_t *)&spkt, sizeof(SerialPacket)); + uint8_t buffer[MAX_SERIAL_PACKET_SIZE]; + uint16_t len = packet->writeTo(buffer + 4); + + buffer[0] = (SERIAL_PKT_MAGIC >> 8) & 0xFF; + buffer[1] = SERIAL_PKT_MAGIC & 0xFF; + buffer[2] = (len >> 8) & 0xFF; + buffer[3] = len & 0xFF; + + uint16_t checksum = fletcher16(buffer + 4, len); + buffer[4 + len] = (checksum >> 8) & 0xFF; + buffer[5 + len] = checksum & 0xFF; + + _serial->write(buffer, len + SERIAL_OVERHEAD); } } void SerialBridge::loop() { while (_serial->available()) { - onPacketReceived(); + uint8_t b = _serial->read(); + + if (_rx_buffer_pos < 2) { + // Waiting for magic word + if ((_rx_buffer_pos == 0 && b == ((SERIAL_PKT_MAGIC >> 8) & 0xFF)) || + (_rx_buffer_pos == 1 && b == (SERIAL_PKT_MAGIC & 0xFF))) { + _rx_buffer[_rx_buffer_pos++] = b; + } else { + _rx_buffer_pos = 0; + } + } else { + // Reading length, payload, and checksum + _rx_buffer[_rx_buffer_pos++] = b; + + if (_rx_buffer_pos >= 4) { + uint16_t len = (_rx_buffer[2] << 8) | _rx_buffer[3]; + if (len > MAX_PACKET_PAYLOAD) { + _rx_buffer_pos = 0; // Invalid length, reset + return; + } + + if (_rx_buffer_pos == len + SERIAL_OVERHEAD) { // Full packet received + uint16_t checksum = (_rx_buffer[4 + len] << 8) | _rx_buffer[5 + len]; + if (checksum == fletcher16(_rx_buffer + 4, len)) { + mesh::Packet *pkt = _mgr->allocNew(); + if (pkt) { + memcpy(pkt->payload, _rx_buffer + 4, len); + pkt->payload_len = len; + onPacketReceived(pkt); + } + } + _rx_buffer_pos = 0; // Reset for next packet + } + } + } } } -void SerialBridge::onPacketReceived() { - static constexpr uint16_t size = sizeof(SerialPacket) + 1; - static uint8_t buffer[size]; - static uint16_t tail = 0; - - buffer[tail] = (uint8_t)_serial->read(); - tail = (tail + 1) % size; - - // Check for complete packet by looking back to where the magic number should be - const uint16_t head = (tail - sizeof(SerialPacket) + size) % size; - if ((buffer[head] | (buffer[(head + 1) % size] << 8)) != SERIAL_PKT_MAGIC) { - return; - } - - uint8_t bytes[MAX_TRANS_UNIT]; - const uint16_t len = buffer[(head + 2) % size] | (buffer[(head + 3) % size] << 8); - - if (len == 0 || len > sizeof(bytes)) { - return; - } - - for (size_t i = 0; i < len; i++) { - bytes[i] = buffer[(head + 6 + i) % size]; - } - - const uint16_t crc = buffer[(head + 4) % size] | (buffer[(head + 5) % size] << 8); - const uint16_t f16 = fletcher16(bytes, len); - - if ((f16 != crc)) { - return; - } - - mesh::Packet *new_pkt = _mgr->allocNew(); - if (new_pkt == NULL) { - return; - } - - new_pkt->readFrom(bytes, len); - if (!_seen_packets.hasSeen(new_pkt)) { - _mgr->queueInbound(new_pkt, 0); +void SerialBridge::onPacketReceived(mesh::Packet* packet) { + if (!_seen_packets.hasSeen(packet)) { + _mgr->queueInbound(packet, 0); } else { - _mgr->free(new_pkt); + _mgr->free(packet); } } diff --git a/src/helpers/SerialBridge.h b/src/helpers/SerialBridge.h index fe3c176f..7b729870 100644 --- a/src/helpers/SerialBridge.h +++ b/src/helpers/SerialBridge.h @@ -21,12 +21,38 @@ public: void begin() override; void loop() override; void onPacketTransmitted(mesh::Packet* packet) override; - void onPacketReceived() override; + void onPacketReceived(mesh::Packet* packet) override; private: + /** + * @brief The 2-byte magic word used to signify the start of a packet. + */ + static constexpr uint16_t SERIAL_PKT_MAGIC = 0xCAFE; + + /** + * @brief The total overhead of the serial protocol in bytes. + * [MAGIC_WORD (2 bytes)] [LENGTH (2 bytes)] [PAYLOAD (variable)] [CHECKSUM (2 bytes)] + */ + static constexpr uint16_t SERIAL_OVERHEAD = 6; + + /** + * @brief The maximum size of a packet on the serial line. + * + * This is calculated as the sum of: + * - 1 byte for the packet header (from mesh::Packet) + * - 4 bytes for transport codes (from mesh::Packet) + * - 1 byte for the path length (from mesh::Packet) + * - MAX_PATH_SIZE for the path itself (from MeshCore.h) + * - MAX_PACKET_PAYLOAD for the payload (from MeshCore.h) + * - SERIAL_OVERHEAD for the serial framing + */ + static constexpr uint16_t MAX_SERIAL_PACKET_SIZE = (MAX_TRANS_UNIT + 1) + SERIAL_OVERHEAD; + Stream* _serial; mesh::PacketManager* _mgr; SimpleMeshTables _seen_packets; + uint8_t _rx_buffer[MAX_SERIAL_PACKET_SIZE]; // Buffer for serial data + uint16_t _rx_buffer_pos = 0; }; #endif From 77ab19153e9c36eddf7f4c3546e1c5c6f0ec7396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Fri, 5 Sep 2025 02:07:26 +0100 Subject: [PATCH 11/21] Add serial logging for TX/RX packets --- examples/simple_repeater/main.cpp | 2 +- src/helpers/SerialBridge.cpp | 18 +++++++++++++++++- src/helpers/SerialBridge.h | 5 ++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 7150f0b2..93d3656f 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -579,7 +579,7 @@ public: _cli(board, rtc, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) { #ifdef BRIDGE_OVER_SERIAL - bridge = new SerialBridge(BRIDGE_OVER_SERIAL, _mgr); + bridge = new SerialBridge(BRIDGE_OVER_SERIAL, _mgr, &rtc); #endif memset(known_clients, 0, sizeof(known_clients)); next_local_advert = next_flood_advert = 0; diff --git a/src/helpers/SerialBridge.cpp b/src/helpers/SerialBridge.cpp index c56645b6..f971e39c 100644 --- a/src/helpers/SerialBridge.cpp +++ b/src/helpers/SerialBridge.cpp @@ -1,5 +1,6 @@ #include "SerialBridge.h" #include +#include #ifdef BRIDGE_OVER_SERIAL @@ -16,7 +17,15 @@ inline static uint16_t fletcher16(const uint8_t *bytes, const size_t len) { return (sum2 << 8) | sum1; }; -SerialBridge::SerialBridge(Stream& serial, mesh::PacketManager* mgr) : _serial(&serial), _mgr(mgr) {} +const char* SerialBridge::getLogDateTime() { + static char tmp[32]; + uint32_t now = _rtc->getCurrentTime(); + DateTime dt = DateTime(now); + sprintf(tmp, "%02d:%02d:%02d - %d/%d/%d U", dt.hour(), dt.minute(), dt.second(), dt.day(), dt.month(), dt.year()); + return tmp; +} + +SerialBridge::SerialBridge(Stream& serial, mesh::PacketManager* mgr, mesh::RTCClock* rtc) : _serial(&serial), _mgr(mgr), _rtc(rtc) {} void SerialBridge::begin() { #if defined(ESP32) @@ -48,6 +57,10 @@ void SerialBridge::onPacketTransmitted(mesh::Packet* packet) { buffer[5 + len] = checksum & 0xFF; _serial->write(buffer, len + SERIAL_OVERHEAD); + +#if MESH_PACKET_LOGGING + Serial.printf("%s: BRIDGE: TX, len=%d crc=0x%04x\n", getLogDateTime(), len, checksum); +#endif } } @@ -77,6 +90,9 @@ void SerialBridge::loop() { if (_rx_buffer_pos == len + SERIAL_OVERHEAD) { // Full packet received uint16_t checksum = (_rx_buffer[4 + len] << 8) | _rx_buffer[5 + len]; if (checksum == fletcher16(_rx_buffer + 4, len)) { +#if MESH_PACKET_LOGGING + Serial.printf("%s: BRIDGE: RX, len=%d crc=0x%04x\n", getLogDateTime(), len, checksum); +#endif mesh::Packet *pkt = _mgr->allocNew(); if (pkt) { memcpy(pkt->payload, _rx_buffer + 4, len); diff --git a/src/helpers/SerialBridge.h b/src/helpers/SerialBridge.h index 7b729870..cc837d5e 100644 --- a/src/helpers/SerialBridge.h +++ b/src/helpers/SerialBridge.h @@ -16,14 +16,16 @@ public: * * @param serial The serial port to use for the bridge. * @param mgr A pointer to the packet manager. + * @param rtc A pointer to the RTC clock. */ - SerialBridge(Stream& serial, mesh::PacketManager* mgr); + SerialBridge(Stream& serial, mesh::PacketManager* mgr, mesh::RTCClock* rtc); void begin() override; void loop() override; void onPacketTransmitted(mesh::Packet* packet) override; void onPacketReceived(mesh::Packet* packet) override; private: + const char* getLogDateTime(); /** * @brief The 2-byte magic word used to signify the start of a packet. */ @@ -50,6 +52,7 @@ private: Stream* _serial; mesh::PacketManager* _mgr; + mesh::RTCClock* _rtc; SimpleMeshTables _seen_packets; uint8_t _rx_buffer[MAX_SERIAL_PACKET_SIZE]; // Buffer for serial data uint16_t _rx_buffer_pos = 0; From 375093f78df41e1632afb5f3362d71441442f2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Fri, 5 Sep 2025 09:22:06 +0100 Subject: [PATCH 12/21] Add nRF52 support and refactor packet handling This commit introduces several improvements to the SerialBridge helper: - Adds support for the nRF52 platform by implementing the `setPins` configuration. - Corrects the type cast for the RP2040 platform from `HardwareSerial` to `SerialUART`. - Refactors packet deserialization to use a new `Packet::readFrom()` method instead of a direct `memcpy`, improving encapsulation. - Updates the packet length validation to use the more appropriate `MAX_TRANS_UNIT` constant. --- src/helpers/SerialBridge.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/helpers/SerialBridge.cpp b/src/helpers/SerialBridge.cpp index f971e39c..eefd751a 100644 --- a/src/helpers/SerialBridge.cpp +++ b/src/helpers/SerialBridge.cpp @@ -29,13 +29,15 @@ SerialBridge::SerialBridge(Stream& serial, mesh::PacketManager* mgr, mesh::RTCCl void SerialBridge::begin() { #if defined(ESP32) - ((HardwareSerial*)_serial)->setPins(BRIDGE_OVER_SERIAL_RX, BRIDGE_OVER_SERIAL_TX); + ((HardwareSerial *)_serial)->setPins(BRIDGE_OVER_SERIAL_RX, BRIDGE_OVER_SERIAL_TX); +#elif defined(NRF52_PLATFORM) + ((HardwareSerial *)_serial)->setPins(BRIDGE_OVER_SERIAL_RX, BRIDGE_OVER_SERIAL_TX); #elif defined(RP2040_PLATFORM) - ((HardwareSerial*)_serial)->setRX(BRIDGE_OVER_SERIAL_RX); - ((HardwareSerial*)_serial)->setTX(BRIDGE_OVER_SERIAL_TX); + ((SerialUART *)_serial)->setRX(BRIDGE_OVER_SERIAL_RX); + ((SerialUART *)_serial)->setTX(BRIDGE_OVER_SERIAL_TX); #elif defined(STM32_PLATFORM) - ((HardwareSerial*)_serial)->setRx(BRIDGE_OVER_SERIAL_RX); - ((HardwareSerial*)_serial)->setTx(BRIDGE_OVER_SERIAL_TX); + ((HardwareSerial *)_serial)->setRx(BRIDGE_OVER_SERIAL_RX); + ((HardwareSerial *)_serial)->setTx(BRIDGE_OVER_SERIAL_TX); #else #error SerialBridge was not tested on the current platform #endif @@ -82,21 +84,20 @@ void SerialBridge::loop() { if (_rx_buffer_pos >= 4) { uint16_t len = (_rx_buffer[2] << 8) | _rx_buffer[3]; - if (len > MAX_PACKET_PAYLOAD) { + if (len > (MAX_TRANS_UNIT + 1)) { _rx_buffer_pos = 0; // Invalid length, reset return; } - if (_rx_buffer_pos == len + SERIAL_OVERHEAD) { // Full packet received - uint16_t checksum = (_rx_buffer[4 + len] << 8) | _rx_buffer[5 + len]; - if (checksum == fletcher16(_rx_buffer + 4, len)) { + if (_rx_buffer_pos == len + SERIAL_OVERHEAD) { // Full packet received + uint16_t checksum = (_rx_buffer[4 + len] << 8) | _rx_buffer[5 + len]; + if (checksum == fletcher16(_rx_buffer + 4, len)) { #if MESH_PACKET_LOGGING - Serial.printf("%s: BRIDGE: RX, len=%d crc=0x%04x\n", getLogDateTime(), len, checksum); + Serial.printf("%s: BRIDGE: RX, len=%d crc=0x%04x\n", getLogDateTime(), len, checksum); #endif - mesh::Packet *pkt = _mgr->allocNew(); + mesh::Packet* pkt = _mgr->allocNew(); if (pkt) { - memcpy(pkt->payload, _rx_buffer + 4, len); - pkt->payload_len = len; + pkt->readFrom(_rx_buffer + 4, len); onPacketReceived(pkt); } } From 5843a12c71c094ef6e928ab714e253c8593c9c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Fri, 5 Sep 2025 11:28:40 +0100 Subject: [PATCH 13/21] Rename `SerialBridge` to `RS232Bridge` --- examples/simple_repeater/main.cpp | 16 ++++----- .../RS232Bridge.cpp} | 34 +++++++++++-------- .../{SerialBridge.h => bridges/RS232Bridge.h} | 6 ++-- variants/heltec_v3/platformio.ini | 14 ++++---- variants/lilygo_tlora_v2_1/platformio.ini | 7 ++-- variants/waveshare_rp2040_lora/platformio.ini | 7 ++-- 6 files changed, 46 insertions(+), 38 deletions(-) rename src/helpers/{SerialBridge.cpp => bridges/RS232Bridge.cpp} (73%) rename src/helpers/{SerialBridge.h => bridges/RS232Bridge.h} (92%) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 93d3656f..7b16ab20 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -78,8 +78,8 @@ /* ------------------------------ Code -------------------------------- */ -#ifdef BRIDGE_OVER_SERIAL -#include "helpers/SerialBridge.h" +#ifdef WITH_RS232_BRIDGE +#include "helpers/bridges/RS232Bridge.h" #endif #define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS @@ -118,7 +118,7 @@ struct ClientInfo { #define MAX_CLIENTS 32 #endif -#ifdef BRIDGE_OVER_SERIAL +#ifdef WITH_RS232_BRIDGE AbstractBridge* bridge; #endif @@ -308,7 +308,7 @@ protected: } } void logTx(mesh::Packet* pkt, int len) override { -#ifdef BRIDGE_OVER_SERIAL +#ifdef WITH_RS232_BRIDGE if (!pkt->isMarkedDoNotRetransmit()) { bridge->onPacketTransmitted(pkt); } @@ -578,8 +578,8 @@ public: : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), _cli(board, rtc, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) { -#ifdef BRIDGE_OVER_SERIAL - bridge = new SerialBridge(BRIDGE_OVER_SERIAL, _mgr, &rtc); +#ifdef WITH_RS232_BRIDGE + bridge = new RS232Bridge(WITH_RS232_BRIDGE, _mgr, &rtc); #endif memset(known_clients, 0, sizeof(known_clients)); next_local_advert = next_flood_advert = 0; @@ -781,7 +781,7 @@ public: } void loop() { -#ifdef BRIDGE_OVER_SERIAL +#ifdef WITH_RS232_BRIDGE bridge->loop(); #endif @@ -833,7 +833,7 @@ void setup() { Serial.begin(115200); delay(1000); -#ifdef BRIDGE_OVER_SERIAL +#ifdef WITH_RS232_BRIDGE bridge->begin(); #endif diff --git a/src/helpers/SerialBridge.cpp b/src/helpers/bridges/RS232Bridge.cpp similarity index 73% rename from src/helpers/SerialBridge.cpp rename to src/helpers/bridges/RS232Bridge.cpp index eefd751a..801bcc5c 100644 --- a/src/helpers/SerialBridge.cpp +++ b/src/helpers/bridges/RS232Bridge.cpp @@ -1,8 +1,8 @@ -#include "SerialBridge.h" +#include "RS232Bridge.h" #include #include -#ifdef BRIDGE_OVER_SERIAL +#ifdef WITH_RS232_BRIDGE // Fletcher-16 // https://en.wikipedia.org/wiki/Fletcher%27s_checksum @@ -17,7 +17,7 @@ inline static uint16_t fletcher16(const uint8_t *bytes, const size_t len) { return (sum2 << 8) | sum1; }; -const char* SerialBridge::getLogDateTime() { +const char* RS232Bridge::getLogDateTime() { static char tmp[32]; uint32_t now = _rtc->getCurrentTime(); DateTime dt = DateTime(now); @@ -25,26 +25,30 @@ const char* SerialBridge::getLogDateTime() { return tmp; } -SerialBridge::SerialBridge(Stream& serial, mesh::PacketManager* mgr, mesh::RTCClock* rtc) : _serial(&serial), _mgr(mgr), _rtc(rtc) {} +RS232Bridge::RS232Bridge(Stream& serial, mesh::PacketManager* mgr, mesh::RTCClock* rtc) : _serial(&serial), _mgr(mgr), _rtc(rtc) {} + +void RS232Bridge::begin() { +#if !defined(WITH_RS232_BRIDGE_RX) || !defined(WITH_RS232_BRIDGE_TX) +#error "WITH_RS232_BRIDGE_RX and WITH_RS232_BRIDGE_TX must be defined" +#endif -void SerialBridge::begin() { #if defined(ESP32) - ((HardwareSerial *)_serial)->setPins(BRIDGE_OVER_SERIAL_RX, BRIDGE_OVER_SERIAL_TX); + ((HardwareSerial *)_serial)->setPins(WITH_RS232_BRIDGE_RX, WITH_RS232_BRIDGE_TX); #elif defined(NRF52_PLATFORM) - ((HardwareSerial *)_serial)->setPins(BRIDGE_OVER_SERIAL_RX, BRIDGE_OVER_SERIAL_TX); + ((HardwareSerial *)_serial)->setPins(WITH_RS232_BRIDGE_RX, WITH_RS232_BRIDGE_TX); #elif defined(RP2040_PLATFORM) - ((SerialUART *)_serial)->setRX(BRIDGE_OVER_SERIAL_RX); - ((SerialUART *)_serial)->setTX(BRIDGE_OVER_SERIAL_TX); + ((SerialUART *)_serial)->setRX(WITH_RS232_BRIDGE_RX); + ((SerialUART *)_serial)->setTX(WITH_RS232_BRIDGE_TX); #elif defined(STM32_PLATFORM) - ((HardwareSerial *)_serial)->setRx(BRIDGE_OVER_SERIAL_RX); - ((HardwareSerial *)_serial)->setTx(BRIDGE_OVER_SERIAL_TX); + ((HardwareSerial *)_serial)->setRx(WITH_RS232_BRIDGE_RX); + ((HardwareSerial *)_serial)->setTx(WITH_RS232_BRIDGE_TX); #else -#error SerialBridge was not tested on the current platform +#error RS232Bridge was not tested on the current platform #endif ((HardwareSerial*)_serial)->begin(115200); } -void SerialBridge::onPacketTransmitted(mesh::Packet* packet) { +void RS232Bridge::onPacketTransmitted(mesh::Packet* packet) { if (!_seen_packets.hasSeen(packet)) { uint8_t buffer[MAX_SERIAL_PACKET_SIZE]; uint16_t len = packet->writeTo(buffer + 4); @@ -66,7 +70,7 @@ void SerialBridge::onPacketTransmitted(mesh::Packet* packet) { } } -void SerialBridge::loop() { +void RS232Bridge::loop() { while (_serial->available()) { uint8_t b = _serial->read(); @@ -108,7 +112,7 @@ void SerialBridge::loop() { } } -void SerialBridge::onPacketReceived(mesh::Packet* packet) { +void RS232Bridge::onPacketReceived(mesh::Packet* packet) { if (!_seen_packets.hasSeen(packet)) { _mgr->queueInbound(packet, 0); } else { diff --git a/src/helpers/SerialBridge.h b/src/helpers/bridges/RS232Bridge.h similarity index 92% rename from src/helpers/SerialBridge.h rename to src/helpers/bridges/RS232Bridge.h index cc837d5e..0e99040f 100644 --- a/src/helpers/SerialBridge.h +++ b/src/helpers/bridges/RS232Bridge.h @@ -4,12 +4,12 @@ #include "helpers/SimpleMeshTables.h" #include -#ifdef BRIDGE_OVER_SERIAL +#ifdef WITH_RS232_BRIDGE /** * @brief A bridge implementation that uses a serial port to connect two mesh networks. */ -class SerialBridge : public AbstractBridge { +class RS232Bridge : public AbstractBridge { public: /** * @brief Construct a new Serial Bridge object @@ -18,7 +18,7 @@ public: * @param mgr A pointer to the packet manager. * @param rtc A pointer to the RTC clock. */ - SerialBridge(Stream& serial, mesh::PacketManager* mgr, mesh::RTCClock* rtc); + RS232Bridge(Stream& serial, mesh::PacketManager* mgr, mesh::RTCClock* rtc); void begin() override; void loop() override; void onPacketTransmitted(mesh::Packet* packet) override; diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 3e7524c9..4ffb11ba 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -59,13 +59,14 @@ build_flags = -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 - -D BRIDGE_OVER_SERIAL=Serial2 - -D BRIDGE_OVER_SERIAL_RX=5 - -D BRIDGE_OVER_SERIAL_TX=6 + -D WITH_RS232_BRIDGE=Serial2 + -D WITH_RS232_BRIDGE_RX=5 + -D WITH_RS232_BRIDGE_TX=6 -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/simple_repeater> lib_deps = ${Heltec_lora32_v3.lib_deps} @@ -217,12 +218,13 @@ build_flags = -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 - -D BRIDGE_OVER_SERIAL=Serial2 - -D BRIDGE_OVER_SERIAL_RX=5 - -D BRIDGE_OVER_SERIAL_TX=6 + -D WITH_RS232_BRIDGE=Serial2 + -D WITH_RS232_BRIDGE_RX=5 + -D WITH_RS232_BRIDGE_TX=6 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + +<../examples/simple_repeater> lib_deps = ${Heltec_lora32_v3.lib_deps} diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 4e146cf6..313b9844 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -68,6 +68,7 @@ lib_deps = extends = LilyGo_TLora_V2_1_1_6 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + +<../examples/simple_repeater> build_flags = ${LilyGo_TLora_V2_1_1_6.build_flags} @@ -76,9 +77,9 @@ build_flags = -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 - -D BRIDGE_OVER_SERIAL=Serial2 - -D BRIDGE_OVER_SERIAL_RX=34 - -D BRIDGE_OVER_SERIAL_TX=25 + -D WITH_RS232_BRIDGE=Serial2 + -D WITH_RS232_BRIDGE_RX=34 + -D WITH_RS232_BRIDGE_TX=25 -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 ; -D CORE_DEBUG_LEVEL=3 diff --git a/variants/waveshare_rp2040_lora/platformio.ini b/variants/waveshare_rp2040_lora/platformio.ini index 0ec745ff..8824ddbd 100644 --- a/variants/waveshare_rp2040_lora/platformio.ini +++ b/variants/waveshare_rp2040_lora/platformio.ini @@ -48,12 +48,13 @@ build_flags = ${waveshare_rp2040_lora.build_flags} -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=8 - -D BRIDGE_OVER_SERIAL=Serial2 - -D BRIDGE_OVER_SERIAL_RX=9 - -D BRIDGE_OVER_SERIAL_TX=8 + -D WITH_RS232_BRIDGE=Serial2 + -D WITH_RS232_BRIDGE_RX=9 + -D WITH_RS232_BRIDGE_TX=8 -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${waveshare_rp2040_lora.build_src_filter} + + +<../examples/simple_repeater> [env:waveshare_rp2040_lora_room_server] From cb99eb4ae8db2a243b56d27e8e679fd222d09859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Fri, 5 Sep 2025 14:49:06 +0100 Subject: [PATCH 14/21] Remove retransmit check for RS232 bridge in logTx Since the flag is preserved and respected by the mesh processing on the receiving end, there's no risk of these packets being retransmitted endlessly. --- examples/simple_repeater/main.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 7b16ab20..3e2795d6 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -309,9 +309,7 @@ protected: } void logTx(mesh::Packet* pkt, int len) override { #ifdef WITH_RS232_BRIDGE - if (!pkt->isMarkedDoNotRetransmit()) { - bridge->onPacketTransmitted(pkt); - } + bridge->onPacketTransmitted(pkt); #endif if (_logging) { File f = openAppend(PACKET_LOG_FILE); From 5b9d11ac8f9c91431ef417ac16280440d1920ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Sun, 7 Sep 2025 21:39:54 +0100 Subject: [PATCH 15/21] Support ESPNow and improve documentation --- examples/simple_repeater/main.cpp | 62 +++++--- src/helpers/bridges/ESPNowBridge.cpp | 184 ++++++++++++++++++++++ src/helpers/bridges/ESPNowBridge.h | 170 ++++++++++++++++++++ src/helpers/bridges/RS232Bridge.cpp | 5 +- src/helpers/bridges/RS232Bridge.h | 77 +++++++-- variants/lilygo_tlora_v2_1/platformio.ini | 71 ++++++--- 6 files changed, 510 insertions(+), 59 deletions(-) create mode 100644 src/helpers/bridges/ESPNowBridge.cpp create mode 100644 src/helpers/bridges/ESPNowBridge.h diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 3e2795d6..b78481d0 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -80,30 +80,36 @@ #ifdef WITH_RS232_BRIDGE #include "helpers/bridges/RS232Bridge.h" +#define WITH_BRIDGE #endif -#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS -#define REQ_TYPE_KEEP_ALIVE 0x02 -#define REQ_TYPE_GET_TELEMETRY_DATA 0x03 +#ifdef WITH_ESPNOW_BRIDGE +#include "helpers/bridges/ESPNowBridge.h" +#define WITH_BRIDGE +#endif -#define RESP_SERVER_LOGIN_OK 0 // response to ANON_REQ +#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS +#define REQ_TYPE_KEEP_ALIVE 0x02 +#define REQ_TYPE_GET_TELEMETRY_DATA 0x03 -struct RepeaterStats { - uint16_t batt_milli_volts; - uint16_t curr_tx_queue_len; - int16_t noise_floor; - int16_t last_rssi; - uint32_t n_packets_recv; - uint32_t n_packets_sent; - uint32_t total_air_time_secs; - uint32_t total_up_time_secs; - uint32_t n_sent_flood, n_sent_direct; - uint32_t n_recv_flood, n_recv_direct; - uint16_t err_events; // was 'n_full_events' - int16_t last_snr; // x 4 - uint16_t n_direct_dups, n_flood_dups; - uint32_t total_rx_air_time_secs; -}; +#define RESP_SERVER_LOGIN_OK 0 // response to ANON_REQ + + struct RepeaterStats { + uint16_t batt_milli_volts; + uint16_t curr_tx_queue_len; + int16_t noise_floor; + int16_t last_rssi; + uint32_t n_packets_recv; + uint32_t n_packets_sent; + uint32_t total_air_time_secs; + uint32_t total_up_time_secs; + uint32_t n_sent_flood, n_sent_direct; + uint32_t n_recv_flood, n_recv_direct; + uint16_t err_events; // was 'n_full_events' + int16_t last_snr; // x 4 + uint16_t n_direct_dups, n_flood_dups; + uint32_t total_rx_air_time_secs; + }; struct ClientInfo { mesh::Identity id; @@ -118,7 +124,7 @@ struct ClientInfo { #define MAX_CLIENTS 32 #endif -#ifdef WITH_RS232_BRIDGE +#ifdef WITH_BRIDGE AbstractBridge* bridge; #endif @@ -308,7 +314,7 @@ protected: } } void logTx(mesh::Packet* pkt, int len) override { -#ifdef WITH_RS232_BRIDGE +#ifdef WITH_BRIDGE bridge->onPacketTransmitted(pkt); #endif if (_logging) { @@ -576,8 +582,14 @@ public: : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), _cli(board, rtc, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) { -#ifdef WITH_RS232_BRIDGE +#ifdef WITH_BRIDGE +#if defined(WITH_RS232_BRIDGE) bridge = new RS232Bridge(WITH_RS232_BRIDGE, _mgr, &rtc); +#elif defined(WITH_ESPNOW_BRIDGE) + bridge = new ESPNowBridge(_mgr, &rtc); +#else +#error "You must choose either RS232 or ESPNow bridge" +#endif #endif memset(known_clients, 0, sizeof(known_clients)); next_local_advert = next_flood_advert = 0; @@ -779,7 +791,7 @@ public: } void loop() { -#ifdef WITH_RS232_BRIDGE +#ifdef WITH_BRIDGE bridge->loop(); #endif @@ -831,7 +843,7 @@ void setup() { Serial.begin(115200); delay(1000); -#ifdef WITH_RS232_BRIDGE +#ifdef WITH_BRIDGE bridge->begin(); #endif diff --git a/src/helpers/bridges/ESPNowBridge.cpp b/src/helpers/bridges/ESPNowBridge.cpp new file mode 100644 index 00000000..a470d521 --- /dev/null +++ b/src/helpers/bridges/ESPNowBridge.cpp @@ -0,0 +1,184 @@ +#include "ESPNowBridge.h" + +#include +#include +#include + +#ifdef WITH_ESPNOW_BRIDGE + +// Static member to handle callbacks +ESPNowBridge *ESPNowBridge::_instance = nullptr; + +// Static callback wrappers +void ESPNowBridge::recv_cb(const uint8_t *mac, const uint8_t *data, int len) { + if (_instance) { + _instance->onDataRecv(mac, data, len); + } +} + +void ESPNowBridge::send_cb(const uint8_t *mac, esp_now_send_status_t status) { + if (_instance) { + _instance->onDataSent(mac, status); + } +} + +// Fletcher16 checksum calculation +static uint16_t fletcher16(const uint8_t *data, size_t len) { + uint16_t sum1 = 0; + uint16_t sum2 = 0; + + while (len--) { + sum1 = (sum1 + *data++) % 255; + sum2 = (sum2 + sum1) % 255; + } + + return (sum2 << 8) | sum1; +} + +ESPNowBridge::ESPNowBridge(mesh::PacketManager *mgr, mesh::RTCClock *rtc) + : _mgr(mgr), _rtc(rtc), _rx_buffer_pos(0) { + _instance = this; +} + +void ESPNowBridge::begin() { + // Initialize WiFi in station mode + WiFi.mode(WIFI_STA); + + // Initialize ESP-NOW + if (esp_now_init() != ESP_OK) { + Serial.printf("%s: ESPNOW BRIDGE: Error initializing ESP-NOW\n", getLogDateTime()); + return; + } + + // Register callbacks + esp_now_register_recv_cb(recv_cb); + esp_now_register_send_cb(send_cb); + + // Add broadcast peer + esp_now_peer_info_t peerInfo = {}; + memset(&peerInfo, 0, sizeof(peerInfo)); + memset(peerInfo.peer_addr, 0xFF, ESP_NOW_ETH_ALEN); // Broadcast address + peerInfo.channel = 0; + peerInfo.encrypt = false; + + if (esp_now_add_peer(&peerInfo) != ESP_OK) { + Serial.printf("%s: ESPNOW BRIDGE: Failed to add broadcast peer\n", getLogDateTime()); + return; + } +} + +void ESPNowBridge::loop() { + // Nothing to do here - ESP-NOW is callback based +} + +void ESPNowBridge::xorCrypt(uint8_t *data, size_t len) { + size_t keyLen = strlen(_secret); + for (size_t i = 0; i < len; i++) { + data[i] ^= _secret[i % keyLen]; + } +} + +void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int len) { + // Ignore packets that are too small + if (len < 3) { +#if MESH_PACKET_LOGGING + Serial.printf("%s: ESPNOW BRIDGE: RX packet too small, len=%d\n", getLogDateTime(), len); +#endif + return; + } + + // Check packet header magic + if (data[0] != ESPNOW_HEADER_MAGIC) { +#if MESH_PACKET_LOGGING + Serial.printf("%s: ESPNOW BRIDGE: RX invalid magic 0x%02X\n", getLogDateTime(), data[0]); +#endif + return; + } + + // Make a copy we can decrypt + uint8_t decrypted[MAX_ESPNOW_PACKET_SIZE]; + memcpy(decrypted, data + 1, len - 1); // Skip magic byte + + // Try to decrypt + xorCrypt(decrypted, len - 1); + + // Validate checksum + uint16_t received_checksum = (decrypted[0] << 8) | decrypted[1]; + uint16_t calculated_checksum = fletcher16(decrypted + 2, len - 3); + + if (received_checksum != calculated_checksum) { + // Failed to decrypt - likely from a different network +#if MESH_PACKET_LOGGING + Serial.printf("%s: ESPNOW BRIDGE: RX checksum mismatch, rcv=0x%04X calc=0x%04X\n", getLogDateTime(), + received_checksum, calculated_checksum); +#endif + return; + } + +#if MESH_PACKET_LOGGING + Serial.printf("%s: ESPNOW BRIDGE: RX, len=%d\n", getLogDateTime(), len - 3); +#endif + + // Create mesh packet + mesh::Packet *pkt = _instance->_mgr->allocNew(); + if (!pkt) return; + + if (pkt->readFrom(decrypted + 2, len - 3)) { + _instance->onPacketReceived(pkt); + } else { + _instance->_mgr->free(pkt); + } +} + +void ESPNowBridge::onDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { + // Could add transmission error handling here if needed +} + +void ESPNowBridge::onPacketReceived(mesh::Packet *packet) { + if (!_seen_packets.hasSeen(packet)) { + _mgr->queueInbound(packet, 0); + } else { + _mgr->free(packet); + } +} + +void ESPNowBridge::onPacketTransmitted(mesh::Packet *packet) { + if (!_seen_packets.hasSeen(packet)) { + uint8_t buffer[MAX_ESPNOW_PACKET_SIZE]; + buffer[0] = ESPNOW_HEADER_MAGIC; + + // Write packet to buffer starting after magic byte and checksum + uint16_t len = packet->writeTo(buffer + 3); + + // Calculate and add checksum + uint16_t checksum = fletcher16(buffer + 3, len); + buffer[1] = (checksum >> 8) & 0xFF; + buffer[2] = checksum & 0xFF; + + // Encrypt payload (not including magic byte) + xorCrypt(buffer + 1, len + 2); + + // Broadcast using ESP-NOW + uint8_t broadcastAddress[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + esp_err_t result = esp_now_send(broadcastAddress, buffer, len + 3); + +#if MESH_PACKET_LOGGING + if (result == ESP_OK) { + Serial.printf("%s: ESPNOW BRIDGE: TX, len=%d\n", getLogDateTime(), len); + } else { + Serial.printf("%s: ESPNOW BRIDGE: TX FAILED!\n", getLogDateTime()); + } +#endif + } +} + +const char *ESPNowBridge::getLogDateTime() { + static char tmp[32]; + uint32_t now = _rtc->getCurrentTime(); + DateTime dt = DateTime(now); + sprintf(tmp, "%02d:%02d:%02d - %d/%d/%d U", dt.hour(), dt.minute(), dt.second(), dt.day(), dt.month(), + dt.year()); + return tmp; +} + +#endif diff --git a/src/helpers/bridges/ESPNowBridge.h b/src/helpers/bridges/ESPNowBridge.h new file mode 100644 index 00000000..7d2dbb0b --- /dev/null +++ b/src/helpers/bridges/ESPNowBridge.h @@ -0,0 +1,170 @@ +#pragma once + +#include "MeshCore.h" +#include "esp_now.h" +#include "helpers/AbstractBridge.h" +#include "helpers/SimpleMeshTables.h" + +#ifdef WITH_ESPNOW_BRIDGE + +#ifndef WITH_ESPNOW_BRIDGE_SECRET +#error WITH_ESPNOW_BRIDGE_SECRET must be defined to use ESPNowBridge +#endif + +/** + * @brief Bridge implementation using ESP-NOW protocol for packet transport + * + * This bridge enables mesh packet transport over ESP-NOW, a connectionless communication + * protocol provided by Espressif that allows ESP32 devices to communicate directly + * without WiFi router infrastructure. + * + * Features: + * - Broadcast-based communication (all bridges receive all packets) + * - Network isolation using XOR encryption with shared secret + * - Duplicate packet detection using SimpleMeshTables tracking + * - Maximum packet size of 250 bytes (ESP-NOW limitation) + * + * Packet Structure: + * [1 byte] Magic Header (0xAB) - Used to identify ESPNowBridge packets + * [2 bytes] Fletcher-16 checksum of encrypted payload (calculated over payload only) + * [n bytes] Encrypted payload containing the mesh packet + * + * The Fletcher-16 checksum is used to validate packet integrity and detect + * corrupted or tampered packets. It's calculated over the encrypted payload + * and provides a simple but effective way to verify packets are both + * uncorrupted and from the same network (since the checksum is calculated + * after encryption). + * + * Configuration: + * - Define WITH_ESPNOW_BRIDGE to enable this bridge + * - Define WITH_ESPNOW_BRIDGE_SECRET with a string to set the network encryption key + * + * Network Isolation: + * Multiple independent mesh networks can coexist by using different + * WITH_ESPNOW_BRIDGE_SECRET values. Packets encrypted with a different key will + * fail the checksum validation and be discarded. + */ +class ESPNowBridge : public AbstractBridge { +private: + static ESPNowBridge *_instance; + static void recv_cb(const uint8_t *mac, const uint8_t *data, int len); + static void send_cb(const uint8_t *mac, esp_now_send_status_t status); + + /** Packet manager for allocating and queuing mesh packets */ + mesh::PacketManager *_mgr; + + /** RTC clock for timestamping debug messages */ + mesh::RTCClock *_rtc; + + /** Tracks seen packets to prevent loops in broadcast communications */ + SimpleMeshTables _seen_packets; + + /** + * Maximum ESP-NOW packet size (250 bytes) + * This is a hardware limitation of ESP-NOW protocol: + * - ESP-NOW header: 20 bytes + * - Max payload: 250 bytes + * Source: ESP-NOW API documentation + */ + static const size_t MAX_ESPNOW_PACKET_SIZE = 250; + + /** + * Magic byte to identify ESPNowBridge packets (0xAB) + */ + static const uint8_t ESPNOW_HEADER_MAGIC = 0xAB; + + /** Buffer for receiving ESP-NOW packets */ + uint8_t _rx_buffer[MAX_ESPNOW_PACKET_SIZE]; + + /** Current position in receive buffer */ + size_t _rx_buffer_pos; + + /** + * Network encryption key from build define + * Must be defined with WITH_ESPNOW_BRIDGE_SECRET + * Used for XOR encryption to isolate different mesh networks + */ + const char *_secret = WITH_ESPNOW_BRIDGE_SECRET; + + /** + * Performs XOR encryption/decryption of data + * + * Uses WITH_ESPNOW_BRIDGE_SECRET as the key in a simple XOR operation. + * The same operation is used for both encryption and decryption. + * While not cryptographically secure, it provides basic network isolation. + * + * @param data Pointer to data to encrypt/decrypt + * @param len Length of data in bytes + */ + void xorCrypt(uint8_t *data, size_t len); + + /** + * ESP-NOW receive callback + * Called by ESP-NOW when a packet is received + * + * @param mac Source MAC address + * @param data Received data + * @param len Length of received data + */ + void onDataRecv(const uint8_t *mac, const uint8_t *data, int len); + + /** + * ESP-NOW send callback + * Called by ESP-NOW after a transmission attempt + * + * @param mac_addr Destination MAC address + * @param status Transmission status + */ + void onDataSent(const uint8_t *mac_addr, esp_now_send_status_t status); + +public: + /** + * Constructs an ESPNowBridge instance + * + * @param mgr PacketManager for allocating and queuing packets + * @param rtc RTCClock for timestamping debug messages + */ + ESPNowBridge(mesh::PacketManager *mgr, mesh::RTCClock *rtc); + + /** + * Initializes the ESP-NOW bridge + * + * - Configures WiFi in station mode + * - Initializes ESP-NOW protocol + * - Registers callbacks + * - Sets up broadcast peer + */ + void begin() override; + + /** + * Main loop handler + * ESP-NOW is callback-based, so this is currently empty + */ + void loop() override; + + /** + * Called when a packet is received via ESP-NOW + * Queues the packet for mesh processing if not seen before + * + * @param packet The received mesh packet + */ + void onPacketReceived(mesh::Packet *packet) override; + + /** + * Called when a packet needs to be transmitted via ESP-NOW + * Encrypts and broadcasts the packet if not seen before + * + * @param packet The mesh packet to transmit + */ + void onPacketTransmitted(mesh::Packet *packet) override; + + /** + * Gets formatted date/time string for logging + * Format: "HH:MM:SS - DD/MM/YYYY U" + * + * @return Formatted date/time string + */ + const char *getLogDateTime(); +}; + +#endif diff --git a/src/helpers/bridges/RS232Bridge.cpp b/src/helpers/bridges/RS232Bridge.cpp index 801bcc5c..5c3b8caa 100644 --- a/src/helpers/bridges/RS232Bridge.cpp +++ b/src/helpers/bridges/RS232Bridge.cpp @@ -4,8 +4,9 @@ #ifdef WITH_RS232_BRIDGE -// Fletcher-16 -// https://en.wikipedia.org/wiki/Fletcher%27s_checksum +// Static Fletcher-16 checksum calculation +// Based on: https://en.wikipedia.org/wiki/Fletcher%27s_checksum +// Used to verify data integrity of received packets inline static uint16_t fletcher16(const uint8_t *bytes, const size_t len) { uint8_t sum1 = 0, sum2 = 0; diff --git a/src/helpers/bridges/RS232Bridge.h b/src/helpers/bridges/RS232Bridge.h index 0e99040f..2adeb503 100644 --- a/src/helpers/bridges/RS232Bridge.h +++ b/src/helpers/bridges/RS232Bridge.h @@ -7,28 +7,87 @@ #ifdef WITH_RS232_BRIDGE /** - * @brief A bridge implementation that uses a serial port to connect two mesh networks. + * @brief Bridge implementation using RS232/UART protocol for packet transport + * + * This bridge enables mesh packet transport over serial/UART connections, + * allowing nodes to communicate over wired serial links. It implements a simple + * packet framing protocol with checksums for reliable transfer. + * + * Features: + * - Point-to-point communication over hardware UART + * - Fletcher-16 checksum for data integrity verification + * - Magic header for packet synchronization + * - Configurable RX/TX pins via build defines + * - Baud rate fixed at 115200 + * + * Packet Structure: + * [2 bytes] Magic Header (0xCAFE) - Used to identify start of packet + * [2 bytes] Fletcher-16 checksum - Data integrity check + * [1 byte] Payload length + * [n bytes] Packet payload + * + * The Fletcher-16 checksum is used to validate packet integrity and detect + * corrupted or malformed packets. It provides error detection capabilities + * suitable for serial communication where noise or timing issues could + * corrupt data. + * + * Configuration: + * - Define WITH_RS232_BRIDGE to enable this bridge + * - Define WITH_RS232_BRIDGE_RX with the RX pin number + * - Define WITH_RS232_BRIDGE_TX with the TX pin number + * + * Platform Support: + * - ESP32 targets + * - NRF52 targets + * - RP2040 targets + * - STM32 targets */ class RS232Bridge : public AbstractBridge { public: /** - * @brief Construct a new Serial Bridge object - * - * @param serial The serial port to use for the bridge. - * @param mgr A pointer to the packet manager. - * @param rtc A pointer to the RTC clock. + * @brief Constructs an RS232Bridge instance + * + * @param serial The hardware serial port to use + * @param mgr PacketManager for allocating and queuing packets + * @param rtc RTCClock for timestamping debug messages */ RS232Bridge(Stream& serial, mesh::PacketManager* mgr, mesh::RTCClock* rtc); + + /** + * Initializes the RS232 bridge + * + * - Configures UART pins based on platform + * - Sets baud rate to 115200 + */ void begin() override; + + /** + * @brief Main loop handler + * Processes incoming serial data and builds packets + */ void loop() override; + + /** + * @brief Called when a packet needs to be transmitted over serial + * Formats and sends the packet with proper framing + * + * @param packet The mesh packet to transmit + */ void onPacketTransmitted(mesh::Packet* packet) override; + + /** + * @brief Called when a complete packet has been received from serial + * Queues the packet for mesh processing if checksum is valid + * + * @param packet The received mesh packet + */ void onPacketReceived(mesh::Packet* packet) override; private: + /** Helper function to get formatted timestamp for logging */ const char* getLogDateTime(); - /** - * @brief The 2-byte magic word used to signify the start of a packet. - */ + + /** Magic number to identify start of RS232 packets (0xCAFE) */ static constexpr uint16_t SERIAL_PKT_MAGIC = 0xCAFE; /** diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 313b9844..5e9874c9 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -64,29 +64,6 @@ lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} ${esp32_ota.lib_deps} -[env:LilyGo_TLora_V2_1_1_6_repeater_bridge] -extends = LilyGo_TLora_V2_1_1_6 -build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} - + - + - +<../examples/simple_repeater> -build_flags = - ${LilyGo_TLora_V2_1_1_6.build_flags} - -D ADVERT_NAME='"TLora-V2.1-1.6 Bridge"' - -D ADVERT_LAT=0.0 - -D ADVERT_LON=0.0 - -D ADMIN_PASSWORD='"password"' - -D MAX_NEIGHBOURS=8 - -D WITH_RS232_BRIDGE=Serial2 - -D WITH_RS232_BRIDGE_RX=34 - -D WITH_RS232_BRIDGE_TX=25 - -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 -; -D CORE_DEBUG_LEVEL=3 -lib_deps = - ${LilyGo_TLora_V2_1_1_6.lib_deps} - ${esp32_ota.lib_deps} - [env:LilyGo_TLora_V2_1_1_6_terminal_chat] extends = LilyGo_TLora_V2_1_1_6 build_flags = @@ -179,3 +156,51 @@ build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} densaugeo/base64 @ ~1.4.0 + +; +; Repeater Bridges +; +[env:LilyGo_TLora_V2_1_1_6_bridge_rs232] +extends = LilyGo_TLora_V2_1_1_6 +build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + + + +<../examples/simple_repeater> +build_flags = + ${LilyGo_TLora_V2_1_1_6.build_flags} + -D ADVERT_NAME='"RS232 Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_RS232_BRIDGE=Serial2 + -D WITH_RS232_BRIDGE_RX=34 + -D WITH_RS232_BRIDGE_TX=25 + -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +; -D CORE_DEBUG_LEVEL=3 +lib_deps = + ${LilyGo_TLora_V2_1_1_6.lib_deps} + ${esp32_ota.lib_deps} + +[env:LilyGo_TLora_V2_1_1_6_bridge_espnow] +extends = LilyGo_TLora_V2_1_1_6 +build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + + + +<../examples/simple_repeater> +build_flags = + ${LilyGo_TLora_V2_1_1_6.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' + -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +; -D CORE_DEBUG_LEVEL=3 +lib_deps = + ${LilyGo_TLora_V2_1_1_6.lib_deps} + ${esp32_ota.lib_deps} \ No newline at end of file From 04e70829a454703638e3740dc017cbf995d9f23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Sun, 7 Sep 2025 21:46:51 +0100 Subject: [PATCH 16/21] Rename RS232 bridge environments --- variants/heltec_v3/platformio.ini | 55 +++++++++++++++++-- variants/lilygo_tlora_v2_1/platformio.ini | 4 +- variants/waveshare_rp2040_lora/platformio.ini | 6 +- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 4ffb11ba..c7ab86d8 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -49,12 +49,12 @@ lib_deps = ${esp32_ota.lib_deps} bakercp/CRC32 @ ^2.0.0 -[env:Heltec_v3_repeater_bridge] +[env:Heltec_v3_repeater_bridge_rs232] extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} -D DISPLAY_CLASS=SSD1306Display - -D ADVERT_NAME='"Heltec Bridge"' + -D ADVERT_NAME='"RS232 Bridge"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' @@ -62,11 +62,33 @@ build_flags = -D WITH_RS232_BRIDGE=Serial2 -D WITH_RS232_BRIDGE_RX=5 -D WITH_RS232_BRIDGE_TX=6 - -D MESH_PACKET_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} - + + + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + ${esp32_ota.lib_deps} + +[env:Heltec_v3_repeater_bridge_espnow] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + + +<../examples/simple_repeater> lib_deps = ${Heltec_lora32_v3.lib_deps} @@ -209,11 +231,11 @@ lib_deps = ${esp32_ota.lib_deps} bakercp/CRC32 @ ^2.0.0 -[env:Heltec_WSL3_repeater_bridge] +[env:Heltec_WSL3_repeater_bridge_rs232] extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} - -D ADVERT_NAME='"Heltec WSL3 Bridge"' + -D ADVERT_NAME='"RS232 Bridge"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' @@ -231,6 +253,27 @@ lib_deps = ${esp32_ota.lib_deps} bakercp/CRC32 @ ^2.0.0 +[env:Heltec_WSL3_repeater_bridge_espnow] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 + [env:Heltec_WSL3_room_server] extends = Heltec_lora32_v3 build_src_filter = ${Heltec_lora32_v3.build_src_filter} diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 5e9874c9..aa957fba 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -176,7 +176,7 @@ build_flags = -D WITH_RS232_BRIDGE=Serial2 -D WITH_RS232_BRIDGE_RX=34 -D WITH_RS232_BRIDGE_TX=25 - -D MESH_PACKET_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 ; -D CORE_DEBUG_LEVEL=3 lib_deps = @@ -198,7 +198,7 @@ build_flags = -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' - -D MESH_PACKET_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 ; -D CORE_DEBUG_LEVEL=3 lib_deps = diff --git a/variants/waveshare_rp2040_lora/platformio.ini b/variants/waveshare_rp2040_lora/platformio.ini index 8824ddbd..0f333069 100644 --- a/variants/waveshare_rp2040_lora/platformio.ini +++ b/variants/waveshare_rp2040_lora/platformio.ini @@ -40,10 +40,10 @@ build_flags = ${waveshare_rp2040_lora.build_flags} build_src_filter = ${waveshare_rp2040_lora.build_src_filter} +<../examples/simple_repeater> -[env:waveshare_rp2040_lora_repeater_bridge] +[env:waveshare_rp2040_lora_repeater_bridge_rs232] extends = waveshare_rp2040_lora build_flags = ${waveshare_rp2040_lora.build_flags} - -D ADVERT_NAME='"RP2040-LoRa Bridge"' + -D ADVERT_NAME='"RS232 Bridge"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' @@ -51,7 +51,7 @@ build_flags = ${waveshare_rp2040_lora.build_flags} -D WITH_RS232_BRIDGE=Serial2 -D WITH_RS232_BRIDGE_RX=9 -D WITH_RS232_BRIDGE_TX=8 - -D MESH_PACKET_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${waveshare_rp2040_lora.build_src_filter} + From 537449e6af5895bd9aa107ddd416f1b3fbb216d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Sun, 7 Sep 2025 22:00:06 +0100 Subject: [PATCH 17/21] Refactor ESPNowBridge packet handling to use 2-byte magic header and improve packet size validation --- src/helpers/bridges/ESPNowBridge.cpp | 88 ++++++++++++++++++++-------- src/helpers/bridges/ESPNowBridge.h | 33 +++++++---- src/helpers/bridges/RS232Bridge.h | 6 +- 3 files changed, 89 insertions(+), 38 deletions(-) diff --git a/src/helpers/bridges/ESPNowBridge.cpp b/src/helpers/bridges/ESPNowBridge.cpp index a470d521..aed63a6b 100644 --- a/src/helpers/bridges/ESPNowBridge.cpp +++ b/src/helpers/bridges/ESPNowBridge.cpp @@ -10,7 +10,7 @@ ESPNowBridge *ESPNowBridge::_instance = nullptr; // Static callback wrappers -void ESPNowBridge::recv_cb(const uint8_t *mac, const uint8_t *data, int len) { +void ESPNowBridge::recv_cb(const uint8_t *mac, const uint8_t *data, int32_t len) { if (_instance) { _instance->onDataRecv(mac, data, len); } @@ -78,33 +78,44 @@ void ESPNowBridge::xorCrypt(uint8_t *data, size_t len) { } } -void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int len) { - // Ignore packets that are too small - if (len < 3) { +void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t len) { + // Ignore packets that are too small to contain header + checksum + if (len < (MAGIC_HEADER_SIZE + CHECKSUM_SIZE)) { #if MESH_PACKET_LOGGING Serial.printf("%s: ESPNOW BRIDGE: RX packet too small, len=%d\n", getLogDateTime(), len); #endif return; } - // Check packet header magic - if (data[0] != ESPNOW_HEADER_MAGIC) { + // Validate total packet size + if (len > MAX_ESPNOW_PACKET_SIZE) { #if MESH_PACKET_LOGGING - Serial.printf("%s: ESPNOW BRIDGE: RX invalid magic 0x%02X\n", getLogDateTime(), data[0]); + Serial.printf("%s: ESPNOW BRIDGE: RX packet too large, len=%d\n", getLogDateTime(), len); +#endif + return; + } + + // Check packet header magic + uint16_t received_magic = (data[0] << 8) | data[1]; + if (received_magic != ESPNOW_HEADER_MAGIC) { +#if MESH_PACKET_LOGGING + Serial.printf("%s: ESPNOW BRIDGE: RX invalid magic 0x%04X\n", getLogDateTime(), received_magic); #endif return; } // Make a copy we can decrypt uint8_t decrypted[MAX_ESPNOW_PACKET_SIZE]; - memcpy(decrypted, data + 1, len - 1); // Skip magic byte + const size_t encryptedDataLen = len - MAGIC_HEADER_SIZE; + memcpy(decrypted, data + MAGIC_HEADER_SIZE, encryptedDataLen); - // Try to decrypt - xorCrypt(decrypted, len - 1); + // Try to decrypt (checksum + payload) + xorCrypt(decrypted, encryptedDataLen); // Validate checksum uint16_t received_checksum = (decrypted[0] << 8) | decrypted[1]; - uint16_t calculated_checksum = fletcher16(decrypted + 2, len - 3); + const size_t payloadLen = encryptedDataLen - CHECKSUM_SIZE; + uint16_t calculated_checksum = fletcher16(decrypted + CHECKSUM_SIZE, payloadLen); if (received_checksum != calculated_checksum) { // Failed to decrypt - likely from a different network @@ -116,14 +127,14 @@ void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int len) } #if MESH_PACKET_LOGGING - Serial.printf("%s: ESPNOW BRIDGE: RX, len=%d\n", getLogDateTime(), len - 3); + Serial.printf("%s: ESPNOW BRIDGE: RX, payload_len=%d\n", getLogDateTime(), payloadLen); #endif // Create mesh packet mesh::Packet *pkt = _instance->_mgr->allocNew(); if (!pkt) return; - if (pkt->readFrom(decrypted + 2, len - 3)) { + if (pkt->readFrom(decrypted + CHECKSUM_SIZE, payloadLen)) { _instance->onPacketReceived(pkt); } else { _instance->_mgr->free(pkt); @@ -144,27 +155,56 @@ void ESPNowBridge::onPacketReceived(mesh::Packet *packet) { void ESPNowBridge::onPacketTransmitted(mesh::Packet *packet) { if (!_seen_packets.hasSeen(packet)) { + + // First validate the packet pointer + if (!packet) { +#if MESH_PACKET_LOGGING + Serial.printf("%s: ESPNOW BRIDGE: TX invalid packet pointer\n", getLogDateTime()); +#endif + return; + } + + // Create a temporary buffer just for size calculation and reuse for actual writing + uint8_t sizingBuffer[MAX_PAYLOAD_SIZE]; + uint16_t meshPacketLen = packet->writeTo(sizingBuffer); + + // Check if packet fits within our maximum payload size + if (meshPacketLen > MAX_PAYLOAD_SIZE) { +#if MESH_PACKET_LOGGING + Serial.printf("%s: ESPNOW BRIDGE: TX packet too large (payload=%d, max=%d)\n", getLogDateTime(), + meshPacketLen, MAX_PAYLOAD_SIZE); +#endif + return; + } + uint8_t buffer[MAX_ESPNOW_PACKET_SIZE]; - buffer[0] = ESPNOW_HEADER_MAGIC; - // Write packet to buffer starting after magic byte and checksum - uint16_t len = packet->writeTo(buffer + 3); + // Write magic header (2 bytes) + buffer[0] = (ESPNOW_HEADER_MAGIC >> 8) & 0xFF; + buffer[1] = ESPNOW_HEADER_MAGIC & 0xFF; - // Calculate and add checksum - uint16_t checksum = fletcher16(buffer + 3, len); - buffer[1] = (checksum >> 8) & 0xFF; - buffer[2] = checksum & 0xFF; + // Write packet payload starting after magic header and checksum + const size_t packetOffset = MAGIC_HEADER_SIZE + CHECKSUM_SIZE; + memcpy(buffer + packetOffset, sizingBuffer, meshPacketLen); - // Encrypt payload (not including magic byte) - xorCrypt(buffer + 1, len + 2); + // Calculate and add checksum (only of the payload) + uint16_t checksum = fletcher16(buffer + packetOffset, meshPacketLen); + buffer[2] = (checksum >> 8) & 0xFF; // High byte + buffer[3] = checksum & 0xFF; // Low byte + + // Encrypt payload and checksum (not including magic header) + xorCrypt(buffer + MAGIC_HEADER_SIZE, meshPacketLen + CHECKSUM_SIZE); + + // Total packet size: magic header + checksum + payload + const size_t totalPacketSize = MAGIC_HEADER_SIZE + CHECKSUM_SIZE + meshPacketLen; // Broadcast using ESP-NOW uint8_t broadcastAddress[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - esp_err_t result = esp_now_send(broadcastAddress, buffer, len + 3); + esp_err_t result = esp_now_send(broadcastAddress, buffer, totalPacketSize); #if MESH_PACKET_LOGGING if (result == ESP_OK) { - Serial.printf("%s: ESPNOW BRIDGE: TX, len=%d\n", getLogDateTime(), len); + Serial.printf("%s: ESPNOW BRIDGE: TX, len=%d\n", getLogDateTime(), meshPacketLen); } else { Serial.printf("%s: ESPNOW BRIDGE: TX FAILED!\n", getLogDateTime()); } diff --git a/src/helpers/bridges/ESPNowBridge.h b/src/helpers/bridges/ESPNowBridge.h index 7d2dbb0b..5c771471 100644 --- a/src/helpers/bridges/ESPNowBridge.h +++ b/src/helpers/bridges/ESPNowBridge.h @@ -25,9 +25,9 @@ * - Maximum packet size of 250 bytes (ESP-NOW limitation) * * Packet Structure: - * [1 byte] Magic Header (0xAB) - Used to identify ESPNowBridge packets + * [2 bytes] Magic Header - Used to identify ESPNowBridge packets * [2 bytes] Fletcher-16 checksum of encrypted payload (calculated over payload only) - * [n bytes] Encrypted payload containing the mesh packet + * [246 bytes max] Encrypted payload containing the mesh packet * * The Fletcher-16 checksum is used to validate packet integrity and detect * corrupted or tampered packets. It's calculated over the encrypted payload @@ -47,7 +47,7 @@ class ESPNowBridge : public AbstractBridge { private: static ESPNowBridge *_instance; - static void recv_cb(const uint8_t *mac, const uint8_t *data, int len); + static void recv_cb(const uint8_t *mac, const uint8_t *data, int32_t len); static void send_cb(const uint8_t *mac, esp_now_send_status_t status); /** Packet manager for allocating and queuing mesh packets */ @@ -60,18 +60,29 @@ private: SimpleMeshTables _seen_packets; /** - * Maximum ESP-NOW packet size (250 bytes) - * This is a hardware limitation of ESP-NOW protocol: - * - ESP-NOW header: 20 bytes - * - Max payload: 250 bytes - * Source: ESP-NOW API documentation + * ESP-NOW Protocol Structure: + * - ESP-NOW header: 20 bytes (handled by ESP-NOW protocol) + * - ESP-NOW payload: 250 bytes maximum + * Total ESP-NOW packet: 270 bytes + * + * Our Bridge Packet Structure (must fit in ESP-NOW payload): + * - Magic header: 2 bytes + * - Checksum: 2 bytes + * - Available payload: 246 bytes */ static const size_t MAX_ESPNOW_PACKET_SIZE = 250; /** - * Magic byte to identify ESPNowBridge packets (0xAB) + * Size constants for packet parsing */ - static const uint8_t ESPNOW_HEADER_MAGIC = 0xAB; + static const size_t MAGIC_HEADER_SIZE = 2; + static const size_t CHECKSUM_SIZE = 2; + static const size_t MAX_PAYLOAD_SIZE = MAX_ESPNOW_PACKET_SIZE - (MAGIC_HEADER_SIZE + CHECKSUM_SIZE); + + /** + * Magic bytes to identify ESPNowBridge packets + */ + static const uint16_t ESPNOW_HEADER_MAGIC = 0xC03E; /** Buffer for receiving ESP-NOW packets */ uint8_t _rx_buffer[MAX_ESPNOW_PACKET_SIZE]; @@ -106,7 +117,7 @@ private: * @param data Received data * @param len Length of received data */ - void onDataRecv(const uint8_t *mac, const uint8_t *data, int len); + void onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t len); /** * ESP-NOW send callback diff --git a/src/helpers/bridges/RS232Bridge.h b/src/helpers/bridges/RS232Bridge.h index 2adeb503..49c781cb 100644 --- a/src/helpers/bridges/RS232Bridge.h +++ b/src/helpers/bridges/RS232Bridge.h @@ -21,7 +21,7 @@ * - Baud rate fixed at 115200 * * Packet Structure: - * [2 bytes] Magic Header (0xCAFE) - Used to identify start of packet + * [2 bytes] Magic Header - Used to identify start of packet * [2 bytes] Fletcher-16 checksum - Data integrity check * [1 byte] Payload length * [n bytes] Packet payload @@ -87,8 +87,8 @@ private: /** Helper function to get formatted timestamp for logging */ const char* getLogDateTime(); - /** Magic number to identify start of RS232 packets (0xCAFE) */ - static constexpr uint16_t SERIAL_PKT_MAGIC = 0xCAFE; + /** Magic number to identify start of RS232 packets */ + static constexpr uint16_t SERIAL_PKT_MAGIC = 0xC03E; /** * @brief The total overhead of the serial protocol in bytes. From 0051ccef2603bcd207217036930654d7b660ccb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Mon, 8 Sep 2025 02:03:08 +0100 Subject: [PATCH 18/21] Refactor bridge implementations to inherit from BridgeBase --- platformio.ini | 1 + src/helpers/bridges/BridgeBase.cpp | 34 +++++++ src/helpers/bridges/BridgeBase.h | 84 +++++++++++++++++ src/helpers/bridges/ESPNowBridge.cpp | 52 +++-------- src/helpers/bridges/ESPNowBridge.h | 22 +---- src/helpers/bridges/RS232Bridge.cpp | 115 ++++++++++++++---------- src/helpers/bridges/RS232Bridge.h | 129 +++++++++++++++++---------- 7 files changed, 282 insertions(+), 155 deletions(-) create mode 100644 src/helpers/bridges/BridgeBase.cpp create mode 100644 src/helpers/bridges/BridgeBase.h diff --git a/platformio.ini b/platformio.ini index 1c89465f..d21f513e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -47,6 +47,7 @@ build_src_filter = +<*.cpp> + + + + + ; ----------------- ESP32 --------------------- diff --git a/src/helpers/bridges/BridgeBase.cpp b/src/helpers/bridges/BridgeBase.cpp new file mode 100644 index 00000000..20551190 --- /dev/null +++ b/src/helpers/bridges/BridgeBase.cpp @@ -0,0 +1,34 @@ +#include "BridgeBase.h" + +const char *BridgeBase::getLogDateTime() { + static char tmp[32]; + uint32_t now = _rtc->getCurrentTime(); + DateTime dt = DateTime(now); + sprintf(tmp, "%02d:%02d:%02d - %d/%d/%d U", dt.hour(), dt.minute(), dt.second(), dt.day(), dt.month(), + dt.year()); + return tmp; +} + +uint16_t BridgeBase::fletcher16(const uint8_t *data, size_t len) { + uint8_t sum1 = 0, sum2 = 0; + + for (size_t i = 0; i < len; i++) { + sum1 = (sum1 + data[i]) % 255; + sum2 = (sum2 + sum1) % 255; + } + + return (sum2 << 8) | sum1; +} + +bool BridgeBase::validateChecksum(const uint8_t *data, size_t len, uint16_t received_checksum) { + uint16_t calculated_checksum = fletcher16(data, len); + return received_checksum == calculated_checksum; +} + +void BridgeBase::handleReceivedPacket(mesh::Packet *packet) { + if (!_seen_packets.hasSeen(packet)) { + _mgr->queueInbound(packet, 0); + } else { + _mgr->free(packet); + } +} diff --git a/src/helpers/bridges/BridgeBase.h b/src/helpers/bridges/BridgeBase.h new file mode 100644 index 00000000..2aff2306 --- /dev/null +++ b/src/helpers/bridges/BridgeBase.h @@ -0,0 +1,84 @@ +#pragma once + +#include "helpers/AbstractBridge.h" +#include "helpers/SimpleMeshTables.h" + +#include + +/** + * @brief Base class implementing common bridge functionality + * + * This class provides common functionality used by different bridge implementations + * like packet tracking, checksum calculation, timestamping, and duplicate detection. + * + * Features: + * - Fletcher-16 checksum calculation for data integrity + * - Packet duplicate detection using SimpleMeshTables + * - Common timestamp formatting for debug logging + * - Shared packet management and queuing logic + */ +class BridgeBase : public AbstractBridge { +public: + virtual ~BridgeBase() = default; + +protected: + /** Packet manager for allocating and queuing mesh packets */ + mesh::PacketManager *_mgr; + + /** RTC clock for timestamping debug messages */ + mesh::RTCClock *_rtc; + + /** Tracks seen packets to prevent loops in broadcast communications */ + SimpleMeshTables _seen_packets; + + /** + * @brief Constructs a BridgeBase instance + * + * @param mgr PacketManager for allocating and queuing packets + * @param rtc RTCClock for timestamping debug messages + */ + BridgeBase(mesh::PacketManager *mgr, mesh::RTCClock *rtc) : _mgr(mgr), _rtc(rtc) {} + + /** + * @brief Gets formatted date/time string for logging + * + * Format: "HH:MM:SS - DD/MM/YYYY U" + * + * @return Formatted date/time string + */ + const char *getLogDateTime(); + + /** + * @brief Calculate Fletcher-16 checksum + * + * Based on: https://en.wikipedia.org/wiki/Fletcher%27s_checksum + * Used to verify data integrity of received packets + * + * @param data Pointer to data to calculate checksum for + * @param len Length of data in bytes + * @return Calculated Fletcher-16 checksum + */ + static uint16_t fletcher16(const uint8_t *data, size_t len); + + /** + * @brief Validate received checksum against calculated checksum + * + * @param data Pointer to data to validate + * @param len Length of data in bytes + * @param received_checksum Checksum received with data + * @return true if checksum is valid, false otherwise + */ + bool validateChecksum(const uint8_t *data, size_t len, uint16_t received_checksum); + + /** + * @brief Common packet handling for received packets + * + * Implements the standard pattern used by all bridges: + * - Check if packet was seen before using _seen_packets.hasSeen() + * - Queue packet for mesh processing if not seen before + * - Free packet if already seen to prevent duplicates + * + * @param packet The received mesh packet + */ + void handleReceivedPacket(mesh::Packet *packet); +}; diff --git a/src/helpers/bridges/ESPNowBridge.cpp b/src/helpers/bridges/ESPNowBridge.cpp index aed63a6b..baf683ca 100644 --- a/src/helpers/bridges/ESPNowBridge.cpp +++ b/src/helpers/bridges/ESPNowBridge.cpp @@ -1,6 +1,5 @@ #include "ESPNowBridge.h" -#include #include #include @@ -22,21 +21,8 @@ void ESPNowBridge::send_cb(const uint8_t *mac, esp_now_send_status_t status) { } } -// Fletcher16 checksum calculation -static uint16_t fletcher16(const uint8_t *data, size_t len) { - uint16_t sum1 = 0; - uint16_t sum2 = 0; - - while (len--) { - sum1 = (sum1 + *data++) % 255; - sum2 = (sum2 + sum1) % 255; - } - - return (sum2 << 8) | sum1; -} - ESPNowBridge::ESPNowBridge(mesh::PacketManager *mgr, mesh::RTCClock *rtc) - : _mgr(mgr), _rtc(rtc), _rx_buffer_pos(0) { + : BridgeBase(mgr, rtc), _rx_buffer_pos(0) { _instance = this; } @@ -115,13 +101,12 @@ void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t l // Validate checksum uint16_t received_checksum = (decrypted[0] << 8) | decrypted[1]; const size_t payloadLen = encryptedDataLen - CHECKSUM_SIZE; - uint16_t calculated_checksum = fletcher16(decrypted + CHECKSUM_SIZE, payloadLen); - if (received_checksum != calculated_checksum) { + if (!validateChecksum(decrypted + CHECKSUM_SIZE, payloadLen, received_checksum)) { // Failed to decrypt - likely from a different network #if MESH_PACKET_LOGGING - Serial.printf("%s: ESPNOW BRIDGE: RX checksum mismatch, rcv=0x%04X calc=0x%04X\n", getLogDateTime(), - received_checksum, calculated_checksum); + Serial.printf("%s: ESPNOW BRIDGE: RX checksum mismatch, rcv=0x%04X\n", getLogDateTime(), + received_checksum); #endif return; } @@ -146,23 +131,19 @@ void ESPNowBridge::onDataSent(const uint8_t *mac_addr, esp_now_send_status_t sta } void ESPNowBridge::onPacketReceived(mesh::Packet *packet) { - if (!_seen_packets.hasSeen(packet)) { - _mgr->queueInbound(packet, 0); - } else { - _mgr->free(packet); - } + handleReceivedPacket(packet); } void ESPNowBridge::onPacketTransmitted(mesh::Packet *packet) { - if (!_seen_packets.hasSeen(packet)) { - - // First validate the packet pointer - if (!packet) { + // First validate the packet pointer + if (!packet) { #if MESH_PACKET_LOGGING - Serial.printf("%s: ESPNOW BRIDGE: TX invalid packet pointer\n", getLogDateTime()); + Serial.printf("%s: ESPNOW BRIDGE: TX invalid packet pointer\n", getLogDateTime()); #endif - return; - } + return; + } + + if (!_seen_packets.hasSeen(packet)) { // Create a temporary buffer just for size calculation and reuse for actual writing uint8_t sizingBuffer[MAX_PAYLOAD_SIZE]; @@ -212,13 +193,4 @@ void ESPNowBridge::onPacketTransmitted(mesh::Packet *packet) { } } -const char *ESPNowBridge::getLogDateTime() { - static char tmp[32]; - uint32_t now = _rtc->getCurrentTime(); - DateTime dt = DateTime(now); - sprintf(tmp, "%02d:%02d:%02d - %d/%d/%d U", dt.hour(), dt.minute(), dt.second(), dt.day(), dt.month(), - dt.year()); - return tmp; -} - #endif diff --git a/src/helpers/bridges/ESPNowBridge.h b/src/helpers/bridges/ESPNowBridge.h index 5c771471..d9962c72 100644 --- a/src/helpers/bridges/ESPNowBridge.h +++ b/src/helpers/bridges/ESPNowBridge.h @@ -2,8 +2,7 @@ #include "MeshCore.h" #include "esp_now.h" -#include "helpers/AbstractBridge.h" -#include "helpers/SimpleMeshTables.h" +#include "helpers/bridges/BridgeBase.h" #ifdef WITH_ESPNOW_BRIDGE @@ -44,21 +43,12 @@ * WITH_ESPNOW_BRIDGE_SECRET values. Packets encrypted with a different key will * fail the checksum validation and be discarded. */ -class ESPNowBridge : public AbstractBridge { +class ESPNowBridge : public BridgeBase { private: static ESPNowBridge *_instance; static void recv_cb(const uint8_t *mac, const uint8_t *data, int32_t len); static void send_cb(const uint8_t *mac, esp_now_send_status_t status); - /** Packet manager for allocating and queuing mesh packets */ - mesh::PacketManager *_mgr; - - /** RTC clock for timestamping debug messages */ - mesh::RTCClock *_rtc; - - /** Tracks seen packets to prevent loops in broadcast communications */ - SimpleMeshTables _seen_packets; - /** * ESP-NOW Protocol Structure: * - ESP-NOW header: 20 bytes (handled by ESP-NOW protocol) @@ -168,14 +158,6 @@ public: * @param packet The mesh packet to transmit */ void onPacketTransmitted(mesh::Packet *packet) override; - - /** - * Gets formatted date/time string for logging - * Format: "HH:MM:SS - DD/MM/YYYY U" - * - * @return Formatted date/time string - */ - const char *getLogDateTime(); }; #endif diff --git a/src/helpers/bridges/RS232Bridge.cpp b/src/helpers/bridges/RS232Bridge.cpp index 5c3b8caa..39a0968a 100644 --- a/src/helpers/bridges/RS232Bridge.cpp +++ b/src/helpers/bridges/RS232Bridge.cpp @@ -1,32 +1,11 @@ #include "RS232Bridge.h" + #include -#include #ifdef WITH_RS232_BRIDGE -// Static Fletcher-16 checksum calculation -// Based on: https://en.wikipedia.org/wiki/Fletcher%27s_checksum -// Used to verify data integrity of received packets -inline static uint16_t fletcher16(const uint8_t *bytes, const size_t len) { - uint8_t sum1 = 0, sum2 = 0; - - for (size_t i = 0; i < len; i++) { - sum1 = (sum1 + bytes[i]) % 255; - sum2 = (sum2 + sum1) % 255; - } - - return (sum2 << 8) | sum1; -}; - -const char* RS232Bridge::getLogDateTime() { - static char tmp[32]; - uint32_t now = _rtc->getCurrentTime(); - DateTime dt = DateTime(now); - sprintf(tmp, "%02d:%02d:%02d - %d/%d/%d U", dt.hour(), dt.minute(), dt.second(), dt.day(), dt.month(), dt.year()); - return tmp; -} - -RS232Bridge::RS232Bridge(Stream& serial, mesh::PacketManager* mgr, mesh::RTCClock* rtc) : _serial(&serial), _mgr(mgr), _rtc(rtc) {} +RS232Bridge::RS232Bridge(Stream &serial, mesh::PacketManager *mgr, mesh::RTCClock *rtc) + : BridgeBase(mgr, rtc), _serial(&serial) {} void RS232Bridge::begin() { #if !defined(WITH_RS232_BRIDGE_RX) || !defined(WITH_RS232_BRIDGE_TX) @@ -46,27 +25,48 @@ void RS232Bridge::begin() { #else #error RS232Bridge was not tested on the current platform #endif - ((HardwareSerial*)_serial)->begin(115200); + ((HardwareSerial *)_serial)->begin(115200); } -void RS232Bridge::onPacketTransmitted(mesh::Packet* packet) { +void RS232Bridge::onPacketTransmitted(mesh::Packet *packet) { + // First validate the packet pointer + if (!packet) { +#if MESH_PACKET_LOGGING + Serial.printf("%s: RS232 BRIDGE: TX invalid packet pointer\n", getLogDateTime()); +#endif + return; + } + if (!_seen_packets.hasSeen(packet)) { + uint8_t buffer[MAX_SERIAL_PACKET_SIZE]; uint16_t len = packet->writeTo(buffer + 4); - buffer[0] = (SERIAL_PKT_MAGIC >> 8) & 0xFF; - buffer[1] = SERIAL_PKT_MAGIC & 0xFF; - buffer[2] = (len >> 8) & 0xFF; - buffer[3] = len & 0xFF; + // Check if packet fits within our maximum payload size + if (len > (MAX_TRANS_UNIT + 1)) { +#if MESH_PACKET_LOGGING + Serial.printf("%s: RS232 BRIDGE: TX packet too large (payload=%d, max=%d)\n", getLogDateTime(), len, + MAX_TRANS_UNIT + 1); +#endif + return; + } + // Build packet header + buffer[0] = (SERIAL_PKT_MAGIC >> 8) & 0xFF; // Magic high byte + buffer[1] = SERIAL_PKT_MAGIC & 0xFF; // Magic low byte + buffer[2] = (len >> 8) & 0xFF; // Length high byte + buffer[3] = len & 0xFF; // Length low byte + + // Calculate checksum over the payload uint16_t checksum = fletcher16(buffer + 4, len); - buffer[4 + len] = (checksum >> 8) & 0xFF; - buffer[5 + len] = checksum & 0xFF; + buffer[4 + len] = (checksum >> 8) & 0xFF; // Checksum high byte + buffer[5 + len] = checksum & 0xFF; // Checksum low byte + // Send complete packet _serial->write(buffer, len + SERIAL_OVERHEAD); #if MESH_PACKET_LOGGING - Serial.printf("%s: BRIDGE: TX, len=%d crc=0x%04x\n", getLogDateTime(), len, checksum); + Serial.printf("%s: RS232 BRIDGE: TX, len=%d crc=0x%04x\n", getLogDateTime(), len, checksum); #endif } } @@ -81,7 +81,12 @@ void RS232Bridge::loop() { (_rx_buffer_pos == 1 && b == (SERIAL_PKT_MAGIC & 0xFF))) { _rx_buffer[_rx_buffer_pos++] = b; } else { + // Invalid magic byte, reset and start over _rx_buffer_pos = 0; + // Check if this byte could be the start of a new magic word + if (b == ((SERIAL_PKT_MAGIC >> 8) & 0xFF)) { + _rx_buffer[_rx_buffer_pos++] = b; + } } } else { // Reading length, payload, and checksum @@ -89,22 +94,44 @@ void RS232Bridge::loop() { if (_rx_buffer_pos >= 4) { uint16_t len = (_rx_buffer[2] << 8) | _rx_buffer[3]; + + // Validate length field if (len > (MAX_TRANS_UNIT + 1)) { +#if MESH_PACKET_LOGGING + Serial.printf("%s: RS232 BRIDGE: RX invalid length %d, resetting\n", getLogDateTime(), len); +#endif _rx_buffer_pos = 0; // Invalid length, reset - return; + continue; } if (_rx_buffer_pos == len + SERIAL_OVERHEAD) { // Full packet received - uint16_t checksum = (_rx_buffer[4 + len] << 8) | _rx_buffer[5 + len]; - if (checksum == fletcher16(_rx_buffer + 4, len)) { + uint16_t received_checksum = (_rx_buffer[4 + len] << 8) | _rx_buffer[5 + len]; + + if (validateChecksum(_rx_buffer + 4, len, received_checksum)) { #if MESH_PACKET_LOGGING - Serial.printf("%s: BRIDGE: RX, len=%d crc=0x%04x\n", getLogDateTime(), len, checksum); + Serial.printf("%s: RS232 BRIDGE: RX, len=%d crc=0x%04x\n", getLogDateTime(), len, + received_checksum); #endif - mesh::Packet* pkt = _mgr->allocNew(); + mesh::Packet *pkt = _mgr->allocNew(); if (pkt) { - pkt->readFrom(_rx_buffer + 4, len); - onPacketReceived(pkt); + if (pkt->readFrom(_rx_buffer + 4, len)) { + onPacketReceived(pkt); + } else { +#if MESH_PACKET_LOGGING + Serial.printf("%s: RS232 BRIDGE: RX failed to parse packet\n", getLogDateTime()); +#endif + _mgr->free(pkt); + } + } else { +#if MESH_PACKET_LOGGING + Serial.printf("%s: RS232 BRIDGE: RX failed to allocate packet\n", getLogDateTime()); +#endif } + } else { +#if MESH_PACKET_LOGGING + Serial.printf("%s: RS232 BRIDGE: RX checksum mismatch, rcv=0x%04x\n", getLogDateTime(), + received_checksum); +#endif } _rx_buffer_pos = 0; // Reset for next packet } @@ -113,12 +140,8 @@ void RS232Bridge::loop() { } } -void RS232Bridge::onPacketReceived(mesh::Packet* packet) { - if (!_seen_packets.hasSeen(packet)) { - _mgr->queueInbound(packet, 0); - } else { - _mgr->free(packet); - } +void RS232Bridge::onPacketReceived(mesh::Packet *packet) { + handleReceivedPacket(packet); } #endif diff --git a/src/helpers/bridges/RS232Bridge.h b/src/helpers/bridges/RS232Bridge.h index 49c781cb..32fad17f 100644 --- a/src/helpers/bridges/RS232Bridge.h +++ b/src/helpers/bridges/RS232Bridge.h @@ -1,7 +1,7 @@ #pragma once -#include "helpers/AbstractBridge.h" -#include "helpers/SimpleMeshTables.h" +#include "helpers/bridges/BridgeBase.h" + #include #ifdef WITH_RS232_BRIDGE @@ -15,21 +15,22 @@ * * Features: * - Point-to-point communication over hardware UART - * - Fletcher-16 checksum for data integrity verification - * - Magic header for packet synchronization + * - Fletcher-16 checksum for data integrity verification + * - Magic header for packet synchronization and frame alignment + * - Duplicate packet detection using SimpleMeshTables tracking * - Configurable RX/TX pins via build defines - * - Baud rate fixed at 115200 + * - Fixed baud rate at 115200 for consistent timing * * Packet Structure: - * [2 bytes] Magic Header - Used to identify start of packet - * [2 bytes] Fletcher-16 checksum - Data integrity check - * [1 byte] Payload length - * [n bytes] Packet payload + * [2 bytes] Magic Header (0xC03E) - Used to identify start of RS232Bridge packets + * [2 bytes] Payload Length - Length of the mesh packet payload + * [n bytes] Mesh Packet Payload - The actual mesh packet data + * [2 bytes] Fletcher-16 Checksum - Calculated over the payload for integrity verification * - * The Fletcher-16 checksum is used to validate packet integrity and detect - * corrupted or malformed packets. It provides error detection capabilities - * suitable for serial communication where noise or timing issues could - * corrupt data. + * The Fletcher-16 checksum is calculated over the mesh packet payload and provides + * error detection capabilities suitable for serial communication where electrical + * noise, timing issues, or hardware problems could corrupt data. The checksum + * validation ensures only valid packets are forwarded to the mesh. * * Configuration: * - Define WITH_RS232_BRIDGE to enable this bridge @@ -37,12 +38,13 @@ * - Define WITH_RS232_BRIDGE_TX with the TX pin number * * Platform Support: - * - ESP32 targets - * - NRF52 targets - * - RP2040 targets - * - STM32 targets + * Different platforms require different pin configuration methods: + * - ESP32: Uses HardwareSerial::setPins(rx, tx) + * - NRF52: Uses HardwareSerial::setPins(rx, tx) + * - RP2040: Uses SerialUART::setRX(rx) and SerialUART::setTX(tx) + * - STM32: Uses HardwareSerial::setRx(rx) and HardwareSerial::setTx(tx) */ -class RS232Bridge : public AbstractBridge { +class RS232Bridge : public BridgeBase { public: /** * @brief Constructs an RS232Bridge instance @@ -51,69 +53,98 @@ public: * @param mgr PacketManager for allocating and queuing packets * @param rtc RTCClock for timestamping debug messages */ - RS232Bridge(Stream& serial, mesh::PacketManager* mgr, mesh::RTCClock* rtc); + RS232Bridge(Stream &serial, mesh::PacketManager *mgr, mesh::RTCClock *rtc); /** * Initializes the RS232 bridge - * - * - Configures UART pins based on platform - * - Sets baud rate to 115200 + * + * - Validates that RX/TX pins are defined + * - Configures UART pins based on target platform + * - Sets baud rate to 115200 for consistent communication + * - Platform-specific pin configuration methods are used */ void begin() override; /** - * @brief Main loop handler - * Processes incoming serial data and builds packets + * @brief Main loop handler for processing incoming serial data + * + * Implements a state machine for packet reception: + * 1. Searches for magic header bytes for packet synchronization + * 2. Reads length field to determine expected packet size + * 3. Validates packet length against maximum allowed size + * 4. Receives complete packet payload and checksum + * 5. Validates Fletcher-16 checksum for data integrity + * 6. Creates mesh packet and forwards if valid */ void loop() override; /** * @brief Called when a packet needs to be transmitted over serial - * Formats and sends the packet with proper framing + * + * Formats the mesh packet with RS232 framing protocol: + * - Adds magic header for synchronization + * - Includes payload length field + * - Calculates Fletcher-16 checksum over payload + * - Transmits complete framed packet + * - Uses duplicate detection to prevent retransmission * * @param packet The mesh packet to transmit */ - void onPacketTransmitted(mesh::Packet* packet) override; + void onPacketTransmitted(mesh::Packet *packet) override; /** - * @brief Called when a complete packet has been received from serial - * Queues the packet for mesh processing if checksum is valid - * - * @param packet The received mesh packet + * @brief Called when a complete valid packet has been received from serial + * + * Forwards the received packet to the mesh for processing. + * The packet has already been validated for checksum integrity + * and parsed successfully at this point. + * + * @param packet The received mesh packet ready for processing */ - void onPacketReceived(mesh::Packet* packet) override; + void onPacketReceived(mesh::Packet *packet) override; private: - /** Helper function to get formatted timestamp for logging */ - const char* getLogDateTime(); + /** + * RS232 Protocol Structure: + * - Magic header: 2 bytes (packet identification) + * - Length field: 2 bytes (payload length) + * - Payload: variable bytes (mesh packet data) + * - Checksum: 2 bytes (Fletcher-16 over payload) + * Total overhead: 6 bytes + */ - /** Magic number to identify start of RS232 packets */ + /** Magic number to identify start of RS232Bridge packets */ static constexpr uint16_t SERIAL_PKT_MAGIC = 0xC03E; /** - * @brief The total overhead of the serial protocol in bytes. - * [MAGIC_WORD (2 bytes)] [LENGTH (2 bytes)] [PAYLOAD (variable)] [CHECKSUM (2 bytes)] + * Size constants for packet parsing and construction */ - static constexpr uint16_t SERIAL_OVERHEAD = 6; + static constexpr uint16_t MAGIC_HEADER_SIZE = 2; + static constexpr uint16_t LENGTH_FIELD_SIZE = 2; + static constexpr uint16_t CHECKSUM_SIZE = 2; /** - * @brief The maximum size of a packet on the serial line. + * @brief The total overhead of the serial protocol in bytes. + * Includes: MAGIC_WORD (2) + LENGTH (2) + CHECKSUM (2) = 6 bytes + */ + static constexpr uint16_t SERIAL_OVERHEAD = MAGIC_HEADER_SIZE + LENGTH_FIELD_SIZE + CHECKSUM_SIZE; + + /** + * @brief The maximum size of a complete packet on the serial line. * * This is calculated as the sum of: - * - 1 byte for the packet header (from mesh::Packet) - * - 4 bytes for transport codes (from mesh::Packet) - * - 1 byte for the path length (from mesh::Packet) - * - MAX_PATH_SIZE for the path itself (from MeshCore.h) - * - MAX_PACKET_PAYLOAD for the payload (from MeshCore.h) - * - SERIAL_OVERHEAD for the serial framing + * - MAX_TRANS_UNIT + 1 for the maximum mesh packet size + * - SERIAL_OVERHEAD for the framing (magic + length + checksum) */ static constexpr uint16_t MAX_SERIAL_PACKET_SIZE = (MAX_TRANS_UNIT + 1) + SERIAL_OVERHEAD; - Stream* _serial; - mesh::PacketManager* _mgr; - mesh::RTCClock* _rtc; - SimpleMeshTables _seen_packets; - uint8_t _rx_buffer[MAX_SERIAL_PACKET_SIZE]; // Buffer for serial data + /** Hardware serial port interface */ + Stream *_serial; + + /** Buffer for building received packets */ + uint8_t _rx_buffer[MAX_SERIAL_PACKET_SIZE]; + + /** Current position in the receive buffer */ uint16_t _rx_buffer_pos = 0; }; From 1d25c87c57ddca51eb0abbad272b5d45951c64e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Mon, 8 Sep 2025 11:15:28 +0100 Subject: [PATCH 19/21] Refactor bridge packet handling to use common magic number and size constants --- src/helpers/bridges/BridgeBase.h | 20 +++++++++++++++++++ src/helpers/bridges/ESPNowBridge.cpp | 24 +++++++++++------------ src/helpers/bridges/ESPNowBridge.h | 9 +-------- src/helpers/bridges/RS232Bridge.cpp | 10 +++++----- src/helpers/bridges/RS232Bridge.h | 12 +----------- variants/lilygo_tlora_v2_1/platformio.ini | 2 +- 6 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/helpers/bridges/BridgeBase.h b/src/helpers/bridges/BridgeBase.h index 2aff2306..c1764ae3 100644 --- a/src/helpers/bridges/BridgeBase.h +++ b/src/helpers/bridges/BridgeBase.h @@ -21,6 +21,26 @@ class BridgeBase : public AbstractBridge { public: virtual ~BridgeBase() = default; + /** + * @brief Common magic number used by all bridge implementations for packet identification + * + * This magic number is placed at the beginning of bridge packets to identify + * them as mesh bridge packets and provide frame synchronization. + */ + static constexpr uint16_t BRIDGE_PACKET_MAGIC = 0xC03E; + + /** + * @brief Common field sizes used by bridge implementations + * + * These constants define the size of common packet fields used across bridges. + * BRIDGE_MAGIC_SIZE is used by all bridges for packet identification. + * BRIDGE_LENGTH_SIZE is used by bridges that need explicit length fields (like RS232). + * BRIDGE_CHECKSUM_SIZE is used by all bridges for Fletcher-16 checksums. + */ + static constexpr uint16_t BRIDGE_MAGIC_SIZE = sizeof(BRIDGE_PACKET_MAGIC); + static constexpr uint16_t BRIDGE_LENGTH_SIZE = sizeof(uint16_t); + static constexpr uint16_t BRIDGE_CHECKSUM_SIZE = sizeof(uint16_t); + protected: /** Packet manager for allocating and queuing mesh packets */ mesh::PacketManager *_mgr; diff --git a/src/helpers/bridges/ESPNowBridge.cpp b/src/helpers/bridges/ESPNowBridge.cpp index baf683ca..a02f804e 100644 --- a/src/helpers/bridges/ESPNowBridge.cpp +++ b/src/helpers/bridges/ESPNowBridge.cpp @@ -66,7 +66,7 @@ void ESPNowBridge::xorCrypt(uint8_t *data, size_t len) { void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t len) { // Ignore packets that are too small to contain header + checksum - if (len < (MAGIC_HEADER_SIZE + CHECKSUM_SIZE)) { + if (len < (BRIDGE_MAGIC_SIZE + BRIDGE_CHECKSUM_SIZE)) { #if MESH_PACKET_LOGGING Serial.printf("%s: ESPNOW BRIDGE: RX packet too small, len=%d\n", getLogDateTime(), len); #endif @@ -83,7 +83,7 @@ void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t l // Check packet header magic uint16_t received_magic = (data[0] << 8) | data[1]; - if (received_magic != ESPNOW_HEADER_MAGIC) { + if (received_magic != BRIDGE_PACKET_MAGIC) { #if MESH_PACKET_LOGGING Serial.printf("%s: ESPNOW BRIDGE: RX invalid magic 0x%04X\n", getLogDateTime(), received_magic); #endif @@ -92,17 +92,17 @@ void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t l // Make a copy we can decrypt uint8_t decrypted[MAX_ESPNOW_PACKET_SIZE]; - const size_t encryptedDataLen = len - MAGIC_HEADER_SIZE; - memcpy(decrypted, data + MAGIC_HEADER_SIZE, encryptedDataLen); + const size_t encryptedDataLen = len - BRIDGE_MAGIC_SIZE; + memcpy(decrypted, data + BRIDGE_MAGIC_SIZE, encryptedDataLen); // Try to decrypt (checksum + payload) xorCrypt(decrypted, encryptedDataLen); // Validate checksum uint16_t received_checksum = (decrypted[0] << 8) | decrypted[1]; - const size_t payloadLen = encryptedDataLen - CHECKSUM_SIZE; + const size_t payloadLen = encryptedDataLen - BRIDGE_CHECKSUM_SIZE; - if (!validateChecksum(decrypted + CHECKSUM_SIZE, payloadLen, received_checksum)) { + if (!validateChecksum(decrypted + BRIDGE_CHECKSUM_SIZE, payloadLen, received_checksum)) { // Failed to decrypt - likely from a different network #if MESH_PACKET_LOGGING Serial.printf("%s: ESPNOW BRIDGE: RX checksum mismatch, rcv=0x%04X\n", getLogDateTime(), @@ -119,7 +119,7 @@ void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t l mesh::Packet *pkt = _instance->_mgr->allocNew(); if (!pkt) return; - if (pkt->readFrom(decrypted + CHECKSUM_SIZE, payloadLen)) { + if (pkt->readFrom(decrypted + BRIDGE_CHECKSUM_SIZE, payloadLen)) { _instance->onPacketReceived(pkt); } else { _instance->_mgr->free(pkt); @@ -161,11 +161,11 @@ void ESPNowBridge::onPacketTransmitted(mesh::Packet *packet) { uint8_t buffer[MAX_ESPNOW_PACKET_SIZE]; // Write magic header (2 bytes) - buffer[0] = (ESPNOW_HEADER_MAGIC >> 8) & 0xFF; - buffer[1] = ESPNOW_HEADER_MAGIC & 0xFF; + buffer[0] = (BRIDGE_PACKET_MAGIC >> 8) & 0xFF; + buffer[1] = BRIDGE_PACKET_MAGIC & 0xFF; // Write packet payload starting after magic header and checksum - const size_t packetOffset = MAGIC_HEADER_SIZE + CHECKSUM_SIZE; + const size_t packetOffset = BRIDGE_MAGIC_SIZE + BRIDGE_CHECKSUM_SIZE; memcpy(buffer + packetOffset, sizingBuffer, meshPacketLen); // Calculate and add checksum (only of the payload) @@ -174,10 +174,10 @@ void ESPNowBridge::onPacketTransmitted(mesh::Packet *packet) { buffer[3] = checksum & 0xFF; // Low byte // Encrypt payload and checksum (not including magic header) - xorCrypt(buffer + MAGIC_HEADER_SIZE, meshPacketLen + CHECKSUM_SIZE); + xorCrypt(buffer + BRIDGE_MAGIC_SIZE, meshPacketLen + BRIDGE_CHECKSUM_SIZE); // Total packet size: magic header + checksum + payload - const size_t totalPacketSize = MAGIC_HEADER_SIZE + CHECKSUM_SIZE + meshPacketLen; + const size_t totalPacketSize = BRIDGE_MAGIC_SIZE + BRIDGE_CHECKSUM_SIZE + meshPacketLen; // Broadcast using ESP-NOW uint8_t broadcastAddress[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; diff --git a/src/helpers/bridges/ESPNowBridge.h b/src/helpers/bridges/ESPNowBridge.h index d9962c72..b43f1744 100644 --- a/src/helpers/bridges/ESPNowBridge.h +++ b/src/helpers/bridges/ESPNowBridge.h @@ -65,14 +65,7 @@ private: /** * Size constants for packet parsing */ - static const size_t MAGIC_HEADER_SIZE = 2; - static const size_t CHECKSUM_SIZE = 2; - static const size_t MAX_PAYLOAD_SIZE = MAX_ESPNOW_PACKET_SIZE - (MAGIC_HEADER_SIZE + CHECKSUM_SIZE); - - /** - * Magic bytes to identify ESPNowBridge packets - */ - static const uint16_t ESPNOW_HEADER_MAGIC = 0xC03E; + static const size_t MAX_PAYLOAD_SIZE = MAX_ESPNOW_PACKET_SIZE - (BRIDGE_MAGIC_SIZE + BRIDGE_CHECKSUM_SIZE); /** Buffer for receiving ESP-NOW packets */ uint8_t _rx_buffer[MAX_ESPNOW_PACKET_SIZE]; diff --git a/src/helpers/bridges/RS232Bridge.cpp b/src/helpers/bridges/RS232Bridge.cpp index 39a0968a..b209a6da 100644 --- a/src/helpers/bridges/RS232Bridge.cpp +++ b/src/helpers/bridges/RS232Bridge.cpp @@ -52,8 +52,8 @@ void RS232Bridge::onPacketTransmitted(mesh::Packet *packet) { } // Build packet header - buffer[0] = (SERIAL_PKT_MAGIC >> 8) & 0xFF; // Magic high byte - buffer[1] = SERIAL_PKT_MAGIC & 0xFF; // Magic low byte + buffer[0] = (BRIDGE_PACKET_MAGIC >> 8) & 0xFF; // Magic high byte + buffer[1] = BRIDGE_PACKET_MAGIC & 0xFF; // Magic low byte buffer[2] = (len >> 8) & 0xFF; // Length high byte buffer[3] = len & 0xFF; // Length low byte @@ -77,14 +77,14 @@ void RS232Bridge::loop() { if (_rx_buffer_pos < 2) { // Waiting for magic word - if ((_rx_buffer_pos == 0 && b == ((SERIAL_PKT_MAGIC >> 8) & 0xFF)) || - (_rx_buffer_pos == 1 && b == (SERIAL_PKT_MAGIC & 0xFF))) { + if ((_rx_buffer_pos == 0 && b == ((BRIDGE_PACKET_MAGIC >> 8) & 0xFF)) || + (_rx_buffer_pos == 1 && b == (BRIDGE_PACKET_MAGIC & 0xFF))) { _rx_buffer[_rx_buffer_pos++] = b; } else { // Invalid magic byte, reset and start over _rx_buffer_pos = 0; // Check if this byte could be the start of a new magic word - if (b == ((SERIAL_PKT_MAGIC >> 8) & 0xFF)) { + if (b == ((BRIDGE_PACKET_MAGIC >> 8) & 0xFF)) { _rx_buffer[_rx_buffer_pos++] = b; } } diff --git a/src/helpers/bridges/RS232Bridge.h b/src/helpers/bridges/RS232Bridge.h index 32fad17f..3b09de75 100644 --- a/src/helpers/bridges/RS232Bridge.h +++ b/src/helpers/bridges/RS232Bridge.h @@ -113,21 +113,11 @@ private: * Total overhead: 6 bytes */ - /** Magic number to identify start of RS232Bridge packets */ - static constexpr uint16_t SERIAL_PKT_MAGIC = 0xC03E; - - /** - * Size constants for packet parsing and construction - */ - static constexpr uint16_t MAGIC_HEADER_SIZE = 2; - static constexpr uint16_t LENGTH_FIELD_SIZE = 2; - static constexpr uint16_t CHECKSUM_SIZE = 2; - /** * @brief The total overhead of the serial protocol in bytes. * Includes: MAGIC_WORD (2) + LENGTH (2) + CHECKSUM (2) = 6 bytes */ - static constexpr uint16_t SERIAL_OVERHEAD = MAGIC_HEADER_SIZE + LENGTH_FIELD_SIZE + CHECKSUM_SIZE; + static constexpr uint16_t SERIAL_OVERHEAD = BRIDGE_MAGIC_SIZE + BRIDGE_LENGTH_SIZE + BRIDGE_CHECKSUM_SIZE; /** * @brief The maximum size of a complete packet on the serial line. diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index aa957fba..d9ff1700 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -198,7 +198,7 @@ build_flags = -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' -; -D MESH_PACKET_LOGGING=1 + -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 ; -D CORE_DEBUG_LEVEL=3 lib_deps = From 1c93c162a18bcf61bedbafe343d680e06333aa40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Mon, 8 Sep 2025 18:49:33 +0100 Subject: [PATCH 20/21] Add ESPNow bridge configurations for all ESP32 targets --- variants/generic-e22/platformio.ini | 94 ++++++++++++++++++ variants/heltec_ct62/platformio.ini | 41 ++++++++ variants/heltec_e213/platformio.ini | 45 +++++++++ variants/heltec_e290/platformio.ini | 45 +++++++++ variants/heltec_t190/platformio.ini | 41 ++++++++ variants/heltec_tracker/platformio.ini | 47 +++++++++ variants/heltec_v2/platformio.ini | 47 +++++++++ variants/heltec_wireless_paper/platformio.ini | 45 +++++++++ variants/lilygo_t3s3/platformio.ini | 45 +++++++++ variants/lilygo_t3s3_sx1276/platformio.ini | 45 +++++++++ variants/lilygo_tbeam_SX1262/platformio.ini | 41 ++++++++ variants/lilygo_tbeam_SX1276/platformio.ini | 43 ++++++++ .../platformio.ini | 41 ++++++++ variants/lilygo_tlora_v2_1/platformio.ini | 2 +- variants/meshadventurer/platformio.ini | 98 +++++++++++++++++++ variants/station_g2/platformio.ini | 84 ++++++++++++++++ variants/tenstar_c3/platformio.ini | 96 ++++++++++++++++++ variants/xiao_s3_wio/platformio.ini | 41 ++++++++ 18 files changed, 940 insertions(+), 1 deletion(-) diff --git a/variants/generic-e22/platformio.ini b/variants/generic-e22/platformio.ini index 8b2c293b..c9a67220 100644 --- a/variants/generic-e22/platformio.ini +++ b/variants/generic-e22/platformio.ini @@ -47,6 +47,53 @@ lib_deps = ${Generic_E22.lib_deps} ${esp32_ota.lib_deps} +; [env:Generic_E22_sx1262_repeater_bridge_rs232] +; extends = Generic_E22 +; build_src_filter = ${Generic_E22.build_src_filter} +; + +; +<../examples/simple_repeater/main.cpp> +; build_flags = +; ${Generic_E22.build_flags} +; -D RADIO_CLASS=CustomSX1262 +; -D WRAPPER_CLASS=CustomSX1262Wrapper +; -D LORA_TX_POWER=22 +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; lib_deps = +; ${Generic_E22.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Generic_E22_sx1262_repeater_bridge_espnow] +extends = Generic_E22 +build_src_filter = ${Generic_E22.build_src_filter} + + + +<../examples/simple_repeater/main.cpp> +build_flags = + ${Generic_E22.build_flags} + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Generic_E22.lib_deps} + ${esp32_ota.lib_deps} + [env:Generic_E22_sx1268_repeater] extends = Generic_E22 build_src_filter = ${Generic_E22.build_src_filter} @@ -66,3 +113,50 @@ build_flags = lib_deps = ${Generic_E22.lib_deps} ${esp32_ota.lib_deps} + +; [env:Generic_E22_sx1268_repeater_bridge_rs232] +; extends = Generic_E22 +; build_src_filter = ${Generic_E22.build_src_filter} +; + +; +<../examples/simple_repeater/main.cpp> +; build_flags = +; ${Generic_E22.build_flags} +; -D RADIO_CLASS=CustomSX1268 +; -D WRAPPER_CLASS=CustomSX1268Wrapper +; -D LORA_TX_POWER=22 +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; lib_deps = +; ${Generic_E22.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Generic_E22_sx1268_repeater_bridge_espnow] +extends = Generic_E22 +build_src_filter = ${Generic_E22.build_src_filter} + + + +<../examples/simple_repeater/main.cpp> +build_flags = + ${Generic_E22.build_flags} + -D RADIO_CLASS=CustomSX1268 + -D WRAPPER_CLASS=CustomSX1268Wrapper + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Generic_E22.lib_deps} + ${esp32_ota.lib_deps} diff --git a/variants/heltec_ct62/platformio.ini b/variants/heltec_ct62/platformio.ini index 0dc512b9..1b83adbf 100644 --- a/variants/heltec_ct62/platformio.ini +++ b/variants/heltec_ct62/platformio.ini @@ -49,6 +49,47 @@ lib_deps = ${Heltec_ct62.lib_deps} ${esp32_ota.lib_deps} +; [env:Heltec_ct62_repeater_bridge_rs232] +; extends = Heltec_ct62 +; build_flags = +; ${Heltec_ct62.build_flags} +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${Heltec_ct62.build_src_filter} +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${Heltec_ct62.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Heltec_ct62_repeater_bridge_espnow] +extends = Heltec_ct62 +build_flags = + ${Heltec_ct62.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_ct62.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_ct62.lib_deps} + ${esp32_ota.lib_deps} + [env:Heltec_ct62_companion_radio_usb] extends = Heltec_ct62 build_flags = diff --git a/variants/heltec_e213/platformio.ini b/variants/heltec_e213/platformio.ini index c8efc819..a6fe2560 100644 --- a/variants/heltec_e213/platformio.ini +++ b/variants/heltec_e213/platformio.ini @@ -93,6 +93,51 @@ lib_deps = ${Heltec_E213_base.lib_deps} ${esp32_ota.lib_deps} +; [env:Heltec_E213_repeater_bridge_rs232] +; extends = Heltec_E213_base +; build_flags = +; ${Heltec_E213_base.build_flags} +; -D DISPLAY_CLASS=E213Display +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${Heltec_E213_base.build_src_filter} +; + +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${Heltec_E213_base.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Heltec_E213_repeater_bridge_espnow] +extends = Heltec_E213_base +build_flags = + ${Heltec_E213_base.build_flags} + -D DISPLAY_CLASS=E213Display + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_E213_base.build_src_filter} + + + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_E213_base.lib_deps} + ${esp32_ota.lib_deps} + [env:Heltec_E213_room_server] extends = Heltec_E213_base build_flags = diff --git a/variants/heltec_e290/platformio.ini b/variants/heltec_e290/platformio.ini index 377162f4..0223b30c 100644 --- a/variants/heltec_e290/platformio.ini +++ b/variants/heltec_e290/platformio.ini @@ -89,6 +89,51 @@ lib_deps = ${Heltec_E290_base.lib_deps} ${esp32_ota.lib_deps} +; [env:Heltec_E290_repeater_bridge_rs232] +; extends = Heltec_E290_base +; build_flags = +; ${Heltec_E290_base.build_flags} +; -D DISPLAY_CLASS=E290Display +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${Heltec_E290_base.build_src_filter} +; + +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${Heltec_E290_base.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Heltec_E290_repeater_bridge_espnow] +extends = Heltec_E290_base +build_flags = + ${Heltec_E290_base.build_flags} + -D DISPLAY_CLASS=E290Display + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_E290_base.build_src_filter} + + + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_E290_base.lib_deps} + ${esp32_ota.lib_deps} + [env:Heltec_E290_room_server] extends = Heltec_E290_base build_flags = diff --git a/variants/heltec_t190/platformio.ini b/variants/heltec_t190/platformio.ini index 7debe178..52bb79e0 100644 --- a/variants/heltec_t190/platformio.ini +++ b/variants/heltec_t190/platformio.ini @@ -94,6 +94,47 @@ lib_deps = ${Heltec_T190_base.lib_deps} ${esp32_ota.lib_deps} +; [env:Heltec_T190_repeater_bridge_rs232] +; extends = Heltec_T190_base +; build_flags = +; ${Heltec_T190_base.build_flags} +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${Heltec_T190_base.build_src_filter} +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${Heltec_T190_base.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Heltec_T190_repeater_bridge_espnow] +extends = Heltec_T190_base +build_flags = + ${Heltec_T190_base.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_T190_base.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_T190_base.lib_deps} + ${esp32_ota.lib_deps} + [env:Heltec_T190_room_server] extends = Heltec_T190_base build_flags = diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index f1477e9f..5c0df007 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -80,6 +80,53 @@ lib_deps = ${Heltec_tracker_base.lib_deps} ${esp32_ota.lib_deps} +; [env:Heltec_Wireless_Tracker_repeater_bridge_rs232] +; extends = Heltec_tracker_base +; build_flags = +; ${Heltec_tracker_base.build_flags} +; -D DISPLAY_ROTATION=1 +; -D DISPLAY_CLASS=ST7735Display +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${Heltec_tracker_base.build_src_filter} +; + +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${Heltec_tracker_base.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Heltec_Wireless_Tracker_repeater_bridge_espnow] +extends = Heltec_tracker_base +build_flags = + ${Heltec_tracker_base.build_flags} + -D DISPLAY_ROTATION=1 + -D DISPLAY_CLASS=ST7735Display + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_base.build_src_filter} + + + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_tracker_base.lib_deps} + ${esp32_ota.lib_deps} + [env:Heltec_Wireless_Tracker_room_server] extends = Heltec_tracker_base build_flags = diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index 352ea34d..d2afe4db 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -40,6 +40,53 @@ lib_deps = ${Heltec_lora32_v2.lib_deps} ${esp32_ota.lib_deps} +; [env:Heltec_v2_repeater_bridge_rs232] +; extends = Heltec_lora32_v2 +; build_flags = +; ${Heltec_lora32_v2.build_flags} +; -D DISPLAY_CLASS=SSD1306Display +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${Heltec_lora32_v2.build_src_filter} +; + +; + +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${Heltec_lora32_v2.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Heltec_v2_repeater_bridge_espnow] +extends = Heltec_lora32_v2 +build_flags = + ${Heltec_lora32_v2.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v2.build_src_filter} + + + + + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_lora32_v2.lib_deps} + ${esp32_ota.lib_deps} + [env:Heltec_v2_room_server] extends = Heltec_lora32_v2 build_flags = diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index 8de826e4..43ac2a82 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -68,6 +68,51 @@ lib_deps = ${Heltec_Wireless_Paper_base.lib_deps} ${esp32_ota.lib_deps} +; [env:Heltec_Wireless_Paper_repeater_bridge_rs232] +; extends = Heltec_Wireless_Paper_base +; build_flags = +; ${Heltec_Wireless_Paper_base.build_flags} +; -D DISPLAY_CLASS=E213Display +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} +; + +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${Heltec_Wireless_Paper_base.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Heltec_Wireless_Paper_repeater_bridge_espnow] +extends = Heltec_Wireless_Paper_base +build_flags = + ${Heltec_Wireless_Paper_base.build_flags} + -D DISPLAY_CLASS=E213Display + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} + + + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_Wireless_Paper_base.lib_deps} + ${esp32_ota.lib_deps} + [env:Heltec_Wireless_Paper_room_server] extends = Heltec_Wireless_Paper_base build_flags = diff --git a/variants/lilygo_t3s3/platformio.ini b/variants/lilygo_t3s3/platformio.ini index 637cc123..ca221108 100644 --- a/variants/lilygo_t3s3/platformio.ini +++ b/variants/lilygo_t3s3/platformio.ini @@ -52,6 +52,51 @@ lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} ${esp32_ota.lib_deps} +; [env:LilyGo_T3S3_sx1262_Repeater_bridge_rs232] +; extends = LilyGo_T3S3_sx1262 +; build_flags = +; ${LilyGo_T3S3_sx1262.build_flags} +; -D DISPLAY_CLASS=SSD1306Display +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} +; + +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${LilyGo_T3S3_sx1262.lib_deps} +; ${esp32_ota.lib_deps} + +[env:LilyGo_T3S3_sx1262_Repeater_bridge_espnow] +extends = LilyGo_T3S3_sx1262 +build_flags = + ${LilyGo_T3S3_sx1262.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + + + + + +<../examples/simple_repeater> +lib_deps = + ${LilyGo_T3S3_sx1262.lib_deps} + ${esp32_ota.lib_deps} + [env:LilyGo_T3S3_sx1262_terminal_chat] extends = LilyGo_T3S3_sx1262 build_flags = diff --git a/variants/lilygo_t3s3_sx1276/platformio.ini b/variants/lilygo_t3s3_sx1276/platformio.ini index 23c58fb8..1c0d5cf1 100644 --- a/variants/lilygo_t3s3_sx1276/platformio.ini +++ b/variants/lilygo_t3s3_sx1276/platformio.ini @@ -50,6 +50,51 @@ lib_deps = ${LilyGo_T3S3_sx1276.lib_deps} ${esp32_ota.lib_deps} +; [env:LilyGo_T3S3_sx1276_Repeater_bridge_rs232] +; extends = LilyGo_T3S3_sx1276 +; build_flags = +; ${LilyGo_T3S3_sx1276.build_flags} +; -D DISPLAY_CLASS=SSD1306Display +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} +; + +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${LilyGo_T3S3_sx1276.lib_deps} +; ${esp32_ota.lib_deps} + +[env:LilyGo_T3S3_sx1276_Repeater_bridge_espnow] +extends = LilyGo_T3S3_sx1276 +build_flags = + ${LilyGo_T3S3_sx1276.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' + ; -D MESH_PACKET_LOGGING=1 + ; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} + + + + + +<../examples/simple_repeater> +lib_deps = + ${LilyGo_T3S3_sx1276.lib_deps} + ${esp32_ota.lib_deps} + [env:LilyGo_T3S3_sx1276_terminal_chat] extends = LilyGo_T3S3_sx1276 build_flags = diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index ea8872de..f7d1a764 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -74,6 +74,47 @@ lib_deps = ${LilyGo_TBeam_SX1262.lib_deps} ${esp32_ota.lib_deps} +; [env:Tbeam_SX1262_repeater_bridge_rs232] +; extends = LilyGo_TBeam_SX1262 +; build_flags = +; ${LilyGo_TBeam_SX1262.build_flags} +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${LilyGo_TBeam_SX1262.build_src_filter} +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${LilyGo_TBeam_SX1262.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Tbeam_SX1262_repeater_bridge_espnow] +extends = LilyGo_TBeam_SX1262 +build_flags = + ${LilyGo_TBeam_SX1262.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_SX1262.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${LilyGo_TBeam_SX1262.lib_deps} + ${esp32_ota.lib_deps} + [env:Tbeam_SX1262_room_server] extends = LilyGo_TBeam_SX1262 build_flags = diff --git a/variants/lilygo_tbeam_SX1276/platformio.ini b/variants/lilygo_tbeam_SX1276/platformio.ini index 782b74c7..d7e119ef 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -72,6 +72,49 @@ lib_deps = ${LilyGo_TBeam_SX1276.lib_deps} ${esp32_ota.lib_deps} +; [env:Tbeam_SX1276_repeater_bridge_rs232] +; extends = LilyGo_TBeam_SX1276 +; build_flags = +; ${LilyGo_TBeam_SX1276.build_flags} +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D PERSISTANT_GPS=1 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${LilyGo_TBeam_SX1276.build_src_filter} +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${LilyGo_TBeam_SX1276.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Tbeam_SX1276_repeater_bridge_espnow] +extends = LilyGo_TBeam_SX1276 +build_flags = + ${LilyGo_TBeam_SX1276.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D PERSISTANT_GPS=1 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_SX1276.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${LilyGo_TBeam_SX1276.lib_deps} + ${esp32_ota.lib_deps} + [env:Tbeam_SX1276_room_server] extends = LilyGo_TBeam_SX1276 build_flags = diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index e6135872..328ebf07 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -52,6 +52,47 @@ lib_deps = ${T_Beam_S3_Supreme_SX1262.lib_deps} ${esp32_ota.lib_deps} +; [env:T_Beam_S3_Supreme_SX1262_repeater_bridge_rs232] +; extends = T_Beam_S3_Supreme_SX1262 +; build_flags = +; ${T_Beam_S3_Supreme_SX1262.build_flags} +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0 +; -D ADVERT_LON=0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter} +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${T_Beam_S3_Supreme_SX1262.lib_deps} +; ${esp32_ota.lib_deps} + +[env:T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow] +extends = T_Beam_S3_Supreme_SX1262 +build_flags = + ${T_Beam_S3_Supreme_SX1262.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0 + -D ADVERT_LON=0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${T_Beam_S3_Supreme_SX1262.lib_deps} + ${esp32_ota.lib_deps} + [env:T_Beam_S3_Supreme_SX1262_room_server] extends = T_Beam_S3_Supreme_SX1262 build_flags = diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index d9ff1700..aa957fba 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -198,7 +198,7 @@ build_flags = -D MAX_NEIGHBOURS=8 -D WITH_ESPNOW_BRIDGE=1 -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' - -D MESH_PACKET_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 ; -D CORE_DEBUG_LEVEL=3 lib_deps = diff --git a/variants/meshadventurer/platformio.ini b/variants/meshadventurer/platformio.ini index 1b881c1a..be3b4943 100644 --- a/variants/meshadventurer/platformio.ini +++ b/variants/meshadventurer/platformio.ini @@ -54,6 +54,55 @@ lib_deps = ${Meshadventurer.lib_deps} ${esp32_ota.lib_deps} +; [env:Meshadventurer_sx1262_repeater_bridge_rs232] +; extends = Meshadventurer +; build_src_filter = ${Meshadventurer.build_src_filter} +; + +; +<../examples/simple_repeater> +; + +; build_flags = +; ${Meshadventurer.build_flags} +; -D RADIO_CLASS=CustomSX1262 +; -D WRAPPER_CLASS=CustomSX1262Wrapper +; -D LORA_TX_POWER=22 +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; lib_deps = +; ${Meshadventurer.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Meshadventurer_sx1262_repeater_bridge_espnow] +extends = Meshadventurer +build_src_filter = ${Meshadventurer.build_src_filter} + + + +<../examples/simple_repeater> + + +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Meshadventurer.lib_deps} + ${esp32_ota.lib_deps} + [env:Meshadventurer_sx1268_repeater] extends = Meshadventurer build_src_filter = ${Meshadventurer.build_src_filter} @@ -75,6 +124,55 @@ lib_deps = ${Meshadventurer.lib_deps} ${esp32_ota.lib_deps} +; [env:Meshadventurer_sx1268_repeater_bridge_rs232] +; extends = Meshadventurer +; build_src_filter = ${Meshadventurer.build_src_filter} +; + +; +<../examples/simple_repeater> +; + +; build_flags = +; ${Meshadventurer.build_flags} +; -D RADIO_CLASS=CustomSX1268 +; -D WRAPPER_CLASS=CustomSX1268Wrapper +; -D LORA_TX_POWER=22 +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; lib_deps = +; ${Meshadventurer.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Meshadventurer_sx1268_repeater_bridge_espnow] +extends = Meshadventurer +build_src_filter = ${Meshadventurer.build_src_filter} + + + +<../examples/simple_repeater> + + +build_flags = + ${Meshadventurer.build_flags} + -D RADIO_CLASS=CustomSX1268 + -D WRAPPER_CLASS=CustomSX1268Wrapper + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Meshadventurer.lib_deps} + ${esp32_ota.lib_deps} + [env:Meshadventurer_sx1262_companion_radio_usb] extends = Meshadventurer build_src_filter = ${Meshadventurer.build_src_filter} diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index 0e1631a8..908d6443 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -45,6 +45,47 @@ lib_deps = ${Station_G2.lib_deps} ${esp32_ota.lib_deps} +; [env:Station_G2_repeater_bridge_rs232] +; extends = Station_G2 +; build_flags = +; ${Station_G2.build_flags} +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${Station_G2.build_src_filter} +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${Station_G2.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Station_G2_repeater_bridge_espnow] +extends = Station_G2 +build_flags = + ${Station_G2.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Station_G2.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${Station_G2.lib_deps} + ${esp32_ota.lib_deps} + [env:Station_G2_logging_repeater] extends = Station_G2 build_flags = @@ -64,6 +105,49 @@ lib_deps = ${Station_G2.lib_deps} ${esp32_ota.lib_deps} +; [env:Station_G2_logging_repeater_bridge_rs232] +; extends = Station_G2 +; build_flags = +; ${Station_G2.build_flags} +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D MESH_PACKET_LOGGING=1 +; -D SX126X_RX_BOOSTED_GAIN=1 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_DEBUG=1 +; build_src_filter = ${Station_G2.build_src_filter} +; + +; +<../examples/simple_repeater> +; lib_deps = +; ${Station_G2.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Station_G2_logging_repeater_bridge_espnow] +extends = Station_G2 +build_flags = + ${Station_G2.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D MESH_PACKET_LOGGING=1 + -D SX126X_RX_BOOSTED_GAIN=1 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_DEBUG=1 +build_src_filter = ${Station_G2.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${Station_G2.lib_deps} + ${esp32_ota.lib_deps} + [env:Station_G2_room_server] extends = Station_G2 build_src_filter = ${Station_G2.build_src_filter} diff --git a/variants/tenstar_c3/platformio.ini b/variants/tenstar_c3/platformio.ini index 4967ec55..c22b3771 100644 --- a/variants/tenstar_c3/platformio.ini +++ b/variants/tenstar_c3/platformio.ini @@ -44,6 +44,55 @@ lib_deps = ${Tenstar_esp32_C3.lib_deps} ${esp32_ota.lib_deps} +; [env:Tenstar_C3_Repeater_sx1262_bridge_rs232] +; extends = Tenstar_esp32_C3 +; build_src_filter = ${Tenstar_esp32_C3.build_src_filter} +; + +; +<../examples/simple_repeater/main.cpp> +; build_flags = +; ${Tenstar_esp32_C3.build_flags} +; -D RADIO_CLASS=CustomSX1262 +; -D WRAPPER_CLASS=CustomSX1262Wrapper +; -D SX126X_RX_BOOSTED_GAIN=1 +; -D LORA_TX_POWER=22 +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; lib_deps = +; ${Tenstar_esp32_C3.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Tenstar_C3_Repeater_sx1262_bridge_espnow] +extends = Tenstar_esp32_C3 +build_src_filter = ${Tenstar_esp32_C3.build_src_filter} + + + +<../examples/simple_repeater/main.cpp> +build_flags = + ${Tenstar_esp32_C3.build_flags} + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D SX126X_RX_BOOSTED_GAIN=1 + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Tenstar_esp32_C3.lib_deps} + ${esp32_ota.lib_deps} + [env:Tenstar_C3_Repeater_sx1268] extends = Tenstar_esp32_C3 build_src_filter = ${Tenstar_esp32_C3.build_src_filter} @@ -63,3 +112,50 @@ build_flags = lib_deps = ${Tenstar_esp32_C3.lib_deps} ${esp32_ota.lib_deps} + +; [env:Tenstar_C3_Repeater_sx1268_bridge_rs232] +; extends = Tenstar_esp32_C3 +; build_src_filter = ${Tenstar_esp32_C3.build_src_filter} +; + +; +<../examples/simple_repeater/main.cpp> +; build_flags = +; ${Tenstar_esp32_C3.build_flags} +; -D RADIO_CLASS=CustomSX1268 +; -D WRAPPER_CLASS=CustomSX1268Wrapper +; -D LORA_TX_POWER=22 +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; lib_deps = +; ${Tenstar_esp32_C3.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Tenstar_C3_Repeater_sx1268_bridge_espnow] +extends = Tenstar_esp32_C3 +build_src_filter = ${Tenstar_esp32_C3.build_src_filter} + + + +<../examples/simple_repeater/main.cpp> +build_flags = + ${Tenstar_esp32_C3.build_flags} + -D RADIO_CLASS=CustomSX1268 + -D WRAPPER_CLASS=CustomSX1268Wrapper + -D LORA_TX_POWER=22 + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' + ; -D MESH_PACKET_LOGGING=1 + ; -D MESH_DEBUG=1 +lib_deps = + ${Tenstar_esp32_C3.lib_deps} + ${esp32_ota.lib_deps} diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index b4f25e53..7408f85d 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -44,6 +44,47 @@ lib_deps = ${Xiao_S3_WIO.lib_deps} ${esp32_ota.lib_deps} +; [env:Xiao_S3_WIO_Repeater_bridge_rs232] +; extends = Xiao_S3_WIO +; build_src_filter = ${Xiao_S3_WIO.build_src_filter} +; + +; +<../examples/simple_repeater/main.cpp> +; build_flags = +; ${Xiao_S3_WIO.build_flags} +; -D ADVERT_NAME='"RS232 Bridge"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=8 +; -D WITH_RS232_BRIDGE=Serial2 +; -D WITH_RS232_BRIDGE_RX=5 +; -D WITH_RS232_BRIDGE_TX=6 +; ; -D MESH_PACKET_LOGGING=1 +; ; -D MESH_DEBUG=1 +; lib_deps = +; ${Xiao_S3_WIO.lib_deps} +; ${esp32_ota.lib_deps} + +[env:Xiao_S3_WIO_Repeater_bridge_espnow] +extends = Xiao_S3_WIO +build_src_filter = ${Xiao_S3_WIO.build_src_filter} + + + +<../examples/simple_repeater/main.cpp> +build_flags = + ${Xiao_S3_WIO.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D WITH_ESPNOW_BRIDGE=1 + -D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_S3_WIO.lib_deps} + ${esp32_ota.lib_deps} + [env:Xiao_S3_WIO_room_server] extends = Xiao_S3_WIO build_src_filter = ${Xiao_S3_WIO.build_src_filter} From a55fa8d8ecc987f2fbdf2f792a6fb62e24099c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Mon, 8 Sep 2025 20:21:33 +0100 Subject: [PATCH 21/21] Add BRIDGE_DELAY as a buffer to prevent immediate processing conflicts in the mesh network --- src/helpers/bridges/BridgeBase.cpp | 4 +++- src/helpers/bridges/BridgeBase.h | 12 ++++++++++-- src/helpers/bridges/RS232Bridge.cpp | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/helpers/bridges/BridgeBase.cpp b/src/helpers/bridges/BridgeBase.cpp index 20551190..03871418 100644 --- a/src/helpers/bridges/BridgeBase.cpp +++ b/src/helpers/bridges/BridgeBase.cpp @@ -1,5 +1,7 @@ #include "BridgeBase.h" +#include + const char *BridgeBase::getLogDateTime() { static char tmp[32]; uint32_t now = _rtc->getCurrentTime(); @@ -27,7 +29,7 @@ bool BridgeBase::validateChecksum(const uint8_t *data, size_t len, uint16_t rece void BridgeBase::handleReceivedPacket(mesh::Packet *packet) { if (!_seen_packets.hasSeen(packet)) { - _mgr->queueInbound(packet, 0); + _mgr->queueInbound(packet, millis() + BRIDGE_DELAY); } else { _mgr->free(packet); } diff --git a/src/helpers/bridges/BridgeBase.h b/src/helpers/bridges/BridgeBase.h index c1764ae3..ab62619f 100644 --- a/src/helpers/bridges/BridgeBase.h +++ b/src/helpers/bridges/BridgeBase.h @@ -23,7 +23,7 @@ public: /** * @brief Common magic number used by all bridge implementations for packet identification - * + * * This magic number is placed at the beginning of bridge packets to identify * them as mesh bridge packets and provide frame synchronization. */ @@ -31,7 +31,7 @@ public: /** * @brief Common field sizes used by bridge implementations - * + * * These constants define the size of common packet fields used across bridges. * BRIDGE_MAGIC_SIZE is used by all bridges for packet identification. * BRIDGE_LENGTH_SIZE is used by bridges that need explicit length fields (like RS232). @@ -41,6 +41,14 @@ public: static constexpr uint16_t BRIDGE_LENGTH_SIZE = sizeof(uint16_t); static constexpr uint16_t BRIDGE_CHECKSUM_SIZE = sizeof(uint16_t); + /** + * @brief Default delay in milliseconds for scheduling inbound packet processing + * + * It provides a buffer to prevent immediate processing conflicts in the mesh network. + * Used in handleReceivedPacket() as: millis() + BRIDGE_DELAY + */ + static constexpr uint16_t BRIDGE_DELAY = 500; // TODO: maybe too high ? + protected: /** Packet manager for allocating and queuing mesh packets */ mesh::PacketManager *_mgr; diff --git a/src/helpers/bridges/RS232Bridge.cpp b/src/helpers/bridges/RS232Bridge.cpp index b209a6da..d182aea6 100644 --- a/src/helpers/bridges/RS232Bridge.cpp +++ b/src/helpers/bridges/RS232Bridge.cpp @@ -54,8 +54,8 @@ void RS232Bridge::onPacketTransmitted(mesh::Packet *packet) { // Build packet header buffer[0] = (BRIDGE_PACKET_MAGIC >> 8) & 0xFF; // Magic high byte buffer[1] = BRIDGE_PACKET_MAGIC & 0xFF; // Magic low byte - buffer[2] = (len >> 8) & 0xFF; // Length high byte - buffer[3] = len & 0xFF; // Length low byte + buffer[2] = (len >> 8) & 0xFF; // Length high byte + buffer[3] = len & 0xFF; // Length low byte // Calculate checksum over the payload uint16_t checksum = fletcher16(buffer + 4, len);