Can't Flash Argon due to Timeouts

I’m using Argons in a classroom, and I’ve had the following issue happen to two devices so far, and they have become unusable.

I can’t point to a specific cause, but the devices are operating normally and at some point there is a flashing error. From then on, the devices are always breathing cyan but flashing keeps timing out.

Here are some things I’ve tried:

  • Flash device from Workbench and Web IDE while breathing cyan–device times out
  • Put device into DFU and run particle update from CLI . This finishes normally, and I try to claim device again. I’m able to connect to Argon from iOS app, but after I select the wifi network, the app hangs at “connecting device to the internet via wifi”.
  • Run particle serial wifi . I’m able to input the wifi network and the device then starts breathing cyan. I can see the device as being online and I can even ping it (in the iOS app). However, when I try to flash it, it times out again (this happens from Web IDE, Workbench, or iOS app).

I thought it might be a bad device when it happened once, but now that a second device is unusable, I’m guessing something else is going on.

You don’t need to reclaim a device after DFU update nor would the WiFi credentials get erased that way (unless you code does that).
Have you tried OTA update with the device in Safe Mode?
If that works, it’s most likely poorly written code that interferes with the OTA update.

e.g. “excessive” use of delay() and/or blocking loops can cause such behaviour.

I’ve seen Flash from Workbench using USB fail while breathing cyan-device many times. It appears as though the transition to DFU mode (yellow blink) doesn’t happen quick enough causing the timeout. Running Flash from the workbench a second time without changing from DFU mode typically succeeds.

Thanks! I wasn’t sure exactly how to do an OTA update. However, based on your suggestion, I put both devices into safe mode, and I used the iOS app to flash Tinker to the devices. After that, they were back to working properly and accepting firmware again.

It it quite interesting to note that during many threads here - that flashing Tinker seems to cure many, many issues :slight_smile:

Probably because the underlying issue is this

"Detoxing" the device by flashing healthy code allows for a fresh start.

2 Likes

I had a handful of Xenons that were timing out over and over. Flashed tinker, and everything went back to normal.

Is there a way to ‘flash healthy code’ prior to an update? The code I was trying to deploy was the same as on 50 other Xenons which makes this a peculiar issue.

You could do that via USB in DFU Mode (particle flash --usb tinker) or by just sending a dummy application like this

void setup() {}
void loop() {}
1 Like

I’m thinking for pushing remote OTA updates for production devices. Sounds like the dummy app is the way to go.

I’ll give this a test. Thanks!

The timeout devices are now throwing a different odd behaviour. I’m trying to deploy a new version of my app to them, and disconnect from Particle. However they keep trying to connect to Particle regardless. I’ve tried flashing a blank app & moving Particle.disconnect() to the top of my Setup.

Any ideas? Wonder if theres a connection with the Timeouts?

I’ve commented out some areas of the code to run the disconnect at the top of Setup but three of the four Xenons still go right back to trying to connect to Particle.

Update: I have now connected them over USB to flash that way, one of my devices, the flash went through still didn’t fully complete setup. A second time, it did complete setup. Very odd…

 // enable retained memory
 SYSTEM_THREAD(ENABLED);
 STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));

 // Settings
 #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
 #define updatePeriod 10 // update every 10 seconds

// pins
int WATER_SENSOR_PIN = D3;
int boardLed = D7;

// general vars
retained int pulseCount = 0;
retained int lastUpdate = 0;

char *version = "0.1.4";
char deviceInfo[120];  //adjust size as required
char json[256];

// Variables for the reset
const uint32_t connectivity_message_interval = 5000;  // 1 sec
uint32_t last_connectivity_message_time = 0;  // controls how often we send the offline message

const uint32_t connectivity_timeout = 180000UL;  // 3 min b/c 30sec is pretty short, but up to you.
uint32_t last_connectivity_change_time = 0;

bool is_particle_connected; // flag to handle the moment of connectivity change

// This is when there is an issue and to reset the Xenon
void handlePulse(const char *event, const char *data) {
    Serial.println("Heard");
}

// Function to find a device
void handleHello(const char *event, const char *data) {
  if (strcmp(data, System.deviceID()) == 0) {
    digitalWrite(boardLed,HIGH);
    delay(2000);
    digitalWrite(boardLed,LOW);
  }
}

// This is when there is an issue and to reset the Xenon
void handleNeedReset(const char *event, const char *data) {
    System.reset();
}

void handleParticleConnect(const char *event, const char *data) {
  if (strcmp(data, System.deviceID()) == 0) {
    Particle.connect();
  }
}

void handleParticleDisconnect(const char *event, const char *data) {
    Particle.disconnect();
}


// setup() runs once, when the device is first turned on.
void setup() {
  Particle.disconnect();
  Serial.begin(9600);
  pinMode(WATER_SENSOR_PIN, INPUT);
  attachInterrupt(WATER_SENSOR_PIN, pulse, CHANGE);

  Mesh.on();  // potentially needed due to bug when mesh module is not already powered up.
  Mesh.connect();

  // Particle.connect();
  // Particle.variable("version", version);
  // Particle.variable("deviceInfo", deviceInfo);
  //   snprintf(deviceInfo, sizeof(deviceInfo)
  //       ,"App: %s, Date: %s, Time: %s, Sysver: %s"
  //       ,__FILENAME__
  //       ,__DATE__
  //       ,__TIME__
  //       ,(const char*)System.version());
  // is_particle_connected = Particle.connected();
  //
  // Particle.syncTime();
  // Particle.publishVitals(3600);
  // Particle.disconnect();

  // // turn off core LED
  // RGB.control(true);
  // RGB.color(0, 0, 0);

  pulseCount = 0;
  lastUpdate  = Time.now();

  // Mesh
  Mesh.subscribe("hello", handleHello);
  Mesh.subscribe("particle-connect", handleParticleConnect);
  Mesh.subscribe("particle-disconnect", handleParticleDisconnect);
  Mesh.subscribe("pulse", handlePulse);
  Mesh.subscribe("reset", handleNeedReset);
  Serial.println("Finished setup");}

void handle_reset() {
  // I the device is in a bad state, resetting it
  Particle.process();

  if (!Particle.connected()) {
    if (is_particle_connected) {
      // handle moment of disconnection
      is_particle_connected = false;
      last_connectivity_change_time = millis();
      Serial.println("Just Went Offline");
    }
    if ((millis() - last_connectivity_change_time) > connectivity_timeout) {
      // probably an issue connecting, we'll try to fix by resetting
      Serial.println("Resetting due to connectivity timeout...");
      delay(1000);  // allow serial message to get read out
      System.reset();
    }
    if ((millis() - last_connectivity_message_time) > connectivity_message_interval) {
      last_connectivity_message_time = millis();
      Serial.println("Still Offline");
    }
  }
  else {
    if (!is_particle_connected) {
      // handle moment of connection
      is_particle_connected = true;
      last_connectivity_change_time = millis();
      Serial.println("Just Came Online");
    }
    // We are connected!  Time for normal connected stuff...
    if ((millis() - last_connectivity_message_time) > connectivity_message_interval) {
      last_connectivity_message_time = millis();
      Serial.println("Still Online");
    }
  }
}

// loop() runs over and over again, as quickly as it can execute.
void loop() {

   // handle_reset();

   // Loop to see if there should be any information published
   if (Time.now() >= (lastUpdate + updatePeriod)) {
     noInterrupts();   // Disables interrupts while publishing data
     publish();
     lastUpdate = Time.now();
     interrupts(); // Re-enables interrupts when data is published
   }
}

void pulse(void) {
    // increment pulse count
    pulseCount++;
}

void publish() {
  // Try to push data to the rest of the Mesh Network

  bool success;
  snprintf(json, sizeof(json), "{\"time\":%d,\"device\":\"%s\",\"count\":%d,\"version\":%s}", Time.now(), System.deviceID().c_str(), pulseCount, version);
  success = Mesh.publish("meter-data", json);
  if (success == 0) {
    pulseCount = 0;
    lastUpdate  = Time.now();
    Serial.println(json);
  } else {
    Serial.println("Not pushed to Mesh");
  }
}

If you want your device to start without trying to connect to the cloud you should use SYSTEM_MODE(SEMI_AUTOMATIC) or SYSTEM_MODE(MANUAL) and only spin up the mesh connection manually.

1 Like

Thank you!