ESP-NOW is a proprietary low-power, low-latency wireless protocol by Espressif that allows ESP32 boards to talk directly to each other without creating full Wi-Fi connections. It’s ideal for building a fast sensor network that reports into Home Assistant through a single ESP-NOW → MQTT gateway.
When to Use ESP-NOW
ESP-NOW is a great fit when:
- Many small nodes, one central point
Star topology: several battery sensor nodes reporting to one always-on gateway ESP32. - Ultra low latency is important
Reaction time in a few milliseconds instead of the hundreds of milliseconds sometimes needed to associate, get IP, and publish MQTT over Wi-Fi. - Battery-powered nodes
Nodes can:- Wake from deep sleep
- Turn on radio just long enough to send an ESP-NOW packet
- Go back to sleep without doing DHCP, TLS, MQTT handshakes, etc.
- Wi-Fi coverage is weak or noisy
ESP-NOW can be more robust in marginal RF conditions because it doesn’t need full Wi-Fi association; it just uses the 2.4 GHz band to blast short frames.
Typical use cases:
- Door/contact sensors
- Room temperature/humidity “pucks”
- Motion detectors
- Meter/pulse counters
- Any low-bandwidth, event-driven sensor
How the ESP-NOW → MQTT → Home Assistant Gateway Works
The typical architecture:
- Sensor nodes (ESP32):
- Use ESP-NOW to send small packets (e.g. JSON or a binary struct) to a single gateway ESP32 MAC address
- Stay in deep sleep between transmissions (optional)
- Gateway ESP32:
- Has Wi-Fi STA mode enabled and is connected to the local network
- Is also configured as an ESP-NOW peer for all sensor nodes
- On each ESP-NOW receive callback:
- Parse the payload
- Publish it to an MQTT broker (e.g. Mosquitto on the Home Assistant machine)
- Home Assistant:
- Uses the standard
mqtt:integration inconfiguration.yaml - Defines sensors that read from topics like
espnow/node1/env,espnow/node2/door, etc.
- Uses the standard
Result: a bunch of ESP-NOW nodes appear to Home Assistant as normal MQTT sensors, but with very low latency and minimal overhead on the sensor side.
Gateway Skeleton: ESP-NOW + MQTT Bridge (ESP32)
Conceptual Arduino-style gateway (simplified, for illustration):
#include <WiFi.h>
#include <esp_now.h>
#include <PubSubClient.h>
#define WIFI_SSID "YOUR_WIFI"
#define WIFI_PASS "YOUR_PASSWORD"
#define MQTT_SERVER "192.168.0.10"
WiFiClient espClient;
PubSubClient mqtt(espClient);
// Example payload structure from nodes
typedef struct {
float temperature;
float humidity;
uint8_t node_id;
} SensorPacket;
void onDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
SensorPacket data;
memcpy(&data, incomingData, sizeof(data));
// Build topic based on node_id
char topic[64];
snprintf(topic, sizeof(topic), "espnow/node%d/env", data.node_id);
// Build JSON payload
char payload[128];
snprintf(payload, sizeof(payload),
"{\"temperature\":%.2f,\"humidity\":%.1f}",
data.temperature, data.humidity);
mqtt.publish(topic, payload);
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(500);
mqtt.setServer(MQTT_SERVER, 1883);
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
}
esp_now_register_recv_cb(onDataRecv);
}
void loop() {
if (!mqtt.connected()) {
while (!mqtt.connected()) mqtt.connect("ESP32_ESPNow_Gateway");
}
mqtt.loop();
}
This gateway:
- Receives ESP-NOW packets from multiple sensor nodes
- Publishes them to MQTT under topics such as
espnow/node1/env
You can extend the packet structure for motion, leak, battery voltage, etc.
Sensor Node Skeleton: ESP-NOW Sender (ESP32)
Each node:
- Knows the gateway’s MAC address
- Wakes, measures, sends, sleeps
#include <WiFi.h>
#include <esp_now.h>
uint8_t gatewayAddress[] = {0x24, 0x6F, 0x28, 0xAA, 0xBB, 0xCC};
typedef struct {
float temperature;
float humidity;
uint8_t node_id;
} SensorPacket;
SensorPacket packet;
void setup() {
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
return;
}
esp_now_peer_info_t peer{};
memcpy(peer.peer_addr, gatewayAddress, 6);
peer.channel = 0;
peer.encrypt = false;
esp_now_add_peer(&peer);
// Read sensors here
packet.node_id = 1;
packet.temperature = 22.5;
packet.humidity = 48.0;
esp_now_send(gatewayAddress, (uint8_t *) &packet, sizeof(packet));
// Optional: deep sleep after sending
esp_deep_sleep(5ULL * 60ULL * 1000000ULL); // 5 minutes
}
void loop() {
// Nothing: node sends once in setup() then sleeps
}
Home Assistant MQTT Integration
On the Home Assistant side, the ESP-NOW network just looks like MQTT.
Example configuration.yaml to read from espnow/node1/env:
mqtt:
sensor:
- name: "Node 1 Temperature"
state_topic: "espnow/node1/env"
value_template: "{{ value_json.temperature }}"
unit_of_measurement: "°C"
- name: "Node 1 Humidity"
state_topic: "espnow/node1/env"
value_template: "{{ value_json.humidity }}"
unit_of_measurement: "%"
Each node simply publishes to a different topic (e.g. node2/env, node3/env) or uses a different node_id inside the JSON.
Ultra Low Latency (Why ESP-NOW Feels “Instant”)
Key advantages vs. classic Wi-Fi + MQTT on every node:
- No Wi-Fi association per wake-up
ESP-NOW doesn’t require each node to connect to an AP and obtain IP → saves hundreds of milliseconds and a lot of current. - Short packets, no TCP/TLS overhead
ESP-NOW uses bare 802.11 action frames, so:- Less airtime
- Lower packet overhead
- Better performance with many small packets
- Gateway handles the expensive work
Only one ESP32 deals with:- Wi-Fi association
- MQTT connection
- Possible TLS, authentication, etc.
All other nodes remain dumb, fast, and frugal: wake → send → sleep.
Typical round-trip:
- Sensor wakes and measures: tens of ms
- ESP-NOW send: a few ms
- Gateway publishes to MQTT → Home Assistant sees update in well under a second
This makes ESP-NOW networks ideal for:
- High-responsiveness motion or door sensors
- Battery nodes that must last months
- Dozens of sensors sharing a single robust gateway into Home Assistant.