Skip to content

Device reboots when the companion app disconnects during WiFi setup #116

Description

@isaka1022

Summary

If the companion app disconnects while the setup wizard is on the app-connection screen, the device asserts inside esp_netif_create_default_wifi_sta() and reboots. The wizard restarts from the beginning, so setup cannot be finished until the app stays connected for the whole flow.

Why it happens

StackChanWifiStation::Start() is guarded by is_started_, but that flag is an instance member while the resources the function creates are process-wide:

// firmware/main/hal/utils/wifi_connect/wifi_station.cc:120
void StackChanWifiStation::Start()
{
    if (is_started_) {
        return;
    }

    esp_netif_init();
    esp_event_loop_create_default();

    station_netif_ = esp_netif_create_default_wifi_sta();
    ...
    is_started_ = true;
}

esp_netif_create_default_wifi_sta() registers a netif under the fixed key WIFI_STA_DEF, so a second call fails and the assert on wifi_default.c:422 fires.

A second call is reachable through the normal wizard flow, because the object is recreated rather than reused:

  • WifiConfigServer::init() (hal_ble.cpp:356) constructs a fresh StackChanWifiStation and calls Start() on it (hal_ble.cpp:378).
  • AppConfigServerWorker::onCreate() (hal_ble.cpp:531) constructs a fresh WifiConfigServer, and Hal::startAppConfigServer() (hal_ble.cpp:554) creates a new worker on every call.
  • connectivity.cpp:212-213 switches back to State::WaitAppConnection on AppConfigEvent::AppDisconnected, and that state calls startAppConfigServer() again at connectivity.cpp:150.

So on disconnect the wizard builds a second worker, a second WifiConfigServer and a second StackChanWifiStation whose is_started_ is false, and Start() runs its body a second time. The previous worker is not destroyed either, so ble_init(true) also runs again and the controller init fails just before the assert.

Steps to reproduce

  1. Erase NVS (or use a device that has not been set up) so the setup wizard runs.
  2. Advance the wizard to the app-connection screen.
  3. Connect to the BLE service e2e5e5ff-1234-5678-1234-56789abcdef0 from the app, or from any BLE client.
  4. Disconnect.

Log

[WifiConfigServer] app Connected
...
[WifiConfigServer] app Disconnected
[HAL-BLE] start app config server
[HAL-BLE] init
I (989571) NimBLE: Stack-Chan callbacks registered
E (989571) BLE_INIT: controller init failed
E (989571) NimBLE_BLE_PRPH: Failed to init nimble 259
[HAL-BLE] init done, factory mac: 80:45:6b:4d:3d:14
E (989601) esp_netif_lwip: esp_netif_new_api: Failed to configure netif with config=0x3fcc0170 (config or if_key is NULL or duplicate key)

assert failed: esp_netif_create_default_wifi_sta wifi_default.c:422 (netif)

Backtrace: 0x4038ef11:0x3fcc0010 0x4038eed9:0x3fcc0030 0x40396c4d:0x3fcc0050 0x42096f73:0x3fcc0170 0x4206745d:0x3fcc01a0 0x42057fd5:0x3fcc0260 0x42058433:0x3fcc02b0 0x42234c15:0x3fcc02e0 0x42097953:0x3fcc0300 0x42097a0d:0x3fcc0320 0x42097a55:0x3fcc0350 0x4206c971:0x3fcc0370 0x42262a23:0x3fcc03b0

Rebooting...

Environment: CoreS3 (K151), ESP-IDF v5.5.4, firmware built from main at 1b57655 with local changes elsewhere in the tree. wifi_station.cc, connectivity.cpp and the AppConfigServerWorker / WifiConfigServer code quoted above are unmodified in that build.

Note on fixing it

I did not open a PR because the obvious one-line change does not look right on its own. Promoting is_started_ to file scope stops the assert, but the second StackChanWifiStation would then have no event handlers, since Start() is also where they are registered with this as the argument:

esp_event_handler_instance_register(
    WIFI_EVENT, ESP_EVENT_ANY_ID, &StackChanWifiStation::WifiEventHandler, this, &instance_any_id_);

The new instance would never see a connect or disconnect event, so the app would sit on wifiConnecting forever instead of rebooting. Reusing one station across restarts of the config server, or tearing the old worker down before creating a new one, both look plausible, but which one you want depends on how you intend the config server to be scoped. Happy to send a PR if you tell me which shape you prefer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions