ESP32-S3 firmware for RealMesh mesh networking nodes with LoRa radio and e-ink display.
- Primary: Heltec Wireless Paper (ESP32-S3 + SX1262 + 2.13" e-ink)
- Radio: SX1262 LoRa @ 868MHz (EU)
- Display: GxEPD2_213_FC1 (250x122px e-ink)
- Platform: ESP32-S3 with Arduino framework
- Frequency: 868 MHz (EU ISM band)
- Spreading Factor: SF12 (maximum range)
- Bandwidth: 125 kHz
- TX Power: 20 dBm
- Coding Rate: 4/5
- Range: Several kilometers line-of-sight
- Flood routing for public messages
- Direct routing for peer-to-peer
- Automatic node discovery
- Heartbeat presence announcements
- Network topology tracking
- Screen 1 (Home): Node identity, online status
- Screen 2 (Messages): Last 3 received messages
- Screen 3 (Node Info): Type, uptime, battery voltage
- Smart refresh (only on content changes)
- PRG button cycles screens
- Low power consumption
- Public channel "svet" (world/everyone) - broadcast to all nodes
- Direct messaging between nodes (node@domain format)
- Message queue and delivery tracking
- CLI and BLE API interfaces
- Mobile app integration
- Device name:
RealMesh-XXXX(last 4 MAC chars) - Service UUID:
12345678-1234-1234-1234-123456789abc - Real-time notifications for incoming messages
- JSON-based command/response protocol
- Interactive command-line interface
- Commands:
status,send,broadcast,scan,name,reboot - Real-time logging and debugging
- Node configuration
# Via pip
pip install platformio
# Or use VS Code extension# Navigate to firmware directory
cd RealMesh-Firmware
# Build for Heltec Wireless Paper
pio run -e heltec_wireless_paper --target upload
# Monitor serial output
pio device monitor
# Combined upload + monitor
pio run -e heltec_wireless_paper --target upload && pio device monitorDevice will:
- Initialize e-ink display
- Generate unique node identity (or load stored)
- Start LoRa radio
- Begin network discovery
- Display node address on screen
- Start BLE advertising
Once connected via serial (pio device monitor @ 115200 baud):
# View node status
status
# Send direct message
send dale@dale Hello from node!
# Broadcast to public channel
broadcast Hello everyone!
# Change node name (requires reboot)
name mynodename mydomain
# Scan for nearby nodes
scan
# Reboot device
reboot
# Show help
help- Service UUID:
12345678-1234-1234-1234-123456789abc - Characteristic UUID:
87654321-4321-4321-4321-cba987654321 - Device name format:
RealMesh-XXXX(last 4 MAC chars)
Get Status:
{"command": "status"}Send Message:
{
"command": "send",
"address": "node@domain",
"message": "Hello!"
}Broadcast (use "svet" or "@"):
{
"command": "send",
"address": "svet",
"message": "Hello everyone!"
}Incoming messages trigger BLE notifications:
{
"type": "message",
"from": "sender@domain",
"message": "Hello!",
"timestamp": 1234567890
}src/
βββ main.cpp # Main application + CLI
βββ RealMeshRadio.cpp # SX1262 LoRa radio driver
βββ RealMeshRouter.cpp # Mesh routing engine
βββ RealMeshNode.cpp # Node identity & management
βββ RealMeshPacket.cpp # Packet serialization
βββ RealMeshDisplay.cpp # Display manager
βββ RealMeshEinkDisplay.cpp # E-ink display adapter
βββ RealMeshMobileAPI.cpp # BLE API implementation
include/
βββ RealMeshConfig.h # Radio/network configuration
βββ RealMeshTypes.h # Core data structures
βββ RealMeshRadio.h # Radio interface
βββ RealMeshRouter.h # Routing interface
βββ RealMeshNode.h # Node interface
βββ RealMeshPacket.h # Packet definitions
βββ RealMeshDisplay.h # Display interface
βββ RealMeshMobileAPI.h # BLE API interface
Edit include/RealMeshConfig.h to modify:
- Radio: Frequency, power, spreading factor, bandwidth
- Network: Heartbeat interval, discovery timeout, TTL
- Display: Refresh timing, screen layout
- BLE: Service/characteristic UUIDs
- Debug: Logging levels
Nodes are identified by nodeId@subdomain format (like email):
- nodeId: Unique node name (e.g., "dale", "node1")
- subdomain: Domain/group name (e.g., "dale", "mesh", "local")
- Full address:
dale@dale,node1@mesh
Identity is stored in NVS (non-volatile storage) and persists across reboots.
# Via CLI
name mynodename mydomain
reboot
# Via BLE API
{"command": "name", "nodeId": "mynodename", "subdomain": "mydomain"}- LoRa radio driver (SX1262)
- Packet serialization/deserialization
- Flood routing for broadcasts
- Direct routing for peer-to-peer
- Node discovery and heartbeats
- E-ink display with multi-screen support
- BLE API with notifications
- Serial CLI interface
- Message queue and delivery
- Node identity storage
- Public channel "svet" support
- Smart display refresh (content-aware)
- Mesh routing (multi-hop)
- ACK/NACK for reliable delivery
- Encryption
- Time synchronization
- Check if
needsUpdateflag is being set on content changes - PRG button should cycle screens (watch serial logs)
- Display refresh takes ~3.7s (e-ink is slow)
- Check device name in serial output:
RealMesh-XXXX - Verify Bluetooth permissions on mobile device
- Try scanning with nRF Connect app first
- Verify antenna is connected (critical!)
- Check frequency matches region (868 MHz EU)
- Monitor RSSI: should be > -120 dBm when receiving
- Use
scancommand to check for nearby nodes
- Must reboot after
namecommand - Check serial logs for "Pending name change detected"
- Identity stored in NVS partition