Go Down

Topic: New Arduino library: MenuSystem (Read 9 times) previous topic - next topic

jonblack


Ok, this is a safer and easier (for the user) way to do it.

The only little disadvantage i see if i understand correctly is that MenuSystem has to iterate through items to create the array, and then the user has to iterate again through array to print names, what is little slower, but... all have a price.

Another cuestion: Why do you pass a pointer to MenuItem in the callback funtion?

Thinking about this library.. it could be used for anything that needs a tree structure, noy only a menu system.


I pass a pointer to the menu item to the callback so it knows what invoked it.

You're right about the double iteration. Still, optimising early is generally considered a bad idea. That said, I'm not totally against you're approach. I will try them both out :)

Finally, you're right again, this could be used for any tree structure. That is the intent of the composite design pattern. I wouldn't use this library for anything other than a menu system though.

jonblack

arcachofo, I had a look at the two options: it was fun. :)

I was leaning towards my option, because I really wanted to prevent the user from doing something stupid like:

Menu* current_menu = ms.get_current_menu();
current_menu->select();

My idea was too hard to implement in a clean and tidy way (because arduino doesn't support new and delete). So I went back to your idea and the answer came to me:

Menu const* get_current_menu() const;

Yup! The function returns a pointer to a const. I made a lot of functions do the same, so if users try silly things, it won't work without them being extra silly and const_casting.

So, you'll be pleased to know that I've applied a slightly modified version of your idea and uploaded it to github. I've also included some examples. Thanks for the idea! Enjoy!

arcachofo

Quote

Menu const* get_current_menu() const;

Yup! The function returns a pointer to a const. I made a lot of functions do the same, so if users try silly things, it won't work without them being extra silly and const_casting.

Yes... looks the proper way, and if someones knows to do a cons_cast is suposed to know what is doing.

Quote
So, you'll be pleased to know that I've applied a slightly modified version of your idea and uploaded it to github. I've also included some examples. Thanks for the idea! Enjoy!

Nice!! i will have a look, thanks.

Regards.

Kipkool

#18
Mar 27, 2013, 03:18 pm Last Edit: Mar 27, 2013, 03:20 pm by Kipkool Reason: 1
Hello Jon

I'm bumping this old thread :)

First, thanks for your menu class, this is exactly what I needed for my project.

I'm actually building an antenna tracker to track my drones using telemetry data.

Code is here: https://code.google.com/p/ghettostation/source/browse/#git%2FGhettoStation

On the ground device, there's an LCD ( i2c lcd03) proposing at startup a menu.

Here is the menu:

Menu Root:
   - START
   - SET_HOME
   - CONFIGURATION:
                 - SERVOS:
                       - PAN:
                              - MIN_PWM
                              - MIN_ANGLE
                              - MAX_PWM
                              - MAX_ANGLE
                       - TILT:
                              - MIN_PWM
                              - MIN_ANGLE
                              - MAX_PWM
                              - MAX_ANGLE
                       - TEST
                 - BAUDRATE


Here is my init menu function:

Code: [Select]
void init_menu() {
rootMenu.add_item(&m1i1Item, &screen_tracking); //start track
rootMenu.add_item(&m1i2Item, &screen_sethome); //set home position
rootMenu.add_menu(&m1m3Menu); //configure
m1m3Menu.add_menu(&m1m3m1Menu); //config servos
m1m3m1Menu.add_menu(&m1m3m1m1Menu); //config pan
m1m3m1m1Menu.add_item(&m1m3m1m1l1Item, &configure_pan_minpwm); // pan min pwm
m1m3m1m1Menu.add_item(&m1m3m1m1l2Item, &configure_pan_minangle); // pan min angle
m1m3m1m1Menu.add_item(&m1m3m1m1l3Item, &configure_pan_maxpwm); // pan max pwm
m1m3m1m1Menu.add_item(&m1m3m1m1l4Item, &configure_pan_maxangle); // pan max angle
m1m3m1Menu.add_menu(&m1m3m1m2Menu); //config tilt
m1m3m1m2Menu.add_item(&m1m3m1m2l1Item, &configure_tilt_minpwm); // tilt min pwm
m1m3m1m2Menu.add_item(&m1m3m1m2l2Item, &configure_tilt_minangle); // tilt min angle
m1m3m1m2Menu.add_item(&m1m3m1m2l3Item, &configure_tilt_maxpwm); // tilt max pwm
m1m3m1m2Menu.add_item(&m1m3m1m2l4Item, &configure_tilt_maxangle); // tilt max angle
                       m1m3m1Menu.add_item(&m1m3m1i3Item, &configure_test_servo);
m1m3Menu.add_item(&m1m3l2Item, &configure_baudrate); //config baudrate
displaymenu.set_root_menu(&rootMenu);
}



Then in the mainloop, when current_activity == "MENU" , it calls at 10hz:

Code: [Select]
void display_menu() {
       Menu const* displaymenu_current = displaymenu.get_current_menu();
//Serial.print("current menu = ");
//Serial.println(displaymenu_current->get_cur_menu_component_num());
MenuComponent const* displaymenu_sel = displaymenu_current->get_selected();
         
       for (int n = 1; n < 5; ++n) {
         char string_buffer[21];
//
           if ( (displaymenu_current->get_num_menu_components()) >= n ) {
               
       
     
       MenuComponent const* displaymenu_comp = displaymenu_current->get_menu_component(n-1);
       String getname = displaymenu_comp->get_name();
 for ( int l = getname.length()-1 ; l<20 ; l++ ) {
getname = getname + " ";
}  

       if (displaymenu_sel == displaymenu_comp) {

      getname.setCharAt(19,'<');
       } else {
        getname.setCharAt(19, ' ');
       }
     
       getname.toCharArray(string_buffer,21);
               
           }
           else {
              empty_line.toCharArray(string_buffer,21);
           }
store_lcdline(n, string_buffer);

};

}


Then LCD screen is refreshed at 10hz on another timed loop in the main loop.

This works almost well.
When I stay inside the menu, I can go next, prev, back and select...
I use click button to select, & long_press button to go back to the parent.

The problem is when I select an item in a submenu. Imagine I've selected CONFIGURATION/TILT/MIN_PWM. My current_activity change from #MENU" to "TILT_MINPWM". ( so the display_menu function is not called anymore. )
It then call another function to display the configuration screen on the LCD & handle configuring parameters.
When I quit this screen, I change the current_status to "MENU", so it displays the menu again.
The problem is it doesn't go back to the previous menu state, it just start from the Root Menu, with the CONFIGURATION already selected.

I'd like to have it going back in menu_root/CONFIGURATION/SERVOS/TILT menu and not Root.

There's probably a mistake I've made somewhere, but I can't find it for now.

If you have a genius idea, I take it :)

Cheers.

Kipkool

Forget it I've found it.

It's un the library, cpp file:

Code: [Select]
// A menu item was selected, so reset the menu ready for when
// it's used again.
_p_curr_menu = _p_root_menu;


So I've added mreset parameter at select() function:
Code: [Select]

void MenuSystem::select(boolean mreset)
{
    MenuComponent* pComponent = _p_curr_menu->activate();

    if (pComponent != NULL)
    {
        _p_curr_menu = (Menu*) pComponent;
    }
    else
    {
       
if (mreset) {
// A menu item was selected, so reset the menu ready for when
        // it's used again.
        _p_curr_menu = _p_root_menu;
}
    }
}

Go Up