Saving P2 program state when the power goes off?
bob_g4bby
Posts: 591
Very often, the state of a P2 based module requires saving at switch-off and restoring at switch on. We have several good libraries for flash or sd card file management now. However, that begs the question - how to save those critical project variables with the least amount of effort? Saving individual variables would seem to be more work than saving one structure of variables. A smartpin can be made to monitor the unregulated power voltage and the capacitor across the regulated supply ensures there's enough time to save and close the file before the P2 stops. Any hints and tips?
Cheers, bob

Comments
Interesting question ….
Maybe save entire hub to nonvolatile every so often and restore if reboot detected ?
That’d be easy but not sure if would be ok
If these "variables" don't change much and are acting more like configuration settings then possibly keep them stored in some i2c or SPI accessible NVRAM / EEPROM to begin with and read them back into RAM at run time and update the NVRAM whenever they change. If they change too much then the device may start to wear however, depending on the HW chosen.
If it’s just a few bytes., some new i2c rtc have user ram…
If all the variables that need saving at power-off are collected together in a structure in the top level object, @mystructure gives you the start address and sizeof(mystructure) gives you the byte count. Not too difficult to write that out to a file and read it back in on start-up.
I'm doing that in a game piece; every time the score or game time changes we save it to a battery-backed RTC module. On power-up (e.g., after a battery change in the middle of an 8-hour Airsoft game) the code looks for a running flag in the RAM. If that is set the scoring and game timer is copied from the RAM and the game auto starts, otherwise the game is reset to defaults and waits for a start single from the referee.
I have done this a couple of ways.
1. FRAM memory, writes quick, virtually no wear. If the additional access time doesn't slow things down too much, you can work from it for critical values, states, etc.
2. Exactly as you describe, monitoring actual power and using capacitive 'hang time' to write. I place the critical variables in a section where I can just copy the entire block of data to a file on power loss, and restore it over the block on power up. Know that capacitors age and you can't always control the drain. you might find someday that the hang time isn't enough. Corrupt data can be worse than no data.
Suppose to be 100% safe, should cycle through two copies of this data just to guard against the off chance that power is lost in the middle of a write …
A good excuse for a checksum?
You’d want something to make sure error was good you’d think…
But maybe the odds of bad write are so low, would depend on application ..
Anything that CAN happen, WILL happen, in a highly visible and embarrassing way, with cringingly expensive pyrotechnics, with the highest number of the most important witnesses, at the WORST time possible. Every. Single. Time.
Don't ask how I know...
Saving variables to flash ROM triggered by a power-good signal seems to be a good idea. In a perfect world this would avoid writing too often which could cause storage wear. But in the real world I'd argue that leaving the system in an inconsistent state (RAM content doesn't match flash content) for a longer than neccessary time means asking for trouble. I had too much bad experience with software that only saves when shutting down, for example Mach3 and Code::Blocks. If something unexpected happens you loose the last input and possibly a lot of work.
I think it's better to save as soon as possible after anything has changed. Chip made a power fail safe file system that supports wear levelling. Please also consider using a file format that supports up- and downward compatibility so that newer software can still read the files from the older version after an update. IFF (developed on the Amiga) is a good example. It was mainly used for pictures but is not limited to.
indeed.
Mm - yes, saving on variable change sounds like a very good idea. Also, if saving to sd-card, multiple copies would be a drop in the ocean. Attempting to load most recent copy and iterating back in time if a checksum fails would add more resilience to project initialise.
I save and load configuration variables directly to the SD card.
VAR long PIXELCONFIG[144] long E131CONFIG[432] byte MACADDRESS[6] ' Must be unique per layer 2 network byte IPADDRESS[4] byte SUBNETMASK[4] byte GATEWAY[4] byte DHCP 'Non zero byte BRIGHTNESS byte DNS[4] byte SPEEDDUPLEX long SCREENTIMEOUT long DMXENABLED byte HOSTNAME[32] nametype OUTPUTNAME[32] long ENDOFCONFIG ' End of the loadable configLater in the program.....
handle := sd.openFileRead(string("blitzen.cfg")) sd.readHandle(handle, @PIXELCONFIG, (@ENDOFCONFIG + 3) - @PIXELCONFIG + 1)Just make sure you only add to that memory structure and keep it in the same order.
@ke4pjw Doesn't Spin rearrange bytes and longs in VAR when compiling? Or, maybe that was fixed?
Maybe compiler dependent, think prop tool does this but maybe SpinTools doesn't...
P1/Spin1, yes. P2/Spin2, no. In Spin2 you can treat a block of variables like a structure. I did this early on with the WAV header. Back then FlexProp did rearrange by size, even in the P2. I don't know if that has changed.
A few minutes later....
FlexProp does layout as defined, too. Perhaps I'm mistaken and it always did.
Works in Spin Tools, too.
@Rayman so far, Prop-Tool, Spin-Tools, and PNUT all work the same. If that changes, I will abstract the config, but so far, so good.
Not sure if this works with flexprop.
Looks like Jon verified it works too.
Ok, good to refresh memory on this. Personally, just do all longs, so don't have to think about it...
If I have control, I do longs, but there are times when getting packed information from another device that the mixed variable sizes is nice. Here are the header variables for a WAV:
The file can be checked with
pub validate(p_str) : result | n '' Return true for 16-bit, mono, PCM, canonical WAV '' -- http://soundfile.sapp.org/doc/WaveFormat/ '' -- returns OK (0) if file is valid '' * error codes are negative if (has_file(p_str) <> OK) return EOF n := rd_buf(@chunkid, 44) ' get header structure if (n <> 44) return -2 if (chunkid <> RIFF) return -3 if (format <> WAVE) return -4 if (audioformat <> 1) return -5 if (numchannels <> 1) return -6 if (samplerate < 1000) || (samplerate > 48_000) return -7 if (bitspersample <> 16) return -8 return OKIn my P1 code I have to read the header into a separate buffer and then use bytemove() to extract individual variables to be checked.
Guess we have the new Spin2 structure stuff...