Thread: embedded menu system

  1. #1
    Registered User
    Join Date
    Feb 2008
    Posts
    13

    embedded menu system

    Hello All,

    New to this board but not new to C programming. As a lone wolf engineer you come across what should be a simple issue but you don't have another engineer in your office so you have no one to bounce ideas off of and you get a little stuck.

    Well I have just that type of issue right now. I am working on an embedded system using C as the programming language and I need to do something I have done in the past but a very distant past and I can't quite recall how I did it. So I thought I would do some research and see if I could find a good way to implement a menu system.

    My project has an LCD and I need to implement some setup functions. The main menu would be something like:

    Main Menu
    1. Item1 4. Item4
    2. Item2 5. Item5
    3. Item3 6. Item6

    Then you choose item #2 so go to submenu #2

    Item2 Menu
    1. Item2.a 4. Item2.d
    2. Item2.b 5. Item2.e
    3. Item2.c 6. Item2.f

    Then select an item from this menu and act accordingly.

    What I am looking for a little help on the best way to store the menu's. Individual character strings? An array of strings? A structure?

    Once I figure out how I want to store the strings I can figure out how to manipulate them.

    I am not looking for someone to do all the work for me but if you have any code snippets you would like to share that would be great.

    Thanks and I look forward to any tips you can provide.

    Dave

  2. #2
    Captain - Lover of the C
    Join Date
    May 2005
    Posts
    341
    When you pick an item from the main menu, does it always go to a sub-menu which always goes to an item? Or does it sometimes go from the Main Menu straight to an item or sometimes go from a sub-menu to another sub-menu? In other words, how rigid is your menu heirarchy?
    Don't quote me on that... ...seriously

  3. #3
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    A menu might have a title, and some number of submenus. So a tree like structure suggests itself:

    Code:
    #define MAX_TITLE 32
    #define MAX_SUBMENUS 16
    
    struct menu
    {
        char title[MAX_TITLE];
        void (*command)();
        short num_submenus;
        struct menu *submenu[MAX_SUBMENUS];
    };
    Then you might write a single function to display a given menu by displaying the titles of all of its submenus.

    A submenu with num_submenus == 0 would indicate a leaf command, so selecting this menu would cause your program to call the command function through the menu->command callback.

    You would have to declare all the menus and submenus statically, and build the pointer structure either statically or at initialization time.

  4. #4
    uint64_t...think positive xuftugulus's Avatar
    Join Date
    Feb 2008
    Location
    Pacem
    Posts
    355
    How about:
    Code:
    typedef struct
    {
        char *option;
        char *submenu[];
    } Menu;
    
    Menu menu[] = 
    {
        {"Item1", {"a", "b", "c"}},
        {"Item2", {"z", "x"}},
        ...
    };
    But this is just for the names. I think brewbuck is more general purpose.
    Code:
    ...
        goto johny_walker_red_label;
    johny_walker_blue_label: exit(-149$);
    johny_walker_red_label : exit( -22$);
    A typical example of ...cheap programming practices.

  5. #5
    Registered User
    Join Date
    Feb 2008
    Posts
    13
    Quote Originally Posted by brewbuck View Post
    A menu might have a title, and some number of submenus. So a tree like structure suggests itself:

    Code:
    #define MAX_TITLE 32
    #define MAX_SUBMENUS 16
    
    struct menu
    {
        char title[MAX_TITLE];
        void (*command)();
        short num_submenus;
        struct menu *submenu[MAX_SUBMENUS];
    };
    Then you might write a single function to display a given menu by displaying the titles of all of its submenus.

    A submenu with num_submenus == 0 would indicate a leaf command, so selecting this menu would cause your program to call the command function through the menu->command callback.

    You would have to declare all the menus and submenus statically, and build the pointer structure either statically or at initialization time.
    OK, this is what I was looking for. But I am not quite sure I see how the menu->command callback would work?

    Most of my programming has been in the embedded world where the only feedback is a single LED or sometimes a COM port but even then it's just a few Bytes. So I have not had to code a structure that has a pointer to a function since school days.

    Thanks,
    Dave

  6. #6
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by v_dave View Post
    But I am not quite sure I see how the menu->command callback would work?
    Assuming the callback functions do not require parameters (and for something like this, they shouldn't), you would call it like:

    Code:
    menu->command();
    Depending on your situation, you would want to return to the root menu after the command was done. i'd write a simple recursive function to descent and display the menu structure.

  7. #7
    Registered User
    Join Date
    Feb 2008
    Posts
    13
    Quote Originally Posted by brewbuck View Post
    Assuming the callback functions do not require parameters (and for something like this, they shouldn't), you would call it like:

    Code:
    menu->command();
    Depending on your situation, you would want to return to the root menu after the command was done. i'd write a simple recursive function to descent and display the menu structure.
    OK, I see the call function. So if do this correctly my structure setup would be
    Code:
    struct menu
    {
        char title[MAX_TITLE];
        void (*command)();
        short num_submenus;
        struct menu *submenu[MAX_SUBMENUS];
    };
    
    menu main_menu  
        {
           "Main Menu",
           display_next_menu(),
           6,
           next_menu
        };
    I guess this is where I am having the tough time is actually implementing the structure.

    thanks.

  8. #8
    Registered User
    Join Date
    Feb 2008
    Posts
    13
    OK so I have spent a couple of days working on what is the best solution for my application and I have come up with an approach that should work but for some reason it is not and I am hooping someone can point me to the error of my ways.

    Code:
    static char Main_Menu [7][11] = 
        {
                {"Main Menu" },
                {"1.Motor   " },
                {"2.Comm    " },
                {"3.Valve   " },
                {"4.Movement" },
                {"5.Calib   " },
                {"6.Info    " }
        };
    
    static char Motor_Menu [4][11] = 
        {
                {"Motor Menu" },
                {"1.G-Ratio " },
                {"2.Encoder " },
                {"3.Step    " }
        };
    
    typedef struct MENU_CNTRL
        {
            enum LCD_SETTINGS settings; // When in settings mode defines which screen
            char idx;                   // index into the menu array
            char **cur_menu;            // Current Menu
        }menu_cntrl;
    
    menu_cntrl Menus;
    
    Then later in my code...
    
    Menus.cur_menu = &Main_Menu; // The current menu we are displaying
    
    // My output routine - specifically writes to an LCD
    gputs(&Menus.cur_menu[0]); // Display the Screen Title
    
    // Now I want to display the next string in the array
    gputs(&Menus.cur_menu[1]);  // Obviously this will display "ain Menu" which is not what I want
    I have done some searches through this forum and there is some great info but I have not found an answer that addresses my specific issue.

    The questions is how do I access the next string in the array?

    Thanks,
    V_dave

  9. #9
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by v_dave View Post
    Code:
    menu main_menu  
        {
           "Main Menu",
           display_next_menu(),
           6,
           next_menu
        };
    Kinda. Suppose your main menu had three submenus with names Sub1, Sub2, and Sub3. These submenus are commands implemented by the functions sub1_command(), sub2_command(), and sub3_command(). You declare the submenus first:

    Code:
    menu sub1 = { "Submenu 1", sub1_command, 0 };
    menu sub2 = { "Submenu 2", sub2_command, 0 };
    menu sub3 = { "Submenu 3", sub3_command, 0 };
    Then you build the main menu and stick these submenus into it:

    Code:
    menu main_menu = { "Main Menu", NULL, 3, { &sub1, &sub2, &sub3 } };
    Then you need a function to allow the user to navigate the menu:

    Code:
    void navigate_menu(menu *mnu)
    {
        if(mnu->num_submenus == 0)
        {
            /* Execute the command */
            mnu->command();
            return;
        }
        /* This is a menu, not a command, so show the menu and get the user's choice */
        display_menu(mnu);
        mnu = get_user_selection(mnu);
        /* Drill down to the selected menu. */
        navigate_menu(mnu);         
    }
    And it's all wrapped in a loop:

    Code:
    while(!quit)
    {
        navigate_menu(&main_menu);
    }

  10. #10
    Registered User
    Join Date
    Feb 2008
    Posts
    13
    So I see how your example works but I have a question on handling the final submenu. For example:

    Code:
    // This is the main Menu for the settings
    static const char Main_Menu [7][11] = 
        {
                {"Main Menu" },
                {"1.Motor   " },
                {"2.Comm    " },
                {"3.Valve   " },
                {"4.Movement" },
                {"5.Calib   " },
                {"6.Info    " }
        };
    
    static const char Motor_Menu [4][11] = 
        {
                {"Motor Menu" },
                {"1.G-Ratio " },
                {"2.Encoder " },
                {"3.Step    " }
        };
    
    static const char GRatio_Menu [6][11] = 
        {
                {"Gear Ratio" },
                {"1.3.14    " },
                {"2.4.75    " },
                {"3.9.85    " },
                {"4.14.91   " },
                {"5.22.25   " }
        };
    Given the above menu strings I would like to accomplish the following:

    1) User enters the main settings menu and then selects option #1 Motor settings
    2) User then selects #1 Gear Ratio
    3) inside the Gear Ration settings menu the user selects #2 4.75

    So with that in mind I would need the following code:
    Code:
    menu GR_1 = 
        {
            "1. 3.14",
            Set_GR_3_14,
            3,
            0
        };
    
    menu GR_2 = 
        {
            "2. 4.75",
            Set_GR_4_75,
            3,
            0
        };
    
    etc...
    Then I would need the following functions:

    Code:
    void Set_GR_3_14(void)
    {
         GR = 314; //no floating point stuff in my little system
    }
    
    void Set_GR_4_75(void)
    {
         GR = 475; //no floating point stuff in my little system
    }
    
    etc...
    Now maybe I should have mentioned this sooner but my target is an 8-bit microcontroller so I don't have a lot of code space. This approach seems a little code space intensive.

    What I am trying to do is have a generic function that I can simply pass an index value to so that it takes less code.

    Code:
    void Set_GR(int idx)
    {
         int options = {314, 475, etc...};
    
         GR = options[idx];
    }
    Unless I have completely missed the point I am not sure your suggested code is going to fit in my code space.

    Any other ideas?

    Thanks,
    v_dave

  11. #11
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by v_dave View Post
    Unless I have completely missed the point I am not sure your suggested code is going to fit in my code space.

    Any other ideas?

    Thanks,
    v_dave
    Okay, we could alter the callback prototype to allow it to have an argument, and add a "data" member to the menu structure:

    Code:
    struct menu
    {
        char title[MAX_TITLE];
        void (*command)(short);
        short data;
        short num_submenus;
        struct menu *submenu[MAX_SUBMENUS];
    };
    When the menu navigation function invokes the callback, it would pass the data member to it:

    Code:
    mnu->callback(mnu->data);
    The problem with this approach is that each menu item pays the price of carrying this data element, even for menu items which don't require one. An even more intensive solution would be to not pass the argument, but instead, pass a pointer to the menu itself, and have the GR callback look inside the menu title to extract the value (since it is already there, in string form). But will that string parsing code take up so much space that the advantage is negated? I think you'll have to try it each way to find out.

  12. #12
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Yes, your "set_GR" function seems more compact code-size wise [although the only way to know for sure is to measure it].

    And:
    Code:
         int options = {314, 475, etc...};
    Perhaps you want that to be:
    Code:
         static const int options[] = {314, 475, etc...};
    The green stuff is to make sure that the compiler doesn't initialize the variable each time you call the function, which would take extra space in both code and data areas.
    The red bit I think is just a typo.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  13. #13
    Registered User
    Join Date
    Feb 2008
    Posts
    13
    Quote Originally Posted by brewbuck View Post
    Okay, we could alter the callback prototype to allow it to have an argument, and add a "data" member to the menu structure:

    Code:
    struct menu
    {
        char title[MAX_TITLE];
        void (*command)(short);
        short data;
        short num_submenus;
        struct menu *submenu[MAX_SUBMENUS];
    };
    When the menu navigation function invokes the callback, it would pass the data member to it:

    Code:
    mnu->callback(mnu->data);
    The problem with this approach is that each menu item pays the price of carrying this data element, even for menu items which don't require one. An even more intensive solution would be to not pass the argument, but instead, pass a pointer to the menu itself, and have the GR callback look inside the menu title to extract the value (since it is already there, in string form). But will that string parsing code take up so much space that the advantage is negated? I think you'll have to try it each way to find out.
    Actually each string starts with a unique line number i.e "1. 3.14", "2. 4.75", etc... All I have to do is look at the first char in the string and I will know which option was chosen. Not necessarily string parsing just a peek at the first char.

    Code:
    menu GR_2 = 
        {
            "2. 4.75",
            Set_GR,
            3,
            0
        };
    
    void Set_GR(char *setting)
    {
         switch(setting[0])
           {
               case '1':GR = 314;
                           break;
                etc...
            }
    }
    This would save some codespace because you would not have all the function calls and the overhead involved with each call.

    Thanks for the input. I will give this a shot and should know in the next day or so...

  14. #14
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by v_dave View Post
    Code:
    menu GR_2 = 
        {
            "2. 4.75",
            Set_GR,
            3,
            0
        };
    I'm just noticing that the num_submenus element here is 3. It should be 0.

    Good luck -- let us know.

    EDIT: Also, looking at just the first character and using that to index the values is great, and a perfectly embedded sort of thing to do But you may find that the total code+data size is actually smaller by including the string parser, since you do not have to duplicate all the GR values (once in the string, and once in the array). Again, it can't be told without trying it...
    Last edited by brewbuck; 03-04-2008 at 05:55 PM.

  15. #15
    Registered User
    Join Date
    Feb 2008
    Posts
    13
    Quote Originally Posted by matsp View Post
    Yes, your "set_GR" function seems more compact code-size wise [although the only way to know for sure is to measure it].

    And:
    Code:
         int options = {314, 475, etc...};
    Perhaps you want that to be:
    Code:
         static const int options[] = {314, 475, etc...};
    The green stuff is to make sure that the compiler doesn't initialize the variable each time you call the function, which would take extra space in both code and data areas.
    The red bit I think is just a typo.

    --
    Mats
    Thanks, I knew I had the typo's but was going more for a pseudo code look than real syntax. Which I recently saw a post on this forum where that is frowned upon so I will try to do better.

    The other item to note is that "static const" also makes sure the data is stored in codespace (ROM) not in the data space (RAM). Since embedded systems usually measure RAM in number of BYTEs not number of K-BYTEs or M-BYTEs it is important to store const in ROM whenever possible.

    Thanks,
    v_dave

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. What I've got so far, creating a menu system:
    By Shamino in forum Game Programming
    Replies: 4
    Last Post: 06-15-2007, 03:03 AM
  2. Replies: 4
    Last Post: 06-13-2005, 09:03 AM
  3. Replies: 3
    Last Post: 06-13-2005, 07:28 AM
  4. Menu stuff
    By Shadow in forum C Programming
    Replies: 10
    Last Post: 04-28-2002, 09:05 AM
  5. quick question about C Dos text menu pgm i was doing
    By Shadow in forum C Programming
    Replies: 2
    Last Post: 09-16-2001, 10:26 AM