Integration
ESP32 Device API
One stable REST endpoint per project — devices POST JSON, the platform stores, alerts, and visualizes.
Live Ingestion
ESP32 posts telemetry to a single URL. No websockets, no MQTT broker required for V1.
Shared ingest key (V1)
Devices authenticate with a single X-Ingest-Key stored server-side. Per-device tokens ship in V2.
Extensible payload
Nested groups — environment · electrical · relay — so PIR, BH1750, SCT-013, ZMPT101B & OTA slot in without API changes.
1 · Manual device registration (V1)
Devices are pre-provisioned in the database — no self-registration endpoint in V1. The prototype ESP32-B1-104 is already seeded and ready to ingest.
- Pre-provisioned device id:
ESP32-B1-104 - Shared ingest key stored as the
ESP32_INGEST_KEYbackend secret — flash the same value onto the device. - Additional devices are added by inserting a row in
devices(V2 will expose a UI + per-device tokens).
2 · Send telemetry
Post one frame or a batch. Every group is optional — the DHT22 prototype only sends `environment.temperature` and `environment.humidity`.
MethodPOST
URLhttps://your-app.lovable.app/api/public/ingest/telemetry
HeadersX-Device-Id, X-Ingest-Key, Content-Type: application/json
V1 — DHT22 only
curl -X POST https://your-app.lovable.app/api/public/ingest/telemetry \
-H "Content-Type: application/json" \
-H "X-Device-Id: ESP32-B1-104" \
-H "X-Ingest-Key: $ESP32_INGEST_KEY" \
-d '{
"environment": { "temperature": 24.6, "humidity": 52 }
}'Future — full payload envelope
# V2+ payload — all groups are optional and extensible
curl -X POST https://your-app.lovable.app/api/public/ingest/telemetry \
-H "Content-Type: application/json" \
-H "X-Device-Id: ESP32-B1-104" \
-H "X-Ingest-Key: $ESP32_INGEST_KEY" \
-d '{
"ts": "2026-07-12T10:00:00Z",
"environment": { "temperature": 24.6, "humidity": 52, "light": 340, "motion": true },
"electrical": { "voltage": 229.4, "current": 0.42, "power": 96.4 },
"relay": { "state": true },
"meta": {
"firmwareVersion": "1.1.0",
"hardwareModel": "ESP32-WROOM-32",
"manufacturer": "Espressif",
"uptime": 18234,
"freeHeap": 142336,
"wifiRssi": -58,
"cpuFrequency": 240,
"resetReason": "POWERON_RESET",
"telemetrySequence": 42,
"bootCount": 7,
"sensorStatus": { "dht22": true, "pir": true, "bh1750": "ok" }
}
}'environment { }
| temperature | number °C | DHT22 · V1 |
| humidity | number % | DHT22 · V1 |
| light | number lux | BH1750 · V2 |
| motion | boolean | PIR · V2 |
electrical { } · relay { }
| voltage | number V | ZMPT101B · V2 |
| current | number A | SCT-013 · V2 |
| power | number W | derived · V2 |
| relay.state | boolean | relay module · V2 |
| ts | ISO 8601 | optional — server time if omitted |
3 · Arduino / ESP32 sketch — DHT22 (V1)
Complete working firmware for the prototype. Flash today, extend the payload as new sensors come online.
#include <WiFi.h>
#include <HTTPClient.h>
#include <DHT.h>
const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-pass";
const char* INGEST_URL = "https://your-app.lovable.app/api/public/ingest/telemetry";
const char* DEVICE_ID = "ESP32-B1-104";
const char* INGEST_KEY = "<paste the ESP32_INGEST_KEY value>";
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(115200);
dht.begin();
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(500);
}
void loop() {
if (WiFi.status() != WL_CONNECTED) return;
float t = dht.readTemperature();
float h = dht.readHumidity();
if (isnan(t) || isnan(h)) { delay(2000); return; }
HTTPClient http;
http.begin(INGEST_URL);
http.addHeader("Content-Type", "application/json");
http.addHeader("X-Device-Id", DEVICE_ID);
http.addHeader("X-Ingest-Key", INGEST_KEY);
// V1 payload — only environment.temperature + environment.humidity from DHT22.
// Add "electrical" and "relay" groups later without changing the endpoint.
String payload = String("{\"environment\":{") +
"\"temperature\":" + String(t, 2) + "," +
"\"humidity\":" + String(h, 1) +
"}}";
int code = http.POST(payload);
Serial.printf("ingest -> %d\n", code);
http.end();
delay(5000);
}Operational notes
- Batching: send
{ "frames": [ ... ] }— up to 200 frames per request, same nested shape. - The endpoint URL is stable across deploys and safe to hardcode in firmware.
- Unknown device ids are rejected with 404 — pre-provision them in
devicesfirst. - V2 roadmap: per-device tokens, relay command channel, OTA firmware delivery, MQTT bridge, WebSocket push.