Hello,
I'm writing a handling sketch for a DFRobot LCD KLeypad Shield, to be run on an Uno.
The sketch has quite a lot of text options to show on the LCD and I started getting compiler warnings along the lines of "memory low - stability issues may occur".
To try to fix this, I've moved all the text options (which were in a 15x4x2 char* array) into program memory using PROGMEM, like this:
PROGMEM const char str_00[] = "Sensors ";
PROGMEM const char str_01[] = "Actuators ";
PROGMEM const char str_02[] = "Control ";
PROGMEM const char str_10[] = "Position ";
PROGMEM const char str_11[] = "Light ";
PROGMEM const char str_12[] = "Temperature ";
PROGMEM const char str_13[] = "Range ";
PROGMEM const char str_20[] = "[Solenoid] ";
...etc...
PROGMEM const char str_D1[] = "[Count] ";
PROGMEM const char str_D2[] = "[Degrees] ";
PROGMEM const char str_D3[] = "[RPM] ";
PROGMEM const char str_E0[] = "[Open-Loop] ";
PROGMEM const char str_E1[] = "[On/Off] ";
PROGMEM const char str_E2[] = "[PID] ";
...plus some strings that aren't unique:
PROGMEM const char str_BL[] = " ";
PROGMEM const char str_RT[] = " >";
PROGMEM const char str_LT[] = "< ";
PROGMEM const char str_RL[] = "< >";
PROGMEM const char str_NA[] = " ";
...and then constructed an array to hold them all:
PROGMEM const char* const options[15][4][2] = {{{str_00,str_RT},{str_01,str_RT},{str_02,str_RT},{str_BL,str_NA}},
{{str_10,str_RL},{str_11,str_RL},{str_12,str_RL},{str_13,str_RL}},
{{str_20,str_LT},{str_21,str_LT},{str_22,str_LT},{str_BL,str_NA}},
{{str_30,str_RL},{str_31,str_RL},{str_BL,str_NA},{str_BL,str_NA}},
{{str_40,str_LT},{str_41,str_LT},{str_BL,str_NA},{str_BL,str_NA}},
{{str_50,str_LT},{str_51,str_LT},{str_52,str_LT},{str_53,str_LT}},
{{str_60,str_RL},{str_61,str_RL},{str_BL,str_NA},{str_BL,str_NA}},
{{str_70,str_RL},{str_71,str_RL},{str_72,str_RL},{str_BL,str_NA}},
{{str_80,str_LT},{str_81,str_LT},{str_82,str_LT},{str_BL,str_NA}},
{{str_90,str_LT},{str_91,str_LT},{str_BL,str_NA},{str_BL,str_NA}},
{{str_A0,str_LT},{str_A1,str_LT},{str_BL,str_NA},{str_BL,str_NA}},
{{str_B0,str_LT},{str_B1,str_LT},{str_B2,str_LT},{str_BL,str_NA}},
{{str_C0,str_LT},{str_C1,str_LT},{str_C2,str_LT},{str_C3,str_LT}},
{{str_D0,str_LT},{str_D1,str_LT},{str_D2,str_LT},{str_D3,str_LT}},
{{str_E0,str_LT},{str_E1,str_LT},{str_E2,str_LT},{str_BL,str_NA}}};
...which is all from the language reference here: PROGMEM - Arduino Reference
Reading stuff out of the array is now a bit of a faff, but it works like this:
lcd.setCursor(0,0);
strcpy_P(cLCDBuffer, (char*)pgm_read_word(&(options[iCurMenu][iCurOption][0])));
//lcd.print(options[iCurMenu][iCurOption][0]);
lcd.print(cLCDBuffer);
...where iCurMenu and iCurOption are indexes updated from the keypad.
Having navigated the menu, the user can then press a SELECT button, which does the following:
case btnSELECT: //SELECT button pressed
if(!keyPressed) {
switch(iMode){
case modeMENU:
//Use the pgm macro to get the vurrent option text grom program memory
strcpy_P(cLCDBuffer, (char*)pgm_read_word(&(options[iCurMenu][iCurOption][0])));
//if(options[iCurMenu][iCurOption][0][0] == '[') {
//check the first character of the current option string
if(cLCDBuffer[0] == '['){
breadcrumbs[iBCIndex][0] = iCurMenu; //Add the current menu to the breadcrumb array...
breadcrumbs[iBCIndex][1] = iCurOption; //...and the option selected
iBCIndex++; //Increment the breadcrumb array index
Serial.print("SELECT:");
for(int i=0;i<iBCIndex;i++) {
Serial.print(breadcrumbs[i][0]);
Serial.print(",");
Serial.print(breadcrumbs[i][1]);
Serial.print(":");
strcpy_P(cLCDBuffer, (char*)pgm_read_word(&options[breadcrumbs[i][0]] [breadcrumbs[i][1]] [0]));
Serial.print(cLCDBuffer);
}
...the menu and option selections are tracked in a breadcrumbs[10][2] array (10x [menu you were in][option you selected]), so here, just to check it's all worked OK, I'm looping through that array, picking out the text from the options[15][4][2] array and echoing to the serial monitor.
Now, here's the problem - this worked fine before I put the options array (and all my other arrays) in program memory. Whilst the menu system itself works (the LCD displays as expected and the keypad responds as expected), this reporting to the serial monitor resets the Uno.
It gets as far echoing:
SE⸮
...where the weird character is a square shape on the LCD and either jumps back to the start of the menu routine or completely resets the Uno.
What I think might be happening is whatever buffer the serial.print function uses (which I read somewhere is in dynamic RAM) is somehow colliding with other stuff in RAM that shouldn't be there, but is, because I've used PROGMEM to move it about...or something. To be honest, I haven't got a clue why it's doing this, but it wasn't before I started using PROGMEM...
I've attached my entire code, but it's quite long to go over line by line here - I can try to explain what I was hoping it would do if anybody has the time to have a look at it for me...
Sorry this is a "my code's broken, can somebody please fix it for me" request, but I've been bashing away at this for days and just going round in circles
Thanks for any help at all!
LCD_Keypad_Shield_Menu_02.ino (28.9 KB)