Love ’em or hate ’em, sometimes your embedded project needs a menu system. Rather than reimplement things each and every time, [sgall17a] put together a simple GUI menu system in Micropython that can be reused in all sorts of projects. The approach uses tables to define the menus and actions, and the demo program comes with a pretty good assortment of examples. Getting up to speed using this module should be fairly easy.
The hardware that [sgall17a] chose to demonstrate the concept couldn’t have been much smaller — it’s a Raspberry Pi Pico development board, an OLED 128 x 64 pixel display, and a rotary encoder with built-in push-button switch (it’s also been tested on ESP32 and ESP8266 boards). The widget under control is one of the commonly available Neopixel development boards. The program is hosted on GitHub, but beware that it’s under development so there may be frequent updates.
This is a good approach to making menus, but is often rejected or not even considered because of the overhead cost of developing the infrastructure. Well, [sgall17a] has done the hard work already — if you have an embedded project requiring local user setup, check out this module.
I think this could become more widely adopted if there were some nice example photos or screenshots of the generated menus in action. The only example I found was the raw code, and that tells me nothing about how it looks on the screen due to that information being in the header/library/whatever-is-going-on-here.
I usually code GUIs by hand and things get moved around the screen and some things are bigger than other things, and in general they look a bit more “graphical” than text based even though it’s basically all just text. If I can’t organize the placement of things on screen and adjust their size, I’ll probably just stick with coding my own GUIs. I don’t usually want to make things super fancy as I’m a minimalist utilitarian type, but I do like to have well organized and informational menus, not just a few lines of text all left aligned on the screen.
Any plans on such future updates?
1. I will try to put a video and some photos of the system in action, but it will take about a week.
2. All display is done by a function called display() which currently resides in the encoder_menu.py file. The display functioning can easily be modified for fancier text display, such different fonts or fancier screen. It was written this way in order to be as simple as possible so it can run on even simpler display methods such as a one or two line liquid crystal display.
This simple display() function can handle menu display, text messages (Info), integer entry (GetInteger) . Selection , and Wizard which essentially leverage off menu display. It would be easy for the Info and GetInteger classes to handle much fancier displays such as colour and graphics. This would involve writing specific display functions in these classes, rather than just using the basic text display. Keep in mind this will also make greater demands on your MCU.
A bigger problem is making fancier menus. Currently only a single line of menu is displayed which minimises display problems such tracking the current menu item and showing next and previous items. Also, of course, a single line runs on minimal hardware.
As it stands the project would fragment, or increase a lot in size, if you had to make too much allowance for many different display and entry methods. It is encouraging that there is some interest in my project, so I will give some thought to making the menus somewhat more sophisticated while keeping the project simple and robust (no promises yet). I would be thinking of turning the number of lines displayed in a menu into a configuration parameter. A method of highlighting the currently selected menu item would also be required. and I would like display() to work similarly across different hardware. Maybe this could as simple as requiring that the display uses Micropython FrameBuffer as a dependency.
I find the multiple use of self in python Classes irritating so I have tended to use simple functions with some global variables.
I think this is neater than much of Python programming using classes, but I do recognize that it is a bit idiosyncratic.
“I will try to put a video and some photos of the system in action,”
There are very few things I would say this about but demonstrating a menu system like this is one use case where an animated png or gif might just hit the spot.
I agree, that’s a good idea.
Thanks for the info. I think the idea of making the number of lines displayed configurable is a good idea. If I might suggest a simple method of displaying the currently selected menu item that I believe will mesh well with your philosophy, appending two square braces with one space between them to each of the lines and simply have an X in between them for the selected item is easy and quickly recognizable, ie. [X] vs [ ] You could even make this customizable by allowing a character string be assigned to nonselected menu items and a different character string to selected menu items. Then people can also use [o] if they prefer or {W} or -@- or whatever floats their boat.
It is interesting to re-visit this project. At the time I knew its workings intimately and things seemed “intuitive”, but coming back not so much. I think I need some marker of menu depth, probably a “bread crumb” row. I agree a textual character or small string would be an easy way of revealing the function and/or state of a menuitem.
A small OLED or liquid graphics (like some of those small colour displays) is pretty basic and commonplace and so there is probably little need to support a one line display, beyond what I have done except for changes mentioned above.
Most of what you suggest could be done by “convention”, ie just something put in the caption when you define the menu. Depth of menu/bread crumb would be a little more dynamic.
OLED and graphic LC display drivers inherit from FrameBufffer in Micropython so a generic highlighting method like inverse text would be OK.
When you consider what events need to be trapped, they are up, down forward and back, so four buttons might be nearly as good as an encoder and the “pseudo-event” back function. An encoder is simple and really quite nice, however.
Coming back to my little project, I think using closures was a good choice and using async keeps menus nicely dynamic even if the processor is doing something else like flashing neopixels.
Thanks for you interest and feedback.
There are more GUI libraries for Micropython: LVGL, µGUI, Nano GUI, GUIslice.