3rd party sim (Google Project Fi) loses connection for 25 minutes repeatably

Hello, I’m using a 3G Electron and have written some code to take sensor data on the analog inputs, store them in a variable, and then allow me to access them via the api link.

After programming I can get several hours (about 16.5 this last time around) of solid connection. After that I get disconnects from the network every 25 minutes.

I set up the SIM card using serial connection, and I have the following lines in my code to keep the connection alive, but it doesn’t seem to be working permanently.

#include "cellular_hal.h"
STARTUP(cellular_credentials_set("h2g2", "", "", NULL));
Particle.keepAlive(15);

The lowest Particle.keepalive(X) I’ve tried is where X=15, but most of the forum posts I’ve seen have this value at X=240.

Any ideas?

FULL CODE:

//#include "Particle.h"
#include "cellular_hal.h"
STARTUP(cellular_credentials_set("h2g2", "", "", NULL));


//create variables for sensors
const int Moisture_Sensor1 = A3;
const int Moisture_Sensor2 = A4;
const int Light_Sensor1 = A2;
const int readings = 10;//take multiple readings to reduce noise

//initial values for acceleration
double P1_M = 0;
double P2_M = 0;
double L1 = 0;

const int sampleSize = 10;

void setup() 
{
    Serial.begin(9600);
    Particle.keepAlive(15);
    Spark.variable("P1_M",&P1_M, DOUBLE);//create spark variable for Plant1 moisture
    Spark.variable("P2_M",&P2_M, DOUBLE);//create spark variable for Plant2 moisture
    Spark.variable("L1",&L1, DOUBLE);//create spark variable for Light Sensor 1
}

void loop()
{
    //call function to give x y and z axis data
	P1_M = readSensor(Moisture_Sensor1);
	P2_M = readSensor(Moisture_Sensor2);
	L1 = readSensor(Light_Sensor1);
   
    delay(100);
}

//--------------------------------------------------------------------------------------------------------------------------------------------
//this function reads data and averages it over packages of samples

int readSensor(int SensorPin)
{
	long reading = 0; //initial for reading
	analogRead(SensorPin); //analog read axisPin
	delay(1); //delay for 1 mS
	for (int i = 0; i < sampleSize; i++) //starting at zero and for each reading until predetermined sample size 
	{
		reading += analogRead(SensorPin); 
	}
	return reading/sampleSize;
}

What system version?
The “modern” syntax would be Particle.variable("P1_M", P1_M).
Why are you using a double when you only ever have integer values?

1 Like

Thanks for the feedback!
I’m using version 0.6.2.

I actually found some code online and sort of forced my functionality into it…

I haven’t really moved forward with cleaning and optimizing the code until I understand the underlying problem of the disconnects for 25 minutes then short reconnects. I might need to switch to a different company for the sim card embedded device.

I’ll update my code to match your suggestion.

@bluesforsalvador, with too short keep-alives, your carrier may be locking you out due to the overly frequent activity. @ScruffR, do you conquer?

1 Like

This might fit the bill and explain why this only happens after a while. The provider might put up with this behaviour for a while but eventually get fed up.
So I won’t conquer your suggestion but concur :wink:

4 Likes

Interesting. I can test this tonight and provide results on it’s effectiveness.

Thanks for the suggestions!

We shall conquer by concurring!

2 Likes

@ScruffR @peekay123
Adding some more data to this issue.

I changed the code in 4 ways (updated code at bottom)

  1. #include “Particle.h” //un-commented this line
  2. Particle.keepAlive(240); //changed from 15 to 240
  3. Spark.variable(“P1_M”, P1_M); //removed ‘&’, removed DOUBLE casting (did this for all 3 variables)
  4. Serial.begin(9600); //removed this line

The electron was accessible to my raspberry pi using the api link to get the variables all night until this morning when I got to work:

As you can see my script captures plant moisture and light data every 50 minutes until about 10am. That’s about when I decided to do the manual grabbing of the variables.

After I manually grabbed these variables the device goes into the reset every 25 minutes routine.

I cannot remotely restart the electron (I will try to set that up tonight from my raspberry pi), but could this be my issue? Something in the manual variable query that is causing the electron to reset every 25 minutes?

FULL CODE:

#include "Particle.h"
#include "cellular_hal.h"
STARTUP(cellular_credentials_set("h2g2", "", "", NULL));


//create variables for sensors
const int Moisture_Sensor1 = A3;
const int Moisture_Sensor2 = A4;
const int Light_Sensor1 = A2;
const int readings = 10;//take multiple readings to reduce noise

//initial values for acceleration
double P1_M = 0;
double P2_M = 0;
double L1 = 0;

const int sampleSize = 10;

void setup() 
{
    Particle.keepAlive(240);
    Spark.variable("P1_M", P1_M);//create spark variable for Plant1 moisture
    Spark.variable("P2_M", P2_M);//create spark variable for Plant2 moisture
    Spark.variable("L1", L1);//create spark variable for Light Sensor 1
}

void loop()
{
    //call function to give x y and z axis data
	P1_M = readSensor(Moisture_Sensor1);
	P2_M = readSensor(Moisture_Sensor2);
	L1 = readSensor(Light_Sensor1);
   
    delay(100);
}

//--------------------------------------------------------------------------------------------------------------------------------------------
//this function reads data and averages it over packages of samples

int readSensor(int SensorPin)
{
	long reading = 0; //initial for reading
	analogRead(SensorPin); //analog read axisPin
	delay(1); //delay for 1 mS
	for (int i = 0; i < sampleSize; i++) //starting at zero and for each reading until predetermined sample size 
	{
		reading += analogRead(SensorPin); 
	}
	return reading/sampleSize;
}

I also saw that the event log could be run from a terminal using this command

curl https://api.particle.io/v1/devices/events?access_token=MYACCESSTOKEN

So I’m using the rpi to constantly look at the electron connection status.

This has been running even though the connection on the electron has been resetting

After the device reconnecting every 25 minutes from around 10am until about 4pm the connection has finally stabilized.

I will not grab the variables manually anymore to see if this issue happens again.

If it doesn’t happen, then I will grab the variables manually to see if that causes the issue

Am I crazy or is the web api affecting my device?

I noticed the device hadn’t gone offline in many hours and I refreshed the “events” page and it went offline basically right afterwards…?

hey @ScruffR where can I find documentation of the correct way to use this command in the "modern" way?

The "modern" syntax would be Particle.variable("P1_M", P1_M).

Everything I see online has the old was with the ampersands before the second variable.
Do I have to cast it as an INT or is that assumed default casting?

@bluesforsalvador, the docs shows the "new" way at the beginning and end with the "old" way with this caveat:

Prior to 0.4.7 firmware, variables were defined with an additional 3rd parameter to specify the data type of the variable. From 0.4.7 onward, the system can infer the type from the actual variable. Additionally, the variable address was passed via the address-of operator (&). With 0.4.7 and newer, this is no longer required.
This is the pre-0.4.7 syntax:...

2 Likes

thanks @peekay123

do I need the following line in my code?
#include "Particle.h"

also do you think the delay is causing any issues in the loop function?

I’m getting spotty results when trying to use the web url to access the variables

@ScruffR and @peekay123

What frequency should I be allowed to use this to get access to my variables?
https://api.particle.io/v1/devices/DEVICE/P1_M/?access_token=ACCESSTOKEN

I’m trying every 10 minutes from my raspberry pi python code using unirest, but I get a timeout most of the time.
I seem to be able to get access to the variables for a short time every 50 minutes.

You should be able to get them once a second (though you might not want to poll that often, so be a good netizen). It’s more likely that your device only stays online for so long at a time, thus the variables being unavailable in the mean time.

@bluesforsalvador, you should be able to hit it every second! First, you can add #include "Particle.h" though that is also done automatically by the Web IDE.

Second, remove the delay(100); in your loop and create a non-blocking delay like this:

unsigned long sampleInterval = 0;  // global variable

void loop()
{
    if (millis() - sampleInterval >= 100) {  // wait 100ms or whatever before sampling
        sampleInterval = millis();
    //call function to give x y and z axis data
        P1_M = readSensor(Moisture_Sensor1);
        P2_M = readSensor(Moisture_Sensor2);
        L1 = readSensor(Light_Sensor1);
   }
}

In readSensor(), you do an “empty” analogRead(SensorPin), wait 1ms then read the same pin multiple times and average. Any reason for that? Is this to “prime” the ADC? Not sure you need this approach or the 1ms delay.

Can you post your latest code?

hey @peekay123

I was looking into the non-blocking delay doing some searches on the web yesterday, I haven’t had a chance to implement this yet though.

The empty read + the averaging isn’t really necessary for plant moisture or light level applications. This code was originally used to read an accelerometer and I just left that part in.

I think just doing 1 read would suffice, but I think the delay is to allow the device time to read from the ADC and store it before returning the contents of the variable. Do I need this delay or not?

Here’s the code that’s currently on the device:

#include "Particle.h"
#include "cellular_hal.h"
STARTUP(cellular_credentials_set("h2g2", "", "", NULL));


//create variables for sensors
const int Moisture_Sensor1 = A3;
const int Moisture_Sensor2 = A4;
const int Light_Sensor1 = A2;
const int readings = 10;//take multiple readings to reduce noise

//initial values for acceleration
double P1_M = 0;
double P2_M = 0;
double L1 = 0;

const int sampleSize = 10;

void setup() 
{
    Particle.keepAlive(240);
    Particle.variable("P1_M", P1_M);//create spark variable for Plant1 moisture
    Particle.variable("P2_M", P2_M);//create spark variable for Plant2 moisture
    Particle.variable("L1", L1);//create spark variable for Light Sensor 1
}

void loop()
{
    //call function to give x y and z axis data
	P1_M = readSensor(Moisture_Sensor1);
	P2_M = readSensor(Moisture_Sensor2);
	L1 = readSensor(Light_Sensor1);
   
    delay(100);
}

//--------------------------------------------------------------------------------------------------------------------------------------------
//this function reads data and averages it over packages of samples

int readSensor(int SensorPin)
{
	long reading = 0; //initial for reading
	analogRead(SensorPin); //analog read axisPin
	delay(1); //delay for 1 mS
	for (int i = 0; i < sampleSize; i++) //starting at zero and for each reading until predetermined sample size 
	{
		reading += analogRead(SensorPin); 
	}
	return reading/sampleSize;
}

Here’s the code I shall program tonight: (I’d program it OTA now, but for some reason I always need to put the device into safe mode (blinking magenta) in order to OTA program, is there a reason for this?

//#include "Particle.h"
#include "cellular_hal.h"
STARTUP(cellular_credentials_set("h2g2", "", "", NULL));


//create variables for sensors
const int Moisture_Sensor1 = A3;
const int Moisture_Sensor2 = A4;
const int Light_Sensor1 = A2;
const int readings = 10;//take multiple readings to reduce noise

//create a variable to perform non-blocking delay
unsigned long sampleInterval = 0;  // global variable

//initial values for acceleration
double P1_M = 0;
double P2_M = 0;
double L1 = 0;

const int sampleSize = 10;

void setup() 
{
    Particle.keepAlive(240);
    Particle.variable("P1_M", P1_M);//create spark variable for Plant1 moisture
    Particle.variable("P2_M", P2_M);//create spark variable for Plant2 moisture
    Particle.variable("L1", L1);//create spark variable for Light Sensor 1
}

void loop()
{
    if (millis() - sampleInterval >= 100) {  // wait 100ms or whatever before sampling
        sampleInterval = millis();
        //call function to give x y and z axis data
	    P1_M = readSensor(Moisture_Sensor1);
	    P2_M = readSensor(Moisture_Sensor2);
	    L1 = readSensor(Light_Sensor1);
    }
}

//--------------------------------------------------------------------------------------------------------------------------------------------
//this function reads data and averages it over packages of samples

int readSensor(int SensorPin)
{
	long reading = 0; //initial for reading
	reading = analogRead(SensorPin); //analog read axisPin

	return reading/sampleSize;
}

Thanks,

@bluesforsalvador, you don't need the sampling delay since the analogRead() function only returns when the reading is completed (blocking).

I noticed you are defining your Particle.variables as doubles (double precision floating point) but your calculations for the values are integers. Which format does your server need as this will affect your calculation "chain".

This concerns me as I don't see any obvious reasons for your code to be blocking the OTA. Nonetheless, I suggest you add SYSTEM_THREAD(ENABLED); after the STARTUP() line to allow the system and user firmware to run in their own threads.

@peekay123

you don't need the sampling delay since the analogRead() function only returns when the reading is completed (blocking).

Ahh good to know, I'll remove the delay as well

re: doubles
I forgot to remove the casting of those variables as doubles. Thanks for pointing that out, though I don't see it casuing an issue.

//initial values for acceleration
int P1_M = 0;
int P2_M = 0;
int L1 = 0;

I also changed the "long" to an "int" in the function:
My Server code expects an INT which it then divides by 4095 and then multiplies by 100 to provide a percentage.

int readSensor(int SensorPin)
{
	int reading = 0; //initial for reading
	reading = analogRead(SensorPin); //analog read axisPin

	return reading;
}

re: safe mode programming
I've added a note to add SYSTEM_THREAD(ENABLED); after STARTUP(); as you suggested. I'll try this tonight when I get home to see if it works.

Current full code:

//#include "Particle.h"
#include "cellular_hal.h"
STARTUP(cellular_credentials_set("h2g2", "", "", NULL));
SYSTEM_THREAD(ENABLED);


//create variables for sensors
const int Moisture_Sensor1 = A3;
const int Moisture_Sensor2 = A4;
const int Light_Sensor1 = A2;
const int readings = 10;//take multiple readings to reduce noise

//create a variable to perform non-blocking delay
unsigned long sampleInterval = 0;  // global variable

//initial values for acceleration
int P1_M = 0;
int P2_M = 0;
int L1 = 0;

void setup() 
{
    Particle.keepAlive(240);
    Particle.variable("P1_M", P1_M);//create spark variable for Plant1 moisture
    Particle.variable("P2_M", P2_M);//create spark variable for Plant2 moisture
    Particle.variable("L1", L1);//create spark variable for Light Sensor 1
}

void loop()
{
    if (millis() - sampleInterval >= 100) {  // wait 100ms or whatever before sampling
        sampleInterval = millis();
        //call function to give x y and z axis data
	    P1_M = readSensor(Moisture_Sensor1);
	    P2_M = readSensor(Moisture_Sensor2);
	    L1 = readSensor(Light_Sensor1);
    }
}

//--------------------------------------------------------------------------------------------------------------------------------------------
//this function reads data and averages it over packages of samples

int readSensor(int SensorPin)
{
	int reading = 0; //initial for reading
	reading = analogRead(SensorPin); //analog read axisPin

	return reading;
}