[SOLVED] Accessing PROGMEM string array using index variable does not work

Hi,

I’m pretty new to Arduino and even newer to C++ so please forgive a possible stupid question regarding strings stored in PROGMEM.

Forgive the poor programming but the problem I have is the ‘scrollMessage(string_table[0]);’ located in setup works fine but the ‘scrollMessage(string_table);’ version in loop that uses a variable to specify the string fails. The code only need to access a string one character at a time and index into a 10x7 font matrix to put scrolling text on a LED matrix.

//We always have to include the libraries
#include <avr/pgmspace.h>

const int scrollDelay = 50;             // Text Scroll speed delay (lower is faster)

// Scrolling Message Strings
char scrollText00[] PROGMEM = {
    " Sample String 0 \0"};
char scrollText01[] PROGMEM = {
    " Sample String 1 \0"};
char scrollText02[] PROGMEM = {
    " Sample String 2 \0"};
char scrollText03[] PROGMEM = {
    " Sample String 3 \0"};
char scrollText04[] PROGMEM = {
    " Sample String 4 \0"};
char scrollText05[] PROGMEM = {
    " Sample String 5 \0"};
char scrollText06[] PROGMEM = {
    " Sample String 6 \0"};
char scrollText07[] PROGMEM = {
    " Sample String 7 \0"};
char scrollText08[] PROGMEM = {
    " Sample String 8 \0"};
char scrollText09[] PROGMEM = {
    " Sample String 9 \0"};
//Scrolling Message String Pointer Table (Needed to access above Scrolling Message Strings in PROGMEM)
char *string_table[] PROGMEM ={
    scrollText00,
    scrollText01,
    scrollText02,
    scrollText03,
    scrollText04,
    scrollText05,
    scrollText06,
    scrollText07,
    scrollText08,
    scrollText09,
};

void setup(){
    Serial.begin(9600);
    scrollMessage(string_table[0]);
}

void loop(){ 
    long loopTime = millis();                           // Read timer
    int mainDelay = 4000;                               // Default main loop delay
    for (int x=0;x<9;x++){
        while ((millis() - loopTime) < mainDelay){
        }
        // *************************************************
        // *************************************************
        // *************************************************
Serial.print ("X=");
Serial.println (x);
        scrollMessage(string_table[x]);
        // *************************************************
        // *************************************************
        // *************************************************
    }
}

// Scroll Message
void scrollMessage(char * messageString) {
    int counter = 0;
    int myChar =  0;
    do {
        // read back a char 
        myChar =  pgm_read_byte_near(messageString + counter); 
        Serial.println(myChar,HEX);
        delay(scrollDelay);
        counter++;
    } while (myChar !=0);
    Serial.println();
}

Riva

string_table is in PROGMEM. When you index into it as if it were a regular array you are fetching from the wrong memory. I think you will need to use pgm_read_word(). See the string table example in http://arduino.cc/en/Reference/PROGMEM . particularly the (char*)pgm_read_word(&(string_table*))*

Hi johnwasser,

I had originally based the code on the info from the link you supplied and tried to fudged it to suit my needs. The example they gave also used PROGMEM for the string_table so I just copied it but I'm no C++ programmer so have missed something.

My problem is the code works fine passing an absolute value but not passing a variable This works... 'scrollMessage(string_table[ 5 ]);' This does not work... 'scrollMessage(string_table[ x ]);'

I can fudge around the problem by using switch/case on the x value and making it a absolute call but that seems madness. switch (x){ case 0: scrollMessage(string_table[0]); break; case 1: scrollMessage(string_table[1]); break; case 2: scrollMessage(string_table[2]); break; case 3: scrollMessage(string_table[3]); break; case 4: scrollMessage(string_table[4]); break; case 5: scrollMessage(string_table[5]); break; case 6: scrollMessage(string_table[6]); break; case 7: scrollMessage(string_table[7]); break; case 8: scrollMessage(string_table[8]); break; case 9: scrollMessage(string_table[9]); break; }

I'm not sure, but I suspect the problem is that you're declaring char *string_table[] PROGMEM which means your array-of-pointers is in PROGMEM, but you are accessing it as if it was a normal variable in RAM.

Try removing the PROGMEM declaration for the array; I suspect your code will then work.

If you really want the array itself to be in PROGMEM I think you would need to read the pointer values from it using pgm_read_....

This may help... http://arduiniana.org/libraries/flash/

I don’t know why “string_table[5]” works. If the table was declared ‘const’ then I would guess that the compiler recognized string_table[5] as a compile-time constant and just used the value rather then generating code to fetch from the array.

To use the array in PROGMEM use (char*)pgm_read_word(&(string_table[x])) instead of string_table[x].

Thanks for all the help regarding this problem.

In the end I did as suggested and used (char*)pgm_read_word(&(string_table[x])) though removing PROGMEM from the string_table so it resided in RAM also worked. Now I can get on and hopefully finish my word clock.

As long as you have enough RAM it's fine - and even faster :)

Just a little hint: originally, both the strings and the array of pointers were in PROGMEM. However, a text in PROGMEM is not of type char*, but of type prog_char * This difference allows print() to detect that it has to get the data from the right location in flash memory.

Thank you to Coding Badly who linked me to the following...

http://arduiniana.org/libraries/flash/

that has totally saved the day as I ran out of ram and need to push my string arrays into FLASH !

And thank you to Mikal Hart for developing the library and graciously publishing it with a liberal license.

[quote author=Coding Badly link=msg=1971459 date=1416627585] And thank you to Mikal Hart for developing the library and graciously publishing it with a liberal license. [/quote]

Absolutely !!! - thank you Mikal :)

im using a completely inelegant but effective and simple way to store an array of strings in progmem :

String NameTransfo ; // global String

void GetNameTransformation ( int number ) {

switch (number) {
 case 0 : NameTransfo =   F("000 zéro") ; break ;
 case 1 : NameTransfo =  F("001 Continuous Random Melody"); break ;
 case 2 : NameTransfo =  F("002 Hammer"); break ;
 case 3 : NameTransfo =  F("003 Hammer Funk"); break ;
 case 4 : NameTransfo =  F("004 Transpose"); break ;
 case 5 : NameTransfo =  F("005 Delete Layer"); break ;
 case 6 : NameTransfo =  F("006 Create 60"); break ;
 case 7 : NameTransfo =  F("007 Copy+CH"); break ;
 case 8 : NameTransfo =  F("008 EquaLgth"); break ;
 case 9 : NameTransfo =  F("009 Deplace"); break ;
case 10 : NameTransfo =    F("010 Decimate"); break ;
 case 11 : NameTransfo =  F("011 multiply"); break ;
 case 12 : NameTransfo =  F("012 MELtiply"); break ;
 case 13 : NameTransfo =  F("013 CH Change"); break ;
 case 14 : NameTransfo =  F("014 CH Info"); break ;
 case 15 : NameTransfo =  F("015 MkrPoly"); break ;
 case 16 : NameTransfo =  F("016 Populate"); break ;
 case 17 : NameTransfo =  F("017 Flip Loc"); break ;
 case 18 : NameTransfo =  F("018 GoldRatio"); break ;
 case 19 : NameTransfo =  F("019 Sketch"); break ;
 case 20 : NameTransfo =  F("020 (un)Mute"); break ;
 case 21 : NameTransfo =  F("021 Reverse"); break ;
 case 22 : NameTransfo =  F("022 PFunk"); break ;
 case 23 : NameTransfo =  F("023 AutoFX"); break ;
 case 24 : NameTransfo =  F("024 RECORD"); break ;
default : NameTransfo = "none" ; 
 } ;

}

When i need to print the content of Transfo[10], i'll do this :

GetNameTransformation(10);
lcd.print(NameTransfo);