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_KEY backend 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 { }
temperaturenumber °CDHT22 · V1
humiditynumber %DHT22 · V1
lightnumber luxBH1750 · V2
motionbooleanPIR · V2
electrical { } · relay { }
voltagenumber VZMPT101B · V2
currentnumber ASCT-013 · V2
powernumber Wderived · V2
relay.statebooleanrelay module · V2
tsISO 8601optional — 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 devices first.
  • V2 roadmap: per-device tokens, relay command channel, OTA firmware delivery, MQTT bridge, WebSocket push.