A battery-friendly ESP-NOW to MQTT bridge for ESPHome, enabling low-power sensors to communicate with Home Assistant without the overhead of Wi-Fi.
This library lets you build battery-powered sensors that last months on a single charge by using ESP-NOW instead of Wi-Fi. It consists of two components:
-
now_mqtt — Runs on battery-powered sensor nodes. Wakes from deep sleep, reads sensors, broadcasts data via ESP-NOW, goes back to sleep. No Wi-Fi stack, no DHCP, no TCP overhead.
-
now_mqtt_bridge — Runs on a mains-powered ESP32 connected to your network. Receives ESP-NOW broadcasts and publishes them to MQTT with Home Assistant auto-discovery.
┌─────────────────┐ ESP-NOW ┌─────────────────┐ MQTT ┌─────────────────┐
│ Battery Node │ ~~~~~~~~~~~~~~~~~~~~> │ Bridge │ ───────────────────>│ Home Assistant │
│ (Deep Sleep) │ (No Wi-Fi) │ (Mains Power) │ (Wi-Fi) │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
This is a fork of Microfire's ESPHomeComponents, specifically the now_mqtt and now_mqtt_bridge components.
Original work by: Microfire LLC
Original article: ESP-NOW & MQTT with ESPHome
The improvements in this fork were developed with assistance from Claude Code.
| Feature | Original | This Fork |
|---|---|---|
| Delivery confirmation | Fire-and-forget | Send callback with retry logic (up to 3 attempts) |
| Wi-Fi channel | Documented as channel 1 only | Configurable via YAML (1-14) |
| Long-range mode | Always on | Configurable via YAML |
| Error handling | ESP_ERROR_CHECK (crashes on failure) |
Graceful logging, failure callbacks |
| Device availability | None | Bridge publishes offline status if no packets received within timeout period (default 5 min) |
| Send result triggers | None | on_send_success / on_send_failure automations |
Add to your ESPHome YAML:
external_components:
- source:
type: git
url: https://github.com/gwlsn/espnowmqtt
components: [now_mqtt] # or [now_mqtt_bridge]esphome:
name: sensor-node
esp32:
board: firebeetle32
external_components:
- source:
type: git
url: https://github.com/gwlsn/espnowmqtt
components: [now_mqtt]
# No wifi: block — this node is ESP-NOW only
now_mqtt:
wifi_channel: 6 # Must match your 2.4GHz AP channel
long_range_mode: true # Optional, default true
on_send_failure:
- logger.log: "Send failed after retries"
i2c:
sda: GPIO21
scl: GPIO22
sensor:
- platform: bme280_i2c
temperature:
name: "Temperature"
humidity:
name: "Humidity"
deep_sleep:
run_duration: 10s
sleep_duration: 10minesphome:
name: espnow-bridge
esp32:
board: esp32dev
external_components:
- source:
type: git
url: https://github.com/gwlsn/espnowmqtt
components: [now_mqtt_bridge]
now_mqtt_bridge:
wifi_channel: 6 # Wifi channel if `wifi:` component not used (if bridge node is on ethernet)
publish_availability: true # Publish online/offline status based on user-defined timeout interval
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
mqtt:
broker: homeassistant.local
username: !secret mqtt_username
password: !secret mqtt_password| Option | Type | Default | Description |
|---|---|---|---|
wifi_channel |
int | 1 | ESP-NOW channel (1-14). Must match bridge/AP. |
long_range_mode |
bool | true | Enable Espressif LR protocol for extended range. |
on_sent |
automation | — | Trigger when data is sent (legacy). |
on_send_success |
automation | — | Trigger when send confirmed successful. |
on_send_failure |
automation | — | Trigger when send fails after all retries. |
| Option | Type | Default | Description |
|---|---|---|---|
wifi_channel |
int | 1 | Fallback channel if wifi: component not used. |
publish_availability |
bool | true | Publish online/offline status (5 min timeout). |
ESP-NOW requires all devices to be on the same 2.4GHz channel. When your bridge is connected to Wi-Fi, it's locked to whatever channel your access point uses.
- Check which channel your 2.4GHz AP uses (or set it manually in your router)
- Use that same channel number in
wifi_channelfor your sensor nodes - It is recommended to lock your 2.4GHz AP to one of channel 1, 6, or 11 (non-overlapping channels) rather than "auto", this prevents the router from switching channels and breaking communication with your sensors
The sensor node has no Wi-Fi stack — OTA updates are not possible. You'll need USB access to reflash. This is an intentional tradeoff for battery life.
The bridge node supports OTA normally since it's always connected to Wi-Fi.
When long_range_mode: true, the sensor uses Espressif's proprietary LR protocol. This extends range significantly but:
- Both ends must be ESP32 (no ESP8266)
- Throughput is reduced (doesn't matter for sensors)
- Both sender and receiver must have it enabled
This project inherits the license from the original Microfire repository. See LICENSE for details.