WiFi.scan() only returns result when connected to a network

Hello,

I’m playing around with the WiFi functions on an Argon (deviceOS 1.4.4) and I notice that WiFi.scan() only seems to scan for networks when the device is connected (i.e. breathing cyan). If the device is not connected (i.e. blinking green), I get no scanned network information. Is this intentional? Seems weird that scanning only works this way.

Scenario is as follows:

  • Program is periodically printing (every 5s) stored wifi credentials and scanning for network
  • When the device is not connected and blinking green, no scanned network information is returned. scanfound is returned as -210.
  • I turn in my phone’s hot spot which has credentials resaved onto the Argon and the Argon automatically connected
  • Once Argon connects, I start getting scanned information of surrounding networks. There are constantly 5-6 other networks aside from my phone’s hotspot. scanfound is returned as 5 or 6 which is what I expected
#include "dct.h"

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);

uint32_t gWiFiPrintTimer = 0;
uint32_t gWiFiPrintPeriod = 5000;

void setup() {
  const uint8_t val = 0x01;
  dct_write_app_data(&val, DCT_SETUP_DONE_OFFSET, 1);
  WiFi.on(); // Need to be turn on when changing system mode
  WiFi.connect(); // Breathing green
  Particle.connect(); // Breathing cyan
  Serial.begin(9600);  
}

void loop() {

  if (millis() > gWiFiPrintTimer) {
    if (WiFi.ready()) Serial.println("\nWiFi is ready");
    else              Serial.println("\nWiFi is not ready");

    // Check Wifi credentials
    WiFiAccessPoint ap[10];
    int found = WiFi.getCredentials(ap, 10);
    for (int i = 0; i < found; i++) {
        Serial.println("WiFi " + String(i, DEC));
        Serial.print("\tSaved ssid: ");
        Serial.println(ap[i].ssid);

        Serial.print("\tSaved security: ");
        Serial.println(ap[i].security);

        Serial.print("\tSaved cipher: ");
        Serial.println(ap[i].cipher);

        Serial.print("\tSaved security: ");
        Serial.println(ap[i].security);

        Serial.print("\tSaved channel: ");
        Serial.println(ap[i].channel);

        Serial.print("\tSaved ssidLength: ");
        Serial.println(ap[i].ssidLength);

        Serial.print("\tSaved rssi: ");
        Serial.println(ap[i].rssi);

        Serial.print("\tSaved maxDataRate: ");
        Serial.println(ap[i].maxDataRate);

        Serial.print("\tSaved bssid: ");
        Serial.printlnf("%02X:%02X:%02X:%02X:%02X:%02X", ap[i].bssid[0], ap[i].bssid[1], ap[i].bssid[2], ap[i].bssid[3], ap[i].bssid[4], ap[i].bssid[5]);

        Serial.print("\tSaved size: ");
        Serial.println(ap[i].size);

        Serial.print("\tSaved ssid length: ");
        Serial.println(ap[i].ssidLength);

        Serial.print("\tSaved channel: ");
        Serial.println(ap[i].channel);
    }
    
    // Scan for ssids
    WiFiAccessPoint aps[5];
    int scanfound = WiFi.scan(aps, 5);
    Serial.printlnf("Found %d networks", scanfound);
    for (int i=0; i<scanfound; i++) {
      WiFiAccessPoint& ap = aps[i];
      Serial.print("Scan in main SSID: ");
      Serial.println(ap.ssid);
      Serial.print("Scan in main Security: ");
      Serial.println(ap.security);
      Serial.print("Scan in main Channel: ");
      Serial.println(ap.channel);
      Serial.print("Scan in main RSSI: ");
      Serial.println(ap.rssi);
    }
    gWiFiPrintTimer = millis() + gWiFiPrintPeriod;
  }
}

Nope, I cannot confirm that.
This code gives me back virtually all the APs nearby (also running Argon 1.4.4)

SYSTEM_MODE(SEMI_AUTOMATIC)
SYSTEM_THREAD(ENABLED)

void setup() {
  WiFi.on();
  pinMode(D7, OUTPUT);
  delay(1000);
  syncScan();
  digitalWrite(D7, !digitalRead(D7));
}

void loop()
{
  static uint32_t ms = 0;
  
  if (!digitalRead(BTN)) { // toggle WiFi.connect() via MODE button
    if (!WiFi.ready()) { 
      WiFi.connect();
      waitUntil(WiFi.ready);
    }
    else
      WiFi.disconnect();

    while(!digitalRead(BTN));
    ms = 0;
  }
  
  if (millis() - ms < 10000) return;
  ms = millis();
  
  int result_count = WiFi.scan(wifi_scan_callback);
  Serial.print(result_count);
  Serial.println(" APs scanned.");
  digitalWrite(D7, !digitalRead(D7));
}

void wifi_scan_callback(WiFiAccessPoint* wap, void* data)
{
  WiFiAccessPoint& ap = *wap;
  Serial.print("SSID: ");
  Serial.println(ap.ssid);
  Serial.print("Security: ");
  Serial.println(ap.security);
  Serial.print("Channel: ");
  Serial.println(ap.channel);
  Serial.print("RSSI: ");
  Serial.println(ap.rssi);
}

void syncScan() {
  WiFiAccessPoint aps[20];
  int found = WiFi.scan(aps, 20);
  for (int i=0; i<found; i++) {
    WiFiAccessPoint& ap = aps[i];
    Serial.print("SSID: ");
    Serial.println(ap.ssid);
    Serial.print("Security: ");
    Serial.println(ap.security);
    Serial.print("Channel: ");
    Serial.println(ap.channel);
    Serial.print("RSSI: ");
    Serial.println(ap.rssi);
  }
}

@ScruffR thanks for your reply. I loaded up your code and my results are as follows

  • When when the device is blinking blue I indeed get the APs printed and scan is working as intended
  • For some reason, the mode botton toggle you implemented didn’t work reliably for me. So instead of fiddling around with the mode button I just added WiFi.connect() to your set up to so we start in the connected mode instead. I don’t think this changes anything else but please correct me if I am wrong. Upon adding the WiFi.connect() call to your set up. I do see the -210 error and scan is not working as intended (i.e. same issue as the opening post)
void setup() {
  WiFi.on();
  WiFi.connect();
  pinMode(D7, OUTPUT);
  delay(1000);
  syncScan();
  digitalWrite(D7, !digitalRead(D7));
}

It seems on your side this is really not reproducible? Perhaps my Argon is not working as intended?

That's due to the latency introduced by the actual WiFi scanning and (mostly) by the delay(10000) in loop() that should be removed :blush:.

However, in my code I have a waitUntil() in place to make sure the device is not attempting a scan while connecting.
But even without that my Argon returns a list of APs just fine.

Ah thanks for the explination. I did some minor improvements using system events for listening to the button instead. This way I can just click the button once and wait until the loop gets around to it. Let me know what you think

SYSTEM_MODE(SEMI_AUTOMATIC)
SYSTEM_THREAD(ENABLED)

bool toggle = false;

void button_clicked(system_event_t event, int param)
{
    int times = system_button_clicks(param);
    Serial.printlnf("button was clicked %d times", times);
    toggle = true;
}

void setup() {
  System.on(button_click, button_clicked);
  WiFi.on();
  pinMode(D7, OUTPUT);
  delay(1000);
  syncScan();
  digitalWrite(D7, !digitalRead(D7));
}

void loop()
{
  static uint32_t ms = 0;
  
  if (toggle) {
    Serial.println("Mode button toggled");
    if (!WiFi.ready()) { 
      WiFi.connect();
      waitUntil(WiFi.ready);
    }
    else
      WiFi.disconnect();
    Serial.println("WiFi toggle completed");
    toggle = false;
  }
  
  if (millis() - ms < 10000) return;
  ms = millis();
  
  Serial.println("Starting scan...");
  int result_count = WiFi.scan(wifi_scan_callback);
  Serial.print(result_count);
  Serial.println(" APs scanned.");
  digitalWrite(D7, !digitalRead(D7));
  delay(10000);
}


void wifi_scan_callback(WiFiAccessPoint* wap, void* data)
{
  WiFiAccessPoint& ap = *wap;
  Serial.print("SSID: ");
  Serial.println(ap.ssid);
  Serial.print("Security: ");
  Serial.println(ap.security);
  Serial.print("Channel: ");
  Serial.println(ap.channel);
  Serial.print("RSSI: ");
  Serial.println(ap.rssi);
}

void syncScan() {
  WiFiAccessPoint aps[20];
  int found = WiFi.scan(aps, 20);
  for (int i=0; i<found; i++) {
    WiFiAccessPoint& ap = aps[i];
    Serial.print("SSID: ");
    Serial.println(ap.ssid);
    Serial.print("Security: ");
    Serial.println(ap.security);
    Serial.print("Channel: ");
    Serial.println(ap.channel);
    Serial.print("RSSI: ");
    Serial.println(ap.rssi);
  }
}

Anyway back on to the orginal topic...

Hmmm I guess this is the route my problem. It seems that scan returns -210 when not connected to a network and is attempting to connect (i.e. blinking green). Since you added a waitUntil() here to prevent this, I assume you knew about this?

In summary, is it intentional that WiFi.scan() does not work when the device is blinking green and attempting to connect to/searching for a network (i.e. has WiFi credentials stored on the device)?

The reason I am asking is because the scenario I am thinking is as follows:

  • I have some credentials stored on the Argon
  • Argon cycles through the credential list attempting to connect to near by WiFi networks
  • Lets say WiFi networks does not exist, after some time I want to send back information about the surrounding networks to the user and perhaps pick a network from that list to connect to

If scanning does not work when the device is searching for connection, I guess I'll just have to stop searching before doing the scan which is fine.

1 Like

What were you trying to do with this?

You can scan for available Wireless APs when connected to an access point but scanning will be blocked when the device is in process of connecting to (flashing green) an access point and also whilst negotiating the Particle cloud connection (flashing cyan). This is why you need to give the connection processes time to complete.

The stored AP credentials should be considered a separate set from the available APs from scanning. This is because an AP's credentials could be stored in memory but not be available. What the device OS is trying to do is make sure that it has tried to connect to all the stored AP credentials - and it will keep trying to do that to get connected.

Once you have more than one AP stored then it gets difficult to determine without using the WICED error code (if you search on the forum there is a solution posted for getting this as it is not standard device OS) what is going on - is the stored AP out of range, is the stored AP's credential (password) wrong, etc. If you haven't come across WICED before - it stands for Wireless Internet Connection for Embedded Devices and is a closed source library that most WiFi devices use including Particle.

This is to toggle the set up done flag so that I automatically do not go into listening mode. This is because I did not set up my Argon using the mobile app, just by USB. Sources:

This only really needs to be done once but I left it there. It does not impact the functionality once the flag is toggled.

I guess this is my problem. I assume scanning would be working when flashing green. I had assume similar characteristics like a mobile phone (bad comparison I know) where you can still scan and see availble networks even though you have credentials saved and trying to connect to known wifi networks (or that they just made it seemless to the end user and automatically toggled scan and connecting states). Maybe this is a typical characteristic when dealing with WiFi at this level? If there are doc's describing this I would appreciate it, so far I'm running off just https://docs.particle.io/reference/device-os/firmware/argon/#scan-

Anyway, thanks to @ScruffR 's code above, it seems that this characteristic apparently widely known since he intentionally does a waitUntil(WiFi.ready); before scanning again. To resolve this issue, I did something similar to his code where I do a if (!WiFi.ready()) { WiFi.disconnect() }before a WiFi.scan(). After the WiFi.scan() I go back to WiFi.connect() again. The scan does have a delay but for my scenario I think its acceptable.

This is helpful, thanks for the information on WICED. I will definately look this up. Its good to always know more about why you are not connecting and probably feed this back to the end user.

1 Like

Note seems like this method is more reliable for putting the device back to a “scannable” state

    if (!WiFi.ready()) {
        Particle.disconnect();
        WiFi.off();
        WiFi.on();
    }