[SOLVED] Upgrading to 0.7.0 then downgrading to 0.6.1 breaks bootloader funcitonality

Here is what happened to me:

Upgraded Electron from release 0.6.1 to 0.7.0

Downgraded back to 0.6.1 by flashing the three spark firmware binaries to Electron.

Ran a test program that reports the reset reason using System.resetReason()

Whenever a reset was instigated by the reset pin, System.resetReason() would return 0 (i.e. RESET_NONE) instead of 20 (i.e. RESET_REASON_PIN)

Used ST-Link utility to manually flash 0.4.8 bootloader to Electron from following link: https://github.com/rickkas7/particle_jtag/blob/master/files/bootloader-electron.bin?raw=true

After that, things started working normally again.

So, my question is: is this supposed to work like this? How do I make sure that the bootloader on any given device works with the spark firmware on that device?

Here is my test program:

#include "Particle.h"

PRODUCT_ID(xxx);
PRODUCT_VERSION(xx);

//Configure how the app runs within the Particle ecosystem
SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);

//Configure Particle system (runs this code before any Particle init code runs):
STARTUP(
   //Configure other Particle runtime parameters
   System.enableFeature(FEATURE_RETAINED_MEMORY);
   //Enable system reset information
   System.enableFeature(FEATURE_RESET_INFO);
   //Make sure that cloud DFU updates are enabled
   System.enableUpdates();
);


//Define log handler using serial monitor (dumps logs to serial monitor)
SerialLogHandler logHandler(LOG_LEVEL_ALL,
   {
      {"app"                  , LOG_LEVEL_ALL   },
   }
);

static Logger myLog("app.main");  //Logger object used in this "main.cpp" file


retained uint32_t reset_data = 0;


enum spark_api_reset_reason_t{
   RESET_PIN_RESET,
   RESET_POWER_MANAGEMENT,
   RESET_POWER_DOWN,
   RESET_POWER_BROWNOUT,
   RESET_WATCHDOG,
   RESET_UPDATE,
   RESET_UPDATE_TIMEOUT,
   RESET_FACTORY_RESET,
   RESET_SAFE_MODE,
   RESET_DFU_MODE,
   RESET_PANIC,
   RESET_USER,
   RESET_UNKNOWN,
   RESET_NONE,
   NUM_RESET_REASONS
};

const char* const spark_api_reset_reason_names[NUM_RESET_REASONS+1] = {
   "RESET_PIN_RESET",
   "RESET_POWER_MANAGEMENT",
   "RESET_POWER_DOWN",
   "RESET_POWER_BROWNOUT",
   "RESET_WATCHDOG",
   "RESET_UPDATE",
   "RESET_UPDATE_TIMEOUT",
   "RESET_FACTORY_RESET",
   "RESET_SAFE_MODE",
   "RESET_DFU_MODE",
   "RESET_PANIC",
   "RESET_USER",
   "RESET_UNKNOWN",
   "RESET_NONE",
   "NO RESET REASON MATCHES!"
};



int resetReason;
int resetReasonData;

void setup() // Put setup code here to run once
{
   Serial.begin(115200);

   delay(3000);


   Serial.println("========================================================================================");
   Serial.println("================  EVALUATING RESET REASON ==================");
   myLog.info("SysVersion: %s", System.version().c_str());
   myLog.trace(
      "System.resetReason() == %d  |  System.resetReasonData() == %lu",
      System.resetReason(), System.resetReasonData()
   );
   Serial.println("========================================================================================");

   resetReason = System.resetReason();
   resetReasonData = System.resetReasonData();

   spark_api_reset_reason_t decoded_reset_reason = NUM_RESET_REASONS;

   switch(resetReason){

      case RESET_REASON_PIN_RESET:
      decoded_reset_reason = RESET_PIN_RESET;
      break;

      case RESET_REASON_POWER_MANAGEMENT:
      decoded_reset_reason = RESET_POWER_MANAGEMENT;
      break;

      case RESET_REASON_POWER_DOWN:
      decoded_reset_reason = RESET_POWER_DOWN;
      break;

      case RESET_REASON_POWER_BROWNOUT:
      decoded_reset_reason = RESET_POWER_BROWNOUT;
      break;

      case RESET_REASON_WATCHDOG:
      decoded_reset_reason = RESET_WATCHDOG;
      break;

      case RESET_REASON_UPDATE:
      decoded_reset_reason = RESET_UPDATE;
      break;

      case RESET_REASON_UPDATE_TIMEOUT:
      decoded_reset_reason = RESET_UPDATE_TIMEOUT;
      break;

      case RESET_REASON_FACTORY_RESET:
      decoded_reset_reason = RESET_FACTORY_RESET;
      break;

      case RESET_REASON_SAFE_MODE:
      decoded_reset_reason = RESET_SAFE_MODE;
      break;

      case RESET_REASON_DFU_MODE:
      decoded_reset_reason = RESET_DFU_MODE;
      break;

      case RESET_REASON_PANIC:
      decoded_reset_reason = RESET_PANIC;
      break;

      case RESET_REASON_USER:
      decoded_reset_reason = RESET_USER;
      break;

      case RESET_REASON_UNKNOWN:
      decoded_reset_reason = RESET_UNKNOWN;
      break;

      case RESET_REASON_NONE:
      decoded_reset_reason = RESET_NONE;
      break;
   }

   myLog.trace(
      "decoded_reset_reason == %s",
      spark_api_reset_reason_names[decoded_reset_reason]
   );
   Serial.println("========================================================================================");


   Serial.println("------- LIST OF RESET REASON ENUMERATED TYPES -------");
   Serial.printlnf("RESET_REASON_NONE = %d", RESET_REASON_NONE);
   Serial.printlnf("RESET_REASON_UNKNOWN = %d", RESET_REASON_UNKNOWN);
   Serial.printlnf("RESET_REASON_PIN_RESET = %d", RESET_REASON_PIN_RESET);
   Serial.printlnf("RESET_REASON_POWER_MANAGEMENT = %d", RESET_REASON_POWER_MANAGEMENT);
   Serial.printlnf("RESET_REASON_POWER_DOWN = %d", RESET_REASON_POWER_DOWN);
   Serial.printlnf("RESET_REASON_POWER_BROWNOUT = %d", RESET_REASON_POWER_BROWNOUT);
   Serial.printlnf("RESET_REASON_WATCHDOG = %d", RESET_REASON_WATCHDOG);
   Serial.printlnf("RESET_REASON_UPDATE = %d", RESET_REASON_UPDATE);
   Serial.printlnf("RESET_REASON_UPDATE_TIMEOUT = %d", RESET_REASON_UPDATE_TIMEOUT);
   Serial.printlnf("RESET_REASON_FACTORY_RESET = %d", RESET_REASON_FACTORY_RESET);
   Serial.printlnf("RESET_REASON_SAFE_MODE = %d", RESET_REASON_SAFE_MODE);
   Serial.printlnf("RESET_REASON_DFU_MODE = %d", RESET_REASON_DFU_MODE);
   Serial.printlnf("RESET_REASON_PANIC = %d", RESET_REASON_PANIC);
   Serial.printlnf("RESET_REASON_USER = %d", RESET_REASON_USER);
   Serial.println("========================================================================================");
}


void loop() // Put code here to loop forever
{

   if(millis() > 40000){
      resetReasonData++;
      System.reset(resetReasonData);
   }
   else{
      Serial.printlnf("looping");
      delay(1000);
   }

   if(Serial.available()){
      System.dfu();
   }

}

Yes, you must downgrade to 0.6.3 first, then you can downgrade to 0.6.1 (or earlier):

From the 0.6.3 release notes:

Downgrade bootloader when downgrading from 0.7.0 or newer. #1416

The reason is that in the past, the bootloader was never downgraded, only upgraded. However, with 0.7.0 this boot loader is not compatible with 0.6.2 or earlier, so a downgrade is necessary.

3 Likes

From 0.7.0 release notes:

Note: Downgrading [Electron/Photon/P1] OTA or YModem transfer:
If you need to downgrade, you must downgrade to 0.6.3(Photon/P1), 0.6.4(Electron) to ensure that the bootloader downgrades automatically. When downgrading to older versions, downgrade to 0.6.3(Photon/P1), 0.6.4(Electron) first, then to an older version such as 0.5.3. You will have to manually downgrade the bootloader as well (see release notes in previous 0.7.0-rc.3 release)

Does this release note apply in my case since I was downgrading via DFU util over USB as opposed to OTA or YModem ?

Assuming that this note does apply to my case and simply isn't worded correctly (i.e. should read "OTA, YModem, or DFU-util transfer"), we move on to next set of release notes mentioned in the above release notes:

From 0.7.0-rc.3 release notes:

Note about Downgrading [Electron] OTA or YModem transfer: Downgrade in normal order with 0.6.2 system binaries: system-part3 -> system-part2 -> system-part1 -> bootloader (v0.6.2).

:pencil2: 0.6.2 bootloader binaries have been added to this release below (on github) for convenience.

:pencil2: You can only flash the bootloader OTA or YModem over USB. DFU mode over USB will fail because it uses the bootloader itself for that mode of operation.
YModem e.g. particle flash --serial bootloader-0.6.2-photon.bin

Again, does this even apply in my case since I was downgrading via DFU util over USB as opposed to OTA or YModem?

Also, how is part3->part2->part1->bootloader the "normal order".... this is actually the reverse of normal order isn't it?

@rickkas7 Can you explain in plain english how to downgrade from 0.7.0 to 0.6.1 properly?

With CLI (I’d use particle flash --usb for DFU instead of dfu-util) you’d download the binaries for the system and the bootloader fitting for the system version and do

particle flash --usb system-part3.bin
particle flash --usb system-part2.bin
particle flash --usb system-part1.bin

(in DFU Mode)
and then

particle flash --serial bootloader.bin

(in Listening Mode)

Or you do all the modules in LM via --serial

1 Like

Awesome, thanks @ScruffR

One more question:

Is there a list or table somewhere indicating which bootloader should be used with a given firmware release? If so, and that table does not contain links to download said bootloader, where can I download said bootloader?

When you do a particle serial inspect it returns integer version numbers. This table maps those into regular version number, and also includes the boot loader version associated with each system firmware version.

It’ doesn’t include a link to the boot loaders, however you only really run into an issue when you cross the 0.7.0 boundary and there are really only a couple problematic cases.

3 Likes

Awesome, thanks!

This might be helpful as well:

Upgrade Chart - Photon/P1

  • You should be able to upgrade any 0.4.6 or later directly to any version for Photon/P1.

There is one important caveat, however: If you are upgrading from a version prior to 0.7.0 to 0.7.0 or later, on the Photon/P1 the device must have cloud access unless you flash the bootloader manually.

The reason is that 0.7.0 requires a bootloader upgrade. In versions prior to 0.7.0 the bootloader was embedded in the system firmware parts, but there wasn’t enough room in 0.7.0 to do that.

If you’re upgrading OTA (including safe mode healer), you’ll notice four reboots or more, for user part, for system part 1, system part 2, and bootloader.

The problem occurs if you upgrade by USB DFU (particle update). You’ll get system part 1 and system part 2, but not the bootloader, because the bootloader cannot be updated using DFU.

It should be updated by safe mode healer, unless you have a device that is not configured for Wi-Fi, then you should manually flash the bootloader in serial mode or JTAG/SWD.

Downgrade Chart - Photon/P1

  • Any version 0.7.0 or later to 0.6.x, you must downgrade to 0.6.3 first. (Reason: only this version downgrades the bootloader.) After you downgrade to 0.6.3 you can downgrade further if necessary. OTA you must flash part2, then part 1.

  • Any version 0.7.0 or later to 0.5.x, you must downgrade to 0.5.5 first. (Reason: only this version downgrades the bootloader.) After you downgrade to 0.5.5 you can downgrade further if necessary. OTA you must flash part2, then part 1.

Upgrade Chart - Electron

Any version prior to 0.5.3, including the old factory default of 0.4.8, you must upgrade to 0.5.3, 0.5.4, or 0.5.5 before upgrading to any higher version. (Reason: support for 3 system parts.)

Downgrade Chart - Electron

  • Any version 0.7.0 or later to 0.6.x, you must downgrade to 0.6.4 first. (Reason: only this version downgrades the bootloader.) After you downgrade to 0.6.4 you can downgrade further if necessary. OTA you must flash part3, part2, then part 1.

  • Any version 0.7.0 or later to 0.5.x, you must downgrade to 0.5.5 first. (Reason: only this version downgrades the bootloader, and downgrade to 2 system parts). OTA you must flash part 2, then part 1.

  • 0.6.x to 0.5.2 or earlier: You must downgrade to 0.5.3, 0.5.4, or 0.5.5 first. (Reason: downgrade to 2 system parts). OTA you must flash part2, then part 1.