It’s been a while since I initially looked into what it takes to make an optimal wifi connection with an Espressif ESP8266 / ESP-01. Using the code from the old post didn’t work … anymore? What the heck? So much for rolling out another kind of battery-powered ESP8266 device. Time to dig into what goes on with the wifi connection timing.
The code is on GitHub. Most of this post is from the readme file there. Which will rank better in Google, a subdirectory of a well-known domain, or my blog post? Whatever, it’s all the same to me :-).
TLDR: The fastest ESP8266 wifi connection time can be less than 170ms
The fastest connection (that I found; including publishing to MQTT) is done with the following setup:
- Connect normally. Cache received BSSID & channel. O(4’000ms)
- Set
WiFi.persistent(true)
. Connect with BSSID & channel. O(1’500ms) - Set
WiFi.persistent(true)
to get persistent information. Connect again with BSSID & channel. O(170ms)
You have to do all of these steps, then repeated connections will be around 170ms. In C++, steps 2 & 3 look like this:
WiFi.persistent(true);
WiFi.mode(WIFI_STA);
WiFi.config(IPAddress(data->ip_address),
IPAddress(data->ip_gateway), IPAddress(data->ip_mask),
IPAddress(data->ip_dns1), IPAddress(data->ip_dns2));
WiFi.begin(data->wifi_ssid, data->wifi_auth, data->wifi_channel, data->wifi_bssid, true);
uint32_t timeout = millis() + FAST_TIMEOUT;
while ((WiFi.status() != WL_CONNECTED) && (millis()<timeout)) { delay(10); }
// connected or timed out
I suspect using this configuration persists not only BSSID and channel-number, but also information from the key-exchange for the WPA2 connection. Weirdly, without using persistent(true)
, the device still scans for the best channel, wasting time. You could recreate the same configuration by manually doing what the ESP8266 Wifi-code does; it’s probably a bit flakey since you can’t check what the firmware does (you just see the interfaces in user_interface.h).
Configuration
- ESP8266 basic board (ESP-01, 512MB)
- USB/TTL programmer board (like this)
- Platformio
- ESP8266 Arduino framework in Platformio
- Wifi AP with WPA2, MQTT server on local network using IPv4
Arduino sketches
This repo includes Arduino sketches if you prefer those. They’re at:
- hardcoded connection
- normal connection – this one has a simple class for building the connection
Variations tested
I included median and 90’th percentile (“p90”) duration timing. 90th percentile means that 90% of the runs were below this number. Timings were measured with the micros()
function, and tracked over a number of iterations. The timing data was output as <key=value>
to the serial port, aggregated with a script that monitors the serial port and tracks the entries into a CSV file).
The total time includes:
- Restoring settings from flash
- Connecting to wifi
- Connecting to the MQTT server
- Publishing 1 MQTT topic (in last variant, 5 topics)
Some of the variants tested (see variations.txt for more):
- Normal connection (just SSID, auth): median 3898ms, p90 4267ms
- Connect with BSSID, channel (Variant “O”): median 1086ms, p90 1146ms
- With
persistent(true)
, no BSSID, channel specified (Variant “K”): median 2911ms, p90 3915ms - With
persistent(true)
, giving BSSID, channel, manually connecting usingwifi_station_connect()
(Variant “J”): median 940ms, p90 1552ms - With
persistent(true)
, giving BSSID, channel, manually connecting usingreconnect()
(variant “I”): median 587ms, p90 1749ms - With
persistent(true)
, giving BSSID, channel, requesting connection inbegin()
(Variant “G”): median 197ms, p90 1201ms - same, but doing 5x MQTT publishes instead of one (Variant “P”): median 188ms, p90 1227ms
The last variant (“P”; similar to the second-last one) is the one that’s overall the fastest, with a median time from start to publishing 5 MQTT topics of 188ms (stddev: 334ms).
The MQTT portion of variant “P” (connecting to server, publishing topics) has a median time of 19ms (p90: 25ms, stddev: 290ms!). The high standard deviation for MQTT publishing is probably what throws off the total time standard deviation. My MQTT server is on a local Raspberry Pi running Home Assistant.
Without doing an IP/port pre-connection (variant “Q”), the total median time goes to 190ms (p90: 278ms, stddev: 257ms), so the IP pre-connection does not save much. Using the hostname instead of IP address (requiring a DNS lookup; variant “R”), the total time goes to median 202ms (p90 840ms, stddev 410ms), so caching the IP address is a good idea.
Anecdotes
The weird & wonderful:
persistent()
is disabled by default in the ESP SDK for wifi. I suspect because of flash wear? YOLO.- Platformio uses ancient ESP8266 “Non-OS” (Arduino) SDKs – the ESP8266 SDKs are much newer. Issue filed in 2018. WTH Platformio?!
- Connecting with BSSID and channel (without
persistent(true)
), will result in scanning for a channel, and may use a different channel. This adds ca 900ms to the connection time. This is probably a bug. This is the configuration that most external mentions for speed optimization suggest, which is better than nothing, but still slower than it needs to be. - The ESP SDK code in ESP8266WiFiSTA.cpp shows how the connection is built, but you can’t check the code for what actually happens. However, you can tell that using
persistent(true)
without specifying a BSSID will result in the persistent data not being used (despite having the BSSID too). - In the code, you also see that
wifi_station_connect()
is called before setting the channel number. Does this mean the persistent channel is not used? Weird. - Using
persistent(true)
without specifying BSSID / channel does not persist the received settings. - The debug output (see below) is the same for all connection types; it’s useless to look at for speed optimizations.
- The SDK versions provided by Platformio (v2.2.1 – 2.2.x, pre-3.0; 2018 to 2019) all have similar timings.
- The unclear difference between
persistent(true)
+ connect with BSSID & channel imo suggest that future SDK versions may be different, and that ESP32 may handle this differently. It’s unclear whatpersistent(true)
actually does. Magic.
ESP8266 Wifi debug output
Platformio has build flags to enable ESP wifi debug output. Unfortunately the main variations all show exactly the same debug output, so you can’t tell what’s happening.
In platformio.ini (docs):
build_flags = -DDEBUG_ESP_WIFI -DDEBUG_ESP_PORT=Serial
Output with normal connection (scan for BSSID, channel, connect), but also without specifying a channel, and the persistent fast connection:
fpm close 3
mode : sta(c4:5b:be:4a:69:34)
add if0
wifi evt: 8
wifi evt: 2
scandone
state: 0 -> 2 (b0)
state: 2 -> 3 (0)
state: 3 -> 5 (10)
add 0
aid 3
cnt
connected with XXXXXX, channel 11
dhcp client start...
wifi evt: 0
ip:192.168.178.111,mask:255.255.255.0,gw:192.168.178.1
wifi evt: 3