Loses Connection With Cloud

Hello,

I am using a couple Photon modules and having trouble getting them to stay connected to the cloud. They seem to stay connected for about a day or two (doesn’t appear to be consistent). I am checking the connection with “if( Particle.connected() )”, if this conditional is “false”, I invoke “Particle.connect();”, but the connection doesn’t seem to re-establish.

My device has two sensors and some LEDs. I have three set-up to monitor and update these components and a fourth timer to maintain the connection with the cloud. This means that my main loop() is essentially empty; it only includes a conditional to help sync the system clock with the cloud. (see below)

//Set-up timers for regular routines:
Timer LED_timer(50, update_LEDs);        //20Hz
Timer qTouch_timer(250, update_qTouch);  //4Hz
Timer IR_timer(100, update_IR);          //10Hz
Timer Cloud_timer(1000, update_Cloud);   //1Hz 

//Main Loop
void loop() 
{
    //Sync with Particle Service once a day to ensure long-term reliability:
    if (millis() - lastTimeSync > ONE_DAY_MILLIS) 
    {
        Particle.syncTime();  // Request time synchronization from the Particle Cloud
        lastTimeSync = millis();
    }
    
} //End of loop()

I suspect that the culprit might be my capacitive touch sensor code. This routine is called 4 times per second. Within it I create a physical interrupt on my sense pin and wait until the interrupt happens. I also have a simple averaging filter with a sample size of 20. With this sample size the execution time seems to range between 500 and 700 micro seconds. (see below)

void update_qTouch()
{
    unsigned long total_qTouch_Time=0;
    
    for (unsigned char i=0; i<QTOUCH_SAMPLESIZE; i++)   
    {
        // discharge capacitance at sense pins
        pinMode(qT_IN, OUTPUT);
        pinResetFast(qT_IN);
        pinResetFast(qT_DRIVER);
        
        pinMode(qT_IN,INPUT);    // revert to high impedance input
        
        attachInterrupt(qT_IN, qTouch_ISR, RISING); //Enable interrupt on sense pin
       
        u_tS = micros();   //Capture current time.
        digitalWriteFast(qT_DRIVER, HIGH); 
    
        while (digitalRead(qT_IN)==LOW);
        
        u_tS_delay = micros();  //Capture time triggered
        
        // accumulate the RC delay samples
        if ( u_tS_delay>u_tS )
            total_qTouch_Time += (u_tS_delay - u_tS);   
        else // Redo reading when micros() overflows
            i--;  //Subtract from count to redo
    }
   
    current_qTouch = total_qTouch_Time; //update current qTouch value
    
} //End of update_qTouch()



void qTouch_ISR()
{
    u_tS_delay = micros();  //Capture time triggered
    detachInterrupt(qT_IN);  //Disable interrupt on qTouch sense pin
    
}  //End of qTouch_ISR

This is potentially dangerous in an ISR if the pin doesn't go LOW within a few µs.

Similarly in a Software Time you should not hang around too long to avoid a back log of events since a timers share the same thread.

Another point to consider is the IP lease time of your router, which may cause the regular disconnects.
And we'd need to see how you are trying to reconnect once the connection dropped out.
You should make sure to allow enough time after Particle.connect() to actually establish the connection before trying it again.

2 Likes

Thanks for the reply.

Currently I am running the code below once per second with the Cloud_timer. I do not have a delay after the Particle.connect(); function. Should I?

if( Particle.connected() ) 
{
    //Debbugging:
    check_qTouch();
    Particle.publish(pairedChannel, String(qT_Delay), 180); 
}
else 
{
    Particle.connect();     //Reinitiate cloud connection (pauses loop until connection is established)
}

For the ISR, would building in timeout help prevent this?How (μs) long is safe? It seems that each cycle takes between 35 and 65 μs.

Just so I better understand the Photon system, what's going on in the background that this ISR could be interfering with?

I'm currently running a test that excludes the capacitive touch sensing code to see if the device is stable without it.

Thanks,
-Joshua

Quick update: I stripped out all use of the capacitive touch sensor and found that the device still disconnects from the cloud. I’ve added a 2 seconds delay after trying to reconnect to the cloud to give some time for the connection to establish. I’ll let you know what I find.

Since the device is disconnecting from the Particle Cloud, not the WiFi, would the WiFi lease time matter? How do I check the IP lease time?

If WiFi does not dropp off, it won't have anything to do with lease time. The lease time is set by your DHCP server and hence would be checked there.
But if you have some code that slowly fragments the heap (e.g. by use of String) a WiFi disconnect due to lease time expiry might be the original cause but only be able to recover partly (reestablish WiFi but not cloud connection).

What is happening in the timer callbacks that you didn't show?

1 Like

Here are the four timers.

Timer LED_timer(50, update_LEDs);        //20Hz
Timer qTouch_timer(250, update_qTouch);  //4Hz
Timer IR_timer(100, update_IR);          //10Hz
Timer Cloud_timer(1000, update_Cloud);   //1Hz

void update_LEDs()
{
    static int t=750;  //4095 is MAX 
   
    //Test LEDs 
    if(IR_Touched)
        analogWrite(LEDS, t);  
     else
        analogWrite(LEDS, 0);
     
    analogWrite(UG_LEDS, t);  

} //End of update_LEDs()




void update_qTouch()
{
    unsigned long total_qTouch_Time=0;
    
    for (unsigned char i=0; i<QTOUCH_SAMPLESIZE; i++)   
    {
        // discharge capacitance at sense pins
        pinMode(qT_IN, OUTPUT);
        pinResetFast(qT_IN);
        pinResetFast(qT_DRIVER);
        
        pinMode(qT_IN,INPUT);    // revert to high impedance input
        
        attachInterrupt(qT_IN, qTouch_ISR, RISING); //Enable interrupt on sense pin
       
        u_tS = micros();   //Capture current time.
        digitalWriteFast(qT_DRIVER, HIGH); 
    
        while (digitalRead(qT_IN)==LOW);
        
        u_tS_delay = micros();  //Capture time triggered
        
        // accumulate the RC delay samples
        if ( u_tS_delay>u_tS )
            total_qTouch_Time += (u_tS_delay - u_tS);   
        else // Redo reading when micros() overflows
            i--;  //Subtract from count to redo
    }
   
    current_qTouch = total_qTouch_Time; //update current qTouch value
    
} //End of update_qTouch()



void qTouch_ISR()
{
    u_tS_delay = micros();  //Capture time triggered
    detachInterrupt(qT_IN);  //Disable interrupt on qTouch sense pin
    
}  //End of qTouch_ISR



void update_IR()
{
    pT = analogRead(IR_IN);     //Get baseline IR value
    digitalWrite(IR_OUT, HIGH); //Turn on IR LED
    
    unsigned long t=micros();  //Capture time
    
    while( micros()<(t+10) ) //Phototransistor takes roughly 10uS to respond.
    {
        if( t>micros() )  //This is incase millis() counter rolls-over.
            t=micros();
    } 

    //Get IR reading difference:
    pT = pT-analogRead(IR_IN);
    
    digitalWrite(IR_OUT, LOW);  //Turn OFF IR LED
    
    if( pT>=IR_TOUCHED )
        IR_Touched = false;
    else
        IR_Touched = true;   //Candle is being snuffed!
        
} //End of update_IR()



void update_Cloud()
{
    static bool toggle=false;
    
    EEPROM.get(0,pairedID);

    Particle.process();

    if( Particle.connected() ) 
    {

//Debbugging:
    check_qTouch();
    Particle.publish(pairedChannel, String(qT_Delay), 180); 

    }
    else 
    {
        Particle.connect();     //Reinitiate cloud connection (pauses loop until connection is established)
    }
    
        
//Debugging (1 second toggle)
    if(toggle)
    {
        toggle = false;
    }
    else
    {
        toggle = true;
    }
    
} //End of update_Cloud()
        if ( u_tS_delay>u_tS )
            total_qTouch_Time += (u_tS_delay - u_tS);   
        else // Redo reading when micros() overflows
            i--;  //Subtract from count to redo

With unsigned integer math this extra check is not required when both variables in the subtraction are of same type …

        total_qTouch_Time += (u_tS_delay - u_tS);   

… is enough


pinSetFast(qT_DRIVER) is even faster than digitalWriteFast(qT_DRIVER, HIGH).


I’d still add a timeout here

 while (digitalRead(qT_IN)==LOW);

something like this

  uint32_t usTimeout = micros();
  while (!digitalRead(qT_IN) && micros() - usTimeout < 500);
  if (micros() - usTimeout > 500) return;

As said above, unsigned integer math works differently - and for some other reasons this …

void update_IR()
{
    pT = analogRead(IR_IN);     //Get baseline IR value
    digitalWrite(IR_OUT, HIGH); //Turn on IR LED
    
    unsigned long t=micros();  //Capture time
    
    while( micros()<(t+10) ) //Phototransistor takes roughly 10uS to respond.
    {
        if( t>micros() )  //This is incase millis() counter rolls-over.
            t=micros();
    } 

    //Get IR reading difference:
    pT = pT-analogRead(IR_IN);
    
    digitalWrite(IR_OUT, LOW);  //Turn OFF IR LED
    
    if( pT>=IR_TOUCHED )
        IR_Touched = false;
    else
        IR_Touched = true;   //Candle is being snuffed!
        
} //End of update_IR()

… could be written like that instead

void update_IR()
{
  pT = analogRead(IR_IN);                              //Get baseline IR value
  pinSetFast(IR_OUT);                                  //Turn on IR LED
  for(uint32_t _us = micros(); micros() - _us < 10;);  // <-- just for demonstration - see comment below
  pT -= analogRead(IR_IN);
  pinResetFast(IR_OUT);                                //Turn OFF IR LED
  IR_Touched = ( pT < IR_TOUCHED ); 
}

But since there already is a dedicated function delayMicroseconds(10) I’d actually just use that :wink:


And I’d also reword void update_Cloud() this way

bool readPairedID = true;
void update_Cloud()
{
  static bool toggle=false;
  static uint32_t msPendingConnect = 0;

  if (readPairedID) {
    EEPROM.get(0,pairedID);
    readPairedID = false;
  }

  if( Particle.connected() ) 
  {
    char val[16];  // avoid using String wherever possible!
    check_qTouch();
    snprintf(val, sizeof(val), "%u", qT_Delay);
    Particle.publish(pairedChannel, val, PRIVATE); 
  }
  else if (millis() - msPendingConnect > 60000)
  { // allow for pending connection attempt to finish
    msPendingConnect = millis();
    Particle.connect();     //Reinitiate cloud connection (pauses loop until connection is established)
  }
        
  toggle = !toggle;

  Particle.process();
} //End of update_Cloud()

        Particle.connect();     //Reinitiate cloud connection (pauses loop until connection is established)

This comment might be true for loop(), but it’s not necessarily true for Software Timers, they will keep firing.

OK, I’ve updated the code according to the suggestions above. (aside from tinkering with my WiFi router settings) I’m running this code on two different devices and both of them lose connection with the cloud within a day or so.

I noticed that the cloud console log starts showing publish events differently after some time. Instead of every second seeing an event, it was every few seconds that it would show an event; like it was “bursting” several at a time and the cloud was limiting them. So I tried changing the cloud update timer from 1.00 seconds to 1.05 seconds and then to 2.00 seconds to see if perhaps my “publish” functions were being called too often. But all had the same results.

What could be going on?