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

@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

@flavio.ferrandi, are you running with SYSTEM_THREAD(ENABLED)? Do you have a Particle.process() after calling WiFi.listen()?

Yes… some code:

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);

//in a timer:

void runTimer() {
    ......
    to_check_selettore = true;
    ......
}   

//in loop:

void loop() {

    if(to_check_selettore == true) {
        to_check_selettore = false;
        getSelettore();
    }

    //leggo il pulsante
    if(request_pulsante == 2) {
        request_pulsante = 0;
        ......
        WiFi.listen();
        Particle.process();
        ......
    }
    
}

//read button status
void getSelettore() {
    int valoreSelettore = analogRead(SELETTORE);
    int lastRead
    .......
    if(valoreSelettore > 2500) lastRead = 2;
    .......
    if(lastRead != lastAnalogRead && lastRead == 2) request_pulsante = 2;
    .......
    lastAnalogRead = valoreSelettore;
    .......
}

thanks a lot!
Flavio

    int valoreSelettore = analogRead(SELETTORE);
    int lastRead
    .......
    if(valoreSelettore > 2500) lastRead = 2;
    .......
    if(lastRead != lastAnalogRead && lastRead == 2) request_pulsante = 2;

I can’t quite make sense of the last if statement.
lastRead will always be an arbitrary value when coming into the function, or will be set to 2, so in any case it’s highly unlikely that it will ever be lastRead == lastAnalogRead.
So in almost every cases this would do just the same thing

  if(valoreSelettore > 2500) request_pulsante = 2;

And even if you declared it a static int, it’s not always true that two analogRead() actions will actually give you exactly the same value, even if the external signal has not changed.

Sorry, it’s only a little piece of the code…
I do it because I call wifi.listen only when I release the button…
This code is working, I can see it with serial logger… The problem is that it takes 30 seconds to execute wifi.listen. If I’m connected to the cloud it’s immediate, if It’s connecting no…
I can’t interrupt particle.connect(). If I try wifi.connect and particle.connect on wifi.ready It works well, but I don’t want to handle both wifi and particle connections… It could be complicate in some case.
Thanks a lot!

I see, have you ever tried my interrupt driven code further up?
Or WiFi.disonnect() as MDMA suggested?

BTW: When you say: “when I release the button”, why are you using analogRead() instead of digitalRead()?
A button would definetly call for an interrupt driven approach.

I tried Wifi.disconnect, not the interrupt (I can’t)
The project is complicated, I don’t have enough pin for every input I need, I have to use an analog pin with a divider to connect 3 button to 1 pin.
So I can’t attach an interrupt to an analog pin… But right now no solution…
Thanks, Flavio

OK, I see, but have you considered repurosing the onboard SETUP button?
That’s possible too.

https://docs.particle.io/reference/firmware/photon/#buttonpushed-
https://docs.particle.io/reference/firmware/electron/#system-events-overview

I can’t, the photon it’s not accesible, It’s closed in a box and hidden.
It’s not possible to create a software interrupt? I tryed also a timer but It didn’t do the trick…
Thanks a lot!

You can still lead the SETUP button to the outside of your eclosure since there is a dedicated solder pad underneath (ment for SMD but still available for through hole use).
That would be pad 26 in this schema

You can create interrupts via hardware timers but that would go a bit deeper.
A good library that does that for you is SparkIntervalTimer which can be found on Web IDE.

As well as calling WiFi.disconnect(), be sure to also disconnect from the cloud first (Particle.disconnect()) when in automatic/semi-automatic mode, or the system will try to re-establish the cloud connection, and bring up Wi-Fi again.

1 Like