State Machine
Mesh-NOW’s behavior is easiest to model as a set of interacting state machines.
Node Lifecycle
stateDiagram-v2
[*] --> Off
Off --> Initializing: mesh_now_init()
Initializing --> Running: ESP-NOW ready, tasks started
Initializing --> Error: init failed
Error --> [*]
Running --> Deinitializing: mesh_now_deinit()
Deinitializing --> Off: cleanup complete
States
| State | Description |
|---|---|
| Off | No mesh activity. ESP-NOW not initialized. |
| Initializing | mesh_now_init() executing. Registering callbacks, adding broadcast peer, creating tasks. |
| Running | Normal operation. Beacon task broadcasting, retransmit task polling, receive callback registered. |
| Error | Initialization failed (ESP-NOW init, callback registration, or task creation). |
| Deinitializing | mesh_now_deinit() executing. Deleting tasks, clearing peers, unregistering callbacks. |
Message State Machine
Broadcast Message (Chat, Group, Presence)
stateDiagram-v2
[*] --> Created: application calls send
Created --> Sent: esp_now_send() OK
Sent --> Delivered: received by peer(s)
Sent --> Lost: not received (out of range)
Delivered --> [*]
Lost --> [*]
Direct Message (with ACK)
stateDiagram-v2
[*] --> Created: application calls send_direct
Created --> Pending: esp_now_send() OK, queued for retransmit
Pending --> Retransmitting: timeout (2s) without ACK
Retransmitting --> Pending: retry < 3, resend
Retransmitting --> Dropped: retry >= 3
Pending --> Cleared: ACK received
Cleared --> [*]
Dropped --> [*]
ACK Message
stateDiagram-v2
[*] --> Generated: received direct message
Generated --> Sent: esp_now_send() OK
Sent --> Relaying: intermediate node, target != self
Sent --> Received: target == self
Relaying --> Received: reaches target
Received --> [*]: clears pending message
Peer States
stateDiagram-v2
[*] --> Unknown
Unknown --> Discovered: beacon or message received
Discovered --> Active: peer_count incremented
Active --> Removed: mesh_now_remove_peer()
Removed --> [*]
Active --> Lost: node reboots (implicit)
Lost --> [*]
Peer Lifecycle
| Transition | Trigger | Action |
|---|---|---|
| Unknown -> Discovered | Beacon or message received from MAC | mesh_now_add_peer() called |
| Discovered -> Active | MAC not in peer table, not self, not duplicate | Added to ESP-NOW subsystem and local table |
| Active -> Removed | mesh_now_remove_peer() called |
Removed from ESP-NOW subsystem and local table |
| Active -> Lost | Node reboots | Peer table cleared on boot |
Message Processing Flow
flowchart TB
A[ESP-NOW receive callback] --> B{Valid length?}
B -->|No| C[Drop]
B -->|Yes| D[Decrypt if encrypted]
D --> E{Message type?}
E -->|Beacon| F[Add peer, sync time, learn zone routes]
E -->|ACK| G{For us?}
G -->|Yes| H[Clear pending message]
G -->|No| I[Forward toward ACK target]
E -->|RREQ| J[Cache, add reverse route, reply or flood]
E -->|RREP| K[Adopt route, relay reply, flush DMs]
E -->|RERR| L[Invalidate routes through announcer, relay]
E -->|Chat| M[Deliver, relay while hop budget remains]
E -->|Direct| N{For us?}
N -->|Yes| O[Send ACK, deliver]
N -->|No| P[Forward along route or flood]
E -->|Group| Q{Group match?}
Q -->|Yes| R[Deliver]
Q -->|No| S[Skip delivery]
R --> T[Relay while hop budget remains]
S --> T
E -->|Presence| U[Deliver, relay while hop budget remains]
E -->|Typing| V{For us?}
V -->|Yes| W[Deliver]
V -->|No| X[Forward toward target]
Timing Constants
| Event | Interval | State Impact |
|---|---|---|
| Beacon broadcast | Every 5000ms | Triggers peer discovery at receivers |
| Retransmit poll | Every 500ms | Checks pending messages for timeout |
| Retransmit timeout | 2000ms | Moves pending message to retransmit state |
| Max retries | 3 | Drops message after 3 failed retransmits |