Photon working both online and offline

Sometimes my photon loses wifi connection or the internet ISP fails. I need my photon to keep working.

When internet fails I want it to store the data it is collecting.
When internet returns I want it to publish that data.

My problem so far is that when I turn the wifi router off (simulating internet failure), the photon blinks green but does not continue working… for example no Serial.print(“no internet”);

I tried MANUAL and SEMI_AUTOMATIC modes and neither work.

void onlinecheck()
{
Particle.connect();

    if (Particle.connected()==false)                    //if no internet store backup 
    {   
        Serial.println("NO INTERNET!!!!!");     //  <<<<< this is never printed, why????
        for(int o=0;o<ARRAYSIZE;o++){
            if(backup[o]=="x")
            {
                backup[o] = "content of string to be backed up";
                Serial.print("backup: ");Serial.print(o);Serial.println(" saved!");
                o=100;
            }
        }
    }
    
    if (Particle.connected()) {                         //if internet, send backups every delay until finished
    Serial.println("");
    Serial.println("INTERNET OK!!");
        for(int o=ARRAYSIZE;o<0;o--){
            if(backup[o]!="x"){
                Particle.publish("measure", backup[o], PRIVATE);
                backup[o]="x";
                delay(1000);
                Particle.process();
            }
        }
    }
}

thanks

You should try SYSTEM_THREAD(ENABLED)

2 Likes

I now added SYSTEM_THREAD(ENABLED)

same behaviour… green flashing and stopped running.

I removed my first line of the script: Particle.connect(); and now it stays solid yellow but wont connect back when the wifi returns and it still does not print “no internet”.

What happens if the photon has no wifi and it hits the line Particle.process(); ?

I changed a few things here and there and it seems to work better now:

void onlinecheck()//checa que pueda subor datos a internet, si no entonces guarda en ram cada medicion hasta que pueda subir los datos pendientes
{
//Particle.connect();

    if (Particle.connected()) {                         //if internet, send backups every delay until finished
    Serial.println("");
    Serial.println("INTERNET OK!!");
    if(backupcounter>0){
        for(int o=ARRAYSIZE;o>=0;o--){
            if(backup[o] != "x"){
                    //backup[o] = ("blablabla")
                   // Particle.publish("measure", backup[o], PRIVATE);
                    Serial.println("");Serial.print("backup: ");Serial.print(o);Serial.println(" sent!");
                    Serial.println(backup[o]);
                    backup[o]="x";
                    delay(1000);
                    Particle.process();
                    backupcounter--;
                }
            }
        }
    }
    
    else {                   //if no internet store backup    
        Serial.println("NO INTERNET!!!!!");
        for(int o=0;o<ARRAYSIZE;o++){
            if(backup[o]=="x")
            {
                backup[o] = ("blablabla");
                Serial.print("backup: ");Serial.print(o);Serial.println(" saved!");
                Serial.print(backup[o]);
                o=100;
                backupcounter++;
            }
        }
    }
}//end onlinecheck

when I say “better” it is because sometimes ti flashes red SOS for a few seconds and then back to breathing cyan…

Could it be that it is running out of RAM? What commands are there to store these Strings into a non volatile memory or how can I know how much RAM i have left WHILE running the code?
thanks

One thing, since we can't see how frequently your original code called onlinecheck(), you should not call Particle.connect() repeatedly.
Once you call it you should use waitFor(), waitUntil() or do other stuff till a connection is successfully established but not call Particle.connect() again till any ongoing attempts to connect have completed (successfully or not).

When you talk about SOS flashes it's always important to also state the number slow blinks between two SOS signals. This code will give you a hint about the prossible cause.

System.freeMemory()
But this does not tell you how fragmented your heap may have become.

What datatype is backup?
But using lots of String objects is a good candidate for causing heap fragmentation.

Is there a way to have the photon work without wifi but also without SYSTEM_THREAD(ENABLED) ?
The reason is that if I enable it, then softAP fails, apparently because enabling system thread consumes additional 7kb o 8kb of RAM.

maybe SINGLE_THREADED_BLOCK() or some other way to free that RAM or any other alternative to use system thread enabled and softAP together?
my photon will 99% of the time be online, but the internet may fail and I must keep track of the sensors constantly. When NOT using SYSTEM_THREAD(ENABLED), the photon just stops working (no serial prints) until the wifi is back.

thanks

You may want to revisit that. Chances are that it would have worked when done right :wink:

But, how come your program uses that much RAM to start with?
Any chance to cut down on that?

thanks for the reply @ScruffR, what do you mean by revisiting that?
I really dont have any clue why my RAM is so low. I do have a String here and there that I will change to char, id appreciate some help for doing this.

The softAP uses a lot of ram too.

Any comments on the how NOT to use SYSTEM_THREAD(ENABLED) and still have the photon work when offline?

here is the code that generates the backups that I store in ram when the device has no internet:

#define ARRAYSIZE 200
String backup[ARRAYSIZE];
String payload;
for(int o=0;o<=ARRAYSIZE-1;o++){

            if(backup[o]=="x")      //all start with "x"
            {
                payload = String::format(  "{\"Timestamp_Device\":\"" + String(currentTime) + "\",\"device_id\":\"" + String(id.c_str()) +  "\",\"temp\":\"" + String(flowtemp1) + "\",\"flowshort\":\"" + String(flowtotal) +  "\",\"flowacum\":\"" + String(flowacum) + "\",\"vbat\":\"" + String(vbat) + "\",\"counterstatus\":\"" + String(counterstatus)+"\"}");
                if(config==1){    backup[o] = payload;}
                backup[o+1]="x";
                Serial.print("backup: ");Serial.print(o);Serial.print(" saved:  "); 
                Serial.print(backup[o]); Serial.println(" ");
                
                if(o==ARRAYSIZE-3){                 //if getting close to the limit delete everything . Id prefer to replace only the first..
                    cleanbackup();    //resets all to "x"
                    backupcounter=0;
                }
                else { o=ARRAYSIZE+1; }
                backupcounter++;
                
                freemem = System.freeMemory();
                Serial.print("free memory: ");
                Serial.println(freemem);
            }
        }

How can I store these payloads without having to declare the size of an array? How to declare it as string but still publish the data in the same format so that the webhook that receives it still works?

thanks!

I'd use something like this

const char jsonTemplate[] =           // string template for creating the JSON string
"{\"Timestamp_Device\":\"%d\""
",\"device_id\":\"%s \""
",\"temp\":\"%.1f\""
",\"flowshort\":\"%.1f\""
",\"flowacum\":\"%.1f\""
",\"vbat\":\"%.1f\""
",\"counterstatus\":\"%d\"}";

struct flowData_t {
  int      ts;                        // timestamp
  float    temp; 
  float    total;
  float    accum;
  float    vbat;
  int      status;
};
const int  maxBuf = 0x7F;             // (127) (power of 2)-1 for easy roll-over 
flowData_t flowBuf[maxBuf+1];         // allow for 0 .. maxBuf elements
int        head, tail;                // to use flowBuf as circular buffer

...
  char msg[sizeof(jsonTemplate)+64];  // allow for up to 64 characters to be inserted into the template
  while(tail != head) {
    snprintf(msg, sizeof(msg), jsonTemplate
            , flowBuf[tail].ts
            , (const char*)id
            , flowBuf[tail].temp
            , flowBuf[tail].total
            , flowBuf[tail].accum 
            , flowBuf[tail].vbat
            , flowBuf[tail].status);
    Serial.println(msg);
    tail++;                            // advance tail as we already treated that element
    tail &= maxBuf;                    // if tail reached end of array roll over when 
  }

This way you only store 24 bytes per set of data and create the actual string on the fly as automatic/local variable which will disappear as soon the function leaves.

When adding a new element to flowBuf[] you should do this

  head++;                     // advance head to next "free" position
  head &= maxBuf;             // when reaching the end of array roll over
  if (head == tail) {         // if the buffer is completely full overwrite oldest data 
    tail++;                   // advance tail one on as the previous entry will be overwritten
    tail &= maxBuf;           // roll over when needed
  }
  // fill data into flowBuf[head]

BTW, since it's not considered good style to put multiple instructions in one line you could rewrite this

as

      Serial.printlnf("backup: %d saved: %s ", o, (const char*)backup[o]); 

Also there are neater ways to format this

e.g. like this

              if(config == 1)
                backup[o] = payload;
... 
              if(o == ARRAYSIZE-3) {         // if getting close to the limit delete everything . Id prefer to replace only the first..
                cleanbackup();               // resets all to "x"
                backupcounter = 0;
              }
              else 
                o = ARRAYSIZE+1; 

Having conditional block of the same level also aligned on the same indent level makes code readability somewhat easier IMO.
Also adding some extra spaces adds readability - in exchange for the lost space the indentation depth could be reduced from 4 to 2 places.

2 Likes