Hi,
I have a product which has been in production for a number of years, and which has a lot of configurable settings. Some of these are set at the factory and are hidden, while others are accessible to the end user.
The environment is an ARM microcontroller using internal storage only. No file system or networking, but it can be connected to a PC via a serial link.
Typically I store all of a product's settings in a C struct called 'nv' ('non-volatile'), which is stored in Flash whenever a change is made, and read back in as part of the boot process.
Within this struct there are all kinds of integers, strings, other structs and so on, and then right at the very end there's a uint32_t which must always be set to a 'magic number' to indicate that the rest of the struct has been populated with valid data. This number could be a fixed value, a CRC, a cryptographic hash, checksum, or whatever.
When the firmware boots, it checks for the magic number. If it's valid, then that's great, carry on as normal. If not, it assumes the configuration is broken, and restores factory default settings including a new magic number.
This works well. It ensures that the unit always boots with valid settings whether it's brand new and is booting for the first time, or if it's been updated to new firmware which (usually) has a larger 'nv' struct with the magic number in a different place.
Until quite recently, firmware updates have only ever been done at a very limited number of authorised sites, where having the unit reset to factory settings after an update isn't a problem. However, I'd now like to explore how to go about preserving settings when a unit is updated by the end user in the field.
The issue is, of course, that new firmware may well require some new settings that weren't supported before. Insert new items into a struct, and everything beyond them has to move.
A few options spring to mind, but I can't quite figure out which I dislike least.
- Keep the original nv struct exactly the same forever. For new settings introduced in the next firmware release, create a new struct called nv2. Then nv3, nv4 etc, each with their own magic number to show they're valid.
- As above, but add the new items to the end of nv so they sit after the magic number, which at least means I don't have to litter my code with nv3.this, nv5.that, nv2.the_other etc
- Reserve space in nv between the last used setting and the magic number, put any new settings here. The magic number doesn't move, so it definitely has to be a CRC or hash, not a fixed value.
- Tag the nv struct with a value that indicates its version number. Include start-up code that recognises previous versions and knows how to migrate settings to the current version.
- Export settings in a readable format, with tags or commands to indicate what each value actually means. After an upgrade, reset to factory defaults, then parse the readable version to get back to a binary.
- The better idea that I haven't thought of yet...!
Suggestions please, folks. What's the industry's solution to this problem?