From b556d338d7f296eabea1657902d0dc99d0b9ee37 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 02:40:59 -0600 Subject: [PATCH 01/38] debug: clean LilyGO setup isolation baseline --- .../overland-controller.ino | 71 +++++++++++++++---- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 901cfc4..c178e58 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -4067,8 +4068,63 @@ void handleStatus() { server.send(200, "application/json", output); } + +void heapCheckpoint(const char* label) { + Serial.print("HEAP CHECK "); + Serial.print(label); + Serial.print(" free="); + Serial.print(ESP.getFreeHeap()); + Serial.print(" min="); + Serial.println(ESP.getMinFreeHeap()); + + if (!heap_caps_check_integrity_all(true)) { + Serial.print("HEAP CORRUPTION DETECTED AT: "); + Serial.println(label); + while (true) delay(1000); + } +} + + void setup() { Serial.begin(115200); + delay(2000); + + Serial.println(); + Serial.println("LilyGO clean isolation: config+relays+sensors+wifi+bare server"); + + heapCheckpoint("setup:start"); + + loadConfig(); + heapCheckpoint("after:loadConfig"); + + printConfig(); + + initRelays(); + heapCheckpoint("after:initRelays"); + + initSensors(); + heapCheckpoint("after:initSensors"); + + loadWifiConfig(); + heapCheckpoint("after:loadWifiConfig"); + + loadApConfig(); + heapCheckpoint("after:loadApConfig"); + + WiFi.persistent(false); + WiFi.setSleep(false); + WiFi.mode(WIFI_AP); + + startAccessPoint(); + heapCheckpoint("after:startAccessPoint"); + + server.begin(); + Serial.println("Bare WebServer started"); + heapCheckpoint("setup:end"); +} + +void originalProjectSetupDisabled() { + Serial.begin(115200); #if DASHBOARD_UART_ENABLED DashboardSerial.begin( @@ -4183,20 +4239,9 @@ void setup() { void loop() { server.handleClient(); - maintainStaWifi(); - handleDebugSerial(); - updateBms(); - updateAlarms(); #if DASHBOARD_UART_ENABLED pollDashboardUart(); #endif - oledLoop(); - - static unsigned long lastSensorUpdate = 0; - - if (millis() - lastSensorUpdate > 5000) { - updateSensors(); - lastSensorUpdate = millis(); - logDebug("Sensor Update"); - } + delay(2); } + From 43d201d040f1581a8a44d2fa53e1e57ae762368e Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 21:59:32 -0600 Subject: [PATCH 02/38] debug: add basic status routes to clean isolation --- .../esp32/overland-controller/overland-controller.ino | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index c178e58..5c82bb3 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4118,6 +4118,15 @@ void setup() { startAccessPoint(); heapCheckpoint("after:startAccessPoint"); + + server.on("/", HTTP_GET, []() { + server.send(200, "text/plain", "Overland Controller"); + }); + + server.on(API_V1("/health"), HTTP_GET, handleHealth); + server.on(API_V1("/capabilities"), HTTP_GET, handleCapabilities); + server.on(API_V1("/status"), handleStatus); + server.begin(); Serial.println("Bare WebServer started"); heapCheckpoint("setup:end"); From 5571aca72640630ce07f3fc07ee50b75e5b40ed2 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 22:02:04 -0600 Subject: [PATCH 03/38] debug: add relay routes to clean isolation --- firmware/esp32/overland-controller/overland-controller.ino | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 5c82bb3..e6dd50a 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4127,6 +4127,11 @@ void setup() { server.on(API_V1("/capabilities"), HTTP_GET, handleCapabilities); server.on(API_V1("/status"), handleStatus); + + server.on(API_V1("/relays"), HTTP_GET, handleGetRelays); + server.on(API_V1("/relays/toggle"), HTTP_POST, handleRelayToggle); + server.on(API_V1("/relays/set"), HTTP_POST, handleRelaySet); + server.begin(); Serial.println("Bare WebServer started"); heapCheckpoint("setup:end"); From acf98eb8e2303680c24fd25054f781695a4f075b Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 22:04:06 -0600 Subject: [PATCH 04/38] debug: add relay set route to clean isolation --- firmware/esp32/overland-controller/overland-controller.ino | 3 --- 1 file changed, 3 deletions(-) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index e6dd50a..f636511 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4128,9 +4128,6 @@ void setup() { server.on(API_V1("/status"), handleStatus); - server.on(API_V1("/relays"), HTTP_GET, handleGetRelays); - server.on(API_V1("/relays/toggle"), HTTP_POST, handleRelayToggle); - server.on(API_V1("/relays/set"), HTTP_POST, handleRelaySet); server.begin(); Serial.println("Bare WebServer started"); From 1f54f39956bcefe8400f13d2b090cfc096b2b10a Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 22:12:55 -0600 Subject: [PATCH 05/38] debug: add relay set route to active isolation setup --- firmware/esp32/overland-controller/overland-controller.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index f636511..4260f5a 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4126,6 +4126,8 @@ void setup() { server.on(API_V1("/health"), HTTP_GET, handleHealth); server.on(API_V1("/capabilities"), HTTP_GET, handleCapabilities); server.on(API_V1("/status"), handleStatus); + server.on(API_V1("/relay/set"), HTTP_POST, handleSetRelayPost); + From 03cad0481b3e4aaa550dafb9c4e719962b1cf593 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 22:18:17 -0600 Subject: [PATCH 06/38] debug: add read-only config routes to LilyGO isolation --- firmware/esp32/overland-controller/overland-controller.ino | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 4260f5a..2df9866 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4131,6 +4131,12 @@ void setup() { + + server.on(API_V1("/config"), HTTP_GET, handleGetConfig); + server.on(API_V1("/config/export"), HTTP_GET, handleExportConfig); + server.on(API_V1("/config/wifi"), HTTP_GET, handleGetWifiConfig); + server.on(API_V1("/config/ap"), HTTP_GET, handleGetApConfig); + server.begin(); Serial.println("Bare WebServer started"); heapCheckpoint("setup:end"); From b21e41341c04b883a3b32c70afc4ae0c6db135b6 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 22:40:10 -0600 Subject: [PATCH 07/38] debug: add config write routes to active LilyGO isolation setup --- .../esp32/overland-controller/overland-controller.ino | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 2df9866..cd2efd0 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4137,6 +4137,14 @@ void setup() { server.on(API_V1("/config/wifi"), HTTP_GET, handleGetWifiConfig); server.on(API_V1("/config/ap"), HTTP_GET, handleGetApConfig); + + server.on(API_V1("/config/device"), HTTP_POST, handleUpdateDeviceConfig); + server.on(API_V1("/config/relay"), HTTP_POST, handleUpdateRelayConfig); + server.on(API_V1("/config/bms"), HTTP_POST, handleUpdateBmsConfig); + server.on(API_V1("/config/temp"), HTTP_POST, handleUpdateTempSensorConfig); + server.on(API_V1("/config/factory-reset"), HTTP_POST, handleFactoryResetConfig); + server.on(API_V1("/config/save"), HTTP_POST, handleSaveConfig); + server.begin(); Serial.println("Bare WebServer started"); heapCheckpoint("setup:end"); From 4f24e6f87ae3b4e544fc6dd9a512c0d533e8f4a3 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 22:43:25 -0600 Subject: [PATCH 08/38] debug: test dashboard HTML route on LilyGO --- firmware/esp32/overland-controller/overland-controller.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index cd2efd0..067539d 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4120,7 +4120,8 @@ void setup() { server.on("/", HTTP_GET, []() { - server.send(200, "text/plain", "Overland Controller"); + Serial.println("HTTP GET / dashboard"); + server.send_P(200, "text/html", INDEX_HTML); }); server.on(API_V1("/health"), HTTP_GET, handleHealth); From 025e95b32fe6bcba306df33e054eb782a31fb3d8 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 22:48:24 -0600 Subject: [PATCH 09/38] debug: test AP STA mode without reconnect logic --- firmware/esp32/overland-controller/overland-controller.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 067539d..e0018d6 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4113,7 +4113,7 @@ void setup() { WiFi.persistent(false); WiFi.setSleep(false); - WiFi.mode(WIFI_AP); + WiFi.mode(WIFI_AP_STA); startAccessPoint(); heapCheckpoint("after:startAccessPoint"); From 3e56143d4baf06bf7bdc80b8794f1fcd14efed7e Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 23:08:27 -0600 Subject: [PATCH 10/38] debug: add WiFi write routes to LilyGO isolation --- firmware/esp32/overland-controller/overland-controller.ino | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index e0018d6..2ebf2ae 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4146,6 +4146,11 @@ void setup() { server.on(API_V1("/config/factory-reset"), HTTP_POST, handleFactoryResetConfig); server.on(API_V1("/config/save"), HTTP_POST, handleSaveConfig); + + server.on(API_V1("/config/wifi"), HTTP_POST, handleUpdateWifiConfig); + server.on(API_V1("/wifi/connect"), HTTP_POST, handleWifiConnect); + server.on(API_V1("/wifi/clear"), HTTP_POST, handleWifiClear); + server.begin(); Serial.println("Bare WebServer started"); heapCheckpoint("setup:end"); From b301400482aff41f47057d13c7b66dcfbf3f13de Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 23:13:01 -0600 Subject: [PATCH 11/38] debug: re-enable STA maintenance in LilyGO isolation build --- firmware/esp32/overland-controller/overland-controller.ino | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 2ebf2ae..e9cf0ab 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4272,9 +4272,13 @@ void originalProjectSetupDisabled() { void loop() { server.handleClient(); + + maintainStaWifi(); + #if DASHBOARD_UART_ENABLED pollDashboardUart(); #endif + delay(2); } From c2f8f1d9a5d041d8e5c0352bcaa513dbf5edc627 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 23:18:13 -0600 Subject: [PATCH 12/38] debug: add BMS init to LilyGO isolation build --- firmware/esp32/overland-controller/overland-controller.ino | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index e9cf0ab..ba7d734 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4105,6 +4105,10 @@ void setup() { initSensors(); heapCheckpoint("after:initSensors"); + heapCheckpoint("before:initBms"); + initBms(); + heapCheckpoint("after:initBms"); + loadWifiConfig(); heapCheckpoint("after:loadWifiConfig"); From a954755c4696aa5217c91f793e0bd13c51d12ddc Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 23:21:05 -0600 Subject: [PATCH 13/38] debug: add BMS update loop to LilyGO isolation build --- firmware/esp32/overland-controller/overland-controller.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index ba7d734..b812a36 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4279,6 +4279,8 @@ void loop() { maintainStaWifi(); + updateBms(); + #if DASHBOARD_UART_ENABLED pollDashboardUart(); #endif From 7fc2d35bd0aeb37961f901f41dc2c30b2bbc1d69 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 23:26:59 -0600 Subject: [PATCH 14/38] debug: delay BMS updates 30s after boot --- firmware/esp32/overland-controller/overland-controller.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index b812a36..ace5c96 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4279,7 +4279,9 @@ void loop() { maintainStaWifi(); - updateBms(); + if (millis() > 30000) { + updateBms(); + } #if DASHBOARD_UART_ENABLED pollDashboardUart(); From 6ae396f084bef9bc7a80c5396940d11a7bc45afb Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 23:37:33 -0600 Subject: [PATCH 15/38] debug: protect config inputs from status refresh --- .../overland-controller.ino | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index ace5c96..5da9e07 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -1046,12 +1046,12 @@ function render(data){ function populateSetupFields(data){ const cfg=data.config||{}; - if($("deviceNameInput")) $("deviceNameInput").value=cfg.device_name||""; + setConfigValue("deviceNameInput", cfg.device_name||""); const bms=cfg.bms||{}; - if($("bmsNameInput")) $("bmsNameInput").value=bms.name||""; - if($("bmsAddressInput")) $("bmsAddressInput").value=bms.address||""; - if($("bmsAddressTypeInput")) $("bmsAddressTypeInput").value=bms.address_type||"public"; + setConfigValue("bmsNameInput", bms.name||""); + setConfigValue("bmsAddressInput", bms.address||""); + setConfigValue("bmsAddressTypeInput", bms.address_type||"public"); } async function saveDeviceConfig(){ @@ -1199,12 +1199,18 @@ function renderConfigControls(data){ const relayBox=$("relayConfigList"); if(relayBox){ - relayBox.innerHTML=relays.map((r,i)=>` -
- - - -
`).join(""); + const signature=relays.map(r=>r.id).join("|"); + if(relayBox.dataset.signature!==signature){ + relayBox.dataset.signature=signature; + relayBox.innerHTML=relays.map((r,i)=>` +
+ + + +
`).join(""); + } + + relays.forEach((r,i)=>setConfigValue("relayName"+i,r.name||"")); } setConfigValue("tempEnabledCount", temps.filter(t=>t.enabled).length); From f249e2ef6b3a41c36496fb453a1e9e4fab3055be Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 23:41:41 -0600 Subject: [PATCH 16/38] debug: add BMS routes to LilyGO isolation --- firmware/esp32/overland-controller/overland-controller.ino | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 5da9e07..935431a 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4161,6 +4161,10 @@ void setup() { server.on(API_V1("/wifi/connect"), HTTP_POST, handleWifiConnect); server.on(API_V1("/wifi/clear"), HTTP_POST, handleWifiClear); + + server.on(API_V1("/bms/scan"), HTTP_POST, handleBleScan); + server.on(API_V1("/bms/select"), HTTP_POST, handleSelectBms); + server.begin(); Serial.println("Bare WebServer started"); heapCheckpoint("setup:end"); From a1a19270d728b1b6e165f04b30230da6769e157e Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 30 Jun 2026 23:57:58 -0600 Subject: [PATCH 17/38] debug: add legacy routes to LilyGO isolation --- .../esp32/overland-controller/overland-controller.ino | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 935431a..60147d3 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4165,6 +4165,15 @@ void setup() { server.on(API_V1("/bms/scan"), HTTP_POST, handleBleScan); server.on(API_V1("/bms/select"), HTTP_POST, handleSelectBms); + + // Legacy compatibility routes. + server.on("/relay/relay_1/on", HTTP_GET, handleGenericRelayRoute); + server.on("/relay/relay_1/off", HTTP_GET, handleGenericRelayRoute); + server.on("/relay/relay_2/on", HTTP_GET, handleGenericRelayRoute); + server.on("/relay/relay_2/off", HTTP_GET, handleGenericRelayRoute); + server.on("/bms/scan", HTTP_POST, handleBleScan); + server.on("/bms/select", HTTP_POST, handleSelectBms); + server.begin(); Serial.println("Bare WebServer started"); heapCheckpoint("setup:end"); From 3a05cb8894e771059e9729b4d032e76ea5235676 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 00:05:16 -0600 Subject: [PATCH 18/38] ui: add BMS scan button --- .../overland-controller.ino | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 60147d3..fa968d3 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -639,12 +639,15 @@ button[onclick^="scan"]{
+
BMS Polling--
+
+
@@ -1070,6 +1073,46 @@ async function saveDeviceConfig(){ clearConfigTouched(["deviceNameInput"]); } + +async function scanBmsDevices(){ + const status=$("bmsScanStatus"); + const box=$("bmsScanResults"); + if(status) status.textContent="Scanning for BLE BMS devices..."; + if(box) box.innerHTML=""; + + try{ + const res=await fetch(api("/bms/scan"),{method:"POST"}); + const data=await res.json(); + const devices=data.devices||[]; + + if(status) status.textContent=devices.length ? `Found ${devices.length} device(s).` : "No BLE devices found."; + + if(box){ + box.innerHTML=devices.map(d=>` +
+
+
${d.name||"Unnamed BLE device"}
+
${d.address||""} RSSI ${d.rssi??"--"}
+
+ +
+ `).join(""); + } + }catch(e){ + if(status) status.textContent="BMS scan failed."; + } +} + +function selectScannedBms(address,name){ + setConfigValue("bmsNameInput",name||"BMS",true); + setConfigValue("bmsAddressInput",address||"",true); + setConfigValue("bmsAddressTypeInput","public",true); + markConfigFieldTouched("bmsNameInput"); + markConfigFieldTouched("bmsAddressInput"); + markConfigFieldTouched("bmsAddressTypeInput"); +} + + async function saveBmsFullConfig(){ const name=$("bmsNameInput")?.value.trim()||"BMS"; const address=$("bmsAddressInput")?.value.trim()||""; From f2bdf363377b2fa743f72112e456d2b455deb285 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 00:17:25 -0600 Subject: [PATCH 19/38] hardware: move DS18B20 data to LilyGO GPIO21 --- firmware/esp32/overland-controller/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/esp32/overland-controller/config.h b/firmware/esp32/overland-controller/config.h index 0255226..6192a43 100644 --- a/firmware/esp32/overland-controller/config.h +++ b/firmware/esp32/overland-controller/config.h @@ -14,7 +14,7 @@ #define RELAY_SHIFT_REGISTER_PIN 255 // GPIO4 is used by the LilyGO relay shift-register OE pin. -#define ONEWIRE_PIN 14 +#define ONEWIRE_PIN 21 #define IGNITION_PIN 34 From 737ec8c10ddb04615bf8c4370d0bbc19c0a78f31 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 00:33:35 -0600 Subject: [PATCH 20/38] debug: add temp scan routes to LilyGO isolation --- firmware/esp32/overland-controller/overland-controller.ino | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index fa968d3..ea40fac 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4217,6 +4217,11 @@ void setup() { server.on("/bms/scan", HTTP_POST, handleBleScan); server.on("/bms/select", HTTP_POST, handleSelectBms); + + server.on(API_V1("/temps/scan"), HTTP_POST, handleTempScan); + server.on(API_V1("/temps/assign"), HTTP_POST, handleTempAssign); + server.on(API_V1("/temps/clear"), HTTP_POST, handleTempClear); + server.begin(); Serial.println("Bare WebServer started"); heapCheckpoint("setup:end"); From 0b04dcf89a261c2cdc36144538bbd4d79812a036 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 00:42:35 -0600 Subject: [PATCH 21/38] debug: add sensor updates to LilyGO isolation loop --- firmware/esp32/overland-controller/overland-controller.ino | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index ea40fac..2ca2865 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4346,6 +4346,12 @@ void loop() { maintainStaWifi(); + if (millis() - lastSensorUpdate > SENSOR_INTERVAL) { + updateSensors(); + lastSensorUpdate = millis(); + logDebug("Sensor Update"); + } + if (millis() > 30000) { updateBms(); } From 0b55ede1fc0767911e2598d73a6f91f6079539c7 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 00:46:46 -0600 Subject: [PATCH 22/38] debug: add sensor update timing globals --- firmware/esp32/overland-controller/config.h | 2 +- firmware/esp32/overland-controller/overland-controller.ino | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/firmware/esp32/overland-controller/config.h b/firmware/esp32/overland-controller/config.h index 6192a43..9b20f0b 100644 --- a/firmware/esp32/overland-controller/config.h +++ b/firmware/esp32/overland-controller/config.h @@ -14,7 +14,7 @@ #define RELAY_SHIFT_REGISTER_PIN 255 // GPIO4 is used by the LilyGO relay shift-register OE pin. -#define ONEWIRE_PIN 21 +#define ONEWIRE_PIN 15 #define IGNITION_PIN 34 diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 2ca2865..a361dfe 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -1509,6 +1509,9 @@ setInterval(load,3000); WebServer server(80); +unsigned long lastSensorUpdate = 0; +const unsigned long SENSOR_INTERVAL = 5000; + #define API_V1(path) "/api/v1" path int findTempConfigIndexById(const String& id) { From 3ec837a08c9ef8ca03a5f4ef9c62b8fa6511e2a1 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 22:36:56 -0600 Subject: [PATCH 23/38] debug: add OTA routes to LilyGO isolation --- firmware/esp32/overland-controller/overland-controller.ino | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index a361dfe..119723e 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4225,6 +4225,10 @@ void setup() { server.on(API_V1("/temps/assign"), HTTP_POST, handleTempAssign); server.on(API_V1("/temps/clear"), HTTP_POST, handleTempClear); + + server.on("/update", HTTP_GET, handleUpdatePage); + server.on("/update", HTTP_POST, handleUpdateUploadDone, handleUpdateUpload); + server.begin(); Serial.println("Bare WebServer started"); heapCheckpoint("setup:end"); From c6021871634af55a7318d536fa70bcc7124db721 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 22:51:50 -0600 Subject: [PATCH 24/38] debug: add OTA routes to LilyGO isolation --- firmware/esp32/overland-controller/overland-controller.ino | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 119723e..6a9b4a8 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4226,8 +4226,10 @@ void setup() { server.on(API_V1("/temps/clear"), HTTP_POST, handleTempClear); - server.on("/update", HTTP_GET, handleUpdatePage); - server.on("/update", HTTP_POST, handleUpdateUploadDone, handleUpdateUpload); + + + server.on("/update", HTTP_GET, handleOtaPage); + server.on("/update", HTTP_POST, handleOtaUploadDone, handleOtaUpload); server.begin(); Serial.println("Bare WebServer started"); From ebed07500aa44a9e833c1addf41fe97d00310c5f Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 23:10:21 -0600 Subject: [PATCH 25/38] debug: add OTA routes using original handlers --- firmware/esp32/overland-controller/overland-controller.ino | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 6a9b4a8..ecb9c77 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4229,7 +4229,12 @@ void setup() { server.on("/update", HTTP_GET, handleOtaPage); - server.on("/update", HTTP_POST, handleOtaUploadDone, handleOtaUpload); + + + server.on("/ota", HTTP_GET, handleOtaPage); + server.on(API_V1("/system/ota"), HTTP_POST, + handleOtaUploadDone, + handleOtaUploadStream); server.begin(); Serial.println("Bare WebServer started"); From 617fef32ce46f3f03731e410e76c412c1e064ada Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 23:22:42 -0600 Subject: [PATCH 26/38] debug: restore final setup bits in LilyGO isolation --- firmware/esp32/overland-controller/overland-controller.ino | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index ecb9c77..3872972 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4151,6 +4151,8 @@ void setup() { printConfig(); + pinMode(IGNITION_PIN, INPUT); + initRelays(); heapCheckpoint("after:initRelays"); @@ -4174,6 +4176,10 @@ void setup() { startAccessPoint(); heapCheckpoint("after:startAccessPoint"); + oledShowSetupPage(); + + lastStaReconnectAttempt = millis() - STA_RECONNECT_INTERVAL_MS; + server.on("/", HTTP_GET, []() { Serial.println("HTTP GET / dashboard"); From 2112e6b58ce07ad36709262fc4be35e94542b788 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 23:26:05 -0600 Subject: [PATCH 27/38] Revert "debug: restore final setup bits in LilyGO isolation" This reverts commit 617fef32ce46f3f03731e410e76c412c1e064ada. --- firmware/esp32/overland-controller/overland-controller.ino | 6 ------ 1 file changed, 6 deletions(-) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 3872972..ecb9c77 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4151,8 +4151,6 @@ void setup() { printConfig(); - pinMode(IGNITION_PIN, INPUT); - initRelays(); heapCheckpoint("after:initRelays"); @@ -4176,10 +4174,6 @@ void setup() { startAccessPoint(); heapCheckpoint("after:startAccessPoint"); - oledShowSetupPage(); - - lastStaReconnectAttempt = millis() - STA_RECONNECT_INTERVAL_MS; - server.on("/", HTTP_GET, []() { Serial.println("HTTP GET / dashboard"); From ac6b0475ffb3984ed9553028cf643f421ccbbb3e Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 23:29:34 -0600 Subject: [PATCH 28/38] debug: restore ignition pin and OLED setup only --- firmware/esp32/overland-controller/overland-controller.ino | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index ecb9c77..9148a06 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4151,6 +4151,8 @@ void setup() { printConfig(); + pinMode(IGNITION_PIN, INPUT); + initRelays(); heapCheckpoint("after:initRelays"); @@ -4174,6 +4176,8 @@ void setup() { startAccessPoint(); heapCheckpoint("after:startAccessPoint"); + oledShowSetupPage(); + server.on("/", HTTP_GET, []() { Serial.println("HTTP GET / dashboard"); From c8d0e4a23f6ad7a022615e4e8164d1079258f3e2 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 23:31:55 -0600 Subject: [PATCH 29/38] Revert "debug: restore ignition pin and OLED setup only" This reverts commit ac6b0475ffb3984ed9553028cf643f421ccbbb3e. --- firmware/esp32/overland-controller/overland-controller.ino | 4 ---- 1 file changed, 4 deletions(-) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 9148a06..ecb9c77 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4151,8 +4151,6 @@ void setup() { printConfig(); - pinMode(IGNITION_PIN, INPUT); - initRelays(); heapCheckpoint("after:initRelays"); @@ -4176,8 +4174,6 @@ void setup() { startAccessPoint(); heapCheckpoint("after:startAccessPoint"); - oledShowSetupPage(); - server.on("/", HTTP_GET, []() { Serial.println("HTTP GET / dashboard"); From 83a392567c8d0e00abdbdf77e261db61aa72369a Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 23:34:08 -0600 Subject: [PATCH 30/38] debug: restore ignition input only --- firmware/esp32/overland-controller/overland-controller.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index ecb9c77..b52ec2b 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4151,6 +4151,8 @@ void setup() { printConfig(); + pinMode(IGNITION_PIN, INPUT); + initRelays(); heapCheckpoint("after:initRelays"); From 983f9cae6166e812c1084460fcc35e737853ad6c Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 23:37:11 -0600 Subject: [PATCH 31/38] hardware: move ignition sense to LilyGO GPIO39 --- firmware/esp32/overland-controller/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/esp32/overland-controller/config.h b/firmware/esp32/overland-controller/config.h index 9b20f0b..65d1527 100644 --- a/firmware/esp32/overland-controller/config.h +++ b/firmware/esp32/overland-controller/config.h @@ -16,7 +16,7 @@ // GPIO4 is used by the LilyGO relay shift-register OE pin. #define ONEWIRE_PIN 15 -#define IGNITION_PIN 34 +#define IGNITION_PIN 39 #define OLED_I2C_SDA_PIN 21 #define OLED_I2C_SCL_PIN 22 From 95fa78ee64c8f477001590925ab76ef0eb3075ca Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 23:43:33 -0600 Subject: [PATCH 32/38] debug: try STA reconnect 5s after LilyGO boot --- firmware/esp32/overland-controller/overland-controller.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index b52ec2b..a562b02 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4240,6 +4240,8 @@ void setup() { server.begin(); Serial.println("Bare WebServer started"); + lastStaReconnectAttempt = millis() - STA_RECONNECT_INTERVAL_MS + 5000; + heapCheckpoint("setup:end"); } From 97048b8ceb43ea62bb2bb9260fd17e5a2a77f257 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 23:49:30 -0600 Subject: [PATCH 33/38] debug: add AP config POST route to LilyGO isolation --- firmware/esp32/overland-controller/overland-controller.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index a562b02..0e233e3 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4195,6 +4195,7 @@ void setup() { server.on(API_V1("/config/export"), HTTP_GET, handleExportConfig); server.on(API_V1("/config/wifi"), HTTP_GET, handleGetWifiConfig); server.on(API_V1("/config/ap"), HTTP_GET, handleGetApConfig); + server.on(API_V1("/config/ap"), HTTP_POST, handleUpdateApConfig); server.on(API_V1("/config/device"), HTTP_POST, handleUpdateDeviceConfig); From 9b5581b2d817e87f58440a7aee98ac1c6013a7a6 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 1 Jul 2026 23:56:17 -0600 Subject: [PATCH 34/38] change WiFi settings back to direct --- firmware/esp32-s3-dashboard/dashboard_config.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/firmware/esp32-s3-dashboard/dashboard_config.h b/firmware/esp32-s3-dashboard/dashboard_config.h index 2eef843..f918f63 100644 --- a/firmware/esp32-s3-dashboard/dashboard_config.h +++ b/firmware/esp32-s3-dashboard/dashboard_config.h @@ -4,7 +4,7 @@ static const char *DASHBOARD_VERSION = "0.1.126-restore-boot-screen"; // Update these if your Cargo ESP AP credentials are different. static const char *CARGO_WIFI_SSID = "OverlandController"; -static const char *CARGO_WIFI_PASSWORD = "overland1234"; +static const char *CARGO_WIFI_PASSWORD = "Ward1707"; //static const char *CARGO_WIFI_SSID = "WardAP"; //static const char *CARGO_WIFI_PASSWORD = "Ward5213"; @@ -14,7 +14,7 @@ static const char *CARGO_API_FAST_STATUS_URL = "http://192.168.4.1/api/v1/status static const char *CARGO_API_SLOW_STATUS_URL = "http://192.168.4.1/api/v1/status?fields=network,system,config"; static const char *CARGO_API_RELAY_SET_URL = "http://192.168.4.1/api/v1/relay/set"; -//static const char *CARGO_API_STATUS_URL = "http://192.168.88.108/api/v1/status"; -//static const char *CARGO_API_FAST_STATUS_URL = "http://192.168.88.108/api/v1/status?fields=battery,temps,relays"; -//static const char *CARGO_API_SLOW_STATUS_URL = "http://192.168.88.108/api/v1/status?fields=network,system,config"; -//static const char *CARGO_API_RELAY_SET_URL = "http://192.168.88.108/api/v1/relay/set"; +//static const char *CARGO_API_STATUS_URL = "http://192.168.88.181/api/v1/status"; +//static const char *CARGO_API_FAST_STATUS_URL = "http://192.168.88.181/api/v1/status?fields=battery,temps,relays"; +//static const char *CARGO_API_SLOW_STATUS_URL = "http://192.168.88.181/api/v1/status?fields=network,system,config"; +//static const char *CARGO_API_RELAY_SET_URL = "http://192.168.88.181/api/v1/relay/set"; From 3b57761498d78204310bf32fccb884fffed58e26 Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 2 Jul 2026 00:08:08 -0600 Subject: [PATCH 35/38] api: restore missing v1 routes for LilyGO --- firmware/esp32/overland-controller/overland-controller.ino | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 0e233e3..a04da64 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -4239,6 +4239,12 @@ void setup() { handleOtaUploadDone, handleOtaUploadStream); + + server.on(API_V1("/config/import"), HTTP_POST, handleImportConfig); + server.on(API_V1("/config/ap/reset"), HTTP_POST, handleResetApConfig); + server.on(API_V1("/bms/setup/enter"), HTTP_POST, handleEnterBmsSetup); + server.on(API_V1("/bms/setup/exit"), HTTP_POST, handleExitBmsSetup); + server.begin(); Serial.println("Bare WebServer started"); lastStaReconnectAttempt = millis() - STA_RECONNECT_INTERVAL_MS + 5000; From d17699a7bebce3a6627e6b954a67e6baf46d0b7b Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 2 Jul 2026 00:24:22 -0600 Subject: [PATCH 36/38] firmware: finalize LilyGO cargo cleanup --- .../overland-controller.ino | 166 +----------------- tests/test_http_api_contract.py | 24 ++- 2 files changed, 22 insertions(+), 168 deletions(-) diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index a04da64..1cb1996 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -1702,7 +1702,6 @@ void sendSimpleJsonError(int status, const char* error, const char* detail) { } - const char OTA_HTML[] PROGMEM = R"rawliteral( @@ -2481,9 +2480,6 @@ int findStatusFieldIndex(const String& field) { } - - - void buildStatusDocument(JsonDocument& doc) { doc["type"] = MSG_STATUS_RESPONSE; doc["timestamp"] = millis(); @@ -3709,7 +3705,6 @@ void handleFactoryResetConfig() { } - void sendWifiConfigJson() { DynamicJsonDocument doc(2048); @@ -4121,64 +4116,37 @@ void handleStatus() { } -void heapCheckpoint(const char* label) { - Serial.print("HEAP CHECK "); - Serial.print(label); - Serial.print(" free="); - Serial.print(ESP.getFreeHeap()); - Serial.print(" min="); - Serial.println(ESP.getMinFreeHeap()); - - if (!heap_caps_check_integrity_all(true)) { - Serial.print("HEAP CORRUPTION DETECTED AT: "); - Serial.println(label); - while (true) delay(1000); - } -} - - void setup() { Serial.begin(115200); delay(2000); Serial.println(); - Serial.println("LilyGO clean isolation: config+relays+sensors+wifi+bare server"); - - heapCheckpoint("setup:start"); + Serial.println("Overland Controller LilyGO T-Relay S3 Booting"); loadConfig(); - heapCheckpoint("after:loadConfig"); printConfig(); pinMode(IGNITION_PIN, INPUT); initRelays(); - heapCheckpoint("after:initRelays"); initSensors(); - heapCheckpoint("after:initSensors"); - heapCheckpoint("before:initBms"); initBms(); - heapCheckpoint("after:initBms"); loadWifiConfig(); - heapCheckpoint("after:loadWifiConfig"); loadApConfig(); - heapCheckpoint("after:loadApConfig"); WiFi.persistent(false); WiFi.setSleep(false); WiFi.mode(WIFI_AP_STA); startAccessPoint(); - heapCheckpoint("after:startAccessPoint"); server.on("/", HTTP_GET, []() { - Serial.println("HTTP GET / dashboard"); server.send_P(200, "text/html", INDEX_HTML); }); @@ -4188,9 +4156,6 @@ void setup() { server.on(API_V1("/relay/set"), HTTP_POST, handleSetRelayPost); - - - server.on(API_V1("/config"), HTTP_GET, handleGetConfig); server.on(API_V1("/config/export"), HTTP_GET, handleExportConfig); server.on(API_V1("/config/wifi"), HTTP_GET, handleGetWifiConfig); @@ -4215,22 +4180,11 @@ void setup() { server.on(API_V1("/bms/select"), HTTP_POST, handleSelectBms); - // Legacy compatibility routes. - server.on("/relay/relay_1/on", HTTP_GET, handleGenericRelayRoute); - server.on("/relay/relay_1/off", HTTP_GET, handleGenericRelayRoute); - server.on("/relay/relay_2/on", HTTP_GET, handleGenericRelayRoute); - server.on("/relay/relay_2/off", HTTP_GET, handleGenericRelayRoute); - server.on("/bms/scan", HTTP_POST, handleBleScan); - server.on("/bms/select", HTTP_POST, handleSelectBms); - - server.on(API_V1("/temps/scan"), HTTP_POST, handleTempScan); server.on(API_V1("/temps/assign"), HTTP_POST, handleTempAssign); server.on(API_V1("/temps/clear"), HTTP_POST, handleTempClear); - - server.on("/update", HTTP_GET, handleOtaPage); @@ -4246,124 +4200,8 @@ void setup() { server.on(API_V1("/bms/setup/exit"), HTTP_POST, handleExitBmsSetup); server.begin(); - Serial.println("Bare WebServer started"); - lastStaReconnectAttempt = millis() - STA_RECONNECT_INTERVAL_MS + 5000; - - heapCheckpoint("setup:end"); -} - -void originalProjectSetupDisabled() { - Serial.begin(115200); - -#if DASHBOARD_UART_ENABLED - DashboardSerial.begin( - DASHBOARD_UART_BAUD, - SERIAL_8N1, - 0, - 0 - ); - Serial.println("Legacy dashboard UART enabled"); -#endif - - Serial.println(); - Serial.println("=================================="); - Serial.println("Overland Controller Booting"); - Serial.println("=================================="); - - oledBegin(); - - loadConfig(); - printConfig(); - - pinMode(IGNITION_PIN, INPUT); - - initRelays(); - initSensors(); - initBms(); - - loadWifiConfig(); - loadApConfig(); - - WiFi.mode(WIFI_AP_STA); - startAccessPoint(); - oledShowSetupPage(); - - // Do not block setup on STA WiFi. Start the AP and web server first so the - // local Web UI/API remains available even when no saved WiFi networks exist. - lastStaReconnectAttempt = millis() - STA_RECONNECT_INTERVAL_MS; - - server.on("/", HTTP_GET, []() { - server.send_P(200, "text/html", INDEX_HTML); - }); - server.on("/ota", HTTP_GET, handleOtaPage); - server.on(API_V1("/system/ota"), HTTP_POST, handleOtaUploadDone, handleOtaUploadStream); - - server.on(API_V1("/status"), handleStatus); - server.on(API_V1("/health"), HTTP_GET, handleHealth); - server.on(API_V1("/capabilities"), HTTP_GET, handleCapabilities); - server.on(API_V1("/relay/set"), HTTP_POST, handleSetRelayPost); - - server.on(API_V1("/config"), HTTP_GET, handleGetConfig); - server.on(API_V1("/config/export"), HTTP_GET, handleExportConfig); - server.on(API_V1("/config/import"), HTTP_POST, handleImportConfig); - server.on(API_V1("/config/wifi"), HTTP_GET, handleGetWifiConfig); - server.on(API_V1("/config/ap"), HTTP_GET, handleGetApConfig); - server.on(API_V1("/config/ap"), HTTP_POST, handleUpdateApConfig); - server.on(API_V1("/config/ap/reset"), HTTP_POST, handleResetApConfig); - server.on(API_V1("/config/wifi"), HTTP_POST, handleUpdateWifiConfig); - server.on(API_V1("/wifi/connect"), HTTP_POST, handleWifiConnect); - server.on(API_V1("/wifi/clear"), HTTP_POST, handleWifiClear); - - server.on(API_V1("/config/device"), HTTP_POST, handleUpdateDeviceConfig); - server.on(API_V1("/config/relay"), HTTP_POST, handleUpdateRelayConfig); - server.on(API_V1("/config/bms"), HTTP_POST, handleUpdateBmsConfig); - server.on(API_V1("/config/temp"), HTTP_POST, handleUpdateTempSensorConfig); - server.on(API_V1("/config/factory-reset"), HTTP_POST, handleFactoryResetConfig); - server.on(API_V1("/config/save"), HTTP_POST, handleSaveConfig); - - server.on(API_V1("/temps/scan"), HTTP_POST, handleTempScan); - server.on(API_V1("/temps/assign"), HTTP_POST, handleTempAssign); - server.on(API_V1("/temps/clear"), HTTP_POST, handleTempClear); - - server.on(API_V1("/bms/setup/enter"), HTTP_POST, handleEnterBmsSetup); - server.on(API_V1("/bms/setup/exit"), HTTP_POST, handleExitBmsSetup); - server.on(API_V1("/bms/scan"), HTTP_POST, handleBleScan); - server.on(API_V1("/bms/select"), HTTP_POST, handleSelectBms); - - // Compatibility aliases for pre-versioned local clients. - server.on("/status", handleStatus); - server.on("/relay/set", HTTP_POST, handleSetRelayPost); - server.on("/relay/relay_1/on", HTTP_GET, handleGenericRelayRoute); - server.on("/relay/relay_1/off", HTTP_GET, handleGenericRelayRoute); - server.on("/relay/relay_2/on", HTTP_GET, handleGenericRelayRoute); - server.on("/relay/relay_2/off", HTTP_GET, handleGenericRelayRoute); - - server.on("/config", HTTP_GET, handleGetConfig); - server.on("/config/wifi", HTTP_GET, handleGetWifiConfig); - server.on("/config/wifi", HTTP_POST, handleUpdateWifiConfig); - server.on("/wifi/connect", HTTP_POST, handleWifiConnect); - server.on("/wifi/clear", HTTP_POST, handleWifiClear); - - server.on("/config/device", HTTP_POST, handleUpdateDeviceConfig); - server.on("/config/relay", HTTP_POST, handleUpdateRelayConfig); - server.on("/config/bms", HTTP_POST, handleUpdateBmsConfig); - server.on("/config/temp", HTTP_POST, handleUpdateTempSensorConfig); - server.on("/config/factory-reset", HTTP_POST, handleFactoryResetConfig); - server.on("/config/save", HTTP_POST, handleSaveConfig); - - server.on("/temps/scan", HTTP_POST, handleTempScan); - server.on("/temps/assign", HTTP_POST, handleTempAssign); - server.on("/temps/clear", HTTP_POST, handleTempClear); - - server.on("/bms/setup/enter", HTTP_POST, handleEnterBmsSetup); - server.on("/bms/setup/exit", HTTP_POST, handleExitBmsSetup); - server.on("/bms/scan", HTTP_POST, handleBleScan); - server.on("/bms/select", HTTP_POST, handleSelectBms); - - - server.begin(); - Serial.println("Web Server Started"); + lastStaReconnectAttempt = millis() - STA_RECONNECT_INTERVAL_MS + 5000; } void loop() { diff --git a/tests/test_http_api_contract.py b/tests/test_http_api_contract.py index d82444a..680f0a2 100644 --- a/tests/test_http_api_contract.py +++ b/tests/test_http_api_contract.py @@ -57,10 +57,23 @@ def test_firmware_registers_current_http_contract_routes(): assert expected_routes <= routes -def test_firmware_keeps_root_compatibility_aliases(): +def test_firmware_uses_v1_routes_without_legacy_aliases(): routes = registered_routes() - compatibility_routes = { + required_v1_routes = { + ("/api/v1/status", "ANY"), + ("/api/v1/config", "HTTP_GET"), + ("/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"), + } + + removed_legacy_routes = { ("/status", "ANY"), ("/config", "HTTP_GET"), ("/relay/set", "HTTP_POST"), @@ -73,7 +86,8 @@ def test_firmware_keeps_root_compatibility_aliases(): ("/temps/clear", "HTTP_POST"), } - assert compatibility_routes <= routes + assert required_v1_routes <= routes + assert routes.isdisjoint(removed_legacy_routes) def test_embedded_webui_uses_versioned_api_routes(): @@ -461,7 +475,9 @@ def test_cargo_oled_shows_setup_and_service_pages(): assert 'oledClear("Relays")' in source assert 'oledClear("Network")' in source assert 'oledClear("Alarms")' in source - assert "oledShowSetupPage();" in source + app_config = Path("firmware/esp32/overland-controller/app_config.h").read_text() + if 'HARDWARE_PROFILE "lilygo_t_relay_s3_6ch"' not in app_config: + assert "oledShowSetupPage();" in source assert "oledLoop();" in source From de6a3f5f922770974e1a8ebde9720c5df7f2d658 Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 2 Jul 2026 00:25:56 -0600 Subject: [PATCH 37/38] test: allow LilyGO cargo firmware without OLED setup page --- tests/test_http_api_contract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_http_api_contract.py b/tests/test_http_api_contract.py index 680f0a2..9b5283b 100644 --- a/tests/test_http_api_contract.py +++ b/tests/test_http_api_contract.py @@ -476,7 +476,7 @@ def test_cargo_oled_shows_setup_and_service_pages(): assert 'oledClear("Network")' in source assert 'oledClear("Alarms")' in source app_config = Path("firmware/esp32/overland-controller/app_config.h").read_text() - if 'HARDWARE_PROFILE "lilygo_t_relay_s3_6ch"' not in app_config: + if 'lilygo_t_relay_s3_6ch' not in app_config: assert "oledShowSetupPage();" in source assert "oledLoop();" in source From fece1f39d0d03a39375fbf27c73612aa78a688fa Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 2 Jul 2026 00:27:13 -0600 Subject: [PATCH 38/38] test: skip OLED setup assertion for LilyGO cargo board --- tests/test_http_api_contract.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_http_api_contract.py b/tests/test_http_api_contract.py index 9b5283b..844e24d 100644 --- a/tests/test_http_api_contract.py +++ b/tests/test_http_api_contract.py @@ -475,8 +475,9 @@ def test_cargo_oled_shows_setup_and_service_pages(): assert 'oledClear("Relays")' in source assert 'oledClear("Network")' in source assert 'oledClear("Alarms")' in source - app_config = Path("firmware/esp32/overland-controller/app_config.h").read_text() - if 'lilygo_t_relay_s3_6ch' not in app_config: + config = Path("firmware/esp32/overland-controller/config.h").read_text() + oled_disabled = "OLED_SETUP_ENABLED 0" in config or "LILYGO_T_RELAY_S3" in config + if not oled_disabled: assert "oledShowSetupPage();" in source assert "oledLoop();" in source