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/48] 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/48] 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/48] 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/48] 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/48] 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/48] 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/48] 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 72b267092f3bdad3dc4d8818f4496b5241689e09 Mon Sep 17 00:00:00 2001 From: recrof Date: Wed, 3 Sep 2025 21:28:46 +0200 Subject: [PATCH 08/48] xiao rp2040 cleanup --- .../xiao_rp2040}/XiaoRP2040Board.cpp | 0 .../xiao_rp2040}/XiaoRP2040Board.h | 15 --------------- variants/xiao_rp2040/platformio.ini | 17 +++++++++++------ variants/xiao_rp2040/target.h | 2 +- 4 files changed, 12 insertions(+), 22 deletions(-) rename {src/helpers/rp2040 => variants/xiao_rp2040}/XiaoRP2040Board.cpp (100%) rename {src/helpers/rp2040 => variants/xiao_rp2040}/XiaoRP2040Board.h (79%) diff --git a/src/helpers/rp2040/XiaoRP2040Board.cpp b/variants/xiao_rp2040/XiaoRP2040Board.cpp similarity index 100% rename from src/helpers/rp2040/XiaoRP2040Board.cpp rename to variants/xiao_rp2040/XiaoRP2040Board.cpp diff --git a/src/helpers/rp2040/XiaoRP2040Board.h b/variants/xiao_rp2040/XiaoRP2040Board.h similarity index 79% rename from src/helpers/rp2040/XiaoRP2040Board.h rename to variants/xiao_rp2040/XiaoRP2040Board.h index c9353906..d2951c75 100644 --- a/src/helpers/rp2040/XiaoRP2040Board.h +++ b/variants/xiao_rp2040/XiaoRP2040Board.h @@ -3,21 +3,6 @@ #include #include -// LoRa radio module pins for the Xiao RP2040 -// https://wiki.seeedstudio.com/XIAO-RP2040/ - -#define P_LORA_DIO_1 27 // D1 -#define P_LORA_NSS 6 // D4 -#define P_LORA_RESET 28 // D2 -#define P_LORA_BUSY 29 // D3 -#define P_LORA_TX_LED 17 - -#define SX126X_RXEN 7 // D5 -#define SX126X_TXEN -1 - -#define SX126X_DIO2_AS_RF_SWITCH true -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - /* * This board has no built-in way to read battery voltage. * Nevertheless it's very easy to make it work, you only require two 1% resistors. diff --git a/variants/xiao_rp2040/platformio.ini b/variants/xiao_rp2040/platformio.ini index 619350ec..48ae3c26 100644 --- a/variants/xiao_rp2040/platformio.ini +++ b/variants/xiao_rp2040/platformio.ini @@ -1,27 +1,32 @@ [Xiao_rp2040] extends = rp2040_base - board = seeed_xiao_rp2040 board_build.filesystem_size = 0.5m - build_flags = ${rp2040_base.build_flags} -I variants/xiao_rp2040 -D SX126X_CURRENT_LIMIT=140 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper - -D LORA_TX_POWER=22 + -D P_LORA_DIO_1=27 ; D1 + -D P_LORA_NSS=6 ; D4 + -D P_LORA_RESET=28 ; D2 + -D P_LORA_BUSY=29 ; D3 + -D P_LORA_TX_LED=17 + -D SX126X_RXEN=7 ; D5 + -D SX126X_TXEN=1 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_RX_BOOSTED_GAIN=1 + -D LORA_TX_POWER=22 ; Debug options ; -D DEBUG_RP2040_WIRE=1 ; -D DEBUG_RP2040_SPI=1 ; -D DEBUG_RP2040_CORE=1 ; -D RADIOLIB_DEBUG_SPI=1 ; -D DEBUG_RP2040_PORT=Serial - build_src_filter = ${rp2040_base.build_src_filter} - + + + +<../variants/xiao_rp2040> - lib_deps = ${rp2040_base.lib_deps} [env:Xiao_rp2040_Repeater] diff --git a/variants/xiao_rp2040/target.h b/variants/xiao_rp2040/target.h index 34861db3..33b3766c 100644 --- a/variants/xiao_rp2040/target.h +++ b/variants/xiao_rp2040/target.h @@ -7,7 +7,7 @@ #include #include #include -#include +#include extern XiaoRP2040Board board; extern WRAPPER_CLASS radio_driver; From c2fc70047a0944b30df0a98304f486b2701c348d Mon Sep 17 00:00:00 2001 From: recrof Date: Wed, 3 Sep 2025 21:37:07 +0200 Subject: [PATCH 09/48] waveshare rp2040 lora cleanup --- .../waveshare_rp2040_lora}/WaveshareBoard.cpp | 0 .../waveshare_rp2040_lora}/WaveshareBoard.h | 12 ------------ variants/waveshare_rp2040_lora/platformio.ini | 18 ++++++++++++------ variants/waveshare_rp2040_lora/target.h | 2 +- 4 files changed, 13 insertions(+), 19 deletions(-) rename {src/helpers/rp2040 => variants/waveshare_rp2040_lora}/WaveshareBoard.cpp (100%) rename {src/helpers/rp2040 => variants/waveshare_rp2040_lora}/WaveshareBoard.h (81%) diff --git a/src/helpers/rp2040/WaveshareBoard.cpp b/variants/waveshare_rp2040_lora/WaveshareBoard.cpp similarity index 100% rename from src/helpers/rp2040/WaveshareBoard.cpp rename to variants/waveshare_rp2040_lora/WaveshareBoard.cpp diff --git a/src/helpers/rp2040/WaveshareBoard.h b/variants/waveshare_rp2040_lora/WaveshareBoard.h similarity index 81% rename from src/helpers/rp2040/WaveshareBoard.h rename to variants/waveshare_rp2040_lora/WaveshareBoard.h index c2b5aff5..694b8bd1 100644 --- a/src/helpers/rp2040/WaveshareBoard.h +++ b/variants/waveshare_rp2040_lora/WaveshareBoard.h @@ -6,18 +6,6 @@ // LoRa radio module pins for Waveshare RP2040-LoRa-HF/LF // https://files.waveshare.com/wiki/RP2040-LoRa/Rp2040-lora-sch.pdf -#define P_LORA_DIO_1 16 -#define P_LORA_NSS 13 // CS -#define P_LORA_RESET 23 -#define P_LORA_BUSY 18 -#define P_LORA_SCLK 14 -#define P_LORA_MISO 24 -#define P_LORA_MOSI 15 -#define P_LORA_TX_LED 25 - -#define SX126X_DIO2_AS_RF_SWITCH true -#define SX126X_DIO3_TCXO_VOLTAGE 0 - /* * This board has no built-in way to read battery voltage. * Nevertheless it's very easy to make it work, you only require two 1% resistors. diff --git a/variants/waveshare_rp2040_lora/platformio.ini b/variants/waveshare_rp2040_lora/platformio.ini index 933c7661..e3c812b6 100644 --- a/variants/waveshare_rp2040_lora/platformio.ini +++ b/variants/waveshare_rp2040_lora/platformio.ini @@ -3,28 +3,34 @@ [waveshare_rp2040_lora] extends = rp2040_base - board = pico board_build.filesystem_size = 0.5m - build_flags = ${rp2040_base.build_flags} -I variants/waveshare_rp2040_lora -D SX126X_CURRENT_LIMIT=140 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper - -D LORA_TX_POWER=22 + -D P_LORA_DIO_1=16 + -D P_LORA_NSS=13 ; CS + -D P_LORA_RESET=23 + -D P_LORA_BUSY=18 + -D P_LORA_SCLK=14 + -D P_LORA_MISO=24 + -D P_LORA_MOSI=15 + -D P_LORA_TX_LED=25 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=0 -D SX126X_RX_BOOSTED_GAIN=1 + -D LORA_TX_POWER=22 ; Debug options ; -D DEBUG_RP2040_WIRE=1 ; -D DEBUG_RP2040_SPI=1 ; -D DEBUG_RP2040_CORE=1 ; -D RADIOLIB_DEBUG_SPI=1 ; -D DEBUG_RP2040_PORT=Serial - build_src_filter = ${rp2040_base.build_src_filter} - + + + +<../variants/waveshare_rp2040_lora> - lib_deps = ${rp2040_base.lib_deps} [env:waveshare_rp2040_lora_Repeater] diff --git a/variants/waveshare_rp2040_lora/target.h b/variants/waveshare_rp2040_lora/target.h index 149b9469..aed55893 100644 --- a/variants/waveshare_rp2040_lora/target.h +++ b/variants/waveshare_rp2040_lora/target.h @@ -7,7 +7,7 @@ #include #include #include -#include +#include extern WaveshareBoard board; extern WRAPPER_CLASS radio_driver; From d8f80f259a962d4437f9bd69840955a02b0c064e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Thu, 4 Sep 2025 13:25:10 +0100 Subject: [PATCH 10/48] Refactor display driver inclusion for Heltec T114 to support configurations without a display --- variants/heltec_t114/NullDisplayDriver.h | 24 ++++++ variants/heltec_t114/platformio.ini | 105 +++++++++++++++++++++-- variants/heltec_t114/target.h | 9 +- 3 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 variants/heltec_t114/NullDisplayDriver.h diff --git a/variants/heltec_t114/NullDisplayDriver.h b/variants/heltec_t114/NullDisplayDriver.h new file mode 100644 index 00000000..2a9670bd --- /dev/null +++ b/variants/heltec_t114/NullDisplayDriver.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class NullDisplayDriver : public DisplayDriver { +public: + NullDisplayDriver() : DisplayDriver(128, 64) { } + bool begin() { return false; } // not present + + bool isOn() override { return false; } + void turnOn() override { } + void turnOff() override { } + void clear() override { } + void startFrame(Color bkg = DARK) override { } + void setTextSize(int sz) override { } + void setColor(Color c) override { } + void setCursor(int x, int y) override { } + void print(const char* str) override { } + void fillRect(int x, int y, int w, int h) override { } + void drawRect(int x, int y, int w, int h) override { } + void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override { } + uint16_t getTextWidth(const char* str) override { return 0; } + void endFrame() { } +}; diff --git a/variants/heltec_t114/platformio.ini b/variants/heltec_t114/platformio.ini index 2cac2eae..e17ac10c 100644 --- a/variants/heltec_t114/platformio.ini +++ b/variants/heltec_t114/platformio.ini @@ -1,3 +1,6 @@ +; +; Heltec T114 without display +; [Heltec_t114] extends = nrf52_base board = heltec_t114 @@ -24,15 +27,11 @@ build_flags = ${nrf52_base.build_flags} -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 + -D DISPLAY_CLASS=NullDisplayDriver -D ST7789 - -D DISPLAY_CLASS=ST7789Display build_src_filter = ${nrf52_base.build_src_filter} + +<../variants/heltec_t114> - + - + - + - + lib_deps = ${nrf52_base.lib_deps} stevemarple/MicroNMEA @ ^2.0.6 @@ -40,7 +39,7 @@ lib_deps = debug_tool = jlink upload_protocol = nrfutil -[env:Heltec_t114_repeater] +[env:Heltec_t114_without_display_repeater] extends = Heltec_t114 build_src_filter = ${Heltec_t114.build_src_filter} +<../examples/simple_repeater> @@ -55,7 +54,7 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -[env:Heltec_t114_room_server] +[env:Heltec_t114_without_display_room_server] extends = Heltec_t114 build_src_filter = ${Heltec_t114.build_src_filter} +<../examples/simple_room_server> @@ -69,7 +68,7 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -[env:Heltec_t114_companion_radio_ble] +[env:Heltec_t114_without_display_companion_radio_ble] extends = Heltec_t114 build_flags = ${Heltec_t114.build_flags} @@ -89,7 +88,7 @@ lib_deps = ${Heltec_t114.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_t114_companion_radio_usb] +[env:Heltec_t114_without_display_companion_radio_usb] extends = Heltec_t114 build_flags = ${Heltec_t114.build_flags} @@ -106,4 +105,92 @@ build_src_filter = ${Heltec_t114.build_src_filter} +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Heltec_t114.lib_deps} + densaugeo/base64 @ ~1.4.0 + +; +; Heltec T114 with ST7789 display +; +[Heltec_t114_with_display] +extends = Heltec_t114 +board = heltec_t114 +board_build.ldscript = boards/nrf52840_s140_v6.ld +build_flags = ${Heltec_t114.build_flags} + -D HELTEC_T114_WITH_DISPLAY + -D DISPLAY_CLASS=ST7789Display +build_src_filter = ${Heltec_t114.build_src_filter} + + + + + + + + +lib_deps = + ${Heltec_t114.lib_deps} +debug_tool = jlink +upload_protocol = nrfutil + +[env:Heltec_t114_repeater] +extends = Heltec_t114_with_display +build_src_filter = ${Heltec_t114_with_display.build_src_filter} + +<../examples/simple_repeater> + +build_flags = + ${Heltec_t114_with_display.build_flags} + -D ADVERT_NAME='"Heltec_T114 Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + +[env:Heltec_t114_room_server] +extends = Heltec_t114_with_display +build_src_filter = ${Heltec_t114_with_display.build_src_filter} + +<../examples/simple_room_server> +build_flags = + ${Heltec_t114_with_display.build_flags} + -D ADVERT_NAME='"Heltec_T114 Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + +[env:Heltec_t114_companion_radio_ble] +extends = Heltec_t114_with_display +build_flags = + ${Heltec_t114_with_display.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 +; -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_t114_with_display.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_t114_with_display.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Heltec_t114_companion_radio_usb] +extends = Heltec_t114_with_display +build_flags = + ${Heltec_t114_with_display.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +; -D BLE_PIN_CODE=123456 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_t114_with_display.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_t114_with_display.lib_deps} densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/variants/heltec_t114/target.h b/variants/heltec_t114/target.h index 053a7c44..3283f009 100644 --- a/variants/heltec_t114/target.h +++ b/variants/heltec_t114/target.h @@ -8,9 +8,14 @@ #include #include #include + #ifdef DISPLAY_CLASS - #include - #include +#include +#ifdef HELTEC_T114_WITH_DISPLAY +#include +#else +#include "NullDisplayDriver.h" +#endif #endif class T114SensorManager : public SensorManager { From 5b2c1715f49891c125f15d82c143d65c03d52e21 Mon Sep 17 00:00:00 2001 From: recrof Date: Thu, 4 Sep 2025 21:45:42 +0200 Subject: [PATCH 11/48] lilygo t-echo cleanup, add AUTO_SHUTDOWN_MILLIVOLTS --- examples/companion_radio/ui-new/UITask.cpp | 10 +- .../lilygo_techo}/TechoBoard.cpp | 0 .../lilygo_techo}/TechoBoard.h | 13 --- .../{techo => lilygo_techo}/platformio.ini | 93 ++++++++++++------- variants/{techo => lilygo_techo}/target.cpp | 0 variants/{techo => lilygo_techo}/target.h | 2 +- variants/{techo => lilygo_techo}/variant.cpp | 0 variants/{techo => lilygo_techo}/variant.h | 0 8 files changed, 63 insertions(+), 55 deletions(-) rename {src/helpers/nrf52 => variants/lilygo_techo}/TechoBoard.cpp (100%) rename {src/helpers/nrf52 => variants/lilygo_techo}/TechoBoard.h (78%) rename variants/{techo => lilygo_techo}/platformio.ini (59%) rename variants/{techo => lilygo_techo}/target.cpp (100%) rename variants/{techo => lilygo_techo}/target.h (95%) rename variants/{techo => lilygo_techo}/variant.cpp (100%) rename variants/{techo => lilygo_techo}/variant.h (100%) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 34ba60e5..0f540878 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -202,8 +202,8 @@ public: display.print(tmp); } else if (_page == HomePage::BLUETOOTH) { display.setColor(DisplayDriver::GREEN); - display.drawXbm((display.width() - 32) / 2, 18, - _task->isSerialEnabled() ? bluetooth_on : bluetooth_off, + display.drawXbm((display.width() - 32) / 2, 18, + _task->isSerialEnabled() ? bluetooth_on : bluetooth_off, 32, 32); display.setTextSize(1); display.drawTextCentered(display.width() / 2, 64 - 11, "toggle: " PRESS_LABEL); @@ -455,8 +455,8 @@ void UITask::setCurrScreen(UIScreen* c) { _next_refresh = 100; } -/* - hardware-agnostic pre-shutdown activity should be done here +/* + hardware-agnostic pre-shutdown activity should be done here */ void UITask::shutdown(bool restart){ @@ -584,7 +584,7 @@ void UITask::loop() { // show low battery shutdown alert // we should only do this for eink displays, which will persist after power loss - #ifdef THINKNODE_M1 + #if defined(THINKNODE_M1) || defined(LILYGO_TECHO) if (_display != NULL) { _display->startFrame(); _display->setTextSize(2); diff --git a/src/helpers/nrf52/TechoBoard.cpp b/variants/lilygo_techo/TechoBoard.cpp similarity index 100% rename from src/helpers/nrf52/TechoBoard.cpp rename to variants/lilygo_techo/TechoBoard.cpp diff --git a/src/helpers/nrf52/TechoBoard.h b/variants/lilygo_techo/TechoBoard.h similarity index 78% rename from src/helpers/nrf52/TechoBoard.h rename to variants/lilygo_techo/TechoBoard.h index 2c05c4ed..4792153a 100644 --- a/src/helpers/nrf52/TechoBoard.h +++ b/variants/lilygo_techo/TechoBoard.h @@ -3,19 +3,6 @@ #include #include -// LoRa radio module pins for LilyGo T-Echo -#define P_LORA_DIO_1 20 -#define P_LORA_NSS 24 -#define P_LORA_RESET 25 -#define P_LORA_BUSY 17 -#define P_LORA_SCLK 19 -#define P_LORA_MISO 23 -#define P_LORA_MOSI 22 -#define SX126X_POWER_EN 37 - -#define SX126X_DIO2_AS_RF_SWITCH true -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - // built-ins #define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096 diff --git a/variants/techo/platformio.ini b/variants/lilygo_techo/platformio.ini similarity index 59% rename from variants/techo/platformio.ini rename to variants/lilygo_techo/platformio.ini index 812baa78..8a090a4d 100644 --- a/variants/techo/platformio.ini +++ b/variants/lilygo_techo/platformio.ini @@ -1,24 +1,26 @@ -[nrf52840_techo] +[LilyGo_T-Echo] extends = nrf52_base -platform_packages = framework-arduinoadafruitnrf52 +board = t-echo +board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} + -I variants/lilygo_techo -I src/helpers/nrf52 -I lib/nrf52/s140_nrf52_6.1.1_API/include -I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52 -lib_deps = - ${nrf52_base.lib_deps} - rweather/Crypto @ ^0.4.0 - -[LilyGo_Techo] -extends = nrf52840_techo -board = t-echo -board_build.ldscript = boards/nrf52840_s140_v6.ld -build_flags = ${nrf52840_techo.build_flags} - -I variants/techo - -DLILYGO_TECHO + -D LILYGO_TECHO -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 + -D P_LORA_DIO_1=20 + -D P_LORA_NSS=24 + -D P_LORA_RESET=25 + -D P_LORA_BUSY=17 + -D P_LORA_SCLK=19 + -D P_LORA_MISO=23 + -D P_LORA_MOSI=22 + -D SX126X_POWER_EN=37 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 -D P_LORA_TX_LED=LED_GREEN @@ -28,23 +30,31 @@ build_flags = ${nrf52840_techo.build_flags} -D GPS_BAUD_RATE=9600 -D PIN_GPS_EN=GPS_EN -D TELEM_BME280_ADDRESS=0x77 -build_src_filter = ${nrf52840_techo.build_src_filter} + -D DISPLAY_CLASS=GxEPDDisplay + -D BACKLIGHT_BTN=PIN_BUTTON2 + -D AUTO_OFF_MILLIS=0 +build_src_filter = ${nrf52_base.build_src_filter} + - + + + + - +<../variants/techo> + + + + + +<../variants/lilygo_techo> lib_deps = - ${nrf52840_techo.lib_deps} + ${nrf52_base.lib_deps} stevemarple/MicroNMEA @ ^2.0.6 adafruit/Adafruit BME280 Library @ ^2.3.0 + zinggjm/GxEPD2 @ 1.6.2 + bakercp/CRC32 @ ^2.0.0 debug_tool = jlink upload_protocol = nrfutil [env:LilyGo_T-Echo_repeater] -extends = LilyGo_Techo -build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_repeater/main.cpp> +extends = LilyGo_T-Echo +build_src_filter = ${LilyGo_T-Echo.build_src_filter} + +<../examples/simple_repeater> build_flags = - ${LilyGo_Techo.build_flags} + ${LilyGo_T-Echo.build_flags} -D ADVERT_NAME='"T-Echo Repeater"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 @@ -54,10 +64,11 @@ build_flags = ; -D MESH_DEBUG=1 [env:LilyGo_T-Echo_room_server] -extends = LilyGo_Techo -build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_room_server/main.cpp> +extends = LilyGo_T-Echo +build_src_filter = ${LilyGo_T-Echo.build_src_filter} + +<../examples/simple_room_server> build_flags = - ${LilyGo_Techo.build_flags} + ${LilyGo_T-Echo.build_flags} -D ADVERT_NAME='"T-Echo Room"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 @@ -66,31 +77,41 @@ build_flags = ; -D MESH_DEBUG=1 [env:LilyGo_T-Echo_companion_radio_ble] -extends = LilyGo_Techo +extends = LilyGo_T-Echo build_flags = - ${LilyGo_Techo.build_flags} + ${LilyGo_T-Echo.build_flags} -I src/helpers/ui -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 - -D DISPLAY_CLASS=GxEPDDisplay -D OFFLINE_QUEUE_SIZE=256 - -D UI_RECENT_LIST_SIZE=9 - -D BACKLIGHT_BTN=PIN_BUTTON2 - -D AUTO_OFF_MILLIS=0 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${LilyGo_Techo.build_src_filter} - + + -D AUTO_SHUTDOWN_MILLIVOLTS=3300 +build_src_filter = ${LilyGo_T-Echo.build_src_filter} + - + - + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-new/*.cpp> lib_deps = - ${LilyGo_Techo.lib_deps} + ${LilyGo_T-Echo.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:LilyGo_T-Echo_companion_radio_usb] +extends = LilyGo_T-Echo +build_flags = + ${LilyGo_T-Echo.build_flags} + -I src/helpers/ui + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D OFFLINE_QUEUE_SIZE=256 + -D UI_RECENT_LIST_SIZE=9 + -D AUTO_SHUTDOWN_MILLIVOLTS=3300 +build_src_filter = ${LilyGo_T-Echo.build_src_filter} + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${LilyGo_T-Echo.lib_deps} densaugeo/base64 @ ~1.4.0 - zinggjm/GxEPD2 @ 1.6.2 - bakercp/CRC32 @ ^2.0.0 diff --git a/variants/techo/target.cpp b/variants/lilygo_techo/target.cpp similarity index 100% rename from variants/techo/target.cpp rename to variants/lilygo_techo/target.cpp diff --git a/variants/techo/target.h b/variants/lilygo_techo/target.h similarity index 95% rename from variants/techo/target.h rename to variants/lilygo_techo/target.h index 134f13b5..2b6ed45f 100644 --- a/variants/techo/target.h +++ b/variants/lilygo_techo/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include #include #include diff --git a/variants/techo/variant.cpp b/variants/lilygo_techo/variant.cpp similarity index 100% rename from variants/techo/variant.cpp rename to variants/lilygo_techo/variant.cpp diff --git a/variants/techo/variant.h b/variants/lilygo_techo/variant.h similarity index 100% rename from variants/techo/variant.h rename to variants/lilygo_techo/variant.h From 816bbf925f1742f701f89bf271df340447e853f8 Mon Sep 17 00:00:00 2001 From: recrof Date: Thu, 4 Sep 2025 23:12:57 +0200 Subject: [PATCH 12/48] t1000 cleanup + move NullDisplayDriver.h to helpers/ui for other variants to use --- .../helpers/ui}/NullDisplayDriver.h | 0 variants/t1000-e/platformio.ini | 22 +- variants/t1000-e/t1000e_sensors.cpp | 194 ++++++++---------- variants/t1000-e/t1000e_sensors.h | 3 +- 4 files changed, 94 insertions(+), 125 deletions(-) rename {variants/t1000-e => src/helpers/ui}/NullDisplayDriver.h (100%) diff --git a/variants/t1000-e/NullDisplayDriver.h b/src/helpers/ui/NullDisplayDriver.h similarity index 100% rename from variants/t1000-e/NullDisplayDriver.h rename to src/helpers/ui/NullDisplayDriver.h diff --git a/variants/t1000-e/platformio.ini b/variants/t1000-e/platformio.ini index b1826139..d43d5bf6 100644 --- a/variants/t1000-e/platformio.ini +++ b/variants/t1000-e/platformio.ini @@ -1,25 +1,13 @@ -; ----------------- NRF52 T1000e--------------------- -[nrf52840_t1000e] +[t1000-e] extends = nrf52_base -platform_packages = framework-arduinoadafruitnrf52 +board = tracker-t1000-e +board_build.ldscript = boards/nrf52840_s140_v7.ld build_flags = ${nrf52_base.build_flags} -I src/helpers/nrf52 -I lib/nrf52/s140_nrf52_7.3.0_API/include -I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52 -lib_ignore = - BluetoothOTA - lvgl - lib5b4 -lib_deps = - ${nrf52_base.lib_deps} - rweather/Crypto @ ^0.4.0 - -[t1000-e] -extends = nrf52840_t1000e -board = tracker-t1000-e -board_build.ldscript = boards/nrf52840_s140_v7.ld -build_flags = ${nrf52840_t1000e.build_flags} -I variants/t1000-e + -I src/helpers/ui -D T1000_E -D PIN_USER_BTN=6 -D USER_BTN_PRESSED=HIGH @@ -27,7 +15,7 @@ build_flags = ${nrf52840_t1000e.build_flags} -D RADIO_CLASS=CustomLR1110 -D WRAPPER_CLASS=CustomLR1110Wrapper -D LORA_TX_POWER=22 -build_src_filter = ${nrf52840_t1000e.build_src_filter} +build_src_filter = ${nrf52_base.build_src_filter} + + +<../variants/t1000-e> diff --git a/variants/t1000-e/t1000e_sensors.cpp b/variants/t1000-e/t1000e_sensors.cpp index 17693022..a5b443cf 100644 --- a/variants/t1000-e/t1000e_sensors.cpp +++ b/variants/t1000-e/t1000e_sensors.cpp @@ -1,119 +1,99 @@ -#include #include "t1000e_sensors.h" -#define HEATER_NTC_BX 4250 // thermistor coefficient B -#define HEATER_NTC_RP 8250 // ohm, series resistance to thermistor -#define HEATER_NTC_KA 273.15 // 25 Celsius at Kelvin -#define NTC_REF_VCC 3000 // mV, output voltage of LDO -#define LIGHT_REF_VCC 2400 // - -static unsigned int ntc_res2[136]={ - 113347,107565,102116,96978,92132,87559,83242,79166,75316,71677, - 68237,64991,61919,59011,56258,53650,51178,48835,46613,44506, - 42506,40600,38791,37073,35442,33892,32420,31020,29689,28423, - 27219,26076,24988,23951,22963,22021,21123,20267,19450,18670, - 17926,17214,16534,15886,15266,14674,14108,13566,13049,12554, - 12081,11628,11195,10780,10382,10000,9634,9284,8947,8624, - 8315,8018,7734,7461,7199,6948,6707,6475,6253,6039, - 5834,5636,5445,5262,5086,4917,4754,4597,4446,4301, - 4161,4026,3896,3771,3651,3535,3423,3315,3211,3111, - 3014,2922,2834,2748,2666,2586,2509,2435,2364,2294, - 2228,2163,2100,2040,1981,1925,1870,1817,1766,1716, - 1669,1622,1578,1535,1493,1452,1413,1375,1338,1303, - 1268,1234,1202,1170,1139,1110,1081,1053,1026,999, - 974,949,925,902,880,858, -}; - -static char ntc_temp2[136]= -{ - -30,-29,-28,-27,-26,-25,-24,-23,-22,-21, - -20,-19,-18,-17,-16,-15,-14,-13,-12,-11, - -10,-9,-8,-7,-6,-5,-4,-3,-2,-1, - 0,1,2,3,4,5,6,7,8,9, - 10,11,12,13,14,15,16,17,18,19, - 20,21,22,23,24,25,26,27,28,29, - 30,31,32,33,34,35,36,37,38,39, - 40,41,42,43,44,45,46,47,48,49, - 50,51,52,53,54,55,56,57,58,59, - 60,61,62,63,64,65,66,67,68,69, - 70,71,72,73,74,75,76,77,78,79, - 80,81,82,83,84,85,86,87,88,89, - 90,91,92,93,94,95,96,97,98,99, - 100,101,102,103,104,105, +#include + +#define HEATER_NTC_BX 4250 // thermistor coefficient B +#define HEATER_NTC_RP 8250 // ohm, series resistance to thermistor +#define HEATER_NTC_KA 273.15 // 25 Celsius at Kelvin +#define NTC_REF_VCC 3000 // mV, output voltage of LDO +#define LIGHT_REF_VCC 2400 // + +static unsigned int ntc_res2[136] = { + 113347, 107565, 102116, 96978, 92132, 87559, 83242, 79166, 75316, 71677, 68237, 64991, 61919, 59011, + 56258, 53650, 51178, 48835, 46613, 44506, 42506, 40600, 38791, 37073, 35442, 33892, 32420, 31020, + 29689, 28423, 27219, 26076, 24988, 23951, 22963, 22021, 21123, 20267, 19450, 18670, 17926, 17214, + 16534, 15886, 15266, 14674, 14108, 13566, 13049, 12554, 12081, 11628, 11195, 10780, 10382, 10000, + 9634, 9284, 8947, 8624, 8315, 8018, 7734, 7461, 7199, 6948, 6707, 6475, 6253, 6039, + 5834, 5636, 5445, 5262, 5086, 4917, 4754, 4597, 4446, 4301, 4161, 4026, 3896, 3771, + 3651, 3535, 3423, 3315, 3211, 3111, 3014, 2922, 2834, 2748, 2666, 2586, 2509, 2435, + 2364, 2294, 2228, 2163, 2100, 2040, 1981, 1925, 1870, 1817, 1766, 1716, 1669, 1622, + 1578, 1535, 1493, 1452, 1413, 1375, 1338, 1303, 1268, 1234, 1202, 1170, 1139, 1110, + 1081, 1053, 1026, 999, 974, 949, 925, 902, 880, 858, }; -static float get_heater_temperature( unsigned int vcc_volt, unsigned int ntc_volt ) -{ - int i = 0; - float Vout = 0, Rt = 0, temp = 0; - Vout = ntc_volt; - - Rt = ( HEATER_NTC_RP * vcc_volt ) / Vout - HEATER_NTC_RP; - - for( i = 0; i < 136; i++ ) - { - if( Rt >= ntc_res2[i] ) - { - break; - } +static char ntc_temp2[136] = { + -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, + -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, +}; + +static float get_heater_temperature(unsigned int vcc_volt, unsigned int ntc_volt) { + int i = 0; + float Vout = 0, Rt = 0, temp = 0; + Vout = ntc_volt; + + Rt = (HEATER_NTC_RP * vcc_volt) / Vout - HEATER_NTC_RP; + + for (i = 0; i < 136; i++) { + if (Rt >= ntc_res2[i]) { + break; } - - temp = ntc_temp2[i - 1] + 1 * ( ntc_res2[i - 1] - Rt ) / ( float )( ntc_res2[i - 1] - ntc_res2[i] ); - - temp = ( temp * 100 + 5 ) / 100; - return temp; + } + + temp = ntc_temp2[i - 1] + 1 * (ntc_res2[i - 1] - Rt) / (float)(ntc_res2[i - 1] - ntc_res2[i]); + + temp = (temp * 100 + 5) / 100; + return temp; } -static int get_light_lv( unsigned int light_volt ) -{ - float Vout = 0, Vin = 0, Rt = 0, temp = 0; - unsigned int light_level = 0; - - if( light_volt <= 80 ) - { - light_level = 0; - return light_level; - } - else if( light_volt >= 2480 ) - { - light_level = 100; - return light_level; - } - Vout = light_volt; - light_level = 100 * ( Vout - 80 ) / LIGHT_REF_VCC; - +static int get_light_lv(unsigned int light_volt) { + float Vout = 0, Vin = 0, Rt = 0, temp = 0; + unsigned int light_level = 0; + + if (light_volt <= 80) { + light_level = 0; return light_level; -} + } else if (light_volt >= 2480) { + light_level = 100; + return light_level; + } + Vout = light_volt; + light_level = 100 * (Vout - 80) / LIGHT_REF_VCC; -float t1000e_get_temperature( void ) -{ - unsigned int ntc_v, vcc_v; - - digitalWrite(PIN_3V3_EN, HIGH); - digitalWrite(SENSOR_EN, HIGH); - analogReference(AR_INTERNAL_3_0); - analogReadResolution(12); - delay(10); - vcc_v = (1000.0*(analogRead(BATTERY_PIN) * ADC_MULTIPLIER * AREF_VOLTAGE)) / 4096; - ntc_v = (1000.0 * AREF_VOLTAGE * analogRead(TEMP_SENSOR)) / 4096; - digitalWrite(PIN_3V3_EN, LOW); - digitalWrite(SENSOR_EN, LOW); - - return get_heater_temperature (vcc_v, ntc_v); + return light_level; } -uint32_t t1000e_get_light( void ) -{ - int lux = 0; - unsigned int lux_v = 0; - - digitalWrite(SENSOR_EN, HIGH); - analogReference(AR_INTERNAL_3_0); - analogReadResolution(12); - delay(10); - lux_v = 1000 * analogRead(LUX_SENSOR) * AREF_VOLTAGE / 4096; - lux = get_light_lv( lux_v ); - digitalWrite(SENSOR_EN, LOW); - - return lux; +float t1000e_get_temperature(void) { + unsigned int ntc_v, vcc_v; + + digitalWrite(PIN_3V3_EN, HIGH); + digitalWrite(SENSOR_EN, HIGH); + analogReference(AR_INTERNAL_3_0); + analogReadResolution(12); + delay(10); + vcc_v = (1000.0 * (analogRead(BATTERY_PIN) * ADC_MULTIPLIER * AREF_VOLTAGE)) / 4096; + ntc_v = (1000.0 * AREF_VOLTAGE * analogRead(TEMP_SENSOR)) / 4096; + digitalWrite(PIN_3V3_EN, LOW); + digitalWrite(SENSOR_EN, LOW); + + return get_heater_temperature(vcc_v, ntc_v); +} + +uint32_t t1000e_get_light(void) { + int lux = 0; + unsigned int lux_v = 0; + + digitalWrite(SENSOR_EN, HIGH); + analogReference(AR_INTERNAL_3_0); + analogReadResolution(12); + delay(10); + lux_v = 1000 * analogRead(LUX_SENSOR) * AREF_VOLTAGE / 4096; + lux = get_light_lv(lux_v); + digitalWrite(SENSOR_EN, LOW); + + return lux; } \ No newline at end of file diff --git a/variants/t1000-e/t1000e_sensors.h b/variants/t1000-e/t1000e_sensors.h index 8f3967ca..e8f790b6 100644 --- a/variants/t1000-e/t1000e_sensors.h +++ b/variants/t1000-e/t1000e_sensors.h @@ -1,7 +1,8 @@ #pragma once +#include // Light and temperature sensors are on ADC ports -// functions adapted from Seeed examples to get values +// functions adapted from Seeed examples to get values // see : https://github.com/Seeed-Studio/Seeed-Tracker-T1000-E-for-LoRaWAN-dev-board extern uint32_t t1000e_get_light(); From 9b9c7289e67abb75670cb688f2e70f03691e9b91 Mon Sep 17 00:00:00 2001 From: recrof Date: Thu, 4 Sep 2025 23:31:05 +0200 Subject: [PATCH 13/48] moved pindefs from board to platformio.ini --- variants/t1000-e/T1000eBoard.h | 20 ++------------------ variants/t1000-e/platformio.ini | 18 +++++++++++------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/variants/t1000-e/T1000eBoard.h b/variants/t1000-e/T1000eBoard.h index f87d71e0..090c1b90 100644 --- a/variants/t1000-e/T1000eBoard.h +++ b/variants/t1000-e/T1000eBoard.h @@ -3,22 +3,6 @@ #include #include -// LoRa and SPI pins -#define P_LORA_DIO_1 (32 + 1) // P1.1 -#define P_LORA_NSS (0 + 12) // P0.12 -#define P_LORA_RESET (32 + 10) // P1.10 -#define P_LORA_BUSY (0 + 7) // P0.7 -#define P_LORA_SCLK (0 + 11) // P0.11 -#define P_LORA_MISO (32 + 8) // P1.8 -#define P_LORA_MOSI (32 + 9) // P0.9 - -#define LR11X0_DIO_AS_RF_SWITCH true -#define LR11X0_DIO3_TCXO_VOLTAGE 1.6 - -// built-ins -//#define PIN_VBAT_READ 5 -//#define ADC_MULTIPLIER (3 * 1.73 * 1000) - class T1000eBoard : public mesh::MainBoard { protected: uint8_t startup_reason; @@ -75,11 +59,11 @@ public: pinMode(GPS_RESETB, OUTPUT); digitalWrite(GPS_RESETB, LOW); #endif - + #ifdef BUZZER_EN digitalWrite(BUZZER_EN, LOW); #endif - + #ifdef PIN_3V3_EN digitalWrite(PIN_3V3_EN, LOW); #endif diff --git a/variants/t1000-e/platformio.ini b/variants/t1000-e/platformio.ini index d43d5bf6..044fffd8 100644 --- a/variants/t1000-e/platformio.ini +++ b/variants/t1000-e/platformio.ini @@ -15,6 +15,17 @@ build_flags = ${nrf52_base.build_flags} -D RADIO_CLASS=CustomLR1110 -D WRAPPER_CLASS=CustomLR1110Wrapper -D LORA_TX_POWER=22 + -D RF_SWITCH_TABLE + -D RX_BOOSTED_GAIN=true + -D P_LORA_BUSY=7 ; P0.7 + -D P_LORA_SCLK=11 ; P0.11 + -D P_LORA_NSS=12 ; P0.12 + -D P_LORA_DIO_1=33 ; P1.1 + -D P_LORA_MISO=40 ; P1.8 + -D P_LORA_MOSI=41 ; P0.9 + -D P_LORA_RESET=42 ; P1.10 + -D LR11X0_DIO_AS_RF_SWITCH=true + -D LR11X0_DIO3_TCXO_VOLTAGE=1.6 build_src_filter = ${nrf52_base.build_src_filter} + + @@ -33,8 +44,6 @@ build_flags = ${t1000-e.build_flags} -D MAX_NEIGHBOURS=8 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 - -D RX_BOOSTED_GAIN=true - -D RF_SWITCH_TABLE build_src_filter = ${t1000-e.build_src_filter} +<../examples/simple_repeater> lib_deps = ${t1000-e.lib_deps} @@ -51,7 +60,6 @@ build_flags = ${t1000-e.build_flags} -D ROOM_PASSWORD='"hello"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 - -D RX_BOOSTED_GAIN=true -D RF_SWITCH_TABLE build_src_filter = ${t1000-e.build_src_filter} +<../examples/simple_room_server> @@ -67,8 +75,6 @@ build_flags = ${t1000-e.build_flags} ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -D OFFLINE_QUEUE_SIZE=256 - -D RX_BOOSTED_GAIN=true - -D RF_SWITCH_TABLE -D DISPLAY_CLASS=NullDisplayDriver -D PIN_BUZZER=25 -D PIN_BUZZER_EN=37 ; P1/5 - required for T1000-E @@ -92,8 +98,6 @@ build_flags = ${t1000-e.build_flags} ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -D OFFLINE_QUEUE_SIZE=256 - -D RX_BOOSTED_GAIN=true - -D RF_SWITCH_TABLE -D DISPLAY_CLASS=NullDisplayDriver -D PIN_BUZZER=25 -D PIN_BUZZER_EN=37 ; P1/5 - required for T1000-E 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 14/48] 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 15/48] 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 16/48] 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 17/48] 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 18/48] 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 963556f9ba85c7334ffd4e489ff44ed7479f43b9 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Fri, 5 Sep 2025 10:46:51 +0200 Subject: [PATCH 19/48] Updated BLE functionality for low power mode in SerialBLEInterface. Updated platformio.ini to enable low power mode and added DC/DC converter support in T1000eBoard for improved power efficiency. --- src/helpers/nrf52/SerialBLEInterface.cpp | 32 +++++++++++++++++++++--- variants/t1000-e/T1000eBoard.cpp | 3 +++ variants/t1000-e/platformio.ini | 1 + 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/helpers/nrf52/SerialBLEInterface.cpp b/src/helpers/nrf52/SerialBLEInterface.cpp index 170a7331..283fe405 100644 --- a/src/helpers/nrf52/SerialBLEInterface.cpp +++ b/src/helpers/nrf52/SerialBLEInterface.cpp @@ -25,10 +25,28 @@ void SerialBLEInterface::begin(const char* device_name, uint32_t pin_code) { char charpin[20]; sprintf(charpin, "%d", pin_code); - Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); - Bluefruit.configPrphConn(250, BLE_GAP_EVENT_LENGTH_MIN, 16, 16); // increase MTU + Bluefruit.configPrphBandwidth( +#ifdef BLE_LOW_POWER + BANDWIDTH_NORMAL +#else + BANDWIDTH_MAX +#endif + ); + Bluefruit.configPrphConn( +#ifdef BLE_LOW_POWER + 400, BLE_GAP_EVENT_LENGTH_MIN, 8, 8 +#else + 250, BLE_GAP_EVENT_LENGTH_MIN, 16, 16 // increase MTU +#endif + ); Bluefruit.begin(); - Bluefruit.setTxPower(4); // Check bluefruit.h for supported values + Bluefruit.setTxPower( +#ifdef BLE_LOW_POWER + 0 +#else + 4 +#endif + ); // Check bluefruit.h for supported values Bluefruit.setName(device_name); Bluefruit.Security.setMITM(true); @@ -80,7 +98,13 @@ void SerialBLEInterface::startAdv() { * https://developer.apple.com/library/content/qa/qa1931/_index.html */ Bluefruit.Advertising.restartOnDisconnect(false); // don't restart automatically as we handle it in onDisconnect - Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms + Bluefruit.Advertising.setInterval( +#ifdef BLE_LOW_POWER + 160, 1600 +#else + 32, 244 +#endif + ); // in unit of 0.625 ms Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds diff --git a/variants/t1000-e/T1000eBoard.cpp b/variants/t1000-e/T1000eBoard.cpp index a17711db..4bcdf98a 100644 --- a/variants/t1000-e/T1000eBoard.cpp +++ b/variants/t1000-e/T1000eBoard.cpp @@ -11,6 +11,9 @@ void T1000eBoard::begin() { sd_power_mode_set(NRF_POWER_MODE_LOWPWR); + // Enable DC/DC converter for improved power efficiency + NRF_POWER->DCDCEN = 1; + #ifdef BUTTON_PIN pinMode(BATTERY_PIN, INPUT); pinMode(BUTTON_PIN, INPUT); diff --git a/variants/t1000-e/platformio.ini b/variants/t1000-e/platformio.ini index b1826139..88c0b45d 100644 --- a/variants/t1000-e/platformio.ini +++ b/variants/t1000-e/platformio.ini @@ -100,6 +100,7 @@ build_flags = ${t1000-e.build_flags} -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 + -D BLE_LOW_POWER=1 ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 From c9e6ae9e6c907a4fcf0bf1917ede33754b508c51 Mon Sep 17 00:00:00 2001 From: recrof Date: Fri, 5 Sep 2025 11:12:17 +0200 Subject: [PATCH 20/48] fix typo in pin configuration --- variants/xiao_rp2040/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/xiao_rp2040/platformio.ini b/variants/xiao_rp2040/platformio.ini index 48ae3c26..6c9c70f6 100644 --- a/variants/xiao_rp2040/platformio.ini +++ b/variants/xiao_rp2040/platformio.ini @@ -13,7 +13,7 @@ build_flags = ${rp2040_base.build_flags} -D P_LORA_BUSY=29 ; D3 -D P_LORA_TX_LED=17 -D SX126X_RXEN=7 ; D5 - -D SX126X_TXEN=1 + -D SX126X_TXEN=RADIOLIB_NC -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_RX_BOOSTED_GAIN=1 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 21/48] 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 22/48] 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 d59724acd05bbcad41cbfb1abfdf7c5678053af5 Mon Sep 17 00:00:00 2001 From: recrof Date: Fri, 5 Sep 2025 16:21:19 +0200 Subject: [PATCH 23/48] new variant: RAK WisMesh Tag --- .../sensors/EnvironmentSensorManager.cpp | 34 ++--- .../rak_wismesh_tag/RAKWismeshTagBoard.cpp | 81 ++++++++++++ variants/rak_wismesh_tag/RAKWismeshTagBoard.h | 80 ++++++++++++ variants/rak_wismesh_tag/platformio.ini | 116 +++++++++++++++++ variants/rak_wismesh_tag/target.cpp | 54 ++++++++ variants/rak_wismesh_tag/target.h | 27 ++++ variants/rak_wismesh_tag/variant.cpp | 21 +++ variants/rak_wismesh_tag/variant.h | 121 ++++++++++++++++++ 8 files changed, 518 insertions(+), 16 deletions(-) create mode 100644 variants/rak_wismesh_tag/RAKWismeshTagBoard.cpp create mode 100644 variants/rak_wismesh_tag/RAKWismeshTagBoard.h create mode 100644 variants/rak_wismesh_tag/platformio.ini create mode 100644 variants/rak_wismesh_tag/target.cpp create mode 100644 variants/rak_wismesh_tag/target.h create mode 100644 variants/rak_wismesh_tag/variant.cpp create mode 100644 variants/rak_wismesh_tag/variant.h diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index f444b67b..df08ed78 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -66,8 +66,8 @@ static Adafruit_INA260 INA260; #endif #if ENV_INCLUDE_INA226 -#define TELEM_INA226_ADDRESS 0x44 -#define TELEM_INA226_SHUNT_VALUE 0.100 +#define TELEM_INA226_ADDRESS 0x44 +#define TELEM_INA226_SHUNT_VALUE 0.100 #define TELEM_INA226_MAX_AMP 0.8 #include static INA226 INA226(TELEM_INA226_ADDRESS); @@ -85,7 +85,11 @@ static Adafruit_MLX90614 MLX90614; static Adafruit_VL53L0X VL53L0X; #endif -#if ENV_INCLUDE_GPS && RAK_BOARD +#if ENV_INCLUDE_GPS && defined(RAK_BOARD) && !defined(RAK_WISMESH_TAG) +#define RAK_WISBLOCK_GPS +#endif + +#ifdef RAK_WISBLOCK_GPS static uint32_t gpsResetPin = 0; static bool i2cGPSFlag = false; static bool serialGPSFlag = false; @@ -96,7 +100,7 @@ static SFE_UBLOX_GNSS ublox_GNSS; bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_GPS - #if RAK_BOARD + #ifdef RAK_WISBLOCK_GPS rakGPSInit(); //probe base board/sockets for GPS #else initBasicGPS(); @@ -457,7 +461,7 @@ void EnvironmentSensorManager::initBasicGPS() { gps_active = false; //Set GPS visibility off until setting is changed } -#ifdef RAK_BOARD +#ifdef RAK_WISBLOCK_GPS void EnvironmentSensorManager::rakGPSInit(){ Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX); @@ -531,7 +535,7 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ void EnvironmentSensorManager::start_gps() { gps_active = true; - #ifdef RAK_BOARD + #ifdef RAK_WISBLOCK_GPS pinMode(gpsResetPin, OUTPUT); digitalWrite(gpsResetPin, HIGH); return; @@ -547,7 +551,7 @@ void EnvironmentSensorManager::start_gps() { void EnvironmentSensorManager::stop_gps() { gps_active = false; - #ifdef RAK_BOARD + #ifdef RAK_WISBLOCK_GPS pinMode(gpsResetPin, OUTPUT); digitalWrite(gpsResetPin, LOW); return; @@ -568,13 +572,7 @@ void EnvironmentSensorManager::loop() { if (millis() > next_gps_update) { if(gps_active){ - #ifndef RAK_BOARD - if (_location->isValid()) { - node_lat = ((double)_location->getLatitude())/1000000.; - node_lon = ((double)_location->getLongitude())/1000000.; - MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); - } - #else + #ifdef RAK_WISBLOCK_GPS if(i2cGPSFlag){ node_lat = ((double)ublox_GNSS.getLatitude())/10000000.; node_lon = ((double)ublox_GNSS.getLongitude())/10000000.; @@ -585,8 +583,12 @@ void EnvironmentSensorManager::loop() { node_lon = ((double)_location->getLongitude())/1000000.; MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); } - //else - //MESH_DEBUG_PRINTLN("No valid GPS data"); + #else + if (_location->isValid()) { + node_lat = ((double)_location->getLatitude())/1000000.; + node_lon = ((double)_location->getLongitude())/1000000.; + MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); + } #endif } next_gps_update = millis() + 1000; diff --git a/variants/rak_wismesh_tag/RAKWismeshTagBoard.cpp b/variants/rak_wismesh_tag/RAKWismeshTagBoard.cpp new file mode 100644 index 00000000..68ce2fd8 --- /dev/null +++ b/variants/rak_wismesh_tag/RAKWismeshTagBoard.cpp @@ -0,0 +1,81 @@ +#include +#include "RAKWismeshTagBoard.h" + +#include +#include + +static BLEDfu bledfu; + +static void connect_callback(uint16_t conn_handle) { + (void)conn_handle; + MESH_DEBUG_PRINTLN("BLE client connected"); +} + +static void disconnect_callback(uint16_t conn_handle, uint8_t reason) { + (void)conn_handle; + (void)reason; + + MESH_DEBUG_PRINTLN("BLE client disconnected"); +} + +void RAKWismeshTagBoard::begin() { + // for future use, sub-classes SHOULD call this from their begin() + startup_reason = BD_STARTUP_NORMAL; + pinMode(PIN_VBAT_READ, INPUT); + pinMode(PIN_USER_BTN, INPUT_PULLUP); + + Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL); + Wire.begin(); + + pinMode(SX126X_POWER_EN, OUTPUT); + digitalWrite(SX126X_POWER_EN, HIGH); + delay(10); // give sx1262 some time to power up +} + +bool RAKWismeshTagBoard::startOTAUpdate(const char* id, char reply[]) { + // Config the peripheral connection with maximum bandwidth + // more SRAM required by SoftDevice + // Note: All config***() function must be called before begin() + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16); + + Bluefruit.begin(1, 0); + // Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4 + Bluefruit.setTxPower(4); + // Set the BLE device name + Bluefruit.setName("WISMESHTAG_OTA"); + + Bluefruit.Periph.setConnectCallback(connect_callback); + Bluefruit.Periph.setDisconnectCallback(disconnect_callback); + + // To be consistent OTA DFU should be added first if it exists + bledfu.begin(); + + // Set up and start advertising + // Advertising packet + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + Bluefruit.Advertising.addName(); + + /* Start Advertising + - Enable auto advertising if disconnected + - Interval: fast mode = 20 ms, slow mode = 152.5 ms + - Timeout for fast mode is 30 seconds + - Start(timeout) with timeout = 0 will advertise forever (until connected) + + For recommended advertising interval + https://developer.apple.com/library/content/qa/qa1931/_index.html + */ + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms + Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode + Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds + + uint8_t mac_addr[6]; + memset(mac_addr, 0, sizeof(mac_addr)); + Bluefruit.getAddr(mac_addr); + sprintf(reply, "OK - mac: %02X:%02X:%02X:%02X:%02X:%02X", + mac_addr[5], mac_addr[4], mac_addr[3], mac_addr[2], mac_addr[1], mac_addr[0]); + + return true; +} diff --git a/variants/rak_wismesh_tag/RAKWismeshTagBoard.h b/variants/rak_wismesh_tag/RAKWismeshTagBoard.h new file mode 100644 index 00000000..22af6f74 --- /dev/null +++ b/variants/rak_wismesh_tag/RAKWismeshTagBoard.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include + +// built-ins +#define PIN_VBAT_READ 5 +#define ADC_MULTIPLIER (3 * 1.73 * 1.187 * 1000) + +class RAKWismeshTagBoard : public mesh::MainBoard { +protected: + uint8_t startup_reason; + +public: + void begin(); + uint8_t getStartupReason() const override { return startup_reason; } + +#if defined(P_LORA_TX_LED) && defined(LED_STATE_ON) + void onBeforeTransmit() override { + digitalWrite(P_LORA_TX_LED, LED_STATE_ON); // turn TX LED on + } + void onAfterTransmit() override { + digitalWrite(P_LORA_TX_LED, !LED_STATE_ON); // turn TX LED off + } +#endif + + #define BATTERY_SAMPLES 8 + + uint16_t getBattMilliVolts() override { + analogReadResolution(12); + + uint32_t raw = 0; + for (int i = 0; i < BATTERY_SAMPLES; i++) { + raw += analogRead(PIN_VBAT_READ); + } + raw = raw / BATTERY_SAMPLES; + + return (ADC_MULTIPLIER * raw) / 4096; + } + + const char* getManufacturerName() const override { + return "RAK WisMesh Tag"; + } + + void reboot() override { + NVIC_SystemReset(); + } + + bool startOTAUpdate(const char* id, char reply[]) override; + + void powerOff() override { + #ifdef BUZZER_EN + digitalWrite(BUZZER_EN, LOW); + #endif + + #ifdef PIN_GPS_EN + digitalWrite(PIN_GPS_EN, LOW); + #endif + + // set led on and wait for button release before poweroff + #ifdef LED_PIN + digitalWrite(LED_PIN, HIGH); + #endif + #ifdef BUTTON_PIN + while(digitalRead(BUTTON_PIN)); + #endif + #ifdef LED_GREEN + digitalWrite(LED_GREEN, LOW); + #endif + #ifdef LED_BLUE + digitalWrite(LED_BLUE, LOW); + #endif + + #ifdef BUTTON_PIN + nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH); + #endif + + sd_power_system_off(); + } +}; diff --git a/variants/rak_wismesh_tag/platformio.ini b/variants/rak_wismesh_tag/platformio.ini new file mode 100644 index 00000000..6f7d28ba --- /dev/null +++ b/variants/rak_wismesh_tag/platformio.ini @@ -0,0 +1,116 @@ +[rak_wismesh_tag] +extends = nrf52_base +platform = https://github.com/maxgerhardt/platform-nordicnrf52.git#rak +board = wiscore_rak4631 +board_check = true +build_flags = ${nrf52_base.build_flags} + ${sensor_base.build_flags} + -I variants/rak_wismesh_tag + -I src/helpers/ui + -D RAK_WISMESH_TAG + -D RAK_BOARD + -D P_LORA_TX_LED=LED_GREEN + -D P_LORA_DIO_1=SX126X_DIO1 + -D P_LORA_NSS=PIN_SPI_NSS + -D P_LORA_RESET=SX126X_RESET + -D P_LORA_BUSY=SX126X_BUSY + -D P_LORA_SCLK=PIN_SPI_SCK + -D P_LORA_MISO=PIN_SPI_MISO + -D P_LORA_MOSI=PIN_SPI_MOSI + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D DISPLAY_CLASS=NullDisplayDriver + -D LORA_TX_POWER=22 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + -D PIN_BUZZER=21 + -D PIN_BOARD_SDA=PIN_WIRE_SDA + -D PIN_BOARD_SCL=PIN_WIRE_SCL +build_src_filter = ${nrf52_base.build_src_filter} + +<../variants/rak_wismesh_tag> + + + + + + + + +lib_deps = + ${nrf52_base.lib_deps} + ${sensor_base.lib_deps} + +[env:RAK_WisMesh_Tag_Repeater] +extends = rak_wismesh_tag +build_flags = + ${rak_wismesh_tag.build_flags} + -D ADVERT_NAME='"RAK WM Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${rak_wismesh_tag.build_src_filter} + +<../examples/simple_repeater> + +[env:RAK_WisMesh_Tag_room_server] +extends = rak_wismesh_tag +build_flags = + ${rak_wismesh_tag.build_flags} + -D ADVERT_NAME='"RAK WM Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${rak_wismesh_tag.build_src_filter} + +<../examples/simple_room_server> + +[env:RAK_WisMesh_Tag_companion_radio_usb] +extends = rak_wismesh_tag +build_flags = + ${rak_wismesh_tag.build_flags} + -I examples/companion_radio/ui-orig + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 +; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 +build_src_filter = ${rak_wismesh_tag.build_src_filter} + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-orig/*.cpp> +lib_deps = + ${rak_wismesh_tag.lib_deps} + densaugeo/base64 @ ~1.4.0 + end2endzone/NonBlockingRTTTL@^1.3.0 + +[env:RAK_WisMesh_Tag_companion_radio_ble] +extends = rak_wismesh_tag +build_flags = + ${rak_wismesh_tag.build_flags} + -I examples/companion_radio/ui-orig + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 + -D MESH_DEBUG=1 +build_src_filter = ${rak_wismesh_tag.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-orig/*.cpp> +lib_deps = + ${rak4631.lib_deps} + densaugeo/base64 @ ~1.4.0 + end2endzone/NonBlockingRTTTL@^1.3.0 + +[env:RAK_WisMesh_Tag_sensor] +extends = rak4631 +build_flags = + ${rak4631.build_flags} + -D ADVERT_NAME='"RAK WM Sensor"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' +; -D MESH_PACKET_LOGGING=1 + -D MESH_DEBUG=1 +build_src_filter = ${rak4631.build_src_filter} + +<../examples/simple_sensor> \ No newline at end of file diff --git a/variants/rak_wismesh_tag/target.cpp b/variants/rak_wismesh_tag/target.cpp new file mode 100644 index 00000000..2bd30864 --- /dev/null +++ b/variants/rak_wismesh_tag/target.cpp @@ -0,0 +1,54 @@ +#include +#include "target.h" +#include + +RAKWismeshTagBoard board; + +#ifndef PIN_USER_BTN + #define PIN_USER_BTN (-1) +#endif + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true); +#endif + +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); + +WRAPPER_CLASS radio_driver(radio, board); + +VolatileRTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +#if ENV_INCLUDE_GPS + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else + EnvironmentSensorManager sensors; +#endif + +bool radio_init() { + rtc_clock.begin(Wire); + return radio.std_init(&SPI); +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/rak_wismesh_tag/target.h b/variants/rak_wismesh_tag/target.h new file mode 100644 index 00000000..150d0831 --- /dev/null +++ b/variants/rak_wismesh_tag/target.h @@ -0,0 +1,27 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include + +#ifdef DISPLAY_CLASS + #include + extern DISPLAY_CLASS display; + #include + extern MomentaryButton user_btn; +#endif + +extern RAKWismeshTagBoard board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/rak_wismesh_tag/variant.cpp b/variants/rak_wismesh_tag/variant.cpp new file mode 100644 index 00000000..ac3af5d2 --- /dev/null +++ b/variants/rak_wismesh_tag/variant.cpp @@ -0,0 +1,21 @@ +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 +}; + +void initVariant() { + // LEDs + pinMode(LED_BLUE, OUTPUT); + pinMode(LED_GREEN, OUTPUT); + digitalWrite(LED_BLUE, LOW); + digitalWrite(LED_GREEN, LOW); + + // GPS + pinMode(PIN_GPS_EN, OUTPUT); + digitalWrite(PIN_GPS_EN, HIGH); +} \ No newline at end of file diff --git a/variants/rak_wismesh_tag/variant.h b/variants/rak_wismesh_tag/variant.h new file mode 100644 index 00000000..b0e51efc --- /dev/null +++ b/variants/rak_wismesh_tag/variant.h @@ -0,0 +1,121 @@ +/* + * variant.h + * Copyright (C) 2023 Seeed K.K. + * MIT License + */ + +#pragma once + +#include "WVariant.h" + +//////////////////////////////////////////////////////////////////////////////// +// Low frequency clock source + +#define USE_LFXO // 32.768 kHz crystal oscillator +#define VARIANT_MCK (64000000ul) + +#define WIRE_INTERFACES_COUNT (1) +#define PIN_TXCO (21) +//////////////////////////////////////////////////////////////////////////////// +// Power + +#define PIN_PWR_EN (12) + +#define BATTERY_PIN (5) +#define ADC_MULTIPLIER (1.73F) + +#define ADC_RESOLUTION (14) +#define BATTERY_SENSE_RES (12) + +#define AREF_VOLTAGE (3.0) + +//////////////////////////////////////////////////////////////////////////////// +// Number of pins + +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +//////////////////////////////////////////////////////////////////////////////// +// UART pin definition + +#define PIN_SERIAL1_RX (15) +#define PIN_SERIAL1_TX (16) + +//////////////////////////////////////////////////////////////////////////////// +// I2C pin definition +#define WIRE_INTERFACES_COUNT (1) +#define PIN_WIRE_SDA (25) +#define PIN_WIRE_SCL (24) + +//////////////////////////////////////////////////////////////////////////////// +// SPI pin definition + +#define SPI_INTERFACES_COUNT (2) + +#define PIN_SPI_MISO (45) +#define PIN_SPI_MOSI (44) +#define PIN_SPI_SCK (43) +#define PIN_SPI_NSS (42) + +//////////////////////////////////////////////////////////////////////////////// +// Builtin LEDs + +#define LED_RED (-1) +#define LED_BLUE (36) +#define LED_GREEN (35) + +//#define PIN_STATUS_LED LED_BLUE +#define LED_BUILTIN LED_GREEN +#define LED_PIN LED_GREEN +#define LED_STATE_ON HIGH + +//////////////////////////////////////////////////////////////////////////////// +// Builtin buttons + +#define PIN_BUTTON1 (9) +#define BUTTON_PIN PIN_BUTTON1 +#define PIN_USER_BTN BUTTON_PIN + +#define PIN_BUTTON2 (12) +#define BUTTON_PIN2 PIN_BUTTON2 + +//////////////////////////////////////////////////////////////////////////////// +// Lora + +#define USE_SX1262 +#define LORA_CS (42) +#define SX126X_DIO1 (47) +#define SX126X_BUSY (46) +#define SX126X_RESET (38) +#define SX126X_POWER_EN (37) +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +//////////////////////////////////////////////////////////////////////////////// +// SPI1 + +#define PIN_SPI1_MISO (29) +#define PIN_SPI1_MOSI (30) +#define PIN_SPI1_SCK (3) + +// GxEPD2 needs that for a panel that is not even used ! +extern const int MISO; +extern const int MOSI; +extern const int SCK; + +//////////////////////////////////////////////////////////////////////////////// +// GPS + +#define PIN_GPS_RX (PIN_SERIAL1_TX) +#define PIN_GPS_TX (PIN_SERIAL1_RX) +#define PIN_GPS_PPS (17) +#define PIN_GPS_EN (34) + +/////////////////////////////////////////////////////////////////////////////// +// OTHER PINS +#define PIN_AREF (2) +#define PIN_NFC1 (9) +#define PIN_NFC2 (10) +#define PIN_BUZZER (21) From 2ef38422e9f3184b533fb52fb9767b918163b9f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Fri, 5 Sep 2025 17:59:59 +0100 Subject: [PATCH 24/48] Delete the variant-specific NullDisplayDriver.h and update target.h to use the shared implementation from #735 --- variants/heltec_t114/NullDisplayDriver.h | 24 ------------------------ variants/heltec_t114/target.h | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 variants/heltec_t114/NullDisplayDriver.h diff --git a/variants/heltec_t114/NullDisplayDriver.h b/variants/heltec_t114/NullDisplayDriver.h deleted file mode 100644 index 2a9670bd..00000000 --- a/variants/heltec_t114/NullDisplayDriver.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -class NullDisplayDriver : public DisplayDriver { -public: - NullDisplayDriver() : DisplayDriver(128, 64) { } - bool begin() { return false; } // not present - - bool isOn() override { return false; } - void turnOn() override { } - void turnOff() override { } - void clear() override { } - void startFrame(Color bkg = DARK) override { } - void setTextSize(int sz) override { } - void setColor(Color c) override { } - void setCursor(int x, int y) override { } - void print(const char* str) override { } - void fillRect(int x, int y, int w, int h) override { } - void drawRect(int x, int y, int w, int h) override { } - void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override { } - uint16_t getTextWidth(const char* str) override { return 0; } - void endFrame() { } -}; diff --git a/variants/heltec_t114/target.h b/variants/heltec_t114/target.h index 3283f009..1876aadc 100644 --- a/variants/heltec_t114/target.h +++ b/variants/heltec_t114/target.h @@ -14,7 +14,7 @@ #ifdef HELTEC_T114_WITH_DISPLAY #include #else -#include "NullDisplayDriver.h" +#include "helpers/ui/NullDisplayDriver.h" #endif #endif From c5180d4588b226f91933f6f2d31f312b610d6346 Mon Sep 17 00:00:00 2001 From: taco Date: Tue, 29 Jul 2025 20:06:35 +1000 Subject: [PATCH 25/48] initial commit: CustomLFS --- examples/companion_radio/DataStore.cpp | 23 ++++++++++++++------ examples/companion_radio/main.cpp | 30 ++++++++++++++++++++++++-- platformio.ini | 4 ++++ src/helpers/IdentityStore.h | 21 +++++++++++++----- variants/xiao_nrf52/platformio.ini | 3 ++- 5 files changed, 66 insertions(+), 15 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index b5d70edc..8e774d91 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -1,6 +1,12 @@ #include #include "DataStore.h" +#if defined(EXTRAFS) || defined(SPIFLASH) + #define MAX_BLOBRECS 100 +#else + #define MAX_BLOBRECS 20 +#endif + DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _clock(&clock), #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) identity_store(fs, "") @@ -41,7 +47,9 @@ void DataStore::begin() { #elif defined(RP2040_PLATFORM) #include #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - #include + // #include // disabled for now, leaving here for dual fs branch + #include + #include #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) @@ -51,9 +59,9 @@ int _countLfsBlock(void *p, lfs_block_t block){ return 0; } -lfs_ssize_t _getLfsUsedBlockCount() { +lfs_ssize_t _getLfsUsedBlockCount(FILESYSTEM* fs) { lfs_size_t size = 0; - lfs_traverse(InternalFS._getFS(), _countLfsBlock, &size); + lfs_traverse(fs->_getFS(), _countLfsBlock, &size); return size; } #endif @@ -67,8 +75,8 @@ uint32_t DataStore::getStorageUsedKb() const { _fs->info(info); return info.usedBytes / 1024; #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - const lfs_config* config = InternalFS._getFS()->cfg; - int usedBlockCount = _getLfsUsedBlockCount(); + const lfs_config* config = _fs->_getFS()->cfg; + int usedBlockCount = _getLfsUsedBlockCount(_fs); int usedBytes = config->block_size * usedBlockCount; return usedBytes / 1024; #else @@ -85,7 +93,7 @@ uint32_t DataStore::getStorageTotalKb() const { _fs->info(info); return info.totalBytes / 1024; #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - const lfs_config* config = InternalFS._getFS()->cfg; + const lfs_config* config = _fs->_getFS()->cfg; int totalBytes = config->block_size * config->block_count; return totalBytes / 1024; #else @@ -109,6 +117,7 @@ bool DataStore::removeFile(const char* filename) { bool DataStore::formatFileSystem() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + // InternalFS.format(); // leaving as placeholder to remind for dual fs branch return _fs->format(); #elif defined(RP2040_PLATFORM) return LittleFS.format(); @@ -336,7 +345,7 @@ void DataStore::checkAdvBlobFile() { if (file) { BlobRec zeroes; memset(&zeroes, 0, sizeof(zeroes)); - for (int i = 0; i < 20; i++) { // pre-allocate to fixed size + for (int i = 0; i < MAX_BLOBRECS; i++) { // pre-allocate to fixed size file.write((uint8_t *) &zeroes, sizeof(zeroes)); } file.close(); diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 1d5ec564..4900d3bc 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -14,7 +14,20 @@ static uint32_t _atoi(const char* sp) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #include - DataStore store(InternalFS, rtc_clock); + #if defined(SPIFLASH) + #include + const int chipSelect = PIN_QSPI_CS; + SPIClass SPI_2(NRF_SPIM2, PIN_QSPI_IO1, PIN_QSPI_SCK, PIN_QSPI_IO0); + DataStore store(FlashFS, rtc_clock); + #else + #if defined(EXTRAFS) + #include + CustomLFS ExtraFS(0xD4000, 0x19000, 128); + DataStore store(ExtraFS, rtc_clock); + #else + DataStore store(InternalFS, rtc_clock); + #endif + #endif #elif defined(RP2040_PLATFORM) #include DataStore store(LittleFS, rtc_clock); @@ -23,6 +36,7 @@ static uint32_t _atoi(const char* sp) { DataStore store(SPIFFS, rtc_clock); #endif + #ifdef ESP32 #ifdef WIFI_SSID #include @@ -117,7 +131,19 @@ void setup() { fast_rng.begin(radio_get_rng_seed()); #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - InternalFS.begin(); + #if defined(SPIFLASH) + if (!FlashFS.begin(chipSelect, SPI_2)) { + // debug output might not be available at this point, might be too early. maybe should fall back to InternalFS here? + MESH_DEBUG_PRINTLN("CustomLFS_SPIFlash: failed to initialize"); + } else { + MESH_DEBUG_PRINTLN("CustomLFS_SPIFlash: initialized successfully"); + } + #else + InternalFS.begin(); + #if defined(EXTRAFS) + ExtraFS.begin(); + #endif + #endif store.begin(); the_mesh.begin( #ifdef DISPLAY_CLASS diff --git a/platformio.ini b/platformio.ini index 1c89465f..5fe46986 100644 --- a/platformio.ini +++ b/platformio.ini @@ -78,6 +78,10 @@ platform = nordicnrf52 build_flags = ${arduino_base.build_flags} -D NRF52_PLATFORM -D LFS_NO_ASSERT=1 + -D EXTRAFS=1 +lib_deps = + ${arduino_base.lib_deps} + https://github.com/oltaco/CustomLFS ; ----------------- RP2040 --------------------- diff --git a/src/helpers/IdentityStore.h b/src/helpers/IdentityStore.h index e81290e7..15219a44 100644 --- a/src/helpers/IdentityStore.h +++ b/src/helpers/IdentityStore.h @@ -4,12 +4,19 @@ #include #define FILESYSTEM fs::FS #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - #include - #define FILESYSTEM Adafruit_LittleFS + #if defined(SPIFLASH) + #include + #define FILESYSTEM CustomLFS_SPIFlash + #elif defined(EXTRAFS) + #include + #define FILESYSTEM CustomLFS + #else + #include + #define FILESYSTEM Adafruit_LittleFS - using namespace Adafruit_LittleFS_Namespace; + using namespace Adafruit_LittleFS_Namespace; + #endif #endif - #include class IdentityStore { @@ -18,7 +25,11 @@ class IdentityStore { public: IdentityStore(FILESYSTEM& fs, const char* dir): _fs(&fs), _dir(dir) { } - void begin() { if (_dir && _dir[0] == '/') { _fs->mkdir(_dir); } } + void begin() { + if (_dir && _dir[0] == '/') { _fs->mkdir(_dir); } + + + } bool load(const char *name, mesh::LocalIdentity& id); bool load(const char *name, mesh::LocalIdentity& id, char display_name[], int max_name_sz); bool save(const char *name, const mesh::LocalIdentity& id); diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index fd4c362b..3717c26d 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -65,7 +65,8 @@ build_flags = -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 + -D MESH_DEBUG=1 + -D SPIFLASH=1 build_src_filter = ${Xiao_nrf52.build_src_filter} + +<../examples/companion_radio/*.cpp> From bdfe9ad27bffcf4e9566c0e561048a38ebd3174e Mon Sep 17 00:00:00 2001 From: taco Date: Sat, 2 Aug 2025 08:31:23 +1000 Subject: [PATCH 26/48] switch to using QSPI bus for external flash --- examples/companion_radio/DataStore.cpp | 6 ++++-- examples/companion_radio/main.cpp | 16 +++++++--------- src/helpers/IdentityStore.h | 6 +++--- variants/nano_g2_ultra/platformio.ini | 1 + variants/thinknode_m1/platformio.ini | 1 + variants/thinknode_m1/variant.h | 9 +++++++++ variants/wio-tracker-l1/platformio.ini | 1 + variants/xiao_nrf52/platformio.ini | 2 +- 8 files changed, 27 insertions(+), 15 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 8e774d91..8c2962d3 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -1,7 +1,7 @@ #include #include "DataStore.h" -#if defined(EXTRAFS) || defined(SPIFLASH) +#if defined(EXTRAFS) || defined(QSPIFLASH) #define MAX_BLOBRECS 100 #else #define MAX_BLOBRECS 20 @@ -49,7 +49,9 @@ void DataStore::begin() { #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) // #include // disabled for now, leaving here for dual fs branch #include - #include + #if defined(QSPIFLASH) + #include + #endif #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 4900d3bc..2dca708d 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -14,11 +14,9 @@ static uint32_t _atoi(const char* sp) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #include - #if defined(SPIFLASH) - #include - const int chipSelect = PIN_QSPI_CS; - SPIClass SPI_2(NRF_SPIM2, PIN_QSPI_IO1, PIN_QSPI_SCK, PIN_QSPI_IO0); - DataStore store(FlashFS, rtc_clock); + #if defined(QSPIFLASH) + #include + DataStore store(QSPIFlash, rtc_clock); #else #if defined(EXTRAFS) #include @@ -131,12 +129,12 @@ void setup() { fast_rng.begin(radio_get_rng_seed()); #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - #if defined(SPIFLASH) - if (!FlashFS.begin(chipSelect, SPI_2)) { + #if defined(QSPIFLASH) + if (!QSPIFlash.begin()) { // debug output might not be available at this point, might be too early. maybe should fall back to InternalFS here? - MESH_DEBUG_PRINTLN("CustomLFS_SPIFlash: failed to initialize"); + MESH_DEBUG_PRINTLN("CustomLFS_QSPIFlash: failed to initialize"); } else { - MESH_DEBUG_PRINTLN("CustomLFS_SPIFlash: initialized successfully"); + MESH_DEBUG_PRINTLN("CustomLFS_QSPIFlash: initialized successfully"); } #else InternalFS.begin(); diff --git a/src/helpers/IdentityStore.h b/src/helpers/IdentityStore.h index 15219a44..99c2bd77 100644 --- a/src/helpers/IdentityStore.h +++ b/src/helpers/IdentityStore.h @@ -4,9 +4,9 @@ #include #define FILESYSTEM fs::FS #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - #if defined(SPIFLASH) - #include - #define FILESYSTEM CustomLFS_SPIFlash + #if defined(QSPIFLASH) + #include + #define FILESYSTEM CustomLFS_QSPIFlash #elif defined(EXTRAFS) #include #define FILESYSTEM CustomLFS diff --git a/variants/nano_g2_ultra/platformio.ini b/variants/nano_g2_ultra/platformio.ini index 7fb2de28..e70c353b 100644 --- a/variants/nano_g2_ultra/platformio.ini +++ b/variants/nano_g2_ultra/platformio.ini @@ -42,6 +42,7 @@ build_flags = -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SH1106Display -D PIN_BUZZER=4 + -D QSPIFLASH=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Nano_G2_Ultra.build_src_filter} diff --git a/variants/thinknode_m1/platformio.ini b/variants/thinknode_m1/platformio.ini index e29e1471..eadc83ae 100644 --- a/variants/thinknode_m1/platformio.ini +++ b/variants/thinknode_m1/platformio.ini @@ -80,6 +80,7 @@ build_flags = -D OFFLINE_QUEUE_SIZE=256 -D PIN_BUZZER=6 -D AUTO_SHUTDOWN_MILLIVOLTS=3300 + -D QSPIFLASH=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${ThinkNode_M1.build_src_filter} diff --git a/variants/thinknode_m1/variant.h b/variants/thinknode_m1/variant.h index 2b58e341..f7f52a6a 100644 --- a/variants/thinknode_m1/variant.h +++ b/variants/thinknode_m1/variant.h @@ -110,6 +110,15 @@ extern const int MISO; extern const int MOSI; extern const int SCK; +//////////////////////////////////////////////////////////////////////////////// +// QSPI +#define PIN_QSPI_SCK (46) +#define PIN_QSPI_CS (47) +#define PIN_QSPI_IO0 (44) // MOSI if using two bit interface +#define PIN_QSPI_IO1 (45) // MISO if using two bit interface +#define PIN_QSPI_IO2 (7) // WP if using two bit interface (i.e. not used) +#define PIN_QSPI_IO3 (5) // HOLD if using two bit interface (i.e. not used) + //////////////////////////////////////////////////////////////////////////////// // Display diff --git a/variants/wio-tracker-l1/platformio.ini b/variants/wio-tracker-l1/platformio.ini index be0086c4..02f09e5c 100644 --- a/variants/wio-tracker-l1/platformio.ini +++ b/variants/wio-tracker-l1/platformio.ini @@ -86,6 +86,7 @@ build_flags = ${WioTrackerL1.build_flags} -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SH1106Display -D PIN_BUZZER=12 + -D QSPIFLASH=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${WioTrackerL1.build_src_filter} diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index 3717c26d..3e181672 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -66,7 +66,7 @@ build_flags = ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 -D MESH_DEBUG=1 - -D SPIFLASH=1 + -D QSPIFLASH=1 build_src_filter = ${Xiao_nrf52.build_src_filter} + +<../examples/companion_radio/*.cpp> From 2b24c575c7dbf77bc322d0155ef3493291780199 Mon Sep 17 00:00:00 2001 From: taco Date: Fri, 22 Aug 2025 13:09:54 +1000 Subject: [PATCH 27/48] support dual filsystems on nrf52 store identity and prefs in UserData and contacts, channels and adv_blobs in ExtraData --- examples/companion_radio/DataStore.cpp | 212 +++++++++++++++++++++++-- examples/companion_radio/DataStore.h | 7 + examples/companion_radio/MyMesh.cpp | 60 ++++++- examples/companion_radio/main.cpp | 10 +- src/helpers/IdentityStore.h | 8 - 5 files changed, 268 insertions(+), 29 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 8c2962d3..ca7337bd 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -18,14 +18,28 @@ DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _clock(& { } -static File openWrite(FILESYSTEM* _fs, const char* filename) { +#if defined(EXTRAFS) || defined(QSPIFLASH) + +DataStore::DataStore(FILESYSTEM& fs, FILESYSTEM& fsExtra, mesh::RTCClock& clock) : _fs(&fs), _fsExtra(&fsExtra), _clock(&clock), #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove(filename); - return _fs->open(filename, FILE_O_WRITE); + identity_store(fs, "") #elif defined(RP2040_PLATFORM) - return _fs->open(filename, "w"); + identity_store(fs, "/identity") #else - return _fs->open(filename, "w", true); + identity_store(fs, "/identity") +#endif +{ +} +#endif + +static File openWrite(FILESYSTEM* fs, const char* filename) { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + fs->remove(filename); + return fs->open(filename, FILE_O_WRITE); +#elif defined(RP2040_PLATFORM) + return fs->open(filename, "w"); +#else + return fs->open(filename, "w", true); #endif } @@ -36,6 +50,9 @@ void DataStore::begin() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) checkAdvBlobFile(); + #if defined(EXTRAFS) || defined(QSPIFLASH) + migrateToSecondaryFS(); + #endif #else // init 'blob store' support _fs->mkdir("/bl"); @@ -46,12 +63,13 @@ void DataStore::begin() { #include #elif defined(RP2040_PLATFORM) #include -#elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - // #include // disabled for now, leaving here for dual fs branch +#elif defined(NRF52_PLATFORM) #include #if defined(QSPIFLASH) #include #endif +#elif defined(STM32_PLATFORM) + #include #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) @@ -77,8 +95,13 @@ uint32_t DataStore::getStorageUsedKb() const { _fs->info(info); return info.usedBytes / 1024; #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) +#if defined(EXTRAFS) || defined(QSPIFLASH) + const lfs_config* config = _fsExtra->_getFS()->cfg; + int usedBlockCount = _getLfsUsedBlockCount(_fsExtra); +#else const lfs_config* config = _fs->_getFS()->cfg; int usedBlockCount = _getLfsUsedBlockCount(_fs); +#endif int usedBytes = config->block_size * usedBlockCount; return usedBytes / 1024; #else @@ -95,7 +118,11 @@ uint32_t DataStore::getStorageTotalKb() const { _fs->info(info); return info.totalBytes / 1024; #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) +#if defined(EXTRAFS) || defined(QSPIFLASH) + const lfs_config* config = _fsExtra->_getFS()->cfg; +#else const lfs_config* config = _fs->_getFS()->cfg; +#endif int totalBytes = config->block_size * config->block_count; return totalBytes / 1024; #else @@ -113,14 +140,31 @@ File DataStore::openRead(const char* filename) { #endif } +File DataStore::openRead(FILESYSTEM* fs, const char* filename) { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + return fs->open(filename, FILE_O_READ); +#elif defined(RP2040_PLATFORM) + return fs->open(filename, "r"); +#else + return fs->open(filename, "r", false); +#endif +} + bool DataStore::removeFile(const char* filename) { return _fs->remove(filename); } +bool DataStore::removeFile(FILESYSTEM* fs, const char* filename) { + return fs->remove(filename); +} + bool DataStore::formatFileSystem() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - // InternalFS.format(); // leaving as placeholder to remind for dual fs branch +#if defined(EXTRAFS) || defined(QSPIFLASH) + return _fs->format() && _fsExtra->format(); // in future maybe return an error code based on which format failed? +#else return _fs->format(); +#endif #elif defined(RP2040_PLATFORM) return LittleFS.format(); #elif defined(ESP32) @@ -214,11 +258,20 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ } void DataStore::loadContacts(DataStoreHost* host) { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) +#if defined(EXTRAFS) || defined(QSPIFLASH) + if (_fsExtra->exists("/contacts3")) { + File file = _fsExtra->open("/contacts3"); +#else if (_fs->exists("/contacts3")) { -#if defined(RP2040_PLATFORM) + File file = _fs->open("/contacts3"); +#endif +#elif defined(RP2040_PLATFORM) + if (_fs->exists("/contacts3")) { File file = _fs->open("/contacts3", "r"); #else - File file = _fs->open("/contacts3"); + if (_fs->exists("/contacts3")) { + File file = _fs->open("/contacts3", "r", false); #endif if (file) { bool full = false; @@ -251,7 +304,11 @@ void DataStore::loadContacts(DataStoreHost* host) { } void DataStore::saveContacts(DataStoreHost* host) { +#if defined(EXTRAFS) || defined(QSPIFLASH) + File file = openWrite(_fsExtra, "/contacts3"); +#else File file = openWrite(_fs, "/contacts3"); +#endif if (file) { uint32_t idx = 0; ContactInfo c; @@ -280,11 +337,20 @@ void DataStore::saveContacts(DataStoreHost* host) { } void DataStore::loadChannels(DataStoreHost* host) { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) +#if defined(EXTRAFS) || defined(QSPIFLASH) + if (_fsExtra->exists("/channels2")) { + File file = _fsExtra->open("/channels2"); +#else + if (_fs->exists("/channels2")) { + File file = _fs->open("/channels2"); +#endif +#elif defined(RP2040_PLATFORM) if (_fs->exists("/channels2")) { -#if defined(RP2040_PLATFORM) File file = _fs->open("/channels2", "r"); #else - File file = _fs->open("/channels2"); + if (_fs->exists("/channels2")) { + File file = _fs->open("/channels2", "r", false); #endif if (file) { bool full = false; @@ -311,7 +377,11 @@ void DataStore::loadChannels(DataStoreHost* host) { } void DataStore::saveChannels(DataStoreHost* host) { +#if defined(EXTRAFS) || defined(QSPIFLASH) + File file = openWrite(_fsExtra, "/channels2"); +#else File file = openWrite(_fs, "/channels2"); +#endif if (file) { uint8_t channel_idx = 0; ChannelDetails ch; @@ -342,8 +412,13 @@ struct BlobRec { }; void DataStore::checkAdvBlobFile() { +#if defined(EXTRAFS) || defined(QSPIFLASH) + if (!_fsExtra->exists("/adv_blobs")) { + File file = openWrite(_fsExtra, "/adv_blobs"); +#else if (!_fs->exists("/adv_blobs")) { File file = openWrite(_fs, "/adv_blobs"); +#endif if (file) { BlobRec zeroes; memset(&zeroes, 0, sizeof(zeroes)); @@ -355,8 +430,117 @@ void DataStore::checkAdvBlobFile() { } } +void DataStore::migrateToSecondaryFS() { + // migrate old adv_blobs, contacts3 and channels2 files to secondary FS if they don't already exist + if (!_fsExtra->exists("/adv_blobs")) { + if (_fs->exists("/adv_blobs")) { + File oldAdvBlobs = openRead(_fs, "/adv_blobs"); + File newAdvBlobs = openWrite(_fsExtra, "/adv_blobs"); + + if (oldAdvBlobs && newAdvBlobs) { + BlobRec rec; + size_t count = 0; + + // Copy 20 BlobRecs from old to new + while (count < 20 && oldAdvBlobs.read((uint8_t *)&rec, sizeof(rec)) == sizeof(rec)) { + newAdvBlobs.seek(count * sizeof(BlobRec)); + newAdvBlobs.write((uint8_t *)&rec, sizeof(rec)); + count++; + } + } + if (oldAdvBlobs) oldAdvBlobs.close(); + if (newAdvBlobs) newAdvBlobs.close(); + _fs->remove("/adv_blobs"); + } + } + if (!_fsExtra->exists("/contacts3")) { + if (_fs->exists("/contacts3")) { + File oldFile = openRead(_fs, "/contacts3"); + File newFile = openWrite(_fsExtra, "/contacts3"); + + if (oldFile && newFile) { + uint8_t buf[64]; + int n; + while ((n = oldFile.read(buf, sizeof(buf))) > 0) { + newFile.write(buf, n); + } + } + if (oldFile) oldFile.close(); + if (newFile) newFile.close(); + _fs->remove("/contacts3"); + } + } + if (!_fsExtra->exists("/channels2")) { + if (_fs->exists("/contacts2")) { + File oldFile = openRead(_fs, "/contacts2"); + File newFile = openWrite(_fsExtra, "/contacts2"); + + if (oldFile && newFile) { + uint8_t buf[64]; + int n; + while ((n = oldFile.read(buf, sizeof(buf))) > 0) { + newFile.write(buf, n); + } + } + if (oldFile) oldFile.close(); + if (newFile) newFile.close(); + _fs->remove("/contacts2"); + } + } + // cleanup nodes which have been testing the extra fs, copy _main.id and new_prefs back to primary + if (_fsExtra->exists("/_main.id")) { + if (_fs->exists("/_main.id")) {_fs->remove("/_main.id");} + File oldFile = openRead(_fsExtra, "/_main.id"); + File newFile = openWrite(_fs, "/_main.id"); + + if (oldFile && newFile) { + uint8_t buf[64]; + int n; + while ((n = oldFile.read(buf, sizeof(buf))) > 0) { + newFile.write(buf, n); + } + } + if (oldFile) oldFile.close(); + if (newFile) newFile.close(); + _fsExtra->remove("/_main.id"); + } + if (_fsExtra->exists("/new_prefs")) { + if (_fs->exists("/new_prefs")) {_fs->remove("/new_prefs");} + File oldFile = openRead(_fsExtra, "/new_prefs"); + File newFile = openWrite(_fs, "/new_prefs"); + + if (oldFile && newFile) { + uint8_t buf[64]; + int n; + while ((n = oldFile.read(buf, sizeof(buf))) > 0) { + newFile.write(buf, n); + } + } + if (oldFile) oldFile.close(); + if (newFile) newFile.close(); + _fsExtra->remove("/new_prefs"); + } + // remove files from where they should not be anymore + if (_fs->exists("/adv_blobs")) { + _fs->remove("/adv_blobs"); + } + if (_fs->exists("/contacts3")) { + _fs->remove("/contacts3"); + } + if (_fsExtra->exists("/_main.id")) { + _fsExtra->remove("/_main.id"); + } + if (_fsExtra->exists("/new_prefs")) { + _fsExtra->remove("/new_prefs"); + } +} + uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { +#if defined(EXTRAFS) || defined(QSPIFLASH) + File file = _fsExtra->open("/adv_blobs"); +#else File file = _fs->open("/adv_blobs"); +#endif uint8_t len = 0; // 0 = not found if (file) { @@ -378,7 +562,11 @@ bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src checkAdvBlobFile(); +#if defined(EXTRAFS) || defined(QSPIFLASH) + File file = _fsExtra->open("/adv_blobs", FILE_O_WRITE); +#else File file = _fs->open("/adv_blobs", FILE_O_WRITE); +#endif if (file) { uint32_t pos = 0, found_pos = 0; uint32_t min_timestamp = 0xFFFFFFFF; diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h index 7bd33301..04579bb3 100644 --- a/examples/companion_radio/DataStore.h +++ b/examples/companion_radio/DataStore.h @@ -15,6 +15,7 @@ public: class DataStore { FILESYSTEM* _fs; + FILESYSTEM* _fsExtra; mesh::RTCClock* _clock; IdentityStore identity_store; @@ -25,8 +26,11 @@ class DataStore { public: DataStore(FILESYSTEM& fs, mesh::RTCClock& clock); + DataStore(FILESYSTEM& fs, FILESYSTEM& fsExtra, mesh::RTCClock& clock); void begin(); bool formatFileSystem(); + FILESYSTEM* getPrimaryFS() const { return _fs; } + FILESYSTEM* getSecondaryFS() const { return _fsExtra; } bool loadMainIdentity(mesh::LocalIdentity &identity); bool saveMainIdentity(const mesh::LocalIdentity &identity); void loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon); @@ -35,10 +39,13 @@ public: void saveContacts(DataStoreHost* host); void loadChannels(DataStoreHost* host); void saveChannels(DataStoreHost* host); + void migrateToSecondaryFS(); uint8_t getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]); bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len); File openRead(const char* filename); + File openRead(FILESYSTEM* fs, const char* filename); bool removeFile(const char* filename); + bool removeFile(FILESYSTEM* fs, const char* filename); uint32_t getStorageUsedKb() const; uint32_t getStorageTotalKb() const; }; diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 1fa5478b..6d4339b0 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1530,27 +1530,65 @@ void MyMesh::checkCLIRescueCmd() { if(root){ File file = root.openNextFile(); while (file) { - + + #if defined(EXTRAFS) || defined(QSPIFLASH) + if (file.isDirectory()) { + Serial.printf("[dir] /FS1/%s\n", file.name()); + } else { + Serial.printf("[file] /FS1/%s (%d bytes)\n", file.name(), file.size()); + } + #else if (file.isDirectory()) { Serial.printf("[dir] %s\n", file.name()); } else { Serial.printf("[file] %s (%d bytes)\n", file.name(), file.size()); } - + #endif // move to next file file = root.openNextFile(); } root.close(); } + #if defined(EXTRAFS) || defined(QSPIFLASH) + root = _store->openRead(_store->getSecondaryFS(), path); + if(root){ + File file = root.openNextFile(); + while (file) { + + if (file.isDirectory()) { + Serial.printf("[dir] /FS2/%s\n", file.name()); + } else { + Serial.printf("[file] /FS2/%s (%d bytes)\n", file.name(), file.size()); + } + + // move to next file + file = root.openNextFile(); + + } + root.close(); + } + #endif } else if (memcmp(cli_command, "cat", 3) == 0) { // get path from command e.g: "cat /contacts3" const char *path = &cli_command[4]; + // + bool is_fs2 = false; + if (memcmp(path, "FS1/", 4) == 0) { + path += 3; // skip "FS1" + } else if (memcmp(path, "FS2/", 4) == 0) { + path += 3; // skip "FS2" + is_fs2 = true; + } + // log file content as hex File file = _store->openRead(path); + if (is_fs2 == true) { + file = _store->openRead(_store->getSecondaryFS(), path); + } if(file){ // get file content @@ -1570,14 +1608,28 @@ void MyMesh::checkCLIRescueCmd() { // get path from command e.g: "rm /adv_blobs" const char *path = &cli_command[4]; - + MESH_DEBUG_PRINTLN("Removing file: %s", path); // ensure path is not empty, or root dir if(!path || strlen(path) == 0 || strcmp(path, "/") == 0){ Serial.println("Invalid path provided"); } else { + bool is_fs2 = false; + if (memcmp(path, "FS1/", 4) == 0) { + path += 3; // skip "FS1" + } else if (memcmp(path, "FS2/", 4) == 0) { + path += 3; // skip "FS2" + is_fs2 = true; + } // remove file - bool removed = _store->removeFile(path); + bool removed; + if (is_fs2) { + MESH_DEBUG_PRINTLN("Removing file from FS2: %s", path); + removed = _store->removeFile(_store->getSecondaryFS(), path); + } else { + MESH_DEBUG_PRINTLN("Removing file from FS1: %s", path); + removed = _store->removeFile(path); + } if(removed){ Serial.println("File removed"); } else { diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 2dca708d..9651c301 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -16,12 +16,12 @@ static uint32_t _atoi(const char* sp) { #include #if defined(QSPIFLASH) #include - DataStore store(QSPIFlash, rtc_clock); + DataStore store(InternalFS, QSPIFlash, rtc_clock); #else #if defined(EXTRAFS) #include CustomLFS ExtraFS(0xD4000, 0x19000, 128); - DataStore store(ExtraFS, rtc_clock); + DataStore store(InternalFS, ExtraFS, rtc_clock); #else DataStore store(InternalFS, rtc_clock); #endif @@ -129,6 +129,7 @@ void setup() { fast_rng.begin(radio_get_rng_seed()); #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + InternalFS.begin(); #if defined(QSPIFLASH) if (!QSPIFlash.begin()) { // debug output might not be available at this point, might be too early. maybe should fall back to InternalFS here? @@ -137,10 +138,9 @@ void setup() { MESH_DEBUG_PRINTLN("CustomLFS_QSPIFlash: initialized successfully"); } #else - InternalFS.begin(); - #if defined(EXTRAFS) + #if defined(EXTRAFS) ExtraFS.begin(); - #endif + #endif #endif store.begin(); the_mesh.begin( diff --git a/src/helpers/IdentityStore.h b/src/helpers/IdentityStore.h index 99c2bd77..e022696a 100644 --- a/src/helpers/IdentityStore.h +++ b/src/helpers/IdentityStore.h @@ -4,18 +4,10 @@ #include #define FILESYSTEM fs::FS #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - #if defined(QSPIFLASH) - #include - #define FILESYSTEM CustomLFS_QSPIFlash - #elif defined(EXTRAFS) - #include - #define FILESYSTEM CustomLFS - #else #include #define FILESYSTEM Adafruit_LittleFS using namespace Adafruit_LittleFS_Namespace; - #endif #endif #include From accd1e0a97c5b6725b9d7653bfe084d42322a963 Mon Sep 17 00:00:00 2001 From: taco Date: Mon, 25 Aug 2025 12:41:59 +1000 Subject: [PATCH 28/48] nrf52 targets: increase limits for contacts and channels --- examples/companion_radio/DataStore.cpp | 22 ++++++++++++++-------- examples/companion_radio/main.cpp | 1 - platformio.ini | 2 +- src/helpers/IdentityStore.h | 11 ++++------- variants/heltec_mesh_solar/platformio.ini | 8 ++++---- variants/heltec_t114/platformio.ini | 8 ++++---- variants/ikoka_stick_nrf/platformio.ini | 8 ++++---- variants/lilygo_techo/platformio.ini | 5 +++-- variants/minewsemi_me25ls01/platformio.ini | 8 ++++---- variants/nano_g2_ultra/platformio.ini | 8 ++++---- variants/promicro/platformio.ini | 8 ++++---- variants/rak4631/platformio.ini | 8 ++++---- variants/sensecap_solar/platformio.ini | 8 ++++---- variants/t1000-e/platformio.ini | 4 ++-- variants/thinknode_m1/platformio.ini | 4 ++-- variants/wio-tracker-l1/platformio.ini | 4 ++-- variants/xiao_nrf52/platformio.ini | 8 ++++---- 17 files changed, 64 insertions(+), 61 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index ca7337bd..a27c4c86 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -63,13 +63,16 @@ void DataStore::begin() { #include #elif defined(RP2040_PLATFORM) #include -#elif defined(NRF52_PLATFORM) - #include +#elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #if defined(QSPIFLASH) #include + #else + #if defined(EXTRAFS) + #include + #else + #include #endif -#elif defined(STM32_PLATFORM) - #include +#endif #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) @@ -471,9 +474,9 @@ void DataStore::migrateToSecondaryFS() { } } if (!_fsExtra->exists("/channels2")) { - if (_fs->exists("/contacts2")) { - File oldFile = openRead(_fs, "/contacts2"); - File newFile = openWrite(_fsExtra, "/contacts2"); + if (_fs->exists("/channels2")) { + File oldFile = openRead(_fs, "/channels2"); + File newFile = openWrite(_fsExtra, "/channels2"); if (oldFile && newFile) { uint8_t buf[64]; @@ -484,7 +487,7 @@ void DataStore::migrateToSecondaryFS() { } if (oldFile) oldFile.close(); if (newFile) newFile.close(); - _fs->remove("/contacts2"); + _fs->remove("/channels2"); } } // cleanup nodes which have been testing the extra fs, copy _main.id and new_prefs back to primary @@ -527,6 +530,9 @@ void DataStore::migrateToSecondaryFS() { if (_fs->exists("/contacts3")) { _fs->remove("/contacts3"); } + if (_fs->exists("/channels2")) { + _fs->remove("/channels2"); + } if (_fsExtra->exists("/_main.id")) { _fsExtra->remove("/_main.id"); } diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 9651c301..89adca59 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -34,7 +34,6 @@ static uint32_t _atoi(const char* sp) { DataStore store(SPIFFS, rtc_clock); #endif - #ifdef ESP32 #ifdef WIFI_SSID #include diff --git a/platformio.ini b/platformio.ini index 5fe46986..c08cd853 100644 --- a/platformio.ini +++ b/platformio.ini @@ -81,7 +81,7 @@ build_flags = ${arduino_base.build_flags} -D EXTRAFS=1 lib_deps = ${arduino_base.lib_deps} - https://github.com/oltaco/CustomLFS + https://github.com/oltaco/CustomLFS @ 0.2 ; ----------------- RP2040 --------------------- diff --git a/src/helpers/IdentityStore.h b/src/helpers/IdentityStore.h index e022696a..d0d7ee45 100644 --- a/src/helpers/IdentityStore.h +++ b/src/helpers/IdentityStore.h @@ -4,10 +4,10 @@ #include #define FILESYSTEM fs::FS #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - #include - #define FILESYSTEM Adafruit_LittleFS + #include + #define FILESYSTEM Adafruit_LittleFS - using namespace Adafruit_LittleFS_Namespace; + using namespace Adafruit_LittleFS_Namespace; #endif #include @@ -18,10 +18,7 @@ public: IdentityStore(FILESYSTEM& fs, const char* dir): _fs(&fs), _dir(dir) { } void begin() { - if (_dir && _dir[0] == '/') { _fs->mkdir(_dir); } - - - } + if (_dir && _dir[0] == '/') { _fs->mkdir(_dir); } } bool load(const char *name, mesh::LocalIdentity& id); bool load(const char *name, mesh::LocalIdentity& id, char display_name[], int max_name_sz); bool save(const char *name, const mesh::LocalIdentity& id); diff --git a/variants/heltec_mesh_solar/platformio.ini b/variants/heltec_mesh_solar/platformio.ini index 9fd3edd5..18c4ac73 100644 --- a/variants/heltec_mesh_solar/platformio.ini +++ b/variants/heltec_mesh_solar/platformio.ini @@ -59,8 +59,8 @@ build_flags = extends = Heltec_mesh_solar build_flags = ${Heltec_mesh_solar.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 @@ -77,8 +77,8 @@ lib_deps = extends = Heltec_mesh_solar build_flags = ${Heltec_mesh_solar.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 diff --git a/variants/heltec_t114/platformio.ini b/variants/heltec_t114/platformio.ini index 2cac2eae..7222bb11 100644 --- a/variants/heltec_t114/platformio.ini +++ b/variants/heltec_t114/platformio.ini @@ -74,8 +74,8 @@ extends = Heltec_t114 build_flags = ${Heltec_t114.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 @@ -94,8 +94,8 @@ extends = Heltec_t114 build_flags = ${Heltec_t114.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 diff --git a/variants/ikoka_stick_nrf/platformio.ini b/variants/ikoka_stick_nrf/platformio.ini index 6e6ae101..1f2bbfe9 100644 --- a/variants/ikoka_stick_nrf/platformio.ini +++ b/variants/ikoka_stick_nrf/platformio.ini @@ -103,8 +103,8 @@ build_src_filter = ${nrf52840_xiao.build_src_filter} extends = ikoka_stick_nrf_baseboard build_flags = ${ikoka_stick_nrf_baseboard.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 -I examples/companion_radio/ui-new @@ -123,8 +123,8 @@ lib_deps = extends = ikoka_stick_nrf_baseboard build_flags = ${ikoka_stick_nrf_baseboard.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -I examples/companion_radio/ui-new ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 diff --git a/variants/lilygo_techo/platformio.ini b/variants/lilygo_techo/platformio.ini index 8a090a4d..91223205 100644 --- a/variants/lilygo_techo/platformio.ini +++ b/variants/lilygo_techo/platformio.ini @@ -82,8 +82,9 @@ build_flags = ${LilyGo_T-Echo.build_flags} -I src/helpers/ui -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D QSPIFLASH=1 -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 diff --git a/variants/minewsemi_me25ls01/platformio.ini b/variants/minewsemi_me25ls01/platformio.ini index 3436062f..71887002 100644 --- a/variants/minewsemi_me25ls01/platformio.ini +++ b/variants/minewsemi_me25ls01/platformio.ini @@ -52,8 +52,8 @@ lib_deps = ${nrf52840_me25ls01.lib_deps} extends = me25ls01 build_flags = ${me25ls01.build_flags} -I examples/companion_radio/ui-orig - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 -D MESH_PACKET_LOGGING=1 @@ -149,8 +149,8 @@ lib_deps = ${me25ls01.lib_deps} extends = me25ls01 build_flags = ${me25ls01.build_flags} -I examples/companion_radio/ui-orig - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ;-D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 -D MESH_PACKET_LOGGING=1 diff --git a/variants/nano_g2_ultra/platformio.ini b/variants/nano_g2_ultra/platformio.ini index e70c353b..163f4311 100644 --- a/variants/nano_g2_ultra/platformio.ini +++ b/variants/nano_g2_ultra/platformio.ini @@ -35,8 +35,8 @@ build_flags = ${Nano_G2_Ultra.build_flags} -I src/helpers/ui -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=0 -D OFFLINE_QUEUE_SIZE=256 @@ -66,8 +66,8 @@ build_flags = ${Nano_G2_Ultra.build_flags} -I src/helpers/ui -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SH1106Display -D PIN_BUZZER=4 diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index 6b77bdcf..5a70f7ba 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -88,8 +88,8 @@ lib_deps = ${Faketec.lib_deps} extends = Faketec build_flags = ${Faketec.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=SSD1306Display ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 @@ -106,8 +106,8 @@ lib_deps = ${Faketec.lib_deps} extends = Faketec build_flags = ${Faketec.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index c9091878..7e7d2234 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -70,8 +70,8 @@ build_flags = -D PIN_USER_BTN=9 -D PIN_USER_BTN_ANA=31 -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${rak4631.build_src_filter} @@ -89,8 +89,8 @@ build_flags = -D PIN_USER_BTN=9 -D PIN_USER_BTN_ANA=31 -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 diff --git a/variants/sensecap_solar/platformio.ini b/variants/sensecap_solar/platformio.ini index bbac5d99..649ace84 100644 --- a/variants/sensecap_solar/platformio.ini +++ b/variants/sensecap_solar/platformio.ini @@ -76,8 +76,8 @@ build_src_filter = ${SenseCap_Solar.build_src_filter} extends = SenseCap_Solar build_flags = ${SenseCap_Solar.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 @@ -94,8 +94,8 @@ lib_deps = extends = SenseCap_Solar build_flags = ${SenseCap_Solar.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${SenseCap_Solar.build_src_filter} diff --git a/variants/t1000-e/platformio.ini b/variants/t1000-e/platformio.ini index 044fffd8..d7b43cce 100644 --- a/variants/t1000-e/platformio.ini +++ b/variants/t1000-e/platformio.ini @@ -91,8 +91,8 @@ lib_deps = ${t1000-e.lib_deps} extends = t1000-e build_flags = ${t1000-e.build_flags} -I examples/companion_radio/ui-orig - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 diff --git a/variants/thinknode_m1/platformio.ini b/variants/thinknode_m1/platformio.ini index eadc83ae..a530d259 100644 --- a/variants/thinknode_m1/platformio.ini +++ b/variants/thinknode_m1/platformio.ini @@ -71,8 +71,8 @@ build_flags = ${ThinkNode_M1.build_flags} -I src/helpers/ui -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D DISPLAY_ROTATION=4 diff --git a/variants/wio-tracker-l1/platformio.ini b/variants/wio-tracker-l1/platformio.ini index 02f09e5c..87670bd0 100644 --- a/variants/wio-tracker-l1/platformio.ini +++ b/variants/wio-tracker-l1/platformio.ini @@ -79,8 +79,8 @@ lib_deps = ${WioTrackerL1.lib_deps} extends = WioTrackerL1 build_flags = ${WioTrackerL1.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index 3e181672..10807476 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -59,8 +59,8 @@ upload_protocol = nrfutil extends = Xiao_nrf52 build_flags = ${Xiao_nrf52.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 ; -D BLE_DEBUG_LOGGING=1 @@ -78,8 +78,8 @@ lib_deps = extends = Xiao_nrf52 build_flags = ${Xiao_nrf52.build_flags} - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_nrf52.build_src_filter} From 3d8355682995ca56be9cd2d6fdc7986f85daf09b Mon Sep 17 00:00:00 2001 From: taco Date: Sat, 6 Sep 2025 14:07:18 +1000 Subject: [PATCH 29/48] refactor: use _getContactsChannelsFS() instead of ifdefs --- examples/companion_radio/DataStore.cpp | 86 ++++++-------------------- examples/companion_radio/DataStore.h | 3 + 2 files changed, 23 insertions(+), 66 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index a27c4c86..7631b905 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -7,7 +7,7 @@ #define MAX_BLOBRECS 20 #endif -DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _clock(&clock), +DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _fsExtra(nullptr), _clock(&clock), #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) identity_store(fs, "") #elif defined(RP2040_PLATFORM) @@ -19,7 +19,6 @@ DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _clock(& } #if defined(EXTRAFS) || defined(QSPIFLASH) - DataStore::DataStore(FILESYSTEM& fs, FILESYSTEM& fsExtra, mesh::RTCClock& clock) : _fs(&fs), _fsExtra(&fsExtra), _clock(&clock), #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) identity_store(fs, "") @@ -66,14 +65,12 @@ void DataStore::begin() { #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #if defined(QSPIFLASH) #include - #else - #if defined(EXTRAFS) + #elif defined(EXTRAFS) #include #else #include #endif #endif -#endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) int _countLfsBlock(void *p, lfs_block_t block){ @@ -98,13 +95,8 @@ uint32_t DataStore::getStorageUsedKb() const { _fs->info(info); return info.usedBytes / 1024; #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) -#if defined(EXTRAFS) || defined(QSPIFLASH) - const lfs_config* config = _fsExtra->_getFS()->cfg; - int usedBlockCount = _getLfsUsedBlockCount(_fsExtra); -#else - const lfs_config* config = _fs->_getFS()->cfg; - int usedBlockCount = _getLfsUsedBlockCount(_fs); -#endif + const lfs_config* config = _getContactsChannelsFS()->_getFS()->cfg; + int usedBlockCount = _getLfsUsedBlockCount(_getContactsChannelsFS()); int usedBytes = config->block_size * usedBlockCount; return usedBytes / 1024; #else @@ -121,11 +113,7 @@ uint32_t DataStore::getStorageTotalKb() const { _fs->info(info); return info.totalBytes / 1024; #elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) -#if defined(EXTRAFS) || defined(QSPIFLASH) - const lfs_config* config = _fsExtra->_getFS()->cfg; -#else - const lfs_config* config = _fs->_getFS()->cfg; -#endif + const lfs_config* config = _getContactsChannelsFS()->_getFS()->cfg; int totalBytes = config->block_size * config->block_count; return totalBytes / 1024; #else @@ -163,11 +151,11 @@ bool DataStore::removeFile(FILESYSTEM* fs, const char* filename) { bool DataStore::formatFileSystem() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) -#if defined(EXTRAFS) || defined(QSPIFLASH) - return _fs->format() && _fsExtra->format(); // in future maybe return an error code based on which format failed? -#else - return _fs->format(); -#endif + if (_fsExtra == nullptr) { + return _fs->format(); + } else { + return _fs->format() && _fsExtra->format(); + } #elif defined(RP2040_PLATFORM) return LittleFS.format(); #elif defined(ESP32) @@ -262,13 +250,8 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ void DataStore::loadContacts(DataStoreHost* host) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) -#if defined(EXTRAFS) || defined(QSPIFLASH) - if (_fsExtra->exists("/contacts3")) { - File file = _fsExtra->open("/contacts3"); -#else - if (_fs->exists("/contacts3")) { - File file = _fs->open("/contacts3"); -#endif + if (_getContactsChannelsFS()->exists("/contacts3")) { + File file = _getContactsChannelsFS()->open("/contacts3"); #elif defined(RP2040_PLATFORM) if (_fs->exists("/contacts3")) { File file = _fs->open("/contacts3", "r"); @@ -307,11 +290,7 @@ void DataStore::loadContacts(DataStoreHost* host) { } void DataStore::saveContacts(DataStoreHost* host) { -#if defined(EXTRAFS) || defined(QSPIFLASH) - File file = openWrite(_fsExtra, "/contacts3"); -#else - File file = openWrite(_fs, "/contacts3"); -#endif + File file = openWrite(_getContactsChannelsFS(), "/contacts3"); if (file) { uint32_t idx = 0; ContactInfo c; @@ -341,13 +320,8 @@ void DataStore::saveContacts(DataStoreHost* host) { void DataStore::loadChannels(DataStoreHost* host) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) -#if defined(EXTRAFS) || defined(QSPIFLASH) - if (_fsExtra->exists("/channels2")) { - File file = _fsExtra->open("/channels2"); -#else - if (_fs->exists("/channels2")) { - File file = _fs->open("/channels2"); -#endif + if (_getContactsChannelsFS()->exists("/channels2")) { + File file = _getContactsChannelsFS()->open("/channels2"); #elif defined(RP2040_PLATFORM) if (_fs->exists("/channels2")) { File file = _fs->open("/channels2", "r"); @@ -380,11 +354,7 @@ void DataStore::loadChannels(DataStoreHost* host) { } void DataStore::saveChannels(DataStoreHost* host) { -#if defined(EXTRAFS) || defined(QSPIFLASH) - File file = openWrite(_fsExtra, "/channels2"); -#else - File file = openWrite(_fs, "/channels2"); -#endif + File file = openWrite(_getContactsChannelsFS(), "/channels2"); if (file) { uint8_t channel_idx = 0; ChannelDetails ch; @@ -415,13 +385,8 @@ struct BlobRec { }; void DataStore::checkAdvBlobFile() { -#if defined(EXTRAFS) || defined(QSPIFLASH) - if (!_fsExtra->exists("/adv_blobs")) { - File file = openWrite(_fsExtra, "/adv_blobs"); -#else - if (!_fs->exists("/adv_blobs")) { - File file = openWrite(_fs, "/adv_blobs"); -#endif + if (!_getContactsChannelsFS()->exists("/adv_blobs")) { + File file = openWrite(_getContactsChannelsFS(), "/adv_blobs"); if (file) { BlobRec zeroes; memset(&zeroes, 0, sizeof(zeroes)); @@ -542,13 +507,8 @@ void DataStore::migrateToSecondaryFS() { } uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { -#if defined(EXTRAFS) || defined(QSPIFLASH) - File file = _fsExtra->open("/adv_blobs"); -#else - File file = _fs->open("/adv_blobs"); -#endif + File file = _getContactsChannelsFS()->open("/adv_blobs"); uint8_t len = 0; // 0 = not found - if (file) { BlobRec tmp; while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) { @@ -565,14 +525,8 @@ uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_b bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) { if (len < PUB_KEY_SIZE+4+SIGNATURE_SIZE || len > MAX_ADVERT_PKT_LEN) return false; - checkAdvBlobFile(); - -#if defined(EXTRAFS) || defined(QSPIFLASH) - File file = _fsExtra->open("/adv_blobs", FILE_O_WRITE); -#else - File file = _fs->open("/adv_blobs", FILE_O_WRITE); -#endif + File file = _getContactsChannelsFS()->open("/adv_blobs", FILE_O_WRITE); if (file) { uint32_t pos = 0, found_pos = 0; uint32_t min_timestamp = 0xFFFFFFFF; diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h index 04579bb3..62580942 100644 --- a/examples/companion_radio/DataStore.h +++ b/examples/companion_radio/DataStore.h @@ -48,4 +48,7 @@ public: bool removeFile(FILESYSTEM* fs, const char* filename); uint32_t getStorageUsedKb() const; uint32_t getStorageTotalKb() const; + +private: + FILESYSTEM* _getContactsChannelsFS() const { if (_fsExtra) return _fsExtra; return _fs;}; }; From bcfc8d37711ec76e1e51a05a34b5026a69a48b2a Mon Sep 17 00:00:00 2001 From: taco Date: Sat, 6 Sep 2025 14:09:05 +1000 Subject: [PATCH 30/48] improved RescueCLI for dual FS --- examples/companion_radio/MyMesh.cpp | 100 ++++++++++++++-------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 6d4339b0..7847d652 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1524,64 +1524,65 @@ void MyMesh::checkCLIRescueCmd() { // get path from command e.g: "ls /adafruit" const char *path = &cli_command[3]; - + + bool is_fs2 = false; + if (memcmp(path, "UserData/", 9) == 0) { + path += 8; // skip "UserData" + } else if (memcmp(path, "ExtraFS/", 8) == 0) { + path += 7; // skip "ExtraFS" + is_fs2 = true; + } + Serial.printf("Listing files in %s\n", path); + // log each file and directory File root = _store->openRead(path); - if(root){ - File file = root.openNextFile(); - while (file) { - - #if defined(EXTRAFS) || defined(QSPIFLASH) - if (file.isDirectory()) { - Serial.printf("[dir] /FS1/%s\n", file.name()); - } else { - Serial.printf("[file] /FS1/%s (%d bytes)\n", file.name(), file.size()); - } - #else - if (file.isDirectory()) { - Serial.printf("[dir] %s\n", file.name()); - } else { - Serial.printf("[file] %s (%d bytes)\n", file.name(), file.size()); - } - #endif - // move to next file - file = root.openNextFile(); - - } - root.close(); - } - #if defined(EXTRAFS) || defined(QSPIFLASH) - root = _store->openRead(_store->getSecondaryFS(), path); - if(root){ + if (is_fs2 == false) { + if (root) { File file = root.openNextFile(); while (file) { - if (file.isDirectory()) { - Serial.printf("[dir] /FS2/%s\n", file.name()); + Serial.printf("[dir] UserData%s/%s\n", path, file.name()); } else { - Serial.printf("[file] /FS2/%s (%d bytes)\n", file.name(), file.size()); + Serial.printf("[file] UserData%s/%s (%d bytes)\n", path, file.name(), file.size()); } - // move to next file file = root.openNextFile(); - } - root.close(); + root.close(); + } } - #endif + if (is_fs2 == true || strlen(path) == 0 || strcmp(path, "/") == 0) { + if (_store->getSecondaryFS() != nullptr) { + File root2 = _store->openRead(_store->getSecondaryFS(), path); + File file = root2.openNextFile(); + while (file) { + if (file.isDirectory()) { + Serial.printf("[dir] ExtraFS%s/%s\n", path, file.name()); + } else { + Serial.printf("[file] ExtraFS%s/%s (%d bytes)\n", path, file.name(), file.size()); + } + // move to next file + file = root2.openNextFile(); + } + root2.close(); + } + } } else if (memcmp(cli_command, "cat", 3) == 0) { // get path from command e.g: "cat /contacts3" const char *path = &cli_command[4]; - // bool is_fs2 = false; - if (memcmp(path, "FS1/", 4) == 0) { - path += 3; // skip "FS1" - } else if (memcmp(path, "FS2/", 4) == 0) { - path += 3; // skip "FS2" + if (memcmp(path, "UserData/", 9) == 0) { + path += 8; // skip "UserData" + } else if (memcmp(path, "ExtraFS/", 8) == 0) { + path += 7; // skip "ExtraFS" is_fs2 = true; + } else { + Serial.println("Invalid path provided, must start with UserData/ or ExtraFS/"); + cli_command[0] = 0; + return; } // log file content as hex @@ -1605,29 +1606,28 @@ void MyMesh::checkCLIRescueCmd() { } } else if (memcmp(cli_command, "rm ", 3) == 0) { - // get path from command e.g: "rm /adv_blobs" - const char *path = &cli_command[4]; + const char *path = &cli_command[3]; MESH_DEBUG_PRINTLN("Removing file: %s", path); // ensure path is not empty, or root dir if(!path || strlen(path) == 0 || strcmp(path, "/") == 0){ Serial.println("Invalid path provided"); } else { - bool is_fs2 = false; - if (memcmp(path, "FS1/", 4) == 0) { - path += 3; // skip "FS1" - } else if (memcmp(path, "FS2/", 4) == 0) { - path += 3; // skip "FS2" - is_fs2 = true; - } + bool is_fs2 = false; + if (memcmp(path, "UserData/", 9) == 0) { + path += 8; // skip "UserData" + } else if (memcmp(path, "ExtraFS/", 8) == 0) { + path += 7; // skip "ExtraFS" + is_fs2 = true; + } // remove file bool removed; if (is_fs2) { - MESH_DEBUG_PRINTLN("Removing file from FS2: %s", path); + MESH_DEBUG_PRINTLN("Removing file from ExtraFS: %s", path); removed = _store->removeFile(_store->getSecondaryFS(), path); } else { - MESH_DEBUG_PRINTLN("Removing file from FS1: %s", path); + MESH_DEBUG_PRINTLN("Removing file from UserData: %s", path); removed = _store->removeFile(path); } if(removed){ From 132ca7273513523952b1e93739465ad0c9f41345 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Sat, 6 Sep 2025 18:45:08 +0200 Subject: [PATCH 31/48] T1000-E: ensure rails off and radio idle before system off; fix button wake pin --- variants/t1000-e/T1000eBoard.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/variants/t1000-e/T1000eBoard.h b/variants/t1000-e/T1000eBoard.h index 090c1b90..581a9074 100644 --- a/variants/t1000-e/T1000eBoard.h +++ b/variants/t1000-e/T1000eBoard.h @@ -56,6 +56,7 @@ public: digitalWrite(GPS_RESET, LOW); digitalWrite(GPS_SLEEP_INT, LOW); digitalWrite(GPS_RTC_INT, LOW); + digitalWrite(GPS_EN, LOW); pinMode(GPS_RESETB, OUTPUT); digitalWrite(GPS_RESETB, LOW); #endif @@ -68,6 +69,13 @@ public: digitalWrite(PIN_3V3_EN, LOW); #endif + #ifdef PIN_3V3_ACC_EN + digitalWrite(PIN_3V3_ACC_EN, LOW); + #endif + #ifdef SENSOR_EN + digitalWrite(SENSOR_EN, LOW); + #endif + // set led on and wait for button release before poweroff #ifdef LED_PIN digitalWrite(LED_PIN, HIGH); @@ -80,7 +88,7 @@ public: #endif #ifdef BUTTON_PIN - nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH); + nrf_gpio_cfg_sense_input(BUTTON_PIN, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH); #endif sd_power_system_off(); From ac8ec172ef74e21bf0febc5e37c416eac88149e2 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Sat, 6 Sep 2025 20:42:11 +0200 Subject: [PATCH 32/48] T1000-E: refactor GPS initialization; set GPS_RESETB pin as OUTPUT and remove redundant pin settings --- variants/t1000-e/target.cpp | 9 --------- variants/t1000-e/variant.cpp | 3 ++- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/variants/t1000-e/target.cpp b/variants/t1000-e/target.cpp index 06509c4f..2a6380d5 100644 --- a/variants/t1000-e/target.cpp +++ b/variants/t1000-e/target.cpp @@ -146,15 +146,6 @@ void T1000SensorManager::stop_gps() { bool T1000SensorManager::begin() { // init GPS Serial1.begin(115200); - - // make sure gps pin are off - digitalWrite(GPS_VRTC_EN, LOW); - digitalWrite(GPS_RESET, LOW); - digitalWrite(GPS_SLEEP_INT, LOW); - digitalWrite(GPS_RTC_INT, LOW); - pinMode(GPS_RESETB, OUTPUT); - digitalWrite(GPS_RESETB, LOW); - return true; } diff --git a/variants/t1000-e/variant.cpp b/variants/t1000-e/variant.cpp index f17b3a8d..f9328e3b 100644 --- a/variants/t1000-e/variant.cpp +++ b/variants/t1000-e/variant.cpp @@ -69,7 +69,7 @@ void initVariant() pinMode(BATTERY_PIN, INPUT); pinMode(EXT_CHRG_DETECT, INPUT); pinMode(EXT_PWR_DETECT, INPUT); - pinMode(GPS_RESETB, INPUT); + pinMode(GPS_RESETB, OUTPUT); pinMode(PIN_BUTTON1, INPUT); pinMode(PIN_3V3_EN, OUTPUT); @@ -92,5 +92,6 @@ void initVariant() digitalWrite(GPS_VRTC_EN, LOW); digitalWrite(GPS_SLEEP_INT, HIGH); digitalWrite(GPS_RTC_INT, LOW); + digitalWrite(GPS_RESETB, LOW); digitalWrite(LED_PIN, LOW); } From 8521b0eb082604691c7b8d9cecdd006c458a2e9f Mon Sep 17 00:00:00 2001 From: taco Date: Sun, 7 Sep 2025 19:54:42 +1000 Subject: [PATCH 33/48] new version of CustomLFS lib --- platformio.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index c08cd853..4702ba56 100644 --- a/platformio.ini +++ b/platformio.ini @@ -81,8 +81,7 @@ build_flags = ${arduino_base.build_flags} -D EXTRAFS=1 lib_deps = ${arduino_base.lib_deps} - https://github.com/oltaco/CustomLFS @ 0.2 - + https://github.com/oltaco/CustomLFS @ 0.2.1 ; ----------------- RP2040 --------------------- [rp2040_base] From acf6110001038ecb69840ed388274c147e99497f Mon Sep 17 00:00:00 2001 From: taco Date: Sun, 7 Sep 2025 19:59:01 +1000 Subject: [PATCH 34/48] add companion usb to ThinkNode M1 --- variants/thinknode_m1/platformio.ini | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/variants/thinknode_m1/platformio.ini b/variants/thinknode_m1/platformio.ini index a530d259..eeeb692e 100644 --- a/variants/thinknode_m1/platformio.ini +++ b/variants/thinknode_m1/platformio.ini @@ -96,3 +96,30 @@ lib_deps = zinggjm/GxEPD2 @ 1.6.2 bakercp/CRC32 @ ^2.0.0 end2endzone/NonBlockingRTTTL@^1.3.0 + +[env:ThinkNode_M1_companion_radio_usb] +extends = ThinkNode_M1 +build_flags = + ${ThinkNode_M1.build_flags} + -I src/helpers/ui + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_ROTATION=4 + -D QSPIFLASH=1 + -D DISPLAY_CLASS=GxEPDDisplay + -D OFFLINE_QUEUE_SIZE=256 + -D PIN_BUZZER=6 + -D AUTO_SHUTDOWN_MILLIVOLTS=3300 +build_src_filter = ${ThinkNode_M1.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${ThinkNode_M1.lib_deps} + densaugeo/base64 @ ~1.4.0 + zinggjm/GxEPD2 @ 1.6.2 + bakercp/CRC32 @ ^2.0.0 + end2endzone/NonBlockingRTTTL@^1.3.0 \ No newline at end of file From 18be92615bd3593b29d60e68126ba506c0d9be96 Mon Sep 17 00:00:00 2001 From: taco Date: Sun, 7 Sep 2025 20:00:44 +1000 Subject: [PATCH 35/48] add QSPI pins to Lilygo T-Echo --- variants/lilygo_techo/variant.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/variants/lilygo_techo/variant.h b/variants/lilygo_techo/variant.h index da8d81d4..1de62d91 100644 --- a/variants/lilygo_techo/variant.h +++ b/variants/lilygo_techo/variant.h @@ -58,6 +58,19 @@ #define PIN_SPI_SCK (19) #define PIN_SPI_NSS (24) +//////////////////////////////////////////////////////////////////////////////// +// QSPI FLASH + +#define PIN_QSPI_SCK (46) +#define PIN_QSPI_CS (47) +#define PIN_QSPI_IO0 (44) +#define PIN_QSPI_IO1 (45) +#define PIN_QSPI_IO2 (7) +#define PIN_QSPI_IO3 (5) + +#define EXTERNAL_FLASH_DEVICES ZD25WQ16BUIGR +#define EXTERNAL_FLASH_USE_QSPI + //////////////////////////////////////////////////////////////////////////////// // Builtin LEDs From 7363a4f67dbeb95cbecffde5f6e6ddd2ae6fcfa8 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Sun, 7 Sep 2025 14:08:26 +0200 Subject: [PATCH 36/48] Few adjustments after testing. --- src/helpers/nrf52/SerialBLEInterface.cpp | 30 ++++-------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/src/helpers/nrf52/SerialBLEInterface.cpp b/src/helpers/nrf52/SerialBLEInterface.cpp index 283fe405..48708951 100644 --- a/src/helpers/nrf52/SerialBLEInterface.cpp +++ b/src/helpers/nrf52/SerialBLEInterface.cpp @@ -25,28 +25,14 @@ void SerialBLEInterface::begin(const char* device_name, uint32_t pin_code) { char charpin[20]; sprintf(charpin, "%d", pin_code); - Bluefruit.configPrphBandwidth( + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + Bluefruit.configPrphConn(250, BLE_GAP_EVENT_LENGTH_MIN, 16, 16); // increase MTU #ifdef BLE_LOW_POWER - BANDWIDTH_NORMAL + Bluefruit.setTxPower(0); #else - BANDWIDTH_MAX + Bluefruit.setTxPower(4); #endif - ); - Bluefruit.configPrphConn( -#ifdef BLE_LOW_POWER - 400, BLE_GAP_EVENT_LENGTH_MIN, 8, 8 -#else - 250, BLE_GAP_EVENT_LENGTH_MIN, 16, 16 // increase MTU -#endif - ); Bluefruit.begin(); - Bluefruit.setTxPower( -#ifdef BLE_LOW_POWER - 0 -#else - 4 -#endif - ); // Check bluefruit.h for supported values Bluefruit.setName(device_name); Bluefruit.Security.setMITM(true); @@ -98,13 +84,7 @@ void SerialBLEInterface::startAdv() { * https://developer.apple.com/library/content/qa/qa1931/_index.html */ Bluefruit.Advertising.restartOnDisconnect(false); // don't restart automatically as we handle it in onDisconnect - Bluefruit.Advertising.setInterval( -#ifdef BLE_LOW_POWER - 160, 1600 -#else - 32, 244 -#endif - ); // in unit of 0.625 ms + Bluefruit.Advertising.setInterval(32, 1600); Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds From 5370667bd85b9c3c24d7f430a318e975dc9ab5d4 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Sun, 7 Sep 2025 15:44:24 +0200 Subject: [PATCH 37/48] Replaced BLE_LOW_POWER with BLE_TX_POWER & updated usages. --- src/helpers/nrf52/SerialBLEInterface.cpp | 6 +----- src/helpers/nrf52/SerialBLEInterface.h | 4 ++++ variants/t1000-e/platformio.ini | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/helpers/nrf52/SerialBLEInterface.cpp b/src/helpers/nrf52/SerialBLEInterface.cpp index 48708951..4bc9d10a 100644 --- a/src/helpers/nrf52/SerialBLEInterface.cpp +++ b/src/helpers/nrf52/SerialBLEInterface.cpp @@ -27,11 +27,7 @@ void SerialBLEInterface::begin(const char* device_name, uint32_t pin_code) { Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); Bluefruit.configPrphConn(250, BLE_GAP_EVENT_LENGTH_MIN, 16, 16); // increase MTU -#ifdef BLE_LOW_POWER - Bluefruit.setTxPower(0); -#else - Bluefruit.setTxPower(4); -#endif + Bluefruit.setTxPower(BLE_TX_POWER); Bluefruit.begin(); Bluefruit.setName(device_name); diff --git a/src/helpers/nrf52/SerialBLEInterface.h b/src/helpers/nrf52/SerialBLEInterface.h index 12a4f46a..239cf6c4 100644 --- a/src/helpers/nrf52/SerialBLEInterface.h +++ b/src/helpers/nrf52/SerialBLEInterface.h @@ -3,6 +3,10 @@ #include "../BaseSerialInterface.h" #include +#ifndef BLE_TX_POWER +#define BLE_TX_POWER 4 +#endif + class SerialBLEInterface : public BaseSerialInterface { BLEUart bleuart; bool _isEnabled; diff --git a/variants/t1000-e/platformio.ini b/variants/t1000-e/platformio.ini index 88c0b45d..8978ec15 100644 --- a/variants/t1000-e/platformio.ini +++ b/variants/t1000-e/platformio.ini @@ -100,7 +100,7 @@ build_flags = ${t1000-e.build_flags} -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 - -D BLE_LOW_POWER=1 + -D BLE_TX_POWER=0 ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 From 006605ce1de40dbc6f0aad4c7ffbe01d9d1aac6f Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 7 Sep 2025 19:48:02 +0200 Subject: [PATCH 38/48] t1000e: revert GPS_RESETB as an INPUT --- variants/t1000-e/T1000eBoard.h | 2 -- variants/t1000-e/variant.cpp | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/variants/t1000-e/T1000eBoard.h b/variants/t1000-e/T1000eBoard.h index 581a9074..359e5e9a 100644 --- a/variants/t1000-e/T1000eBoard.h +++ b/variants/t1000-e/T1000eBoard.h @@ -57,8 +57,6 @@ public: digitalWrite(GPS_SLEEP_INT, LOW); digitalWrite(GPS_RTC_INT, LOW); digitalWrite(GPS_EN, LOW); - pinMode(GPS_RESETB, OUTPUT); - digitalWrite(GPS_RESETB, LOW); #endif #ifdef BUZZER_EN diff --git a/variants/t1000-e/variant.cpp b/variants/t1000-e/variant.cpp index f9328e3b..f17b3a8d 100644 --- a/variants/t1000-e/variant.cpp +++ b/variants/t1000-e/variant.cpp @@ -69,7 +69,7 @@ void initVariant() pinMode(BATTERY_PIN, INPUT); pinMode(EXT_CHRG_DETECT, INPUT); pinMode(EXT_PWR_DETECT, INPUT); - pinMode(GPS_RESETB, OUTPUT); + pinMode(GPS_RESETB, INPUT); pinMode(PIN_BUTTON1, INPUT); pinMode(PIN_3V3_EN, OUTPUT); @@ -92,6 +92,5 @@ void initVariant() digitalWrite(GPS_VRTC_EN, LOW); digitalWrite(GPS_SLEEP_INT, HIGH); digitalWrite(GPS_RTC_INT, LOW); - digitalWrite(GPS_RESETB, LOW); digitalWrite(LED_PIN, LOW); } 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 39/48] 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 40/48] 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 41/48] 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 42/48] 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 a44b8e626a05692001573337fc15e8834cd904be Mon Sep 17 00:00:00 2001 From: recrof Date: Mon, 8 Sep 2025 13:26:19 +0200 Subject: [PATCH 43/48] set the max_contacts and max_group channels in line with other nrf52 targets --- variants/rak_wismesh_tag/platformio.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/variants/rak_wismesh_tag/platformio.ini b/variants/rak_wismesh_tag/platformio.ini index 6f7d28ba..572919eb 100644 --- a/variants/rak_wismesh_tag/platformio.ini +++ b/variants/rak_wismesh_tag/platformio.ini @@ -69,8 +69,8 @@ extends = rak_wismesh_tag build_flags = ${rak_wismesh_tag.build_flags} -I examples/companion_radio/ui-orig - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${rak_wismesh_tag.build_src_filter} @@ -86,8 +86,8 @@ extends = rak_wismesh_tag build_flags = ${rak_wismesh_tag.build_flags} -I examples/companion_radio/ui-orig - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 From a0e7b47e29ebfc78820f98ac0d87050311b70a60 Mon Sep 17 00:00:00 2001 From: taco Date: Mon, 8 Sep 2025 22:06:15 +1000 Subject: [PATCH 44/48] correct max contacts and channels for some nrf devices --- variants/heltec_t114/platformio.ini | 8 ++++---- variants/lilygo_techo/platformio.ini | 4 ++-- variants/mesh_pocket/platformio.ini | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/variants/heltec_t114/platformio.ini b/variants/heltec_t114/platformio.ini index 696b39d3..dec3282d 100644 --- a/variants/heltec_t114/platformio.ini +++ b/variants/heltec_t114/platformio.ini @@ -161,8 +161,8 @@ extends = Heltec_t114_with_display build_flags = ${Heltec_t114_with_display.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 @@ -181,8 +181,8 @@ extends = Heltec_t114_with_display build_flags = ${Heltec_t114_with_display.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 ; -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 diff --git a/variants/lilygo_techo/platformio.ini b/variants/lilygo_techo/platformio.ini index 91223205..7d64fad7 100644 --- a/variants/lilygo_techo/platformio.ini +++ b/variants/lilygo_techo/platformio.ini @@ -105,8 +105,8 @@ build_flags = ${LilyGo_T-Echo.build_flags} -I src/helpers/ui -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D OFFLINE_QUEUE_SIZE=256 -D UI_RECENT_LIST_SIZE=9 -D AUTO_SHUTDOWN_MILLIVOLTS=3300 diff --git a/variants/mesh_pocket/platformio.ini b/variants/mesh_pocket/platformio.ini index 3fa4c7b8..7c996157 100644 --- a/variants/mesh_pocket/platformio.ini +++ b/variants/mesh_pocket/platformio.ini @@ -70,8 +70,8 @@ extends = Mesh_pocket build_flags = ${Mesh_pocket.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 -D AUTO_OFF_MILLIS=0 @@ -92,8 +92,8 @@ extends = Mesh_pocket build_flags = ${Mesh_pocket.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 - -D MAX_GROUP_CHANNELS=8 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 -D AUTO_OFF_MILLIS=0 ; -D BLE_PIN_CODE=123456 ; -D BLE_DEBUG_LOGGING=1 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 45/48] 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 46/48] 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 47/48] 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); From 5344f04d899cb0fadf5a36ef28b89af312ebd53f Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 9 Sep 2025 18:46:30 +1000 Subject: [PATCH 48/48] * Repeater: slight refactor of 'bridge' instantiation --- examples/simple_repeater/main.cpp | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 4862d7d0..e4d8a454 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -124,10 +124,6 @@ struct ClientInfo { #define MAX_CLIENTS 32 #endif -#ifdef WITH_BRIDGE -AbstractBridge* bridge; -#endif - struct NeighbourInfo { mesh::Identity id; uint32_t advert_timestamp; @@ -154,6 +150,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { float pending_bw; uint8_t pending_sf; uint8_t pending_cr; +#if defined(WITH_RS232_BRIDGE) + RS232Bridge bridge; +#elif defined(WITH_ESPNOW_BRIDGE) + ESPNowBridge bridge; +#endif ClientInfo* putClient(const mesh::Identity& id) { uint32_t min_time = 0xFFFFFFFF; @@ -315,7 +316,7 @@ protected: } void logTx(mesh::Packet* pkt, int len) override { #ifdef WITH_BRIDGE - bridge->onPacketTransmitted(pkt); + bridge.onPacketTransmitted(pkt); #endif if (_logging) { File f = openAppend(PACKET_LOG_FILE); @@ -581,16 +582,12 @@ public: MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables) : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), _cli(board, rtc, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) - { -#ifdef WITH_BRIDGE #if defined(WITH_RS232_BRIDGE) - bridge = new RS232Bridge(WITH_RS232_BRIDGE, _mgr, &rtc); + , bridge(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 + , bridge(_mgr, &rtc) #endif + { memset(known_clients, 0, sizeof(known_clients)); next_local_advert = next_flood_advert = 0; set_radio_at = revert_radio_at = 0; @@ -626,6 +623,10 @@ public: // load persisted prefs _cli.loadPrefs(_fs); + #ifdef WITH_BRIDGE + bridge.begin(); + #endif + radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_tx_power(_prefs.tx_power_dbm); @@ -792,7 +793,7 @@ public: void loop() { #ifdef WITH_BRIDGE - bridge->loop(); + bridge.loop(); #endif mesh::Mesh::loop(); @@ -843,10 +844,6 @@ void setup() { Serial.begin(115200); delay(1000); -#ifdef WITH_BRIDGE - bridge->begin(); -#endif - board.begin(); #ifdef DISPLAY_CLASS