Files
MeshCore-Evo/src/helpers/bridges/ESPNowBridge.h

171 lines
5.2 KiB
C
Raw Normal View History

#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