Photon flash OTA problem

Hi :slight_smile:

I’ve a problem with my particle: It seems that the internal antenna is broken (or never worked because it’s a brand-new photon) and it wasn’t possible to connect it properly to any network. So I purchased an external antenna and now it’s working. But sometimes when I want to flash a new code OTA it says “flash unsuccessful” but in fact the photon starts to flash/blink mangenta at the same time when “flash unsuccessful” appears (during 30sec-1min). When it’s working properly it says “flash successful” and mangenta lasts only for 10sec.

Why it isn’t working properly?

Best regards

I think this is a known quirk usually when there is excessive Network latency or heavy usage on the cloud services. You can effectively ignore the unsuccessful message if you know your application flashed successfully. It is a good practice to add a particle.function() with your code version number. That way you can query the device to make sure the flash was successful. Increment your version number on each iteration of flashing.

So normally I get an “App-Hash message” in my console (if it’s a new version). Do you mean to send this message with particle.function() again?

What I meant is that you can add a version number to your user code like this:

const char version[] = "Mesh_MarcoPoloHeartbeat_Marco_v0.5.0";

void setup() {
    Particle.variable("version", version);
}

You can see this in context in my sample code here: https://github.com/ninjatill/Mesh_MarcoPoloHeartbeat/tree/master/v0.5.0

Any time you need to flash a code iteration, increment the version number. In the example above, I would change 0.5.0 to 0.5.1 and flash the device OTA. If I received a flash unsuccessful message but on the device I observed blinking magenta, I would wait a bit until the magenta flashing finished. When the device goes back to breathing cyan, I would go to the console, find the device, and request the version variable. If the version came back at 0.5.1, I know the flash was indeed successful and you can ignore the messages from the IDE.

1 Like

Does the flash eventually complete? The 30 second delay is common for me (can result either from blocking application code or some usages of TCP), but the flash still happens and usually completes fine. What exact messages do you get on the console when you try to flash?

The version number / hash is published on a full handshake with the cloud (which updates the version number in Particle Products). You can force this by having a cloud function that can call Particle.publish("spark/device/session/end", "", PRIVATE); to double check, or use a variable if needed.

@electronweather have you tried putting the photon into safe mode before trying to flash?

Your app may not be giving enough airtime to DeviceOS to do its thing…

I will give it a try :slight_smile:
But usually my photon isnt at the same location as my computer so it’s a little bit difficult…

Really the best way to make sure that you are giving enough time for the OTA update is to listen for the update pending event and then skip any blocking code that you can to give priority to the system OTA update process. This has improved the consistency of my OTA updates a lot.

Is there a command which ensures that the Photon is listening to the update event?

This is roughly what I use:

bool ota_ongoing;

void firmware_update_handler(system_event_t event, int param)
    {
        if (param == firmware_update_begin) {
            ota_ongoing = true;
        }
        else if (param == firmware_update_progress) {
            // do nothing, this is kinda spammy
        }
        else if (param == firmware_update_complete) {
            ota_ongoing = true;
        }
        else if (param == firmware_update_failed) {
            ota_ongoing = false;
            if (Particle.connected()) Particle.publish("spark/device/session/end", "", PRIVATE);
        }
    }

    void firmware_update_pending_handler()
    {
        ota_ongoing = System.updatesEnabled();
    }

setup() {
        ota_ongoing = false;
        System.on(firmware_update, firmware_update_handler);
        System.on(firmware_update_pending, firmware_update_pending_handler);
}

loop() {
    // do critical stuff here

    if (ota_ongoing) {
        Particle.process();
        return;
    }

    // do less critical stuff here
}
1 Like

@justicefreed_amper, very interesting and clever!

@electronweather, am not suggesting that you put the device into safe mode every time, just as an experiment to see if this fixes your issue. If it does fix the issue, then this points to an issue within your app and you can try techniques likes @justicefreed_amper’s and/or look for blocking code.

Just a minor note on style: Since param is an int your code would possibly look a bit neater using this

  switch(param) {
    case firmware_update_begin:
    case firmware_update_progress: // in case we missed the begin event set true anyway
      ota_ongoing = true;
      break;
    case firmware_update_failed:
      if (Particle.connected()) 
        Particle.publish("spark/device/session/end", "", PRIVATE);
    case firmware_update_complete:
      ota_ongoing = false;
      break;
    default:
      break;
  }

BTW, is firmware_update_complete supposed to signal ota_ongoing = true?
Since it didn’t seem logical I changed that in my sample implementation to show how to use case-fall-through.

1 Like

BTW, is firmware_update_complete supposed to signal ota_ongoing = true ?

It's intentional. From a system firmware process standpoint, after a firmware_update_complete, the device will simply reset a couple seconds later. (This assumes of course that you have resets enabled. If you don't then this flag should be used to trigger a reset. In my case resets are enabled as normal)

I don't want to suddenly start doing a bunch of things one second before the device resets. This way, I leave my networking MQTT stuff off and minimize the amount that I'm doing writes to my Sd Card. Otherwise I'm going to be halfway through a bunch of processes when I reset, and I'd rather make sure there is nothing preventing the device from resetting quickly and safely.

Since param is an int your code would possibly look a bit neater using this

True, agreed.

1 Like

Of course, that makes perfect sense :+1: