19 KiB
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:
http://192.168.4.1
Current API base path:
/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:
0.5.0
Current Endpoints
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:
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:
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.
{
"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.
{
"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:
{
"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
{
"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.
{
"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
{
"id": "relay_1",
"name": "Aux Power",
"pin": 16,
"enabled": true,
"state": false
}
Valid relay IDs:
relay_1
relay_2
Relay names are user configuration. Firmware and clients should use generic IDs for commands.
vehicle
{
"ignition_on": false
}
network
{
"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
{
"low_soc": false,
"critical_soc": false,
"low_voltage": false,
"high_battery_temp": false,
"cell_imbalance": false,
"bms_disconnected": false
}
system
{
"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.
{
"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.
{
"device_name": "Overland Controller"
}
Returns the updated config.
POST /api/v1/config/relay
Updates relay configuration.
{
"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.
{
"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.
{
"enabled": true,
"name": "House Battery",
"address": "AA:BB:CC:DD:EE:FF",
"address_type": "public"
}
Address types:
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.
{
"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.
{
"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.
{
"id": "relay_1",
"state": true
}
Response:
{
"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:
GET /relay/relay_1/on
GET /relay/relay_1/off
GET /relay/relay_2/on
GET /relay/relay_2/off
Response:
{
"ok": true,
"id": "relay_1",
"state": true
}
WiFi
GET /api/v1/config/wifi
Returns AP/STA WiFi configuration status. Passwords are not returned.
{
"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:
{
"networks": [
{
"ssid": "Starlink",
"password": "password_here",
"priority": 1
},
{
"ssid": "Home WiFi",
"password": "password_here",
"priority": 2
}
]
}
Legacy single-network shape is also accepted:
{
"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.
{
"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:
{
"id": "temp_1",
"index": 1
}
By slot number:
{
"slot": 1,
"index": 1
}
Returns the updated config.
POST /api/v1/temps/clear
Clears one or all temperature assignments.
By ID:
{
"id": "temp_1"
}
By slot number:
{
"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:
{
"type": "bms_setup_response",
"ok": true,
"mode": "setup"
}
POST /api/v1/bms/setup/exit
Exits BMS setup mode.
Response:
{
"type": "bms_setup_response",
"ok": true,
"mode": "normal"
}
POST /api/v1/bms/scan
Scans for BLE devices.
Response:
{
"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.
{
"index": 1
}
Response:
{
"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:
{
"ok": false,
"error": "invalid_json"
}
Known error codes:
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:
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:
cabinfridgeoutsidebatteryother
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_enabledhigh_alert_fhigh_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.