This project is now a hybrid tracking scaffold:
- Browser BLE scanner and GATT explorer UI.
- Shared event schema/adapters for BLE, Wi-Fi emitter/probe, ESP-NOW, and heartbeat events.
- Local gateway API with SQLite append-only event store.
- Recurring-device and dwell-time analytics endpoints.
- Floor-plan view with receiver anchors and live placements.
- Floor-plan differentiates BLE vs Wi-Fi emitters.
- ESP32 receiver firmware prototype for passive BLE + heartbeat packets.
index.html,app.js,style.css- frontend scanner/explorer/history/analytics/floor-plan.shared/observation-schema.js- canonical event contracts + source adapters.gateway/src/server.js- ingest/query API.gateway/src/db.js- SQLite storage and receiver registry.gateway/src/analytics.js- recurring/dwell/floor placement analytics.firmware/esp32_receiver/esp32_receiver.ino- receiver node prototype.
- Install dependencies:
npm install
- Start gateway + static web server:
npm run dev
- Open the web app (
http://localhost:5500if using defaultservesettings). - In the app, keep gateway URL as
http://localhost:8787.
- Command:
npm run start:bridge:stdin - Environment variables:
GATEWAY_URL(defaulthttp://localhost:8787)GATEWAY_API_KEY(if enabled on gateway)BRIDGE_FLUSH_MS(default1200)BRIDGE_MAX_BATCH(default50)
- Input format: one JSON packet per line on stdin. Packets are forwarded to
POST /api/ingest/espnow.
Use this helper script to read JSON lines from ESP32 serial and feed them into the bridge automatically:
powershell -ExecutionPolicy Bypass -File ".\scripts\windows-esp32-serial-forward.ps1" `
-ComPort "COM6" `
-GatewayUrl "https://your-render-service.onrender.com" `
-GatewayApiKey "your-ingest-api-key" `
-BaudRate 115200Notes:
- Firmware must emit one JSON packet per line on serial (current
esp32_receiver.inodoes this). - Non-JSON serial noise is ignored.
GET /api/healthPOST /api/eventsPOST /api/events/batchPOST /api/ingest/espnowGET /api/eventsGET /api/events/by-stable-key?stableKey=...&minutes=...&limit=...GET /api/devicesGET /api/analytics/recurringGET /api/analytics/dwellPOST /api/receivers/registerPOST /api/receivers/:receiverId/roomGET /api/receiversGET /api/floorplan/live
- Write endpoints (
POST /api/events,/api/events/batch,/api/ingest/espnow, receiver register/update) can be protected with an API key. - Set environment variable
INGEST_API_KEYon the gateway service. - Send key in
x-api-keyheader (orAuthorization: Bearer <key>). - Frontend supports this in Gateway Connection via Gateway API key input.
- Receiver anchors are managed in a server-side cache and persisted to:
gateway/data/receiver-cache.json
- On startup, gateway loads that snapshot and also merges receiver rows from DB.
- Receiver cache writes are debounced to reduce disk churn.
- For restart-safe persistence on Render, attach a persistent disk or move to external DB/KV.
- Expensive read endpoints (
/api/devices, recurring, dwell, by-stable-key trends) use short TTL response caching to reduce repeated compute cost under frequent polling.
- Add ESP-NOW bridge process from gateway radio/serial to
/api/ingest/espnow. - Add Wi-Fi probe packet emit support in firmware (where hardware/SDK supports monitor capture).
- Add robust time sync and offline buffering on receivers.
- Replace simple floor placement heuristic with calibrated multi-receiver solver.