A self-healing mesh network for $2 433 MHz radio modules (FS1000A / XY-MK-5V) on Arduino Uno/Nano.
Built and running on real hardware: PIR sensor nodes send alerts to a master with an LCD and buzzer. Nodes that can't reach the master relay through each other, and find a new route when a relay dies.
[node 3] ))) [node 2] ))) [node 1] ))) [MASTER] "Motion at 3"
\ /
))) [node 5] ))) [node 4] ))) ---- used if node 2 dies
- One sketch and one packet format for every board, so any node can relay for any other
- Relay discovery by hop count, with per-hop acks and automatic failover
- Random backoff and short control frames to dodge collisions (ASK, amplitude-shift keying: these on/off radios can't hear each other transmit)
- Duplicate and loop protection: hop limit, sequence numbers, boot counter in EEPROM
- Commands go back down the mesh (arm/disarm from the master)
- No
delay(), so nodes keep listening
Libraries: RadioHead, LiquidCrystal I2C. Set NODE_ID in mesh433.ino (0 = master), then:
arduino-cli compile -b arduino:avr:uno firmware/mesh433
arduino-cli upload -p /dev/ttyUSB0 -b arduino:avr:uno firmware/mesh433| Part | Pin |
|---|---|
| RX data / TX data | D11 / D12 |
| PIR | D2 |
| Button (to GND) | A1 |
| TX LED / alert LED / buzzer | D5 / D6 / D7 |
| 16x2 LCD (I2C 0x27) | A4, A5 |
D10 is taken: RadioHead's RH_ASK driver uses it as its push-to-talk pin by default.
A 17 cm wire antenna helps a lot.
make -C test testThe same mesh.h also compiles on a PC, so routing changes get checked against a model of the ASK link (collisions, loss) before they go on the boards: chains, failover, a lossy 3x3 grid, an 8-node alarm storm, reboots, millis() rollover and downlink fan-out.
- Packet authentication (MAC + counter)
- Heartbeats and jamming alerts
- Sleeping battery nodes
- RFM69 / LoRa radios
Copyright (c) 2026 codemonkeying. All rights reserved.