Comparison of a Volatile; Uno works, Nano Every does not

Howdy folks. I wrote up some code that has a Volatile variable to read button inputs for a quick exit of a sub program and to enter the next correct sub program. It compares what the last button press was (set as a local constant) and what the current button press is (volatile ran in a sub program).

Really like how it works. Works great on my Leonardo Uno. On the Nano Every though I got this error:

binding reference of type 'const arduino::String&' to 'volatile arduino::String' discards qualifiers

Testing again on the Uno and it compiles just fine. Is there something I need to change? Currently I have it set up along the lines of:

Remote(); //sets volatile Button
if(Button!=Scene){
j=NUM_LEDS; //exit condition
}

Is there something I need to change to make this work on the Nano Every? I know I could make Remote() return a value and not have it volatile, but I would rather not have to go down that road as it will make my code rather ugly.

Cheers!

Please post your code.

If you have any volatile Strings in your code, that's almost certainly an error. Volatile integers are useful, but having a volatile String is meaningless: none of the member functions can (should) be used, only the size and the pointer to the data will be volatile, not the characters themselves, and in the context where volatile might be useful such as interrupt handlers, you shouldn't use functions that might allocate.

1 Like

Post full code and full compiler output please. In code tags.

Was hoping to not have to post code because it is very long, especially as I am new to coding and thus is not elegant.

I changed the "String" the a "byte" and it works! Thanks for the tip! Seems on my Uno using "String" is fine, but the Nano Every does no like that.

Could you explain "in the context where volatile might be useful such as interrupt handlers, you shouldn't use functions that might allocate"? Still a coding newbie, so not understanding.

It's a bug on the Uno as well, it's just that the Arduino developers decided to suppress some errors for the AVR boards. (This was a mistake of course, you want the compiler to notify you of possible errors, but it was impossible to revert without breaking a lot of code.)

Well one way to get better at coding is by having more experienced programmers review your work.

Strings dynamically allocate memory to store the array of characters. Usually, resizing or assigning a String eventually results in a call to the C library function malloc which allocates memory on the “heap”.

AFAIK, most implementations of malloc are non-reentrant, as malloc keeps a global list of free blocks of memory. If your main program calls malloc, and then an interrupt fires when you are right in the middle of it, you cannot call malloc again because the previous call (in the main program) has not finished yet. If you do call malloc from the interrupt handler, it will most likely corrupt the global state. On platforms with a thread-safe malloc implementation, it'll most likely just deadlock because there's no way to make any progress while your interrupt handler is waiting for the main code to finish, and the main code is waiting for the interrupt handler to finish.

Even if you have a reentrant implementation, malloc can be nondeterministic (you don't know how long it will take), which is not something you want in an interrupt handler in a real-time system, interrupt handlers should be kept as short as possible.

On the ESP32, which is a multicore microcontroller, they have this to say about it:

Heap functions are thread safe, meaning they can be called from different tasks simultaneously without any limitations.

It is technically possible to call malloc, free, and related functions from interrupt handler (ISR) context. However this is not recommended, as heap function calls may delay other interrupts. It is strongly recommended to refactor applications so that any buffers used by an ISR are pre-allocated outside of the ISR. Support for calling heap functions from ISRs may be removed in a future update.

The avr-libc malloc documentation doesn't mention reentrancy, but looking at the source code, it doesn't do anything to make reentrant calls possible.

Additionally, if ISRs aren't involved then 'volatile' variables are unnecessary (avoiding for now the issue of memory-mapped) processor registers / peripherals). In fact, 'volatile' will make non-ISR code slower because it forces the compiler to code actual memory operations for every access to the variable. So, it's not allowed to optimize things by storing variables in registers.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.