Files
kvida-os/components/transport/README.md
Ronny Eia af0c2da146 Add Wi-Fi commissioning: SoftAP captive portal with IPv6 support
Implements first-boot Wi-Fi setup with no companion app required: a
SoftAP + captive portal (DNS wildcard redirect, DHCP option 114, HTTP
form) lets the user submit Wi-Fi credentials from any browser. On
submit, credentials are saved to NVS and the device switches to STA
mode; on subsequent boots it reconnects automatically via stored
credentials. dns_server.c/.h are vendored from ESP-IDF's official
captive_portal example (Unlicense/CC0).

Requests both link-local and global IPv6 addresses once the STA
connection has an IPv4 address (not on WIFI_EVENT_STA_CONNECTED --
esp_netif_create_ip6_linklocal() reliably fails there because
esp-netif's own handler, which marks the lwIP netif "up", hasn't run
yet due to event-handler registration order). Verified end-to-end on
real ESP32-C6 hardware: SoftAP comes up, credential submission works,
STA reconnects on reboot, and both a link-local and a router-assigned
global IPv6 address are obtained via SLAAC.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 21:24:38 +02:00

2.8 KiB

transport

Wi-Fi connectivity, MQTT client, and Home Assistant MQTT Discovery payloads. Exposes a semantic publish(topic, value)-style API — application code (including Lua) never depends on MQTT directly, per the transport abstraction principle in AGENTS.md. No dependency on other Kvida components.

Also owns Wi-Fi/MQTT configuration storage for now (not broken out into its own component since AGENTS.md doesn't call out a separate config layer — revisit if this grows).

Future transports (Zigbee, Thread, Matter) should implement the same publish API as alternate backends behind this component's interface, without changing callers.

Wi-Fi commissioning

include/commissioning.h / src/commissioning.cpp implement first-time Wi-Fi setup, no companion app required:

  • On boot, try_stored_credentials() attempts to connect using credentials saved in NVS from a previous session.
  • If none exist (or the caller chooses to), start_commissioning() opens a SoftAP (Kvida-XXXXXX, derived from the MAC) with a captive portal: a DNS server (src/dns_server.c/.h, vendored from ESP-IDF's official captive_portal example, Unlicense/CC0) answers every query with the AP's own IP, and DHCP option 114 plus a 404-redirects-to-/ HTTP handler get most phones/laptops to auto-open the setup page (src/portal.html) in a plain browser.
  • Submitting the form (POST /connect) saves the credentials to NVS and switches Wi-Fi to STA mode. The AP is open (no password) by design -- see the comment above ap_config.ap.authmode in commissioning.cpp for the reasoning and the tradeoff.
  • A 5-minute esp_timer closes the commissioning window automatically if nobody submits credentials.
  • IPv6: once STA gets its IPv4 address (IP_EVENT_STA_GOT_IP -- not WIFI_EVENT_STA_CONNECTED, see the comment in commissioning.cpp for why that timing matters), esp_netif_create_ip6_linklocal() requests a link-local address (CONFIG_LWIP_IPV6=y is set explicitly in sdkconfig.defaults); IP_EVENT_GOT_IP6 is logged when it arrives. Verified end-to-end on real ESP32-C6 hardware.
  • Not yet wired up: re-entering commissioning on an already-configured device needs a physical trigger (the user button) per AGENTS.md's "configuration mode requires physical user interaction" principle -- blocked on drivers having a real button implementation. Network scanning (SSID dropdown instead of free text) was left out of this first pass to keep scope tight.

TODO: built on ESP-IDF's own esp_wifi and mqtt_client (esp-mqtt) components once real code lands — both ship with ESP-IDF, no extra registry dependency needed.

TODO: A/B OTA update-checking belongs here, driven by ESP-IDF's native esp_ota_ops against the ota_0/ota_1 partitions in partitions.csv — no extra dependency needed either.