Lookup tables in a class - static const PROGMEM

I am trying to put a lookup table in a class, and I "want it all":

  • The same lookup table can be used by all functions in the class
  • It's declared static, so even if I have 100 instances of the class, it doesn't take up any more space
  • It's in PROGMEM, as the target is an Arduino Uno with limited RAM

Here is a very cut-down version of my code. It works properly if I leave out "PROGMEM", but when I put it in, I get blanks instead of the string. I tried some alternatives - see below - and got very strange results.

// ----------------------------------------
// Multiple_Instances.ino
// ----------------------------------------

#include "MyStuff.h"

void setup()
{
    Serial.begin(115200);
}

const unsigned char elements = 100;
MyStuff ms[elements];
unsigned long lastPrint;

void loop()
{
    if ((millis() - lastPrint) > 5000)
    {
        for (unsigned char i=0;i<elements;i++)
            ms[i].printIt();

        lastPrint = millis();
    }
}

// ----------------------------------------
// MyStuff.h
// ----------------------------------------

#ifndef MyStuff_h
#define MyStuff_h

#include "Arduino.h" 

class MyStuff
{
    public:
        MyStuff();
        void printIt();

    private:
        static const char base64[] PROGMEM;
};

#endif

// ----------------------------------------
// MyStuff.cpp
// ----------------------------------------

#include "MyStuff.h"

static const char MyStuff::base64[] PROGMEM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

MyStuff::MyStuff() { }

void MyStuff::printIt()
{
    Serial.print(">");

    Serial.println(base64); // all blanks

    // Serial.println(base64[3]); // this works, 'D'

    // This produces random garbage
    /*
    for (unsigned char i=0; i<50; i++)
        Serial.print(base64[i]);
    Serial.println("");
    */    
}

You could try Serial.println((__FlashStringHelper*)base64);

Seems you don't know much about using PROGMEM, study the following:

https://www.nongnu.org/avr-libc/user-manual/pgmspace.html

Hi Whandall,

Thanks for the advice and the page - it was very helpful. It would be great if a link to that page was in the documentation here.

For anyone else who happens upon this post, this line of code worked for me:

    Serial.println((__FlashStringHelper*)base64); // suggested by Whandall on the Arduino forum. THIS WORKS!
                                                  // Also see https://www.nongnu.org/avr-libc/user-manual/pgmspace.html

Or for character access, as suggested on the page Whandall provided, this worked for me as well:

    char c;
    
    for (unsigned char i=0; i<64; i++)
    {
        c = (char)pgm_read_byte(&(base64[i])); // From https://www.nongnu.org/avr-libc/user-manual/pgmspace.html,
        Serial.print(c);                       // then I added the cast to (char). THIS ALSO WORKS!
    }
    Serial.println("");

This code was needed for the Arduino Uno ATmega328, but as it is for a library, I wanted the same code to work on the Seeed Studio Xiao SAMD21, Adafruit Feather M4 Express, and the PJRC Teensy 4.1. Suprisingly, it worked the same on all of them, with no compiler preprocessor directives needed.

I am a little skeptical of "pgm_read_byte" working on larger programs, as it is a 16-bit near address in program space. It's working fine now on a small test program, but in larger programs, far might become necessary. So I will use compiler preprocessor directives around everything involving PROGMEM, to avoid the issue entirely.

Have a look at

https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#ga8ddf0e398bde8078aa9395ac77c83f0a

This gets a far pointer, which can be used in all the _FP routines.

Maybe these platforms don't use Harvard architecture,
and can access flash with normal instructions.
Then the pgm_read could be mapped to simple accesses.

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