Summary
WPA2-Enterprise (802.1X) networks cannot be joined, and the way it fails is misleading. firmware/sdkconfig.defaults:44 sets
CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT=n
With that off, the esp_eap_client_* functions compile to stubs that return ESP_OK, so nothing reports an error, but the station never leaves PSK mode. Every association then fails with reason 210 (WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY) even though the scan found the AP:
I (582903) WifiStation: Found AP: <SSID>, BSSID: ee:38:83:6b:2c:a2, RSSI: -72, Channel: 1, Authmode: 5
I (582963) WifiBoard: WiFi connecting to <SSID>
W (585373) StackChanEap: disconnected from <SSID>, reason=210
I (585373) WifiStation: Reconnecting <SSID> (attempt 1 / 5)
Authmode: 5 is WIFI_AUTH_WPA2_ENTERPRISE, so the AP is clearly identified as enterprise; the station just has no EAP support to answer with. I spent a while chasing PMF and scan settings before finding the sdkconfig line, so I think this is worth an issue even if you decide not to support enterprise networks — the silent ESP_OK is what makes it hard to diagnose.
It works once enterprise support is enabled
I have a working build on a CoreS3 (K151) with ESP-IDF v5.5.4 joining a PEAP/MSCHAPv2 network, reconnecting on reboot, and reaching MQTT and OTA normally:
I (162868) esp_netif_handlers: sta ip: 192.168.17.181, mask: 255.255.252.0, gw: 192.168.16.1
I (162868) WifiBoard: Connected to WiFi: <SSID>
I (170978) MQTT: Connected to endpoint
The firmware side is small: three esp_eap_client_set_* calls plus esp_wifi_sta_enterprise_enable() before starting the station, and wpa_supplicant added to the component requirements. I put it behind a Kconfig option that does select ESP_WIFI_ENTERPRISE_SUPPORT, defaulting to n, so the flash cost is only paid by people who ask for it. Enabling it adds roughly 34 KB.
Questions before I send a PR
I would rather agree on the shape first than send something you would have to redesign.
1. How should credentials reach the device?
- (a) Extend the BLE
setWifi message with optional identity and username fields. A message without username takes the existing PSK path unchanged, so the current app keeps working. This is what I am running now, driven by a small Python BLE client. It needs matching fields in the Flutter app to be usable by normal users.
- (b) Keep it build-time only: put the credentials in Kconfig for people who build their own firmware, and leave the app alone.
2. Where should the enterprise credentials be stored?
SsidManager comes from 78/esp-wifi-connect, and SsidItem only holds an SSID and a password:
// managed_components/78__esp-wifi-connect/include/ssid_manager.h:7-10
struct SsidItem {
std::string ssid;
std::string password;
};
The identity and username have nowhere to go there, so after a reboot the normal WifiBoard::StartNetwork() path retries as PSK and fails.
- (a) Store them in a StackChan-owned NVS namespace keyed by SSID, and apply them in the
StartNetwork() override in firmware/main/hal/board/stackchan.cc. Self-contained in this repository. This is what I am running.
- (b) Extend
SsidItem upstream in 78/esp-wifi-connect. Arguably the right home, but it means a PR there, a release, and a dependency bump here before anything can land.
I lean towards (a) for both, but the second one in particular feels like your call, since managed_components/ is regenerated from dependencies.lock and a local patch would not survive.
One thing I could not test
I cannot verify any app-side change end to end, because stackChanBluePrivateKey in app/lib/util/value_constant.dart:311-312 is an empty string in this repository. select_blue_device.dart:174 tries an RSA-OAEP decrypt with it, the key parse throws, and activateDevice() is never reached, so the official app cannot complete pairing when built from source. If you want the app-side fields as well, I can write them and confirm flutter analyze and the build, but I would have to say plainly in the PR that the flow itself is unverified.
Summary
WPA2-Enterprise (802.1X) networks cannot be joined, and the way it fails is misleading.
firmware/sdkconfig.defaults:44setsWith that off, the
esp_eap_client_*functions compile to stubs that returnESP_OK, so nothing reports an error, but the station never leaves PSK mode. Every association then fails with reason 210 (WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY) even though the scan found the AP:Authmode: 5isWIFI_AUTH_WPA2_ENTERPRISE, so the AP is clearly identified as enterprise; the station just has no EAP support to answer with. I spent a while chasing PMF and scan settings before finding the sdkconfig line, so I think this is worth an issue even if you decide not to support enterprise networks — the silentESP_OKis what makes it hard to diagnose.It works once enterprise support is enabled
I have a working build on a CoreS3 (K151) with ESP-IDF v5.5.4 joining a PEAP/MSCHAPv2 network, reconnecting on reboot, and reaching MQTT and OTA normally:
The firmware side is small: three
esp_eap_client_set_*calls plusesp_wifi_sta_enterprise_enable()before starting the station, andwpa_supplicantadded to the component requirements. I put it behind a Kconfig option that doesselect ESP_WIFI_ENTERPRISE_SUPPORT, defaulting ton, so the flash cost is only paid by people who ask for it. Enabling it adds roughly 34 KB.Questions before I send a PR
I would rather agree on the shape first than send something you would have to redesign.
1. How should credentials reach the device?
setWifimessage with optionalidentityandusernamefields. A message withoutusernametakes the existing PSK path unchanged, so the current app keeps working. This is what I am running now, driven by a small Python BLE client. It needs matching fields in the Flutter app to be usable by normal users.2. Where should the enterprise credentials be stored?
SsidManagercomes from78/esp-wifi-connect, andSsidItemonly holds an SSID and a password:The identity and username have nowhere to go there, so after a reboot the normal
WifiBoard::StartNetwork()path retries as PSK and fails.StartNetwork()override infirmware/main/hal/board/stackchan.cc. Self-contained in this repository. This is what I am running.SsidItemupstream in78/esp-wifi-connect. Arguably the right home, but it means a PR there, a release, and a dependency bump here before anything can land.I lean towards (a) for both, but the second one in particular feels like your call, since
managed_components/is regenerated fromdependencies.lockand a local patch would not survive.One thing I could not test
I cannot verify any app-side change end to end, because
stackChanBluePrivateKeyinapp/lib/util/value_constant.dart:311-312is an empty string in this repository.select_blue_device.dart:174tries an RSA-OAEP decrypt with it, the key parse throws, andactivateDevice()is never reached, so the official app cannot complete pairing when built from source. If you want the app-side fields as well, I can write them and confirmflutter analyzeand the build, but I would have to say plainly in the PR that the flow itself is unverified.