Resetting my photon wifi if no connection is found

Hi, I’m sure there’s a tutorial about this but I’m not finding it. My project, a grill/smoker monitor, is complete and working, but I need it to be flexible in the wifi connection. It’s currently connected to my home wifi, but I’d like to add some code to my sketch so that if it doesn’t connect to wifi within a minute or so, it will go into that mode where it was when I first bought it–where I would use my phone and the Particle app, and connect my phone to the wireless photon-xxx and enter the SSID & password. This way I can use my grill/smoker monitor at a friends house, etc. without having to reflash code to it.
thanks.

Great! I found the term I was looking for. It’s called WiFi.listen();

And this post explains how to setup a timer to check if wifi is connected or not, and then go into listen mode.
https://community.particle.io/t/make-particle-photon-go-into-listening-mode-when-no-wifi-is-found/41985

Well, I tried this, but it doesn’t seem to be putting my Photon into listen mode. I’m not sure why. Here’s my code:

// This #include statement was automatically added by the Particle IDE.
#include <LiquidCrystal.h>

// This #include statement was automatically added by the Particle IDE.
#include <blynk.h>

#include "math.h"
// We're going to start by declaring which pins everything is plugged into.
int Probe1Pin = A0; //first probe
int Probe2Pin = A1; //second probe


//Declare variables
int probe1Value; // Here we are declaring the integer variable analogvalue, which we will use later to store the value of the photoresistor.
int probe2Value; // Here we are declaring the integer variable analogvalue, which we will use later to store the value of the photoresistor.
unsigned long loopMillis;

struct settings {
    bool Probe1Enabled; //V0; true = we're using probe 1
    bool Probe2Enabled; //V1; true = we're using probe 2
    int Probe1MaxTemp;  //V2; max temp for probe
    int Probe1MinTemp;  //V3; min temp for probe
    int Probe2MaxTemp;  //V4; max temp for probe
    int Probe2MinTemp;  //V5; min temp for probe
    bool Probe1Notifications; //V6; true will send them
    bool Probe2Notifications; //V7; true will send them
    
} s;

//Blynk init
char auth[] = "<redacted>";



// Make sure to update these to match how you've wired your pins.
// pinout on LCD [RS, EN, D4, D5, D6, D7];
// pin nums LCD  [ 4,  6, 11, 12, 13, 14];
// Shield Shield [RS, EN, D4, D5, D6, D7];
// Spark Core    [D3, D5, D2, D4, D7, D8];
LiquidCrystal lcd(D0, D1, D2, D3, D4, D5);







void setup() {
    EEPROM.get(1, s); //get the settings from EEPROM


    // set up the LCD's number of columns and rows: 
    lcd.begin(16,2);
    // Print a message to the LCD.
    lcd.print("GRILL MONITOR V1");
    delay(2000);
    lcd.clear();
    
    STARTUP(WiFi.selectAntenna(ANT_EXTERNAL)); // selects the u.FL antenna
    // We are going to declare a Particle.variable() here so that we can access the value of the photoresistor from the cloud.
    Particle.variable("probe1Value", &probe1Value, INT);

    Particle.variable("probe2Value", &probe2Value, INT);
    Blynk.begin(auth); //begin Blynk

}


void loop() {
    //Check if WiFi is already established.
    if (!WiFi.ready()) {
        //Check if we are already waiting for WiFi to be ready and not listening for WiFi.
        if (waitingMillis && !listeningForWiFi) {
        //Check how long we have been waiting for WiFi to be ready.
            if (millis() - waitingMillis > 30000) {  //2 minutes wait time.
            listeningForWiFi = true;  //Set a flag so that we call WiFi.listen() once and not on every loop.
            WiFi.listen();  //Put WiFi into listening mode.
            }
        } else {
        //Record the time that we started waiting for WiFi to be ready.
        waitingMillis = millis();
        }
    } else {
        waitingMillis = 0;  //Reset the waiting variable if WiFi is already established.
        listeningForWiFi = false;  //Reset the listening variable if WiFi is already established.
    }


    Blynk.run();
    if (millis() - loopMillis > 5000UL) { //do this every 5 seconds
        loopMillis = millis(); //update the timer
        // check to see what the value of the photoresistor is and store it in the int variable analogvalue
        probe1Value = thermister_temp(analogRead(Probe1Pin));
        probe2Value = thermister_temp(analogRead(Probe2Pin));
        gaugeColors();
        if (s.Probe1Enabled) {
            Blynk.virtualWrite(V10, probe1Value);
        }
        else {
            Blynk.virtualWrite(V10, 0);
        }
        if (s.Probe2Enabled) {
            Blynk.virtualWrite(V11, probe2Value);
        }
        else {
            Blynk.virtualWrite(V11, 0);
        }
        //update wifi strength every 5 seconds
        SendWIFIlevel();
        updateLCD();
    }
    Send_Notifications();

}


int thermister_temp(int aval) {
  double R, T;

  // These were calculated from the thermister data sheet
  //  A = 2.3067434E-4;
  //  B = 2.3696596E-4;
  //  C = 1.2636414E-7;
  //
  // This is the value of the other half of the voltage divider
  //  Rknown = 22200;

  // Do the log once so as not to do it 4 times in the equation
  //  R = log(((1024/(double)aval)-1)*(double)22200);
  R = log((1 / ((4095 / (double) aval) - 1)) * (double) 22200);
  //lcd.print("A="); lcd.print(aval); lcd.print(" R="); lcd.print(R);
  // Compute degrees C
  T = (1 / ((2.3067434E-4) + (2.3696596E-4) * R + (1.2636414E-7) * R * R * R)) - 273.25;
  // return degrees F
  return ((int) ((T * 9.0) / 5.0 + 32.0));
}

BLYNK_WRITE(V0) {     //probe 1 enabled
    s.Probe1Enabled = param.asInt();
    EEPROM.put(1, s); //save the settings to EEPROM
    if (s.Probe1Enabled) lcd.clear(); //remove the chars left on the display
}

BLYNK_WRITE(V1) {     //probe 2 enabled
    s.Probe2Enabled = param.asInt();
    EEPROM.put(1, s); //save the settings to EEPROM
    if (s.Probe1Enabled) lcd.clear(); //remove the chars left on the display
}

BLYNK_WRITE(V2) { //probe 1 max temp
    s.Probe1MaxTemp = param.asInt();
    EEPROM.put(1, s); //save the settings to EEPROM
}

BLYNK_WRITE(V3) { //probe 1 min temp
    s.Probe1MinTemp = param.asInt();
    EEPROM.put(1, s); //save the settings to EEPROM
}

BLYNK_WRITE(V4) { //probe 2 max temp
    s.Probe2MaxTemp = param.asInt();
    EEPROM.put(1, s); //save the settings to EEPROM
}

BLYNK_WRITE(V5) { //probe 2 min temp
    s.Probe2MinTemp = param.asInt();
    EEPROM.put(1, s); //save the settings to EEPROM
}

BLYNK_WRITE(V6) { //probe 1 notifications
    s.Probe1Notifications = param.asInt();
    EEPROM.put(1, s); //save the settings to EEPROM
}

BLYNK_WRITE(V7) { //probe 2 notifications
    s.Probe2Notifications = param.asInt();
    EEPROM.put(1, s); //save the settings to EEPROM
}

BLYNK_CONNECTED() {
    Blynk.virtualWrite(V0, s.Probe1Enabled);
    Blynk.virtualWrite(V1, s.Probe2Enabled);
    Blynk.virtualWrite(V2, s.Probe1MaxTemp);
    Blynk.virtualWrite(V3, s.Probe1MinTemp);
    Blynk.virtualWrite(V4, s.Probe2MaxTemp);
    Blynk.virtualWrite(V5, s.Probe2MinTemp);
    Blynk.virtualWrite(V6, s.Probe1Notifications);
    Blynk.virtualWrite(V7, s.Probe2Notifications);
}

void gaugeColors() {
  //Set meat probe gauge colors
  //Green if OK, Red if out of range
    if (s.Probe1Enabled) {
        if (probe1Value > s.Probe1MaxTemp) { //probe 1 is higher than normal
            Blynk.setProperty(V10, "color", "#D3435C"); //change it to red
        }
        else if (probe1Value < s.Probe1MinTemp) { //probe 1 is less than normal
            Blynk.setProperty(V10, "color", "#04C0F8"); //blue
        }
        else Blynk.setProperty(V10, "color", "#23C48E"); //green //set to normal color
    }
    else Blynk.setProperty(V10, "color", "#23C48E"); //green //set to normal color
  
  //probe 2
   if (s.Probe2Enabled) {
        if (probe2Value > s.Probe2MaxTemp) { //probe 2 is higher than normal
            Blynk.setProperty(V11, "color", "#D3435C"); //change it to red
        }
        else if (probe2Value < s.Probe2MinTemp) { //probe 2 is less than normal
            Blynk.setProperty(V11, "color", "#04C0F8"); //blue
        }
        else Blynk.setProperty(V11, "color", "#23C48E"); //green //set to normal color
    }
    else Blynk.setProperty(V11, "color", "#23C48E"); //green //set to normal color
}

void Send_Notifications() {
    static unsigned long prevMillis; //timer for notifications
    if (millis() - prevMillis > 60000) { //only send notifications this often
        if (s.Probe1Notifications && s.Probe1Enabled) {    
            if (probe1Value > s.Probe1MaxTemp) { //probe 1 is higher than normal
                Blynk.notify("Probe 1 temperature is too high");
                prevMillis = millis(); //reset timer
            }
            else if (probe1Value < s.Probe1MinTemp) { //probe 1 is less than normal
                Blynk.notify("Probe 1 temperature is too low");
                prevMillis = millis(); //reset timer
            }
        }
        
         if (s.Probe2Notifications && s.Probe2Enabled) {    
            if (probe2Value > s.Probe2MaxTemp) { //probe 1 is higher than normal
                Blynk.notify("Probe 2 temperature is too high");
                prevMillis = millis(); //reset timer
            }
            else if (probe2Value < s.Probe2MinTemp) { //probe 1 is less than normal
                Blynk.notify("Probe 2 temperature is too low");
                prevMillis = millis(); //reset timer
            }
        }
    }
}
    
void SendWIFIlevel() {
    int rssi = WiFi.RSSI();
    //WiFiSignal rssi = WiFi.RSSI();
    Blynk.virtualWrite(V8, rssi);


}

void updateLCD() {
    lcd.setCursor(0, 0);
    if (s.Probe1Enabled) {
         lcd.print("PROBE 1: ");
         lcd.print(probe1Value);
         lcd.write(0xdf);
         lcd.print("F  ");
    }
    else {
        lcd.print("PROBE 1 DISABLED");
    }
    
    lcd.setCursor(0, 1);
    if (s.Probe2Enabled) {
         lcd.print("PROBE 2: ");
         lcd.print(probe2Value);
         lcd.write(0xdf);
         lcd.print("F  ");
    }
    else {
        lcd.print("PROBE 2 DISABLED");
    }
    


}


Well, I’m at a loss. I’ve spent a few hours trying to get this to work, to no avail. I made a basic sketch of what I thought should cause the Photon to go into Listen mode if it doesn’t get wifi connection within 30 seconds. Here is my code. If it has wifi, the LCD counts out the millis like it’s supposed to. A few seconds after turning out my router, and the LCD says no wifi, but doesn’t count the millis, indicating that it’s not continuing in the loop function to get to the timeout where it puts it in listen mode.
Does anyone have a basic example of code putting it into listen mode if no wifi is found after a minute or so? Thanks so much.

// This #include statement was automatically added by the Particle IDE.
#include <LiquidCrystal.h>

// This #include statement was automatically added by the Particle IDE.

unsigned long loopMillis;
LiquidCrystal lcd(D0, D1, D2, D3, D4, D5);


void setup() {
    // set up the LCD's number of columns and rows: 
    lcd.begin(16,2);
    // Print a message to the LCD.
    lcd.print("Welcome");
    delay(2000);
    lcd.clear();
    
    STARTUP(WiFi.selectAntenna(ANT_EXTERNAL)); // selects the u.FL antenna
}


unsigned long waitingMillis = 0;
bool listeningForWiFi = false;


void loop() {
    //Check if WiFi is already established.
    if (!WiFi.ready()) { //if wifi is not ready
        lcd.setCursor(0, 0);
        lcd.print("NO WIFI.");
        lcd.print(millis()/1000);
        if (!listeningForWiFi) { //if we aren't already in listening mode
            //Check how long we have been waiting for WiFi to be ready.
            if (millis() - waitingMillis > 30000) {  //2 minutes wait time.
                lcd.setCursor(0, 1);
                lcd.print("Now listening");
                listeningForWiFi = true;  //Set a flag so that we call WiFi.listen() once and not on every loop.
                WiFi.listen();  //Put WiFi into listening mode.
            }
        } 
    } 
    else { //wifi is ready
        listeningForWiFi = false;  //Reset the listening variable if WiFi is already established.
        waitingMillis = millis(); //Record the time that we started waiting for WiFi to be ready.
        lcd.setCursor(0, 0);
        lcd.print("GOOD WIFI.");
        lcd.print(millis()/1000);
    }
}


Hi Charlie, maybe you have thought about this already: why not forcing your photon to enter listen mode by pressing the setup button for more than 3 seconds?

https://docs.particle.io/tutorials/device-os/led/photon/#listening-mode

Thanks!

Not as sophisticated, but is using the phone as a wifi hotspot an option?

image
Because it’s all tucked away inside this enclosure. I made a custom pcb that’s a daughter board to the lcd display.

I just assumed it was easy to put the device in listen mode programmatically. I’m quite surprised there isn’t any working code example of this. I’ve seen a few topics about it but no actual working code that I’ve found. Some I tried made me spend 2 hours downloading some utility to unbrick my photon Because it wouldn’t connect to WiFi or take a sketch upload. :slight_smile:

Hi @SouthernAtHeart -

I had the same issue and solved it with the code below. It has been a while since I last used it, humble apologies if there is some glitch :slight_smile:

#include "Particle.h"

//STARTUP(WiFi.selectAntenna(ANT_INTERNAL));
STARTUP(RGB.mirrorTo(D3, D2, D1)); 

unsigned long old_time = millis();              // set Timeout to enter Listening Mode

int redPin = D3;       
int greenPin = D2;    
int bluePin = D1;

void setup() {
    
    pinMode(redPin, OUTPUT);
    pinMode(greenPin, OUTPUT);
    pinMode(bluePin, OUTPUT);

void loop() {

    if(millis() - old_time >= 30000 && millis()) {
            if(!WiFi.ready()){
                WiFi.listen();
            }
      }    
    

In the code it enters Listen mode after 30s of not being connected to WiFi. You can also set a Listen mode timeout in the beginning exiting the listen mode again after some time, up to you.

edit: What I have done recently (if you have any exposed pins on you enclosure for any sort of connector) was to use interrupt pins. This way, by either pulling that pin HIGH or LOW can enable listen mode. If you need a code example for this, I will be happy to send it.

Hope this help.

Regards,
Friedl

There are some issues with your code

  • the STARTUP() macro needs to be outside of setup() - it has no business inside any other function
  • in default SYSTEM_MODE your code will not run (hence never reach your WiFi.listen() call) when there is no connection to the cloud. To overcome that the easiest is to add SYSTEM_THREAD(ENABLED) to decouple your code from the cloud process - be aware that your code will then start running immediately whether a connection exists or not (use waitFor() before calling anything that relies on a connection - e.g. Blynk.begin()) - IMO this is also a reason why @friedl_1977’s solution cannot really work as is :wink:
  • you should put your Particle.variable() registrations before any potentially long running calls, particularly befor your delay(2000)
  • you are using the outdated syntax for Particle.variable() - it should now only be Particle.variable("probe1Value", probe1Value);
  • I redacted your Blynk token so noone can take over your smoker

BTW, the correct term is thermistor not thermister

2 Likes

sorry… :see_no_evil:

my understanding was it has been connected to WiFi and then looses connection. I encountered the situation where (if it has never been connected to a router) it will not enter enter Listen Mode as pointed out, and therefor went with the second option (exploiting the exposed pin and use interrupt pin) . In this case I use SYSTEM_MODE(SEMI_AUTOMATIC); and it seems to work just fine.

SYSTEM_MODE(SEMI_AUTOMATIC);

boolean connectToCloud = true;

void setup() {
    
    pinMode(D7, OUTPUT);
    attachInterrupt(A2, connect, FALLING);
    
    WiFi.setListenTimeout(300);
}

void loop() {
    
     if(connectToCloud) {
        if(Particle.connected() == false) {
             Particle.connect();
             connectToCloud = false;

        }
    }
}

 void connect() {
     connectToCloud = true;
     WiFi.listen();
    }

I used this in conjunction with SoftAP, seems do be doing the job. I am by no means coming it is the best or neatest code, but best I could come up with :relaxed:

Apologies again if I steered @SouthernAtHeart in the wrong direction :exploding_head:

Regards,
Friedl

1 Like

Even then, with default SYSTEM_MODE(AUTOMATIC), the flow will stop as soon the device realises the lost connection and won’t call loop() again until the issue has been resolved.
Of course interrupts and system event triggers are not affected by this (directly) as they run at a lower level closer to the HW.

1 Like

Maybe why I never got that working 100% :rofl:

1 Like

Thanks, I’ll make these adjustments you mentioned and see if that helps. I’ll find out how to use system thread. And waitfor, and will maybe maybe a stripped down test code which includes Blink run in it but not much else, to see if I can get it going.
I’ll holler back next week.
Thanks,
Cheers,

HI @SouthernAtHeart -

Good plan, I actually just recommended same approach to fault finding to another user. Start with very basic functionality and incrementally adding more. Sounds like you are on the right track.

Well, at least this is how I do it :smile:

Best of luck.
Regards,

One solution will be to declare your different credential in the code !
Here is what i do for one of my old project :

int timeout = 10000;
SYSTEM_MODE(SEMI_AUTOMATIC);

void setup() {
  WiFi.on();
  WiFi.setCredentials("SID1", "Password1");
  WiFi.setCredentials("SID2", "Password2");
  WiFi.connect();
  waitFor(WiFi.ready, timeout);
  
  if (!WiFi.ready()) {System.reset();}

Hope it helps !
Regards,
Thierry

1 Like

Just one word of caution for that: If the network(s) you try to set this way are not present/visible at time of execution it’s not guaranteed that the credentials will actually get stored as the encryption scheme and WLAN cipher cannot be determined but are required for a successful connection.

1 Like

Hi -

Just my 2 cents worth. I have used this approach before one a project, see my code below:

WiFi.on();

if (!WiFi.hasCredentials()) {
WiFi.setCredentials("SSID NAME", "YourPassword", WPA2, WLAN_CIPHER_AES);      
}

WiFi.connect();
waitUntil(WiFi.ready);
Particle.connect();

The reason I do not like this approach is that if some clever techie decides for no apparent reason as they often do, it is time to change the Network credentials or update routers and AP’s… the device will stop working and you will have to get hands on the reconnect the device.

The second reason is exactly the one pointed out by @ScruffR. If the WiFi network out of range or simply too weak signal, it will cause a problem. I had to open one of the devices while client was looking suspiciously at me, to ‘force’ the device into listen mode, and enter credentials via Particle App.

Hence my search for fail safe solutions to be able to get the device back in listen mode without having to open the enclosure.

Best of luck!!
Regards, Friedl.

A reed switch would spring to mind - on a Photon it can even be wired parallel to the MODE/SETUP button via the solder pad at the bottom :wink:

1 Like

Thanks @ScruffR -

As I usually have exposed pins on the enclosure for external sensor, I decided to use interrupts and have one of the ‘ports’ to double up as a “enter listen mode switch” by pulling it to GND.

Not sure it this is how it should be done, but seems to be working :relaxed: The reed switch seems like a great idea, I will most certainly look into it!

Regards,
Friedl.

Facilitating the MODE/SETUP pad would also free up one custom GPIO for other use - it’s also active LOW (pulling to GND for activation).

1 Like