Compare commits
70
Commits
v0.3.0-working
...
v0.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2966518da | ||
|
|
90c4490a3d | ||
|
|
f1683897bc | ||
|
|
19538781e8 | ||
|
|
cf722a7287 | ||
|
|
f5e2c5fd24 | ||
|
|
9cb06b86ff | ||
|
|
6acfa61ddb | ||
|
|
8004ce86e1 | ||
|
|
fc4999b9d2 | ||
|
|
989e0458aa | ||
|
|
094bfb33a4 | ||
|
|
bd00b6e7ca | ||
|
|
c49361488d | ||
|
|
8dfcc63688 | ||
|
|
69db0d7fe5 | ||
|
|
0f7697e9dd | ||
|
|
c43f121dc8 | ||
|
|
730da5482f | ||
|
|
954bef4291 | ||
|
|
75c1d4d909 | ||
|
|
46d52052e3 | ||
|
|
bc56514489 | ||
|
|
d4dff6d49c | ||
|
|
567b84261e | ||
|
|
dc70e4f8e5 | ||
|
|
9c2069dd28 | ||
|
|
0f7b5206b6 | ||
|
|
2f973a279c | ||
|
|
40ce727986 | ||
|
|
b665bf1c82 | ||
|
|
594edc3dd9 | ||
|
|
4474b6d11a | ||
|
|
e7965f4b99 | ||
|
|
512a1aa302 | ||
|
|
a721f6b539 | ||
|
|
6cc93667ab | ||
|
|
d87080d23f | ||
|
|
ec39182bbd | ||
|
|
d9b851aa53 | ||
|
|
f609f2ea43 | ||
|
|
dc8340a1ec | ||
|
|
fdf3d657b6 | ||
|
|
e1ea6a19e0 | ||
|
|
a0cad804a2 | ||
|
|
3a386437fa | ||
|
|
63f1d691ec | ||
|
|
a2b747d741 | ||
|
|
78f6a9e53e | ||
|
|
4fe7491afa | ||
|
|
a3b335e708 | ||
|
|
7ab75408b7 | ||
|
|
df9b49292d | ||
|
|
f05f259523 | ||
|
|
3f02f6146e | ||
|
|
5d8afd2e4a | ||
|
|
757b9acdd9 | ||
|
|
2f61c968a2 | ||
|
|
573f24e0a9 | ||
|
|
a86a0b58c4 | ||
|
|
47dd9a7b8b | ||
|
|
1ee73ad00d | ||
|
|
261d8983df | ||
|
|
8a31048646 | ||
|
|
594fa3b9ce | ||
|
|
af8de8fa1e | ||
|
|
175630cda2 | ||
|
|
0043e50533 | ||
|
|
961ab3758b | ||
|
|
9d506135a0 |
@@ -0,0 +1,41 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Workflow
|
||||
|
||||
- Inspect the repo before changing code.
|
||||
- Make minimal, targeted changes.
|
||||
- Preserve existing architecture.
|
||||
- Update docs when APIs, behavior, wiring, or hardware assumptions change.
|
||||
- Run available validation/tests before finishing.
|
||||
- Use meaningful commits.
|
||||
- Explain what changed, why, and any side effects.
|
||||
|
||||
## Critical Constraints
|
||||
|
||||
- Do not modify JBD/Xiaoxiang BLE behavior without explicit approval.
|
||||
- Do not change BLE timeout, reconnect, scan, or BMS-selection behavior without approval.
|
||||
- Cargo ESP32 remains the controller, API server, WebUI host, and source of truth.
|
||||
- Dashboard ESP32-S3 is a client only.
|
||||
- Do not move relay/BMS/control authority into the dashboard.
|
||||
- Do not hardcode install-specific names into firmware.
|
||||
- Preserve API compatibility when practical.
|
||||
- Prefer HTTP API integration over custom protocols.
|
||||
|
||||
## Hardware Rules
|
||||
|
||||
- ESP32 relay board outputs trigger external fused automotive relays/contactors.
|
||||
- Do not route fridge, Starlink, inverter, or other major load current through the ESP32 relay board.
|
||||
- Dashboard target is Waveshare ESP32-S3-Touch-LCD-5.
|
||||
- Dashboard connects to Cargo ESP32 AP over WiFi.
|
||||
- Dashboard is powered from ACC switched 12V into VIN.
|
||||
- Do not power dashboard from OBD-II pin 16 in the permanent install.
|
||||
- CAN termination must stay disabled when connected to vehicle CAN.
|
||||
|
||||
## Documentation
|
||||
|
||||
Primary docs:
|
||||
|
||||
- docs/ARCHITECTURE.md
|
||||
- docs/HARDWARE.md
|
||||
- docs/ROADMAP.md
|
||||
- docs/API.md when API changes
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
|
||||
---
|
||||
|
||||
# Relay Wiring
|
||||
|
||||
Current architecture uses the onboard relay contacts
|
||||
as the load switching device.
|
||||
|
||||
Positive-side switching:
|
||||
|
||||
Battery +
|
||||
|
|
||||
Fuse
|
||||
|
|
||||
Relay COM
|
||||
|
|
||||
Relay NO
|
||||
|
|
||||
Load +
|
||||
|
||||
Load negative returns directly to the ground bus.
|
||||
|
||||
Relay 1:
|
||||
|
||||
Starlink Mini
|
||||
|
||||
Relay 2:
|
||||
|
||||
Fridge
|
||||
|
||||
@@ -1,260 +1,107 @@
|
||||
Overland Controller
|
||||
# Overland Controller
|
||||
|
||||
A modular ESP32 and Raspberry Pi Pico based monitoring and control system for overland vehicles, campers, trailers, van builds, and mobile power systems.
|
||||
Overland Controller is a local-first vehicle power and monitoring system designed for overland, camping, and off-grid vehicle use.
|
||||
|
||||
The project uses a distributed architecture consisting of:
|
||||
Current target vehicle:
|
||||
|
||||
* An ESP32 controller mounted near the battery and electrical system
|
||||
* A Raspberry Pi Pico 2 W dashboard mounted in the vehicle cabin
|
||||
* Local WiFi communication
|
||||
* Offline-first operation
|
||||
* Expandable sensor and relay support
|
||||
* Open JSON APIs for future integrations
|
||||
overland vehicle
|
||||
|
||||
⸻
|
||||
The firmware itself is intended to stay vehicle-agnostic.
|
||||
|
||||
Project Goals
|
||||
## Current Version
|
||||
|
||||
The goal of Overland Controller is to provide a self-hosted alternative to commercial vehicle monitoring systems.
|
||||
0.5.0
|
||||
|
||||
The platform is designed to monitor and control:
|
||||
## What It Does
|
||||
|
||||
* House battery systems
|
||||
* Temperature sensors
|
||||
* Auxiliary relays
|
||||
* Vehicle telemetry
|
||||
* Overland accessories
|
||||
* Future automation systems
|
||||
The ESP32 controller currently provides:
|
||||
|
||||
The system should be:
|
||||
- Local WebUI
|
||||
- HTTP API
|
||||
- UART JSON protocol
|
||||
- USB serial maintenance commands
|
||||
- Relay control
|
||||
- JBD/Xiaoxiang BLE BMS telemetry
|
||||
- DS18B20 temperature sensor support
|
||||
- Persistent configuration
|
||||
- WiFi AP mode
|
||||
- WiFi STA mode
|
||||
- Multiple saved WiFi networks with priority
|
||||
|
||||
* Local-first
|
||||
* Cloud independent
|
||||
* Vehicle independent
|
||||
* Easy to troubleshoot
|
||||
* Easy to expand
|
||||
## Current Hardware
|
||||
|
||||
⸻
|
||||
Current controller:
|
||||
|
||||
Current Architecture
|
||||
- ESP32 2-relay board
|
||||
|
||||
ESP32 Controller
|
||||
Primary dashboard target:
|
||||
|
||||
The ESP32 acts as the primary control and monitoring node.
|
||||
- Waveshare ESP32-S3-Touch-LCD-5
|
||||
- WiFi client to the Cargo ESP32 access point
|
||||
- HTTP API integration
|
||||
|
||||
Current responsibilities:
|
||||
Optional future expansion:
|
||||
|
||||
* JBD/Xiaoxiang BMS monitoring
|
||||
* DS18B20 temperature monitoring
|
||||
* Relay control
|
||||
* Configuration management
|
||||
* Local web API
|
||||
* WiFi Access Point
|
||||
* Dashboard communications
|
||||
* System status reporting
|
||||
- Pi Zero for long-term logging, charts, and local integrations
|
||||
|
||||
⸻
|
||||
## WebUI
|
||||
|
||||
Raspberry Pi Pico Dashboard
|
||||
The embedded WebUI is served directly from the ESP32.
|
||||
|
||||
The Pico dashboard acts as the user interface.
|
||||
Access through AP mode:
|
||||
|
||||
Planned responsibilities:
|
||||
http://192.168.4.1
|
||||
|
||||
* Touchscreen dashboard
|
||||
* Relay control
|
||||
* Battery monitoring display
|
||||
* Temperature monitoring display
|
||||
* Vehicle status display
|
||||
* Configuration interface
|
||||
* Alarm notifications
|
||||
Access through STA mode:
|
||||
|
||||
⸻
|
||||
http://<controller-ip>
|
||||
|
||||
Current Features
|
||||
The WebUI includes:
|
||||
|
||||
Battery Monitoring
|
||||
- Overview tab
|
||||
- Battery tab
|
||||
- Config tab
|
||||
|
||||
Supported:
|
||||
The Config tab is the preferred initial setup workflow.
|
||||
|
||||
* JBD / Xiaoxiang Bluetooth BMS
|
||||
## Core Concepts
|
||||
|
||||
Available telemetry:
|
||||
The ESP32 is the source of truth.
|
||||
|
||||
* State of charge
|
||||
* Voltage
|
||||
* Current
|
||||
* Remaining capacity
|
||||
* Total capacity
|
||||
* Runtime estimates
|
||||
* Temperature
|
||||
* Cycle count
|
||||
* Cell voltages
|
||||
* Cell imbalance
|
||||
* Cell count
|
||||
The Waveshare ESP32-S3 dashboard is a client-only local display and control surface.
|
||||
|
||||
⸻
|
||||
The WebUI is used for setup, configuration, and phone access from camp.
|
||||
|
||||
Temperature Monitoring
|
||||
A future Pi Zero may be added for long-term logging or richer local integrations, but it must not become required for core control.
|
||||
|
||||
Supported:
|
||||
## Documentation
|
||||
|
||||
* DS18B20 OneWire temperature sensors
|
||||
Important docs:
|
||||
|
||||
Features:
|
||||
- `docs/project-state.md`
|
||||
- `docs/API.md`
|
||||
- `docs/ARCHITECTURE.md`
|
||||
- `docs/HARDWARE.md`
|
||||
- `docs/dashboard-esp32s3.md`
|
||||
- `docs/CONFIGURATION.md`
|
||||
- `docs/UART_PROTOCOL.md`
|
||||
- `docs/SERIAL_COMMANDS.md`
|
||||
- `docs/PARITY_MATRIX.md`
|
||||
- `docs/TEMP_SETUP.md`
|
||||
- `docs/ROADMAP.md`
|
||||
- `docs/CHANGELOG.md`
|
||||
|
||||
* Up to 8 sensors
|
||||
* User-configurable names
|
||||
* Online/offline detection
|
||||
* Sensor health reporting
|
||||
* JSON API reporting
|
||||
## Safety
|
||||
|
||||
⸻
|
||||
This project controls vehicle electrical loads.
|
||||
|
||||
Relay Control
|
||||
Use:
|
||||
|
||||
Features:
|
||||
- Proper fusing
|
||||
- Proper wire gauge
|
||||
- Proper terminals/ferrules
|
||||
- Common ground
|
||||
- Strain relief
|
||||
- Testing before connecting expensive loads
|
||||
|
||||
* Multiple relay outputs
|
||||
* User-configurable names
|
||||
* API control
|
||||
* Serial console control
|
||||
* Dashboard control (planned)
|
||||
|
||||
Recommended usage:
|
||||
|
||||
* Fridge power relays
|
||||
* Starlink power relays
|
||||
* Lighting relays
|
||||
* Inverter enable relays
|
||||
* Accessory relays
|
||||
|
||||
The ESP32 relay outputs are intended to drive automotive relays rather than directly power large loads.
|
||||
|
||||
⸻
|
||||
|
||||
Configuration System
|
||||
|
||||
Configuration is stored locally and survives reboot.
|
||||
|
||||
Configurable items include:
|
||||
|
||||
Device
|
||||
|
||||
* Device name
|
||||
|
||||
Relays
|
||||
|
||||
* Relay name
|
||||
* GPIO assignment
|
||||
* Enabled state
|
||||
|
||||
Temperature Sensors
|
||||
|
||||
* Sensor name
|
||||
* Sensor address
|
||||
* Enabled state
|
||||
|
||||
BMS
|
||||
|
||||
* BMS name
|
||||
* Bluetooth address
|
||||
* Address type
|
||||
|
||||
⸻
|
||||
|
||||
Communication Interfaces
|
||||
|
||||
Serial Console
|
||||
|
||||
Used for:
|
||||
|
||||
* Initial setup
|
||||
* Troubleshooting
|
||||
* Configuration changes
|
||||
* BLE discovery
|
||||
* Diagnostic logging
|
||||
|
||||
See SERIAL_COMMANDS.md for details.
|
||||
|
||||
⸻
|
||||
|
||||
HTTP API
|
||||
|
||||
Provides:
|
||||
|
||||
* Status reporting
|
||||
* Configuration management
|
||||
* Relay control
|
||||
* Dashboard integration
|
||||
|
||||
See API.md for details.
|
||||
|
||||
⸻
|
||||
|
||||
Current Development Status
|
||||
|
||||
Completed
|
||||
|
||||
* Configuration storage
|
||||
* Relay control
|
||||
* JSON status API
|
||||
* BLE BMS integration
|
||||
* BMS setup workflow
|
||||
* DS18B20 framework
|
||||
* Access Point mode
|
||||
* Web server
|
||||
* Dashboard communications framework
|
||||
* Generic naming architecture
|
||||
|
||||
In Progress
|
||||
|
||||
* Dashboard software
|
||||
* Temperature sensor assignment workflow
|
||||
* Expanded configuration API
|
||||
* Documentation cleanup
|
||||
|
||||
Planned
|
||||
|
||||
* Pico touchscreen dashboard
|
||||
* OBD-II integration
|
||||
* GPS integration
|
||||
* MQTT support
|
||||
* Home Assistant integration
|
||||
* Historical data logging
|
||||
* Graphing
|
||||
* Alerting system
|
||||
* OTA updates
|
||||
* Multi-controller support
|
||||
|
||||
⸻
|
||||
|
||||
Design Philosophy
|
||||
|
||||
The project follows several core principles:
|
||||
|
||||
1. Local-first operation
|
||||
2. No cloud dependency
|
||||
3. Generic hardware naming
|
||||
4. Open interfaces
|
||||
5. Modular architecture
|
||||
6. Expandable hardware support
|
||||
7. Easy field troubleshooting
|
||||
8. Vehicle-independent deployment
|
||||
|
||||
⸻
|
||||
|
||||
Documentation
|
||||
|
||||
* README.md — Project overview
|
||||
* API.md — HTTP API documentation
|
||||
* SERIAL_COMMANDS.md — Serial console commands
|
||||
* HARDWARE.md — Hardware overview
|
||||
* ARCHITECTURE.md — System architecture
|
||||
* CHANGELOG.md — Project history
|
||||
|
||||
⸻
|
||||
|
||||
License
|
||||
|
||||
Private project currently under active development.
|
||||
Do not connect vehicle 12V directly to ESP32 GPIO pins or dashboard GPIO pins.
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# Legacy Pico Dashboard
|
||||
|
||||
This directory contains the archived Raspberry Pi Pico dashboard implementation.
|
||||
|
||||
It is no longer active project firmware.
|
||||
|
||||
## Why this was archived
|
||||
|
||||
The current dashboard direction is the Waveshare ESP32-S3 touchscreen dashboard communicating with the Cargo ESP32 over WiFi/HTTP REST.
|
||||
|
||||
The Pico dashboard path was based around:
|
||||
|
||||
- Raspberry Pi Pico / Pico 2 W
|
||||
- UART communication
|
||||
- Pico-specific display/touch/buzzer hardware
|
||||
- Python/MicroPython-style dashboard structure
|
||||
|
||||
That architecture is no longer the current plan.
|
||||
|
||||
## What may still be useful
|
||||
|
||||
Some concepts may still be useful as reference material when building the ESP32-S3 dashboard:
|
||||
|
||||
- HTTP client flow
|
||||
- State model ideas
|
||||
- Alarm logic concepts
|
||||
- Screen/view-model structure
|
||||
- API payload expectations
|
||||
|
||||
Treat this as reference only. Do not add new active dashboard code here.
|
||||
|
||||
## Current dashboard target
|
||||
|
||||
Active dashboard work should go under:
|
||||
|
||||
firmware/esp32-s3-dashboard/
|
||||
|
||||
or another explicitly approved ESP32-S3 dashboard firmware location.
|
||||
+6
-5
@@ -1,16 +1,17 @@
|
||||
import json
|
||||
|
||||
|
||||
class HttpClient:
|
||||
def __init__(self, requests_module, base_url="http://192.168.4.1"):
|
||||
self.requests = requests_module
|
||||
self.base_url = base_url.rstrip("/")
|
||||
if not self.base_url.endswith("/api/v1"):
|
||||
self.base_url = f"{self.base_url}/api/v1"
|
||||
|
||||
def get_status(self):
|
||||
response = self.requests.get(f"{self.base_url}/status")
|
||||
return response.json()
|
||||
|
||||
def set_relay(self, relay, enabled):
|
||||
state = "on" if enabled else "off"
|
||||
response = self.requests.get(f"{self.base_url}/relay/{relay}/{state}")
|
||||
response = self.requests.post(
|
||||
f"{self.base_url}/relay/set",
|
||||
json={"id": relay, "state": enabled},
|
||||
)
|
||||
return response.json()
|
||||
+4
-2
@@ -24,6 +24,8 @@ class AppState:
|
||||
}
|
||||
|
||||
self.relays = {
|
||||
"relay_1": False,
|
||||
"relay_2": False,
|
||||
"starlink": False,
|
||||
"fridge": False,
|
||||
}
|
||||
@@ -51,8 +53,8 @@ class AppState:
|
||||
self.network.update(message.get("network", {}))
|
||||
|
||||
def update_from_relay_response(self, message):
|
||||
relay = message.get("relay")
|
||||
enabled = message.get("enabled")
|
||||
relay = message.get("relay", message.get("id"))
|
||||
enabled = message.get("enabled", message.get("state"))
|
||||
|
||||
if relay in self.relays and enabled is not None:
|
||||
self.relays[relay] = bool(enabled)
|
||||
@@ -241,6 +241,7 @@ class FakeResponse:
|
||||
class FakeRequests:
|
||||
def __init__(self):
|
||||
self.urls = []
|
||||
self.posts = []
|
||||
self.responses = []
|
||||
|
||||
def get(self, url):
|
||||
@@ -251,6 +252,15 @@ class FakeRequests:
|
||||
|
||||
return FakeResponse({"ok": True})
|
||||
|
||||
def post(self, url, json=None):
|
||||
self.urls.append(url)
|
||||
self.posts.append({"url": url, "json": json})
|
||||
|
||||
if self.responses:
|
||||
return FakeResponse(self.responses.pop(0))
|
||||
|
||||
return FakeResponse({"ok": True})
|
||||
|
||||
|
||||
def test_http_client_get_status():
|
||||
from comms.http_client import HttpClient
|
||||
@@ -264,7 +274,7 @@ def test_http_client_get_status():
|
||||
|
||||
payload = client.get_status()
|
||||
|
||||
assert fake_requests.urls == ["http://192.168.4.1/status"]
|
||||
assert fake_requests.urls == ["http://192.168.4.1/api/v1/status"]
|
||||
assert payload["type"] == "status_response"
|
||||
assert payload["battery"]["soc"] == 82
|
||||
|
||||
@@ -275,9 +285,13 @@ def test_http_client_set_relay_on():
|
||||
fake_requests = FakeRequests()
|
||||
client = HttpClient(fake_requests)
|
||||
|
||||
payload = client.set_relay("starlink", True)
|
||||
payload = client.set_relay("relay_1", True)
|
||||
|
||||
assert fake_requests.urls == ["http://192.168.4.1/relay/starlink/on"]
|
||||
assert fake_requests.urls == ["http://192.168.4.1/api/v1/relay/set"]
|
||||
assert fake_requests.posts == [{
|
||||
"url": "http://192.168.4.1/api/v1/relay/set",
|
||||
"json": {"id": "relay_1", "state": True},
|
||||
}]
|
||||
assert payload == {"ok": True}
|
||||
|
||||
|
||||
@@ -287,9 +301,13 @@ def test_http_client_set_relay_off():
|
||||
fake_requests = FakeRequests()
|
||||
client = HttpClient(fake_requests)
|
||||
|
||||
payload = client.set_relay("fridge", False)
|
||||
payload = client.set_relay("relay_2", False)
|
||||
|
||||
assert fake_requests.urls == ["http://192.168.4.1/relay/fridge/off"]
|
||||
assert fake_requests.urls == ["http://192.168.4.1/api/v1/relay/set"]
|
||||
assert fake_requests.posts == [{
|
||||
"url": "http://192.168.4.1/api/v1/relay/set",
|
||||
"json": {"id": "relay_2", "state": False},
|
||||
}]
|
||||
assert payload == {"ok": True}
|
||||
|
||||
|
||||
@@ -317,7 +335,7 @@ def test_communication_service_http_fallback_status():
|
||||
assert response["type"] == "status_response"
|
||||
assert state.battery["soc"] == 66
|
||||
assert fake_uart.writes == []
|
||||
assert fake_requests.urls == ["http://192.168.4.1/status"]
|
||||
assert fake_requests.urls == ["http://192.168.4.1/api/v1/status"]
|
||||
|
||||
|
||||
def test_communication_service_http_fallback_relay():
|
||||
@@ -330,8 +348,8 @@ def test_communication_service_http_fallback_relay():
|
||||
fake_requests.responses = [
|
||||
{
|
||||
"type": "relay_response",
|
||||
"relay": "fridge",
|
||||
"enabled": True,
|
||||
"id": "relay_1",
|
||||
"state": True,
|
||||
"ok": True,
|
||||
}
|
||||
]
|
||||
@@ -344,12 +362,16 @@ def test_communication_service_http_fallback_relay():
|
||||
)
|
||||
|
||||
service.enable_http_fallback()
|
||||
response = service.set_relay("fridge", True)
|
||||
response = service.set_relay("relay_1", True)
|
||||
|
||||
assert response["type"] == "relay_response"
|
||||
assert state.relays["fridge"] is True
|
||||
assert state.relays["relay_1"] is True
|
||||
assert fake_uart.writes == []
|
||||
assert fake_requests.urls == ["http://192.168.4.1/relay/fridge/on"]
|
||||
assert fake_requests.urls == ["http://192.168.4.1/api/v1/relay/set"]
|
||||
assert fake_requests.posts == [{
|
||||
"url": "http://192.168.4.1/api/v1/relay/set",
|
||||
"json": {"id": "relay_1", "state": True},
|
||||
}]
|
||||
|
||||
|
||||
def test_communication_service_marks_uart_connected_on_status():
|
||||
+806
-469
File diff suppressed because it is too large
Load Diff
+177
-212
@@ -1,93 +1,107 @@
|
||||
# Architecture
|
||||
|
||||
Overland Controller is a distributed monitoring and control platform for vehicles, trailers, campers, and mobile power systems.
|
||||
Overland Controller is a distributed monitoring and control platform for mobile power systems and overland vehicles.
|
||||
|
||||
The system separates electrical control from user interface logic.
|
||||
Core principle:
|
||||
|
||||
## Main Components
|
||||
Cargo ESP32 = controller / source of truth
|
||||
Waveshare ESP32-S3 dashboard = local touchscreen client
|
||||
Phone/laptop WebUI = configuration interface
|
||||
Home server = future optional remote access layer
|
||||
|
||||
### ESP32 Controller
|
||||
The system must work locally without internet.
|
||||
|
||||
The ESP32 is the system authority.
|
||||
## Cargo ESP32 Controller
|
||||
|
||||
The Cargo ESP32 owns critical control state and configuration.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- Read battery/BMS data
|
||||
- Read temperature sensors
|
||||
- Control relay outputs
|
||||
- Read JBD/Xiaoxiang BMS data over BLE
|
||||
- Read DS18B20 temperature sensors
|
||||
- Control relay trigger outputs
|
||||
- Store configuration
|
||||
- Serve the HTTP API
|
||||
- Communicate with the Pico dashboard over UART
|
||||
- Maintain system status
|
||||
- Handle alarms
|
||||
- Serve HTTP API
|
||||
- Serve local WebUI
|
||||
- Run WiFi AP mode
|
||||
- Maintain status, alarms, and fault state
|
||||
|
||||
### Pico Dashboard
|
||||
The Cargo ESP32 should not depend on the dashboard, phone, home server, or internet for core operation.
|
||||
|
||||
The Pico is the primary user interface.
|
||||
## Waveshare ESP32-S3 Dashboard
|
||||
|
||||
Target hardware:
|
||||
|
||||
- Waveshare ESP32-S3-Touch-LCD-5
|
||||
- 5 inch 800x480 capacitive touchscreen
|
||||
- ESP32-S3
|
||||
- 16MB flash
|
||||
- 8MB PSRAM
|
||||
- WiFi + BLE
|
||||
- CAN with onboard TJA1051 transceiver
|
||||
- microSD
|
||||
- 7-36V VIN support
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- Request status from ESP32
|
||||
- Display battery data
|
||||
- Display temperature data
|
||||
- Display relay states
|
||||
- Send relay commands
|
||||
- Send configuration commands
|
||||
- Provide setup workflows
|
||||
- Operate without internet
|
||||
- Native LVGL dashboard
|
||||
- Connect to Cargo ESP32 AP as WiFi client
|
||||
- Poll Cargo ESP32 HTTP API
|
||||
- Send simple control commands through HTTP API
|
||||
- Display battery, relay, temp, vehicle, and alert data
|
||||
- Future CAN vehicle data
|
||||
- Future transmission temperature sender display
|
||||
|
||||
The Pico should not own critical control state. The ESP32 remains the source of truth.
|
||||
The dashboard is a client only. It must not own relay state, BMS config, alarms, or persistent configuration.
|
||||
|
||||
## Communication Paths
|
||||
## WebUI
|
||||
|
||||
### UART
|
||||
The WebUI remains hosted by the Cargo ESP32.
|
||||
|
||||
Primary dashboard/control path.
|
||||
Use WebUI for:
|
||||
|
||||
Used for:
|
||||
|
||||
- Status requests
|
||||
- Relay control
|
||||
- Configuration updates
|
||||
- Setup/configuration
|
||||
- Relay names
|
||||
- Temperature sensor names
|
||||
- BMS setup
|
||||
- BLE scan requests
|
||||
- BMS selection
|
||||
|
||||
UART should eventually support every core management action needed by the Pico.
|
||||
|
||||
### HTTP
|
||||
|
||||
Secondary setup/debug path.
|
||||
|
||||
Used for:
|
||||
|
||||
- Browser testing
|
||||
- Phone/laptop setup
|
||||
- Future web UI
|
||||
- API debugging
|
||||
|
||||
HTTP should mirror the same data model as UART where practical.
|
||||
|
||||
### USB Serial
|
||||
|
||||
Service/debug path.
|
||||
|
||||
Used for:
|
||||
|
||||
- Development
|
||||
- Recovery
|
||||
- Troubleshooting
|
||||
- Emergency configuration
|
||||
- API testing
|
||||
|
||||
## Data Model
|
||||
Avoid complex configuration on the touchscreen dashboard.
|
||||
|
||||
The same generic data model should be used across:
|
||||
## Network
|
||||
|
||||
- HTTP API
|
||||
- UART protocol
|
||||
- Pico dashboard
|
||||
Cargo ESP32 runs WPA2 AP mode continuously.
|
||||
|
||||
Expected local URL:
|
||||
|
||||
http://192.168.4.1
|
||||
|
||||
Dashboard connects to that AP and talks HTTP.
|
||||
|
||||
No UART cable is planned between dashboard and Cargo ESP32.
|
||||
|
||||
## HTTP API
|
||||
|
||||
HTTP is the primary integration contract for:
|
||||
|
||||
- WebUI
|
||||
- Dashboard
|
||||
- Simulator
|
||||
- Future MQTT/Home Assistant integrations
|
||||
- Future home server
|
||||
- Future MQTT/Home Assistant/Grafana integrations
|
||||
|
||||
Preferred direction:
|
||||
|
||||
GET /api/v1/health
|
||||
GET /api/v1/capabilities
|
||||
GET /api/v1/status
|
||||
GET /api/v1/config
|
||||
GET /api/v1/config/export
|
||||
POST /api/v1/config/import
|
||||
POST /api/v1/relay/set
|
||||
|
||||
Pre-versioned root routes remain registered as compatibility aliases for existing local clients.
|
||||
|
||||
## Generic IDs
|
||||
|
||||
@@ -109,9 +123,9 @@ Temperature sensors:
|
||||
temp_7
|
||||
temp_8
|
||||
|
||||
Installation-specific names belong only in saved configuration.
|
||||
Install-specific names belong in saved configuration only.
|
||||
|
||||
Examples of configuration names:
|
||||
Examples:
|
||||
|
||||
Fridge
|
||||
Starlink
|
||||
@@ -119,33 +133,11 @@ Examples of configuration names:
|
||||
Outside
|
||||
House Battery
|
||||
|
||||
These names should not be hardcoded into firmware behavior.
|
||||
## Status Model
|
||||
|
||||
## Configuration Ownership
|
||||
Dashboard should be able to render from status/config responses without duplicating business logic.
|
||||
|
||||
Configuration is stored on the ESP32.
|
||||
|
||||
Stored configuration includes:
|
||||
|
||||
- Device name
|
||||
- Relay names
|
||||
- Relay enable states
|
||||
- Temperature sensor names
|
||||
- Temperature sensor addresses
|
||||
- Temperature sensor enable states
|
||||
- BMS name
|
||||
- BMS BLE address
|
||||
- BMS address type
|
||||
|
||||
The Pico may edit configuration through UART, but the ESP32 stores and owns it.
|
||||
|
||||
## Status Flow
|
||||
|
||||
Pico sends:
|
||||
|
||||
status_request
|
||||
|
||||
ESP32 responds with the same shape as HTTP /status:
|
||||
Expected status groups:
|
||||
|
||||
battery
|
||||
temps
|
||||
@@ -156,176 +148,149 @@ ESP32 responds with the same shape as HTTP /status:
|
||||
system
|
||||
config
|
||||
|
||||
## Relay Control Flow
|
||||
## Vehicle Data
|
||||
|
||||
Pico sends a generic relay command:
|
||||
Use standard OBD-II over CAN first.
|
||||
|
||||
relay_1 on
|
||||
relay_1 off
|
||||
relay_2 on
|
||||
relay_2 off
|
||||
Planned standard PIDs:
|
||||
|
||||
ESP32 updates the relay output and reports the new state.
|
||||
0105 coolant temperature
|
||||
010C RPM
|
||||
010D vehicle speed
|
||||
|
||||
## BMS Setup Flow
|
||||
Transmission temperature is planned through a dedicated sender rather than Nissan CAN reverse engineering.
|
||||
|
||||
BMS setup can be started from the Pico.
|
||||
## Future Home Server
|
||||
|
||||
Expected flow:
|
||||
Future optional architecture:
|
||||
|
||||
enter_bms_setup
|
||||
scan_ble
|
||||
scan_ble
|
||||
select_bms
|
||||
save_config
|
||||
Phone/PWA
|
||||
-> Home server HTTPS
|
||||
-> MQTT/WebSocket/HTTPS bridge
|
||||
-> Cargo ESP32 outbound connection
|
||||
|
||||
The ESP32 handles BLE scanning and BMS selection.
|
||||
Remote features must not break local operation.
|
||||
|
||||
The Pico only displays options and sends the selected index or address.
|
||||
|
||||
## HTTP / UART Parity
|
||||
## Cargo-led AP Pairing and Credential Migration
|
||||
|
||||
Long-term goal:
|
||||
The Cargo ESP32 owns the local overland network. It is the AP owner, source of truth, and configuration authority.
|
||||
|
||||
- HTTP /status equals UART status_request
|
||||
- HTTP /config equals UART config_request
|
||||
- HTTP /config/device equals UART config_device
|
||||
- HTTP /config/relay equals UART config_relay
|
||||
- HTTP /config/temp equals UART config_temp
|
||||
- HTTP /config/bms equals UART config_bms
|
||||
- HTTP relay control equals UART set_relay
|
||||
- HTTP factory reset equals UART factory_reset
|
||||
Target behavior:
|
||||
|
||||
## Current Priority
|
||||
1. Cargo ESP32 starts a WPA2-protected AP.
|
||||
2. Cargo OLED shows setup credentials and critical recovery/status information.
|
||||
3. Phone connects to the Cargo AP and uses the WebUI for setup/admin.
|
||||
4. Dashboard ESP32-S3 joins the Cargo AP as a client.
|
||||
5. If the user changes the Cargo AP SSID/password, the Cargo ESP stages the new credentials and coordinates dashboard migration.
|
||||
6. Dashboard stores the new credentials before the Cargo AP restarts.
|
||||
7. Cargo AP applies the new credentials.
|
||||
8. Dashboard reconnects automatically.
|
||||
|
||||
The next implementation priority is UART parity.
|
||||
The dashboard should not own network authority. A temporary dashboard setup AP may exist only as a recovery/fallback mechanism, not the normal pairing path.
|
||||
|
||||
The Pico should be able to do the important setup and control operations without relying on WiFi.
|
||||
Recovery target:
|
||||
|
||||
Minimum UART v1 scope:
|
||||
- A setup/status button on the Cargo ESP enclosure can show AP credentials on the OLED.
|
||||
- A long hold can restore factory AP credentials or enter recovery mode.
|
||||
|
||||
- status_request
|
||||
- set_relay
|
||||
- config_request
|
||||
- config_device
|
||||
- config_relay
|
||||
- config_temp
|
||||
- config_bms
|
||||
- save_config
|
||||
- factory_reset
|
||||
- enter_bms_setup
|
||||
- scan_ble
|
||||
- select_bms
|
||||
- exit_bms_setup
|
||||
|
||||
## Future Integrations
|
||||
## Current communications and GPIO decision
|
||||
|
||||
Possible future integrations:
|
||||
Active dashboard communication is WiFi/HTTP using `/api/v1`.
|
||||
|
||||
- MQTT
|
||||
- Home Assistant
|
||||
- Grafana
|
||||
- InfluxDB
|
||||
- OBD-II
|
||||
- CAN bus
|
||||
- GPS
|
||||
- Data logging
|
||||
- OTA updates
|
||||
The old Pico/dashboard UART path is retired from active hardware use. USB Serial remains available for development, debug, and manual configuration from a computer.
|
||||
|
||||
All future integrations should consume the same generic status/config model.
|
||||
Current Cargo ESP32 GPIO plan:
|
||||
|
||||
---
|
||||
- GPIO 16: Relay 1 trigger output
|
||||
- GPIO 17: Relay 2 trigger output
|
||||
- GPIO 4: DS18B20 OneWire temperature bus
|
||||
- GPIO 34: Ignition sense input
|
||||
- GPIO 21: SSD1306 OLED I2C SDA
|
||||
- GPIO 22: SSD1306 OLED I2C SCL
|
||||
- GPIO 25: OLED setup/status button
|
||||
|
||||
## Networking Architecture
|
||||
## Role Boundary Summary
|
||||
|
||||
### Current Network Mode
|
||||
See `docs/controller-roles.md` for the authoritative role split.
|
||||
|
||||
The ESP32 currently runs in Access Point mode.
|
||||
Committed v1 architecture:
|
||||
|
||||
Default access:
|
||||
Dashboard ESP32-S3
|
||||
<-> WiFi / HTTP REST
|
||||
Cargo ESP32 AP
|
||||
<-> control outputs
|
||||
Automotive load-switching layer
|
||||
<-> fused loads
|
||||
Fridge / Starlink / lights / spare outputs
|
||||
|
||||
http://192.168.4.1
|
||||
The Cargo ESP32 owns power-system control.
|
||||
|
||||
The AP is the recovery/setup network.
|
||||
The Dashboard ESP32-S3 renders the touchscreen UI and future vehicle visualization.
|
||||
|
||||
### Planned Network Mode
|
||||
For v1, controller-to-dashboard communication is WiFi/HTTP only. ESP-NOW, MQTT, UART, and CAN-between-controllers are not part of the v1 dashboard link.
|
||||
|
||||
The planned long-term network mode is AP + STA.
|
||||
The load-switching hardware is not finalized. Bosch relays, WUPP-style modules, smart high-side switching, or a future custom PCB remain implementation options until explicitly selected.
|
||||
|
||||
AP mode:
|
||||
## Dashboard Hardware Target
|
||||
|
||||
Always available
|
||||
Used for recovery and direct setup
|
||||
The current dashboard hardware target is the Waveshare ESP32-S3 Touch LCD 5B.
|
||||
|
||||
STA mode:
|
||||
Selected dashboard hardware:
|
||||
|
||||
Connects to a configured WiFi network
|
||||
Used for camp/home access from phone or laptop
|
||||
- Waveshare ESP32-S3 Touch LCD 5B
|
||||
- 5-inch touchscreen
|
||||
- 1024x600 display resolution
|
||||
- Capacitive touch
|
||||
- Native WiFi
|
||||
- LVGL-capable ESP32-S3 platform
|
||||
- Integrated CAN transceiver available for future vehicle integration
|
||||
|
||||
Example networks:
|
||||
This replaces the earlier Pico dashboard concept as the active dashboard architecture.
|
||||
|
||||
Starlink
|
||||
Home WiFi
|
||||
Shop WiFi
|
||||
The Dashboard ESP32-S3 communicates with the Cargo ESP32 over WiFi/HTTP REST for v1. CAN/OBD-II support remains a future dashboard-side vehicle-visualization feature and is not part of the Cargo ESP32 role.
|
||||
|
||||
### Multiple WiFi Profiles
|
||||
## Existing DS18B20 Temperature Support
|
||||
|
||||
The ESP32 should eventually store multiple WiFi profiles and try them in priority order.
|
||||
DS18B20 temperature sensor support is already part of the Cargo ESP architecture.
|
||||
|
||||
Example:
|
||||
Current responsibility split:
|
||||
|
||||
1. Starlink
|
||||
2. Home WiFi
|
||||
3. Shop WiFi
|
||||
- Cargo ESP32 owns DS18B20 sensor reading.
|
||||
- Cargo ESP32 exposes temperature state through the WebUI/API path.
|
||||
- Dashboard ESP32-S3 should consume temperature data from the Cargo ESP API.
|
||||
- Dashboard ESP32-S3 should not wire directly to cargo-area DS18B20 sensors for v1.
|
||||
|
||||
Expected behavior:
|
||||
This means future dashboard work should display existing Cargo ESP temperature telemetry rather than reimplementing temperature sensing on the dashboard.
|
||||
|
||||
Start AP first
|
||||
Try configured STA networks
|
||||
Connect to the first available network
|
||||
Keep AP available even after STA connects
|
||||
## Cargo ESP API Contract
|
||||
|
||||
Reason:
|
||||
The Cargo ESP API contract is documented in `docs/cargo-api-contract.md`.
|
||||
|
||||
- Starlink may be off
|
||||
- Home WiFi may only be available in the driveway
|
||||
- AP must remain available for recovery
|
||||
- User should not lose access because of a bad WiFi password
|
||||
The Cargo ESP32 is the source of truth for:
|
||||
|
||||
### Phone Dashboard Access
|
||||
- Relay/output state
|
||||
- DS18B20 temperature readings
|
||||
- BMS telemetry
|
||||
- Alarm/fault state
|
||||
- Local configuration
|
||||
|
||||
When connected to the ESP32 AP:
|
||||
The ESP32-S3 dashboard should consume Cargo ESP API data over WiFi/HTTP REST instead of directly owning cargo sensors or relay state.
|
||||
|
||||
http://192.168.4.1
|
||||
## Cargo ESP Hardware Transition Plan
|
||||
|
||||
When connected through Starlink/home WiFi:
|
||||
Current active development and testing remains on the existing 2-channel relay board.
|
||||
|
||||
http://controller-ip
|
||||
A LILYGO T-Relay-S3 6-way board has been ordered as a future Cargo ESP candidate.
|
||||
|
||||
Future mDNS target:
|
||||
Transition strategy:
|
||||
|
||||
http://overland-controller.local
|
||||
- Keep current 2-channel board working.
|
||||
- Make the Cargo ESP API output-count agnostic.
|
||||
- Represent outputs as a list of output objects.
|
||||
- Let clients render whatever output list the Cargo ESP exposes.
|
||||
- Add a hardware profile/config layer before migrating firmware to LilyGO.
|
||||
- Bench test LilyGO relay boot behavior before connecting it to the WUPP block.
|
||||
|
||||
### Dashboard Roles
|
||||
|
||||
ESP32:
|
||||
|
||||
Source of truth
|
||||
Lightweight phone dashboard
|
||||
HTTP API
|
||||
Recovery AP
|
||||
|
||||
Pico 2 W:
|
||||
|
||||
Instant physical dashboard
|
||||
UART client
|
||||
Touchscreen UI
|
||||
|
||||
Optional future Pi Zero:
|
||||
|
||||
Advanced web dashboard
|
||||
Logging
|
||||
Charts
|
||||
Integrations
|
||||
|
||||
The Pi Zero should be optional, not required for core operation.
|
||||
The API should not require redesign when moving from 2 outputs to 6 outputs.
|
||||
|
||||
+64
-206
@@ -1,208 +1,66 @@
|
||||
# CHANGELOG.md
|
||||
|
||||
# Changelog
|
||||
|
||||
All notable changes to Overland Controller will be documented in this file.
|
||||
|
||||
The format loosely follows Keep a Changelog principles.
|
||||
|
||||
---
|
||||
|
||||
# [0.3.0] - 2026-06-04
|
||||
|
||||
## Major Changes
|
||||
|
||||
### Project Renaming
|
||||
|
||||
- Renamed project from Xterra Controller to Overland Controller
|
||||
- Updated firmware naming
|
||||
- Updated repository naming
|
||||
- Updated documentation naming
|
||||
- Began removal of vehicle-specific assumptions
|
||||
|
||||
### Generic Configuration Architecture
|
||||
|
||||
Removed hardcoded installation-specific values from firmware.
|
||||
|
||||
Firmware now uses generic identifiers:
|
||||
|
||||
Relays:
|
||||
|
||||
text id="4xvc8l" relay_1 relay_2
|
||||
|
||||
Temperature sensors:
|
||||
|
||||
text id="xq2e8v" temp_1 temp_2 temp_3 temp_4 temp_5 temp_6 temp_7 temp_8
|
||||
|
||||
Installation-specific names now belong in saved configuration.
|
||||
|
||||
### JSON Status API Improvements
|
||||
|
||||
Reworked status response format.
|
||||
|
||||
Changed from:
|
||||
|
||||
json id="k8l5yx" { "temps": { "outside": 72.0 } }
|
||||
|
||||
To:
|
||||
|
||||
json id="k3g0ey" { "temps": [ { "id": "temp_1", "name": "Outside", "temperature_f": 72.0 } ] }
|
||||
|
||||
Benefits:
|
||||
|
||||
- Dynamic sensor support
|
||||
- Generic architecture
|
||||
- Easier dashboard integration
|
||||
- Better future scalability
|
||||
|
||||
### Relay Status Improvements
|
||||
|
||||
Relay reporting converted to structured arrays.
|
||||
|
||||
Example:
|
||||
|
||||
json id="wh1f5k" { "id": "relay_1", "name": "Aux Power", "state": false }
|
||||
|
||||
### BMS Integration
|
||||
|
||||
Implemented:
|
||||
|
||||
- JBD BLE communication
|
||||
- Xiaoxiang compatibility
|
||||
- Cell voltage reporting
|
||||
- SOC reporting
|
||||
- Current reporting
|
||||
- Capacity reporting
|
||||
- Temperature reporting
|
||||
- Cycle count reporting
|
||||
|
||||
### BLE Setup Workflow
|
||||
|
||||
Added:
|
||||
|
||||
- Setup mode
|
||||
- BLE device scanning
|
||||
- BMS selection
|
||||
- Persistent BMS storage
|
||||
|
||||
Known limitation:
|
||||
|
||||
- Some BMS devices advertise infrequently and may require multiple scans before appearing.
|
||||
|
||||
### Documentation
|
||||
|
||||
Added:
|
||||
|
||||
- README.md
|
||||
- API.md
|
||||
- SERIAL_COMMANDS.md
|
||||
- HARDWARE.md
|
||||
- ARCHITECTURE.md
|
||||
- CHANGELOG.md
|
||||
|
||||
---
|
||||
|
||||
# [0.2.x]
|
||||
|
||||
## Initial Controller Development
|
||||
|
||||
Implemented:
|
||||
|
||||
- ESP32 firmware framework
|
||||
- Relay control
|
||||
- Configuration storage
|
||||
- Access Point mode
|
||||
- Web server
|
||||
- UART communications
|
||||
- JSON status reporting
|
||||
|
||||
---
|
||||
|
||||
# [0.1.x]
|
||||
|
||||
## Project Foundation
|
||||
|
||||
Initial proof-of-concept work.
|
||||
|
||||
Implemented:
|
||||
|
||||
- Repository structure
|
||||
- ESP32 firmware skeleton
|
||||
- Basic relay testing
|
||||
- Initial dashboard communications concept
|
||||
|
||||
---
|
||||
|
||||
# Upcoming
|
||||
|
||||
## Planned for 0.4.x
|
||||
|
||||
### Dashboard Development
|
||||
|
||||
- Pico 2 W integration
|
||||
- Dashboard communications
|
||||
- Touchscreen support
|
||||
- Relay control interface
|
||||
|
||||
### Temperature System
|
||||
|
||||
- Sensor assignment workflow
|
||||
- Sensor discovery workflow
|
||||
- Improved diagnostics
|
||||
|
||||
### Configuration System
|
||||
|
||||
- Expanded configuration API
|
||||
- Additional runtime configuration
|
||||
- Improved validation
|
||||
|
||||
### Documentation
|
||||
|
||||
- Installation guide
|
||||
- Wiring guide
|
||||
- Development guide
|
||||
|
||||
---
|
||||
|
||||
## Planned for 0.5.x
|
||||
|
||||
### Vehicle Integration
|
||||
|
||||
Potential support for:
|
||||
|
||||
- OBD-II
|
||||
- ELM327
|
||||
- CAN Bus
|
||||
|
||||
### Data Logging
|
||||
|
||||
Potential support for:
|
||||
|
||||
- Historical battery data
|
||||
- Historical temperature data
|
||||
- Event logging
|
||||
|
||||
### Integrations
|
||||
|
||||
Potential support for:
|
||||
|
||||
- MQTT
|
||||
- Home Assistant
|
||||
- Grafana
|
||||
- InfluxDB
|
||||
|
||||
---
|
||||
|
||||
## Long-Term Roadmap
|
||||
|
||||
Potential future capabilities:
|
||||
|
||||
- GPS support
|
||||
- Route logging
|
||||
- Multi-controller support
|
||||
- OTA updates
|
||||
- Alerting system
|
||||
- Mobile-friendly dashboard
|
||||
- Remote monitoring
|
||||
|
||||
The roadmap is intentionally flexible and may change as the project evolves.
|
||||
## 0.5.0 - 2026-06-08
|
||||
|
||||
### Added
|
||||
- WPA2 AP configuration API and WebUI controls.
|
||||
- AP reset recovery action with regenerated password.
|
||||
- Status API field filtering for smaller dashboard/WebUI payloads.
|
||||
- Config page subtabs for General, WiFi/AP, Relays, Temperatures, BMS, and Maintenance.
|
||||
- Relay button state/command feedback in the WebUI.
|
||||
- Optional Cargo OLED display scaffold.
|
||||
- GPIO plan assigning OLED I2C to GPIO 21/22 and setup button to GPIO 25.
|
||||
|
||||
### Changed
|
||||
- Dashboard communication direction is now WiFi/HTTP API.
|
||||
- Retired active dashboard UART pin usage.
|
||||
- Improved WebUI visual styling, mobile temperature settings layout, and config form behavior during polling.
|
||||
- Removed vehicle-specific branding from active project docs/examples.
|
||||
|
||||
### Fixed
|
||||
- Config inputs no longer get overwritten while typing during polling.
|
||||
- Temperature config lookup remains available outside disabled UART code.
|
||||
- Misleading WebUI connection badge text.
|
||||
|
||||
## 0.4.0 - 2026-06-04
|
||||
|
||||
### Added
|
||||
|
||||
- Embedded tabbed WebUI.
|
||||
- Overview, Battery, and Config tabs.
|
||||
- Estimated runtime display.
|
||||
- Battery detail page with BMS status, cell voltages, cell delta, capacity, remaining Ah, cycles, and battery temperature.
|
||||
- WebUI setup workflow.
|
||||
- Device name configuration from WebUI.
|
||||
- Relay name configuration from WebUI.
|
||||
- Temperature sensor name and enabled-count configuration from WebUI.
|
||||
- Temperature probe scan and assignment workflow from WebUI.
|
||||
- BMS enable/disable controls from WebUI.
|
||||
- BMS reconnect action from WebUI/API.
|
||||
- System restart action from WebUI/API.
|
||||
- WiFi AP + STA mode.
|
||||
- Multiple saved STA WiFi networks.
|
||||
- WiFi priority and runtime reconnect behavior.
|
||||
- HTTP/UART/USB serial command parity documentation.
|
||||
- DS18B20 configurable temperature probe support.
|
||||
- Preferred relay command endpoint: `POST /api/v1/relay/set`.
|
||||
|
||||
### Changed
|
||||
|
||||
- ESP32 remains the controller/source of truth.
|
||||
- WebUI is now the preferred initial configuration workflow.
|
||||
- Waveshare ESP32-S3 is the primary physical dashboard target.
|
||||
- ESP32 relay board outputs are treated as control/trigger outputs for external fused automotive relays/contactors on major loads.
|
||||
- BMS source now reports configured JBD BMS even when disconnected.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Restored default NimBLE connection behavior after custom timeout caused BMS connection failures.
|
||||
- Trimmed WiFi credentials from setup inputs.
|
||||
- Improved WebUI organization by moving setup/configuration controls to the Config tab.
|
||||
|
||||
### Known Notes
|
||||
|
||||
- BMS connection can still temporarily block the ESP32 while attempting BLE connection if the BMS is out of range.
|
||||
- WebUI BMS disable/enable controls are intended to make bench testing easier.
|
||||
- DS18B20 multi-probe final install should use a proper terminal/distribution board rather than temporary Wago wiring.
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
# ChatGPT Handoff
|
||||
|
||||
Last updated: 2026-06-07
|
||||
|
||||
Use this document to bring a new ChatGPT/Codex session up to speed quickly. Keep it current when project goals, architecture, APIs, hardware assumptions, validation status, or near-term priorities change.
|
||||
|
||||
## How to Use This
|
||||
|
||||
At the start of a new assistant session, provide this file along with:
|
||||
|
||||
- `AGENTS.md`
|
||||
- `docs/project-state.md`
|
||||
- `docs/ARCHITECTURE.md`
|
||||
- `docs/HARDWARE.md`
|
||||
- `docs/API.md`
|
||||
- `docs/ROADMAP.md`
|
||||
|
||||
The assistant should treat the Cargo ESP32 firmware and primary docs as the source of truth, then update this handoff before finishing any meaningful change.
|
||||
|
||||
## Non-Negotiable Constraints
|
||||
|
||||
- Cargo ESP32 remains the controller, API server, WebUI host, and source of truth.
|
||||
- Waveshare ESP32-S3 dashboard is a client only.
|
||||
- Do not move relay, BMS, or control authority into the dashboard.
|
||||
- Do not modify JBD/Xiaoxiang BLE behavior without explicit approval.
|
||||
- Do not change BLE timeout, reconnect, scan, or BMS-selection behavior without approval.
|
||||
- Do not hardcode install-specific names into firmware.
|
||||
- Preserve API compatibility when practical.
|
||||
- Prefer HTTP API integration over custom protocols.
|
||||
- ESP32 relay board outputs trigger external fused automotive relays/contactors only.
|
||||
- Do not route fridge, Starlink, inverter, or other major load current through the ESP32 relay board.
|
||||
- Dashboard target is Waveshare ESP32-S3-Touch-LCD-5.
|
||||
- Dashboard connects to Cargo ESP32 AP over WiFi and is powered from ACC switched 12V into VIN.
|
||||
- Do not power dashboard from OBD-II pin 16 in the permanent install.
|
||||
- CAN termination must stay disabled when connected to vehicle CAN.
|
||||
|
||||
## Current Status
|
||||
|
||||
Current firmware version:
|
||||
|
||||
```text
|
||||
0.5.0
|
||||
```
|
||||
|
||||
Current architecture:
|
||||
|
||||
- ESP32 relay board is the cargo controller.
|
||||
- Embedded WebUI is served by the Cargo ESP32.
|
||||
- HTTP API base path is `/api/v1`.
|
||||
- Root HTTP routes remain as compatibility aliases for existing local clients.
|
||||
- Dashboard direction is Waveshare ESP32-S3 over WiFi/HTTP, not Pico/UART.
|
||||
- UART JSON remains optional for diagnostics or alternate clients.
|
||||
|
||||
Current working feature areas:
|
||||
|
||||
- Relay control through WebUI, HTTP API, UART JSON, and USB serial.
|
||||
- JBD/Xiaoxiang BLE BMS telemetry.
|
||||
- DS18B20 temperature scan, assignment, clear, and configured naming workflow.
|
||||
- WiFi AP recovery network at `192.168.4.1`.
|
||||
- STA mode with saved WiFi networks and priorities.
|
||||
- Config-driven generic relay and temperature IDs.
|
||||
- Config backup/restore API.
|
||||
- BMS setup workflow API.
|
||||
|
||||
## API Direction
|
||||
|
||||
New clients should use `/api/v1`.
|
||||
|
||||
Dashboard MVP should initially consume only:
|
||||
|
||||
```text
|
||||
GET /api/v1/status
|
||||
POST /api/v1/relay/set
|
||||
```
|
||||
|
||||
Other current HTTP API areas include:
|
||||
|
||||
- Health and capability discovery.
|
||||
- Configuration read/update/save/factory-reset.
|
||||
- Config export/import.
|
||||
- WiFi config/connect/clear.
|
||||
- Temperature scan/assign/clear.
|
||||
- BMS setup enter/exit, scan, and select.
|
||||
|
||||
See `docs/API.md` for the current contract and `tests/test_http_api_contract.py` for contract coverage.
|
||||
|
||||
## Near-Term Goals
|
||||
|
||||
1. Verify WebUI setup workflow after a fresh flash.
|
||||
2. Clean up DS18B20 physical distribution hardware.
|
||||
3. Test 2-4 temperature probes after the distribution board is installed.
|
||||
4. Start the Waveshare ESP32-S3 dashboard MVP over WiFi/HTTP.
|
||||
5. Improve BMS out-of-range handling only with explicit approval for BLE behavior changes.
|
||||
6. Define future Pi Zero logging/integration role without moving control authority off the Cargo ESP32.
|
||||
|
||||
## Recent Context
|
||||
|
||||
- The project recently standardized current HTTP routes under `/api/v1`.
|
||||
- Health, capabilities, config backup/restore, configuration update, WiFi, temperature setup, and BMS setup endpoints are documented in `docs/API.md`.
|
||||
- The embedded WebUI uses `const API_BASE="/api/v1";`.
|
||||
- Pre-versioned HTTP routes remain registered as compatibility aliases.
|
||||
- `docs/archive/` contains older or superseded planning material and should not override the primary docs.
|
||||
|
||||
## Validation Status
|
||||
|
||||
Available validation is driven by:
|
||||
|
||||
```text
|
||||
python3 run_tests.py
|
||||
```
|
||||
|
||||
The script performs Python compile checks for the dashboard code, then runs:
|
||||
|
||||
```text
|
||||
python3 -m pytest tests/test_pico_core.py
|
||||
python3 -m pytest tests/test_http_api_contract.py
|
||||
```
|
||||
|
||||
As of this update, the compile step passes in the local environment, but full test execution is blocked because `pytest` is not installed for the active `python3`.
|
||||
|
||||
## Handoff Maintenance Checklist
|
||||
|
||||
Update this document when any of these change:
|
||||
|
||||
- Active project phase or near-term goals.
|
||||
- Cargo ESP32 vs dashboard responsibility boundaries.
|
||||
- Current firmware version.
|
||||
- HTTP API route list, response shape, or compatibility policy.
|
||||
- Hardware assumptions, wiring, power, relay, CAN, or dashboard target.
|
||||
- Validation commands, dependencies, or current test status.
|
||||
- Important decisions that a future assistant should not rediscover from scratch.
|
||||
|
||||
|
||||
## Current communications and GPIO decision
|
||||
|
||||
Active dashboard communication is WiFi/HTTP using `/api/v1`.
|
||||
|
||||
The old Pico/dashboard UART path is retired from active hardware use. USB Serial remains available for development, debug, and manual configuration from a computer.
|
||||
|
||||
Current Cargo ESP32 GPIO plan:
|
||||
|
||||
- GPIO 16: Relay 1 trigger output
|
||||
- GPIO 17: Relay 2 trigger output
|
||||
- GPIO 4: DS18B20 OneWire temperature bus
|
||||
- GPIO 34: Ignition sense input
|
||||
- GPIO 21: SSD1306 OLED I2C SDA
|
||||
- GPIO 22: SSD1306 OLED I2C SCL
|
||||
- GPIO 25: OLED setup/status button
|
||||
|
||||
## Current Architecture Guardrails
|
||||
|
||||
- Inspect the repo before proposing code or documentation changes.
|
||||
- Preserve the Cargo ESP vs Dashboard ESP32-S3 role split.
|
||||
- Cargo ESP32 does not own CAN, OBD-II, tilt sensing, vehicle data, or dashboard rendering.
|
||||
- Dashboard ESP32-S3 does not own relay authority, BMS authority, alarm authority, or persistent cargo-controller configuration.
|
||||
- v1 dashboard communication is WiFi/HTTP REST only.
|
||||
- ESP-NOW, MQTT, UART dashboard link, and CAN-between-controllers are not v1 architecture.
|
||||
- Load-switching hardware is not finalized. Bosch relays, WUPP-style fused distribution, smart high-side switching, and custom PCB options remain implementation choices until explicitly selected.
|
||||
|
||||
## Legacy Pico Dashboard Archive
|
||||
|
||||
The old Pico dashboard implementation has been archived at `archive/legacy-pico-dashboard/`.
|
||||
|
||||
Do not treat it as active firmware. It may be referenced for old HTTP/state/alarm/UI ideas, but new dashboard work should target the ESP32-S3 dashboard path.
|
||||
|
||||
Default validation should use the repo-local virtual environment:
|
||||
|
||||
source .venv/bin/activate
|
||||
python run_tests.py
|
||||
|
||||
## DS18B20 Implementation Note
|
||||
|
||||
DS18B20 temperature support is already implemented in the Cargo ESP/WebUI/API path.
|
||||
|
||||
Do not suggest adding DS18B20 support as if it is missing. Future work should preserve existing Cargo ESP temperature ownership and expose/display that telemetry through the API and ESP32-S3 dashboard.
|
||||
|
||||
## Cargo API Contract Guardrail
|
||||
|
||||
The Cargo ESP API contract is documented in `docs/cargo-api-contract.md`.
|
||||
|
||||
Before making dashboard firmware changes, inspect the current API tests and this contract. Do not move relay, BMS, DS18B20, alarm, or configuration ownership from the Cargo ESP to the dashboard.
|
||||
|
||||
## Pre-LilyGO Baseline Checklist
|
||||
|
||||
Before suggesting a release/tag prior to Cargo ESP hardware migration, check `docs/pre-lilygo-baseline-checklist.md`.
|
||||
|
||||
Do not suggest migrating active Cargo ESP firmware to LilyGO hardware until the user explicitly decides to buy/use that board.
|
||||
|
||||
## LilyGO Transition Guardrail
|
||||
|
||||
The user has ordered a LILYGO T-Relay-S3 6-way board, but it will not arrive for a couple weeks.
|
||||
|
||||
Until the user confirms hardware is available and bench tested:
|
||||
|
||||
- Keep the existing 2-channel relay board as the active test target.
|
||||
- Do not remove 2-channel support.
|
||||
- Do not assume LilyGO firmware is active.
|
||||
- Prepare API/config/docs for output-count-agnostic operation.
|
||||
- Treat LilyGO migration as a future hardware-profile change.
|
||||
+19
-6
@@ -9,7 +9,7 @@ Configuration survives reboot.
|
||||
- Keep firmware generic
|
||||
- Store installation-specific names in config
|
||||
- Support multiple vehicle/camper layouts
|
||||
- Allow setup from serial, HTTP, and eventually Pico UART
|
||||
- Allow setup from HTTP/WebUI and service interfaces
|
||||
- Avoid hardcoded device names, relay names, sensor names, or BMS names
|
||||
|
||||
## Stored Configuration
|
||||
@@ -38,7 +38,7 @@ Serial command:
|
||||
|
||||
HTTP endpoint:
|
||||
|
||||
POST /config/device
|
||||
POST /api/v1/config/device
|
||||
|
||||
## Relay Configuration
|
||||
|
||||
@@ -67,7 +67,7 @@ Serial commands:
|
||||
|
||||
HTTP endpoint:
|
||||
|
||||
POST /config/relay
|
||||
POST /api/v1/config/relay
|
||||
|
||||
## Temperature Sensor Configuration
|
||||
|
||||
@@ -102,7 +102,7 @@ Serial commands:
|
||||
|
||||
HTTP endpoint:
|
||||
|
||||
POST /config/temp
|
||||
POST /api/v1/config/temp
|
||||
|
||||
## BMS Configuration
|
||||
|
||||
@@ -127,7 +127,7 @@ Serial commands:
|
||||
|
||||
HTTP endpoint:
|
||||
|
||||
POST /config/bms
|
||||
POST /api/v1/config/bms
|
||||
|
||||
## BMS Setup Mode
|
||||
|
||||
@@ -157,7 +157,7 @@ Serial command:
|
||||
|
||||
HTTP endpoint:
|
||||
|
||||
POST /config/factory-reset
|
||||
POST /api/v1/config/factory-reset
|
||||
|
||||
After factory reset:
|
||||
|
||||
@@ -166,6 +166,19 @@ After factory reset:
|
||||
- Temperature names return to generic defaults
|
||||
- BMS is unconfigured until selected or manually entered
|
||||
|
||||
## Backup And Restore
|
||||
|
||||
Configuration backup includes controller config and saved STA WiFi networks.
|
||||
|
||||
HTTP endpoints:
|
||||
|
||||
GET /api/v1/config/export
|
||||
POST /api/v1/config/import
|
||||
|
||||
`config/export` includes saved WiFi passwords so the backup can be restored. Treat exported JSON as sensitive.
|
||||
|
||||
`config/import` persists the restored config. If WiFi networks are imported, run `POST /api/v1/wifi/connect` afterward to attempt STA connection.
|
||||
|
||||
## Configuration Persistence
|
||||
|
||||
Configuration is stored using ESP32 non-volatile storage.
|
||||
|
||||
+167
-209
@@ -1,279 +1,237 @@
|
||||
# HARDWARE.md
|
||||
# Hardware
|
||||
|
||||
# Hardware Overview
|
||||
## Cargo ESP32
|
||||
|
||||
Overland Controller is designed as a distributed system consisting of a control module and a dashboard module.
|
||||
|
||||
The architecture intentionally separates power management from user interface functions.
|
||||
|
||||
---
|
||||
|
||||
# System Architecture
|
||||
|
||||
text House Battery │ │ ▼ +-------------------+ | ESP32 Controller | +-------------------+ │ ├── JBD/Xiaoxiang BMS ├── DS18B20 Sensors ├── Relay Outputs ├── Local Web API └── WiFi Network ⇅ +-------------------+ | Pico 2 W Dashboard| +-------------------+ │ ├── Touchscreen ├── Dashboard UI ├── Vehicle Display └── User Controls
|
||||
|
||||
---
|
||||
|
||||
# ESP32 Controller
|
||||
|
||||
The ESP32 controller is installed near the house battery and electrical system.
|
||||
The cargo ESP32 is the controller and source of truth.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- Battery monitoring
|
||||
- Temperature monitoring
|
||||
- Relay control
|
||||
- Configuration storage
|
||||
- Local API server
|
||||
- Dashboard communications
|
||||
- BMS BLE
|
||||
- Relays
|
||||
- DS18B20 temps
|
||||
- WebUI
|
||||
- HTTP API
|
||||
- AP mode
|
||||
- Alarms
|
||||
|
||||
Current development target:
|
||||
Relay board outputs are trigger/control outputs only.
|
||||
|
||||
text ESP32 DevKit
|
||||
High-current loads must use properly fused automotive relays or contactors.
|
||||
|
||||
Future hardware can be substituted without major firmware changes.
|
||||
Do not directly route fridge, Starlink, inverter, lights, or other major loads through the ESP32 relay board.
|
||||
|
||||
---
|
||||
## Dashboard
|
||||
|
||||
# Raspberry Pi Pico Dashboard
|
||||
Target:
|
||||
|
||||
The dashboard provides the primary user interface.
|
||||
Waveshare ESP32-S3-Touch-LCD-5
|
||||
|
||||
Current development target:
|
||||
Reasons:
|
||||
|
||||
text Raspberry Pi Pico 2 W
|
||||
- ESP32-S3 is mature enough for LVGL dashboard
|
||||
- 5 inch 800x480 display gives enough UI space
|
||||
- WiFi connects to Cargo ESP32 AP
|
||||
- CAN interface with onboard TJA1051 supports future OBD-II vehicle data
|
||||
- microSD allows future logging
|
||||
- 7-36V VIN supports vehicle power
|
||||
|
||||
Planned display hardware:
|
||||
## Dashboard Power
|
||||
|
||||
text 3.5" Touchscreen
|
||||
Preferred:
|
||||
|
||||
Responsibilities:
|
||||
ACC switched 12V
|
||||
-> fuse
|
||||
-> Waveshare VIN
|
||||
|
||||
- Display system status
|
||||
- Display battery information
|
||||
- Display temperatures
|
||||
- Control relays
|
||||
- Configuration interface
|
||||
- Alarm notifications
|
||||
Do not use OBD-II pin 16 for permanent dashboard power.
|
||||
|
||||
---
|
||||
## CAN / OBD-II
|
||||
|
||||
# Battery Monitoring
|
||||
OBD-II pins:
|
||||
|
||||
Supported BMS:
|
||||
Pin 6 = CAN-H
|
||||
Pin 14 = CAN-L
|
||||
Pin 5 = Signal Ground, optional if needed
|
||||
Pin 16 = Battery 12V, do not use for permanent display power
|
||||
|
||||
text JBD Xiaoxiang
|
||||
Waveshare CAN termination must remain disabled on vehicle CAN.
|
||||
|
||||
Communication:
|
||||
Preferred CAN wiring:
|
||||
|
||||
text Bluetooth Low Energy (BLE)
|
||||
OBD pin 6 -> Waveshare CAN-H
|
||||
OBD pin 14 -> Waveshare CAN-L
|
||||
|
||||
Data collected:
|
||||
Optional:
|
||||
|
||||
- State of charge
|
||||
- Voltage
|
||||
- Current
|
||||
- Capacity
|
||||
- Temperature
|
||||
- Cycle count
|
||||
- Cell voltages
|
||||
- Cell imbalance
|
||||
OBD pin 5 -> board/system ground
|
||||
|
||||
---
|
||||
Use twisted pair. Cat5e/Cat6 twisted pair is acceptable for short vehicle runs.
|
||||
|
||||
# Temperature Sensors
|
||||
Avoid Scotchlok/vampire taps on vehicle CAN. Prefer OBD splitter or proper automotive splice.
|
||||
|
||||
Supported sensor type:
|
||||
## Vehicle Data
|
||||
|
||||
text DS18B20
|
||||
Coolant temp:
|
||||
|
||||
Connection:
|
||||
Standard OBD-II PID 0105
|
||||
|
||||
text 1-Wire Bus
|
||||
RPM:
|
||||
|
||||
Current firmware supports:
|
||||
Standard OBD-II PID 010C
|
||||
|
||||
text Up to 8 sensors
|
||||
Vehicle speed:
|
||||
|
||||
Recommended uses:
|
||||
Standard OBD-II PID 010D
|
||||
|
||||
- Refrigerator compartment
|
||||
- Freezer compartment
|
||||
- Cabin temperature
|
||||
- Exterior temperature
|
||||
- Electronics enclosure
|
||||
- Battery compartment
|
||||
## Transmission Temperature Sender
|
||||
|
||||
---
|
||||
Preferred over Nissan-specific trans-temp CAN reverse engineering.
|
||||
|
||||
# Relay Outputs
|
||||
Candidate:
|
||||
|
||||
Current firmware supports:
|
||||
Autometer 2258 / 2259 style sender
|
||||
|
||||
text 2 relay outputs
|
||||
Resistance table:
|
||||
|
||||
Default GPIO assignments:
|
||||
100F = 1123 ohms
|
||||
120F = 708 ohms
|
||||
140F = 460 ohms
|
||||
150F = 374 ohms
|
||||
170F = 253 ohms
|
||||
190F = 175 ohms
|
||||
210F = 123 ohms
|
||||
230F = 89 ohms
|
||||
250F = 65 ohms
|
||||
280F = 42 ohms
|
||||
300F = 32 ohms
|
||||
320F = 25 ohms
|
||||
340F = 20 ohms
|
||||
|
||||
| Relay | GPIO |
|
||||
|---------|---------|
|
||||
| relay_1 | 16 |
|
||||
| relay_2 | 17 |
|
||||
Implementation:
|
||||
|
||||
These outputs are intended to drive relay coils, not high-current loads directly.
|
||||
- Sender to ADC voltage divider, or
|
||||
- Sender to ADS1115 over I2C if no suitable exposed ADC exists
|
||||
|
||||
---
|
||||
Use lookup-table interpolation.
|
||||
|
||||
# High Current Loads
|
||||
|
||||
Recommended design:
|
||||
## Cargo OLED service display
|
||||
|
||||
text ESP32 GPIO │ ▼ Relay Module │ ▼ Automotive Relay │ ▼ Load
|
||||
Planned optional hardware:
|
||||
|
||||
Examples:
|
||||
- 0.96 inch SSD1306 OLED
|
||||
- 128x64 resolution
|
||||
- I2C interface
|
||||
- Typical address: 0x3C
|
||||
- Suggested initial pins:
|
||||
- SDA: GPIO 21
|
||||
- SCL: GPIO 22
|
||||
- VCC: 3.3V
|
||||
- GND: common ground
|
||||
|
||||
- Refrigerator
|
||||
Purpose:
|
||||
|
||||
- Show first-boot AP setup credentials without requiring Serial Monitor.
|
||||
- Show AP SSID, password, and IP address during setup/recovery.
|
||||
- Show quick service status:
|
||||
- battery SOC
|
||||
- voltage/current
|
||||
- relay states
|
||||
- WiFi/IP
|
||||
- active alarms
|
||||
|
||||
Firmware support is optional and disabled by default with OLED_ENABLED set to 0. Enable only after the OLED libraries are installed and the display is wired.
|
||||
|
||||
|
||||
## Current communications and GPIO decision
|
||||
|
||||
Active dashboard communication is WiFi/HTTP using `/api/v1`.
|
||||
|
||||
The old Pico/dashboard UART path is retired from active hardware use. USB Serial remains available for development, debug, and manual configuration from a computer.
|
||||
|
||||
Current Cargo ESP32 GPIO plan:
|
||||
|
||||
- GPIO 16: Relay 1 trigger output
|
||||
- GPIO 17: Relay 2 trigger output
|
||||
- GPIO 4: DS18B20 OneWire temperature bus
|
||||
- GPIO 34: Ignition sense input
|
||||
- GPIO 21: SSD1306 OLED I2C SDA
|
||||
- GPIO 22: SSD1306 OLED I2C SCL
|
||||
- GPIO 25: OLED setup/status button
|
||||
|
||||
## Automotive Load-Switching Layer
|
||||
|
||||
Current v1 load-control assumption:
|
||||
|
||||
Cargo ESP32
|
||||
-> control outputs
|
||||
-> automotive load-switching layer
|
||||
-> fused loads
|
||||
|
||||
Expected fused loads:
|
||||
|
||||
- Fridge
|
||||
- Starlink
|
||||
- Inverter enable
|
||||
- Lighting circuits
|
||||
- Water pump
|
||||
- Air compressor
|
||||
- Lights
|
||||
- Spare output
|
||||
|
||||
---
|
||||
The ESP32 relay board is treated as a trigger/control layer only. Major load current must be handled by properly fused automotive wiring, relays, contactors, smart high-side switches, or another suitable fused distribution module.
|
||||
|
||||
# Automotive Relay Recommendation
|
||||
The exact load-switching hardware is not finalized yet.
|
||||
|
||||
Recommended relay type:
|
||||
Candidate options:
|
||||
|
||||
text Bosch-style 30A or 40A relay
|
||||
- Bosch-style automotive relays
|
||||
- WUPP-style fused relay/distribution module
|
||||
- Smart high-side switching
|
||||
- Future custom PCB
|
||||
|
||||
Each high-current circuit should have:
|
||||
## Selected Dashboard Hardware
|
||||
|
||||
- Dedicated fuse
|
||||
- Appropriate wire gauge
|
||||
- Proper grounding
|
||||
- Automotive-rated connectors
|
||||
The selected dashboard hardware target is the Waveshare ESP32-S3 Touch LCD 5B.
|
||||
|
||||
---
|
||||
Key hardware notes:
|
||||
|
||||
# Power System
|
||||
- 5-inch touchscreen display
|
||||
- 1024x600 resolution
|
||||
- ESP32-S3 based
|
||||
- Capacitive touch
|
||||
- Native WiFi
|
||||
- Integrated CAN transceiver available for future vehicle integration
|
||||
|
||||
Typical installation:
|
||||
This is the active dashboard platform for the project. The previous Pico dashboard implementation has been archived and is reference-only.
|
||||
|
||||
text House Battery │ ├── Main Fuse │ ├── ESP32 Controller │ ├── Relay Circuits │ └── Accessories
|
||||
## DS18B20 Temperature Sensors
|
||||
|
||||
The ESP32 should remain powered from the house battery so monitoring remains active when the vehicle is parked.
|
||||
DS18B20 temperature sensors are already part of the Cargo ESP implementation and WebUI/API flow.
|
||||
|
||||
---
|
||||
Current expectation:
|
||||
|
||||
# Communications
|
||||
- DS18B20 sensors connect to the Cargo ESP side.
|
||||
- Cargo ESP owns sensor reads and temperature state.
|
||||
- The WebUI displays the temperature data.
|
||||
- The Dashboard ESP32-S3 will consume temperature data from the Cargo ESP API.
|
||||
|
||||
Current communications:
|
||||
Do not move cargo/fridge temperature sensing to the dashboard controller unless the architecture is explicitly changed later.
|
||||
|
||||
## ESP32 ↔ BMS
|
||||
## Ordered Cargo ESP Hardware: LILYGO T-Relay-S3 6-Way
|
||||
|
||||
text Bluetooth Low Energy
|
||||
The LILYGO T-Relay-S3 6-way board has been ordered as a future Cargo ESP candidate.
|
||||
|
||||
---
|
||||
Current active testing should continue on the existing 2-channel relay board until the LilyGO board arrives and is bench tested.
|
||||
|
||||
## ESP32 ↔ Dashboard
|
||||
Planned LilyGO role:
|
||||
|
||||
Current:
|
||||
LILYGO T-Relay-S3 6-way
|
||||
-> low-current control/relay outputs
|
||||
WUPP 6-relay fused block
|
||||
-> fused load switching
|
||||
Fridge / Starlink / lights / spare / spare / spare
|
||||
|
||||
text UART
|
||||
Important guardrails:
|
||||
|
||||
Future:
|
||||
|
||||
text WiFi HTTP API MQTT
|
||||
|
||||
---
|
||||
|
||||
# WiFi Network
|
||||
|
||||
The ESP32 currently provides:
|
||||
|
||||
text Access Point Mode
|
||||
|
||||
Default address:
|
||||
|
||||
text 192.168.4.1
|
||||
|
||||
The dashboard and user devices connect directly to the controller.
|
||||
|
||||
No internet connection is required.
|
||||
|
||||
---
|
||||
|
||||
# Future Hardware Support
|
||||
|
||||
Planned additions:
|
||||
|
||||
## Vehicle Telemetry
|
||||
|
||||
Possible interfaces:
|
||||
|
||||
text OBD-II ELM327 CAN Bus
|
||||
|
||||
Potential data:
|
||||
|
||||
- Engine RPM
|
||||
- Coolant temperature
|
||||
- Fuel level
|
||||
- Vehicle speed
|
||||
- Diagnostic trouble codes
|
||||
|
||||
---
|
||||
|
||||
## GPS
|
||||
|
||||
Potential features:
|
||||
|
||||
- Location tracking
|
||||
- Trip logging
|
||||
- Route history
|
||||
- Speed tracking
|
||||
|
||||
---
|
||||
|
||||
## Environmental Sensors
|
||||
|
||||
Potential additions:
|
||||
|
||||
- Humidity
|
||||
- Barometric pressure
|
||||
- Air quality
|
||||
- Water tank monitoring
|
||||
|
||||
---
|
||||
|
||||
# Current Bill of Materials
|
||||
|
||||
## Installed
|
||||
|
||||
- ESP32 Controller
|
||||
- Raspberry Pi Pico 2 W
|
||||
- ESP32 Relay Module
|
||||
|
||||
---
|
||||
|
||||
## Planned
|
||||
|
||||
- 3.5" Touchscreen
|
||||
- DS18B20 Sensors
|
||||
- Automotive Relays
|
||||
- Fuse Block
|
||||
- Wiring Harness
|
||||
- Enclosures
|
||||
|
||||
---
|
||||
|
||||
# Design Goals
|
||||
|
||||
The hardware platform is intended to be:
|
||||
|
||||
- Modular
|
||||
- Vehicle independent
|
||||
- Serviceable
|
||||
- Expandable
|
||||
- Offline capable
|
||||
- Easy to troubleshoot
|
||||
- Suitable for long-term overland use
|
||||
- Do not migrate active firmware assumptions until the board is physically available.
|
||||
- Bench test relay boot behavior before connecting to WUPP or vehicle loads.
|
||||
- Confirm relay polarity/state behavior.
|
||||
- Confirm available GPIO for DS18B20 and any optional OLED/status hardware.
|
||||
- Treat the LilyGO board as the controller, not the high-current load path.
|
||||
|
||||
+15
-10
@@ -12,14 +12,18 @@ Control surfaces:
|
||||
|
||||
| Feature | USB Serial | UART JSON | HTTP API | Notes |
|
||||
|---|---:|---:|---:|---|
|
||||
| Status | Yes | Yes | Yes | `/status` equals `status_request` |
|
||||
| Config view | Yes | Yes | Yes | `/config` equals `config_request` |
|
||||
| Relay control | Yes | Yes | Yes | Preferred HTTP endpoint is `POST /relay/set` |
|
||||
| Device name config | Yes | Yes | Yes | `devicename`, `config_device`, `/config/device` |
|
||||
| Relay config | Yes | Yes | Yes | `relayname`, `config_relay`, `/config/relay` |
|
||||
| Temperature config | Yes | Yes | Yes | `tempname`, `config_temp`, `/config/temp` |
|
||||
| BMS config | Yes | Yes | Yes | `bmsname`/`bmsaddr`, `config_bms`, `/config/bms` |
|
||||
| Save config | Yes | Yes | Yes | `save`, `save_config`, `/config/save` |
|
||||
| Health | No | No | Yes | `/api/v1/health` is lightweight HTTP liveness |
|
||||
| Capabilities | No | No | Yes | `/api/v1/capabilities` supports client discovery |
|
||||
| Status | Yes | Yes | Yes | `/api/v1/status` equals `status_request` |
|
||||
| Config view | Yes | Yes | Yes | `/api/v1/config` equals `config_request` |
|
||||
| Config export | No | No | Yes | `/api/v1/config/export` includes WiFi passwords |
|
||||
| Config import | No | No | Yes | `/api/v1/config/import` persists restored config |
|
||||
| Relay control | Yes | Yes | Yes | Preferred HTTP endpoint is `POST /api/v1/relay/set` |
|
||||
| Device name config | Yes | Yes | Yes | `devicename`, `config_device`, `/api/v1/config/device` |
|
||||
| Relay config | Yes | Yes | Yes | `relayname`, `config_relay`, `/api/v1/config/relay` |
|
||||
| Temperature config | Yes | Yes | Yes | `tempname`, `config_temp`, `/api/v1/config/temp` |
|
||||
| BMS config | Yes | Yes | Yes | `bmsname`/`bmsaddr`, `config_bms`, `/api/v1/config/bms` |
|
||||
| Save config | Yes | Yes | Yes | `save`, `save_config`, `/api/v1/config/save` |
|
||||
| Factory reset | Yes | Yes | Yes | AP remains recovery path |
|
||||
| BMS setup enter | Yes | Yes | Yes | Setup pauses normal BMS reconnect behavior |
|
||||
| BMS setup exit | Yes | Yes | Yes | Returns to normal mode |
|
||||
@@ -58,9 +62,9 @@ Legacy UART relay fields may still be accepted:
|
||||
|
||||
Preferred HTTP relay endpoint:
|
||||
|
||||
POST /relay/set
|
||||
POST /api/v1/relay/set
|
||||
|
||||
Legacy HTTP relay routes may remain for compatibility:
|
||||
Legacy root HTTP relay routes remain for compatibility:
|
||||
|
||||
GET /relay/relay_1/on
|
||||
GET /relay/relay_1/off
|
||||
@@ -107,3 +111,4 @@ Future additions must be implemented on:
|
||||
|
||||
before parity is considered maintained.
|
||||
|
||||
- Temperature sensor config/status parity now includes the `weather` boolean flag for outside-air display selection.
|
||||
|
||||
+23
-4
@@ -12,15 +12,17 @@ This folder contains project documentation for Overland Controller.
|
||||
|
||||
## Project Tracking
|
||||
|
||||
- `CHATGPT_HANDOFF.md` - Concise assistant handoff with current status, goals, constraints, and validation notes
|
||||
- `project-state.md` - Current project state
|
||||
- `roadmap.md` - Roadmap and future plans
|
||||
- `hardware-status.md` - Hardware status and bring-up notes
|
||||
- `decisions.md` - Architecture and implementation decisions
|
||||
|
||||
## Technical References
|
||||
|
||||
- `protocol.md` - Dashboard/controller protocol
|
||||
- `wiring.md` - Wiring notes and plans
|
||||
- `dashboard-esp32s3.md` - Current Waveshare ESP32-S3 dashboard plan
|
||||
- `CONFIGURATION.md` - Configuration model and setup concepts
|
||||
- `TEMP_SETUP.md` - DS18B20 temperature sensor setup
|
||||
- `UART_PROTOCOL.md` - Optional UART diagnostics protocol
|
||||
|
||||
## Archive
|
||||
|
||||
@@ -28,7 +30,24 @@ Older or duplicate documents have been moved to `archive/`.
|
||||
|
||||
Archived documents are kept for history, but the primary documents above should be treated as the current source of truth.
|
||||
|
||||
|
||||
## Parity
|
||||
|
||||
- `PARITY_MATRIX.md` - Command parity across USB serial, UART JSON, and HTTP API
|
||||
|
||||
- `controller-roles.md` - Cargo ESP vs Dashboard ESP32-S3 responsibility boundaries
|
||||
|
||||
- `../archive/legacy-pico-dashboard/` - Archived Pico dashboard implementation kept for reference only.
|
||||
- `../firmware/esp32-s3-dashboard/` - Current ESP32-S3 dashboard firmware placeholder.
|
||||
|
||||
- `repo-cleanup-audit.md` - Active repo audit for stale Pico/UART dashboard references and DS18B20 documentation status.
|
||||
- `API.md` - High-level API notes and telemetry ownership.
|
||||
|
||||
- `cargo-api-contract.md` - Cargo ESP API ownership and expected API areas for WebUI/dashboard clients.
|
||||
|
||||
- `pre-lilygo-baseline-checklist.md` - Checklist for creating a known-good Cargo ESP 2-channel baseline before future LilyGO migration.
|
||||
|
||||
## LilyGO Transition
|
||||
|
||||
The LILYGO T-Relay-S3 6-way board has been ordered as a future Cargo ESP candidate.
|
||||
|
||||
Current development remains on the 2-channel relay board while the API and docs are prepared for output-count-agnostic operation.
|
||||
|
||||
+46
-21
@@ -1,8 +1,10 @@
|
||||
# UART Protocol
|
||||
|
||||
The Pico dashboard communicates with the ESP32 controller over UART.
|
||||
This document describes the optional UART JSON protocol supported by the ESP32 controller.
|
||||
|
||||
UART is the primary dashboard/control link. HTTP should mirror the same concepts, but the Pico should be able to operate the controller without WiFi.
|
||||
UART is not the primary dashboard/control link. The current dashboard target is the Waveshare ESP32-S3 display over WiFi/HTTP.
|
||||
|
||||
Keep this protocol for diagnostics, simulator work, service tools, or alternate clients. Do not treat it as evidence that a Pico dashboard is planned.
|
||||
|
||||
## Serial Settings
|
||||
|
||||
@@ -41,7 +43,7 @@ Temperature sensors:
|
||||
|
||||
## Status Request
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"status_request"}
|
||||
|
||||
@@ -49,7 +51,7 @@ ESP32 responds with the same shape as HTTP /status.
|
||||
|
||||
## Relay Control
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"set_relay","relay":"relay_1","enabled":true}
|
||||
|
||||
@@ -59,7 +61,7 @@ ESP32 responds:
|
||||
|
||||
## Config Request
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"config_request"}
|
||||
|
||||
@@ -69,37 +71,37 @@ ESP32 responds:
|
||||
|
||||
## Device Name Update
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"config_device","device_name":"Overland Controller"}
|
||||
|
||||
## Relay Config Update
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"config_relay","id":"relay_1","name":"Aux Power","enabled":true}
|
||||
|
||||
## Temperature Sensor Config Update
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"config_temp","id":"temp_1","name":"Cabin","address":"","enabled":true}
|
||||
|
||||
## BMS Config Update
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"config_bms","enabled":true,"name":"House Battery","address":"aa:bb:cc:dd:ee:ff","address_type":"public"}
|
||||
|
||||
## Save Config
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"save_config"}
|
||||
|
||||
## Factory Reset
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"factory_reset"}
|
||||
|
||||
@@ -156,14 +158,14 @@ The UART protocol should eventually support the same management capabilities as
|
||||
|
||||
## Notes
|
||||
|
||||
- UART is the preferred dashboard/control path.
|
||||
- HTTP is useful for setup, debugging, and future web UI.
|
||||
- HTTP is the preferred dashboard/control path.
|
||||
- UART is useful for diagnostics and alternate clients.
|
||||
- USB serial remains the emergency service/debug interface.
|
||||
- The Pico should not depend on WiFi for normal control.
|
||||
- No Pico dashboard is currently planned.
|
||||
|
||||
## WiFi Config Request
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"wifi_request"}
|
||||
|
||||
@@ -173,19 +175,19 @@ ESP32 responds:
|
||||
|
||||
## WiFi Config Update
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"config_wifi","ssid":"Starlink","password":"password_here"}
|
||||
|
||||
## WiFi Connect
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"wifi_connect"}
|
||||
|
||||
## WiFi Clear
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"wifi_clear"}
|
||||
|
||||
@@ -239,7 +241,7 @@ Lower priority numbers are tried first.
|
||||
|
||||
## Scan Temperature Sensors
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"scan_temps"}
|
||||
|
||||
@@ -249,7 +251,7 @@ ESP32 responds:
|
||||
|
||||
## Assign Temperature Sensor
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"assign_temp","id":"temp_1","index":1}
|
||||
|
||||
@@ -259,10 +261,33 @@ Alternative:
|
||||
|
||||
## Clear Temperature Sensor
|
||||
|
||||
Pico sends:
|
||||
Client sends:
|
||||
|
||||
{"type":"clear_temp","id":"temp_1"}
|
||||
|
||||
Alternative:
|
||||
|
||||
{"type":"clear_temp","slot":1}
|
||||
|
||||
### Temperature weather flag
|
||||
|
||||
`status_response.config.temperature_sensors[]` and `status_response.temps[]` include `weather: false`.
|
||||
|
||||
`config_temp` may include `weather: true` to mark one configured sensor as the outside/weather sensor. Clients should treat this as optional and default missing values to `false`.
|
||||
|
||||
|
||||
## Current communications and GPIO decision
|
||||
|
||||
Active dashboard communication is WiFi/HTTP using `/api/v1`.
|
||||
|
||||
The old Pico/dashboard UART path is retired from active hardware use. USB Serial remains available for development, debug, and manual configuration from a computer.
|
||||
|
||||
Current Cargo ESP32 GPIO plan:
|
||||
|
||||
- GPIO 16: Relay 1 trigger output
|
||||
- GPIO 17: Relay 2 trigger output
|
||||
- GPIO 4: DS18B20 OneWire temperature bus
|
||||
- GPIO 34: Ignition sense input
|
||||
- GPIO 21: SSD1306 OLED I2C SDA
|
||||
- GPIO 22: SSD1306 OLED I2C SCL
|
||||
- GPIO 25: OLED setup/status button
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Xterra Overland Dashboard - Codex Handoff
|
||||
# Overland Controller - Codex Handoff
|
||||
|
||||
## Repository
|
||||
|
||||
@@ -27,7 +27,7 @@ Before making any code changes:
|
||||
|
||||
# Project Overview
|
||||
|
||||
The project is an overland vehicle monitoring and control system for a Nissan Xterra.
|
||||
The project is an overland vehicle monitoring and control system for a overland vehicle.
|
||||
|
||||
Architecture:
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Legacy Current-Cleanup Archive
|
||||
|
||||
These files were moved out of the active docs during the June 2026 documentation cleanup.
|
||||
|
||||
They are retained for historical reference only. Current source-of-truth docs are:
|
||||
|
||||
- `docs/ARCHITECTURE.md`
|
||||
- `docs/HARDWARE.md`
|
||||
- `docs/ROADMAP.md`
|
||||
- `docs/API.md`
|
||||
- `docs/dashboard-esp32s3.md`
|
||||
|
||||
Do not use the archived Pico/UART wiring or handoff notes for current implementation work.
|
||||
@@ -0,0 +1,170 @@
|
||||
# Overland Controller Handoff
|
||||
|
||||
This handoff is stale and may include legacy Pico/UART dashboard assumptions.
|
||||
|
||||
Current source of truth:
|
||||
|
||||
- `docs/ARCHITECTURE.md`
|
||||
- `docs/HARDWARE.md`
|
||||
- `docs/ROADMAP.md`
|
||||
- `docs/API.md`
|
||||
- `docs/dashboard-esp32s3.md`
|
||||
|
||||
The current dashboard target is the Waveshare ESP32-S3-Touch-LCD-5 over WiFi/HTTP. No Pico dashboard is planned.
|
||||
|
||||
## Current Version
|
||||
|
||||
Firmware: 0.4.0
|
||||
|
||||
## Current State
|
||||
|
||||
The ESP32 backend is now the source of truth and is mostly Phase 1 complete.
|
||||
|
||||
Working:
|
||||
|
||||
- ESP32 WebUI
|
||||
- AP + STA WiFi
|
||||
- Multiple saved WiFi networks with priority
|
||||
- Relay control through built-in relay board
|
||||
- JBD/Xiaoxiang BLE BMS telemetry
|
||||
- DS18B20 temperature support
|
||||
- Temperature scan/assign/config workflow
|
||||
- HTTP API
|
||||
- UART JSON protocol
|
||||
- USB serial maintenance commands
|
||||
- Config-driven relay/temp/BMS/device naming
|
||||
- WebUI Config tab for initial setup
|
||||
|
||||
## Important Recent Fix
|
||||
|
||||
Do not use `client->setConnectTimeout()` for the JBD/Xiaoxiang BMS.
|
||||
|
||||
It caused Greta/BMS BLE connection failures.
|
||||
|
||||
Known-good BMS behavior uses the default NimBLE connection timeout.
|
||||
|
||||
## Current Hardware Direction
|
||||
|
||||
Rear enclosure:
|
||||
|
||||
- ESP32 2-relay board
|
||||
- Mini fuse block
|
||||
- Ground bus
|
||||
- DS18B20 distribution board
|
||||
- CAT6 keystone
|
||||
- Built-in relays used for Starlink Mini and fridge
|
||||
- Positive-side relay switching
|
||||
|
||||
Dashboard enclosure:
|
||||
|
||||
- Raspberry Pi Pico 2 W / RP2350
|
||||
- 3.5" 320x480 ST7796S display
|
||||
- FT6336U capacitive touch, wired but not required for MVP
|
||||
- 12V over CAT5e from rear enclosure
|
||||
- Local 12V-to-5V buck converter in dash enclosure
|
||||
- Pico and display powered from dash buck converter
|
||||
|
||||
## CAT5e Plan
|
||||
|
||||
Rear to dash:
|
||||
|
||||
- Two conductors for fused +12V
|
||||
- Two conductors for ground
|
||||
- One conductor for ESP32 TX to Pico RX
|
||||
- One conductor for ESP32 RX to Pico TX
|
||||
- Spare pair reserved
|
||||
|
||||
All grounds are common:
|
||||
|
||||
- ESP32 GND
|
||||
- Buck GND
|
||||
- Pico GND
|
||||
- Display GND
|
||||
- UART GND
|
||||
|
||||
## Pico Display Solder Map
|
||||
|
||||
Display:
|
||||
|
||||
- VCC -> 5V buck output
|
||||
- GND -> GND
|
||||
- SCLK -> Pico GPIO18 / Pin 24
|
||||
- MOSI -> Pico GPIO19 / Pin 25
|
||||
- LCD_CS -> Pico GPIO17 / Pin 22
|
||||
- LCD_DC -> Pico GPIO16 / Pin 21
|
||||
- LCD_RST -> Pico GPIO20 / Pin 26
|
||||
- LCD_BL -> Pico GPIO21 / Pin 27
|
||||
|
||||
Touch, wired for future:
|
||||
|
||||
- TP_SDA -> Pico GPIO26 / Pin 31
|
||||
- TP_SCL -> Pico GPIO27 / Pin 32
|
||||
- TP_INT -> Pico GPIO28 / Pin 34
|
||||
- TP_RST -> Pico GPIO15 / Pin 20
|
||||
|
||||
UART to ESP32 dashboard UART:
|
||||
|
||||
- Pico GPIO0 / Pin 1 RX <- ESP32 GPIO21 TX
|
||||
- Pico GPIO1 / Pin 2 TX -> ESP32 GPIO22 RX
|
||||
- Pico GND -> ESP32 GND
|
||||
|
||||
Do not use the ESP32 6-pin UART header for dashboard UART. It has continuity to RXD and is likely UART0/programming/debug.
|
||||
|
||||
## Pico MVP Goal
|
||||
|
||||
MVP means Minimum Viable Product.
|
||||
|
||||
Pico MVP should be read-only first:
|
||||
|
||||
- Request status from ESP32 over UART
|
||||
- Parse JSON status response
|
||||
- Display battery SOC, voltage, current, runtime/time-to-full
|
||||
- Display temps
|
||||
- Display relay states
|
||||
- Display ESP32 connection status
|
||||
|
||||
No touch controls for MVP.
|
||||
|
||||
## Runtime / Time-To-Full Logic
|
||||
|
||||
If BMS current is negative/discharging:
|
||||
|
||||
runtime_hours = remaining_ah / abs(current)
|
||||
|
||||
If BMS current is positive/charging:
|
||||
|
||||
time_to_full_hours = (capacity_ah - remaining_ah) / current
|
||||
|
||||
If current is near zero:
|
||||
|
||||
show Idle
|
||||
|
||||
## Next Work
|
||||
|
||||
1. Build Pico MVP.
|
||||
2. First prove UART status over USB serial.
|
||||
3. Then prove display Hello World.
|
||||
4. Then draw read-only dashboard.
|
||||
5. Later add touch navigation and relay controls.
|
||||
6. Later add config backup/restore.
|
||||
7. Later improve BMS out-of-range behavior without changing NimBLE connect timeout.
|
||||
|
||||
|
||||
|
||||
## Dashboard Direction
|
||||
|
||||
Current dashboard target:
|
||||
|
||||
- Waveshare 5" ESP32-S3 Display
|
||||
- WiFi client
|
||||
- Connects to cargo ESP32 AP
|
||||
- Uses existing HTTP API
|
||||
- Planned LVGL implementation
|
||||
|
||||
Important:
|
||||
|
||||
- ESP32 cargo controller remains source of truth.
|
||||
- Dashboard is a client only.
|
||||
- Existing UART protocol remains supported but is no longer the primary dashboard transport.
|
||||
|
||||
See `docs/dashboard-esp32s3.md` for the current Waveshare ESP32-S3 dashboard plan.
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
Last Updated: 2026-06-03
|
||||
|
||||
This document is stale and records earlier hardware ordering/status notes. Current hardware direction is documented in `docs/HARDWARE.md` and `docs/dashboard-esp32s3.md`.
|
||||
|
||||
The current dashboard target is the Waveshare ESP32-S3-Touch-LCD-5 over WiFi/HTTP. No Pico dashboard is planned.
|
||||
|
||||
--------------------------------------------------
|
||||
|
||||
## Dashboard Hardware
|
||||
@@ -1,10 +1,10 @@
|
||||
# ESP32 ↔ Pico Communication Protocol
|
||||
# Legacy UART Communication Protocol
|
||||
|
||||
## Current Decision
|
||||
|
||||
Primary communication is **UART over CAT5**.
|
||||
Primary dashboard communication is **WiFi HTTP API** between the Waveshare ESP32-S3 dashboard and the Cargo ESP32.
|
||||
|
||||
Backup communication is the **ESP32 WiFi HTTP API**.
|
||||
This legacy UART protocol is retained for historical reference, diagnostics, simulator work, or alternate clients. It is not the current dashboard architecture.
|
||||
|
||||
RS-485/MAX3485 is fallback-only and is not currently planned.
|
||||
|
||||
@@ -14,7 +14,7 @@ RS-485/MAX3485 is fallback-only and is not currently planned.
|
||||
* Baud: 115200
|
||||
* Format: 8N1
|
||||
* Framing: newline-delimited JSON
|
||||
* Direction: Pico dashboard ↔ ESP32 cargo controller
|
||||
* Direction: UART client ↔ ESP32 cargo controller
|
||||
|
||||
## Message Rules
|
||||
|
||||
@@ -28,7 +28,7 @@ Example:
|
||||
|
||||
Responses should also be one JSON object followed by a newline.
|
||||
|
||||
## Pico → ESP32 Messages
|
||||
## Client → ESP32 Messages
|
||||
|
||||
### Status Request
|
||||
|
||||
@@ -68,7 +68,7 @@ Expected response:
|
||||
}
|
||||
```
|
||||
|
||||
## ESP32 → Pico Messages
|
||||
## ESP32 → Client Messages
|
||||
|
||||
### Status Response
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
# Wiring
|
||||
|
||||
This document describes the current planned wiring for the Overland Controller install.
|
||||
This document contains older wiring notes and may include legacy Pico/UART dashboard assumptions.
|
||||
|
||||
The current system uses:
|
||||
Current dashboard and hardware source of truth:
|
||||
|
||||
- ESP32 2-relay controller board
|
||||
- Raspberry Pi Pico 2 W dashboard
|
||||
- CAT5/CAT6 cable between rear controller and dash
|
||||
- 12V-to-5V buck converter in the dash enclosure
|
||||
- `docs/HARDWARE.md`
|
||||
- `docs/dashboard-esp32s3.md`
|
||||
|
||||
Do not use Pico-specific wiring in this file for the current dashboard build.
|
||||
|
||||
Current planned system uses:
|
||||
|
||||
- Cargo ESP32 2-relay controller board
|
||||
- Waveshare ESP32-S3-Touch-LCD-5 dashboard over WiFi/HTTP
|
||||
- ACC switched 12V fused dashboard power into Waveshare VIN
|
||||
- DS18B20 temperature sensors
|
||||
- JBD/Xiaoxiang BMS over BLE
|
||||
- External automotive relays for high-current loads
|
||||
- External fused automotive relays or contactors for major loads
|
||||
|
||||
---
|
||||
|
||||
@@ -547,3 +553,111 @@ Suggested fuse allocation:
|
||||
Spare optional
|
||||
|
||||
External automotive relays remain a good upgrade path for higher-current loads.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Current Relay Switching Decision
|
||||
|
||||
For the current Starlink Mini and fridge use case, the built-in ESP32 relay board relays are the planned switching method.
|
||||
|
||||
Use positive-side switching:
|
||||
|
||||
Battery +
|
||||
|
|
||||
Fuse
|
||||
|
|
||||
Relay COM
|
||||
|
|
||||
Relay NO
|
||||
|
|
||||
Load +
|
||||
|
||||
Load -
|
||||
|
|
||||
Ground bus
|
||||
|
||||
External automotive relays are reserved for future higher-current loads.
|
||||
|
||||
## DS18B20 Final Install Direction
|
||||
|
||||
Temporary Wago wiring proved one probe, but final install should use a proper terminal/distribution board.
|
||||
|
||||
Recommended:
|
||||
|
||||
3.3V bus
|
||||
DATA bus
|
||||
GND bus
|
||||
|
||||
With:
|
||||
|
||||
4.7k resistor between DATA and 3.3V
|
||||
|
||||
Only three wires need to return to the ESP32:
|
||||
|
||||
3.3V
|
||||
GPIO4
|
||||
GND
|
||||
|
||||
---
|
||||
|
||||
## Pico Dashboard Wiring
|
||||
|
||||
The Pico dashboard should be powered from a local 12V-to-5V buck converter in the dash enclosure.
|
||||
|
||||
Rear enclosure sends fused 12V over CAT5e.
|
||||
|
||||
Dash side:
|
||||
|
||||
CAT5e +12V -> buck IN+
|
||||
CAT5e GND -> buck IN-
|
||||
|
||||
buck OUT+ 5V -> Pico VSYS
|
||||
buck OUT+ 5V -> Display VCC
|
||||
|
||||
buck OUT- GND -> Pico GND
|
||||
buck OUT- GND -> Display GND
|
||||
|
||||
Display wiring:
|
||||
|
||||
SCLK -> Pico GPIO18
|
||||
MOSI -> Pico GPIO19
|
||||
LCD_CS -> Pico GPIO17
|
||||
LCD_DC -> Pico GPIO16
|
||||
LCD_RST -> Pico GPIO20
|
||||
LCD_BL -> Pico GPIO21
|
||||
|
||||
Touch wiring, reserved for future:
|
||||
|
||||
TP_SDA -> Pico GPIO26
|
||||
TP_SCL -> Pico GPIO27
|
||||
TP_INT -> Pico GPIO28
|
||||
TP_RST -> Pico GPIO15
|
||||
|
||||
UART wiring:
|
||||
|
||||
ESP32 GPIO21 TX -> Pico GPIO0 RX
|
||||
ESP32 GPIO22 RX -> Pico GPIO1 TX
|
||||
ESP32 GND -> Pico GND
|
||||
|
||||
Do not use the ESP32 6-pin UART header for the dashboard UART. It appears to be connected to RXD/TXD for UART0/programming/debug.
|
||||
|
||||
|
||||
|
||||
## Dashboard Wiring (Current Direction)
|
||||
|
||||
Dashboard Hardware:
|
||||
- Waveshare 5" ESP32-S3 Display
|
||||
|
||||
Communications:
|
||||
- WiFi connection to cargo ESP32 AP
|
||||
- HTTP API polling
|
||||
- No UART required for normal operation
|
||||
|
||||
CAT5e Usage:
|
||||
- 12V power from cargo area to dashboard
|
||||
- Local buck converter in dashboard enclosure
|
||||
- Common ground
|
||||
|
||||
Optional Future:
|
||||
- UART over CAT5e for diagnostics or alternate dashboard hardware
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Purpose
|
||||
|
||||
The Raspberry Pi Pico 2 W dashboard is the primary user interface for the Xterra Overland Power & Monitoring Dashboard.
|
||||
The Raspberry Pi Pico 2 W dashboard is the primary user interface for the Overland Controller dashboard.
|
||||
|
||||
Its responsibilities are:
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
The first hardware version will use UART over CAT5 between the Pico dashboard and the ESP32 controller.
|
||||
|
||||
UART is simpler than RS-485 and should be reliable for the short cable run inside the Xterra.
|
||||
UART is simpler than RS-485 and should be reliable for the short cable run inside the vehicle.
|
||||
|
||||
RS-485 remains a future fallback if testing reveals communication issues.
|
||||
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
# Cargo ESP API Contract
|
||||
|
||||
The Cargo ESP32 is the source of truth for power-system state.
|
||||
|
||||
The WebUI, ESP32-S3 dashboard, and future clients should consume this API rather than reading sensors or relay state directly.
|
||||
|
||||
## Ownership
|
||||
|
||||
Cargo ESP32 owns:
|
||||
|
||||
- Relay/output state
|
||||
- DS18B20 temperature readings
|
||||
- BMS telemetry
|
||||
- Alarm/fault state
|
||||
- Local configuration
|
||||
- WebUI/API service
|
||||
|
||||
Dashboard ESP32-S3 consumes:
|
||||
|
||||
- Status
|
||||
- Battery/BMS telemetry
|
||||
- Temperature telemetry
|
||||
- Relay/output state
|
||||
- Alarm/fault state
|
||||
|
||||
The dashboard may request relay changes through the API, but the Cargo ESP remains authoritative.
|
||||
|
||||
## Expected API Areas
|
||||
|
||||
### System Status
|
||||
|
||||
Used by clients to determine whether the Cargo ESP is online and healthy.
|
||||
|
||||
Expected data:
|
||||
|
||||
- Device name
|
||||
- Firmware version
|
||||
- Uptime
|
||||
- WiFi/AP status
|
||||
- API version
|
||||
- Alarm/fault summary
|
||||
|
||||
### Battery / BMS
|
||||
|
||||
Used for house battery display.
|
||||
|
||||
Expected data:
|
||||
|
||||
- Voltage
|
||||
- Current
|
||||
- State of charge
|
||||
- Charging/discharging state
|
||||
- Cell data when available
|
||||
- BMS online/offline status
|
||||
- Protection/fault state
|
||||
|
||||
### Temperatures
|
||||
|
||||
DS18B20 support is already implemented in the Cargo ESP/WebUI/API path.
|
||||
|
||||
Expected data:
|
||||
|
||||
- Sensor ID
|
||||
- Display name
|
||||
- Enabled state
|
||||
- Online/offline state
|
||||
- Temperature value
|
||||
- Temperature unit
|
||||
- Role/location, such as fridge, cargo, ambient, or spare
|
||||
|
||||
The ESP32-S3 dashboard should display this data from the Cargo ESP API. It should not directly own cargo/fridge DS18B20 sensor wiring for v1.
|
||||
|
||||
### Relays / Outputs
|
||||
|
||||
Used for fridge, Starlink, lights, spare, and future switched outputs.
|
||||
|
||||
Expected data:
|
||||
|
||||
- Output ID
|
||||
- Display name
|
||||
- Enabled state
|
||||
- Current state
|
||||
- Requested state
|
||||
- Fault/lockout state when available
|
||||
- Optional output type or role
|
||||
|
||||
Relay/output writes should go through the Cargo ESP API.
|
||||
|
||||
### Configuration
|
||||
|
||||
Configuration should remain owned by the Cargo ESP.
|
||||
|
||||
Expected future areas:
|
||||
|
||||
- Output names
|
||||
- Output enabled/disabled state
|
||||
- Temperature sensor names
|
||||
- Temperature sensor roles
|
||||
- Alarm thresholds
|
||||
- Network/AP settings
|
||||
- OTA settings
|
||||
|
||||
## v1 Client Communication
|
||||
|
||||
Current v1 client path:
|
||||
|
||||
ESP32-S3 Dashboard
|
||||
<-> WiFi / HTTP REST
|
||||
Cargo ESP32 AP
|
||||
|
||||
Not v1 API transport:
|
||||
|
||||
- ESP-NOW
|
||||
- MQTT
|
||||
- UART dashboard link
|
||||
- CAN between controllers
|
||||
|
||||
## Compatibility Rule
|
||||
|
||||
When possible, future changes should add fields rather than rename or remove existing fields.
|
||||
|
||||
Dashboard firmware should tolerate unknown fields.
|
||||
|
||||
Cargo ESP firmware should preserve stable field names once dashboard implementation begins.
|
||||
|
||||
## Output Hardware Profiles
|
||||
|
||||
The Cargo ESP API should describe outputs generically instead of assuming a fixed relay board.
|
||||
|
||||
Current active hardware:
|
||||
|
||||
- Generic ESP32 with 2-channel relay board
|
||||
- Used for ongoing development and testing
|
||||
|
||||
Ordered/future hardware:
|
||||
|
||||
- LILYGO T-Relay-S3 6-way
|
||||
- Intended to trigger the 6 relays on the WUPP fuse/relay block
|
||||
- Not active firmware target until hardware arrives and is bench tested
|
||||
|
||||
API clients should not assume exactly 2 outputs or exactly 6 outputs.
|
||||
|
||||
Instead, clients should read the output list exposed by the Cargo ESP and render whatever outputs are available.
|
||||
|
||||
Expected output object fields:
|
||||
|
||||
- `id`
|
||||
- `name`
|
||||
- `role`
|
||||
- `enabled`
|
||||
- `state`
|
||||
- `requested_state`
|
||||
- `available`
|
||||
- `hardware_channel`
|
||||
- `hardware_profile`
|
||||
|
||||
Example 2-channel profile:
|
||||
|
||||
{
|
||||
"hardware_profile": "generic_esp32_2ch_relay",
|
||||
"outputs": [
|
||||
{"id": 1, "name": "Fridge", "role": "fridge", "hardware_channel": 1},
|
||||
{"id": 2, "name": "Starlink", "role": "starlink", "hardware_channel": 2}
|
||||
]
|
||||
}
|
||||
|
||||
Example future 6-channel profile:
|
||||
|
||||
{
|
||||
"hardware_profile": "lilygo_t_relay_s3_6ch_wupp",
|
||||
"outputs": [
|
||||
{"id": 1, "name": "Fridge", "role": "fridge", "hardware_channel": 1},
|
||||
{"id": 2, "name": "Starlink", "role": "starlink", "hardware_channel": 2},
|
||||
{"id": 3, "name": "Lights", "role": "lights", "hardware_channel": 3},
|
||||
{"id": 4, "name": "Spare 1", "role": "spare", "hardware_channel": 4},
|
||||
{"id": 5, "name": "Spare 2", "role": "spare", "hardware_channel": 5},
|
||||
{"id": 6, "name": "Spare 3", "role": "spare", "hardware_channel": 6}
|
||||
]
|
||||
}
|
||||
|
||||
Compatibility rule:
|
||||
|
||||
- The API should support the current 2-channel board while allowing a future 6-output profile without changing dashboard/client assumptions.
|
||||
- Clients should render outputs dynamically from the API response.
|
||||
- Clients should not hardcode relay count.
|
||||
@@ -0,0 +1,108 @@
|
||||
# Controller Roles
|
||||
|
||||
This document separates committed v1 architecture from future wishlist items.
|
||||
|
||||
## Cargo ESP32 Controller
|
||||
|
||||
The Cargo ESP32 is the power-system controller and source of truth.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- WiFi AP
|
||||
- Embedded WebUI
|
||||
- REST/HTTP API
|
||||
- Relay/output control
|
||||
- Automotive load-switching control outputs
|
||||
- JBD/Xiaoxiang BLE BMS telemetry
|
||||
- DS18B20 temperature sensors
|
||||
- Optional SSD1306 OLED service/status display
|
||||
- Local configuration storage
|
||||
- Alarm and fault state
|
||||
|
||||
Does not do:
|
||||
|
||||
- CAN sniffing
|
||||
- OBD-II polling
|
||||
- Vehicle data ownership
|
||||
- Tilt/roll sensing
|
||||
- Dashboard rendering
|
||||
- Gauge cluster rendering
|
||||
|
||||
## Dashboard ESP32-S3
|
||||
|
||||
The Dashboard ESP32-S3 is a touchscreen client and future vehicle-visualization node.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- Touchscreen UI
|
||||
- LVGL dashboard rendering
|
||||
- Vehicle gauges
|
||||
- CAN bus access
|
||||
- OBD-II polling
|
||||
- Future VIN decoding
|
||||
- Future fault-code display
|
||||
- Future transmission temperature display
|
||||
- Future tilt/roll sensor display
|
||||
- Future off-road screen
|
||||
- Vehicle status visualization
|
||||
|
||||
The dashboard must not own relay state, BMS state, alarm authority, or persistent cargo-controller configuration.
|
||||
|
||||
## v1 Communications
|
||||
|
||||
Current planned v1 path:
|
||||
|
||||
Dashboard ESP32-S3
|
||||
<-> WiFi / HTTP REST
|
||||
Cargo ESP32 AP
|
||||
<-> control outputs
|
||||
Automotive load-switching layer
|
||||
<-> fused loads
|
||||
Fridge / Starlink / lights / spare outputs
|
||||
|
||||
Not planned for v1 controller-to-dashboard communication:
|
||||
|
||||
- ESP-NOW
|
||||
- MQTT
|
||||
- CAN between controllers
|
||||
- UART between dashboard and Cargo ESP
|
||||
|
||||
MQTT may be considered later for optional Home Assistant, Grafana, InfluxDB, or home-server integrations, but it is not the v1 dashboard link.
|
||||
|
||||
## Power Distribution
|
||||
|
||||
Current v1 assumption:
|
||||
|
||||
Cargo ESP32 control outputs
|
||||
↓
|
||||
Automotive load-switching layer
|
||||
↓
|
||||
Properly fused loads:
|
||||
- Fridge
|
||||
- Starlink
|
||||
- Lights
|
||||
- Spare
|
||||
|
||||
The ESP32 relay board must not directly carry fridge, Starlink, inverter, compressor, or lighting load current.
|
||||
|
||||
The exact load-switching hardware is not finalized yet. Candidate implementation options include:
|
||||
|
||||
- Bosch-style automotive relays
|
||||
- WUPP-style fused relay/distribution module
|
||||
- Smart high-side switching
|
||||
- Future custom PCB
|
||||
|
||||
## Hardware Wishlist / Future
|
||||
|
||||
Keep these separate from committed architecture until explicitly promoted:
|
||||
|
||||
- BNO085/BNO086 tilt sensor
|
||||
- CAN sniffing
|
||||
- Fault-code support
|
||||
- Standalone dashboard mode
|
||||
- Additional outputs
|
||||
- Custom PCB
|
||||
- Smart high-side switching
|
||||
- GPS
|
||||
- microSD logging
|
||||
- Home Assistant / MQTT / Grafana / InfluxDB
|
||||
@@ -0,0 +1,205 @@
|
||||
# ESP32-S3 Dashboard Plan
|
||||
|
||||
## Direction
|
||||
|
||||
The primary dashboard target is now a Waveshare 5 inch ESP32-S3 display.
|
||||
|
||||
The cargo ESP32 remains the source of truth. The dashboard is a WiFi client that connects to the cargo ESP32 AP and consumes the existing HTTP API.
|
||||
|
||||
## Architecture
|
||||
|
||||
Cargo ESP32 Controller
|
||||
- AP / STA WiFi
|
||||
- HTTP API
|
||||
- WebUI
|
||||
- Relays
|
||||
- JBD/Xiaoxiang BMS
|
||||
- DS18B20 sensors
|
||||
|
||||
Waveshare ESP32-S3 Dashboard
|
||||
- Connects to cargo ESP32 AP
|
||||
- Polls /status
|
||||
- Renders LVGL dashboard
|
||||
- Sends relay commands over HTTP
|
||||
|
||||
## Transport
|
||||
|
||||
Primary transport:
|
||||
- WiFi
|
||||
- HTTP JSON API
|
||||
|
||||
Not required for normal dashboard operation:
|
||||
- MicroPython display drivers
|
||||
- CAT5e UART data pair
|
||||
|
||||
UART JSON remains useful for diagnostics, alternate displays, or debug clients, but it is not part of the planned dashboard path.
|
||||
|
||||
## Power
|
||||
|
||||
Dashboard power path:
|
||||
|
||||
ACC switched 12V
|
||||
-> fuse
|
||||
-> Waveshare VIN
|
||||
|
||||
All grounds remain common.
|
||||
|
||||
## API endpoints consumed
|
||||
|
||||
Minimum dashboard MVP:
|
||||
- GET /api/v1/health
|
||||
- GET /api/v1/status
|
||||
- POST /api/v1/relay/set
|
||||
|
||||
Potential later use:
|
||||
- GET /api/v1/capabilities
|
||||
- GET /api/v1/config
|
||||
- GET /api/v1/config/export
|
||||
- POST /api/v1/config/import
|
||||
- POST /api/v1/config/save
|
||||
- POST /api/v1/temps/scan
|
||||
- POST /api/v1/temps/assign
|
||||
- POST /api/v1/temps/clear
|
||||
|
||||
## MVP Display Goals
|
||||
|
||||
Read-only first:
|
||||
- ESP32 connection status
|
||||
- Battery SOC
|
||||
- Voltage
|
||||
- Current
|
||||
- Runtime / time-to-full
|
||||
- BMS state
|
||||
- Temperature sensors
|
||||
- Weather/outside temp badge
|
||||
- Relay states
|
||||
|
||||
Then interactive:
|
||||
- Relay ON/OFF buttons
|
||||
- Alarm/details pages
|
||||
- Settings/status pages
|
||||
|
||||
## Suggested LVGL screens
|
||||
|
||||
### Main Dashboard
|
||||
- SOC gauge
|
||||
- Voltage/current
|
||||
- Runtime or time-to-full
|
||||
- Outside temp/weather badge
|
||||
- Fridge/freezer temps
|
||||
- Relay state summary
|
||||
|
||||
### Battery Detail
|
||||
- SOC
|
||||
- Voltage
|
||||
- Current
|
||||
- Remaining Ah
|
||||
- Capacity Ah
|
||||
- Cell voltages
|
||||
- Cell delta
|
||||
- BMS temp
|
||||
- Cycle count
|
||||
|
||||
### Relay Control
|
||||
- Starlink relay
|
||||
- Fridge relay
|
||||
- Future accessory relays
|
||||
- Clear ON/OFF touch targets
|
||||
|
||||
### Temperature Detail
|
||||
- Freezer
|
||||
- Fridge
|
||||
- Outside/weather
|
||||
- Cargo/ambient
|
||||
- Online/offline status
|
||||
|
||||
### System Status
|
||||
- Cargo ESP firmware version
|
||||
- WiFi state
|
||||
- API connectivity
|
||||
- Uptime
|
||||
- Alarm status
|
||||
|
||||
## Connection Flow
|
||||
|
||||
1. Dashboard boots.
|
||||
2. Connects to cargo ESP32 AP.
|
||||
3. Checks GET /api/v1/health.
|
||||
4. Polls GET /api/v1/status.
|
||||
5. Shows disconnected state until valid JSON is received.
|
||||
6. Updates dashboard on a fixed interval.
|
||||
7. Relay buttons call POST /api/v1/relay/set.
|
||||
|
||||
## Polling Guidance
|
||||
|
||||
Initial MVP:
|
||||
- Poll /status every 1-2 seconds.
|
||||
- Treat failed requests as dashboard disconnected.
|
||||
- Avoid blocking UI rendering while waiting for HTTP.
|
||||
|
||||
Later:
|
||||
- Add retry/backoff.
|
||||
- Add cached last-known values.
|
||||
- Add visual stale-data indicator.
|
||||
|
||||
## Important Constraints
|
||||
|
||||
- Do not move source-of-truth logic to the dashboard.
|
||||
- Do not duplicate BMS parsing on the dashboard.
|
||||
- Do not require internet access.
|
||||
- Do not make dashboard config the primary setup workflow.
|
||||
- WebUI Config tab remains the preferred setup workflow.
|
||||
- Cargo ESP32 should continue to work without the dashboard powered on.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
No Pico dashboard is planned. Existing Pico/UART material is legacy unless explicitly revived for diagnostics or alternate hardware.
|
||||
|
||||
|
||||
## Current communications and GPIO decision
|
||||
|
||||
Active dashboard communication is WiFi/HTTP using `/api/v1`.
|
||||
|
||||
The old Pico/dashboard UART path is retired from active hardware use. USB Serial remains available for development, debug, and manual configuration from a computer.
|
||||
|
||||
Current Cargo ESP32 GPIO plan:
|
||||
|
||||
- GPIO 16: Relay 1 trigger output
|
||||
- GPIO 17: Relay 2 trigger output
|
||||
- GPIO 4: DS18B20 OneWire temperature bus
|
||||
- GPIO 34: Ignition sense input
|
||||
- GPIO 21: SSD1306 OLED I2C SDA
|
||||
- GPIO 22: SSD1306 OLED I2C SCL
|
||||
- GPIO 25: OLED setup/status button
|
||||
|
||||
## Not Dashboard Responsibilities
|
||||
|
||||
The Dashboard ESP32-S3 does not own:
|
||||
|
||||
- Relay state authority
|
||||
- BMS connection/configuration
|
||||
- Cargo ESP configuration
|
||||
- Alarm authority
|
||||
- Load-switching output ownership
|
||||
|
||||
It may display state and request changes through the Cargo ESP HTTP API, but the Cargo ESP remains the source of truth.
|
||||
|
||||
## Temperature Data Source
|
||||
|
||||
The ESP32-S3 dashboard should display temperature data from the Cargo ESP API.
|
||||
|
||||
DS18B20 sensors are already implemented on the Cargo ESP/WebUI/API side. For v1, the dashboard should not directly own cargo/fridge DS18B20 wiring or sensor reads.
|
||||
|
||||
## API Dependency
|
||||
|
||||
The ESP32-S3 dashboard depends on the Cargo ESP API contract documented in `docs/cargo-api-contract.md`.
|
||||
|
||||
For v1, the dashboard should consume:
|
||||
|
||||
- System status
|
||||
- Battery/BMS telemetry
|
||||
- DS18B20 temperature telemetry
|
||||
- Relay/output state
|
||||
- Alarm/fault state
|
||||
|
||||
The dashboard may request output changes through the Cargo ESP API, but the Cargo ESP remains the source of truth.
|
||||
+29
-12
@@ -20,38 +20,45 @@ Reason:
|
||||
|
||||
The ESP32 is installed near the electrical system and should continue operating even if the dashboard is disconnected.
|
||||
|
||||
## Decision: Pico Is a Dashboard, Not the Controller
|
||||
## Decision: Waveshare ESP32-S3 Is the Dashboard Client
|
||||
|
||||
The Pico dashboard is responsible for user interaction.
|
||||
The Waveshare ESP32-S3 touchscreen dashboard is responsible for local user interaction.
|
||||
|
||||
It should not own critical control state.
|
||||
It connects to the Cargo ESP32 access point and consumes the HTTP API. It should not own critical control state.
|
||||
|
||||
Reason:
|
||||
|
||||
The dashboard may reboot, disconnect, or be removed without disabling the controller.
|
||||
|
||||
## Decision: UART Is the Primary Dashboard Link
|
||||
## Decision: HTTP Is the Primary Dashboard Link
|
||||
|
||||
UART is the preferred Pico-to-ESP32 communication path.
|
||||
HTTP over the Cargo ESP32 local WiFi network is the preferred dashboard-to-controller communication path.
|
||||
|
||||
Reason:
|
||||
|
||||
- Simple wiring
|
||||
- Deterministic
|
||||
- No WiFi dependency
|
||||
- Works offline
|
||||
- Easier to debug electrically
|
||||
- Matches the WebUI and future integration contract
|
||||
- Avoids a dedicated dashboard data cable
|
||||
- Supports the Waveshare ESP32-S3 dashboard target
|
||||
- Keeps the dashboard a simple client
|
||||
|
||||
HTTP remains useful for setup, debugging, and future web UI.
|
||||
The Cargo ESP32 AP remains available for local operation without internet.
|
||||
|
||||
## Decision: UART Is Optional Diagnostics
|
||||
|
||||
UART JSON may remain useful for service diagnostics, simulator work, or alternate clients.
|
||||
|
||||
Reason:
|
||||
|
||||
USB serial and UART are useful recovery/debug paths, but the dashboard architecture should not depend on a Pico or UART cable.
|
||||
|
||||
## Decision: HTTP and UART Should Share a Data Model
|
||||
|
||||
The same status/config model should be used across:
|
||||
|
||||
- UART
|
||||
- HTTP
|
||||
- Simulator
|
||||
- Future MQTT/Home Assistant integrations
|
||||
- Optional UART diagnostics
|
||||
|
||||
Reason:
|
||||
|
||||
@@ -177,3 +184,13 @@ Cloud dashboards and remote access are not current priorities.
|
||||
Reason:
|
||||
|
||||
The core controller/dashboard system should be reliable locally before remote features are considered.
|
||||
|
||||
## Decision: Future Pi Zero Is Optional
|
||||
|
||||
A Pi Zero may be added later for long-term logging, charts, dashboards, or local integrations.
|
||||
|
||||
It must not own relay control, BMS state, alarm authority, or persistent controller configuration.
|
||||
|
||||
Reason:
|
||||
|
||||
The Cargo ESP32 must continue operating as the controller and source of truth without any higher-level computer powered on.
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Pre-LilyGO Baseline Checklist
|
||||
|
||||
Use this checklist before creating a release/tag for the current Cargo ESP 2-channel baseline.
|
||||
|
||||
## Goal
|
||||
|
||||
Capture a known-good project state before migrating Cargo ESP hardware to a LilyGO T-Relay-S3 board or other expanded-output controller.
|
||||
|
||||
## Required Before Tagging
|
||||
|
||||
- [ ] Tests pass with `source .venv/bin/activate && python run_tests.py`
|
||||
- [ ] Working Cargo ESP WebUI confirmed
|
||||
- [ ] Relay/output control confirmed on current 2-channel board
|
||||
- [ ] DS18B20 temperature display confirmed in WebUI
|
||||
- [ ] Cargo ESP API contract documented
|
||||
- [ ] Controller roles documented
|
||||
- [ ] Pico dashboard archived
|
||||
- [ ] Waveshare ESP32-S3 Touch LCD 5B dashboard target documented
|
||||
- [ ] Load-switching hardware boundaries documented
|
||||
- [ ] No stale active Pico/UART dashboard references remain
|
||||
- [ ] Repo is clean: `git status --short`
|
||||
|
||||
## Suggested Tag Name
|
||||
|
||||
v0.1.0-cargo-esp32-2ch-baseline
|
||||
|
||||
## Suggested Release Title
|
||||
|
||||
Cargo ESP32 2-Channel Relay Board Baseline
|
||||
|
||||
## Suggested Release Notes
|
||||
|
||||
Baseline working release before future Cargo ESP hardware migration.
|
||||
|
||||
This release captures the current Cargo ESP32 controller architecture using the 2-channel relay board.
|
||||
|
||||
Includes:
|
||||
|
||||
- Cargo ESP WebUI/API architecture
|
||||
- DS18B20 temperature support
|
||||
- Relay/output control
|
||||
- HTTP API contract tests
|
||||
- Legacy Pico dashboard archived
|
||||
- ESP32-S3 Waveshare 5B dashboard direction documented
|
||||
- Controller roles clarified
|
||||
- Load-switching hardware boundaries documented
|
||||
|
||||
Known future work:
|
||||
|
||||
- LilyGO T-Relay-S3 hardware evaluation
|
||||
- Expanded relay/output mapping
|
||||
- OTA workflow
|
||||
- ESP32-S3 dashboard firmware
|
||||
- Dashboard screen mockups
|
||||
+199
-332
@@ -1,398 +1,265 @@
|
||||
# Project State
|
||||
|
||||
Last Updated: June 2026
|
||||
Last updated: 2026-06-07
|
||||
|
||||
## Project Name
|
||||
## Version
|
||||
|
||||
Overland Controller
|
||||
Current firmware version:
|
||||
|
||||
## Current Status
|
||||
0.5.0
|
||||
|
||||
Phase 1 is functional.
|
||||
## Current Architecture
|
||||
|
||||
The ESP32 controller can:
|
||||
The project is now an ESP32-based overland controller with a lightweight embedded WebUI.
|
||||
|
||||
- Host a local WiFi access point
|
||||
- Serve a web interface
|
||||
- Expose an HTTP API
|
||||
- Read JBD/Xiaoxiang BLE BMS data
|
||||
- Persist configuration
|
||||
- Factory reset configuration
|
||||
- Control relay outputs
|
||||
- Report relay status
|
||||
- Report BMS status
|
||||
- Report temperature sensor status
|
||||
- Communicate over UART
|
||||
- Support BLE setup mode for BMS discovery
|
||||
Core controller:
|
||||
|
||||
The Pico dashboard hardware has not yet been brought online.
|
||||
ESP32 relay board
|
||||
|
||||
---
|
||||
Current dashboard plan:
|
||||
|
||||
## Hardware Owned
|
||||
Waveshare ESP32-S3-Touch-LCD-5 over WiFi/HTTP
|
||||
|
||||
### ESP32 Controller
|
||||
Optional future expansion:
|
||||
|
||||
Owned and operational.
|
||||
Pi Zero for long-term logging, charts, and local integrations
|
||||
|
||||
Current hardware:
|
||||
## ESP32 Responsibilities
|
||||
|
||||
- ESP32 relay board
|
||||
- 2 relay outputs
|
||||
The ESP32 is the source of truth for:
|
||||
|
||||
### Dashboard
|
||||
- Relay control
|
||||
- JBD/Xiaoxiang BLE BMS telemetry
|
||||
- DS18B20 temperature sensors
|
||||
- Persistent configuration
|
||||
- HTTP API
|
||||
- Embedded WebUI
|
||||
- WiFi AP recovery network
|
||||
- WiFi STA client mode
|
||||
- Optional UART JSON protocol for diagnostics or alternate clients
|
||||
|
||||
Owned but not yet integrated.
|
||||
## Current Working Features
|
||||
|
||||
Current hardware:
|
||||
- BMS live data from Greta/JBD BMS
|
||||
- Relay control
|
||||
- Embedded phone-friendly WebUI
|
||||
- AP mode at `192.168.4.1`
|
||||
- STA mode on saved WiFi networks
|
||||
- Multiple saved WiFi networks with priority
|
||||
- WiFi reconnect/failover behavior
|
||||
- DS18B20 scan/assign/config workflow
|
||||
- One DS18B20 probe verified working
|
||||
- Generic relay IDs
|
||||
- Generic temperature IDs
|
||||
- Config-driven relay names
|
||||
- Config-driven temperature names
|
||||
- WebUI setup workflow
|
||||
- HTTP API
|
||||
- UART JSON protocol
|
||||
- USB serial maintenance commands
|
||||
|
||||
- Raspberry Pi Pico 2 W
|
||||
## WebUI Tabs
|
||||
|
||||
Potential display:
|
||||
### Overview
|
||||
|
||||
- 3.5" touchscreen expansion board
|
||||
Shows:
|
||||
|
||||
### Vehicle
|
||||
|
||||
Current target vehicle:
|
||||
|
||||
- Nissan Xterra
|
||||
|
||||
The firmware itself should remain vehicle-agnostic.
|
||||
|
||||
---
|
||||
|
||||
## BMS Status
|
||||
|
||||
Working.
|
||||
|
||||
Current battery:
|
||||
|
||||
- JBD / Xiaoxiang BLE BMS
|
||||
|
||||
Current telemetry:
|
||||
|
||||
- SOC
|
||||
- Battery SOC
|
||||
- Voltage
|
||||
- Current
|
||||
- Estimated runtime / charging time-to-full
|
||||
- Temperature sensors
|
||||
- Relay controls
|
||||
- Network status
|
||||
- Alarm summary
|
||||
|
||||
### Battery
|
||||
|
||||
Shows:
|
||||
|
||||
- BMS online/offline
|
||||
- Source
|
||||
- Remaining Ah
|
||||
- Capacity
|
||||
- Temperature
|
||||
- Capacity Ah
|
||||
- Battery temperature
|
||||
- Cycle count
|
||||
- Cell voltages
|
||||
- Cell count
|
||||
- Cell delta
|
||||
- Individual cell voltages
|
||||
- BMS enable/disable controls
|
||||
|
||||
Configuration is stored in persistent settings.
|
||||
### Config
|
||||
|
||||
---
|
||||
Supports:
|
||||
|
||||
## Relay Status
|
||||
|
||||
Working.
|
||||
|
||||
Current relay outputs:
|
||||
|
||||
relay_1
|
||||
relay_2
|
||||
|
||||
Current installation-specific names:
|
||||
|
||||
Starlink
|
||||
Fridge
|
||||
|
||||
These names are configuration only.
|
||||
|
||||
No relay names are intended to be hardcoded.
|
||||
|
||||
---
|
||||
|
||||
## Temperature Sensor Status
|
||||
|
||||
Partially complete.
|
||||
|
||||
Framework exists.
|
||||
|
||||
Current status:
|
||||
|
||||
- Configuration exists
|
||||
- Status reporting exists
|
||||
- Naming exists
|
||||
|
||||
Not yet completed:
|
||||
|
||||
- Physical DS18B20 integration
|
||||
- Sensor discovery workflow
|
||||
- Sensor assignment workflow
|
||||
|
||||
Planned support:
|
||||
|
||||
Up to 8 DS18B20 sensors.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Status
|
||||
|
||||
Persistent configuration works.
|
||||
|
||||
Stored settings include:
|
||||
|
||||
- Device name
|
||||
- Relay names
|
||||
- Relay enabled state
|
||||
- Temperature sensor names
|
||||
- Temperature sensor enabled state
|
||||
- BMS name
|
||||
- BMS address
|
||||
- BMS address type
|
||||
|
||||
Factory reset is functional.
|
||||
|
||||
---
|
||||
|
||||
## HTTP API Status
|
||||
|
||||
Working.
|
||||
|
||||
Implemented:
|
||||
|
||||
- Status endpoint
|
||||
- Config endpoint
|
||||
- Relay control
|
||||
- Configuration updates
|
||||
- Save configuration
|
||||
- Device name editing
|
||||
- BMS name/address/address type editing
|
||||
- BMS reconnect
|
||||
- Temperature probe scan
|
||||
- Temperature probe assignment
|
||||
- Relay renaming
|
||||
- Temperature sensor enabled count
|
||||
- Temperature sensor renaming
|
||||
- WiFi network configuration
|
||||
- Restart
|
||||
- Factory reset
|
||||
|
||||
Documentation:
|
||||
## Hardware Direction
|
||||
|
||||
See:
|
||||
Current rear enclosure target:
|
||||
|
||||
docs/api.md
|
||||
- ESP32 relay board
|
||||
- Compact fuse block
|
||||
- Ground bus
|
||||
- CAT6 keystone
|
||||
- DS18B20 terminal/distribution board
|
||||
- Built-in relay outputs for Starlink Mini and fridge
|
||||
|
||||
---
|
||||
Relay strategy:
|
||||
|
||||
## UART Status
|
||||
ESP32 relay board output
|
||||
-> external fused automotive relay or contactor trigger
|
||||
-> fused load circuit
|
||||
|
||||
Partially complete.
|
||||
The ESP32 relay board outputs are control/trigger outputs only. Fridge, Starlink, inverter, and other major loads must use properly fused automotive relay or contactor circuits.
|
||||
|
||||
Current UART capabilities:
|
||||
## Temperature Sensor Direction
|
||||
|
||||
- Status requests
|
||||
- Dashboard communications
|
||||
- Basic command support
|
||||
DS18B20 bus:
|
||||
|
||||
Missing goal:
|
||||
ESP32 GPIO4 = data
|
||||
ESP32 3.3V = VCC
|
||||
ESP32 GND = ground
|
||||
|
||||
UART should eventually expose every operation available through HTTP and serial.
|
||||
Required:
|
||||
|
||||
Target:
|
||||
One 4.7k pull-up resistor between data and 3.3V
|
||||
|
||||
UART becomes the primary dashboard integration interface.
|
||||
Final install should use a terminal/distribution board.
|
||||
|
||||
---
|
||||
|
||||
## Simulator Status
|
||||
|
||||
Working.
|
||||
|
||||
Simulator provides:
|
||||
|
||||
- Fake ESP32 controller
|
||||
- Fake Pico dashboard
|
||||
- Test data generation
|
||||
- Protocol validation
|
||||
|
||||
Useful for development before hardware is connected.
|
||||
|
||||
---
|
||||
|
||||
## Documentation Status
|
||||
|
||||
Primary documents:
|
||||
|
||||
README.md
|
||||
HARDWARE.md
|
||||
|
||||
docs/project-state.md
|
||||
docs/architecture.md
|
||||
docs/api.md
|
||||
docs/protocol.md
|
||||
docs/roadmap.md
|
||||
docs/decisions.md
|
||||
docs/SERIAL_COMMANDS.md
|
||||
|
||||
Older planning/design documents may be archived later.
|
||||
|
||||
---
|
||||
|
||||
## Highest Priority Next Tasks
|
||||
|
||||
### Priority 1
|
||||
|
||||
UART parity.
|
||||
|
||||
Goal:
|
||||
|
||||
Anything possible through serial or HTTP should also be possible through UART.
|
||||
|
||||
Required additions:
|
||||
|
||||
- config_request
|
||||
- config_device
|
||||
- config_relay
|
||||
- config_temp
|
||||
- config_bms
|
||||
- save_config
|
||||
- factory_reset
|
||||
- enter_bms_setup
|
||||
- scan_ble
|
||||
- select_bms
|
||||
- exit_bms_setup
|
||||
|
||||
### Priority 2
|
||||
|
||||
DS18B20 support.
|
||||
|
||||
Needed:
|
||||
|
||||
- Scan sensors
|
||||
- Assign sensors
|
||||
- Save assignments
|
||||
|
||||
### Priority 3
|
||||
|
||||
Pico hardware bring-up.
|
||||
|
||||
Needed:
|
||||
|
||||
- UART communication
|
||||
- Display status screen
|
||||
- Relay control screen
|
||||
|
||||
---
|
||||
|
||||
## Long-Term Vision
|
||||
|
||||
A vehicle-agnostic overland monitoring and control platform.
|
||||
|
||||
Core principles:
|
||||
|
||||
- Local-first
|
||||
- No cloud required
|
||||
- Generic hardware model
|
||||
- Generic API model
|
||||
- Generic UART model
|
||||
- Dashboard optional
|
||||
- Vehicle independent
|
||||
|
||||
Target use cases:
|
||||
|
||||
- Overland vehicles
|
||||
- Campers
|
||||
- Trailers
|
||||
- RV electrical systems
|
||||
- Mobile command systems
|
||||
- Off-grid battery monitoring
|
||||
|
||||
---
|
||||
|
||||
## Current Direction Update
|
||||
|
||||
The project direction is now:
|
||||
|
||||
ESP32:
|
||||
|
||||
Controller
|
||||
Source of truth
|
||||
BMS telemetry
|
||||
Temperature sensors
|
||||
Relay control
|
||||
Configuration storage
|
||||
HTTP API
|
||||
Lightweight embedded web dashboard
|
||||
WiFi AP recovery network
|
||||
|
||||
Pico 2 W:
|
||||
|
||||
Instant-on physical dashboard
|
||||
UART client
|
||||
Touchscreen UI
|
||||
Local control surface
|
||||
|
||||
Optional future Pi Zero 2 W:
|
||||
|
||||
Advanced web app
|
||||
Historical logging
|
||||
Graphs
|
||||
MQTT/Home Assistant bridge
|
||||
Advanced integrations
|
||||
|
||||
The base system should remain ESP32 + Pico.
|
||||
|
||||
A Pi Zero may be added later as an optional expansion module, not a required component.
|
||||
|
||||
---
|
||||
|
||||
## Networking Direction
|
||||
## Network Direction
|
||||
|
||||
Current:
|
||||
|
||||
ESP32 AP mode
|
||||
http://192.168.4.1
|
||||
- AP always available
|
||||
- STA attempts saved networks by priority
|
||||
- AP remains available even when STA is connected
|
||||
|
||||
Planned:
|
||||
Examples:
|
||||
|
||||
ESP32 AP + STA mode
|
||||
Priority 1: Starlink
|
||||
Priority 2: Home WiFi
|
||||
|
||||
Expected future behavior:
|
||||
## Next Recommended Work
|
||||
|
||||
AP always remains enabled
|
||||
ESP32 attempts to join configured WiFi networks
|
||||
Multiple saved networks are tried in priority order
|
||||
1. Verify WebUI setup workflow after fresh flash.
|
||||
2. Clean up DS18B20 physical distribution hardware.
|
||||
3. Test 2-4 temperature probes after distribution board is installed.
|
||||
4. Start Waveshare ESP32-S3 dashboard MVP over WiFi/HTTP.
|
||||
5. Add config backup/restore.
|
||||
6. Improve BMS out-of-range behavior without changing NimBLE connect timeout.
|
||||
7. Define future Pi Zero logging/integration role without moving control authority off the Cargo ESP32.
|
||||
|
||||
Example network priority:
|
||||
Recent notes:
|
||||
|
||||
1. Starlink
|
||||
2. Home WiFi
|
||||
3. Shop WiFi
|
||||
- WebUI temperature scan, assign, clear, and weather/outside-air selection are implemented.
|
||||
- DS18B20 slots now require explicit assigned addresses before reporting values.
|
||||
- Relay controls and temperature configuration remain driven by generic IDs plus saved configuration names.
|
||||
|
||||
This allows:
|
||||
|
||||
- Direct connection when camping without Starlink
|
||||
- Phone access over Starlink at camp
|
||||
- Phone/laptop access over home WiFi in the driveway
|
||||
- Recovery access even when STA setup fails
|
||||
## Dashboard Architecture Update
|
||||
|
||||
---
|
||||
Primary dashboard architecture is now:
|
||||
|
||||
## Recent Design Decisions
|
||||
Cargo ESP32 Controller
|
||||
→ WiFi AP/STA
|
||||
→ HTTP API
|
||||
→ WebUI
|
||||
|
||||
The onboard ESP32 relays may be acceptable for low-current loads such as:
|
||||
Dashboard Client:
|
||||
→ Waveshare 5" ESP32-S3 Display
|
||||
→ Connects to cargo ESP32 over WiFi
|
||||
→ Consumes existing HTTP API
|
||||
→ Planned LVGL-based dashboard UI
|
||||
|
||||
Starlink Mini
|
||||
12V fridge
|
||||
small accessories
|
||||
There is no planned Pico dashboard path. Existing Pico/UART files are legacy artifacts unless explicitly revived later.
|
||||
|
||||
Reason:
|
||||
UART JSON remains optional for diagnostics or alternate clients. New dashboard work should target the Waveshare ESP32-S3 display and the HTTP API.
|
||||
|
||||
- Starlink Mini current draw is modest
|
||||
- Typical 12V fridge current is modest
|
||||
- Smaller enclosure is a priority
|
||||
|
||||
External automotive relays remain recommended for higher-current loads or future expansion.
|
||||
## Current communications and GPIO decision
|
||||
|
||||
The current enclosure concept includes:
|
||||
Active dashboard communication is WiFi/HTTP using `/api/v1`.
|
||||
|
||||
- ESP32 relay board
|
||||
- Small fuse block
|
||||
- Ground bus
|
||||
- CAT6 keystone
|
||||
- CAT5/CAT6 run to dash
|
||||
- 12V feed to dash
|
||||
- Buck converter in dash enclosure
|
||||
The old Pico/dashboard UART path is retired from active hardware use. USB Serial remains available for development, debug, and manual configuration from a computer.
|
||||
|
||||
The dash power plan is:
|
||||
Current Cargo ESP32 GPIO plan:
|
||||
|
||||
Rear fused 12V
|
||||
-> CAT5/CAT6 blue pair
|
||||
-> dash buck converter
|
||||
-> Pico VSYS
|
||||
- GPIO 16: Relay 1 trigger output
|
||||
- GPIO 17: Relay 2 trigger output
|
||||
- GPIO 4: DS18B20 OneWire temperature bus
|
||||
- GPIO 34: Ignition sense input
|
||||
- GPIO 21: SSD1306 OLED I2C SDA
|
||||
- GPIO 22: SSD1306 OLED I2C SCL
|
||||
- GPIO 25: OLED setup/status button
|
||||
|
||||
## Current Role Boundary
|
||||
|
||||
The authoritative role split is documented in `docs/controller-roles.md`.
|
||||
|
||||
Summary:
|
||||
|
||||
- Cargo ESP32: power-system controller, WebUI, REST API, output control, BMS, DS18B20, local config, optional OLED.
|
||||
- Dashboard ESP32-S3: touchscreen UI, gauges, future CAN/OBD-II, future tilt/roll/off-road visualization.
|
||||
- v1 communications: Dashboard connects to Cargo ESP AP over WiFi/HTTP REST.
|
||||
- Not v1 communications: ESP-NOW, MQTT, UART dashboard link, or CAN between controllers.
|
||||
- Load-switching hardware is still an implementation choice, not frozen architecture.
|
||||
|
||||
## Pico Dashboard Archive Cleanup
|
||||
|
||||
The old `pico-dashboard/` implementation has been moved to `archive/legacy-pico-dashboard/`.
|
||||
|
||||
It is retained as reference material only. It is not active firmware and is no longer part of the default validation path.
|
||||
|
||||
Current dashboard work should target the ESP32-S3 dashboard direction documented in `docs/dashboard-esp32s3.md` and the placeholder at `firmware/esp32-s3-dashboard/`.
|
||||
|
||||
## Selected Dashboard Hardware
|
||||
|
||||
The current dashboard target is the Waveshare ESP32-S3 Touch LCD 5B with a 5-inch 1024x600 touchscreen.
|
||||
|
||||
This replaces the earlier Pico dashboard concept. The Pico dashboard has been archived as reference-only material.
|
||||
|
||||
## Implemented DS18B20/WebUI Support
|
||||
|
||||
DS18B20 support is already implemented in the Cargo ESP/WebUI/API path.
|
||||
|
||||
This is not a future feature to add from scratch.
|
||||
|
||||
Future work should focus on:
|
||||
|
||||
- Keeping the API contract accurate
|
||||
- Displaying existing temperature telemetry on the ESP32-S3 dashboard
|
||||
- Preserving Cargo ESP ownership of cargo/fridge temperature sensors
|
||||
|
||||
## Cargo API Contract
|
||||
|
||||
The current Cargo ESP API ownership and expected endpoint areas are documented in `docs/cargo-api-contract.md`.
|
||||
|
||||
This contract should guide future ESP32-S3 dashboard work so dashboard firmware targets stable Cargo ESP data instead of duplicating sensor or relay ownership.
|
||||
|
||||
## Pre-LilyGO Baseline
|
||||
|
||||
Before migrating Cargo ESP hardware to a LilyGO T-Relay-S3 or other expanded-output controller, use `docs/pre-lilygo-baseline-checklist.md` to confirm the current 2-channel Cargo ESP baseline is stable enough to tag.
|
||||
|
||||
## LilyGO Ordered / Transition Plan
|
||||
|
||||
The LILYGO T-Relay-S3 6-way board has been ordered but is not yet the active hardware target.
|
||||
|
||||
Current testing continues on the original 2-channel relay board.
|
||||
|
||||
Near-term work should prepare the Cargo ESP API and configuration model so the future LilyGO migration is a hardware-profile change instead of an API redesign.
|
||||
|
||||
Primary goal:
|
||||
|
||||
- Keep 2-channel testing functional now.
|
||||
- Support future 6-output rendering/control cleanly through the same API shape.
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Repo Cleanup Audit
|
||||
|
||||
Last updated: 2026-06-09
|
||||
|
||||
## Scope
|
||||
|
||||
This audit checks active repository files for stale Pico/UART dashboard references after the Pico dashboard implementation was archived.
|
||||
|
||||
Excluded paths:
|
||||
|
||||
- `archive/`
|
||||
- `.git/`
|
||||
- `.venv/`
|
||||
- build/cache folders
|
||||
|
||||
## Result
|
||||
|
||||
The following active references still mention Pico/UART dashboard terms.
|
||||
|
||||
These are not automatically wrong. Some may be intentional history or handoff notes, but each should be reviewed before the next baseline release.
|
||||
|
||||
| File | Line | Term | Text |
|
||||
|---|---:|---|---|
|
||||
| `tools/pico_desktop_sim.py` | 5 | `pico-dashboard` | sys.path.insert(0, str(ROOT / "pico-dashboard")) |
|
||||
| `docs/project-state.md` | 185 | `Pico dashboard` | There is no planned Pico dashboard path. Existing Pico/UART files are legacy artifacts unless explicitly revived later. |
|
||||
| `docs/project-state.md` | 215 | `UART dashboard` | - Not v1 communications: ESP-NOW, MQTT, UART dashboard link, or CAN between controllers. |
|
||||
| `docs/project-state.md` | 218 | `Pico Dashboard` | ## Pico Dashboard Archive Cleanup |
|
||||
| `docs/project-state.md` | 220 | `pico-dashboard` | The old `pico-dashboard/` implementation has been moved to `archive/legacy-pico-dashboard/`. |
|
||||
| `docs/project-state.md` | 230 | `Pico dashboard` | This replaces the earlier Pico dashboard concept. The Pico dashboard has been archived as reference-only material. |
|
||||
| `docs/HARDWARE.md` | 202 | `Pico dashboard` | This is the active dashboard platform for the project. The previous Pico dashboard implementation has been archived and is reference-only. |
|
||||
| `docs/ARCHITECTURE.md` | 250 | `Pico dashboard` | This replaces the earlier Pico dashboard concept as the active dashboard architecture. |
|
||||
| `docs/CHATGPT_HANDOFF.md` | 157 | `UART dashboard` | - ESP-NOW, MQTT, UART dashboard link, and CAN-between-controllers are not v1 architecture. |
|
||||
| `docs/CHATGPT_HANDOFF.md` | 160 | `Pico Dashboard` | ## Legacy Pico Dashboard Archive |
|
||||
| `docs/CHATGPT_HANDOFF.md` | 162 | `pico-dashboard` | The old Pico dashboard implementation has been archived at `archive/legacy-pico-dashboard/`. |
|
||||
| `docs/dashboard-esp32s3.md` | 156 | `Pico dashboard` | No Pico dashboard is planned. Existing Pico/UART material is legacy unless explicitly revived for diagnostics or alternate hardware. |
|
||||
| `docs/README.md` | 39 | `pico-dashboard` | - `../archive/legacy-pico-dashboard/` - Archived Pico dashboard implementation kept for reference only. |
|
||||
| `docs/UART_PROTOCOL.md` | 7 | `Pico dashboard` | Keep this protocol for diagnostics, simulator work, service tools, or alternate clients. Do not treat it as evidence that a Pico dashboard is planned. |
|
||||
| `docs/UART_PROTOCOL.md` | 164 | `Pico dashboard` | - No Pico dashboard is currently planned. |
|
||||
| `firmware/esp32-s3-dashboard/README.md` | 44 | `Pico UART` | - Pico UART |
|
||||
| `firmware/esp32-s3-dashboard/README.md` | 64 | `Pico dashboard` | This is the current active dashboard target. The archived Pico dashboard is not active firmware. |
|
||||
| `firmware/esp32/overland-controller/config.h` | 21 | `UART dashboard` | // Legacy UART dashboard support is retired from active hardware use. |
|
||||
|
||||
## Current Dashboard Direction
|
||||
|
||||
Active dashboard work targets the Waveshare ESP32-S3 Touch LCD 5B with a 5-inch 1024x600 touchscreen.
|
||||
|
||||
The archived Pico dashboard is retained only for historical reference and should not be treated as active firmware.
|
||||
|
||||
## Existing DS18B20 Support
|
||||
|
||||
DS18B20 temperature sensor support is already implemented in the Cargo ESP/WebUI/API path.
|
||||
|
||||
Future work should focus on preserving and exposing that existing support cleanly through the API contract and dashboard client, not re-adding DS18B20 support from scratch.
|
||||
+118
-363
@@ -1,406 +1,161 @@
|
||||
# Roadmap
|
||||
|
||||
This roadmap tracks planned Overland Controller development.
|
||||
## Phase 1: Solid Local Cargo Controller
|
||||
|
||||
The roadmap is flexible and may change as the project evolves.
|
||||
- Reliable relay control
|
||||
- Reliable DS18B20 temps
|
||||
- Reliable JBD/Xiaoxiang BMS BLE
|
||||
- WPA2 AP mode
|
||||
- Stable WebUI
|
||||
- Stable HTTP API
|
||||
- Clear status/config/control model
|
||||
|
||||
## Phase 1: ESP32 Controller Foundation
|
||||
## Phase 2: Waveshare ESP32-S3 Dashboard
|
||||
|
||||
Status: mostly complete
|
||||
|
||||
Goals:
|
||||
|
||||
- Establish ESP32 firmware structure
|
||||
- Implement relay control
|
||||
- Implement status reporting
|
||||
- Implement local configuration
|
||||
- Implement BMS telemetry
|
||||
- Implement generic relay/sensor naming
|
||||
- Establish HTTP and UART communication models
|
||||
|
||||
Completed:
|
||||
|
||||
- Relay control
|
||||
- JSON status API
|
||||
- Configuration persistence
|
||||
- Factory reset
|
||||
- BMS BLE telemetry
|
||||
- Cell voltage telemetry
|
||||
- Generic relay IDs
|
||||
- Generic temperature sensor IDs
|
||||
- HTTP API
|
||||
- Serial console commands
|
||||
- Documentation cleanup
|
||||
|
||||
Remaining:
|
||||
|
||||
- Finish UART parity
|
||||
- Improve BLE setup reliability
|
||||
- Add stronger config validation
|
||||
- Add better command help output
|
||||
|
||||
## Phase 2: UART Parity
|
||||
|
||||
Status: next priority
|
||||
|
||||
Goal:
|
||||
|
||||
The Pico dashboard should be able to configure and control the ESP32 over UART without relying on WiFi.
|
||||
|
||||
Minimum UART v1 messages:
|
||||
|
||||
- status_request
|
||||
- set_relay
|
||||
- config_request
|
||||
- config_device
|
||||
- config_relay
|
||||
- config_temp
|
||||
- config_bms
|
||||
- save_config
|
||||
- factory_reset
|
||||
- enter_bms_setup
|
||||
- scan_ble
|
||||
- select_bms
|
||||
- exit_bms_setup
|
||||
|
||||
Expected result:
|
||||
|
||||
The Pico can:
|
||||
|
||||
- Read full system status
|
||||
- Display current configuration
|
||||
- Control relays
|
||||
- Rename relays
|
||||
- Rename temperature sensors
|
||||
- Configure BMS
|
||||
- Run BMS setup workflow
|
||||
- Save configuration
|
||||
|
||||
## Phase 3: Temperature Sensors
|
||||
|
||||
Status: partially started
|
||||
|
||||
Goals:
|
||||
|
||||
- Complete DS18B20 hardware wiring
|
||||
- Add proper 4.7k pull-up resistor
|
||||
- Detect connected sensors
|
||||
- Report sensor addresses
|
||||
- Assign sensors to config slots
|
||||
- Persist sensor address assignments
|
||||
|
||||
Planned serial commands:
|
||||
|
||||
- scan temps
|
||||
- assign temp 1 <address>
|
||||
- clear temp 1
|
||||
|
||||
Planned UART messages:
|
||||
|
||||
- scan_temps
|
||||
- assign_temp
|
||||
- clear_temp
|
||||
|
||||
Expected result:
|
||||
|
||||
A user can connect multiple DS18B20 sensors, identify each one, name it, and save the configuration.
|
||||
|
||||
## Phase 4: Pico Dashboard Bring-Up
|
||||
|
||||
Status: not started on hardware
|
||||
|
||||
Goals:
|
||||
|
||||
- Bring up Pico 2 W
|
||||
- Bring up touchscreen
|
||||
- Establish UART connection to ESP32
|
||||
- Display status response
|
||||
- Display battery data
|
||||
- Display relay states
|
||||
- Display temperature data
|
||||
- LVGL project scaffold
|
||||
- WiFi client to Cargo ESP32 AP
|
||||
- Poll /status
|
||||
- Display battery, relays, temps, and alarms
|
||||
- Send relay commands
|
||||
- Handle offline/stale/failed-command states
|
||||
|
||||
Initial dashboard screens:
|
||||
UI direction:
|
||||
|
||||
- Main overview
|
||||
- Battery screen
|
||||
- Temperature screen
|
||||
- Relay control screen
|
||||
- Settings screen
|
||||
- Dark theme
|
||||
- Large readable text
|
||||
- Circular SOC gauge
|
||||
- Card-based layout
|
||||
- Smooth subtle animations
|
||||
- No stock LVGL demo look
|
||||
|
||||
## Phase 5: Pico Configuration UI
|
||||
## Phase 3: Vehicle Data
|
||||
|
||||
Status: planned
|
||||
- Connect Waveshare CAN to OBD-II CAN-H/CAN-L
|
||||
- Keep CAN termination disabled
|
||||
- Read coolant temp via PID 0105
|
||||
- Read RPM via PID 010C
|
||||
- Read vehicle speed via PID 010D
|
||||
|
||||
Goals:
|
||||
## Phase 4: Transmission Temperature
|
||||
|
||||
- Configure device name
|
||||
- Configure relay names
|
||||
- Configure temperature names
|
||||
- Configure BMS
|
||||
- Run BLE scan workflow
|
||||
- Save config
|
||||
- Factory reset
|
||||
- Install dedicated sender
|
||||
- Read through exposed ADC or ADS1115 over I2C
|
||||
- Convert resistance to temp with lookup interpolation
|
||||
- Add alert thresholds
|
||||
|
||||
Expected result:
|
||||
## Phase 5: Future Home Server
|
||||
|
||||
A new installation can be configured from the dashboard without using a laptop.
|
||||
- Cargo ESP32 outbound connection
|
||||
- Remote read-only dashboard first
|
||||
- Optional commands later
|
||||
- HTTPS reverse proxy/PWA
|
||||
- Possible MQTT/WebSocket bridge
|
||||
- Optional Grafana/Home Assistant/InflDB
|
||||
|
||||
## Phase 6: Vehicle Inputs
|
||||
## Phase 6: Optional Enhancements
|
||||
|
||||
Status: planned
|
||||
|
||||
Possible inputs:
|
||||
|
||||
- Ignition signal
|
||||
- Vehicle battery voltage
|
||||
- OBD-II
|
||||
- CAN bus
|
||||
- microSD logging
|
||||
- GPS
|
||||
- OTA updates
|
||||
- Dashboard alert sounds
|
||||
- More vehicle PIDs
|
||||
- Historical charts
|
||||
|
||||
Potential data:
|
||||
|
||||
- Ignition state
|
||||
- Engine RPM
|
||||
- Coolant temperature
|
||||
- Vehicle speed
|
||||
- Fuel level
|
||||
- Diagnostic trouble codes
|
||||
- Location
|
||||
- Trip data
|
||||
## AP Security, OLED Setup, and Dashboard Pairing
|
||||
|
||||
## Phase 7: Logging and History
|
||||
### Phase 1 — Cargo AP security
|
||||
|
||||
Status: planned
|
||||
- Require WPA2 on the Cargo ESP32 AP.
|
||||
- Generate a unique AP password on first boot.
|
||||
- Store AP SSID/password in Preferences.
|
||||
- Add /api/v1/config/ap for AP configuration.
|
||||
- Add AP auth/SSID metadata to status responses.
|
||||
- Do not expose AP password in API responses.
|
||||
|
||||
Goals:
|
||||
### Phase 2 — Cargo OLED setup display
|
||||
|
||||
- Log battery history
|
||||
- Log temperature history
|
||||
- Log relay events
|
||||
- Log alarm events
|
||||
- Show historical charts
|
||||
- Add 128x64 SSD1306 OLED support on the Cargo ESP32.
|
||||
- Show AP SSID/password/IP during first boot and setup mode.
|
||||
- Show critical operational state during normal operation:
|
||||
- SOC
|
||||
- voltage/current
|
||||
- relay state
|
||||
- WiFi/IP
|
||||
- alarms
|
||||
- Add setup/status button:
|
||||
- short press cycles pages
|
||||
- long press shows AP credentials
|
||||
- extended hold enters recovery/reset mode
|
||||
|
||||
Possible storage:
|
||||
### Phase 3 — Dashboard credential migration
|
||||
|
||||
- ESP32 flash
|
||||
- SD card
|
||||
- External service
|
||||
- Future InfluxDB integration
|
||||
- Dashboard ESP32-S3 connects as a client to the Cargo ESP AP.
|
||||
- Cargo ESP remains the network/configuration authority.
|
||||
- When AP credentials are changed from the phone WebUI, Cargo stages the new credentials.
|
||||
- Dashboard fetches and saves pending credentials before Cargo restarts AP.
|
||||
- Cargo applies the new AP settings.
|
||||
- Dashboard reconnects automatically.
|
||||
- Temporary dashboard setup AP is fallback/recovery only.
|
||||
|
||||
## Phase 8: Integrations
|
||||
### Phase 4 — User-friendly setup polish
|
||||
|
||||
Status: planned
|
||||
- Add QR code display for joining Cargo AP.
|
||||
- Add dashboard settings UI.
|
||||
- Add LVGL on-screen keyboard only after core dashboard functionality is stable.
|
||||
|
||||
Potential integrations:
|
||||
|
||||
- MQTT
|
||||
- Home Assistant
|
||||
- Grafana
|
||||
- InfluxDB
|
||||
- Node-RED
|
||||
## Cargo OLED firmware foundation
|
||||
|
||||
Design goal:
|
||||
Status: planned / scaffolded
|
||||
|
||||
Integrations should consume the same generic status/config model used by HTTP and UART.
|
||||
Initial OLED firmware goals:
|
||||
|
||||
## Phase 9: Enclosure and Installation
|
||||
- Add optional SSD1306 128x64 support behind a compile-time flag.
|
||||
- Keep OLED support disabled by default so the controller firmware builds without extra libraries.
|
||||
- Display boot/setup status.
|
||||
- Display AP SSID/password/IP for phone-first setup.
|
||||
- Display rotating service pages:
|
||||
- battery
|
||||
- relays
|
||||
- network
|
||||
- alarms
|
||||
|
||||
Status: planned
|
||||
Future OLED goals:
|
||||
|
||||
Goals:
|
||||
- Add setup/status button.
|
||||
- Short press cycles pages.
|
||||
- Long press shows AP credentials.
|
||||
- Extended hold enters recovery/reset mode.
|
||||
- Add first-boot setup screen once AP password generation is finalized.
|
||||
|
||||
- Design ESP32 enclosure
|
||||
- Design Pico dashboard enclosure
|
||||
- Design wiring harness
|
||||
- Document installation
|
||||
- Create BOM
|
||||
- Create printable STL files
|
||||
- Validate thermal and vibration behavior
|
||||
|
||||
## Phase 10: Public Release
|
||||
## Current communications and GPIO decision
|
||||
|
||||
Status: future
|
||||
Active dashboard communication is WiFi/HTTP using `/api/v1`.
|
||||
|
||||
Goals:
|
||||
The old Pico/dashboard UART path is retired from active hardware use. USB Serial remains available for development, debug, and manual configuration from a computer.
|
||||
|
||||
- Clean documentation
|
||||
- Stable API
|
||||
- Stable UART protocol
|
||||
- Installation guide
|
||||
- Wiring guide
|
||||
- Build guide
|
||||
- Example configurations
|
||||
- Versioned releases
|
||||
Current Cargo ESP32 GPIO plan:
|
||||
|
||||
Potential release name:
|
||||
- GPIO 16: Relay 1 trigger output
|
||||
- GPIO 17: Relay 2 trigger output
|
||||
- GPIO 4: DS18B20 OneWire temperature bus
|
||||
- GPIO 34: Ignition sense input
|
||||
- GPIO 21: SSD1306 OLED I2C SDA
|
||||
- GPIO 22: SSD1306 OLED I2C SCL
|
||||
- GPIO 25: OLED setup/status button
|
||||
|
||||
Overland Controller v1.0
|
||||
## Future Wishlist Parking Lot
|
||||
|
||||
## Long-Term Ideas
|
||||
These are intentionally not committed v1 architecture until explicitly promoted:
|
||||
|
||||
Potential future features:
|
||||
|
||||
- OTA firmware updates
|
||||
- Multi-controller support
|
||||
- Expansion modules
|
||||
- Water tank monitoring
|
||||
- Inverter monitoring
|
||||
- Solar charge controller monitoring
|
||||
- Starlink diagnostics
|
||||
- Bluetooth sensor support
|
||||
- Mobile web dashboard
|
||||
- Plugin-style integrations
|
||||
|
||||
---
|
||||
|
||||
## Networking Roadmap
|
||||
|
||||
### Current
|
||||
|
||||
The ESP32 currently operates as a local access point.
|
||||
|
||||
Default local access:
|
||||
|
||||
http://192.168.4.1
|
||||
|
||||
This provides a reliable recovery/setup path even when no other WiFi network is available.
|
||||
|
||||
### Planned: AP + STA Mode
|
||||
|
||||
The ESP32 should support AP and STA mode at the same time.
|
||||
|
||||
Access Point mode:
|
||||
|
||||
Overland-Controller
|
||||
192.168.4.1
|
||||
|
||||
Station mode:
|
||||
|
||||
Connects to a configured WiFi network such as Starlink, home WiFi, or shop WiFi.
|
||||
|
||||
The AP should remain enabled even when STA mode is connected.
|
||||
|
||||
Reason:
|
||||
|
||||
- Always available recovery path
|
||||
- Easier setup
|
||||
- Less risk of locking yourself out
|
||||
- Useful when Starlink/home WiFi is unavailable
|
||||
|
||||
### Planned: Multiple Saved WiFi Networks
|
||||
|
||||
The controller should eventually support multiple saved WiFi profiles.
|
||||
|
||||
Example priority order:
|
||||
|
||||
1. Starlink
|
||||
2. Home WiFi
|
||||
3. Shop WiFi
|
||||
|
||||
Expected boot behavior:
|
||||
|
||||
Start AP
|
||||
Try Starlink
|
||||
If Starlink fails, try Home WiFi
|
||||
If Home WiFi fails, try Shop WiFi
|
||||
If all fail, remain AP-only
|
||||
|
||||
The ESP32 only connects to one STA network at a time, but it can remember and try multiple networks.
|
||||
|
||||
### Planned WiFi Configuration
|
||||
|
||||
Future configuration should include:
|
||||
|
||||
ap_enabled
|
||||
sta_enabled
|
||||
hostname
|
||||
saved networks
|
||||
priority order
|
||||
|
||||
Future setup options:
|
||||
|
||||
- Serial console
|
||||
- HTTP API
|
||||
- Pico dashboard UI
|
||||
- Embedded web dashboard
|
||||
|
||||
### Planned mDNS
|
||||
|
||||
The controller should eventually support mDNS.
|
||||
|
||||
Example:
|
||||
|
||||
http://overland-controller.local
|
||||
|
||||
This would avoid needing to look up the controller IP when connected through Starlink or home WiFi.
|
||||
|
||||
---
|
||||
|
||||
## Embedded Web Dashboard
|
||||
|
||||
### Current Direction
|
||||
|
||||
The ESP32 should serve a lightweight mobile-friendly dashboard.
|
||||
|
||||
Purpose:
|
||||
|
||||
- Check battery status from a phone
|
||||
- Check fridge/temp status from camp
|
||||
- Toggle relays if needed
|
||||
- View alarms
|
||||
- Provide fallback UI without the Pico display
|
||||
|
||||
Design limits:
|
||||
|
||||
- No React
|
||||
- No external libraries
|
||||
- No images
|
||||
- Small inline HTML/CSS/JS
|
||||
- Poll /status every few seconds
|
||||
- Use existing relay endpoints
|
||||
|
||||
This dashboard is not intended to replace the physical Pico dashboard.
|
||||
|
||||
---
|
||||
|
||||
## Optional Future Pi Zero Module
|
||||
|
||||
The base system should not require a Pi Zero.
|
||||
|
||||
Base system:
|
||||
|
||||
ESP32 controller
|
||||
Pico 2 W dashboard
|
||||
|
||||
Optional future advanced module:
|
||||
|
||||
Pi Zero 2 W
|
||||
|
||||
Possible Pi Zero functions:
|
||||
|
||||
- Rich web dashboard
|
||||
- Historical logging
|
||||
- Graphs
|
||||
- SQLite database
|
||||
- MQTT bridge
|
||||
- Home Assistant bridge
|
||||
- Advanced configuration UI
|
||||
|
||||
The core controller should remain functional without the Pi Zero.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Backup and Restore
|
||||
|
||||
Planned future feature.
|
||||
|
||||
Goals:
|
||||
|
||||
- Export configuration JSON
|
||||
- Import configuration JSON
|
||||
- Backup install-specific setup
|
||||
- Restore after factory reset
|
||||
- Make support easier for future users
|
||||
|
||||
This is important if the project becomes shareable or sellable.
|
||||
- BNO085/BNO086 tilt sensor
|
||||
- CAN sniffing beyond standard OBD-II polling
|
||||
- Fault-code support
|
||||
- Standalone dashboard mode
|
||||
- Additional cargo outputs
|
||||
- Custom PCB
|
||||
- Smart high-side switching
|
||||
- WUPP-style fused distribution module
|
||||
- MQTT/Home Assistant/Grafana/InfluxDB integrations
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# ESP32-S3 Dashboard Firmware
|
||||
|
||||
This is the placeholder for the current dashboard firmware direction.
|
||||
|
||||
## Target hardware
|
||||
|
||||
Current dashboard target:
|
||||
|
||||
- Waveshare ESP32-S3 touchscreen display
|
||||
- WiFi connection to the Cargo ESP32 access point
|
||||
- HTTP/REST communication with the Cargo ESP32 API
|
||||
- Future LVGL-based dashboard UI
|
||||
|
||||
## Responsibilities
|
||||
|
||||
The ESP32-S3 dashboard is responsible for:
|
||||
|
||||
- Touchscreen UI
|
||||
- Vehicle gauges
|
||||
- Dashboard rendering
|
||||
- Future CAN/OBD-II vehicle visualization
|
||||
- Future tilt/roll/off-road screen visualization
|
||||
|
||||
## Not responsible for
|
||||
|
||||
The ESP32-S3 dashboard does not own:
|
||||
|
||||
- Relay authority
|
||||
- BMS authority
|
||||
- Alarm authority
|
||||
- Cargo ESP configuration storage
|
||||
- Load-switching hardware control as the source of truth
|
||||
|
||||
The Cargo ESP32 remains the power-system controller and source of truth.
|
||||
|
||||
## v1 communication path
|
||||
|
||||
Dashboard ESP32-S3
|
||||
<-> WiFi / HTTP REST
|
||||
Cargo ESP32 AP
|
||||
|
||||
Not v1 dashboard communication:
|
||||
|
||||
- Pico UART
|
||||
- ESP-NOW
|
||||
- MQTT
|
||||
- CAN between controllers
|
||||
|
||||
## Selected Hardware
|
||||
|
||||
The selected dashboard hardware target is:
|
||||
|
||||
Waveshare ESP32-S3 Touch LCD 5B
|
||||
|
||||
Display details:
|
||||
|
||||
- 5-inch touchscreen
|
||||
- 1024x600 resolution
|
||||
- Capacitive touch
|
||||
- ESP32-S3 platform
|
||||
- Native WiFi
|
||||
- Integrated CAN transceiver available for future vehicle integration
|
||||
|
||||
This is the current active dashboard target. The archived Pico dashboard is not active firmware.
|
||||
@@ -31,7 +31,8 @@ void loadDefaultConfig() {
|
||||
"temp_" + String(i + 1),
|
||||
"Temperature " + String(i + 1),
|
||||
"",
|
||||
i < appConfig.tempSensorCount
|
||||
i < appConfig.tempSensorCount,
|
||||
false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -90,6 +91,10 @@ void loadConfig() {
|
||||
(prefix + "enabled").c_str(),
|
||||
appConfig.tempSensors[i].enabled
|
||||
);
|
||||
appConfig.tempSensors[i].weather = preferences.getBool(
|
||||
(prefix + "weather").c_str(),
|
||||
appConfig.tempSensors[i].weather
|
||||
);
|
||||
}
|
||||
|
||||
appConfig.bms.enabled = preferences.getBool("bms_enabled", appConfig.bms.enabled);
|
||||
@@ -124,6 +129,7 @@ void saveConfig() {
|
||||
preferences.putString((prefix + "name").c_str(), appConfig.tempSensors[i].name);
|
||||
preferences.putString((prefix + "addr").c_str(), appConfig.tempSensors[i].address);
|
||||
preferences.putBool((prefix + "enabled").c_str(), appConfig.tempSensors[i].enabled);
|
||||
preferences.putBool((prefix + "weather").c_str(), appConfig.tempSensors[i].weather);
|
||||
}
|
||||
|
||||
preferences.putBool("bms_enabled", appConfig.bms.enabled);
|
||||
|
||||
@@ -17,6 +17,7 @@ struct TempSensorConfig {
|
||||
String name;
|
||||
String address;
|
||||
bool enabled;
|
||||
bool weather;
|
||||
};
|
||||
|
||||
struct BmsConfig {
|
||||
|
||||
@@ -12,9 +12,14 @@
|
||||
// Ignition Sense
|
||||
#define IGNITION_PIN 34
|
||||
|
||||
// UART over CAT5 to Pico dashboard
|
||||
#define DASHBOARD_UART_TX_PIN 21
|
||||
#define DASHBOARD_UART_RX_PIN 22
|
||||
// Cargo OLED service/setup display
|
||||
#define OLED_I2C_SDA_PIN 21
|
||||
#define OLED_I2C_SCL_PIN 22
|
||||
#define OLED_SETUP_BUTTON_PIN 25
|
||||
|
||||
// Dashboard communication is WiFi/HTTP.
|
||||
// Legacy UART dashboard support is retired from active hardware use.
|
||||
#define DASHBOARD_UART_ENABLED 0
|
||||
#define DASHBOARD_UART_BAUD 115200
|
||||
|
||||
// RS-485/MAX3485 is fallback only and not currently planned.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,9 @@ DeviceAddress sensorAddresses[MAX_TEMP_SENSORS];
|
||||
String sensorAddressStrings[MAX_TEMP_SENSORS];
|
||||
int sensorCount = 0;
|
||||
|
||||
int tempScanResultIndexes[MAX_TEMP_SENSORS];
|
||||
int tempScanResultCount = 0;
|
||||
|
||||
float cToF(float c) {
|
||||
return (c * 9.0 / 5.0) + 32.0;
|
||||
}
|
||||
@@ -52,6 +55,26 @@ bool addressMatches(DeviceAddress address, const String& expected) {
|
||||
return actual == normalizedExpected;
|
||||
}
|
||||
|
||||
bool isTempAddressAssigned(const String& address) {
|
||||
if (address.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String normalizedAddress = address;
|
||||
normalizedAddress.toUpperCase();
|
||||
|
||||
for (int i = 0; i < MAX_TEMP_SENSORS; i++) {
|
||||
String assigned = appConfig.tempSensors[i].address;
|
||||
assigned.toUpperCase();
|
||||
|
||||
if (assigned == normalizedAddress) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void clearSensorRuntimeData() {
|
||||
for (int i = 0; i < MAX_TEMP_SENSORS; i++) {
|
||||
sensors.tempsF[i] = -127.0;
|
||||
@@ -68,29 +91,48 @@ int scanTempSensors() {
|
||||
sensorCount = MAX_TEMP_SENSORS;
|
||||
}
|
||||
|
||||
tempScanResultCount = 0;
|
||||
|
||||
for (int i = 0; i < MAX_TEMP_SENSORS; i++) {
|
||||
sensorAddressStrings[i] = "";
|
||||
tempScanResultIndexes[i] = -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sensorCount; i++) {
|
||||
if (ds18b20.getAddress(sensorAddresses[i], i)) {
|
||||
sensorAddressStrings[i] = addressToString(sensorAddresses[i]);
|
||||
if (!ds18b20.getAddress(sensorAddresses[i], i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String addressString = addressToString(sensorAddresses[i]);
|
||||
sensorAddressStrings[i] = addressString;
|
||||
|
||||
if (isTempAddressAssigned(addressString)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tempScanResultIndexes[tempScanResultCount] = i;
|
||||
tempScanResultCount++;
|
||||
}
|
||||
|
||||
return sensorCount;
|
||||
return tempScanResultCount;
|
||||
}
|
||||
|
||||
int getTempScanResultCount() {
|
||||
return sensorCount;
|
||||
return tempScanResultCount;
|
||||
}
|
||||
|
||||
String getTempScanResultAddress(int index) {
|
||||
if (index < 0 || index >= sensorCount) {
|
||||
if (index < 0 || index >= tempScanResultCount) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return sensorAddressStrings[index];
|
||||
int physicalIndex = tempScanResultIndexes[index];
|
||||
|
||||
if (physicalIndex < 0 || physicalIndex >= sensorCount) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return sensorAddressStrings[physicalIndex];
|
||||
}
|
||||
|
||||
void printSensorAddresses() {
|
||||
@@ -128,17 +170,8 @@ void updateSensors() {
|
||||
String configuredAddress = appConfig.tempSensors[configIndex].address;
|
||||
|
||||
if (configuredAddress.length() == 0) {
|
||||
// Backward-compatible behavior:
|
||||
// if no address is assigned, use physical scan order.
|
||||
if (configIndex < sensorCount) {
|
||||
float tempC = ds18b20.getTempC(sensorAddresses[configIndex]);
|
||||
|
||||
if (validTempC(tempC)) {
|
||||
sensors.tempsF[configIndex] = cToF(tempC);
|
||||
sensors.online[configIndex] = true;
|
||||
}
|
||||
}
|
||||
|
||||
sensors.tempsF[configIndex] = -127.0;
|
||||
sensors.online[configIndex] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -164,11 +197,32 @@ bool assignTempSensorByIndex(int configIndex, int scanIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (scanIndex < 0 || scanIndex >= sensorCount) {
|
||||
if (scanIndex < 0 || scanIndex >= tempScanResultCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
appConfig.tempSensors[configIndex].address = sensorAddressStrings[scanIndex];
|
||||
int physicalIndex = tempScanResultIndexes[scanIndex];
|
||||
|
||||
if (physicalIndex < 0 || physicalIndex >= sensorCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String selectedAddress = sensorAddressStrings[physicalIndex];
|
||||
|
||||
for (int i = 0; i < MAX_TEMP_SENSORS; i++) {
|
||||
if (i == configIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (appConfig.tempSensors[i].address == selectedAddress) {
|
||||
appConfig.tempSensors[i].address = "";
|
||||
appConfig.tempSensors[i].enabled = false;
|
||||
sensors.tempsF[i] = -127.0;
|
||||
sensors.online[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
appConfig.tempSensors[configIndex].address = selectedAddress;
|
||||
appConfig.tempSensors[configIndex].enabled = true;
|
||||
|
||||
return true;
|
||||
@@ -181,6 +235,9 @@ bool clearTempSensor(int configIndex) {
|
||||
|
||||
appConfig.tempSensors[configIndex].address = "";
|
||||
appConfig.tempSensors[configIndex].enabled = false;
|
||||
appConfig.tempSensors[configIndex].weather = false;
|
||||
sensors.tempsF[configIndex] = -127.0;
|
||||
sensors.online[configIndex] = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define FIRMWARE_VERSION "0.3.0"
|
||||
#define FIRMWARE_VERSION "0.5.0"
|
||||
#define FIRMWARE_NAME "overland-controller"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Hardware Archive
|
||||
|
||||
These pinout notes are legacy references from earlier dashboard and wiring plans.
|
||||
|
||||
The current hardware source of truth is `docs/HARDWARE.md`, with the current dashboard plan in `docs/dashboard-esp32s3.md`.
|
||||
@@ -1,5 +1,7 @@
|
||||
# CAT5 Pinout
|
||||
|
||||
Legacy note: this file describes an older Pico/UART dashboard wiring plan. Do not use it for the current Waveshare ESP32-S3 dashboard build.
|
||||
|
||||
## Primary Configuration
|
||||
|
||||
UART over CAT5
|
||||
@@ -1,5 +1,7 @@
|
||||
# ESP32 Pinout (Planned)
|
||||
|
||||
Legacy note: Pico/UART references in this file are not part of the current dashboard plan. Current dashboard target is Waveshare ESP32-S3 over WiFi/HTTP.
|
||||
|
||||
GPIO 16 Relay 1 (Starlink)
|
||||
GPIO 17 Relay 2 (Fridge)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Pico Pinout (Planned)
|
||||
|
||||
Legacy note: no Pico dashboard is currently planned. This file is retained only as historical/reference material.
|
||||
|
||||
## Display
|
||||
|
||||
3.5 inch SPI capacitive touchscreen
|
||||
@@ -1,5 +1,7 @@
|
||||
# UART CAT5 Pinout
|
||||
|
||||
Legacy note: this file describes an older Pico/UART dashboard wiring plan. Do not use it for the current Waveshare ESP32-S3 dashboard build.
|
||||
|
||||
## Pair 1
|
||||
|
||||
Blue +5V
|
||||
+3
-6
@@ -2,15 +2,13 @@
|
||||
|
||||
## Ordered
|
||||
|
||||
- Raspberry Pi Pico 2 W
|
||||
- ESP32 2-Channel Relay Board
|
||||
- 3.5" SPI Capacitive Touchscreen
|
||||
|
||||
## Required
|
||||
|
||||
- 4x DS18B20 Waterproof Sensors
|
||||
- 12V→5V Buck Converter
|
||||
- CAT5 Cable
|
||||
- Waveshare ESP32-S3-Touch-LCD-5 dashboard
|
||||
- Fused ACC switched 12V dashboard power
|
||||
- Bosch Relay Sockets
|
||||
- 30A/40A Bosch Relays
|
||||
- Automotive Fuse Block
|
||||
@@ -18,8 +16,7 @@
|
||||
|
||||
## Future / Optional Fallback
|
||||
|
||||
- 2x MAX3485 RS-485 Modules, only if UART over CAT5 proves unreliable
|
||||
- Pi Zero 2 W
|
||||
- Pi Zero for long-term logging and local integrations
|
||||
- GPS Module
|
||||
- ELM327 OBD-II Interface
|
||||
- CAN Interface
|
||||
|
||||
+1
-20
@@ -3,26 +3,7 @@ import sys
|
||||
|
||||
|
||||
COMMANDS = [
|
||||
[sys.executable, "-m", "py_compile",
|
||||
"pico-dashboard/main.py",
|
||||
"pico-dashboard/app.py",
|
||||
"pico-dashboard/comms/protocol.py",
|
||||
"pico-dashboard/comms/uart_client.py",
|
||||
"pico-dashboard/comms/http_client.py",
|
||||
"pico-dashboard/comms/communication_service.py",
|
||||
"pico-dashboard/state/app_state.py",
|
||||
"pico-dashboard/alarms/alarm_definitions.py",
|
||||
"pico-dashboard/alarms/alarm_manager.py",
|
||||
"pico-dashboard/ui/screen_manager.py",
|
||||
"pico-dashboard/ui/dashboard_view_model.py",
|
||||
"pico-dashboard/ui/detail_view_models.py",
|
||||
"pico-dashboard/ui/alarm_view_model.py",
|
||||
"pico-dashboard/ui/touch_router.py",
|
||||
"pico-dashboard/ui/renderers.py",
|
||||
"pico-dashboard/hardware/display.py",
|
||||
"pico-dashboard/hardware/touch.py",
|
||||
"pico-dashboard/hardware/buzzer.py"],
|
||||
[sys.executable, "-m", "pytest", "tests/test_pico_core.py"],
|
||||
[sys.executable, "-m", "pytest", "tests/test_http_api_contract.py"],
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
flask
|
||||
pytest
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Xterra Dashboard Simulator</title>
|
||||
<title>Overland Controller Simulator</title>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<main>
|
||||
<header>
|
||||
<h1>Xterra Dashboard</h1>
|
||||
<h1>Overland Controller</h1>
|
||||
<div id="statusLine">UART: -- | WiFi: --</div>
|
||||
</header>
|
||||
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"device_name": "Cargo Controller",
|
||||
"relays": [
|
||||
{
|
||||
"id": "relay_1",
|
||||
"name": "Aux Power",
|
||||
"pin": 16,
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "relay_2",
|
||||
"name": "Fridge",
|
||||
"pin": 17,
|
||||
"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": "28ff001122334455",
|
||||
"enabled": true,
|
||||
"weather": false
|
||||
}
|
||||
]
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"ok": false,
|
||||
"error": "invalid_json"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"id": "relay_1",
|
||||
"state": true
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"type": "relay_response",
|
||||
"ok": true,
|
||||
"id": "relay_1",
|
||||
"state": true
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"type": "status_response",
|
||||
"timestamp": 123456,
|
||||
"battery": {
|
||||
"source": "jbd_bms",
|
||||
"connected": true,
|
||||
"soc": 70,
|
||||
"voltage": 13.34,
|
||||
"current": -5.2,
|
||||
"remaining_ah": 104.8,
|
||||
"capacity_ah": 150.0,
|
||||
"runtime_hours": 20.15,
|
||||
"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
|
||||
},
|
||||
"temps": [
|
||||
{
|
||||
"id": "temp_1",
|
||||
"name": "Cabin",
|
||||
"enabled": true,
|
||||
"weather": false,
|
||||
"online": true,
|
||||
"temperature_f": 72.4
|
||||
}
|
||||
],
|
||||
"relays": [
|
||||
{
|
||||
"id": "relay_1",
|
||||
"name": "Aux Power",
|
||||
"pin": 16,
|
||||
"enabled": true,
|
||||
"state": false
|
||||
},
|
||||
{
|
||||
"id": "relay_2",
|
||||
"name": "Fridge",
|
||||
"pin": 17,
|
||||
"enabled": true,
|
||||
"state": true
|
||||
}
|
||||
],
|
||||
"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": 1,
|
||||
"saved_networks": [
|
||||
{
|
||||
"index": 1,
|
||||
"ssid": "Starlink",
|
||||
"priority": 1,
|
||||
"active": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"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.4.0",
|
||||
"build_date": "Jun 07 2026",
|
||||
"build_time": "12:00:00",
|
||||
"uptime_seconds": 42
|
||||
},
|
||||
"config": {
|
||||
"device_name": "Cargo 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": "28ff001122334455",
|
||||
"enabled": true,
|
||||
"weather": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"id": "temp_1",
|
||||
"index": 1
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"device_name": "Cargo Controller",
|
||||
"relays": [],
|
||||
"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": "28ff001122334455",
|
||||
"enabled": true,
|
||||
"weather": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"id": "temp_1"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"device_name": "Cargo Controller",
|
||||
"relays": [],
|
||||
"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": "",
|
||||
"enabled": false,
|
||||
"weather": false
|
||||
}
|
||||
]
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"type": "temp_scan_response",
|
||||
"ok": true,
|
||||
"devices": [
|
||||
{
|
||||
"index": 1,
|
||||
"address": "28ff001122334455"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"networks": [
|
||||
{
|
||||
"ssid": "Starlink",
|
||||
"password": "example-password",
|
||||
"priority": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"type": "wifi_config_response",
|
||||
"ok": true,
|
||||
"wifi": {
|
||||
"ap_enabled": true,
|
||||
"sta_enabled": true,
|
||||
"network_count": 1,
|
||||
"active_ssid": "",
|
||||
"sta_connected": false,
|
||||
"ap_ip": "192.168.4.1",
|
||||
"sta_ip": "",
|
||||
"networks": [
|
||||
{
|
||||
"index": 1,
|
||||
"ssid": "Starlink",
|
||||
"priority": 1,
|
||||
"password_set": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,566 @@
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
FIXTURES = ROOT / "tests" / "fixtures" / "http_api"
|
||||
FIRMWARE = ROOT / "firmware" / "esp32" / "overland-controller" / "overland-controller.ino"
|
||||
|
||||
|
||||
def load_fixture(name):
|
||||
return json.loads((FIXTURES / name).read_text())
|
||||
|
||||
|
||||
def firmware_source():
|
||||
return FIRMWARE.read_text()
|
||||
|
||||
|
||||
def assert_keys(payload, keys):
|
||||
missing = set(keys) - set(payload)
|
||||
assert not missing, f"missing keys: {sorted(missing)}"
|
||||
|
||||
|
||||
def registered_routes():
|
||||
source = firmware_source()
|
||||
literal_pattern = re.compile(r'server\.on\("([^"]+)"(?:,\s*(HTTP_[A-Z]+))?')
|
||||
macro_pattern = re.compile(r'server\.on\(API_V1\("([^"]+)"\)(?:,\s*(HTTP_[A-Z]+))?')
|
||||
|
||||
routes = {(match.group(1), match.group(2) or "ANY") for match in literal_pattern.finditer(source)}
|
||||
routes.update({
|
||||
(f"/api/v1{match.group(1)}", match.group(2) or "ANY")
|
||||
for match in macro_pattern.finditer(source)
|
||||
})
|
||||
return routes
|
||||
|
||||
|
||||
def test_firmware_registers_current_http_contract_routes():
|
||||
routes = registered_routes()
|
||||
|
||||
expected_routes = {
|
||||
("/api/v1/status", "ANY"),
|
||||
("/api/v1/health", "HTTP_GET"),
|
||||
("/api/v1/capabilities", "HTTP_GET"),
|
||||
("/api/v1/config", "HTTP_GET"),
|
||||
("/api/v1/config/export", "HTTP_GET"),
|
||||
("/api/v1/config/import", "HTTP_POST"),
|
||||
("/api/v1/relay/set", "HTTP_POST"),
|
||||
("/api/v1/config/wifi", "HTTP_GET"),
|
||||
("/api/v1/config/wifi", "HTTP_POST"),
|
||||
("/api/v1/wifi/connect", "HTTP_POST"),
|
||||
("/api/v1/wifi/clear", "HTTP_POST"),
|
||||
("/api/v1/temps/scan", "HTTP_POST"),
|
||||
("/api/v1/temps/assign", "HTTP_POST"),
|
||||
("/api/v1/temps/clear", "HTTP_POST"),
|
||||
}
|
||||
|
||||
assert expected_routes <= routes
|
||||
|
||||
|
||||
def test_firmware_keeps_root_compatibility_aliases():
|
||||
routes = registered_routes()
|
||||
|
||||
compatibility_routes = {
|
||||
("/status", "ANY"),
|
||||
("/config", "HTTP_GET"),
|
||||
("/relay/set", "HTTP_POST"),
|
||||
("/config/wifi", "HTTP_GET"),
|
||||
("/config/wifi", "HTTP_POST"),
|
||||
("/wifi/connect", "HTTP_POST"),
|
||||
("/wifi/clear", "HTTP_POST"),
|
||||
("/temps/scan", "HTTP_POST"),
|
||||
("/temps/assign", "HTTP_POST"),
|
||||
("/temps/clear", "HTTP_POST"),
|
||||
}
|
||||
|
||||
assert compatibility_routes <= routes
|
||||
|
||||
|
||||
def test_embedded_webui_uses_versioned_api_routes():
|
||||
source = firmware_source()
|
||||
|
||||
assert 'const API_BASE="/api/v1";' in source
|
||||
assert 'fetch(api("' in source
|
||||
assert 'fetch("/status"' not in source
|
||||
assert 'fetch("/config' not in source
|
||||
assert 'fetch("/relay' not in source
|
||||
assert 'fetch("/temps' not in source
|
||||
assert 'fetch("/wifi' not in source
|
||||
assert "/bms/reconnect" not in source
|
||||
assert "/system/restart" not in source
|
||||
|
||||
|
||||
def test_status_fixture_matches_dashboard_contract_shape():
|
||||
payload = load_fixture("status_response.json")
|
||||
|
||||
assert_keys(payload, [
|
||||
"type",
|
||||
"timestamp",
|
||||
"battery",
|
||||
"temps",
|
||||
"relays",
|
||||
"vehicle",
|
||||
"network",
|
||||
"alarms",
|
||||
"system",
|
||||
"config",
|
||||
])
|
||||
assert payload["type"] == "status_response"
|
||||
|
||||
assert_keys(payload["battery"], [
|
||||
"source",
|
||||
"connected",
|
||||
"soc",
|
||||
"voltage",
|
||||
"current",
|
||||
"remaining_ah",
|
||||
"capacity_ah",
|
||||
"runtime_hours",
|
||||
"temperature_f",
|
||||
"cycle_count",
|
||||
"cell_count",
|
||||
"ntc_count",
|
||||
"cell_voltages",
|
||||
"cell_min_voltage",
|
||||
"cell_max_voltage",
|
||||
"cell_delta_mv",
|
||||
"cells_valid",
|
||||
])
|
||||
assert_keys(payload["temps"][0], [
|
||||
"id",
|
||||
"name",
|
||||
"enabled",
|
||||
"weather",
|
||||
"online",
|
||||
"temperature_f",
|
||||
])
|
||||
assert_keys(payload["relays"][0], ["id", "name", "pin", "enabled", "state"])
|
||||
assert_keys(payload["network"], [
|
||||
"wifi_enabled",
|
||||
"uart_connected",
|
||||
"ap_enabled",
|
||||
"ap_ip",
|
||||
"sta_enabled",
|
||||
"sta_connected",
|
||||
"sta_ssid",
|
||||
"sta_ip",
|
||||
"saved_network_count",
|
||||
"saved_networks",
|
||||
])
|
||||
assert_keys(payload["system"], [
|
||||
"firmware_name",
|
||||
"firmware_version",
|
||||
"build_date",
|
||||
"build_time",
|
||||
"uptime_seconds",
|
||||
])
|
||||
|
||||
|
||||
def test_config_fixture_matches_current_config_response_shape():
|
||||
payload = load_fixture("config_response.json")
|
||||
|
||||
assert_keys(payload, [
|
||||
"device_name",
|
||||
"relays",
|
||||
"bms",
|
||||
"temperature_sensor_count",
|
||||
"temperature_sensors",
|
||||
])
|
||||
assert_keys(payload["relays"][0], ["id", "name", "pin", "enabled"])
|
||||
assert_keys(payload["bms"], ["enabled", "name", "address", "address_type"])
|
||||
assert_keys(payload["temperature_sensors"][0], [
|
||||
"id",
|
||||
"name",
|
||||
"address",
|
||||
"enabled",
|
||||
"weather",
|
||||
])
|
||||
|
||||
|
||||
def test_health_and_capabilities_contracts_are_registered():
|
||||
source = firmware_source()
|
||||
|
||||
assert '"health_response"' in source
|
||||
assert '"capabilities_response"' in source
|
||||
assert '"GET /api/v1/health"' in source
|
||||
assert '"GET /api/v1/capabilities"' in source
|
||||
assert '"config_backup_restore"' in source
|
||||
|
||||
|
||||
def test_config_backup_restore_contract_is_registered():
|
||||
source = firmware_source()
|
||||
|
||||
assert 'API_V1("/config/export")' in source
|
||||
assert 'API_V1("/config/import")' in source
|
||||
assert '"config_export_response"' in source
|
||||
assert '"password"' in source
|
||||
assert '"invalid_config"' in source
|
||||
|
||||
|
||||
def test_relay_set_contract_fixtures_and_firmware_errors():
|
||||
request = load_fixture("relay_set_request.json")
|
||||
response = load_fixture("relay_set_response.json")
|
||||
|
||||
assert_keys(request, ["id", "state"])
|
||||
assert_keys(response, ["type", "ok", "id", "state"])
|
||||
assert response["type"] == "relay_response"
|
||||
assert response["ok"] is True
|
||||
|
||||
source = firmware_source()
|
||||
assert '"invalid_json"' in source
|
||||
assert '"unknown_relay"' in source
|
||||
|
||||
|
||||
def test_temp_scan_assign_clear_contract_fixtures():
|
||||
scan = load_fixture("temp_scan_response.json")
|
||||
assign_request = load_fixture("temp_assign_request.json")
|
||||
assign_response = load_fixture("temp_assign_response.json")
|
||||
clear_request = load_fixture("temp_clear_request.json")
|
||||
clear_response = load_fixture("temp_clear_response.json")
|
||||
|
||||
assert_keys(scan, ["type", "ok", "devices"])
|
||||
assert scan["type"] == "temp_scan_response"
|
||||
assert_keys(scan["devices"][0], ["index", "address"])
|
||||
|
||||
assert_keys(assign_request, ["id", "index"])
|
||||
assert_keys(assign_response, ["device_name", "relays", "bms", "temperature_sensors"])
|
||||
assert assign_response["temperature_sensors"][0]["address"]
|
||||
assert assign_response["temperature_sensors"][0]["enabled"] is True
|
||||
|
||||
assert_keys(clear_request, ["id"])
|
||||
assert_keys(clear_response, ["device_name", "relays", "bms", "temperature_sensors"])
|
||||
assert clear_response["temperature_sensors"][0]["address"] == ""
|
||||
assert clear_response["temperature_sensors"][0]["enabled"] is False
|
||||
|
||||
source = firmware_source()
|
||||
assert '"unknown_temp_sensor"' in source
|
||||
assert '"invalid_temp_selection"' in source
|
||||
|
||||
|
||||
def test_wifi_config_contract_fixtures():
|
||||
request = load_fixture("wifi_config_request.json")
|
||||
response = load_fixture("wifi_config_response.json")
|
||||
|
||||
assert_keys(request, ["networks"])
|
||||
assert_keys(request["networks"][0], ["ssid", "password", "priority"])
|
||||
assert_keys(response, ["type", "ok", "wifi"])
|
||||
assert response["type"] == "wifi_config_response"
|
||||
assert response["ok"] is True
|
||||
assert_keys(response["wifi"], [
|
||||
"ap_enabled",
|
||||
"sta_enabled",
|
||||
"network_count",
|
||||
"active_ssid",
|
||||
"sta_connected",
|
||||
"ap_ip",
|
||||
"sta_ip",
|
||||
"networks",
|
||||
])
|
||||
assert_keys(response["wifi"]["networks"][0], [
|
||||
"index",
|
||||
"ssid",
|
||||
"priority",
|
||||
"password_set",
|
||||
])
|
||||
|
||||
|
||||
def test_http_error_fixture_and_known_error_codes_are_stable():
|
||||
payload = load_fixture("error_response.json")
|
||||
source = firmware_source()
|
||||
|
||||
assert payload == {"ok": False, "error": "invalid_json"}
|
||||
|
||||
for error_code in [
|
||||
"invalid_json",
|
||||
"invalid_config",
|
||||
"unknown_relay",
|
||||
"unknown_temp_sensor",
|
||||
"invalid_temp_selection",
|
||||
"invalid_bms_selection",
|
||||
"invalid_relay_route",
|
||||
"missing_relay_action",
|
||||
"invalid_relay_action",
|
||||
]:
|
||||
assert error_code in source
|
||||
|
||||
|
||||
def test_status_endpoint_supports_field_selection():
|
||||
source = firmware_source()
|
||||
|
||||
assert 'server.hasArg("fields")' in source
|
||||
assert 'server.arg("fields")' in source
|
||||
assert 'findStatusFieldIndex' in source
|
||||
assert '"invalid_field"' in source
|
||||
assert 'Unknown field' in source
|
||||
assert 'server.send(400, "application/json", errorOutput)' in source
|
||||
|
||||
|
||||
def test_embedded_webui_uses_partial_status_for_overview_polling():
|
||||
source = firmware_source()
|
||||
|
||||
assert 'const STATUS_FIELDS_OVERVIEW="battery,temps,relays,vehicle,network,alarms,system";' in source
|
||||
assert 'function statusUrl()' in source
|
||||
assert 'api("/status?fields="+STATUS_FIELDS_OVERVIEW)' in source
|
||||
assert 'fetch(statusUrl(),{cache:"no-store"})' in source
|
||||
assert 'function mergeStatus(previous,next)' in source
|
||||
assert 'api("/status?fields=config")' in source
|
||||
|
||||
|
||||
def test_cargo_ap_uses_wpa2_password():
|
||||
source = firmware_source()
|
||||
|
||||
assert "Preferences apPrefs;" in source
|
||||
assert "generateDefaultApPassword" in source
|
||||
assert "validApPassword" in source
|
||||
assert "loadApConfig();" in source
|
||||
assert "startAccessPoint()" in source
|
||||
assert "WiFi.softAP(apSsid.c_str(), apPassword.c_str())" in source
|
||||
assert 'network["ap_auth"] = "wpa2";' in source
|
||||
assert 'server.on(API_V1("/config/ap"), HTTP_GET, handleGetApConfig);' in source
|
||||
assert 'server.on(API_V1("/config/ap"), HTTP_POST, handleUpdateApConfig);' in source
|
||||
|
||||
|
||||
def test_cargo_ap_password_is_not_returned_by_get_config_ap():
|
||||
source = firmware_source()
|
||||
|
||||
get_start = source.index("void handleGetApConfig()")
|
||||
get_end = source.index("void handleUpdateApConfig()")
|
||||
get_body = source[get_start:get_end]
|
||||
|
||||
assert 'response["password_set"]' in get_body
|
||||
assert 'response["password"]' not in get_body
|
||||
|
||||
|
||||
def test_embedded_webui_has_ap_settings_controls():
|
||||
source = firmware_source()
|
||||
|
||||
assert "Access Point" in source
|
||||
assert 'id="apSsidInput"' in source
|
||||
assert 'id="apPasswordInput"' in source
|
||||
assert "function saveApConfig()" in source
|
||||
assert 'api("/config/ap")' in source
|
||||
assert "toggleApPassword" in source
|
||||
assert "renderApConfig(data)" in source
|
||||
assert "Dashboard auto-migration is planned but not implemented yet" in source
|
||||
|
||||
|
||||
def test_embedded_webui_config_page_is_grouped():
|
||||
source = firmware_source()
|
||||
|
||||
assert "configGroupTitle" in source
|
||||
assert "Home / Starlink WiFi" in source
|
||||
assert "Relay Outputs" in source
|
||||
assert "Temperature Assignment" in source
|
||||
assert "Maintenance" in source
|
||||
|
||||
|
||||
def test_embedded_webui_config_subtabs():
|
||||
source = firmware_source()
|
||||
|
||||
assert "function showConfigSubtab(name)" in source
|
||||
assert "config-general" in source
|
||||
assert "config-wifi" in source
|
||||
assert "config-relays" in source
|
||||
assert "config-temps" in source
|
||||
assert "config-bms" in source
|
||||
assert "config-maintenance" in source
|
||||
assert "WiFi / AP" in source
|
||||
assert "Maintenance" in source
|
||||
|
||||
|
||||
def test_embedded_webui_api_badge_labels_are_clear():
|
||||
source = firmware_source()
|
||||
|
||||
assert "API Connecting..." in source
|
||||
assert "API Live" in source
|
||||
assert "API Error" in source
|
||||
assert 'textContent="Offline"' not in source
|
||||
|
||||
|
||||
def test_embedded_webui_config_page_is_grouped():
|
||||
source = firmware_source()
|
||||
|
||||
assert "configGroupTitle" in source
|
||||
assert "General" in source
|
||||
assert "Home / Starlink WiFi" in source
|
||||
assert "Relay Outputs" in source
|
||||
assert "Temperature Assignment" in source
|
||||
assert "BMS Config" in source
|
||||
assert "Maintenance" in source
|
||||
|
||||
|
||||
def test_embedded_webui_config_subtabs():
|
||||
source = firmware_source()
|
||||
|
||||
assert "function showConfigSubtab(name)" in source
|
||||
assert "config-general" in source
|
||||
assert "config-wifi" in source
|
||||
assert "config-relays" in source
|
||||
assert "config-temps" in source
|
||||
assert "config-bms" in source
|
||||
assert "config-maintenance" in source
|
||||
assert "WiFi / AP" in source
|
||||
assert "subtabbtn active" in source
|
||||
|
||||
|
||||
def test_cargo_oled_support_is_optional():
|
||||
source = firmware_source()
|
||||
|
||||
assert "#define OLED_ENABLED 0" in source
|
||||
assert "#if OLED_ENABLED" in source
|
||||
assert "void oledBegin()" in source
|
||||
assert "void oledLoop()" in source
|
||||
assert "void oledShowSetupPage()" in source
|
||||
assert "OLED_I2C_ADDR = 0x3C" in source
|
||||
assert "Adafruit_SSD1306" in source
|
||||
|
||||
|
||||
def test_cargo_oled_shows_setup_and_service_pages():
|
||||
source = firmware_source()
|
||||
|
||||
assert 'oledClear("Setup / AP")' in source
|
||||
assert 'oledClear("Battery")' in source
|
||||
assert 'oledClear("Relays")' in source
|
||||
assert 'oledClear("Network")' in source
|
||||
assert 'oledClear("Alarms")' in source
|
||||
assert "oledShowSetupPage();" in source
|
||||
assert "oledLoop();" in source
|
||||
|
||||
|
||||
def test_gpio_pin_plan_assigns_oled_to_21_22():
|
||||
config = (ROOT / "firmware" / "esp32" / "overland-controller" / "config.h").read_text()
|
||||
source = firmware_source()
|
||||
|
||||
assert "#define OLED_I2C_SDA_PIN 21" in config
|
||||
assert "#define OLED_I2C_SCL_PIN 22" in config
|
||||
assert "#define OLED_SETUP_BUTTON_PIN 25" in config
|
||||
assert "const int OLED_I2C_SDA = OLED_I2C_SDA_PIN;" in source
|
||||
assert "const int OLED_I2C_SCL = OLED_I2C_SCL_PIN;" in source
|
||||
assert "const int OLED_BUTTON_PIN = OLED_SETUP_BUTTON_PIN;" in source
|
||||
|
||||
|
||||
def test_legacy_dashboard_uart_is_disabled_by_default():
|
||||
config = (ROOT / "firmware" / "esp32" / "overland-controller" / "config.h").read_text()
|
||||
source = firmware_source()
|
||||
|
||||
assert "#define DASHBOARD_UART_ENABLED 0" in config
|
||||
assert "DASHBOARD_UART_TX_PIN 21" not in config
|
||||
assert "DASHBOARD_UART_RX_PIN 22" not in config
|
||||
assert "#if DASHBOARD_UART_ENABLED" in source
|
||||
assert "pollDashboardUart();" in source
|
||||
|
||||
|
||||
def test_temp_config_lookup_is_available_to_http_handlers():
|
||||
source = firmware_source()
|
||||
|
||||
assert "int findTempConfigIndexById(" in source
|
||||
assert "configIndex = findTempConfigIndexById(id);" in source
|
||||
assert "findTempConfigIndexForUart" not in source
|
||||
|
||||
helper_pos = source.index("int findTempConfigIndexById(")
|
||||
first_uart_guard_pos = source.index("#if DASHBOARD_UART_ENABLED")
|
||||
assert helper_pos < first_uart_guard_pos
|
||||
|
||||
|
||||
def test_embedded_webui_preserves_config_input_while_polling():
|
||||
source = firmware_source()
|
||||
|
||||
assert "const configTouched={};" in source
|
||||
assert "function markConfigFieldTouched(id)" in source
|
||||
assert "function setConfigValue(id,value)" in source
|
||||
assert "document.activeElement===el" in source
|
||||
assert 'document.addEventListener("input"' in source
|
||||
assert 'setConfigValue("apSsidInput"' in source
|
||||
assert "deviceNameInput" in source
|
||||
assert "bmsNameInput" in source
|
||||
assert "tempEnabledCount" in source
|
||||
|
||||
|
||||
def test_ap_config_can_be_reset_to_defaults():
|
||||
source = firmware_source()
|
||||
|
||||
assert "void resetApConfigToDefaults()" in source
|
||||
assert 'saveApConfig("OverlandController", newPassword);' in source
|
||||
assert "void handleResetApConfig()" in source
|
||||
assert 'server.on(API_V1("/config/ap/reset"), HTTP_POST, handleResetApConfig);' in source
|
||||
assert 'api("/config/ap/reset")' in source
|
||||
assert "Reset AP to Default" in source
|
||||
assert "regenerated password" in source
|
||||
|
||||
|
||||
def test_embedded_webui_has_consistent_button_intent_styles():
|
||||
source = firmware_source()
|
||||
|
||||
assert "/* UI polish v2 */" in source
|
||||
assert "button.save" in source
|
||||
assert "button.danger" in source
|
||||
assert "button.secondary" in source
|
||||
assert "configGroupTitle" in source
|
||||
assert "linear-gradient" in source
|
||||
assert 'class="save" onclick="saveApConfig()"' in source
|
||||
assert 'class="danger" onclick="resetApConfig()"' in source
|
||||
assert 'class="save" onclick="saveWifi()"' in source
|
||||
|
||||
|
||||
def test_embedded_webui_theme_v3():
|
||||
source = firmware_source()
|
||||
|
||||
assert "/* UI theme v3 */" in source
|
||||
assert "--accent:#5f8fcf" in source
|
||||
assert "@media(min-width:980px)" in source
|
||||
assert "#configPage" in source
|
||||
assert "grid-template-columns:190px 1fr" in source
|
||||
assert "button.save" in source
|
||||
assert "button.danger" in source
|
||||
assert "button.secondary" in source
|
||||
|
||||
|
||||
def test_embedded_webui_main_pages_are_mutually_exclusive():
|
||||
source = firmware_source()
|
||||
|
||||
assert "/* Desktop page layout fix */" in source
|
||||
assert ".page:not(.active){display:none!important}" in source
|
||||
assert "#configPage.active" in source
|
||||
assert "Main page navigation override" in source
|
||||
assert 'overview:"overviewPage"' in source
|
||||
assert 'battery:"batteryPage"' in source
|
||||
assert 'config:"configPage"' in source
|
||||
assert 'showConfigSubtab("general")' in source
|
||||
|
||||
|
||||
def test_embedded_webui_mobile_temperature_config_cards():
|
||||
source = firmware_source()
|
||||
|
||||
assert "/* Mobile temperature config layout */" in source
|
||||
assert "tempConfigCard" in source
|
||||
assert "tempConfigControls" in source
|
||||
assert "Temp Slot " in source
|
||||
assert "Clear Assignment" in source
|
||||
assert "Weather Badge" in source
|
||||
assert "@media(max-width:700px)" in source
|
||||
|
||||
|
||||
def test_embedded_webui_temp_config_uses_mobile_cards():
|
||||
source = firmware_source()
|
||||
start = source.index('const tempBox=$("tempConfigList");')
|
||||
end = source.index("async function saveRelayConfig", start)
|
||||
body = source[start:end]
|
||||
|
||||
assert "tempConfigCard" in body
|
||||
assert "Temp Slot " in body
|
||||
assert "Clear Assignment" in body
|
||||
assert "Weather Badge" in body
|
||||
assert "configrow tempConfigRow" not in body
|
||||
assert "Address:" not in body
|
||||
|
||||
|
||||
def test_embedded_webui_formats_uptime():
|
||||
source = firmware_source()
|
||||
|
||||
assert "function formatUptime(totalSeconds)" in source
|
||||
assert 'parts.push(days+"d")' in source
|
||||
assert 'parts.push(hours+"h")' in source
|
||||
assert 'parts.push(minutes+"m")' in source
|
||||
assert 'parts.push(seconds+"s")' in source
|
||||
assert 'formatUptime(s.uptime_seconds||0)' in source
|
||||
assert '+" sec"' not in source
|
||||
@@ -11,7 +11,7 @@ void setup() {
|
||||
Serial.print("Target: ");
|
||||
Serial.println(TARGET_ADDRESS);
|
||||
|
||||
NimBLEDevice::init("XterraESP32");
|
||||
NimBLEDevice::init("OverlandController");
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
|
||||
NimBLEAddress address(std::string(TARGET_ADDRESS), BLE_ADDR_PUBLIC);
|
||||
|
||||
@@ -44,7 +44,7 @@ void setup() {
|
||||
Serial.print("Target: ");
|
||||
Serial.println(TARGET_ADDRESS);
|
||||
|
||||
NimBLEDevice::init("XterraESP32");
|
||||
NimBLEDevice::init("OverlandController");
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
|
||||
NimBLEAddress address(std::string(TARGET_ADDRESS), BLE_ADDR_PUBLIC);
|
||||
|
||||
Reference in New Issue
Block a user