Pip-boy 32,000 (Nearly finished)

In hindsight, I think maybe the type alias stuff and the const part wasn’t strictly necessary, and I probably should have included reinterpret_cast in the list, but in fairness I was rushing at the time.

The F macro trick combines several techniques, which is why it’s a bit awkward to explain it in simple terms without using too much ‘jargon’.

I think the best place to start is why the F macro exists.

Normally, if you use a bare string literal in Arduino code, the string is actually located in RAM, and what effectively happens is that the string will be stored in progmem and then copied into RAM before it’s used, which is typically inefficient.

Fortunately it’s possible to create a macro that places a string literal in progmem and suppresses the creation of the RAM string, which is more efficient. avr-libc, the library that underpins AVR implementations of the Arduino library, provides this ability as a macro named PSTR, which the F macro uses internally to store a string literal in progmem. However, putting a string literal in progmem is only half the problem.

The other half of the problem is that normally there’s no way to differentiate between a pointer that points to RAM and a pointer that points to progemem, both are represented as a normal pointer. This is a problem because different CPU instructions are required for reading from progmem.

In most cases you can get away by having separate functions for reading from each, but for the printing mechanism used in Arduino, the authors wanted a way to be able to use print for both RAM strings and progmem strings.

The solution they chose was to create a ‘dummy’ type, class __FlashStringHelper;, which is declared but never defined, which means it’s possible to create pointers to such a type, but those pointers cannot be dereferenced. Pointers to __FlashStringHelper are then used to signify a string that’s stored in progmem, which in turn allows the same function name to be used to handle both a string in RAM (const char *) and a string in progmem (const __FlashStringHelper *).

Due to the fact the __FlashStringHelper type doesn’t occur ‘naturally’, there has to be an extra conversion (a cast) inserted somewhere so what would normally be a const char * can be interpreted as a const __FlashStringHelper *. And that’s the other thing that the F macro does - performing that type conversion.

With that all said, the following two lines in WString.h should now more or less make sense:

And if you look over at Print.h, you will see the progmem-accepting const __FlashStringHelper * overload of print and the RAM-accepting const char [] overload of print:

Does that all make sense?


Anyway, to cut a long story short, assuming Keyboard inherits Print, you should be able to redefine your CommandAtRun functions to take const __FlashStringHelper * arguments, and then they’ll accept strings wrapped in the F macro.

1 Like

Thanks! This is a pretty straightforward explanation, I appreciate it. I’ll make a few changes to see if I can get those functions able to take arguments from __FlashStringHelper.

1 Like