All Retained Variables change their address in memory when new ones are added to the end of the list

Device: P1
Device OS: 2.0.1

On the first version of the my firmware, I had a list of retained variables that are simply declared. Not in the class and not in the structure.
In (Picture 1), shown the physical memory addresses of each retained variable that I have declared.

Picture 1

On the second firmware version I added two retained variables to the end of the list, as described in the reference (bool hasFlowSensor and double amountPerRotation, Picture 2 and 3).

Picture 2 and 3

3 2

I updated the firmware and looked at their addresses (Picture 4).

Picture 4

The addresses of all variables have shifted - the count starts from the last. As a result, the last variable that had the address 0x40024000 became the address 0x40024009. This resulted in an SOS error (5 blinks between sos blinks). I can explain why, but this is irrelevant to the question.
The question is: why does the official reference say that the retained variable should be added after all the others? (Picture 5)

Picture 5

If I add retained variables to the beginning of the list, then everything will be fine, the addresses of the variables will go after the old ones (Picture 6).

Picture 6

Is this a mistake in the reference or am I missing something?

@Vladek9921, do you have test code to share? I wonder if wrapping the variables in a structure would not better guarantee order. @ScruffR, any thoughts?

1 Like

Not sure how it’s handled with the latest compiler, but in some previous version the order in which the variables were stored was inverted compared to the order of declaration - however there is no rule in which order a compiler “has to store” global variables.
A struct is definitely the better option in any case.

Relying on the placement of independent variables is never a good idea.
Irrespective of using struct or not, having a magic number for any new firmware revision and a check before using any persisted data would be the minimum I’d add. A checksum would be the next.
Since one needs a way to validate the data after a cold start anyhow, it should be common practice in all cases.

3 Likes

@peekay123, Here’s the code that works as expected - the address is added at the end of the previous one (I used Web IDE).
You should first flash the first option, then change the comments and flash the second:

Simple code without libs

SerialLogHandler traceLog(LOG_LEVEL_TRACE);

SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
STARTUP(WiFi.selectAntenna(ANT_AUTO));
STARTUP(System.disableFeature(FEATURE_WIFI_POWERSAVE_CLOCK));

// It is necessary to uncomment correctly
#define FIRST_VARIANT
//#define SECOND_VARIANT

#ifdef FIRST_VARIANT
retained uint32_t val1;
retained uint32_t val2;
retained uint32_t val3;
retained double val4;
retained double val5;
retained double val6;
retained uint32_t val7;
retained uint32_t val8;
#endif

#ifdef SECOND_VARIANT
retained uint32_t val1;
retained uint32_t val2;
retained uint32_t val3;
retained double val4;
retained double val5;
retained double val6;
retained uint32_t val7;
retained uint32_t val8;
retained bool val9;
retained double val10;
#endif

bool isNeed = false;
unsigned int timeUpd = 0;

void setup()
{
Particle.connect();
Serial.begin(9600);
}

void loop()
{
if (timeUpd + 5000 < millis())
{
#ifdef FIRST_VARIANT
Log.info(“1 %p”, &val1);
Log.info(“2 %p”, &val2);
Log.info(“3 %p”, &val3);
Log.info(“4 %p”, &val4);
Log.info(“5 %p”, &val5);
Log.info(“6 %p”, &val6);
Log.info(“7 %p”, &val7);
Log.info(“8 %p”, &val8);
#endif
#ifdef SECOND_VARIANT
Log.info(“1 %p”, &val1);
Log.info(“2 %p”, &val2);
Log.info(“3 %p”, &val3);
Log.info(“4 %p”, &val4);
Log.info(“5 %p”, &val5);
Log.info(“6 %p”, &val6);
Log.info(“7 %p”, &val7);
Log.info(“8 %p”, &val8);
Log.info(“9 %p”, &val9);
Log.info(“10 %p”, &val10);
#endif
timeUpd = millis();
}
}

But in the VSCode project I made a library. And here addressing starts from the end. I give a link to the project and you can test it.

VSCode project with library


111
https://drive.google.com/file/d/18EKEStpnG8WNUbyuJ-7sTisSiX9ktipO/view?usp=sharing

@ScruffR Got it, thanks for the answer. But the description in the reference can be confusing for newbies. Could it be better to make a clarification?

@Vladek9921, in your VSCode library, where in the .ino file do you `#include “Retained.h”? I assume it is off screen in you screen capture.

@peekay123 Yes of course. Otherwise it wouldn’t compile. I have attached a link to the project under the screenshots.

@Vladek9921 did you try using a structure?

@peekay123 Yes, I’ve used it before. But once I had some kind of problem with it (don’t remember). After removing the structure, the problem disappeared and I decided to live without it. It was on 1.5.2. But I’ll experiment with the structure again, thanks for the advice.