OTA update timing out on particle electron

Hello all,

I need help figuring out why I am not able to update firmware on my particle electron which is online and functioning ok.
I have a third party sim in it on local carrier. I am getting a timeout when I run the update either from the web IDE or from CLI. Below is what I have in terms of software versions and the resulting failure line.

macbook:Downloads goof$ particle list
kafue1 [430048001451343334363036] (Electron) is online
  Functions:
    int batt(String args) 
macbook:Downloads goof$ particle flash kafue1 firmware-2.bin 
Including:
    firmware-2.bin

! Flashing firmware Over The Air (OTA) uses cellular data, which may cause you to incur usage charges.
! This flash is estimated to use at least 0.019 MB, but may use more depending on network conditions.

! Please type 0.019 below to confirm you wish to proceed with the OTA flash.
! Any other input will cancel.
? Confirm the amount of data usage in MB: 0.019
attempting to flash firmware to your device kafue1
Flash device failed
Timed out.
macbook:Downloads goof$ npm --version
3.9.5
macbook:Downloads goof$ particle --version
1.14.2
macbook:Downloads goof$ node --version
v4.4.5

Can you try to put your device into Safe Mode and try the same again?
If this works any better, it’s most likely some behaviour of your running code that contributes to the issue.
Showing your code might help then.

Many thanks. I will try that later today when I get physical access to the device. My code is below:

// This #include statement was automatically added by the Particle IDE.
#include "ThingSpeak/ThingSpeak.h"

#define MB1043_5V_PIN D2
#define MB1043_SENSOR_PIN D4
#define LEDPin D7
#define publish_cycle 600000 // Only publish every 10 minutes



/* Thingspeak */
TCPClient client;
unsigned long myChannelNumber = XXXXXX;
const char * myWriteAPIKey = "YYYYYYYY";
//
unsigned int lastPublish = 0;
int arraysize =9;
FuelGauge fuel;
//float analogValue0;
float distance;


//declare an array to store the samples from the ultrasonic sensor
//not necessary to zero the array values here, it just makes the code clearer
int rangevalue[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0};
long pulse;
int modE;



void setup() {
    // Connect to ThingSpeak
    ThingSpeak.begin(client);
    Particle.function("batt", batteryStatus);

    // Give power to the sensor
    pinMode(MB1043_5V_PIN, OUTPUT);
    digitalWrite(MB1043_5V_PIN, HIGH);
    pinMode(LEDPin, OUTPUT);
    // Connect variables to particle cloud
    // This allows you to save data to particle.io, and run commands against it such as "particle variable Photon get light"
    //Particle.variable("distance", &distance, DOUBLE);
    
    Serial.begin(9600);
    // Wait for the sensor to stabilize
    delay(1000);
}  

void loop() {
    //Serial.print("testing loop");
    unsigned long now = millis();
    // Read value from sensor Pin
    distance = float(get_distance()); // get mean distance from sonar sensor
   // Update the ThingSpeak fields with the new data
    ThingSpeak.setField(1, float(distance) );
    
    // Write the fields that you've set all at once.
    // Publish to thinkspeak. We only publish if it has been 10min since the last time we published
    if ((now - lastPublish) > publish_cycle) {
        ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
        lastPublish = now; // update the last time we published to current time
        Serial.println(" - Published!");
    }
    else {
        Serial.println();
    }
    //print to serial
    Serial.println(distance);
    // Give time for the message to reach ThingSpeak before next loop
    delay(2000); // wait for 2 seconds

    // Sleep for 15 minutes to save battery
    //System.sleep(SLEEP_MODE_DEEP, 15 * 60);
    //Serial.println(distance);
}


// sort function (Author: Bill Gentles, Nov. 12, 2010)
void isort(int *a, int n){
// *a is an array pointer function
  for (int i = 1; i < n; ++i)
  {
    int j = a[i];
    int k;
    for (k = i - 1; (k >= 0) && (j < a[k]); k--)
    {
      a[k + 1] = a[k];
    }
    a[k + 1] = j;
  }
}

//Mode function, returning the mode or median.
int mode(int *x,int n){

  int i = 0;
  int count = 0;
  int maxCount = 0;
  int mode = 0;
  int bimodal;
  int prevCount = 0;
  while(i<(n-1)){
    prevCount=count;
    count=0;
    while(x[i]==x[i+1]){
      count++;
      i++;
    }
    if(count>prevCount&count>maxCount){
      mode=x[i];
      maxCount=count;
      bimodal=0;
    }
    if(count==0){
      i++;
    }
    if(count==maxCount){//If the dataset has 2 or more modes.
      bimodal=1;
    }
    if(mode==0||bimodal==1){//Return the median if there is no mode.
      mode=x[(n/2)];
    }
    return mode;
  }
}

//get the distance from the sonar sensor
int get_distance() {
    for(int i = 0; i < arraysize; i++)
  {                    
    pulse = pulseIn(MB1043_SENSOR_PIN, HIGH);
    rangevalue[i] = pulse/10;
    delay(10);
  }
  
  isort(rangevalue,arraysize);
  
  modE = mode(rangevalue,arraysize);
  
  if (modE <=250) {
      digitalWrite(LEDPin, HIGH);
  }
  else { digitalWrite(LEDPin, LOW); 
  }
  return modE;    
}


// Lets you remotely check the battery status by calling the function "batt"
// Triggers a publish with the info (so subscribe or watch the dashboard)
// and also returns a '1' if there's >10% battery left and a '0' if below
int batteryStatus(String command){
    // Publish the battery voltage and percentage of battery remaining
    // if you want to be really efficient, just report one of these
    // the String::format("%f.2") part gives us a string to publish,
    // but with only 2 decimal points to save space
    Particle.publish("B", 
          "v:" + String::format("%.2f",fuel.getVCell()) + 
          ",c:" + String::format("%.2f",fuel.getSoC()),
          60, PRIVATE
    );
    // if there's more than 10% of the battery left, then return 1
    if(fuel.getSoC()>10){ return 1;} 
    // if you're running out of battery, return 0
    else { return 0;}
}

Looking at your code, I’d guess you will have better OTA flashing chances when you have a detectable object in front of your MB1043 sensor and will fail most the time if you haven’t :wink:

I will try to place some object close by, except I have set up sensor to continuously range and detect something as can be seen here: https://thingspeak.com/channels/105765

Anything else you can see to be potential issue? Does the use of third party sim have any effect?

Anthony

I tried to watch the logs on the Dashboard as I run the OTA flash process from CLI. While it reports timeout on the console, the log says failed instead. See image below:

It should not.

Have you tried Safe Mode?

Try to reduce the sections in your code that block for more than just a few milliseconds to an absolute minimum and wherever you need more time add a Particle.process() call (e.g. in loops).

1 Like

@ScruffR, the normal CLI OTA worked once after many attempts yesterday. It has not worked again. After that it has only consistently worked for me when in Safe Mode. This will be rather hard to do when the device is up on a pole 10km away. Is there a way to remotely put it in safe mode?

Thanks

anthony

When you can consistently OTA in Safe Mode but not while your own code is running it’s definetly your code that does prevent OTA from working.
So the best way forward is as I already said: “Find the blocking sections and unblock them.”

pulseIn() is one candidate that might contribute as it will block for up to three seconds per call (in your case 10 times in a row with 10ms delay between despite the datasheet info of 10Hz sampling rate which would mean 100ms).

But there is a function System.enterSafeMode()
On the other hand how would you trigger it? Blocking code also interferes with Particle.function() calls.

1 Like

Many thanks. I will surely look into making the code less chatty.

Anthony

@ScruffR, I took a closer look at the code. You were absolutely right.
The culprits were the Serial.println() statements in the loop() function. After I commented them out , the OTA flashing succeeds 80% of the time.

Thanks

Anthony

1 Like

Aha! @ScruffR to the rescue! Thanks for the help :slight_smile:

1 Like

Ahhhh!!!, I spoke too soon. Today OTA is not working at all. More home work I suppose.

Anthony

I would not have thought the Serial.println() statements would introduce enough lag to break OTA, I’m still convinced it has to do with pulseIn().
Just try to reduce your arraysize to 2 for testing and add some Particle.process() calls in any of your for()/while() blocks.

I’d also replace the delay(2000) in your loop() with a non-blocking aproach like

void loop()
{
  static uint32_t msSoftDelay;
  if (millis() - msSoftDelay < 2000) return;
  msSoftDelay = millis();
  ...
}

Thanks @ScruffR, I will do that and provide an update later.

anthony

I have had more success by not having any delay() function in the loop() as well as inserting Particle.process() after every publish to ThingSpeak channel and at the very end of the loop() function. I will see how this holds up in the next week but for now it has worked 90% of the time.

thanks

Anthony

2 Likes

Hello @goof2092
If you can post the code.
I have a similar problem. When the interval of sending data over TCP is too frequent for example every 15S, then I can not make a OTA.

Thanks.

sure @developer_bt, see below. I still have failures but is not too bad. Goes through after several attempts. I still need to debug more but its better than before when it was impossible to update.

// This #include statement was automatically added by the Particle IDE.
#include "ThingSpeak/ThingSpeak.h"
#include "application.h"

#define MB1043_5V_PIN D2
#define MB1043_SENSOR_PIN D4
#define LEDPin D7
#define publish_cycle 15000 // Only publish every 10 minutes



/* Thingspeak */
TCPClient client;
unsigned long myChannelNumber = XXXXXX;
const char * myWriteAPIKey = "YYYYYYYY";
//
unsigned int lastPublish = 0;
int arraysize =9;
FuelGauge fuel;
//float analogValue0;
float distance;
char publishStr[20];


//declare an array to store the samples from the ultrasonic sensor
//not necessary to zero the array values here, it just makes the code clearer
int rangevalue[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0};
int rangevalue2;
long pulse;
int modE;
uint32_t lastTime = millis()-(1800*1000UL);


void setup() {
    // Connect to ThingSpeak
    ThingSpeak.begin(client);
    // Give power to the sensor
    pinMode(MB1043_5V_PIN, OUTPUT);
    digitalWrite(MB1043_5V_PIN, HIGH);
    pinMode(LEDPin, OUTPUT);
    // Connect variables to particle cloud
    // This allows you to save data to particle.io, and run commands against it such as "particle variable Photon get light"
    Particle.variable("dist", double(distance));
    Particle.function("batt", batteryStatus);
    Serial.begin(9600);
    // Wait for the sensor to stabilize
    delay(10);
}  

void loop() {
    //Serial.print("testing loop");
    unsigned long now = millis();
    // Read value from sensor Pin
    distance = float(get_distance()); // get mean distance from sonar sensor
    /*
    We will publish and update ThingSpeak channel only when values of distance correspond to object detection range
    so that we do not keep the little guy busy with data we do not care about, thus avoid OTA update failures
    */
    if (distance < 450.00 ) {
        //Turn on visual LED  so we can have visual clue when it 'sees' something from 
        digitalWrite(LEDPin, HIGH);
        
        // Update the ThingSpeak fields with the new data
        ThingSpeak.setField(1, distance );
    
        // Write the fields that you've set all at once.
        // Publish to thinkspeak. We only publish if it has been 60 seconds since the last time we published
        //if ((now - lastPublish) > publish_cycle) {
            ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
            lastPublish = now; // update the last time we published to current time
            Serial.println(" - Published!");
            Serial.println(String(distance));
            Particle.publish("B", 
            "v:" + String::format("%.2f",fuel.getVCell()) + 
            ",c:" + String::format("%.2f",fuel.getSoC()),
            60, PRIVATE);
          
            Particle.publish("D", 
            "d:" + String::format("%.2f",distance,60,PRIVATE));  
            Particle.process(); // Looks like this helps with OTA updates
    
        }
      // else {
        //    Serial.println("Still under 2 minutes");
        //}
    //}
    
    else {
        Serial.println("No object detected");
        digitalWrite(LEDPin, LOW); 
        Particle.process(); // looks like this helps with OTA updates
    }
   
   /* 
    static uint32_t msSoftDelay;
    if (millis() - msSoftDelay < 1000) return;
    msSoftDelay = millis(); */
    
}

//get the distance from the sonar sensor
int get_distance() {
    ///for(int i = 0; i < arraysize; i++)
  //{                    
    pulse = pulseIn(MB1043_SENSOR_PIN, HIGH);
    //rangevalue[i] = pulse/10;
    rangevalue2 =pulse/10;
    //delay(10);
    Particle.process(); // May be they are too many now, don't know but they seem to help with OTA updates
  //}
  
  //isort(rangevalue,arraysize);
  
  //modE = mode(rangevalue,arraysize);
  
  return rangevalue2;    
}


// sort function (Author: Bill Gentles, Nov. 12, 2010)
void isort(int *a, int n){
// *a is an array pointer function
  for (int i = 1; i < n; ++i)
  {
    int j = a[i];
    int k;
    for (k = i - 1; (k >= 0) && (j < a[k]); k--)
    {
      a[k + 1] = a[k];
    }
    a[k + 1] = j;
  }
}

//Mode function, returning the mode or median.
int mode(int *x,int n){

  int i = 0;
  int count = 0;
  int maxCount = 0;
  int mode = 0;
  int bimodal =0;
  int prevCount = 0;
  while(i<(n-1)){
    prevCount=count;
    count=0;
    while(x[i]==x[i+1]){
      count++;
      i++;
    }
    if((count>prevCount)&(count>maxCount)){
      mode=x[i];
      maxCount=count;
      bimodal=0;
    }
    if(count==0){
      i++;
    }
    if(count==maxCount){//If the dataset has 2 or more modes.
      bimodal=1;
    }
    if(mode==0||bimodal==1){//Return the median if there is no mode.
      mode=x[(n/2)];
    }
    return mode;
  }
}



// Lets you remotely check the battery status by calling the function "batt"
// Triggers a publish with the info (so subscribe or watch the dashboard)
// and also returns a '1' if there's >10% battery left and a '0' if below
int batteryStatus(String command){
    // Publish the battery voltage and percentage of battery remaining
    // if you want to be really efficient, just report one of these
    // the String::format("%f.2") part gives us a string to publish,
    // but with only 2 decimal points to save space
    Particle.publish("B", 
          "v:" + String::format("%.2f",fuel.getVCell()) + 
          ",c:" + String::format("%.2f",fuel.getSoC()),
          60, PRIVATE
    );
    // if there's more than 10% of the battery left, then return 1
    if(fuel.getSoC()>10){ return 1;} 
    // if you're running out of battery, return 0
    else { return 0;}
}

1 Like

@goof2092, any reason you are not using SYSTEM_THREAD(ENABLED);?

Hi @peekay123,

It is more of ignorance on my part than anything else. I am still a “trial and error hack other people’s code” type of coder at the moment, not yet accomplished. Any pointer regarding how to use that functionality?

Thanks

Anthony