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