Optional Wifi connection using Argon

I’m trying to move some code from a Photon to an Argon and I’m running in to a problem.

I often want to run my devices with or without wifi as they are often used when no wifi is available. I’ve gotten used to doing this flow below.
It tries the wifi … and connects if it’s there, if it’s not, it continues offlne.

I tried porting to the Argon, but It looks like SparkIntervalTImer isn’t supported. Thats a bummer, because the timer interrupt is how I figure that I couldn’t connect.
The only timer options I see for the Argon are polled… which I can’t see how to use with Particle.connect() as I think it’s blocking…

Any ideas on how to make this sort of ‘use wifi if available, continue without it if not’ … BTW. I’ve seen examples that do this using a manual button push to do the connection… but that won’t work in my application.

Any ideas most welcome !
-jc

include <SparkIntervalTimer.h>

// ......... 

SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);

// vars

volatile bool  onLine = true;  // is phonton conneted to wifi ?
IntervalTimer myTimer;        // 3 for the Core


//  disconnect from Wi-Fi

void noCon(void) {
  Particle.disconnect();
  onLine = false;
}

//  attempt to connect to Wi-Fi

void doCon(void) {
  myTimer.begin(noCon, 10000, hmSec);
  Particle.connect();
  myTimer.end();
}

void setup() {
 // ....
}

>void loop() { 

 if ((onLine && !Particle.connected()) == true) {
    doCon();
  }
 // ...
}

IMHO SparkIntervalTimer is overkill as you don’t really need its precision nor its “ruthlessness” to interrupt your other code :wink:
I’d think a Software Timer would be suitable replacement for both Gen2 and Gen3 devcies.

However, with the Argon you may want to allow for considerably more time than on the Photon. In my personal experience the Argon takes about 3-5 times as long as the Photon did.

BTW, I’m not convinced the myTimer.end() call after Particle.connect() is a good idea.
Particle.connect() is not guaranteed to block and hence you may stop the timer even before the connection is actually established. As far I understand your strategy that would be counter productive, wouldn’t it?

Which Device OS version are you using? There are some changes with the latest sleep functions available for the Argon with OS 2.0.0

There shouldn’t be any difference in your code between the Photon and the Argon.

I agree with @ScruffR that your current approach is overkill. My model as pseudo code is typically like this:

SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);

setup()
{
connectedOnce = false;
call WiFi connect function
register Particle cloud functions
register Particle cloud variables
}

loop()
{
as required reconnect (e.g. after sleeping)

on a millis() timed basis - check if WiFi.ready and issue Particle.connect if not done before (should not be required!)

as required disconnect from WiFi (Particle.disconnect followed by WiFi.disconnect)

}

WiFi connect function
{
WiFi.connect
if (waitFor(WiFi.ready, 10s))
{
    if (!connectedOnce  && waitFor(Particle.connect, 20s))
    {
        connectedOnce = true
    }
}
}

You need to remember that the WiFi connection call from setup() is handled differently than from loop() - don’t call Particle.connect unless WiFi.ready is true. This code does not handle all conditions - e.g. WAP out of range nor bad credentials, etc.

@armor, @ScruffR thanks for your help. That worked well for the Argon

here’s my fleshed in code sample for others to use for Argon. Does it look ok ? It seems to work :slight_smile:

// top matter
SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);
volatile bool   onLine = true;  // connected to wifi ?'
bool connected = false;

//  helper functions definition
Timer timer(10000, noCon);  // set up timeout on no connect

void noCon(void) {  // we could not connect to wifi
    Particle.disconnect();
    onLine = false;
}


void doCon(void) { // attempt to connect to wifi
    timer.start(); // set up timer for no connect
    Particle.connect();
    timer.stop();
}

// in setup 
void setup() {
// normal setup code here 
}


// in Loop 
void loop() {
 
     if ((onLine && !Particle.connected()) == true) { // if not connected
            doCon();
     } 

// functional loop code here 

}
1 Like

I would always use a waitFor() instead of your timer handler noCon().
Also, you need to be careful to only call Particle.connect() once - otherwise the memory gets used up.
IMHO it is best to test for WiFi connection rather than set a flag and WiFi connection is different from Cloud connection and your code doesn’t seem to differentiate!
My testing of connection (WiFi then Cloud) in the pseudo code is only done every 10-20 seconds not every loop() - i.e. 1,000/second.

I appreciate your help… Can you help me create a more complete example? I’m not sure I’m following the flow you suggest.
Also I’m not sure what you mean about the difference between a cloud and Wi-Fi connection. I never use the particle cloud functions… So I’m only looking for local Wi-Fi because most of the time he’s devices will be off-line. Do I need to connect to the particle cloud if I’m not using any of those functions?

Umm, I think you need to read up in the Reference docs.

With a WiFi connection - initiate with WiFi.connect(); stop with WiFi.disconnect(); you are opening a channel of communication using Wireless-LAN - you know when this has happened as you have a local IP address assigned. There is an issue with the Argon and you need to read the docs.

With a Cloud connection - initiate with Particle.connect(); stop with Particle.disconnect(); you are opening a session (UDP) with the cloud server for communication with the Particle cloud. Again you need to read the docs.

There are examples in the reference documents.
This is all a moving feast because each device OS (and specifically 2.0.0) brings new functions and built in ways to achieve what you might need to like waiting for a system event with a timeout - waitFor();

Here is one you can try:

//  device  ???
//  device os ???
int errCode = 0;
uint32_t g_tmsNow; //  current millis
uint32_t g_tmsMyTimer;
bool goOnline = false; // don't connect until timer instructed
bool sendInfo = false; // send info once per connection

SerialLogHandler logHandler; // Use primary serial over USB interface for logging output

//  choose your SYSTEM_MODE       uncomment one statement to activate or skip
//SYSTEM_MODE(AUTOMATIC);         //  default SYSTEM_MODE is AUTOMATIC
SYSTEM_MODE(SEMI_AUTOMATIC);          
//SYSTEM_MODE(MANUAL);        

SYSTEM_THREAD(ENABLED);         //  uncomment to activate   

void setup()  {   
  // Log some messages with different logging levels
  Log.info("This is info message");
  Log.warn("This is warning message");
  Log.error("This is error message, error=%d", errCode);

  // Format text message
  Log.info("System version: %s", (const char*)System.version());
    
  //  your code .......
}

void loop() {   
    g_tmsNow = millis();
    
    if (goOnline)   {
        if (Particle.connected() == false) { //  NOT connected to Particle cloud
            Particle.connect();
            //  After you call Particle.connect(), your loop will not be called again until the device finishes 
            //  connecting to the Cloud. Typically, you can expect a delay of approximately one second.
            waitFor(Particle.connected, 60000); 
        }
    }

    if (Particle.connected() == true) {
        if (!goOnline)   {
            Particle.disconnect(); //  disconnect from particle cloud
        } else if (sendInfo) {
            Particle.publish("eventName", "eventData", PRIVATE); //  modify to meet data structure in "your code" above
            delay(1s);
            sendInfo = false; // send info only once
            goOnline = false; // disconnect right away
            delay(30s); // this long delay is for Web IDE - a much shorter delay here may stop you from re-flashing otherwise
        }
    }
    
    //  your code here & below .......
    
    if (g_tmsNow - g_tmsMyTimer >= 60000) { // timer expired           
        g_tmsMyTimer = g_tmsNow; // reset timer
        // do something useful here
        // decide to connect or disconnect by modifying: goOnline
        // decide to report info by modifying: sendInfo
        //
        Log.info("myLogInfo"); //  modify to meet data structure in "your code" above

        goOnline = true; // test
        sendInfo = true; // test
    }

    Particle.process();
}

I ran this on an Argon with os 2.0.0-rc.4

Particle.connect() is among the Device OS API cloud functions. Perhaps you might have been referring to Particle.function()? I see you are not using Particle.variable() or Particle.function().

As @armor pointed out, please use only one statement, preferably in you main loop(), for Particle.connect() and reuse it if you disconnect or become disconnected.

I hope this is helpful to you.

Rob,
This is super helpful ! I’ll give it a try…

Based on @armor’s comments I think I have a pretty fundamental misunderstanding of the difference between ‘just’ a wifi connection and a connection to the Particle cloud. I’m not using the Particle cloud to call functions or read variables via web services. I use the wifi connection primarily for 2 thing… for doing OTA flash of code using the http://build.particle.io portal, and occasionally to use MQTT a pub/sub protocol that I love. Do I need the do a Particle.connect() to do either of those… or can I just use the lower level wifi calls ? (eg Wifi.on(), Wifi.connect())

thanks for your help !
-jc

Yes for OTA
No for MQTT