Go into listening mode if no known networks are available

Hi guys,

I’m starting to create my own product with the Photon. I want the Photon to enter listening mode if there are no known networks available so the Photon will go into “setup mode” and the SoftAP page setup page will be available on the Photon for the customer to enter new wifi credentials.

What is the best way to achieve this?

Here is my code so far:

#include "Particle.h"
#include "softap_http.h"

SYSTEM_MODE(MANUAL);
SYSTEM_THREAD(ENABLED);

/* SoftAP Stuff
Ommitted */

void setup()
{
  WiFi.on();
  WiFi.connect();

if (!WiFi.connecting())
  {
    ; // Wait until no longer connecting
  }


if (!WiFi.ready())
  {
    WiFi.listen();
  }

}


void loop()
{

}

Probably not the best way, but one way

void setup()
{
  WiFi.on();
  WiFi.connect();
  if(!waitFor(WiFi.ready, 75000)) {
    WiFi.listen();
  }
  else {
    // either procede with your app code while in listening mode
    // or wait forever
    // waitUntil(WiFi.ready); 
  }
}

If you want you can first get the number of stored creds and wait x * 15000 to shorten the time. But you should allow for each stored network some seconds to be found and get connected.

1 Like

Thanks. I was hoping for there to be something like waitFor().

I’ll try this later tonight.

1 Like

Also you might want to put some logic in to time out of listening mode and try connecting again. Otherwise, if the Wi-Fi access point hangs/crashes/stops working all the Photons will go into listening mode and stay there forever, even if someone reboots the router/access point and it starts working again.

1 Like

I am getting some really weird behavior with this. I assume it is because I am using SYSTEM_THREAD(ENABLED);.

void setup()
{
        Serial.begin(9600);

        WiFi.on();
        WiFi.connect();
        Serial.println(millis());
        if(!waitFor(WiFi.ready, 10000)) {
                        Serial.println(millis());
                        WiFi.listen();
        }
        else {
                // either procede with your app code while in listening mode
                // or wait forever
                // waitUntil(WiFi.ready);
        }

}

Basically I cleared the credentials and then added one set of credentials to the Photon (my phone’s hotspot). I turned off my hotspot and then I tried the Photon with the setup above. Strangely, the Photon does not go into listening mode after ten seconds. It always takes ~40 seconds. I think this has something to do with WiFi.connect() blocking WiFi.listen().

Update:
Commenting out SYSTEM_THREAD(ENABLED); does not have any effect, it just makes it take longer for the Photon to give up on WiFi.connect(). (about 80 seconds, or two cycles.)

Does WiFi.disconnect() before entering Listening Mode change anything?

1 Like

Yes. Adding WiFi.disconnect() fixes it.

#include "Particle.h"
#include "softap_http.h"

SYSTEM_MODE(MANUAL);
SYSTEM_THREAD(ENABLED);

WiFiAccessPoint ap[5];

int found;

/* SoftAP Stuff
Ommitted */

void setup()
{
        WiFi.on();
        found = WiFi.getCredentials(ap, 5);
        WiFi.connect();
        if(!waitFor(WiFi.ready, found * 15000))
        {
                WiFi.disconnect();
                WiFi.listen();
        }
        else {
                // either procede with your app code while in listening mode
                // or wait forever
                // waitUntil(WiFi.ready);
        }
}


void loop()
{
        if (!WiFi.ready()) // If WiFi connection is interrupted...
        {
                WiFi.connect(); // Attempt to reconnect
                if(!waitFor(WiFi.ready, found * 15000)) // If the connection times out...
                {
                        WiFi.disconnect();
                        WiFi.listen(); // Go into SoftAP setup mode
                }
        }
}
1 Like

Apologies for resurrecting this old thread, but I haven't found anything more current that's as close to what I'm trying to do.

Do I need to be in Manual system mode and system thread enabled for this to work? I have (I believe) a simpler situation. I just want the Particle to go into listen mode if it doesn't connect to a known WiFi network during initial startup.

So whenever my device is moved to a new location, it will be powered down and will look for known WiFi networks as soon as it's plugged in. If it doesn't find one, it should enter listen mode (doesn't need to run application code while this is happening) and allow the customer to connect via the softap and give the new WiFi network/password before continuing.

Thanks.

Yes, you will need Manual mode and system thread enabled for this to work. With the correct modifications the code above will work for your situation.

1 Like

Thanks. I will give it a go and post code back here for questions if I crash and burn, or for help to someone else down the road if it works out.

1 Like

You may also use SYSTEM_MODE(SEMI_AUTOMATIC)

1 Like

Ok, here’s the code I ended up with. It’s certainly not as compact/clean as it could be, but I’m still learning so trying to keep it as simple as possible for my benefit. Any comments/advice appreciated.

UPDATE: Moved WiFi.disconnect() and listen() calls outside of signaling for() loop as caught by @ScruffR below (thanks).

#include "Particle.h"
#include "softap_http.h"

SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);

                                            // INITIALIZE VARIABLES =================================

int LED = D7;                               // LED = onboard LED pin (D7).
bool newWiFi = false;                       // Flag to indicate looking for new WiFi credentials.

STARTUP(WiFi.setListenTimeout(120));         // Set max time to listen for new WiFi credentials.

                                            // SETUP ================================================

void setup() {
    pinMode (LED, OUTPUT);                  // Set LED pin to OUTPUT mode.

    WiFi.on();                              // Do manual WiFi initialization
    WiFi.connect();
    if(waitFor(WiFi.ready, 15000)) {        // Wait 15 seconds to find WiFi network
        for (int i = 0; i < 3; i++) {       // 3 Flashes = we have WiFi.
            digitalWrite(LED, HIGH);        // Send signal then...
            delay(100);
            digitalWrite(LED, LOW);
            delay(100);
        }
        Particle.connect();                 // Connect to Particle Cloud.
    }
    else {
        for (int i = 0; i < 6; i++) {       // 6 Flashes = no WiFi found after 15 sec.
            digitalWrite(LED, HIGH);        // Send signal then...
            delay(100);
            digitalWrite(LED, LOW);
            delay(100);
        }
        WiFi.disconnect();              // Disconnect WiFi.
        newWiFi = true;                 // Set flag to say we are listening for new WiFi credentials.
        WiFi.listen();                  // Listen for new WiFi credentials.
    }
}

                                            // MAIN PROGRAM LOOP =====================================

void loop() {
    if ((newWiFi) && (!WiFi.listening())) {
        System.reset();                     // If newWiFi and ListenTimeout already expired, reset.
    }

// Do Stuff()                               // Do rest of loop() code which will run while listening for new WiFi credentials.

}

What it appears to be doing successfully now is…

On power up it will manually start the WiFi connection (required since we’re not in Auto mode).

Wait 15 seconds to try to connect with the existing credentials.

Then either connect to the Particle cloud if successful, or

Disconnect the WiFi and start listening mode if no successful WiFi connection.
A newWiFi flag lets me know that I’m now acquiring new WiFi credentials via softap

And finally, a line at the top of the loop() function (which runs as soon as the setup() function is finished since we’re running SYSTEM_THREAD(ENABLED)) checks to see if we are getting newWiFi info and the WiFi listen timeout has expired, after which it resets the system so that it can try to connect again with the new information.

This prevents the new user from having to open the enclosure to enter new WiFi info when the product arrives at a new location, and does it’s own power-cycle to connect with that new data once it’s entered. Instead of system.reset() , I’m guessing I could probably just do a new WiFi.connect() at this point with the new connection info instead of power cycling the device?

BTW, the flashing messages are my trouble-shooting feedback. Since I’m programming from web IDE, I don’t have Serial.println() available to send debugging messages? Is there a better choice for these when working from web IDE?

Thanks in advance, it’s very nice to finally see it working but I know I have quite a bit of work yet to go.

Why is this part of your code inside the for() loop?

            WiFi.disconnect();              // Disconnect WiFi.
            newWiFi = true;                 // Set flag to say we are listening for new WiFi credentials.
            WiFi.listen();                  // Listen for new WiFi credentials.

Web IDE does not prevent you from receiving the serial output. You can use any terminal program on your computer to catch the output.
You can also use particle serial monitor --follow for that purpose.

Oops, thank you for catching that. It should have been outside of the for() loop for signal flashing but still within the else() statements.

Re: Serial monitoring, I currently have the Particle inserted into a Relay shield and receiving power from that. So I could still attach a USB cable from the Particle to my computer and watch the serial output without causing power problems? I assume so, but had to ask to make sure.

Otherwise, the code looks "OK" if a bit verbose/simple?

Thanks

Yes, you can.
There is a protection diode in the current path that prevents your Vin voltage feeding back into USB, so you are safe to connect USB too.

1 Like

Resurrecting the thread, sorry. Seems to make more sense to keep it here.

Both the proposed code sets are failing for me. I’m building on 0.8.0-rc11 using the Web IDE. (EDIT: downgraded and tested on 0.7.0 with same result).

When using @AaronD 's code (shared) and below, it works fine when the WiFi network is present. I see 3 blue flashes and it connects. However, when the WiFi is not present (also when present but not connected to the internet), it goes through a cycle with red flashes/green but never enters blue flashing listening mode (the flashing is fast and crazy in a way I can’t follow whether it is an SOS or simply no internet/cloud connection).

I feel telling, there is neither the expected 6 LED flashes nor 3.

On the other hand, @nrobinson2000 's post #7 code when WiFi is present never connects and remains in green breathing state. I can’t figure out what the problem is.

I’ve re-read the docs on all the WiFi instructions and can’t seem to find anything wrong with either code…yet neither works.

#include "Particle.h"
#include "softap_http.h"

SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);

                                            // INITIALIZE VARIABLES =================================

int LED = D7;                               // LED = onboard LED pin (D7).
bool newWiFi = false;                       // Flag to indicate looking for new WiFi credentials.

STARTUP(WiFi.setListenTimeout(120));         // Set max time to listen for new WiFi credentials.

                                            // SETUP ================================================

void setup() {
    pinMode (LED, OUTPUT);                  // Set LED pin to OUTPUT mode.

    WiFi.on();                              // Do manual WiFi initialization
    WiFi.connect();
    if(waitFor(WiFi.ready, 15000)) {        // Wait 15 seconds to find WiFi network
        for (int i = 0; i < 3; i++) {       // 3 Flashes = we have WiFi.
            digitalWrite(LED, HIGH);        // Send signal then...
            delay(100);
            digitalWrite(LED, LOW);
            delay(100);
        }
        Particle.connect();                 // Connect to Particle Cloud.
    }
    else {
        for (int i = 0; i < 6; i++) {       // 6 Flashes = no WiFi found after 15 sec.
            digitalWrite(LED, HIGH);        // Send signal then...
            delay(100);
            digitalWrite(LED, LOW);
            delay(100);
        }
        WiFi.disconnect();              // Disconnect WiFi.
        newWiFi = true;                 // Set flag to say we are listening for new WiFi credentials.
        WiFi.listen();                  // Listen for new WiFi credentials.
    }
}

                                            // MAIN PROGRAM LOOP =====================================

void loop() {
    if ((newWiFi) && (!WiFi.listening())) {
        System.reset();                     // If newWiFi and ListenTimeout already expired, reset.
    }

// Do Stuff()                               // Do rest of loop() code which will run while listening for new WiFi credentials.

}

Lots of additional testing. The code above actually works on 0.6.3! But the Photon ends up with a hard fault and automatic reset when on 0.7.0 and 0.8.0-rc11 when the WiFi network is not available (also when available, but internet connection is not). The failure happens on WiFi.connect, ends up being blocked and then SOS.

So something changed on the device OS firmware…and I still can’t get a working solution. I’ll start a new thread and look into submitting a Github issue.

PS: I did find and check to see if @ScruffR 's stack size / assertion failure SOS would fix this situation…but it doesn’t.

1 Like

Yeah, the code I shared was part of a project I made on 0.6.3 and I haven't tried it on 0.7.0 or 0.8.0. Strange that it doesn't work.

1 Like

Started a new thread over here, and also submitted an issue for it. Hopefully, we'll get it resolved.

2 Likes

An update for anyone stumbling across this thread, I’ve had the same issue in the newly released 1.0.0 where there is a hard fault SOS on WiFi.connect() when the stored wifi networks are not available or out of range.

However, after clearing all credentials, and setting up 1 wifi AP this issue went away. It would appear that maybe something was corrupted in the stored AP credentials (although under normal conditions it connected fine to the network). This happened on 4 different Photon devices so I never before considered for any reason that the stored credentials could present any problem.

My code for quickly clearing the wifi credentials on github.