Skip to content

feat: Advanced Web UI Dashboard with Live Map, Route Resolution, and Dynamic Settings - #33

Open
iboguslavsky wants to merge 1 commit into
MatixYo:mainfrom
iboguslavsky:feature/advanced-web-dashboard
Open

feat: Advanced Web UI Dashboard with Live Map, Route Resolution, and Dynamic Settings#33
iboguslavsky wants to merge 1 commit into
MatixYo:mainfrom
iboguslavsky:feature/advanced-web-dashboard

Conversation

@iboguslavsky

Copy link
Copy Markdown

Summary

This PR introduces a comprehensive, dark-themed Web UI dashboard to monitor local ADS-B airspace from a browser, fully synchronized with the physical GC9A01 radar display. It includes live interactive mapping, airline route resolution, and real-time NVS configuration updates without requiring device reboots.

Key Features

  • Live Interactive Radar Map: Integrates Leaflet.js to render a responsive, dark-themed map in the browser. Planes are plotted dynamically as SVG icons rotated to match their actual heading. The heavy Haversine trigonometric distance/bearing math is offloaded directly to the browser client.
  • Airline Route Fetcher: Adds an asynchronous RouteFetcher service that automatically resolves cryptic ADS-B IATA codes (e.g. DEN-LGA) into full city names (e.g. Denver - New York). The data is securely cached using RTOS Semaphores to ensure thread-safety on the ESP32 without crashing the LwIP network stack.
  • Metric/Imperial Toggle: Added a dynamic Unit System toggle allowing users to instantly swap between Metric (km/h, m) and Imperial (kts, ft) measurements on both the physical display and the Web UI table.
  • Radar Range Control: Exposes the physical ring radar range to the web settings panel, letting users dynamically zoom the physical display in and out from 5km to 25km remotely.
  • Browser GPS Integration: Users can click "Use Browser GPS" to grant browser geolocation access. The Web UI instantly captures their Latitude/Longitude, POSTs it to the ESP32 API, updates the NVS flash memory, and automatically recenters the map.

Technical Improvements

  • Memory & DMA Optimizations: Migrated the Web UI's HTML payload to PROGMEM to drastically reduce SRAM fragmentation during early boot, preventing LovyanGFX DMA buffer allocation failures.
  • Network Stack Stability: Refactored WebServer initialization to explicitly wait until wifiSetupConnect() has successfully resolved an IP, completely eliminating xQueueSemaphoreTake boot loop crashes inside api_msg.c.
  • REST API: Added asynchronous /api/planes and /api/config GET/POST endpoints that serve lightweight ArduinoJson payloads to drive the frontend.

@Reid-n0rc

Copy link
Copy Markdown

Hardware & testing methodology: ESP32-C3 Super Mini (esp32-c3-devkitm-1, RISC-V, 320KB RAM), 1.28" round GC9A01 240×240 SPI display, USB-C native serial. Built with PlatformIO, espressif32@6.5.0, Arduino core framework-arduinoespressif32@3.20014.231204. Connected to a real WiFi AP with live ADS-B traffic from opendata.adsb.fi; dashboard reachable over LAN. This PR needed by far the most extensive testing: monitored USB serial continuously across many flash cycles (~2+ hours total) to catch crash/reboot banners; exercised /api/config and /api/planes directly via curl (GET and POST) independent of any browser; simulated the dashboard's periodic polling with scripted repeated requests over 45–100 second windows to check for degradation under sustained use; soak-tested the route-lookup feature specifically by counting ESP-ROM boot banners in the serial stream (to detect silent reboots) and SSL - Memory allocation failed / stuck-write error counts (as heap-health indicators) across multiple fix iterations.

Results: several real bugs, fixed locally in my integration build (not yet upstreamed):

1. Wrong board targetedplatformio.ini and include/config.h:

; This PR's platformio.ini
[env:xiao_esp32c6]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
board = seeed_xiao_esp32c6
// This PR's config.h — different pins entirely
constexpr gpio_num_t kDisplayPinRst = GPIO_NUM_16;
constexpr gpio_num_t kDisplayPinCs  = GPIO_NUM_17;
// ...

Silently retargets the whole build to a Seeed XIAO ESP32C6 with different GPIO pins — incompatible with the repo's actual default board. I reverted both files to the existing [env:supermini] / ESP32-C3 Super Mini config and kept only the dashboard's actual code changes.

2. Build breaks on this repo's pinned Arduino core (espressif32@6.5.0), src/services/route_fetcher.cpp and src/services/web_server.cpp:

// route_fetcher.cpp
- #include <NetworkClientSecure.h>
+ #include <WiFiClientSecure.h>
...
- NetworkClientSecure client;
+ WiFiClientSecure client;

// web_server.cpp
- s_server = std::make_unique<WebServer>(80);
+ s_server.reset(new WebServer(80));

NetworkClientSecure.h and std::make_unique aren't available on this repo's pinned core.

3. Port conflict, src/services/web_server.cpp:

- s_server.reset(new WebServer(80));
+ constexpr uint16_t kDashboardPort = 8080;
+ s_server.reset(new WebServer(kDashboardPort));

The new dashboard and the existing WiFiManager LAN portal (from #45) both bound port 80, racing for the socket — whichever started first won, the other became silently unreachable.

4. Crash serving the dashboard page, src/services/web_server.cpp handleRoot():

// Before (crashes): copies the ~18KB PROGMEM page into a fresh heap String
s_server->send(200, "text/html", kWebUiHtml);

// After: stream in 2KB chunks, bail out if the client drops or after 5s
constexpr size_t kChunkSize = 2048;
constexpr unsigned long kMaxServeMs = 5000;
const unsigned long deadline = millis() + kMaxServeMs;
const size_t total = strlen_P(kWebUiHtml);
s_server->setContentLength(total);
s_server->send(200, "text/html", "");
for (size_t sent = 0; sent < total; sent += kChunkSize) {
  if (!s_server->client().connected() || millis() >= deadline) break;
  const size_t n = std::min(kChunkSize, total - sent);
  s_server->sendContent_P(kWebUiHtml + sent, n);
}

send() copying 18KB into a heap String competes with the display's ~115KB frame buffer for contiguous heap and crashed the device (confirmed on hardware: physical display blanks on reboot, browser gets an empty response). Switching to send_P alone avoided the copy but then hit ESP32's small default TCP send buffer — a single write() bigger than the buffer gets EAGAIN forever instead of a partial write, so the request just hung. Chunking fixes both.

5. Route-lookup feature (Airport Data Mode) is not viable on this board. After several rounds of fixes — not caching transient failures as permanent, removing an over-aggressive heap gate that silently blocked every fetch attempt, adding a mutex to fully serialize TLS use against the main ADS-B fetch, and reusing the TLS client instead of rebuilding it per request — route lookups to api.adsbdb.com still fail with SSL - Memory allocation failed on nearly every attempt and caused at least one hard reboot during testing, even fully serialized against the main fetch (which itself keeps working fine to a different host). This looks like a genuine memory ceiling on the ESP32-C3's 320KB RAM for this specific TLS handshake, not a scheduling bug. I left the feature defaulted off (AirportDataMode::NONE) and would recommend the same upstream, or gating it behind a board with more RAM.

The core dashboard (live map, aircraft table, settings save) works well once #1#4 are fixed. Happy to share my local diffs if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants