Cellular.connect() does not connect after Cellular.disconnect() without first calling Cellular.off and Cellular.on

While doing some tests to better understand the behavior of networking function calls with
SYSTEM_THREAD(ENABLED), I encountered this unexpected behavior. If I call Cellular.disconnect() and then call Cellular.connect(), the Electron will not connect until it hits a timeout and turns the modem off and then back on. This process takes around 6 minutes. If instead I turn the modem all the way off and on before calling Cellular.connect() it connects very quickly (19 seconds). This behavior is consistent across any SYSTEM_MODE or SYSTEM_THREAD.

What is the intended purpose of Cellular.disconnect()? It doesn’t appear to have a functional use without also using Cellular.off().

Serial Output:

Connected to Particle Cloud


TESTING CELLULAR DISCONNECT INCLUDING TURNING CELLULAR OFF...
Particle.disconnect() returned after 0 millis
Disconnecting from Particle Cloud...
Took 523 millis to disconnect
Disconnected from Particle Cloud
Cellular.disconnect() returned after 0 millis
Took 1 millis to disconnect
Cellular.off() returned after 0 millis
Network powering off...
Network off
Took 6310 millis to turn off
Cellular.on() returned after 0 millis
Network powering on...
Network on
Took 3351 millis to turn on
Cellular.connect() returned after 0 millis
Connecting to network...
Took 18631 millis to connect
Connected to network
Particle.connect() returned after 0 millis
Connecting to Particle Cloud...
Connected to Particle Cloud
Took 368 millis to connect


TESTING CELLULAR DISCONNECT WITHOUT TURNING CELLULAR OFF...
Particle.disconnect() returned after 0 millis
Disconnecting from Particle Cloud...
Took 280 millis to disconnect
Disconnected from Particle Cloud
Cellular.disconnect() returned after 0 millis
Took 1 millis to disconnect
Cellular.connect() returned after 0 millis
Connecting to network...
Network powering off...
Network off
Network powering on...
Network on
Connecting to network...
Took 344730 millis to connect
Connected to network
Particle.connect() returned after 0 millis
Connecting to Particle Cloud...
Took 358 millis to connect
Connected to Particle Cloud

My test code:

#include "Particle.h"


SYSTEM_THREAD(ENABLED);      // Enable system threading
SYSTEM_MODE(AUTOMATIC);      // Allow Particle to manage it's own connectivity



#if Wiring_Cellular
    // Set your 3rd-party SIM APN here
    // https://docs.particle.io/reference/firmware/electron/#setcredentials-
    STARTUP(cellular_credentials_set("hologram", "", "", NULL));

    // Cell indicator Config
    CellularSignal  sig;
    int             strength;

    // 60 seconds chosen since hologram UDP timeout is estimated at around 180 seconds but in practice worked best at 60
    const int       keepAliveInterval     = 20;      // keepAlive interval in seconds
#endif
// Used for both WiFi and Cell
int     rssi;
String  signal_strength;


bool network_status_local = false;

uint32_t time_start;



//..............................................................................
//..............................................................................
void cloud_status_handler(system_event_t event, int param)
{
    if (param == cloud_status_connecting)
    {
        Serial.println("Connecting to Particle Cloud...");
    }
    else if (param == cloud_status_connected)
    {
        // init the keepAlive interval to maintain connection to Particle cloud
        #if Wiring_Cellular
        Particle.keepAlive(keepAliveInterval);
        #endif
        Serial.println("Connected to Particle Cloud");
    }
    else if (param == cloud_status_disconnecting)
    {
        Serial.println("Disconnecting from Particle Cloud...");
    }
    else if (param == cloud_status_disconnected)
    {
        Serial.println("Disconnected from Particle Cloud");
    }
}
//..............................................................................



//..............................................................................
//..............................................................................
void network_status_handler(system_event_t event, int param)
{

    if (param == network_status_connecting)
    {
        Serial.println("Connecting to network...");
    }
    else if (param == network_status_connected)
    {
        Serial.println("Connected to network");
    }
    else if (param == network_status_off)
    {
        Serial.println("Network off");
		network_status_local = false;
    }
    else if (param == network_status_on)
    {
        Serial.println("Network on");
		network_status_local = true;
    }
    else if (param == network_status_powering_on)
    {
        Serial.println("Network powering on...");
    }
    else if (param == network_status_powering_off)
    {
        Serial.println("Network powering off...");
    }
}
//..............................................................................



bool waitCloudDisconnect()	{	return !Particle.connected();	}

bool waitCellDisconnect()	{	return !Cellular.ready();	}

bool waitCellOff() 			{	return !network_status_local;	}

bool waitCellOn() 			{	return network_status_local;	}

/* This function is called once at start up ----------------------------------*/
void setup()
{
	Serial.begin(9600);

    System.on(cloud_status, cloud_status_handler);
    System.on(network_status, network_status_handler);

	pinMode(A0, OUTPUT);
	digitalWrite(A0, LOW);

	delay(10000);


	Serial.println("PERFORMING INITIAL SETUP");

	// TURN ON CELLULAR
	time_start = millis();
	Cellular.on();
	Serial.println("Cellular.on() returned after " + String(millis() - time_start) + " millis");
	time_start = millis();
	waitUntil(waitCellOn);
	Serial.println("Took " + String(millis() - time_start) + " millis to turn on");

	delay(1000);

	// CONNECT TO CELLULAR NETWORK
	time_start = millis();
	Cellular.connect();
	Serial.println("Cellular.connect() returned after " + String(millis() - time_start) + " millis");
	time_start = millis();
	waitUntil(Cellular.ready);
	Serial.println("Took " + String(millis() - time_start) + " millis to connect");

	delay(1000);


	// CONNECT TO PARTICLE CLOUD
	time_start = millis();
	Particle.connect();
	Serial.println("Particle.disconnect() returned after " + String(millis() - time_start) + " millis");
	time_start = millis();


	waitUntil(Particle.connected);
	Serial.println("Took " + String(millis() - time_start) + " millis to connect");

	delay(1000);
	Serial.println("");
	Serial.println("");

	Serial.println("Setup Complete");
	Serial.println("");
	Serial.println("");
}


/* This function loops forever --------------------------------------------*/
void loop() {

	// now let's kick - for a kick to be valid it must be
    //   at least 100ns
    ATOMIC_BLOCK()  // we'll consider this timing critical
    {
        digitalWrite(A0, HIGH); // rising edge to begin DONE signal detection on TPL5010
        delayMicroseconds(10);           // wait 10x minimum time of 100ns
        digitalWrite(A0, LOW);  // bring line back to wait for next kick
    }


		if (Particle.connected())
		{
			Serial.println("");
			Serial.println("");
			Serial.println("TESTING CELLULAR DISCONNECT INCLUDING TURNING CELLULAR OFF...");
			// DISCONNECT FROM PARTICLE CLOUD
			time_start = millis();
	        Particle.disconnect();
			Serial.println("Particle.disconnect() returned after " + String(millis() - time_start) + " millis");
			time_start = millis();
			waitUntil(waitCloudDisconnect);
			Serial.println("Took " + String(millis() - time_start) + " millis to disconnect");

			delay(1000);


			// DISCONNECT FROM CELLULAR NETWORK
			time_start = millis();
	        Cellular.disconnect();
			Serial.println("Cellular.disconnect() returned after " + String(millis() - time_start) + " millis");
			time_start = millis();
			waitUntil(waitCellDisconnect);
			Serial.println("Took " + String(millis() - time_start) + " millis to disconnect");

			delay(1000);

			// TURN OFF CELLULAR
			time_start = millis();
			Cellular.off();
			Serial.println("Cellular.off() returned after " + String(millis() - time_start) + " millis");
			time_start = millis();
			waitUntil(waitCellOff);
			Serial.println("Took " + String(millis() - time_start) + " millis to turn off");

			delay(1000);

			// TURN ON CELLULAR
			time_start = millis();
			Cellular.on();
			Serial.println("Cellular.on() returned after " + String(millis() - time_start) + " millis");
			time_start = millis();
			waitUntil(waitCellOn);
			Serial.println("Took " + String(millis() - time_start) + " millis to turn on");

			delay(1000);

			// CONNECT TO CELLULAR NETWORK
			time_start = millis();
			Cellular.connect();
			Serial.println("Cellular.connect() returned after " + String(millis() - time_start) + " millis");
			time_start = millis();
			waitUntil(Cellular.ready);
			Serial.println("Took " + String(millis() - time_start) + " millis to connect");

			delay(1000);


			// CONNECT TO PARTICLE CLOUD
			time_start = millis();
	        Particle.connect();
			Serial.println("Particle.connect() returned after " + String(millis() - time_start) + " millis");
			time_start = millis();


			waitUntil(Particle.connected);
			Serial.println("Took " + String(millis() - time_start) + " millis to connect");

			delay(1000);
			Serial.println("");
			Serial.println("");
		}



		// now let's kick - for a kick to be valid it must be
	    //   at least 100ns
	    ATOMIC_BLOCK()  // we'll consider this timing critical
	    {
	        digitalWrite(A0, HIGH); // rising edge to begin DONE signal detection on TPL5010
	        delayMicroseconds(10);           // wait 10x minimum time of 100ns
	        digitalWrite(A0, LOW);  // bring line back to wait for next kick
	    }




	if (Particle.connected())
	{
		Serial.println("TESTING CELLULAR DISCONNECT WITHOUT TURNING CELLULAR OFF...");
		// DISCONNECT FROM PARTICLE CLOUD
		time_start = millis();
        Particle.disconnect();
		Serial.println("Particle.disconnect() returned after " + String(millis() - time_start) + " millis");
		time_start = millis();
		waitUntil(waitCloudDisconnect);
		Serial.println("Took " + String(millis() - time_start) + " millis to disconnect");

		delay(1000);


		// DISCONNECT FROM CELLULAR NETWORK
		time_start = millis();
        Cellular.disconnect();
		Serial.println("Cellular.disconnect() returned after " + String(millis() - time_start) + " millis");
		time_start = millis();
		waitUntil(waitCellDisconnect);
		Serial.println("Took " + String(millis() - time_start) + " millis to disconnect");

		delay(1000);


		// CONNECT TO CELLULAR NETWORK
		time_start = millis();
		Cellular.connect();
		Serial.println("Cellular.connect() returned after " + String(millis() - time_start) + " millis");
		time_start = millis();
		waitUntil(Cellular.ready);
		Serial.println("Took " + String(millis() - time_start) + " millis to connect");

		delay(1000);


		// CONNECT TO PARTICLE CLOUD
		time_start = millis();
        Particle.connect();
		Serial.println("Particle.connect() returned after " + String(millis() - time_start) + " millis");
		time_start = millis();


		waitUntil(Particle.connected);
		Serial.println("Took " + String(millis() - time_start) + " millis to connect");

		delay(1000);
		Serial.println("");
		Serial.println("");
	}


	// now let's kick - for a kick to be valid it must be
    //   at least 100ns
    ATOMIC_BLOCK()  // we'll consider this timing critical
    {
        digitalWrite(A0, HIGH); // rising edge to begin DONE signal detection on TPL5010
        delayMicroseconds(10);           // wait 10x minimum time of 100ns
        digitalWrite(A0, LOW);  // bring line back to wait for next kick
    }



    delay(5000);

}
1 Like