Ver Fonte

Merge branch 'dev' into rep-room-acl

# Conflicts:
#	examples/simple_repeater/main.cpp
Scott Powell há 11 meses atrás
pai
commit
08f91f8d95
35 ficheiros alterados com 1970 adições e 15 exclusões
  1. 16 0
      examples/simple_repeater/MyMesh.cpp
  2. 14 0
      examples/simple_repeater/MyMesh.h
  3. 11 1
      examples/simple_repeater/main.cpp
  4. 1 0
      platformio.ini
  5. 34 0
      src/helpers/AbstractBridge.h
  6. 36 0
      src/helpers/bridges/BridgeBase.cpp
  7. 112 0
      src/helpers/bridges/BridgeBase.h
  8. 196 0
      src/helpers/bridges/ESPNowBridge.cpp
  9. 156 0
      src/helpers/bridges/ESPNowBridge.h
  10. 147 0
      src/helpers/bridges/RS232Bridge.cpp
  11. 141 0
      src/helpers/bridges/RS232Bridge.h
  12. 94 0
      variants/generic-e22/platformio.ini
  13. 41 0
      variants/heltec_ct62/platformio.ini
  14. 45 0
      variants/heltec_e213/platformio.ini
  15. 45 0
      variants/heltec_e290/platformio.ini
  16. 4 4
      variants/heltec_t114/platformio.ini
  17. 41 0
      variants/heltec_t190/platformio.ini
  18. 47 0
      variants/heltec_tracker/platformio.ini
  19. 47 0
      variants/heltec_v2/platformio.ini
  20. 88 0
      variants/heltec_v3/platformio.ini
  21. 45 0
      variants/heltec_wireless_paper/platformio.ini
  22. 45 0
      variants/lilygo_t3s3/platformio.ini
  23. 45 0
      variants/lilygo_t3s3_sx1276/platformio.ini
  24. 41 0
      variants/lilygo_tbeam_SX1262/platformio.ini
  25. 43 0
      variants/lilygo_tbeam_SX1276/platformio.ini
  26. 41 0
      variants/lilygo_tbeam_supreme_SX1262/platformio.ini
  27. 2 2
      variants/lilygo_techo/platformio.ini
  28. 48 0
      variants/lilygo_tlora_v2_1/platformio.ini
  29. 4 4
      variants/mesh_pocket/platformio.ini
  30. 98 0
      variants/meshadventurer/platformio.ini
  31. 4 4
      variants/rak_wismesh_tag/platformio.ini
  32. 84 0
      variants/station_g2/platformio.ini
  33. 96 0
      variants/tenstar_c3/platformio.ini
  34. 17 0
      variants/waveshare_rp2040_lora/platformio.ini
  35. 41 0
      variants/xiao_s3_wio/platformio.ini

+ 16 - 0
examples/simple_repeater/MyMesh.cpp

@@ -207,6 +207,9 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
 }
 
 void MyMesh::logTx(mesh::Packet *pkt, int len) {
+#ifdef WITH_BRIDGE
+  bridge->onPacketTransmitted(pkt);
+#endif
   if (_logging) {
     File f = openAppend(PACKET_LOG_FILE);
     if (f) {
@@ -471,6 +474,15 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
                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);
+#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;
   set_radio_at = revert_radio_at = 0;
@@ -655,6 +667,10 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
 }
 
 void MyMesh::loop() {
+#ifdef WITH_BRIDGE
+  bridge->loop();
+#endif
+
   mesh::Mesh::loop();
 
   if (next_flood_advert && millisHasNowPassed(next_flood_advert)) {

+ 14 - 0
examples/simple_repeater/MyMesh.h

@@ -21,6 +21,20 @@
 #include <RTClib.h>
 #include <target.h>
 
+#ifdef WITH_RS232_BRIDGE
+#include "helpers/bridges/RS232Bridge.h"
+#define WITH_BRIDGE
+#endif
+
+#ifdef WITH_ESPNOW_BRIDGE
+#include "helpers/bridges/ESPNowBridge.h"
+#define WITH_BRIDGE
+#endif
+
+#ifdef WITH_BRIDGE
+extern AbstractBridge* bridge;
+#endif
+
 struct RepeaterStats {
   uint16_t batt_milli_volts;
   uint16_t curr_tx_queue_len;

+ 11 - 1
examples/simple_repeater/main.cpp

@@ -8,6 +8,10 @@
   static UITask ui_task(display);
 #endif
 
+#ifdef WITH_BRIDGE
+AbstractBridge* bridge;
+#endif
+
 StdRNG fast_rng;
 SimpleMeshTables tables;
 
@@ -23,6 +27,10 @@ void setup() {
   Serial.begin(115200);
   delay(1000);
 
+#ifdef WITH_BRIDGE
+  bridge->begin();
+#endif
+
   board.begin();
 
 #ifdef DISPLAY_CLASS
@@ -34,7 +42,9 @@ void setup() {
   }
 #endif
 
-  if (!radio_init()) { halt(); }
+  if (!radio_init()) {
+    halt();
+  }
 
   fast_rng.begin(radio_get_rng_seed());
 

+ 1 - 0
platformio.ini

@@ -47,6 +47,7 @@ build_src_filter =
   +<*.cpp>
   +<helpers/*.cpp>
   +<helpers/radiolib/*.cpp>
+  +<helpers/bridges/BridgeBase.cpp>
   +<helpers/ui/MomentaryButton.cpp>
 
 ; ----------------- ESP32 ---------------------

+ 34 - 0
src/helpers/AbstractBridge.h

@@ -0,0 +1,34 @@
+#pragma once
+
+#include <Mesh.h>
+
+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.
+   * 
+   * @param packet The packet that was received.
+   */
+  virtual void onPacketReceived(mesh::Packet* packet) = 0;
+};

+ 36 - 0
src/helpers/bridges/BridgeBase.cpp

@@ -0,0 +1,36 @@
+#include "BridgeBase.h"
+
+#include <Arduino.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, millis() + BRIDGE_DELAY);
+  } else {
+    _mgr->free(packet);
+  }
+}

+ 112 - 0
src/helpers/bridges/BridgeBase.h

@@ -0,0 +1,112 @@
+#pragma once
+
+#include "helpers/AbstractBridge.h"
+#include "helpers/SimpleMeshTables.h"
+
+#include <RTClib.h>
+
+/**
+ * @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;
+
+  /**
+   * @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);
+
+  /**
+   * @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;
+
+  /** 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);
+};

+ 196 - 0
src/helpers/bridges/ESPNowBridge.cpp

@@ -0,0 +1,196 @@
+#include "ESPNowBridge.h"
+
+#include <WiFi.h>
+#include <esp_wifi.h>
+
+#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, int32_t 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);
+  }
+}
+
+ESPNowBridge::ESPNowBridge(mesh::PacketManager *mgr, mesh::RTCClock *rtc)
+    : BridgeBase(mgr, 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, int32_t len) {
+  // Ignore packets that are too small to contain header + checksum
+  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
+    return;
+  }
+
+  // Validate total packet size
+  if (len > MAX_ESPNOW_PACKET_SIZE) {
+#if MESH_PACKET_LOGGING
+    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 != BRIDGE_PACKET_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];
+  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 - BRIDGE_CHECKSUM_SIZE;
+
+  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(),
+                  received_checksum);
+#endif
+    return;
+  }
+
+#if MESH_PACKET_LOGGING
+  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 + BRIDGE_CHECKSUM_SIZE, payloadLen)) {
+    _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) {
+  handleReceivedPacket(packet);
+}
+
+void ESPNowBridge::onPacketTransmitted(mesh::Packet *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;
+  }
+
+  if (!_seen_packets.hasSeen(packet)) {
+
+    // 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];
+
+    // Write magic header (2 bytes)
+    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 = BRIDGE_MAGIC_SIZE + BRIDGE_CHECKSUM_SIZE;
+    memcpy(buffer + packetOffset, sizingBuffer, meshPacketLen);
+
+    // 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 + BRIDGE_MAGIC_SIZE, meshPacketLen + BRIDGE_CHECKSUM_SIZE);
+
+    // Total packet size: magic header + checksum + payload
+    const size_t totalPacketSize = BRIDGE_MAGIC_SIZE + BRIDGE_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, totalPacketSize);
+
+#if MESH_PACKET_LOGGING
+    if (result == ESP_OK) {
+      Serial.printf("%s: ESPNOW BRIDGE: TX, len=%d\n", getLogDateTime(), meshPacketLen);
+    } else {
+      Serial.printf("%s: ESPNOW BRIDGE: TX FAILED!\n", getLogDateTime());
+    }
+#endif
+  }
+}
+
+#endif

+ 156 - 0
src/helpers/bridges/ESPNowBridge.h

@@ -0,0 +1,156 @@
+#pragma once
+
+#include "MeshCore.h"
+#include "esp_now.h"
+#include "helpers/bridges/BridgeBase.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:
+ * [2 bytes] Magic Header - Used to identify ESPNowBridge packets
+ * [2 bytes] Fletcher-16 checksum of encrypted payload (calculated over payload only)
+ * [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
+ * 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 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);
+
+  /**
+   * 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;
+
+  /**
+   * Size constants for packet parsing
+   */
+  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];
+
+  /** 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, int32_t 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;
+};
+
+#endif

+ 147 - 0
src/helpers/bridges/RS232Bridge.cpp

@@ -0,0 +1,147 @@
+#include "RS232Bridge.h"
+
+#include <HardwareSerial.h>
+
+#ifdef WITH_RS232_BRIDGE
+
+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)
+#error "WITH_RS232_BRIDGE_RX and WITH_RS232_BRIDGE_TX must be defined"
+#endif
+
+#if defined(ESP32)
+  ((HardwareSerial *)_serial)->setPins(WITH_RS232_BRIDGE_RX, WITH_RS232_BRIDGE_TX);
+#elif defined(NRF52_PLATFORM)
+  ((HardwareSerial *)_serial)->setPins(WITH_RS232_BRIDGE_RX, WITH_RS232_BRIDGE_TX);
+#elif defined(RP2040_PLATFORM)
+  ((SerialUART *)_serial)->setRX(WITH_RS232_BRIDGE_RX);
+  ((SerialUART *)_serial)->setTX(WITH_RS232_BRIDGE_TX);
+#elif defined(STM32_PLATFORM)
+  ((HardwareSerial *)_serial)->setRx(WITH_RS232_BRIDGE_RX);
+  ((HardwareSerial *)_serial)->setTx(WITH_RS232_BRIDGE_TX);
+#else
+#error RS232Bridge was not tested on the current platform
+#endif
+  ((HardwareSerial *)_serial)->begin(115200);
+}
+
+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);
+
+    // 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] = (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
+
+    // Calculate checksum over the payload
+    uint16_t checksum = fletcher16(buffer + 4, len);
+    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: RS232 BRIDGE: TX, len=%d crc=0x%04x\n", getLogDateTime(), len, checksum);
+#endif
+  }
+}
+
+void RS232Bridge::loop() {
+  while (_serial->available()) {
+    uint8_t b = _serial->read();
+
+    if (_rx_buffer_pos < 2) {
+      // Waiting for magic word
+      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 == ((BRIDGE_PACKET_MAGIC >> 8) & 0xFF)) {
+          _rx_buffer[_rx_buffer_pos++] = b;
+        }
+      }
+    } 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];
+
+        // 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
+          continue;
+        }
+
+        if (_rx_buffer_pos == len + SERIAL_OVERHEAD) { // Full packet received
+          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: RS232 BRIDGE: RX, len=%d crc=0x%04x\n", getLogDateTime(), len,
+                          received_checksum);
+#endif
+            mesh::Packet *pkt = _mgr->allocNew();
+            if (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
+        }
+      }
+    }
+  }
+}
+
+void RS232Bridge::onPacketReceived(mesh::Packet *packet) {
+  handleReceivedPacket(packet);
+}
+
+#endif

+ 141 - 0
src/helpers/bridges/RS232Bridge.h

@@ -0,0 +1,141 @@
+#pragma once
+
+#include "helpers/bridges/BridgeBase.h"
+
+#include <Stream.h>
+
+#ifdef WITH_RS232_BRIDGE
+
+/**
+ * @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 and frame alignment
+ * - Duplicate packet detection using SimpleMeshTables tracking
+ * - Configurable RX/TX pins via build defines
+ * - Fixed baud rate at 115200 for consistent timing
+ *
+ * Packet Structure:
+ * [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 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
+ * - Define WITH_RS232_BRIDGE_RX with the RX pin number
+ * - Define WITH_RS232_BRIDGE_TX with the TX pin number
+ *
+ * Platform Support:
+ * 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 BridgeBase {
+public:
+  /**
+   * @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
+   *
+   * - 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 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 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;
+
+  /**
+   * @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;
+
+private:
+  /**
+   * 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
+   */
+
+  /**
+   * @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 = BRIDGE_MAGIC_SIZE + BRIDGE_LENGTH_SIZE + BRIDGE_CHECKSUM_SIZE;
+
+  /**
+   * @brief The maximum size of a complete packet on the serial line.
+   *
+   * This is calculated as the sum of:
+   * - 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;
+
+  /** 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;
+};
+
+#endif

+ 94 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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}

+ 41 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../examples/simple_repeater>
+lib_deps =
+  ${Heltec_ct62.lib_deps}
+  ${esp32_ota.lib_deps}
+
 [env:Heltec_ct62_companion_radio_usb]
 extends = Heltec_ct62
 build_flags =

+ 45 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<helpers/ui/E213Display.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<helpers/ui/E213Display.cpp>
+  +<../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 =

+ 45 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<helpers/ui/E290Display.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<helpers/ui/E290Display.cpp>
+  +<../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 =

+ 4 - 4
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

+ 41 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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 =

+ 47 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<helpers/ui/ST7735Display.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<helpers/ui/ST7735Display.cpp>
+  +<../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 =

+ 47 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<helpers/ui/SSD1306Display.cpp>
+;   +<helpers/ui/MomentaryButton.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<helpers/ui/SSD1306Display.cpp>
+  +<helpers/ui/MomentaryButton.cpp>
+  +<../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 =

+ 88 - 0
variants/heltec_v3/platformio.ini

@@ -49,6 +49,51 @@ lib_deps =
   ${esp32_ota.lib_deps}
   bakercp/CRC32 @ ^2.0.0
 
+[env:Heltec_v3_repeater_bridge_rs232]
+extends = Heltec_lora32_v3
+build_flags =
+  ${Heltec_lora32_v3.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_v3.build_src_filter}
+  +<helpers/bridges/RS232Bridge.cpp>
+  +<helpers/ui/SSD1306Display.cpp>
+  +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<helpers/ui/SSD1306Display.cpp>
+  +<../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 =
@@ -186,6 +231,49 @@ lib_deps =
   ${esp32_ota.lib_deps}
   bakercp/CRC32 @ ^2.0.0
 
+[env:Heltec_WSL3_repeater_bridge_rs232]
+extends = Heltec_lora32_v3
+build_flags =
+  ${Heltec_lora32_v3.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_lora32_v3.build_src_filter}
+  +<helpers/bridges/RS232Bridge.cpp>
+  +<../examples/simple_repeater>
+lib_deps =
+  ${Heltec_lora32_v3.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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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}

+ 45 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<helpers/ui/E213Display.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<helpers/ui/E213Display.cpp>
+  +<../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 =

+ 45 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<helpers/ui/SSD1306Display.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<helpers/ui/SSD1306Display.cpp>
+  +<../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 =

+ 45 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<helpers/ui/SSD1306Display.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<helpers/ui/SSD1306Display.cpp>
+  +<../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 =

+ 41 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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 =

+ 43 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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 =

+ 41 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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 =

+ 2 - 2
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

+ 48 - 0
variants/lilygo_tlora_v2_1/platformio.ini

@@ -156,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}
+  +<helpers/ui/SSD1306Display.cpp>
+  +<helpers/bridges/RS232Bridge.cpp>
+  +<../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}
+  +<helpers/ui/SSD1306Display.cpp>
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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}

+ 4 - 4
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

+ 98 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../examples/simple_repeater>
+;   +<helpers/ui/SSD1306Display.cpp>
+; 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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../examples/simple_repeater>
+  +<helpers/ui/SSD1306Display.cpp>
+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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../examples/simple_repeater>
+;   +<helpers/ui/SSD1306Display.cpp>
+; 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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../examples/simple_repeater>
+  +<helpers/ui/SSD1306Display.cpp>
+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}

+ 4 - 4
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

+ 84 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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}

+ 96 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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}

+ 17 - 0
variants/waveshare_rp2040_lora/platformio.ini

@@ -46,6 +46,23 @@ 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_rs232]
+extends = waveshare_rp2040_lora
+build_flags = ${waveshare_rp2040_lora.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=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}
+  +<helpers/bridges/RS232Bridge.cpp>
+  +<../examples/simple_repeater>
+
 [env:waveshare_rp2040_lora_room_server]
 extends = waveshare_rp2040_lora
 build_flags = ${waveshare_rp2040_lora.build_flags}

+ 41 - 0
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}
+;   +<helpers/bridges/RS232Bridge.cpp>
+;   +<../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}
+  +<helpers/bridges/ESPNowBridge.cpp>
+  +<../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}