The idea
A small firmware project for the ESP32 microcontroller that continuously scans for Bluetooth Low Energy advertisements from devices you configure (phones, watches, earbuds). When a known device is in range the board publishes home to an MQTT topic; when it disappears the topic flips to away. Drop one board per room, point them all at a local Mosquitto broker, and any automation platform — Home Assistant, Node-RED, n8n — gets room-level presence without a camera, a subscription, or a cloud handshake.
Why build this
Passive infrared sensors miss people who are sitting still. Camera-based solutions create a permanent video record of your home. BLE scanning sidesteps both problems: it detects occupancy rather than motion, and it works silently in the background using hardware your household already carries. ESP32 boards cost under $5, Mosquitto runs on any Raspberry Pi or home server, and the firmware is straightforward enough to ship in a weekend. The main gap is a well-documented, opinionated reference implementation that just works out of the box.
Stack sketch
- Firmware: ESP32 with ESP-IDF (C) or MicroPython; uses the built-in BLE scan API to look for known service UUIDs or locally-administered MAC addresses
- Message transport: MQTT over Wi-Fi; topics follow
presence/<room>/<device>so Home Assistant's MQTT Discovery can auto-register sensors - Broker: Mosquitto on a Raspberry Pi, home server, or the same machine running Home Assistant
- Bridge script: A small Python service (
asyncio+aiomqtt) that aggregates readings from multiple ESP32 nodes, applies a debounce window (60 s), and republishes stable presence state - Optional history: InfluxDB 2 + Grafana for an occupancy heatmap over time
Scope for v1
- Single-room firmware that scans for up to eight configured device identifiers
- Publishes
homeorawayevery 30 seconds (interval configurable via a JSON file on the device filesystem) - Python bridge that fans in from multiple nodes and handles debounce before republishing
- Home Assistant YAML snippets in the README so users can copy-paste sensor definitions
- OTA update support via ESP-IDF's built-in partition scheme so boards don't need physical reflashing
Deliberately out of scope for v1: a web dashboard, phone app, automatic device discovery, or any cloud connectivity. Users add device identifiers (MAC or UUID) manually to a config file.
Where it could go
Once you have multiple nodes deployed, the natural next step is RSSI-based room inference rather than binary presence. Each ESP32 reports signal strength to the bridge, which applies a weighted average across nodes to guess which room has the strongest signal. This catches the "hallway" problem — a phone detected by two rooms simultaneously — and resolves it without changing the firmware at all, just the bridge logic.
The other extension is BLE tag support. iOS and Android randomize MAC addresses by default, which makes stable tracking tricky. Cheap Bluetooth tags ($3–5 each, Tile-style or ATC1441 clone) advertise a fixed UUID and can be clipped to a pet collar, attached to a key fob, or sewn into a bag. Supporting tags alongside phones turns the system into whole-household presence tracking, not just phone tracking.
Watch out for
iOS 14+ and Android 10+ both randomize BLE MAC addresses every few minutes, so tracking by MAC alone is unreliable. The firmware needs to match on BLE service UUIDs or manufacturer-specific advertisement data instead — most phones broadcast stable identifiers at that layer even when the MAC rotates.