Ardiuno rebooting when using PROGMEM

Hi All,

I have the following code example that I am trying to get running.

const char string_1[] PROGMEM = "String 1";
const char string_2[] PROGMEM = "String 2";
const char string_3[] PROGMEM = "String 3";
const char string_4[] PROGMEM = "String 4";
const char string_5[] PROGMEM = "String 5";

PGM_P string_table[] = 
{
   string_1,
   string_2,
   string_3,
   string_4,
   string_5
};

void setup(){
  Serial.begin(19200);
  Serial.println(F("Hello World!"));
  delay(100);
  Serial.print(F("In Setup: "));
  Serial.println(foo(1));
}
void loop(){}

char* foo(int i)
{
    char buffer[10];
    strcpy_P(buffer, (PGM_P)pgm_read_word(&(string_table[i])));
    // Display buffer on LCD.
    Serial.print(F("In Function: "));
    Serial.println(buffer);
    return buffer;
}

Ouput on Serial:
I ª±±½�World!
I ª±±½�World!
I ª±±½�World!
InHello World!
I ª±±½�World!
I ª±±½�World!
I ª±±½�World!
I ª±±½�World!
I ª±±½�World!

The Ardiuno (UNO R3) unit seems to be rebooting when calling the foo() function.

I would like to use the foo() function to be able to get the data that I need and pass it to different exit points later down the line. eg. Ethernet, wireless or in this case serial.

I have been trying to follow a few tutorials on memory optimization such as these:

lesson 4a Ethernet WEb client intro - YouTube (quite old)

Ultimately I am trying to refactor the code from the youtube video but with my own uses.

Can someone please explain why the device is rebooting and or a better way of accessing this data and then passing it into different functions.

You are mixing a few things and forgot a few things.
Please stick to these tutorials:
Gammon Forum : Electronics : Microprocessors : Putting constant data into program memory (PROGMEM) (good)
PROGMEM - Arduino Reference (outdated)

The buffer[] in the function "foo" is created on the stack. When the function ends, the buffer[] is out of scope and no longer valid. You can make it a global variable or make it 'static', then it has a fixed location in ram.

Could you make changes. It should be used like this:

const char * const string_table[] PROGMEM =
{
  ...


char* foo(int i)
{
    static char buffer[40];
    strcpy_P(buffer, (char *)pgm_read_word(&(string_table[i])));

Perhaps the (PGM_P) is okay for the strcpy_P, I'm not sure about that one.

Hi Guys,

Thanks for the replies.

I have managed to get something working now.
Once I changed the table to const char * const it stopped crashing.

I have expanded the code a bit to give more of a view to what I wanted.
I do still need to work on rejecting strings that are longer than the buffer. This causes another crash if it is too bit :slight_smile:

Comments on the current code are welcome.

char buffer[10];
enum {string1,string2,string3,string4,string5};

const char string_1[] PROGMEM = "String 1";
const char string_2[] PROGMEM = "String 2";
const char string_3[] PROGMEM = "String 3";
const char string_4[] PROGMEM = "String 4";
const char string_5[] PROGMEM = "String 5";
//const char string_5[] PROGMEM = "This is a extra long string to break stuff and see what happens";

const char * const string_table[] PROGMEM = 
{
   string_1,
   string_2,
   string_3,
   string_4,
   string_5
};

void setup(){
  Serial.begin(115200);
  Serial.println(F("Hello World!"));
  if (foo(string1)) Serial.print(F("In Setup: ")); Serial.println(buffer);
  if (foo(string2)) Serial.print(F("In Setup: ")); Serial.println(buffer);
}
void loop(){
  Serial.println("tick");
  delay(3000);

  for (int i = 0; i < 5; i++)
  {
    if (foo(i)) Serial.print(F("In loop. | ")); Serial.println(buffer);
  }
}

bool foo(int i)
{
    // Display buffer on LCD.
    Serial.print(F("In Function. | "));
    char * ptr = (char *)pgm_read_word(&string_table [i]);
    strcpy_P(buffer, ptr );
    Serial.print("buffer = ");
    Serial.println(buffer);
    return true;
}

You use the PROGMEM with a string table as it should be.

The 'enum' is almost the same as using an index. It might also be confusing that "string1" is an index and "string_1" is a string in flash memory.
Do you want the 'enum' to have more explaining names for the strings ? Because at the moment is the only an extra name for an index.

Please use indents, spaces, brackets and everything in the same way. Don't try to put everything on the same line.
This is not okay:
if (foo(string1)) Serial.print(F("In Setup: ")); Serial.println(buffer);The first Serial.print is within the if-statement, but the second Serial.println is outside the if-statement.

Everyone has his/her own style to write code. I would do it like this:

enum {stringIndex0, stringIndex1, stringIndex2, stringIndex3, stringIndexApples};

const char string_0[] PROGMEM = "String 1";
const char string_1[] PROGMEM = "String 2";
const char string_2[] PROGMEM = "String 3";
const char string_3[] PROGMEM = "String 4";
const char string_4[] PROGMEM = "Apples  ";

const char * const string_table[] PROGMEM =
{
  string_0,          // stringIndex0
  string_1,          // stringIndex1
  string_2,          // stringIndex2
  string_3,          // stringIndex3
  string_4,          // stringIndexApples
};

char buffer[20];

void setup() 
{
  Serial.begin(115200);
  
  while (!Serial);          // Added for Leonardo
  
  Serial.println(F("Hello World!"));
  
  if (foo(stringIndex0))
  {
    Serial.print(F("In Setup: ")); 
    Serial.println(buffer);
  }
    
  if (foo(stringIndex1)) 
  {
    Serial.print(F("In Setup: ")); 
    Serial.println(buffer);
  }
}

void loop() 
{
  Serial.println("tick");
  delay(3000);

  for (int i = 0; i < 5; i++)
  {
    if (foo(i)) 
    {
      Serial.print(F("In loop. | ")); 
      Serial.println(buffer);
    }
  }
}

bool foo(int i)
{
  // Display buffer on LCD.
  Serial.print(F("In Function. | "));

  // The next line retrieves the pointer to the string in flash.
  // This is also allowed: const char * ptr = (const char *)pgm_read_word(&string_table[i]);
  char * ptr = (char *)pgm_read_word(&string_table[i]);
  strcpy_P(buffer, ptr);
  
  Serial.print("buffer = ");
  Serial.println(buffer);

  return true;
}

An array starts at zero, so it is easier to name the first string with a zero. Both links in my Reply #1 use names that start with a zero.

What is it for ? For a menu ?

@Koepel, Thanks for the input.
I was not aware that the if statement was only until the first ;. I assumed that it was till the end of the line aka \n.
My cheeky lazy coding has failed me on that one.

The idea here was to refactor the code from the original youtube video which no longer works with the newer versions of the libraries. Here is the github to the original code.

The enum idea was taken from the above and allows for the use of a case statement when looking at incoming commands.

What I want out of it is basically a API that I can interrogate for states/readings and also to control things like pumps, lights and fans.
This hopefully will then evolve into the controllers for an automated poly tunnel.

If all goes well hopefully also a decent instructables post :slight_smile:

I see, then the 'enum' makes sense. It makes it indeed easier to read the code.

However, the code on that Github page is only half-way finished.
I noticed this:

char* getStringfromProgMem(const char* Table[], int i)
{
char buffer[progMemBuffer];
strcpy_P(buffer, (char*)pgm_read_word(&(Table[i])));
return buffer;
};

Don't copy that bad code into your sketch. The (temporary) buffer is created on the stack, and after the function returns the buffer is out of scope and may be overwritten. But the pointer to the (temporary) buffer is still used outside the function. That is bad programming.
A semicolon after a function is not needed.

Also don't use something like this:

if(IsValid) analogWrite(pin, value); else Serial.println(getStringfromProgMem(Txt_table,invalidValue));

It makes it hard to read when everything is in a single line.

And the brackets are not needed here:

   default :             {
                           Serial.print(getStringfromProgMem(Txt_table,errorText));
                           Serial.println(commandBuffer);
                           break;
                         }