Wifi Stratagey Template

Hi All,
I have selected the Photon for a project that will be outside and not accessible very often. The project needs to connection to the cloud to register ‘events’.

The program needs to capture events regardless of Wifi Connection, which we have done, but we find that when Wifi access goes away (sometimes Photon seems to get bored, or if the wifi is really not available) the system seems to hang and cannot go about it’s normal business.

We have researched the forums and of course there is a lot of chatter.

I would imagine with Wifi being the biggest single point of failure, that the Particle guys will have built the product to deal with this situation.

We have tried all sorts of wifi.connect() type code snippets (all from the web) but none seem to work and they all seem to get more and more complex.

My test harness is:

  1. Using an Iphone Hotspot next to the Photon, with System Automatic, an external atenna and code something like this in setup.

    WiFi.on();
    WiFi.clearCredentials();
    WiFi.setCredentials(SSID, PASSWORD);
    WiFi.connect();
    if (WiFi.ready() )
    {
    Particle.connect();
    waitFor(Particle.connected, 10000);
    Particle.syncTime();

     Particle.publish(" Setup Completed", VERSION_STRING);
    

    }

Forgetting the complexities in the loop, here is what happens when we reset the Photon.

  1. Reset the Photon with the hotspot up and running.
    All works fine

  2. Reset the Photon with the hotspot not running
    We get flashing blue - not green as we thought we would

  3. Reset the Photon with the hotspot running but the password changed
    We get flashing green, it duly waits 10 seconds and then carries on, note if we then change the password back on the hotspot, we then suddenly get a rapid green flash for a few seconds followed by a breathing green, We then have to reset the Photon to get back to a working state.

What I cannot simulate is a connection that is not stable. We have noticed that the Photon often goes in to a ‘trying to connect’ mode after a couple of days and cannot get back on, this of course will drain the batteries you are on and eventually the whole system is out…

One real worry is that when the system is up and running (breathing Cyan) and we then take the hotspot down, then the program seems to hang (we have an LCD display to help on this and of course Serial Monitor) and we have a flashing green.

This is the worst situation for us, as in this position we cannot record any data in the hope of doing a lazy upload later.

I am sorry this is a long winded post, but I think it is probably what most people would expect to be able to do out of the box, So we must be missing something, and I think a ‘robust’ code template would be a really useful tool.

Thanks for reading and look forward to your pointers.

jon

Have you tried the system threading?
https://docs.particle.io/reference/firmware/photon/#system-thread

1 Like

In addition to @Moors7 suggestion, I'd still suggest to consider SYSTEM_MODE(SEMI_AUTOMATIC) as first line of defence and only additionally use SYSTEM_THREAD(ENABLED) as your Swiss Army Knife.

BTW, unconditionally clearing credentials is not the best practice, since it imposes flash wear on the device and if you're unlucky to you get a crash between that and re-setting the creds, you'll be stuck in Listening Mode.
However, you could set a LM timeout or write your own timeout logic for that. But still unconditional clearing and re-setting creds is bad.
Why not just use the five available slots for credentials? Once these are filled, the oldest set will be overwritten.

Also to set the creds by code you either need to have the WiFi present or use the four-parameter overload of WiFi.setCredentials().

And yes, you imagine right, Particle has already put all these things in place to tackle such "issues"

Hi Jon,

When I read your original post a couple of times over, I wonder if there are a couple of things going on at the same time.

When you say

and

it makes me think that the application code is keeping the device too busy to process the WiFi connection, which it needs to do regularly. Particle has given us system threading and SYSTEM_MODE as @Moors7 and @ScruffR said along with the particle.process() command to help manage the WiFi processing when the application code is large or complex. I would suggest taking a look to see if this might be the case; there are lots of threads on the forum dealing with these cases.
The other possibility when it hangs is the heap memory may have become too fragmented. That is often from using Strings in the application code and again, there are lots of threads dealing with this issue.
These two things I mentioned above do cause the WiFi to be lost and the device to behave strangely.

Now, if WiFi is not available to the device, that situation can be dealt with as well. However, the solutions below do not work if any of the issues I mentioned above have not been dealt with first.

This particular bit of code could be an issue:

waitFor(Particle.connected, 10000);
Particle.syncTime();

The wait command waits until it is connected or it times out after a period of 10000 milliseconds.
Then the code tries to sync the time whether it is connected or not. I am not certain, but there is a possibility of strange behavior to syncTime when there is no connection.
Maybe try something like this.

waitFor(Particle.connected, 10000);
if particle.connected() {
    Particle.syncTime();
}
else // not connected
{
    // do something else
}

This particular example may be a bad example, since particle.connected may do syncTime authomatically (UPDATE: Yes, it is a bad example since the time is synced automatically; see https://docs.particle.io/reference/firmware/photon/#particle-synctime-), but it gives an idea of strategies to handle WiFi throughout the application code.

If you want to record data while WiFi is not available, then there are a couple of options.
The first option is using Backup RAM, which is in the docs here:
https://docs.particle.io/reference/firmware/photon/#backup-ram-sram-
If you are also worried about losing power or the device resetting, then the EEPROM function can store data even with loss of power, see the docs here:
https://docs.particle.io/reference/firmware/photon/#eeprom
If you need these capabilities, there are examples to be found in the forums.

We might be able to provide more specific advice, but we would need something more specific to analyze.
Hope this helps!

Hi All,

Thanks for your time and helpful comments. I have read around the forum and picked up some code and created a test harness, it makes it simpler.

The test harness simply wants to connect to a single network, and then be able to read some sensors.
The Setup uses code lifted from the forums and the loop simply waits one second, prints the time, time since last call and the current network.

The goal of my harness is to be able to connect to the network, know if I am connected or not and always be able to run code in my loop so I do not miss data.

My Test harness consists of a Photon connected to an external antenna and my iPhone 7 hotspot.

My testing routes so far have been:
1. Reset the Photon with the hotspot on and running
– sample output to Serial Monitor

Connecting to LAN

Already connected to network.
Connected to Network:
Setup Completed
Thu Jan 1 00:00:02 1970 (2), Connected to Network: Jsx_iphone
Thu Jan 1 00:00:03 1970 (1), Connected to Network: Jsx_iphone
Thu Jan 1 00:00:04 1970 (1), Connected to Network: Jsx_iphone
Tue Feb 28 14:37:23 2017 (1488292639), Connected to Network: Jsx_iphone
Tue Feb 28 14:37:24 2017 (1), Connected to Network: Jsx_iphone
Tue Feb 28 14:37:25 2017 (1), Connected to Network: Jsx_iphone
Tue Feb 28 14:37:26 2017 (1), Connected to Network: Jsx_iphone
Tue Feb 28 14:37:27 2017 (1), Connected to Network: Jsx_iphone

2. Drop the sharing hotspot
– sample output to Serial Monitor
Tue Feb 28 14:38:20 2017 (1), Connected to Network:
Tue Feb 28 14:39:02 2017 (42), Connected to Network:
Tue Feb 28 14:39:44 2017 (42), Connected to Network:
Tue Feb 28 14:40:26 2017 (42), Connected to Network:

3. Reset the Photon whilst the hotspot is not on
– sample output to Serial Monitor
NO DATA - Setup never gets called

Results

Test 1 works perfectly as expected, I get a delay of 1 second per loop once up and running.

Test 2, Photon flashes green and the loop now only gets called every 42 seconds - this means I would miss a lot of data.

Test 3 Photon Flashes Blue, Setup never gets called - all data lost

All of the tests are what I would expect to happen when I am on site.

Again all help and pointers are gratefully accepted.
BTW this is for a school Traffic Monitor webby project for our Year 6-7 !

Here is the code

 #define WAIT_FOR_CONNECTION_SECONDS (60 * 1000) 
void setup() {
    Serial.begin(9600);
    Time.setTime(0);
  
    delay(2000);
    Serial.println();
    Serial.println("Connecting to LAN");
    connect();
    Serial.println("Setup Completed");
}

unsigned long lastTime= 0;

void loop() {
    int diff = Time.now() - lastTime;
    Serial.printlnf("%s (%d), Connected to Network: %s", Time.timeStr().c_str(), Time.now() - lastTime, WiFi.SSID());
    lastTime = Time.now();
    delay(1000);
    
  // collect some data etc.
  return;
  
}

void connect() 
{
    time_t timeStarted = Time.now();
    
    WiFi.on();
    
    if (!Particle.connected()) 
    {
        Serial.printlnf("Connecting to network at %s ( %d seconds), waiting for up to %d seconds...", Time.timeStr().c_str(), Time.now(), WAIT_FOR_CONNECTION_SECONDS);
    
        Particle.connect();
        if (waitFor(Particle.connected, WAIT_FOR_CONNECTION_SECONDS)) 
        {
            Serial.printlnf("Connected at %s ( %d seconds, connection process took %d seconds).", Time.timeStr().c_str(), Time.now(), Time.now() - timeStarted);
        } 
        else 
        {
            Serial.printlnf("WARNING: connection failed at %s ( %d seconds, timeout was %d seconds).", Time.timeStr().c_str(), Time.now(), Time.now() - timeStarted);
        }
    } 
    else 
    {
        Serial.println("Already connected to network.");
    }
    
    Serial.printlnf("Connected to Network: ", WiFi.SSID());
}

@jsx001, you need to use SYSTEM_MODE(SEMI_AUTOMATIC) and SYSTEM_THREAD(ENABLED) along with managing the wifi connection. Your code is essentially working against the system firmware which seems, by your included code, to be in default AUTOMATIC mode. Furthermore, without threading, the user code will block when the system is trying to reconnect to wifi and cloud. There are plenty of threads on this forum that address this issue. Take the time to learn about these modes.

Personally, using the modes mentioned above coupled with the WiFi status functions (connecting(), listening, etc.), I collect data without connectivity and then probe wifi to see if I can connect. If not, I keep sampling and storing. When I finally get a connection, I dump the data whichever way I need (publish, tcp, udp, etc.).

Well spotted on the SEMI_AUTOMATIC mode, re testing now shows that Setup always gets called, and loop. When no network is present, loop gets called every 42 seconds.

I like your suggested approach of connection, then try Wifi.

I will work a test harness on that method.

Does this thread help at all?

I posted some code that lets the Photon disconnect from cloud when there is no wifi, run certain code when it is offline, and try to reconnect to wifi every minute, and run different code when online, without any blocking or delays.

2 Likes

Cool, works perfectly, I have re posted the test harness code I had below with your connection code/strategy just in case someone hits this post - works like a dream.

The Code##

SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);

#define disconnectDelay 15000 // After fifteen seconds of no WiFi, disconnect
#define checkInterval 60000 // How often to check for WiFi once disconnected


unsigned int lastAttempt, lastDisconnect;
bool connected = false;


void setup() 
{
    Serial.begin(9600);
    Time.setTime(0);
  
    delay(2000);
    Serial.println();
    Serial.println("Connecting to LAN");
    Particle.connect();
    Serial.println("Setup Completed");
}

unsigned long lastTime= 0;

void loop() {
    int diff = Time.now() - lastTime;

    checkConnection();

    if (connected ) 
    {
        Serial.printlnf("%s (%d), Connected to Network: %s", Time.timeStr().c_str(), Time.now() - lastTime, WiFi.SSID());
        // send data to server
    }
    else
    {
        Serial.printlnf("%s (%d), No Connection to Network: %s", Time.timeStr().c_str(), Time.now() - lastTime, WiFi.SSID());
    }
    lastTime = Time.now();
    delay(1000);
    
  // collect some data etc.
}


void checkConnection()
{
        if (Particle.connected() == false && millis() - lastDisconnect >= disconnectDelay)
        {
                lastDisconnect = millis();
                WiFi.off();
                connected = false;
        }

        if (millis() - lastAttempt >= checkInterval)
        {
                WiFi.on();
                Particle.connect();
                lastAttempt = millis();
        }

        if (millis() - lastAttempt >= disconnectDelay)
        {
                connected = Particle.connected();
        }
}
2 Likes