Skip to content

gretel/rns-if-espnow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

16 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“‘ RNS Interface ESP-NOW

ESP32-based wireless interface for Reticulum Network Stack using ESP-NOW.

⚠️ Current Status

This code is under active development and not yet ready for general use. While core functionality works, some features need testing and optimization:

  • ⚑ Power management strategies
  • πŸ“» Long range mode behavior
  • 🎯 Channel selection refinement
  • 🌊 Network congestion handling

πŸ€” Why?

ESP-NOW provides an elegant and efficient transport layer for Reticulum networks:

  • πŸ—οΈ No infrastructure required - direct peer-to-peer
  • πŸš€ High bandwidth (up to 1Mbps)
  • πŸ“‘ Long range capability with ESP32
  • ⚑ Low latency (<4ms)
  • πŸ’° Built into most ESP32 (~$5)
  • 🧩 Simple protocol design
  • βš™οΈ Zero configuration needed

🐍 Why MicroPython?

MicroPython provides ideal characteristics for RNS ESP-NOW interface development:

  • Rapid Development: Interactive REPL and runtime execution enables fast prototyping and testing without compilation cycles
  • Python Ecosystem: Directly aligns with Reticulum's Python codebase, allowing shared implementation patterns and code reuse
  • Performance: AsyncIO enables efficient concurrent I/O handling for UART and ESP-NOW operations

πŸ“Έ Flashing

Before starting, MicroPython needs to be flashed to the ESP32. The mpremote tool is recommended for device management and file operations.

🌐 System Design

Modules

classDiagram
    class RNSNOW {
        -Logger log
        -HDLCProcessor hdlc
        -Fragmentor fragmentor
        -Hardware hw
        -UART uart
        +__init__(baud: int)
        +send_espnow(data: bytes, raw: bool)
        -_send_ping()
        -_initial_channel_scan()
        +process_uart()
        +process_espnow()
    }

    class HDLCProcessor {
        -Logger log
        -bytearray rx_buffer
        -bool in_frame
        -bool escape
        +frame_data(data: bytes)
        +process_byte(byte: int)
        -_escape_hdlc(data: bytes)
    }

    class Fragmentor {
        -Logger log
        -int _packet_id
        -dict _reassembly_buffers
        +fragment_data(data: bytes)
        +process_fragment(fragment: bytes)
        -_next_packet_id()
    }

    class Hardware {
        -Pin led
        -Pin btn
        -int last_button_press
        -callback button_callback
        +blink_led(times: int)
        -_check_buttons()
    }

    class Logger {
        -str name
        -int level
        +debug(msg: str)
        +info(msg: str)
        +warning(msg: str)
        +error(msg: str)
        +critical(msg: str)
        +exc(e: Exception)
    }

    RNSNOW --> HDLCProcessor
    RNSNOW --> Fragmentor
    RNSNOW --> Hardware
    RNSNOW --> Logger
    HDLCProcessor --> Logger
    Fragmentor --> Logger
Loading

Framing is done using HDLC from/to Reticulum.

πŸ”Œ UART Processing

  • Interfaces with Reticulum daemon (rnsd)
  • Configurable pins and baud rate
  • Handles frame buffering and delimiting

πŸ“» ESP-NOW Transport

  • WiFi station mode (no access point needed - "ad hoc")
  • Group broadcast approach (think VLAN)
  • Long range mode enabled (needs testing)
  • Power management optimized (also needs testing)
  • Channel scanning for automatic peer discovery

πŸ”„ Data Flow

UART to ESP-NOW

  1. Buffer serial data
  2. Process complete frames
  3. Broadcast via ESP-NOW

ESP-NOW to UART

  1. Receive broadcasts
  2. Handle special frames (ping/probe)
  3. Forward regular frames to UART

πŸ‘Ύ Hardware

Simply put, it's very simple πŸ˜‰

graph TD
    subgraph "ESP32 Development Board"
        CPU[ESP32 MCU]
        
        subgraph "Peripherals"
            LED[Status LED]
            BTN[User Button]
        end
        
        subgraph "Communications"
            UART0[UART0 - MicroPython REPL]
            UART1[UART1 - RNS Serial Interface]
            WIFI[WiFi Radio - ESP-NOW]
        end
    end
    
    subgraph "Connections"
        HOST[Host Computer]
        RNSD[RNS Daemon]
        AIR[Wireless Medium]
    end
    
    CPU --> LED
    BTN --> CPU
    CPU <--> UART0
    CPU <--> UART1
    CPU <--> WIFI
    
    UART0 <--> HOST
    UART1 <--> RNSD
    WIFI <--> AIR
    
    classDef peripheral fill:#f9f,stroke:#333
    classDef comm fill:#bbf,stroke:#333
    class LED,BTN peripheral
    class UART0,UART1,WIFI comm
Loading

Due to the lack of CDC support but on the rpi ports of MicroPython we have to use two UARTs. This may change in the future.

πŸ“‘ RF Characteristics

Bandwidth & Performance

Real-world performance varies significantly based on environment and settings:

  • Standard Mode

    • Open environment: ~214 Kbps
    • Ideal conditions: ~555 Kbps
    • Maximum PHY rate: 1 Mbps
  • Long Range Mode

    • 250K mode: ~150 Kbps practical
    • 500K mode: ~300 Kbps practical
    • Actual throughput depends heavily on environment
  • Packet Size

    • Maximum payload: 250 bytes
    • Limited by IEEE 802.11 vendor action frame spec
    • Larger transfers must be split into multiple packets

Performance can be optimized by:

  • Finding optimal channel with least interference
  • Using appropriate antenna placement
  • Balancing range vs speed requirements
  • Implementing efficient packet fragmentation
  • Choosing appropriate retry strategies

πŸš‡ Long Range Mode

ESP-NOW on ESP32 supports a special long range (LR) mode that extends communication range at the cost of bandwidth. When enabled:

  • PHY rate drops to 512Kbps or 256Kbps
  • Range can increase by up to 2-3x compared to standard mode
  • Signal penetration through obstacles improves
  • More robust in noisy RF environments
  • Power consumption increases slightly

The tradeoff between range and speed could be configured through the ESP-NOW API:

esp_wifi_config_espnow_rate(WIFI_IF_STA, WIFI_PHY_RATE_LORA_250K);  // Longest range
esp_wifi_config_espnow_rate(WIFI_IF_STA, WIFI_PHY_RATE_LORA_500K);  // Balanced

Unfortunately, setting rate requires IDF>=4.3.0 which MicroPython currently does not support.

πŸ” Network Discovery

πŸ“‘ Channel Selection

  1. Scan preferred channels first
  2. Send probes, count responses
  3. Select best channel
  4. Eventually fall back to default

πŸ“¦ Special Frames

  • GROUP_ID prefixed:
    • PING: Connectivity test
    • PROBE: Channel scanning
    • ACK: Probe response

🎯 Development Target

While this interface should work on any ESP32-based platform, current development and testing is being done exclusively on the M5StickC Plus. Development is ongoing and testing with other ESP32 platforms will follow as the project matures.

Technical specifications and setup instructions currently assume the M5StickC Plus hardware configuration. When using other ESP32 boards, you may need to adjust pin assignments and hardware-specific settings accordingly.

🀝 Contributing

Contributions welcome! Please:

  • πŸ› Report bugs
  • πŸ’‘ Suggest features
  • πŸ”§ Submit pull requests
  • πŸ“’ Share your experiences

🎫 Sponsor

This work is supported by the Critical Decentralisation Cluster (CDC) - thank you very much!

πŸ“„ License

MIT License - See LICENSE file for full details.

References