I thought I would get the ball rolling in this advanced group by posting up a code snippet that shows something useful that maybe not everyone knows. This is running and working code, but it may not be complete - for the advanced user it should be enough to get them going.
This is something I have recently been looking at and this code is now working a treat, although I do have a question for the group as well. This is the guts of my wifi AP connection code, which does the following:
- I have a list of known APs that I store in persistent storage (flash) (variable wifi_p)
- I have the last AP I was connected to, it's bssid and its channel and I store those in volatile storage (RTC) (variable wifi_v)
- when I wake up I look to see if I have anything in volatile store, and if so I set the wifi config so that it tries going directly to the bssid and channel, if the AP is still exactly the same it connects extremely quickly
- if that fails, I drop the channel and try again
- if that fails, I drop the bssid and try again
- if that fails I try the AP one last time just with SSID
- if that doesn't work I then start cycling through my list of APs looking for a connection
- when I manage to connect, I save the channel and bssid for next time
There are other failure modes I also deal with:
- if the AP connects but I can't get an IP address, I disconnect and reconnect the same way
- if the AP rejects me because the password has changed, I skip it and move on
- if the AP is connected and then drops out, I first try to reconnect the same way but then go through my fallback sequence
Here is the code:
/*
* File : wifi.c
* Description:
* Author : Davyd Norris
*
* Copyright(c) 2019 Olive Grove IT Pty Ltd
*/
.
.
.
static void ICACHE_FLASH_ATTR configure_ap_info(void) {
APInfo_t *ap_info = &(wifi_p->ap_list[wifi_v->ap]);
INFO("WIFI", "configuring AP %d...", wifi_v->ap);
INFO("WIFI", "setting ssid = %s, password = %s", ap_info->ssid, ap_info->password);
struct station_config station_conf;
os_memset(&station_conf, 0, sizeof(struct station_config));
os_sprintf(station_conf.ssid, "%s", ap_info->ssid);
os_sprintf(station_conf.password, "%s", ap_info->password);
if (os_memcmp(wifi_v->bssid, "\0\0\0\0\0\0\0", 6) != 0) { /* if bssid is known then try it again to speed up connect */
INFO("WIFI", "setting bssid = "MACSTR, MAC2STR(wifi_v->bssid));
os_memcpy(&station_conf.bssid, wifi_v->bssid, 6);
station_conf.bssid_set = 1;
}
wifi_station_set_config_current(&station_conf);
if (wifi_v->channel != 0) { /* if channel is known then try it again too */
INFO("WIFI", "setting channel = %d", wifi_v->channel);
wifi_set_channel(wifi_v->channel); /* has to be set after wifi_station_set_config_*() */
}
}
static void ICACHE_FLASH_ATTR wifi_event_cb(System_Event_t *evt) {
INFO("WIFI", "event: %s", wifi_events[evt->event]);
switch (evt->event) {
case EVENT_STAMODE_CONNECTED: {
wifi_status = WIFI_CONNECTED;
Event_StaMode_Connected_t *e = &(evt->event_info.connected);
INFO("WIFI", "connected ssid %s, channel %d, bssid = "MACSTR, e->ssid, e->channel, MAC2STR(e->bssid));
/* connected - cache bssid and channel info for faster reconnect next time (AP index is already cached) */
wifi_v->channel = e->channel;
os_memcpy(wifi_v->bssid, e->bssid, 6);
sysinfo_set_dirty(SS_WIFI|STORAGE_VOLATILE);
}
break;
case EVENT_STAMODE_DISCONNECTED: {
wifi_status = WIFI_DISCONNECTED;
Event_StaMode_Disconnected_t *e = &(evt->event_info.disconnected);
uint8_t reason = e->reason;
const char *reason_str = (reason >= 200) ? wifi_reason_ap[reason - 200] : wifi_reason_phy[reason];
INFO("WIFI", "disconnect ssid %s, reason %d, %s", e->ssid, reason, reason_str);
/* disconnected - work out why and adjust wifi AP connection params */
if (reason == REASON_AUTH_FAIL) { /* bad password - try another AP */
wifi_v->channel = 0;
os_memset(wifi_v->bssid, '\0', 6);
wifi_v->ap = (wifi_v->ap + 1) % wifi_p->num_aps;
} else if (reason == REASON_NO_AP_FOUND) { /* can't find AP - relax filters */
if (wifi_v->channel != 0) { /* ignore last channel */
wifi_v->channel = 0;
} else if (os_memcmp(wifi_v->bssid, "\0\0\0\0\0\0\0", 6) != 0) { /* ignore bssid */
os_memset(wifi_v->bssid, '\0', 6);
} else { /* really not there - try another AP */
wifi_v->ap = (wifi_v->ap + 1) % wifi_p->num_aps;
}
} else if (reason == REASON_BEACON_TIMEOUT) { /* AP has stopped responding - try to reconnect */
wifi_station_disconnect();
} else if (reason == REASON_ASSOC_LEAVE) { /* device has disconnected itself */
break;
}
configure_ap_info();
wifi_status = WIFI_CONNECTING;
wifi_station_connect();
}
break;
case EVENT_STAMODE_AUTHMODE_CHANGE: {
Event_StaMode_AuthMode_Change_t *e = &(evt->event_info.auth_change);
INFO("WIFI", "authmode change %s -> %s", wifi_auth_mode[e->old_mode], wifi_auth_mode[e->new_mode]);
}
break;
case EVENT_STAMODE_GOT_IP: {
wifi_status = WIFI_IP_ACQUIRED;
Event_StaMode_Got_IP_t *e = &(evt->event_info.got_ip);
INFO("WIFI", "got IP addr= " IPSTR ", mask= " IPSTR ", gw= " IPSTR,
IP2STR(&e->ip), IP2STR(&e->mask), IP2STR(&e->gw));
/* we have an IP and are ready to go - connect the time */
configure_sntp();
}
break;
case EVENT_STAMODE_DHCP_TIMEOUT: {
INFO("WIFI", "couldn't get an IP address. Retry...");
/* don't have an IP address - try to disconnect and reconnect */
wifi_status = WIFI_DISCONNECTING;
wifi_station_disconnect();
wifi_status = WIFI_CONNECTING;
wifi_station_connect();
}
break;
default:
break;
}
}
void ICACHE_FLASH_ATTR wifi_connect(WifiCallback cb) {
.
.
.
INFO("WIFI", "Initialising connection");
wifi_status = WIFI_CONNECTING;
wifi_set_opmode_current(STATION_MODE);
wifi_station_set_reconnect_policy(false);
wifi_set_event_handler_cb(wifi_event_cb);
configure_ap_info();
wifi_station_connect();
}