Most ESP32 projects I see treat the chip as an island that boots, joins a Wi-Fi network, and talks to Home Assistant. The chip is merely treated as a thing that produces data for something else to consume, and it works fine that way. It's the entire reason ESPHome exists, and it's exactly the kind of thing that these boards are actually for.

However, the ESP32 has a built-in protocol called ESP-NOW that lets boards talk directly to each other without even requiring a shared Wi-Fi network. I'd seen it mentioned in passing for quite a while now, but I'd never really used it for anything interesting. I already had an ESP32 Cheap Yellow Display, but Elecrow recently sent me two of their own ESP32 CYD boards as well, and it got me thinking. What if I used ESP-NOW to give each one a single planet from the three-body problem, and see how they keep gravity working between them?

Surprisingly, it works! Well, mostly. Packets get lost, ticks drift, and the simulation does diverge in the ways you'd expect as a result. It's a fun experiment, and it still proves, to me, that ESP-NOW is the most underused feature on the ESP32.

ESP-NOW is Wi-Fi without the Wi-Fi

Just a distributed network

ESP-NOW lives at the data-link layer. It uses the same 2.4 GHz radio as Wi-Fi using the same channels and the same hardware, but instead of going through the full association handshake and IP stack, it sends a vendor-specific action frame straight at a MAC address. The frame goes out, and any board listening on the same channel and configured to receive from that MAC picks it up.

What this means, in practice, is that a board can power up, send a packet, and be heard by another board within milliseconds. Espressif describes ESP-NOW as a millisecond-level, quick-response protocol, and advertises open-space control distances up to around 200 metres under favourable conditions. In my own use, sitting on the same desk, the round-trip is defined by my 33 ms tick interval rather than by the radio.

ESP-NOW used to have a payload limit constrained to just 250 bytes per packet. However, ESP-NOW v2.0 lifted that to 1,470 bytes, close to a standard Ethernet MTU, and enough to carry meaningful state in a single shot. The cap on paired devices is 20, with up to 17 of them encrypted using ESP-NOW’s PMK/LMK key scheme and CCMP protection. It's a small network, but enough for most distributed ESP32 projects.

If you don't want every board to know every other board's MAC address, broadcast mode gets you most of the way there. A node can send to FF:FF:FF:FF:FF:FF, and every ESP-NOW device listening on the same channel can receive it and decide for itself whether the packet matters. In ESP-IDF, you still configure the broadcast address as a peer before sending, but there's no per-device pairing ceremony or MAC-address exchange between all the nodes.

That design makes it possible to build practically anything. For example, a sensor mesh becomes trivial because each node just shouts its reading and a base station picks the ones it cares about. A swarm of small robots can synchronise behaviour by broadcasting state at a fixed tick rate, and the same approach could drive a distributed light show that stays in step without a central controller. ESP-NOW coexists with regular Wi-Fi on the same radio, so the boards can still talk to Home Assistant or pull NTP time at the same time. You don't have to choose.

The catch is that broadcast is unreliable by design. Packets get no acknowledgement and no retry, and a packet that gets drowned out by Wi-Fi traffic on the same channel is simply gone, and the next packet arrives a few ticks later as if nothing happened. You build around that by sending state often and assuming the network is lossy, rather than by sending events once and trusting they arrived. It's much closer to UDP than to a publish-subscribe broker, and it suits projects where the latest reading from each node, give or take a few milliseconds, is good enough.

I split a three-body simulation across three ESP32 boards

One planet per board, and ESP-NOW does the rest

I built a three-body gravity simulator that runs on three ESP32-CYD boards at once, and each board owns exactly one body. Every 33 milliseconds, the board integrates its own body's position and velocity using Newtonian gravity, broadcasts that state over ESP-NOW, and renders all three bodies on its little 320x240 screen. Every board renders all three bodies, but none of them has the whole truth. Each node owns one body and guesses the other two from the last packets it heard, which means the project is really three overlapping simulations trying, imperfectly, to stay in agreement.

The packet itself is tiny. A magic byte, a protocol version, the node ID, the simulation mode, a tick counter, then mass, position, velocity, a flags field, and a checksum. It's all well under the 250-byte v1.0 limit, meaning there's quite a bit left to spare. When a packet from another board lands, the receiver overwrites its cached copy of that body, stamps a last_seen_ms timer, and uses the new state in the next gravity calculation. If a peer's last_seen_ms exceeds 250 ms, the system flips into a "degraded" mode where the top bar turns red and the missing body stops contributing to gravity until packets resume.

There's also a toggle between lockstep and async mode. In lockstep, a board refuses to advance its own tick until it's heard a packet from every peer for the current tick, with a degraded-mode escape if the network is fully down. In async, every board just runs its own clock and uses whatever it last heard from the others. Lockstep keeps the three displays showing roughly the same universe, but async lets them drift.

The three-body problem is already famous for being sensitive to initial conditions. Two simulations starting with positions that differ in the seventh decimal place can end up in totally different orbits a few thousand ticks later. Splitting it across three boards amplifies that. Each node integrates using its own local clock and whatever it last heard from the others, and a 30 ms late packet means the gravity vector this board computes is pulling against a position the other board has already moved past.

That's a distributed-systems failure mode I can watch on a desk, and it's the same class of problem as a multiplayer game predicting where another player will be, or two database replicas disagreeing about which write came first. The underlying issue on three ESP32 boards is exactly the same one I've read in distributed systems papers, except now I can poke it with my finger by flinging one of the bodies, then watch the other two start to fall apart.

Freezing one board immediately changes what the other two do, because the gravity contribution from the frozen body stops moving. Unplugging it triggers degraded mode on its peers within a quarter-second. Plugging it back in resyncs almost instantly because the broadcast packets resume and the cached states snap to the new positions.

The same trick works for sensor meshes and robot swarms

It's not just for silly toys

ESP-NOW handles distributed sensor networks where nodes coordinate without a hub, and the same approach scales down to robot swarms reacting to each other's state at sub-50 ms latency. Because the same chip can run Wi-Fi at the same time, you don't have to give up cloud connectivity to get the local mesh. ESPHome added an official ESP-NOW component in the August 2025 release, so even projects built in YAML can use it without dropping to C.

The limitation is that 20 peers isn't a lot, and broadcast traffic on a shared channel will eventually collide with regular Wi-Fi on the same band. If you're building a coordinated lighting rig across a room, that's not really a problem. However, for a bigger deployment that might actually have a production use, you'd want to look at ESP-MESH or proper Wi-Fi infrastructure. ESP-NOW basically bridges the gap between a single microcontroller and a complete network, but there are genuine uses for that.

In other words, a pile of ESP32 boards is no longer just a pile of independent microcontrollers if you're willing to let them work together. It's a tiny, lossy, latency-bound distributed computer, and the protocol to make that happen is already compatible with every ESP32 gathering dust in your drawer.