Photon won't enter listening mode while connecting to wifi (works using setup button but not using external button)

That is true, but WiFi.listen() in the OPs code on one side and most of the sub steps of cloud connection on the other side demand access to the WiFi module which is a shared resource.
So whoever comes first (which in this case will be the system thread) will set a mutex (or any other thread sync object) to block other threads from interfering, so your code is probably locked out till the temporary owner of the shared resource releases it.

So if you really need SYSTEM_MODE(AUTOMATIC) your best bet would be STARTUP() and the safest way would be to go non-AUTOMATIC.

1 Like

have to ask, I presumed that if wifi creds were originally setup, then the password on the wifi was changed later, the wifi connection would time out and go to the listening mode. Is that false? does it hang in a loop believing it will connect?

I’m pretty sure it won’t enter Listening Mode just by itself.

One reasion:
Since it could well be that the SSID stored and the one your device currently sees are the same but don’t actually name the same network (e.g. AndroidAP is quite common).
So you don’t necessarily want your device to go into Listening Mode but rather have it try one of the other stored WiFi credential sets agains this or another SSID availabe.

And yes, the device will keep trying to connect to one of the stored WiFis.
Imagine a device sitting in your car and collecting data and waiting for you to return home to hop on your home WiFi. You wouldn’t want your device falling into Listening Mode and hence missing your return home.

But still, in such cases (planned offline use and change in WiFi settings) it’s still best to choose a suitable system mode and code accordingly.

1 Like

@ScruffR & @MORA. Thanks for your help. Here’s the issue though, I’m not looking for code that executes before the photon connects to wifi. I’m wanting to enter setup mode after the photon has started trying to connect to wifi but before it successfully connects. You can replicate the issue by changing your wifi password or leaving the range of your wifi network and then reseting your photon. I can use the included “setup” button in order to enter setup mode. In my product, we have another button that fits with the case. We use it both for reset and to enter listening mode. Listening mode doesn’t work unless they’re already connected to the cloud, or the hold the button while resetting the device. When the photon is stuck connecting, the entering Wifi.listen() doesn’t work.

To achieve this you’d need to interrupt any running connect and the tool for this are interrupts.

But you’d need an event that triggers the interrupt.
So you’d need to set up your button as a way to trigger the interrupt.

Is the interrupt sure to work?

No guarantees, but give this a try

//SYSTEM_THREAD(ENABLED)
SYSTEM_MODE(SEMI_AUTOMATIC)
STARTUP(prepare())

#define DEBOUNCE 250

volatile uint32_t msListening;

void ISR()
{
    if (millis() - msListening < DEBOUNCE) return;
    msListening = millis();
    // start/stop listening depending on current state
    
    if (!WiFi.listening())
    {
        Particle.disconnect();
        WiFi.listen();
    }
    else
    {
        WiFi.listen(false);
        Particle.connect();
    }
}

void prepare()
{
    pinMode(D7, OUTPUT);
    pinMode(D6, INPUT_PULLUP);
    attachInterrupt(D6, ISR, FALLING);
    Particle.connect();
}

void setup() { }

void loop() { }
1 Like

hi ScruffR:
I was searching this forum, and it works perfectly to me and it’s what exactly I want. I just have a question. I saw a link from here. Photon Interrupt Simultaneous Pin Usage
this link doesn’t mention about D6 can be interrupt pin. So how does your code work so perfect?

This totally looks like it’s going to work!! Thanks so much! You saved me!!!

One last followup: this is totally different than how I was doing my button before. Is there a simple way to differentiate between short and long presses?

I guess you can have 2 debounce ifs… One for long one for short…??

If you want to distinguish between long and short press you need to get informed about the button release too.
You could either do that by moving most of the logic out of the ISR and only set msListening (which you’d rename to something apropriate for its new use), and then poll for the button release in loop().

Or you go the “because-I-can-do-it” way :wink:
For that you put all the timing logic into the ISR too and set the trigger on CHANGE instead of FALLING, read the trigger pin to see if it’s a press or release event and act uppon the time between the two.

@mikelo, the thread you linked talks about a point to keep in mind when using attachInterrupt(), but the docs tell the whole story.

@ScruffR, the code has to stay in the ISR since the whole reason for the ISR is the fact that WiFi.listen() stalls loop(). The biggest thing to remember is that millis() does NOT run in an ISR so it can’t be used for timing, only debouncing. During bounce, the ISR is called multiple times but once the bounce has settled, it is no longer called. Even with a CHANGE interrupt, the ISR will not be able to time the amount of time the button is pushed since millis() won’t change within the ISR.

@peekay123, true to some extent.

Once Listening Mode is entered loop won’t run (unless SYSTEM_THREAD(ENABLED)), so entering Listening Mode and resetting will work independently even outside of loop.
And you can’t reset once you entered Listening Mode with that approach - unless you use SYSTEM_THREAD(ENABLED) since Listening Mode got fixed to not block in multi threading. But this would reintroduce the thread-sync-issue pointed out above, counteracting the abort-connect feature.

And true about millis() for one trigger to the ISR, but for the CHANGE you’ll get multiple triggers between which millis() will be pushed on, allowing to time the press-hold time (FALL triggers once and leaves, RAISE triggers again and leaves again - two different values for millis())
Alternatively micros() could be used even within one ISR call.

@ScruffR, good points highlighting the fact that we (Elites, Particle) should have a “standardized” way to dealing with the WiFi.listen() “states”.

As for the CHANGE interrupt, you will get both firing on the bounce, creating double interrupts which will get filtered via the debouncing. You are correct, however, in that once the button is released, a second (non debounce) millis() counter will indicate the amount of time the button was held down for, but ONLY when the button is released. So the user will need to short or long press/release for the ISR to do its magic :wink:

1 Like

Agreed, I’d go for the CHANGE ISR anyway and when I find time extend my above code for short/long press, just for the fun of it :sunglasses:

1 Like

@tslater & @peekay123: I know there might be better ways to do this and it’s not completely bullet proof, but here I’ve got a little demo code that supports short/long/excessive button-press recoginition via an interrupt.
The two range-check blocks can be filled with any functionality (including System.reset(), WiFi.listen(), …) and there is also the option for double tap recognition

//SYSTEM_THREAD(ENABLED)
SYSTEM_MODE(SEMI_AUTOMATIC)
STARTUP(prepare())

#define DEBOUNCE      5
#define SHORTPRESS 1500
#define LONGPRESS  3000
#define DBLTAP     1500

const int pinIRQ = D6;

volatile uint32_t blinkPattern = 0x88;
volatile uint32_t msTrig;    // when was last reliable trigger
volatile uint32_t msPress;   // when was the last press/hold
volatile uint32_t msRelease; // when did it get released
volatile uint32_t msHeld;    // how long was it held
volatile uint32_t msDblTap;  // how much time since last release

void ISR()
{
  if (millis() - msTrig <= DEBOUNCE) return;
  msTrig = millis();
  
  if (!digitalRead(pinIRQ))
  { // PRESSED
    msPress = msTrig;
    msDblTap = msPress - msRelease;
    msRelease = 0;
    if (msDblTap > DBLTAP)
      msDblTap = 0;
  }
  else
  { // RELEASED
    msRelease = msTrig;
    msHeld = msRelease - msPress;
    msPress = 0;
    // ******************** range check for hold time *********************
    if (DEBOUNCE < msHeld && msHeld < SHORTPRESS)
      blinkPattern = 0x08;
    else if (SHORTPRESS < msHeld && msHeld < LONGPRESS)
      blinkPattern = 0x80;
    else
      blinkPattern = 0x88;
    // ********************************************************************
  }
}

void prepare()
{
    pinMode(D7, OUTPUT);
    pinMode(pinIRQ, INPUT_PULLUP);
    attachInterrupt(pinIRQ, ISR, CHANGE);
    Particle.connect();
}

void setup() { }

void loop() 
{
  uint32_t msHold;
  uint8_t  factor = 0;
  
  if (msPress)
  { // feedback during hold
    msHold = millis() - msPress;
    factor = 4;  // dim the LED
  }
  else // after release
    msHold = msHeld;
    
  // ******************** range check for hold time *********************
  if (DEBOUNCE < msHold && msHold < SHORTPRESS)
  {
    RGB.control(true);
    RGB.color(0, 0, 255 >> factor);
  }
  else if (SHORTPRESS < msHold && msHold < LONGPRESS)
  {
    RGB.control(true);
    RGB.color(255 >> factor ,128 >> factor, 0);
  }
  else
    RGB.control(false);
  // ********************************************************************

  digitalWrite(D7, (millis() >> 2) & blinkPattern);    
}

@tslater, have a play with the time constants and get a feel about the response time :wink:

1 Like

Any chance to get this working with SYSTEM_THREAD(ENABLED) on? I’ve tried a couple tricks but it won’t work for me

What problems are you seeing?

One thing you need to consider is that Listening Mode takes exclusive access to shared resources and hence the attempt to access these from application code will block the application thread.
For this the new firmware 0.4.9 has got some tricks - have a look
https://docs.particle.io/reference/firmware/photon/#system-thread

1 Like

You can try calling WiFi.disconnect() before calling WiFi.listen() - this will interrupt the current connection attempt and enter listening mode more quickly.

2 Likes

Anyone solved this?
I have a button on a5, I can’t attach an interrupt. If the photon is connecting to wifi (blinking green) and I press the button, I see with serial logger that it’s pressed and I call wifi.listen but I have to wait 30-40sec. I also try to put wifi.listen in a timer but same behaviour.
Thansk, Flavio