Unable to execute reliable WiFi scanning

Here my current code to make an async WiFi scan on ESP32:

#include <Arduino.h>
#include <WiFi.h>
#include <esp_wifi.h>
#include <DNSServer.h>

DNSServer _dns;

void setup() 
{
  Serial.begin(115200);
  Serial.setTimeout(2);

  delay(3000);

  WiFi.mode(WIFI_MODE_AP); // tried also WIFI_MODE_APSTA
  WiFi.softAPConfig(IPAddress(192, 168, 4, 1), IPAddress(192, 168, 4, 1), IPAddress(255, 255, 255, 0));
  WiFi.softAP("ssid", "password");
  WiFi.softAPsetHostname("My app");
  esp_wifi_set_ps(WIFI_PS_NONE);
  _dns.start(53, "*", WiFi.softAPIP());
  WiFi.scanNetworks(true);
}

void loop() 
{
  _dns.processNextRequest();

  int n = WiFi.scanComplete();
  if (n == WIFI_SCAN_FAILED)
  {
    Serial.println("Scan failed");
    Serial.println("\n\nStart scan");
    WiFi.scanNetworks(true);

  }
  else if (n >= 0)
  {
    Serial.printf("Found %d networks\n", n);
    for (int i = 0; i < n; ++i)
    {
      Serial.printf("\t%d. %s [%d]\n", n, WiFi.SSID(i), WiFi.RSSI(i));
    }
    Serial.println("\n\nStart scan");
    WiFi.scanNetworks(true);
  }

  delay(1000);
}

But I get either a "Scan failed" or a successfully scan but with 0 networks found (and at my location there are at least 6 available networks).

Sometimes, instead it takes more time to end the procedure and it triggers the watchdog.

What is my mistake here?

It seems to me that your code contains a lot of unnecessary stuff: soft_api mode, DNS, power saving etc...

Why not to start from the Arduino IDE example?
Open File > Examples > WiFi > WiFiScan sketch.

Thanks for the hint.
Actually the example you proposed is blocking, but works. Instead using the async example, without any change, exhibits the same behavior of my code:

Scan start
Loop running...
Loop running...
Loop running...
Loop running...
Loop running...
Loop running...
Loop running...
Loop running...
Loop running...
Loop running...
Loop running...
WiFi Scan has failed. Starting again.
Scan start
Loop running...
no networks found
Scan start
Loop running...
no networks found
Scan start
Loop running...
no networks found
Scan start
...

While as said the blocking version returns the available networks correctly.

About the "lot of unnecessary stuff". I understand your point, but in the actual application I need that stuff. I've already cut down to the minimum in order to reproduce the issue.

I apologize, but please read my last message:

Instead, using the async example, without any change, exhibits the same behavior of my code

I'm using the official example code now, nothing else!
And I still cannot get a correct scan of the WiFi network.
Instead using the blocking version of the example it works.

sorry, i don't catch it

Your link to the official example is on the master branch. This could change at any time. It's better to click on the branch/tag switcher in the top left, and choose the latest permanent-looking tag, like the most recent Release Candidate 3.0.0-rc1. BTW, this example is part of version 3 -- not yet released -- and not present in version 2.

Anyway... yeah, it doesn't work reliably for me either. Try replacing the loop function with this different version, along with some supporting code

int16_t apCount = WIFI_SCAN_RUNNING;
uint32_t wifiScanCount;

void resetWifiScanCounters() {  // Can move these statements to startWiFiScan
  apCount = WIFI_SCAN_RUNNING;
  wifiScanCount = 0;
}

void wifiScanLoop() {
  wifiScanCount++;
  apCount = WiFi.scanComplete();

  switch (apCount) {
    case WIFI_SCAN_FAILED:
      Serial.printf("WiFi: scan failed after %d scan loops\n", wifiScanCount);
      //fallthrough
    case WIFI_SCAN_RUNNING:
      delay(155);  // yield while scanning, a little longer for demo
      break;
    case 0:
      Serial.printf("WiFi: no access points found after %d scan loops\n", wifiScanCount);
      break;
    default:
      Serial.printf("WiFi: found %d APs after %d scan loops\n", apCount, wifiScanCount);
  }
}

enum {
  WIFI_SCAN,
  MAIN,
} what_am_i_doing;

bool scanRepeatedly = true;

void loop() {
  switch (what_am_i_doing) {
    case WIFI_SCAN:
      if (apCount < 0) {
        // Haven't settled on number of APs yet; keep checking
        wifiScanLoop();  // does its own delay()
        Serial.printf("Just to prove scan is async at %lu...\n", millis());
      } else { // Found Zero or more Wireless Networks
        printScannedNetworks(apCount);  // this does WiFi.scanDelete!
        if (scanRepeatedly) {
          resetWifiScanCounters();
          startWiFiScan(); // start over...
        } else {
          what_am_i_doing = MAIN;
        }
      }
      break;
    case MAIN:
      // what's next
      Serial.printf("Loop running at %lu...\n", millis());
      delay(2500);  // longer for demo
      break;
  }
  // Could also do something here regardless of `what_am_i_doing`
}

It also helps to, under the Tools menu, set Core Debug Level to Debug.

With scanRepeatedly true, the first time it usually works without errors

Just to prove scan is async at 2401...
Just to prove scan is async at 2556...
Just to prove scan is async at 2711...
[  2775][D][WiFiGeneric.cpp:1039] _eventCallback(): Arduino Event: 1 - SCAN_DONE
Just to prove scan is async at 2866...
WiFi: found 21 APs after 18 scan loops
Just to prove scan is async at 2866...

Scan done
21 networks found

(Note the [D] debug message.) But the second time

Just to prove scan is async at 8987...
Just to prove scan is async at 9142...
WiFi: scan failed after 40 scan loops
Just to prove scan is async at 9297...
WiFi: scan failed after 41 scan loops
Just to prove scan is async at 9452...
WiFi: scan failed after 42 scan loops
Just to prove scan is async at 9607...
WiFi: scan failed after 43 scan loops
Just to prove scan is async at 9762...
WiFi: scan failed after 44 scan loops
Just to prove scan is async at 9917...
WiFi: scan failed after 45 scan loops
Just to prove scan is async at 10072...
WiFi: scan failed after 46 scan loops
[ 10104][D][WiFiGeneric.cpp:1039] _eventCallback(): Arduino Event: 1 - SCAN_DONE
Just to prove scan is async at 10227...
WiFi: found 23 APs after 47 scan loops
Just to prove scan is async at 10227...

Scan done
23 networks found

it returns WIFI_SCAN_FAILED for a while before completing successfully. This might be due to the aforementioned slight version mismatch between the underlying code in the board. I'm using version 2 of the library, which actually corresponds with version 4 of ESP-IDF; while version 3 is version 5.1 or thereabout.

I use the async, and just let it finish.

1 Like

Thanks for your hint about the branches/tags.
At least, with your code I get a consistent behavior: it fails for some times before providing a new successful scan.

Now I need to find a way to clean it up in order to avoid to show the failures to the user and add again all the "unnecessary stuff" hoping it will still work!

By the way, I don't think is necessary to explicitly call scanDelete() since it is already called inside the scanNetworks() function:

int16_t WiFiScanClass::scanNetworks(bool async, bool show_hidden, bool passive, uint32_t max_ms_per_chan, uint8_t channel, const char * ssid, const uint8_t * bssid)
{
    if(WiFiGenericClass::getStatusBits() & WIFI_SCANNING_BIT) {
        return WIFI_SCAN_RUNNING;
    }

    WiFiScanClass::_scanTimeout = max_ms_per_chan * 20;
    WiFiScanClass::_scanAsync = async;

    WiFi.enableSTA(true);

    scanDelete();

I was also struggling with this topic like Mark81 after building from sample code found on the web. I was experiencing the same issues.

I think the root of the problem is actually the number of scans being started. I think there are too many!

The reason for too many scans is the way the WIFI_SCAN_FAILED status code is being interpreted. Like Mark81's, the example I was working from began a new scan when a WIFI_SCAN_FAILED code was returned by WiFi.scanComplete() .

WIFI_SCAN_FAILED is defined with value -2. It's real meaning however is not clear and I found this documentation relating to the ESP8266:

IDE example — ESP8266 Arduino Core 3.1.2-21-ga348833 documentation (arduino-esp8266.readthedocs.io)

The important point is that in that documentation the -2 code does not mean the scan has completed , far from it, with reference to WiFi.scanComplete() it says "If scanning has not been triggered yet, it would return -2."

Combining this knowledge with the example I was working from I realised starting another scan when getting a -2 is not the correct action to take; unless you have reason to believe the scan will never start.

kenb4's solution comes at it from a slightly different angle but achieves the same. My explanation I hope clarifies why kenb4's solution works and helps answering Mark81's post #8.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.