Files
overland-controller/docs/API.md
T

17 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

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:

  • 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.