-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Summary
I am facing a WPA2-Enterprise issue where some ESP-based luminaires repeatedly re-associate and re-authenticate approximately every 30 seconds on a customer network.
All devices run exactly the same firmware, using WPA2-Enterprise in STA mode only. However:
- Some devices stay connected and stable.
- Other devices on the same site, same SSID, and same firmware keep going through association + 802.1X authentication every ~30 seconds.
The customer’s Cisco WLAN controller keeps these devices in an "Authenticating" state and shows that the whole association/authentication sequence is repeated periodically. Their network team suspects this behavior is being triggered by the client module itself (Espressif), not by the AP or the controller.
I would like to confirm that our WPA2-Enterprise implementation is correct and understand whether there is any known issue or configuration detail on the ESP side that could cause this behavior.
Environment
- Module / SoC: ESP32-WROOM-32UE
- ESP-IDF version: 5.0
- Wi-Fi mode: STA
- Authentication: WPA2-Enterprise (EAP, username/password)
Behavior observed on the WLAN controller
On the Cisco controller, when the issue occurs they see the client:
- Associate to the AP (association success).
- Start 802.1X / DOT1X L2 authentication.
- Stay in an Authenticating state.
- After ~30 seconds, the same sequence repeats (association + 802.1X).
They shared the following log sequence, which repeats every 30 seconds while the client is in "Authenticating":
2025/11/05 10:23:12.940520677 {wncd_x_R0-0}{1}: [client-orch-sm] [15594]: (note): MAC: a4e5.7c54.6a78 Association received. BSSID e438.7e11.c963, WLAN BWSIA2, Slot 0 AP e438.7e11.c960, vtt-ap-166114, Site tag ES_Vtt, Policy tag APG_Vtt, Policy profile BWSIA2, Switching Central, old BSSID e438.7e11.c963, Socket delay 0ms
2025/11/05 10:23:12.940620698 {wncd_x_R0-0}{1}: [client-orch-state] [15594]: (note): MAC: a4e5.7c54.6a78 Client state transition: S_CO_L2_AUTH_IN_PROGRESS -> S_CO_L2_AUTH_IN_PROGRESS
2025/11/05 10:23:12.940997973 {wncd_x_R0-0}{1}: [dot11] [15594]: (note): MAC: a4e5.7c54.6a78 Association success. AID 2, Roaming = False, WGB = False, 11r = False, 11w = False Fast roam = False
2025/11/05 10:23:12.941302122 {wncd_x_R0-0}{1}: [client-orch-state] [15594]: (note): MAC: a4e5.7c54.6a78 Client state transition: S_CO_L2_AUTH_IN_PROGRESS -> S_CO_L2_AUTH_IN_PROGRESS
2025/11/05 10:23:12.941624216 {wncd_x_R0-0}{1}: [client-auth] [15594]: (note): MAC: a4e5.7c54.6a78 ADD MOBILE sent. Client state flags: 0x71 BSSID: MAC: e438.7e11.c963 capwap IFID: 0x9000002a, Add mobiles sent: 1
2025/11/05 10:23:12.964827607 {wncd_x_R0-0}{1}: [client-auth] [15594]: (note): MAC: a4e5.7c54.6a78 L2 Authentication initiated. method DOT1X, Policy VLAN 62, AAA override = 0 , NAC = 0
2025/11/05 10:23:12.971668901 {iosrp_R0-0}{1}: [pki] [4191]: (note): CRYPTO_PKI: Begin local cert chain retrieval.
2025/11/05 10:23:12.971688741 {iosrp_R0-0}{1}: [pki] [4191]: (note): CRYPTO_PKI: Done with local cert chain fetch 0.
According to them, this looks like a client-side reconnection loop, not something driven by the controller.
My WPA2-Enterprise implementation
This project is a pure STA configuration (point–multipoint) using WPA2-Enterprise.
Below is the main code that manages Wi-Fi connectivity and WPA2-Enterprise settings:
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
esp_netif_dns_info_t dns;
char url[128];
if (event_base == WIFI_EVENT) {
switch (event_id) {
case WIFI_EVENT_STA_START: {
esp_wifi_connect();
} break;
case WIFI_EVENT_STA_DISCONNECTED: {
wifi_event_sta_disconnected_t *disconn = (wifi_event_sta_disconnected_t*)event_data;
if (disconn->reason == WIFI_REASON_ROAMING) {
ESP_LOGI(MAIN_TAG, "station roaming, do nothing");
} else {
set_led(0);
mqtt_stop();
esp_wifi_connect();
}
} break;
case WIFI_EVENT_STA_CONNECTED: {
ESP_LOGI(MAIN_TAG, "WiFi conectado correctamente al AP");
set_led(2);
if (enableBT == 1) ble_start_scan();
} break;
case WIFI_EVENT_AP_START: {
} break;
case WIFI_EVENT_AP_STOP: {
} break;
case WIFI_EVENT_AP_STACONNECTED: {
wifi_event_ap_staconnected_t *sta_conn = (wifi_event_ap_staconnected_t*)event_data;
add_client(sta_conn->mac);
} break;
case WIFI_EVENT_AP_STADISCONNECTED: {
wifi_event_ap_stadisconnected_t *sta_dis = (wifi_event_ap_stadisconnected_t*)event_data;
remove_client(sta_dis->mac);
} break;
}
} else if (event_base == IP_EVENT) {
switch (event_id) {
case IP_EVENT_STA_GOT_IP: {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(MAIN_TAG, ">>>>>> IP_EVENT_STA_GOT_IP <<<<<<");
sprintf(IPV4_ADDRESS_STR, IPSTR, IP2STR(&event->ip_info.ip));
sprintf(IPV4_PARENT_ADDRESS_STR, IPSTR, IP2STR(&event->ip_info.gw));
ESP_LOGI(MAIN_TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
// Establecer DNS principal usando esp_netif_set_dns_info
if (esp_netif_get_dns_info(wifiSTA, ESP_NETIF_DNS_MAIN, &dns) == ESP_OK) {
esp_netif_dns_info_t dns_info;
dns_info.ip.u_addr.ip4 = dns.ip.u_addr.ip4;
dns_info.ip.type = IPADDR_TYPE_V4;
ESP_ERROR_CHECK(esp_netif_set_dns_info(wifiSTA, ESP_NETIF_DNS_MAIN, &dns_info));
ESP_LOGI(MAIN_TAG, "DNS configurado a:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
}
s_retry_num = 0;
if (detectedILWIFI) {
sprintf(url, "%s", "mqtt://setup.digitaliot.es:2883");
} else {
sprintf(url, "%s", MQTT_URL);
}
mqtt_start(url, group, topics_handler, mqtt_status_handler);
} break;
}
}
}void init_wifi() {
static ip_addr_t dnsserver;
.
.
.
.
.
static wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT();
uint32_t IL_AP_IP = ipaddr_addr("10.8.0.1");
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
wifiSTA = esp_netif_create_default_wifi_sta();
wifiAP = esp_netif_create_default_wifi_ap();
ESP_ERROR_CHECK(esp_wifi_init(&config));
/* INICIAO ESCANEO DE REDES WIFI */
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_start());
#define DEFAULT_SCAN_LIST_SIZE 16
uint16_t number = DEFAULT_SCAN_LIST_SIZE;
wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE];
uint16_t ap_count = 0;
memset(ap_info, 0, sizeof(ap_info));
esp_wifi_disconnect();
esp_wifi_scan_start(NULL, true);
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
ESP_LOGI("SCAN******", "\n\n\n");
ESP_LOGI("SCAN******", "Total APs scanned = %u", ap_count);
for (int i = 0; (i < DEFAULT_SCAN_LIST_SIZE) && (i < ap_count); i++) {
ESP_LOGI("SCAN", "SSID \t\t%s", ap_info[i].ssid);
ESP_LOGI("SCAN", "RSSI \t\t%d", ap_info[i].rssi);
ESP_LOGI("SCAN", "Channel \t\t%d\n", ap_info[i].primary);
if (strcmp((const char*)ap_info[i].ssid,IL_AP_SSID)==0) detectedILAP = 1;
if (strcmp((const char*)ap_info[i].ssid,IL_WIFI_SSID)==0) detectedILWIFI = 1;
}
ESP_ERROR_CHECK(esp_wifi_stop());
/* FIN ESCANEO WIFI */
/* CONFIGURACION FINAL DE LA WIFI. */
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,IP_EVENT_STA_GOT_IP,&event_handler,NULL,NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,ESP_EVENT_ANY_ID,&event_handler,NULL,NULL));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW,&esp_bss_rssi_low_handler, NULL));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));//WIFI_STORAGE_FLASH;
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_MIN_MODEM));
if (enableAP) ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA) );
else ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
/* Configuration de la STA. */
bzero(&wifi_config, sizeof(wifi_config_t));
wifi_config.sta.pmf_cfg.capable = true;
wifi_config.sta.pmf_cfg.required = false;
wifi_config.sta.threshold.rssi = -127;
wifi_config.sta.bssid_set = false;
sprintf(IPV4_PARENT_ADDRESS_STR,"0.0.0.0");
sprintf(IPV4_ADDRESS_STR,"0.0.0.0");
if (detectedILWIFI) {
memcpy(wifi_config.sta.ssid, IL_WIFI_SSID, strlen(IL_WIFI_SSID));
wifi_config.sta.ssid[strlen(IL_WIFI_SSID)]=0;
memcpy(wifi_config.sta.password, IL_WIFI_PASSWD, strlen(IL_WIFI_PASSWD));
wifi_config.sta.password[strlen(IL_WIFI_PASSWD)]=0;
wifi_config.sta.threshold.authmode = (wifi_auth_mode_t)WIFI_AUTH_WPA2_PSK;
ESP_LOGI("SCAN", "ENTRAMOS EN MODO CONFIGURACION +++++++++++\n");
} else {
memcpy(wifi_config.sta.ssid, ROUTER_SSID, strlen(ROUTER_SSID));
wifi_config.sta.ssid[strlen(ROUTER_SSID)]=0;
if (ROUTER_AUTH_MODE==WIFI_AUTH_WPA2_ENTERPRISE) {
wifi_config.sta.password[0]=0;
} else {
memcpy(wifi_config.sta.password, ROUTER_PASSWD, strlen(ROUTER_PASSWD));
wifi_config.sta.password[strlen(ROUTER_PASSWD)]=0;
}
wifi_config.sta.threshold.authmode = (wifi_auth_mode_t)ROUTER_AUTH_MODE;
if (ROUTER_AUTH_MODE==WIFI_AUTH_WPA2_ENTERPRISE) {
ESP_ERROR_CHECK(esp_eap_client_set_identity((uint8_t*)ROUTER_WPA2_ENT_USER,strlen(ROUTER_WPA2_ENT_USER)));
ESP_ERROR_CHECK(esp_eap_client_set_username((uint8_t*)ROUTER_WPA2_ENT_USER,strlen(ROUTER_WPA2_ENT_USER)));
ESP_ERROR_CHECK(esp_eap_client_set_password((uint8_t*)ROUTER_PASSWD,strlen(ROUTER_PASSWD)));
ESP_ERROR_CHECK(esp_wifi_sta_enterprise_enable());
}
ESP_LOGI("SCAN", "ENTRAMOS EN MODO NORMAL +++++++++++\n");
}
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA,&wifi_config));
if (enableAP) {
/* Configuracion del AP */
init_clients();
esp_netif_ip_info_t ipInfo_ap;
ipInfo_ap.ip.addr = IL_AP_IP;
ipInfo_ap.gw.addr = IL_AP_IP;
IP4_ADDR(&ipInfo_ap.netmask, 255,255,255,0);
esp_netif_dhcps_stop(wifiAP); // stop before setting ip WifiAP
esp_netif_set_ip_info(wifiAP, &ipInfo_ap);
esp_netif_dhcps_start(wifiAP);
wifi_config_t ap_config = {
.ap = {
.channel = 0,
.authmode = WIFI_AUTH_WPA2_PSK,
.ssid_hidden = 0,
.max_connection = MAX_CLIENTS,
.beacon_interval = 100,
}
};
memcpy((char*)ap_config.sta.ssid, IL_AP_SSID, strlen(IL_AP_SSID));
ap_config.sta.ssid[strlen(IL_AP_SSID)]=0;
memcpy((char*)ap_config.sta.password, IL_AP_PASSWD, strlen(IL_AP_PASSWD));
ap_config.sta.password[strlen(IL_AP_PASSWD)]=0;
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config));
/* Configuracion del servidor DNS para el AP */
uint32_t dns_addr = ipaddr_addr("8.8.8.8"); // Dirección del servidor DNS
ESP_ERROR_CHECK(set_dhcps_dns(wifiAP, dns_addr));
}
ESP_ERROR_CHECK(esp_wifi_start());
esp_efuse_mac_get_default(_mac);
if (enableAP) ip_napt_enable(IL_AP_IP, 1);//Habilita el routeo de paquetes.
}From my side, the reconnection logic is very simple: I only call esp_wifi_connect() on:
WIFI_EVENT_STA_START, andWIFI_EVENT_STA_DISCONNECTED.
There is no timer in my application that forces a reconnection every 30 seconds.
Questions
-
Could you please confirm if this is the correct / recommended way to set up WPA2-Enterprise on ESP devices with:
esp_eap_client_set_identity()esp_eap_client_set_username()esp_eap_client_set_password()esp_wifi_sta_enterprise_enable()
and the STA configuration above?
-
Based on the controller logs and my code, do you see anything that could explain a client-side re-association / re-authentication loop every ~30 seconds?
I look forward to your guidance on whether our implementation is correct and how we can further debug this behavior. Thank you very much.