[SOLVED] Adapting MenuBackend library to use strings in PROGMEM

I started an arduino project about a year ago, being totally new to electronics and C programming. It's basically about controlling cameras, strobes and solenoids for photographing water drops. For those who understand German more information on the project can be found here. My source code (most variable names are in English but the comments are in German) can be downloaded in a .zip file.

I recently ran out of RAM when trying to add more features so now I am trying to move some strings to program memory. I understand that the PROGMEM directive can do just that, I also found a nice tutorial here. However, applying it to my program seems to exceed my programming skills.

Most of the strings in use are necessary for the LCD menu. I use the LiquidCrystal library to interface with the LCD and the MenuBackend library (found here) for the menus. What I am trying to do now is adapt the MenuBackend library to use PROGMEM strings. However, I have trouble understanding the code in the MenuBackend.h file and even more trouble making the necessary changes.

Thanks for any help as to how to go about it. Any other remarks about optimizing memory usage by the program are also most welcome.

There are two things you're going to have to tackle. First, all your string definitions are going to have to be changed to place them in program space:

  :
#define TEXT_FINISHED                          "fertig"
  :

becomes

#include <avr/pgmspace.h
  
  :
#define TEXT_FINISHED                          PSTR("fertig")
  :

Then, when you go to send the string to the LCD, your're going to have to have a jacket function to lcd.print() that understands the PROGMEM string.

void lcd_print_P(const PROGMEM char *s)
{
  uint8_t c;
  while ((c = pgm_read_byte_near(s++)) != 0)
     lcd.print(c);
}

And where you write to the LCD, thats going to use your spiffy new jacket function:

    clear_second_line();
    lcd.setCursor(0,1);
    lcd.print(TEXT_PICTURETAKEN);

becomes

    clear_second_line();
    lcd.setCursor(0,1);
    lcd_print_P(TEXT_PICTURETAKEN);

This is a rough approximation, but should give you a reasonable idea what needs doing.

Thanks. This should take care of everything that is not used with the MenuBackend library, which will still expect a regular string. I will start with that part, it should free quite a bit of memory already.
However, I still have no clue how to modify the MenuBackend library to accept PROGMEM strings.

Nevermind, the MenuBackend library will actually readily accept PROGMEM strings since it uses only pointers to strings. However, you need to define your strings using

const char this_is_my_string[] PROGMEM = "My string in flash memory";

as opposed to using the PSTR macro

PSTR("Another string in flash memory")

which will render an error when you try to compile your source code ("statement-expressions are not allowed outside functions nor in template-argument lists").

In case anybody else has encountered this problem I wrote some example code that works and should be easily adapted to your needs.

/* Example code for using PROGMEM strings with MenuBackend library
Will create the following menu structure with menu names in program memory:
FILE       TOOLS       HELP
Open       Settings    About
Save
*/

#include <MenuBackend.h>
#include <avr/pgmspace.h>

const char mtext_file[] PROGMEM = "FILE";
const char mtext_file_open[] PROGMEM = "Open";
const char mtext_file_save[] PROGMEM = "Save";
const char mtext_tools[] PROGMEM = "TOOLS";
const char mtext_tools_settings[] PROGMEM = "Settings";
const char mtext_help[] PROGMEM = "HELP";
const char mtext_help_about[] PROGMEM = "About";

MenuBackend menu = MenuBackend(MenuEventUse, MenuEventChange);
MenuItem mi_file = MenuItem(mtext_file);
MenuItem mi_file_open = MenuItem(mtext_file_open);
MenuItem mi_file_save = MenuItem(mtext_file_save);
MenuItem mi_tools = MenuItem(mtext_tools);
MenuItem mi_tools_settings = MenuItem(mtext_tools_settings);
MenuItem mi_help = MenuItem(mtext_help);
MenuItem mi_help_about = MenuItem(mtext_help_about);

void MenuEventUse(MenuUseEvent used) {
  // do whatever you want to do when your current menu item has been used
}

void MenuEventChange(MenuChangeEvent changed) {
  // do whatever you want to do when your current menu item has changed
}

void setup() {
  // create menu structure
  menu.getRoot().add(mi_file);
  mi_file.add(mi_file_open).add(mi_file_save);
  mi_file.addRight(mi_tools).add(mi_tools_settings);
  mi_tools.addRight(mi_help).add(mi_help_about);
}

void loop() {
  // add your program code here
}

Hope this helps.

Strangely enough I can not get this to work. I've already got a full menu setup using menubackend, and I also already have some data save into PROGMEM. Both are working no problem, but as soon as I try to move my menu label names to progmem I just get junk to both my LCD and Serial. I tried defining my name exactly as in the sample, but it appears I'm grabbing the wrong chunk of memory. I also tried defining the name using the arduino recommended prog_char data type (this is what I'm using for my other progmem data). Still no love, here's the pertinent code snippet that doesn't work, though the other menu stuff and other progmem data works. Is there something I'm missing?

should I be calling the progmem menu names using some special call like I do when I print my other variables. To print these labels I'm using the menu getfilename call, I've included it below. Finally, I know there are different versions of this menu floating around, so I wonder if we are on different ones. I'm using v1.4

prog_char paintDirOption0[] PROGMEM  = "L > R"; // this variable is printed correctly 
// prog_char mtext_imgSel[] PROGMEM = "imgSel"; //did not work

const char mtext_imgSel[] PROGMEM = "imgSel"; //also did not work


MenuBackend menu = MenuBackend(menuUseEvent,menuChangeEvent);
	//beneath is list of menu items needed to build the menu
	MenuItem imageSelect = MenuItem(mtext_imgSel);  // should be in progmem, but its junk.
	MenuItem fDelay = MenuItem("fDelay");
	MenuItem fRepeat = MenuItem("Repeat");
        MenuItem imgIncr = MenuItem("iIncr");

 Serial.print(menu.getCurrent().getName());  //how I print
      lcd.print(menu.getCurrent().getName()); // how I print, works for standard menu items

Yeah, there are quite a few versions floating around. I recently decided to make things easier for other users of my program by simply including the library I use with the program. You can download the zip-File and see if you find some useful information in there.

Download Link

Champro,

were you able to get it working? Care to share? I'm in the boat now and need to put my menu in progmem to free up sram. Please help.

Paul

I hate to open up a 'resolved' thread, however, it seems that this library isn't working correctly or that i'm missing something. I've written up a simply menu system and all that is getting returned through serial is jibberish. can anyone help? the library i'm using is the current CMmenus one: http://crazymachine.nicolai-korff.de/downloads/crazymachine_version0.4beta.zip

i've been trying for weeks to get progmem to work with this library and can't seem to get it, i'm about to give up! :cold_sweat:

#include "CMmenus.h"              
#include <avr/pgmspace.h>         


PROGMEM const char a[] = "a";
PROGMEM const char b[] = "b";
PROGMEM const char c[] = "c";
PROGMEM const char d[] = "d";


//this controls the menu backend and the event generation
MenuBackend menu = MenuBackend(menuUseEvent,menuChangeEvent);


MenuItem mi_a = MenuItem(a);
MenuItem mi_b = MenuItem(b);
MenuItem mi_c = MenuItem(c);
MenuItem mi_d = MenuItem(d);

//this function builds the menu and connects the correct items together
void menuSetup()
{
Serial.println("Setting up menu...");
menu.getRoot().add(mi_a);
mi_a.add(mi_b).add(mi_c).add(mi_d);
}


void menuUseEvent(MenuUseEvent used)
{

}

void menuChangeEvent(MenuChangeEvent changed)
{
    Serial.print("Menu change ");
    Serial.print(changed.from.getName());
    Serial.print(" ");
    Serial.println(changed.to.getName());
}

void setup()
{
    Serial.begin(9600);

    menuSetup();
    Serial.println("Starting navigation:\r\nUp: w   Down: s   Left: a   Right: d   Use: e");
}

void loop()
{
    if (Serial.available()) {
        byte read = Serial.read();
        switch (read) {
            case 'w': menu.moveUp(); break;
            case 's': menu.moveDown(); break;
            case 'd': menu.moveRight(); break;
            case 'a': menu.moveLeft(); break;
            case 'e': menu.use(); break;
        }
    }
}

I've written up a simply menu system and all that is getting returned through serial is jibberish.

Be nice to know what the gibberish is. If nothing prints correctly, then it seems likely that you have not selected the correct baud rate. If some stuff prints correctly, but other does not, that's a different story.

PaulS:
Be nice to know what the gibberish is. If nothing prints correctly, then it seems likely that you have not selected the correct baud rate. If some stuff prints correctly, but other does not, that's a different story.

It does print out some things, mostly square boxes, and unrecognizable symbols; not the character text I was expecting.

It does print out some things, mostly square boxes, and unrecognizable symbols; not the character text I was expecting.

Have you confirmed that the baud rate being used matches that of the Arduino?

PaulS:
Have you confirmed that the baud rate being used matches that of the Arduino?

I've only tried 9600 on my Uno, which is what the Serial window is also set at. Is there another baud setup I should try?

Does

void setup()
{
   Serial.begin(9600);
   Serial.println("This is some text");
}

void loop()
{
}

produce readable text?

PaulS:
Does

void setup()

{
   Serial.begin(9600);
   Serial.println("This is some text");
}

void loop()
{
}



produce readable text?

yes