WiFi.listen(false) does not exit listening with 0.6.1

I am using the SoftAP to setup wifi. The basic process works fine and I select a WAP and can enter credentials and the photon accepts the credentials, I can then exit listening mode using WiFi.listen(false); and the device will automatically connect to the wifi details stored and the cloud.

However, I have a timer running for timeout of the wifi setup process. When this ends, I display a message and then call WiFi.listen(false); to exit listening mode. However, in this case the photon does not exit and stays flashing dark blue rather than breathing dark blue (no credentials stored but wifi on) as I was expecting. I am using 0.6.1. Is this a ‘feature’ that has been introduced as I know that this was working before. Is this linked to the new feature for WiFi.setListenTimeout(seconds);?

What SYSTEM_MODE are you using and are you using SYSTEM_THREAD(ENABLED)?

Yes,
SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);

I have the same basic code operating in another application and I have not encountered this issue before. Possible that I have not tested it since building with 0.6.1.

Would using the WiFi.setListenTimeout() function help? I am not that clear how it works. If I set the timeout before I call WiFi.listen() it will automatically call WiFi.listen(false) when the timer finishes? So to know whether the listen command has timed out I need to check with WiFi.listening() == false?

Yup, this would rid you of the need to call WiFi.listen(false), but I don't quite know how this could be explained any clearer than it already is :wink:
https://docs.particle.io/reference/firmware/photon/#setlistentimeout-

Not exactly, but on expiry the system will do something very similar to what WiFi.listen(false) would do.

Something like that.
Again the difference is in the wording. You don't check whether the timer has expired, but you check whether your device (still) is in Listening Mode or not - which is somewhat synonymous, but not completely.
There are also other reasons why your device could drop out of LM than just the timeout and !WiFi.listening() wouldn't give you a clue why it's not listening anymore.

And for your original question, WiFi.listen(false) should still work as it used to - I'll have to check.

I have been doing more testing.

Instead of WiFi.listen(false); I have tried while(WiFi.listening()) WiFi.listen(false); No difference.

When there are no credentials stored and WiFi.listen(false) is called it does nothing and leaves the device listening (flashing blue LED).

If I restart the device the device goes to breathing blue (correct).

If I try WiFi.disconnect(); then WiFi.connect(); it briefly goes to breathing blue then back to flashing blue!

If I use WiFi.setListenTimeout(120); this does breakout of listening.

That would have been some crucial info to start with :wink:

BTW, with this little test sketch I see the expected behaviour just fine

SYSTEM_MODE(SEMI_AUTOMATIC)
SYSTEM_THREAD(ENABLED)

void setup() {
  WiFi.on();
  WiFi.listen();
  pinMode(D7, OUTPUT);
}

void loop() {
  static uint32_t ms = 0;
  digitalWrite(D7, (millis() >> 2) & 0x88);
  if (millis() - ms < 40000) return;
  ms = millis();
  WiFi.listen(false);
  //if (Particle.connected()) Particle.disconnect();
  //else                      Particle.connect();
}

With stored credentials the device droppes out of LM after 40sec into breathing green and without creds into breathing blue.
Even after entering LM a second time via SETUP it still works.
Only with the Particle.connect() command in place and no credentials stored, after dropping out of LM the device will immediately enter LM again - but that is also the expected behaviour, as Particle.connect() undoubtedly will require credentials and hence will implicitly force your device back into LM - so would WiFi.connect()

It has taken me a while to fully understand what was going on.

Eventually I noticed that at the end of the timeout period - whether I used my own timer and called WiFi.listen(false) at the end of the timeout period or used WiFi.setListenTimeout(); it did stop listening but then immediately went back to listening. This turned out to be because in an asynchronous millis() driven checking loop I was updating the wifi connection symbol and this function called Particle.connect() which should not have happened (flag setting issue!!).

I am still convinced that something in the operation of these WiFi functions has been changed in 0.6.1 or 0.6.2.

The key to finding this your last para:

Only with the Particle.connect() command in place and no credentials stored, after dropping out of LM the device will immediately enter LM again - but that is also the expected behaviour, as Particle.connect() undoubtedly will require credentials and hence will implicitly force your device back into LM - so would WiFi.connect()

Thanks

1 Like

Update on this. It appears that Particle.connect() is not being called after WiFi.listen(false); but was called before the wifi setup process was started. Maybe this is what you meant - but I did not understand on first reading.

As the setup process clears the credentials before going into listening mode when the listening stops (and it does for a while after the WiFi.listen(false)) the particle connect mode seems to step in and push it back into listening mode. Thus, if I want the device to connect to the cloud, it cannot timeout of listening with no credentials unless I call Particle.disconnect()? However, I then need to call Particle.connect() before any further wifi setup attempt? Is that the right logic?

I'm not entirely sure what your use case is here :confused:

If your code may or may not have cleared the credentials I'd see the responsibility to check for the availability of credentials before calling WiFi.connect() or Particle.connect() via WiFi.hasCredentials().
If you expect your device to have no credentials out the box or due to user interaction you should also check the availability of creds before any connection attempt.

As you guessed correctly, when a Particle.connect() (or WiFi.connect()) implicitly forces the device into Listening Mode the connection attempt cannot be considered finished but is still ongoing while and till valid credentials are supplied and the attempt succeeded. So if you want to end that attempt you need to actively abort this process (or even better not even attempt in first place as said above).

Actually that's not what I meant since I never would have thought round these corners as I wouldn't try an attempt that might with relatively high probability fail - I'm used to defensive programming :sunglasses:

1 Like

The Use Case(s) are as follows:

Product user (the photon is inside of) presses a button during startup (after a power on) - this follows normal startup which turns on wifi and attempts to connect to the Particle Cloud and then presents instructions for wifi setup using softAP.

There is a timeout on this setup process which returns the device to the ‘standby’ if the user does not manage to complete the credentials input or abandons the setup.

A user may try setup straight out of the box when no credentials are on the device.

The wifi setup process has to be robust and straight-forward for the end users, but also, I need the device to gain and maintain a cloud connection.

I am checking for credentials before calling Particle.connect(); See below. Whilst you clearly understand these processes I only now understand that the device will try and try to gain a cloud connection once a single connect() is requested and that state continues through listening and thereafter.

            if (!isTriedConnectOnce)
            {
                if (WiFi.hasCredentials())
                {
                    Particle.connect();
                    isTriedConnectOnce = true;
                }
            }

Another approach to this is if there are no credentials when the listening mode is about to be started then issue a Particle.disconnect(); I appreciate that this is messy and is getting away from the idea whoever created the Particle.connect() function was thinking of.