Skip to content

Device setup improvments (Device identity, WiFi provisioning, MQTT autodiscovery) - #14

Merged
Konkzor merged 18 commits into
mainfrom
feature/setup-refactor
Sep 11, 2026
Merged

Konkzor merged 18 commits into
mainfrom
feature/setup-refactor

Conversation

@Konkzor

@Konkzor Konkzor commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Summary

This PR refactors Linkey setup around runtime provisioning and per-device identity.

It removes build-time WiFi credentials, adds BLE WiFi provisioning through the Espressif provisioning app, introduces per-device factory secrets for BLE PoP and MQTT password, and derives the default device name, MQTT username, and MQTT topics from the ESP32 WiFi MAC suffix. It also adds Home Assistant MQTT broker discovery via mDNS with a configurable fallback URI.

Main Changes

WiFi

  • Replace build-time WiFi credentials with BLE provisioning through the Espressif app.
  • Store WiFi credentials in ESP-IDF NVS.
  • Add BOOT/GPIO0 long press to manually re-enter provisioning.

MQTT

  • Derive default MQTT username and topics from the device MAC suffix.
  • Add Home Assistant broker discovery via mDNS, with a fallback MQTT URI.
  • Read the MQTT password from per-device factory data when available.

Factory Setup

  • Add a factory_data NVS partition for per-device secrets.
  • Add tools/factory_flash.py to generate/reuse secrets, flash factory data, and produce setup information.
  • Add a quick-start guide template for installation handoff.

CI / Tests / Docs

  • Update QEMU MQTT tests for authenticated, per-device topics.
  • Add QEMU coverage for manual provisioning trigger.
  • Extend CI firmware builds to cover debug logs.
  • Document the new provisioning, factory flashing, and MQTT setup flows.

What is needed next

  • PCB and casing revision to better integrate the button and its new use

Konkzor added 18 commits August 6, 2026 22:05
Enable ESP WiFi NVS storage so provisioned station credentials are loaded by the driver.

Stop starting WiFi during wifi_init(); keep radio startup in wifi_start() so the FSM controls power use.

Add helpers to detect stored credentials and synchronize state when another component starts WiFi.
Enable ESP32 BLE/Bluedroid support and the larger single-app partition defaults needed for provisioning.

Add the qrcode managed component and lockfile so debug builds can print provisioning QR codes.

Ignore managed_components because the component manager regenerates it from the lockfile.
Add a BLE provisioning FSM state that starts when no WiFi credentials are stored.

Store credentials through the standard Espressif provisioning app without running immediate WiFi validation.

Replace WiFi scan requests with an empty low-power scan endpoint because scanning is not supportable on the power budget.

Print the provisioning URL and emit the terminal QR code only in debug builds.
Describe the BLE provisioning flow, including manual SSID entry, disabled WiFi scans, and deferred WiFi validation.

Update menuconfig setup instructions now that WiFi credentials are provisioned instead of built in.

Document debug QR code output and the qrcode component dependency.
Before only a unique white flash was done before entering provisoning.

Now, a 10ms/0,5Hz flash is emitted.
Poll the BOOT button from the existing FSM loop so long-press detection does not add an always-running task or extra wakeups.

Enter BLE provisioning after GPIO0 is held for at least two seconds, and require release before accepting another trigger.

Keep existing WiFi credentials when manual provisioning fails so stored credentials are only replaced after the provisioning app submits new ones.
Run NVS, LED, ADC, and power-management initialization once before entering the main state loop so INIT no longer participates in state LED blinking.

Remove STATE_INIT from the pure FSM model and update voltage watchdog tests to reflect that WAIT_VOLTAGE is now the first runtime state.

Update the firmware README diagram, state table, and test description to match the init-plus-FSM boot flow.
Keep normal QEMU runs out of the manual provisioning path by wrapping GPIO0 as released by default and by mocking wifi_has_config() instead of calling the real WiFi driver state API.

Exclude the real BLE provisioning implementation from QEMU builds and provide an emulation stub so manual provisioning requests can be observed without pulling in unsupported BLE runtime behavior.

Add a manual provisioning QEMU mode, build-only support in the runner, a CI job, and a log assertion that verifies the simulated BOOT long press reaches the provisioning stub without panic/assert markers.
- MQTT discovery: discover Home Assistant via _home-assistant._tcp.local, build mqtt://<ha-ip>:1883, and fall back to mqtt://homeassistant.local:1883.

- MQTT topics: remove the configurable topic prefix and derive runtime topics as linkey/<mac_suffix>/state, status, and debug paths.

- MQTT username: use linkey_<mac_suffix> by default when MQTT Username is left empty, while keeping an explicit Kconfig username override.

- Add the espressif/mdns managed dependency and disable mDNS autodiscovery in QEMU defaults.

- Update QEMU MQTT assertions, CI probes, and documentation for MAC-derived identity and topics.
Add a factory_data NVS partition and a shared factory_config_schema.h that owns the factory partition label, namespace, NVS key names, and development fallback secrets.

Read MQTT password and BLE provisioning PoP from the factory partition when available, with empty Kconfig values acting only as development overrides instead of owning defaults.

Keep normal firmware logs from printing provisioning credentials; QR/provisioning URL output remains debug-log only.
Add a Python factory flashing tool that reads the ESP32 MAC, generates or reuses per-device MQTT and BLE provisioning secrets, creates the factory NVS image, flashes factory_data, and prints install information for Home Assistant/provisioning.

Read the factory schema from factory_config_schema.h so the Python tool does not duplicate partition, namespace, key, or fallback definitions.

Keep the ESP32 in ROM bootloader after MAC read and flash factory_data before the final firmware flash, avoiding a second manual BOOT/reset cycle on boards without reliable auto-boot.

Ignore generated per-device factory files and the isolated build-factory directory.
Describe the factory_data NVS partition, the per-device BLE PoP and MQTT password keys, and how tools/factory_flash.py generates, flashes, and prints the installation information.

Clarify that Kconfig password and PoP values are optional development overrides, while factory_config_schema.h owns the fallback values and factory_data overrides them in production.
Configure CI Mosquitto to reject anonymous clients and require the deterministic QEMU credentials linkey_ddeeff / mqtt-password, matching the mocked MAC suffix and factory schema fallback.

Authenticate CI probe, subscription capture, and debug request publishing so the smoke test fails if firmware MQTT auth regresses.

Align the local Docker Mosquitto setup and emulation docs with the same credential-gated broker behavior.

Add a lightweight CI validation step for factory_flash.py schema parsing so schema/header changes cannot silently break the factory tool.
Make the generated Mosquitto password file world-readable so the broker can open it after dropping privileges to its runtime user.
- Rename BLE provisioning service names from Linkey-XXXXXX to Linkey_XXXXXX in firmware and newly generated factory setup data.

- Make the Home Assistant MQTT discovery device name default to Linkey_<last6mac> when no explicit menuconfig override is set.

- Document the BLE and MQTT/Home Assistant naming behavior so multiple Linkey devices get distinct names.
- Add a two-sided quick start guide HTML template with Linkey branding, provisioning QR placement, and MQTT credential placeholders.

- Extend factory_flash.py to generate per-device quick_start.html and quick_start.pdf next to the existing setup text file.

- Resolve logo and app badge assets to local file URIs before PDF rendering so headless Chrome embeds them reliably.

- Include local Google Play and App Store badge assets and add --chrome / --no-guide-pdf controls for the generator.
@Konkzor
Konkzor force-pushed the feature/setup-refactor branch from 9ae004e to 7d22ed6 Compare September 7, 2026 20:48
@Konkzor
Konkzor merged commit b286989 into main Sep 11, 2026
20 checks passed
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.

1 participant