Merge pull request #66 from cjkas/fix/wifi-fast-scan

fix: join the AP our scan picked without rescanning every channel
This commit is contained in:
cjkas 2026-10-19 16:16:08 +02:00 • committed by GitHub
commit 31581b42e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 57 additions and 4 deletions

View file

@ -2,6 +2,7 @@
#include <ETH.h>
#endif
#include <WiFi.h>
#include <esp_wifi.h>
#include <ESPmDNS.h>
#include <esp_task_wdt.h>
#include "esp_log.h"
@ -174,6 +175,7 @@ bool ESPNetwork::changeAP(const uint8_t *bssid, const int32_t channel) {
this->connType = conn_types_t::unset;
this->_connecting = true;
this->connectStart = millis();
WiFi.setScanMethod(WIFI_FAST_SCAN);
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase, channel, bssid);
this->connectStart = millis();
return false;
@ -460,6 +462,11 @@ bool ESPNetwork::connectWiFi(const uint8_t *bssid, const int32_t channel) {
this->connType = conn_types_t::unset;
ESP_LOGI(TAG, "WiFi begin...");
this->_connecting = true;
// Our own scan already picked the AP. An all-channel scan here makes the
// driver sweep every channel again first, about 17s, before it even
// authenticates. A fast scan starts on the AP's channel and joins the
// first match.
WiFi.setScanMethod(WIFI_FAST_SCAN);
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase, channel, bssid);
this->connectStart = millis();
}
@ -488,11 +495,14 @@ bool ESPNetwork::connectWiFi(const uint8_t *bssid, const int32_t channel) {
int32_t _channel = 0;
if(!settings.WIFI.hidden && this->getStrongestAP(settings.WIFI.ssid, _bssid, &_channel)) {
ESP_LOGD(TAG, "Found strongest AP %02X:%02X:%02X:%02X:%02X:%02X CH:%d", _bssid[0], _bssid[1], _bssid[2], _bssid[3], _bssid[4], _bssid[5], _channel);
WiFi.setScanMethod(WIFI_FAST_SCAN); // The AP is known, see connectWiFi(bssid).
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase, _channel, _bssid);
}
else
else {
// If the user has the hidden flag set just connect to whatever the AP gives us.
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase);
}
}
this->connectStart = millis();
return true;
@ -550,8 +560,9 @@ bool ESPNetwork::getStrongestAP(const char *ssid, uint8_t *bssid, int32_t *chann
if(connected) {
const uint8_t *b = WiFi.BSSID(i);
const uint8_t *cur = WiFi.BSSID();
ESP_LOGI(TAG, "AP %02X:%02X:%02X:%02X:%02X:%02X ch %ld %ld dBm%s", b[0], b[1], b[2], b[3], b[4], b[5],
(long)WiFi.channel(i), (long)WiFi.RSSI(i), (cur && memcmp(b, cur, 6) == 0) ? " (connected)" : "");
ESP_LOGI(TAG, "AP %02X:%02X:%02X:%02X:%02X:%02X ch %ld %ld dBm %s%s", b[0], b[1], b[2], b[3], b[4], b[5],
(long)WiFi.channel(i), (long)WiFi.RSSI(i), wifiAuthModeName(WiFi.encryptionType(i)),
(cur && memcmp(b, cur, 6) == 0) ? " (connected)" : "");
}
if(worthRoaming(connected, currentRssi, WiFi.RSSI(i), ROAM_MIN_GAIN_DB) && WiFi.RSSI(i) > strength) {
strength = WiFi.RSSI(i);
@ -601,7 +612,15 @@ void ESPNetwork::networkEvent(WiFiEvent_t event) {
if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname);
break;
case ARDUINO_EVENT_WIFI_STA_STOP: ESP_LOGI(TAG, "(evt) WiFi clients stopped"); break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED: ESP_LOGI(TAG, "(evt) Connected to WiFi STA access point"); break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED: {
// How long the join took and the security the AP offers, so a slow
// connect shows in the log.
wifi_ap_record_t ap;
const bool haveAp = esp_wifi_sta_get_ap_info(&ap) == ESP_OK;
ESP_LOGI(TAG, "(evt) Connected to WiFi STA access point after %lums (%s)",
(unsigned long)(millis() - net.connectStart), haveAp ? wifiAuthModeName(ap.authmode) : "?");
break;
}
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
ESP_LOGI(TAG, "(evt) Disconnected from WiFi STA access point. Connecting: %d", net.connecting());
net.connType = conn_types_t::unset;

View file

@ -147,6 +147,20 @@ void RssiBroadcast::sent(bool broadcast, int rssi, int channel, uint32_t now) {
bool worthRoaming(bool connected, int currentRssi, int apRssi, int minGain) {
return !connected || apRssi >= currentRssi + minGain;
}
const char *wifiAuthModeName(uint8_t authMode) {
switch(authMode) {
case 0: return "OPEN";
case 1: return "WEP";
case 2: return "WPA";
case 3: return "WPA2";
case 4: return "WPA/WPA2";
case 5: return "WPA2-ENT";
case 6: return "WPA3";
case 7: return "WPA2/WPA3";
case 8: return "WAPI";
default: return "UNKNOWN";
}
}
heap_emit_t heapEmitTarget(uint8_t num, uint32_t sinceLastMs, uint32_t intervalMs, bool roomHasClients) {
if(num != 255) return heap_emit_t::client;
if(sinceLastMs >= intervalMs) return heap_emit_t::broadcast;

View file

@ -74,6 +74,9 @@ struct RssiBroadcast {
// connected it has to be at least minGain dBm stronger than the current AP so
// a small swing doesn't make the device hop between APs.
bool worthRoaming(bool connected, int currentRssi, int apRssi, int minGain);
// A short name for a wifi_auth_mode_t value, for the logs. The values up to
// WAPI are the same in ESP-IDF 4.4 and 5.x.
const char *wifiAuthModeName(uint8_t authMode);
// Who gets a memStatus event.
enum class heap_emit_t : uint8_t { none, client, broadcast, room };
// num is the socket client asked for, 255 for everyone. A broadcast goes out