# HTTP API Reference The Cargo ESP32 exposes a local JSON HTTP API for the embedded WebUI, the planned Waveshare ESP32-S3 dashboard, diagnostics, simulators, and future local integrations. Default AP address: ```text http://192.168.4.1 ``` Current API base path: ```text /api/v1 ``` All documented endpoints are local-first and must continue to work without internet access. Consumers should ignore unknown response fields for forward compatibility. Current firmware version: ```text 0.5.0 ``` ## Current Endpoints ```text GET / GET /api/v1/health GET /api/v1/capabilities GET /api/v1/status GET /api/v1/config GET /api/v1/config/export POST /api/v1/relay/set POST /api/v1/config/import POST /api/v1/config/device POST /api/v1/config/relay POST /api/v1/config/temp POST /api/v1/config/bms POST /api/v1/config/save POST /api/v1/config/factory-reset GET /api/v1/config/wifi POST /api/v1/config/wifi POST /api/v1/wifi/connect POST /api/v1/wifi/clear POST /api/v1/temps/scan POST /api/v1/temps/assign POST /api/v1/temps/clear POST /api/v1/bms/setup/enter POST /api/v1/bms/setup/exit POST /api/v1/bms/scan POST /api/v1/bms/select ``` Pre-versioned root routes remain registered as compatibility aliases for existing local clients. New clients should use `/api/v1`. Legacy GET relay routes remain available only as root compatibility aliases: ```text GET /relay/relay_1/on GET /relay/relay_1/off GET /relay/relay_2/on GET /relay/relay_2/off ``` ## Dashboard Contract The Waveshare ESP32-S3 dashboard MVP should use only: ```text GET /api/v1/status POST /api/v1/relay/set ``` The dashboard is a client only. It may cache last-known values for display, but the Cargo ESP32 remains the source of truth for relay state, BMS state, alarms, and configuration. ## Health ### GET /api/v1/health Returns a lightweight liveness response for dashboards and service tools. ```json { "type": "health_response", "ok": true, "api_version": "v1", "firmware_name": "overland-controller", "firmware_version": "0.5.0", "uptime_seconds": 123, "network": { "ap_enabled": true, "ap_ip": "192.168.4.1", "sta_enabled": true, "sta_connected": false, "sta_ssid": "", "sta_ip": "" }, "bms": { "configured": true, "connected": false } } ``` ## Capabilities ### GET /api/v1/capabilities Returns API and feature discovery data for clients. ```json { "type": "capabilities_response", "ok": true, "api_version": "v1", "firmware_name": "overland-controller", "firmware_version": "0.5.0", "endpoints": [ "GET /api/v1/status", "POST /api/v1/relay/set" ], "limits": { "relay_count": 2, "temperature_sensor_count": 8, "runtime_temperature_status_count": 4, "wifi_network_count": 3 }, "features": { "relay_control": true, "temperature_scan": true, "wifi_config": true, "config_backup_restore": true, "bms_setup": true, "uart_json": true, "root_compatibility_aliases": true } } ``` ## Status ### GET /api/v1/status Returns complete controller status. Example requests: GET /api/v1/status GET /api/v1/status?fields=battery,relays,temps Top-level full response: ```json { "type": "status_response", "timestamp": 123456, "battery": {}, "temps": [], "relays": [], "vehicle": {}, "network": {}, "alarms": {}, "system": {}, "config": {} } ``` #### Optional `fields` query parameter `GET /api/v1/status` accepts an optional comma-separated `fields` query parameter. Valid fields: battery, temps, relays, vehicle, network, alarms, system, config If `fields` is omitted, the endpoint returns the complete status payload. If an unknown field is requested, the Cargo ESP32 returns HTTP `400`: { "ok": false, "error": "invalid_field", "details": ["Unknown field 'xyz'"] } ### battery ```json { "source": "jbd_bms", "connected": true, "soc": 70, "voltage": 13.34, "current": 0.0, "remaining_ah": 104.8, "capacity_ah": 150.0, "runtime_hours": 0, "temperature_f": 76.5, "cycle_count": 3, "cell_count": 4, "ntc_count": 2, "cell_voltages": [3.334, 3.333, 3.334, 3.333], "cell_min_voltage": 3.333, "cell_max_voltage": 3.334, "cell_delta_mv": 1, "cells_valid": true } ``` `source` is `jbd_bms` when a configured BMS is expected, even if disconnected. It is `unconfigured` when BMS is not configured. `runtime_hours` is discharge runtime estimated from remaining amp-hours and negative current. Charging time-to-full is derived by clients from `capacity_ah`, `remaining_ah`, and positive current. ### temps Runtime temperature sensors are returned as an array. ```json { "id": "temp_1", "name": "Cabin", "enabled": true, "weather": false, "online": true, "temperature_f": 72.4 } ``` Fields: | Field | Description | |---|---| | `id` | Generic sensor ID | | `name` | User-configured display name | | `enabled` | Configured sensor enabled state | | `weather` | Marks the outside/weather display sensor | | `online` | Current sensor detection state | | `temperature_f` | Current Fahrenheit value, or `null` when offline | Config supports `temp_1` through `temp_8`. Current `/api/v1/status` runtime output is limited to the configured count capped at four sensors. ### relays ```json { "id": "relay_1", "name": "Aux Power", "pin": 16, "enabled": true, "state": false } ``` Valid relay IDs: ```text relay_1 relay_2 ``` Relay names are user configuration. Firmware and clients should use generic IDs for commands. ### vehicle ```json { "ignition_on": false } ``` ### network ```json { "wifi_enabled": true, "uart_connected": true, "ap_enabled": true, "ap_ip": "192.168.4.1", "sta_enabled": true, "sta_connected": false, "sta_ssid": "", "sta_ip": "", "saved_network_count": 2, "saved_networks": [ { "index": 1, "ssid": "Starlink", "priority": 1, "active": false } ] } ``` The AP remains enabled even when STA WiFi is configured or connected. ### alarms ```json { "low_soc": false, "critical_soc": false, "low_voltage": false, "high_battery_temp": false, "cell_imbalance": false, "bms_disconnected": false } ``` ### system ```json { "firmware_name": "overland-controller", "firmware_version": "0.5.0", "build_date": "Jun 7 2026", "build_time": "12:00:00", "uptime_seconds": 123 } ``` ### config `/api/v1/status.config` embeds the same core configuration model used by `GET /api/v1/config`. ## Configuration ### GET /api/v1/config Returns saved controller configuration. ```json { "device_name": "Overland Controller", "relays": [ { "id": "relay_1", "name": "Aux Power", "pin": 16, "enabled": true } ], "bms": { "enabled": true, "name": "House Battery", "address": "AA:BB:CC:DD:EE:FF", "address_type": "public" }, "temperature_sensor_count": 4, "temperature_sensors": [ { "id": "temp_1", "name": "Cabin", "address": "28:AA:BB:CC:DD:EE:FF:00", "enabled": true, "weather": false } ] } ``` ### POST /api/v1/config/device Updates the controller display name. ```json { "device_name": "Overland Controller" } ``` Returns the updated config. ### POST /api/v1/config/relay Updates relay configuration. ```json { "id": "relay_1", "name": "Aux Power", "enabled": true } ``` Fields: | Field | Required | Description | |---|---:|---| | `id` | Yes | `relay_1` or `relay_2` | | `name` | No | User display name | | `enabled` | No | Configured enabled state. When set to `false`, the relay output is forced OFF and future relay control requests are rejected with `relay_disabled`. | Returns the updated config. ### POST /api/v1/config/temp Updates temperature sensor configuration. ```json { "id": "temp_1", "name": "Cabin", "address": "28:AA:BB:CC:DD:EE:FF:00", "enabled": true, "weather": false } ``` Fields: | Field | Required | Description | |---|---:|---| | `id` | Yes | `temp_1` through `temp_8` | | `name` | No | User display name | | `address` | No | DS18B20 address | | `enabled` | No | Configured enabled state | | `weather` | No | Outside/weather display flag | Returns the updated config. ### POST /api/v1/config/bms Updates BMS configuration. ```json { "enabled": true, "name": "House Battery", "address": "AA:BB:CC:DD:EE:FF", "address_type": "public" } ``` Address types: ```text public random ``` Returns the updated config. ### POST /api/v1/config/save Persists the current active configuration. Returns the current config. ### POST /api/v1/config/factory-reset Clears saved configuration and restores firmware defaults. Returns the reset config. ### GET /api/v1/config/export Returns a backup envelope containing controller config and saved WiFi networks. Important: export includes saved WiFi passwords so the backup can be restored. Treat the response as sensitive. ```json { "type": "config_export_response", "ok": true, "api_version": "v1", "firmware_name": "overland-controller", "firmware_version": "0.5.0", "config": { "device_name": "Overland Controller", "temperature_sensor_count": 4, "relays": [], "bms": {}, "temperature_sensors": [] }, "wifi": { "networks": [ { "index": 1, "ssid": "Starlink", "priority": 1, "password_set": true, "password": "password_here" } ] } } ``` ### POST /api/v1/config/import Restores a config backup. The preferred request shape is the same envelope returned by `GET /api/v1/config/export`. ```json { "config": { "device_name": "Overland Controller", "temperature_sensor_count": 4, "relays": [], "bms": {}, "temperature_sensors": [] }, "wifi": { "networks": [] } } ``` The endpoint also accepts the bare config object without an outer `config` field. When `wifi.networks` is included, saved STA WiFi networks are replaced and persisted. It does not initiate STA reconnect; use `POST /api/v1/wifi/connect` after import if needed. Returns the updated config. ## Relay Control ### POST /api/v1/relay/set Preferred relay command endpoint. ```json { "id": "relay_1", "state": true } ``` Response: ```json { "type": "relay_response", "ok": true, "id": "relay_1", "state": true } ``` ### Legacy GET relay routes These routes remain for compatibility but should not be used by new clients: ```text GET /relay/relay_1/on GET /relay/relay_1/off GET /relay/relay_2/on GET /relay/relay_2/off ``` Response: ```json { "ok": true, "id": "relay_1", "state": true } ``` ## WiFi ### GET /api/v1/config/wifi Returns AP/STA WiFi configuration status. Passwords are not returned. ```json { "type": "wifi_config_response", "ok": true, "wifi": { "ap_enabled": true, "sta_enabled": true, "network_count": 2, "active_ssid": "Starlink", "sta_connected": true, "ap_ip": "192.168.4.1", "sta_ip": "192.168.1.50", "networks": [ { "index": 1, "ssid": "Starlink", "priority": 1, "password_set": true } ] } } ``` ### POST /api/v1/config/wifi Preferred multi-network request: ```json { "networks": [ { "ssid": "Starlink", "password": "password_here", "priority": 1 }, { "ssid": "Home WiFi", "password": "password_here", "priority": 2 } ] } ``` Legacy single-network shape is also accepted: ```json { "ssid": "Starlink", "password": "password_here" } ``` Returns WiFi config status. Runtime behavior: - AP remains enabled. - Lower priority numbers are tried first. - If STA disconnects, saved networks are retried by priority. ### POST /api/v1/wifi/connect Attempts STA connection using saved networks by priority. Returns WiFi config status. ### POST /api/v1/wifi/clear Clears saved STA WiFi networks. Returns WiFi config status. ## Temperature Probe Setup ### POST /api/v1/temps/scan Scans the DS18B20 bus and returns unassigned probe addresses. ```json { "type": "temp_scan_response", "ok": true, "devices": [ { "index": 1, "address": "28:AA:BB:CC:DD:EE:FF:00" } ] } ``` Already-assigned probe addresses are hidden from scan results until cleared. ### POST /api/v1/temps/assign Assigns a scanned probe to a logical temperature slot. By ID: ```json { "id": "temp_1", "index": 1 } ``` By slot number: ```json { "slot": 1, "index": 1 } ``` Returns the updated config. ### POST /api/v1/temps/clear Clears one or all temperature assignments. By ID: ```json { "id": "temp_1" } ``` By slot number: ```json { "slot": 1 } ``` Empty POST body clears all temperature assignments. Clearing removes the stored DS18B20 address, disables the slot, clears the weather flag, resets cached temperature state, and persists config. Returns the updated config. ## BMS Setup These endpoints are for BMS discovery and selection. Do not change JBD/Xiaoxiang BLE behavior without explicit approval. ### POST /api/v1/bms/setup/enter Enters BMS setup mode. Response: ```json { "type": "bms_setup_response", "ok": true, "mode": "setup" } ``` ### POST /api/v1/bms/setup/exit Exits BMS setup mode. Response: ```json { "type": "bms_setup_response", "ok": true, "mode": "normal" } ``` ### POST /api/v1/bms/scan Scans for BLE devices. Response: ```json { "type": "ble_scan_response", "devices": [ { "index": 1, "name": "House Battery", "address": "aa:bb:cc:dd:ee:ff", "rssi": -65 } ] } ``` Some BMS devices advertise intermittently. Repeated scans may be needed. ### POST /api/v1/bms/select Selects a BMS from the most recent scan result. ```json { "index": 1 } ``` Response: ```json { "type": "bms_setup_response", "ok": true, "name": "House Battery", "address": "aa:bb:cc:dd:ee:ff" } ``` Selection enables BMS config, saves the selected address, exits setup mode, and reconnects on the next update cycle. ## Embedded WebUI ### GET / Serves the embedded WebUI. The WebUI is a lightweight local setup and phone dashboard surface. It should remain small and self-contained: - Inline HTML/CSS/JS - No external libraries - No large assets - No heavy JavaScript framework The WebUI is not a replacement for the planned Waveshare ESP32-S3 physical dashboard. ## Errors Error responses use this shape: ```json { "ok": false, "error": "invalid_json" } ``` Known error codes: ```text invalid_json invalid_config unknown_relay unknown_temp_sensor invalid_temp_selection invalid_bms_selection invalid_relay_route missing_relay_action invalid_relay_action ``` Some serial/UART errors use `message` instead of `error`; HTTP clients should rely on `error` for HTTP failures. ## Compatibility Current stable API prefix is `/api/v1`, for example `GET /api/v1/status`. Pre-versioned root routes remain registered as local compatibility aliases. Do not add new root-only HTTP API routes. New fields may be added to existing JSON objects. Consumers should ignore unknown fields. ## Not Current API These routes are not part of the current registered HTTP API: ```text POST /api/v1/bms/reconnect POST /api/v1/system/restart ``` If added later, update this document and add contract tests. ### AP configuration The Cargo ESP32 access point uses WPA2 authentication. On first boot, if no AP password exists, the controller generates and saves a unique password. Until the OLED setup display is added, that generated password is printed to Serial during boot. #### GET /api/v1/config/ap Returns AP metadata. The password is never returned. Example response: { "ok": true, "ssid": "OverlandController", "password_set": true, "password_min_length": 8, "password_max_length": 63, "auth": "wpa2" } #### POST /api/v1/config/ap Updates the AP SSID/password and restarts the access point. Example request: { "ssid": "Overland-Controller", "password": "new-secure-password" } Rules: - SSID must be 1-32 characters. - Password must be 8-63 characters. - Password is stored in controller preferences. - Password is not returned by status or config APIs. - Future dashboard pairing will use Cargo-led credential migration so the dashboard follows AP credential changes automatically. #### POST /api/v1/config/ap/reset Resets the Cargo ESP32 access point to default setup credentials. Behavior: - SSID is reset to `OverlandController`. - A new random WPA2 password is generated. - The new credentials are saved in Preferences. - The AP is restarted. - The password is not returned by the API response. The regenerated password must be read from the OLED setup display or Serial Monitor. Example response: { "ok": true, "ssid": "OverlandController", "password_set": true, "auth": "wpa2", "ap_ip": "192.168.4.1", "message": "AP reset to default SSID with regenerated password." } ## Temperature Telemetry DS18B20 temperature support is already implemented in the Cargo ESP/WebUI/API path. Architecture expectation: - Cargo ESP32 owns DS18B20 sensor reads. - Cargo ESP32 exposes temperature telemetry through the API. - WebUI displays temperature telemetry from Cargo ESP state. - Dashboard ESP32-S3 consumes temperature telemetry from the Cargo ESP API. The dashboard should not directly read cargo/fridge DS18B20 sensors for v1. ### Temperature Sensor Grouping Temperature sensors now support a `group` field in addition to the existing `weather` flag. Supported group values: - `cabin` - `fridge` - `outside` - `battery` - `other` The dashboard overview combines multiple online sensors in the same group into one tile. For example, two fridge sensors may display as `34°/45°` under a single `Fridge` tile. Sensors marked `weather: true` continue to be treated as the outside/weather sensor for dashboard display. ### Temperature Sensor Priority Temperature sensors support a numeric `priority` field. Lower numbers are displayed first within a grouped dashboard tile. Example: - Fridge Zone 1: `group=fridge`, `priority=1` - Fridge Zone 2: `group=fridge`, `priority=2` The dashboard displays the grouped fridge tile in priority order, for example `34°/45°`. ### Temperature Slot and Group Display The Cargo ESP currently supports 4 temperature sensor slots. Temperature sensor `group` values are free-form text in the WebUI. Entering a new group name effectively creates that group. The dashboard overview shows the first two available non-weather groups by priority. Multiple sensors in the same group are combined into one tile, such as `34°/45°`. Sensors marked `weather: true` are excluded from the temperature card and shown in the top status strip as `OUT ##°`. ### Temperature High Alerts Each temperature sensor supports optional high-temperature alerting: - `high_alert_enabled` - `high_alert_f` - `high_alert` When enabled and the live sensor temperature exceeds the configured threshold, the Cargo ESP includes `high_alert: true` for that sensor in `/api/v1/status`. The ESP32-S3 dashboard colors the affected grouped temperature tile red if any sensor in that displayed group is in alert. ### Hidden Dashboard Diagnostics The dashboard has a hidden diagnostics overlay. Tap the top status card five times within roughly three seconds to open it. Double-tap the diagnostics overlay to return to the main dashboard. The diagnostics overlay shows dashboard version, uptime, heap, WiFi RSSI/IP, Cargo API status, Cargo firmware version, Cargo uptime, Cargo heap, Cargo AP client count, BMS status, relay count, and basic control-loop state.