From fa7eacdd7594844974a8b4f871d3a54e7c913ec9 Mon Sep 17 00:00:00 2001 From: Marcus Rasmussen Date: Tue, 7 Jul 2026 06:12:15 -0500 Subject: [PATCH] Document OpenEmber data model ERD --- README.md | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/README.md b/README.md index 94c10ee..a2867cb 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,138 @@ Recipe (JSON, "Make Now") → RecipeEngine → GrillController → auger/i All fire safety lives in `GrillController`; recipes and the network can only ask it to do safe things. Full design in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md). +## Data model / ERD + +OpenEmber does not use SQL. The ERD below maps the persisted LittleFS JSON files +and the live REST/MQTT status payloads to their firmware structs. + +```mermaid +erDiagram + DEVICE_CONFIG { + string hostname + string wifiSsid + string wifiPass + float kp + float ki + float kd + float grillOffsetF + string mqttHost + int mqttPort + string mqttUser + string mqttPass + bool haDiscovery + } + + RECIPE { + string id PK + string name + string author + bool keepWarm + int stepCount + } + + RECIPE_STEP { + string recipeId FK + int stepIndex PK + string name + float setpointF + bool superSmoke + } + + ADVANCE_RULE { + string recipeId FK + int stepIndex FK + string type + int seconds + int probeIndex + float targetF + } + + RESUME_STATE { + bool active + string recipeId FK + int stepIndex FK + bool paused + } + + STATUS_SNAPSHOT { + string fw + int uptimeS + } + + GRILL_STATUS { + string state + float tempF + float setpointF + bool augerOn + float augerDuty + bool igniterOn + bool fanOn + bool superSmoke + bool lit + int secondsInState + string error + } + + COOK_STATUS { + string state + string recipe + int stepIndex + int stepCount + string stepName + float stepSetpointF + bool stepSuperSmoke + string advanceType + float stepProgress + int stepElapsedS + int stepRemainingS + int percentComplete + int probeIndex + float probeTargetF + } + + PROBE_READING { + int index PK + bool connected + float tempF + } + + NET_STATUS { + string mode + bool connected + string ip + } + + OUTPUT_COMMAND { + float augerSpeed + bool augerReverse + bool igniter + float fanSpeed + bool aux + } + + DEVICE_CONFIG ||--o{ RECIPE : stores + DEVICE_CONFIG ||--|| NET_STATUS : configures + RECIPE ||--|{ RECIPE_STEP : contains + RECIPE_STEP ||--|| ADVANCE_RULE : advances_by + RECIPE ||--o| RESUME_STATE : persisted_as + RECIPE_STEP ||--o| RESUME_STATE : resumes_at + RECIPE ||--o| COOK_STATUS : drives + RECIPE_STEP ||--o| COOK_STATUS : current_step + STATUS_SNAPSHOT ||--|| GRILL_STATUS : includes + STATUS_SNAPSHOT ||--o| COOK_STATUS : includes + STATUS_SNAPSHOT ||--|{ PROBE_READING : includes + STATUS_SNAPSHOT ||--|| NET_STATUS : includes + GRILL_STATUS ||--|| OUTPUT_COMMAND : commands + PROBE_READING ||--o| ADVANCE_RULE : can_satisfy +``` + +Persistence is intentionally small: `/config.json` stores `DeviceConfig`, +`/recipes/.json` stores each `Recipe` with its ordered steps and advance +rules, and `/resume.json` stores the current recipe/step pointer for reboot +recovery. `GET /api/status` and `/state` publish `StatusJson`, which +embeds grill, cook, probe, and network status in one snapshot. + ## Hardware You need a small controller board: an ESP32, an RTD amplifier (MAX31865) for the