Is 32 bits more than 8 bits?
This question is certainly in the minds of many electronics makers: "Should I use a 32 bits microcontroller instead of an 8 bits microcontroller ?" As an Arduino maker I...
One button menu for Arduino
There are many methods and code examples available to implement a menu on an Arduino board. For my current project I chose to use a one button menu to reduce the footprint of this functionality. I don’t need a better ergonomy since the number of menu entries is low and this is something used only to define parameters once a while. I also wanted to implement a reliable code. That is why I chose to use available libraries that have been tested and used by many.
In the example below we will be running a simple menu to choose two parameters for bliking LEDs:
The possible button events will be:
The objective is to show the menu functionality. We will not implement the variable intensity blinking of LEDS but simply show the parameters on a LCD screen.
The complete sketch example can be downloaded from here.
The physical implementation is straightforward. Here is the virtual TinkerCAD implementation using a UNO board:
Circuit design of the “One button menu Arduino circuit”
The LCD screen is connected as :
Of course data lines and RS, RW, EN can be connected to other pins depending on your available pins.
The pushbutton is connected to the ground on one side and to INT0 pin on the other side.
Installing the libraries
If you need to know how to install a library, see https://www.arduino.cc/en/Guide/Libraries
These libraries must be installed:
And linked in the header of the program
Defining the menu entries and the menu hierarchy
First we have to define the menu entries. The structure of the menu will be
Frequency
1
2
10
Intensity
Low
Middle
High
The menu and menu entries are objects defined in the MenuBackend library. They must be instantiated first:
//beneath is list of menu items needed to build the menu
MenuItem MFrequency = MenuItem(“Frequency > “);
MenuItem MFreq1 = MenuItem(“1 “);
MenuItem MFreq2 = MenuItem(“2 “);
MenuItem MFreq10 = MenuItem(“10 “);
MenuItem MIntensity = MenuItem(“Intensity > “);
MenuItem MIntensityLow = MenuItem(“Low “);
MenuItem MIntensityMiddle = MenuItem(“Middle “);
MenuItem MIntensityHigh = MenuItem(“High “);
A menu object is instantiated: MenuBackend Menu = MenuBackend(menuUseEvent,menuChangeEvent);
The instance ID will be menu. The constructor has 2 parameters that are functions used to handle events raised by the menu: menuUseEvent and menuChangeEvent.
MenuItems are then instantiated. Their constructor has one name parameter that we will use to display the current menu on the LCD screen.
We complete each name with blanks to fill the 16 characters of the LCD screen.
In our implementation events will be raised by our finite state machine. Hence we will define blank menu handlers.
void menuChangeEvent(MenuChangeEvent changed)
{
// nothing here, events will be handled by oneButton event handlers
}
Then we have to define the relations between menu items. This will allow to move from one item to the other.
MFrequency.addRight(MFreq1);
MFreq1.addAfter(MFreq2);
MFreq1.addLeft(MFrequency);
MFreq2.addAfter(MFreq10);
MFreq2.addLeft(MFrequency);
MFreq10.addAfter(MFreq1);
MFreq10.addLeft(MFrequency);
MFrequency.addAfter(MIntensity);
MIntensity.addRight(MIntensityLow);
MIntensityLow.addAfter(MIntensityMiddle);
MIntensityLow.addLeft(MIntensity);
MIntensityMiddle.addAfter(MIntensityHigh);
MIntensityMiddle.addLeft(MIntensity);
MIntensityHigh.addAfter(MIntensityLow);
MIntensityHigh.addLeft(MIntensity);
MIntensity.addAfter(MFrequency);
}
menuSetup() function adds all the relations between menu and sub-menu entries. It will be invoked in the setup() of the program.
menu.getRoot().add(MFrequency); adds to the menu root the first item : “MFrequency”. This makes the first menu entry.
Then we add the Frequency sub-menu entries moves
We then add the other menu entry MFrequency.addAfter(MIntensity); and use the same kind of commands to allow moves from sub-menu entries to sub-menu entries.
Then we allow to move from the last menu entry to the top menu entry using the command MIntensity.addAfter(MFrequency);
That is it! We have finished the structure of the menu.
First we will instantiate a button object in the declarative part of the program:
Then we have to manage the 3 possible events (click, double click, long click) raised by the button. We have to attach the 3 event handlers in the setup section of the program. It is also possible to set some parameters to fine tune the behavior of the button. setClickTicks sets the minimum delay to count a click and setPressTicks sets the minimum duration of a press. We will use a function for this:
// these are optional parameters to fine-tune the behavior. Values are in ms. Given values here are default values.
button.setClickTicks(600);
button.setPressTicks(1000);
}
We now have to code the 3 handlers : buttonClick, buttonDoubleclick and buttonPress.
We will define a Boolean variable that is set when the user is using the menu and unset otherwise.
We defined that a single click moves from one menu entry to the other:
When a buttonClick event is raised by the object button, if we are currently using the menu (inMenu = True) we use the moveDown() functions provided by the menu library to move to the menu or sub menu entry located under the current one.
Then we read the name of the menu entry (for example “Intensity >”), and use a function to print to LCD screen (explained after on).
In the declarative part we have to declare two variables for frequency and intensity of LEDs:
We defined that a double click sets the current menu value.
void buttonDoubleclick() {
if (inMenu) {
MenuItem currentMenu = menu.getCurrent();
if (currentMenu == MFrequency) {
menu.moveRight();
}
if (currentMenu == MFreq1) {
Frequency = 1;
menu.moveLeft();
}
if (currentMenu == MFreq2) {
Frequency = 2;
menu.moveLeft();
}
if (currentMenu == MFreq10) {
Frequency = 10;
menu.moveLeft();
}
if (currentMenu == MIntensity) {
menu.moveRight();
}
if (currentMenu == MIntensityLow) {
Intensity = “Low”;
menu.moveLeft();
}
if (currentMenu == MIntensityMiddle) {
Intensity = “Mid”;
menu.moveLeft();
}
if (currentMenu == MIntensityHigh) {
Intensity = “High”;
menu.moveLeft();
}
printToLCD(menu.getCurrent().getName(),0);
}
}
Like the single click, the double click is taken into consideration only if we are currently using the menu ( if (inMenu)). Otherwise a accidental double click will not change any variable.
We compare the currentMenu Item with all the sub menu items that we have defined and add the relevant code.
For example if the currentMenu is Freq1, we set the Frequency variable to 1, then we move to the menu entry level and we display the “Frequency >” menu.
We defined that a buttonPress enters the menu or exits the menu (after setting the current sub-menu value)
// buttonPress enters or exists the menu, hence the boolean inMenu value changes
inMenu = !inMenu;
if (inMenu) {
lcd.clear();
// if we just entered the menu, we have to move to the first menu entry
if (menu.getCurrent().getName() == “MenuRoot”) {
menu.moveDown();
}
printToLCD(menu.getCurrent().getName(),0);
} else { // exiting the menu
MenuItem currentMenu = menu.getCurrent();
if (currentMenu == MFreq1) {
Frequency = 1;
}
if (currentMenu == MFreq2) {
Frequency = 2;
}
if (currentMenu == MFreq10) {
Frequency = 10;
}
if (currentMenu == MIntensityLow) {
Intensity = “Low”;
}
if (currentMenu == MIntensityMiddle) {
Intensity = “Mid”;
}
if (currentMenu == MIntensityHigh) {
Intensity = “High”;
}
menu.toRoot();
lcd.clear();
}
}
First, buttonPress will toggle between inMenu / !inMenu, then if we are at the root of the menu we have to move to the first menu entry. If we are leaving the menu, we set the current sub menu value like we did in buttonDoubleclick. We move to the menu root and clear the LCD.
We have to initialise the LCD in the declarative section:
And we need a little function to display messages:
void loop() {
button.tick(); // checks regularly if the button is pressed or not and raises events according to the finite state
if (!inMenu) {
// under normal operation put your code using Frequency and Intensity parameters here.
// in this example we simply display them on the LCD screen
msg = “Frequency: ” + String(Frequency);
printToLCD(msg, 0);
msg = “Intensity: ” + Intensity;
printToLCD(msg, 1);
}
}
In the loop we invoke button.tick(); so the finite state machine evaluates if we are in one of the possible states : no click, singleClick, doubleClick, Press and it raises events as needed.
If we are not currently using the menu we simply display selected values of frequency and intensity on the LCD.
We then add a little delay so the click and double click duration are functioning well. May need a little adjustment for your software.
We are done!
After a long press on the button the menu displays the first menu entry “Frequency >”
Displaying Frequency menu
After a single click the menu displays the second menu entry “Intensity>”
Displaying Intensity menu
After a double click the sub-menu value “Low” is displayed. If we single click we will see the other values “Middle” and the “High” displayed.
Low sub menu value
After a long click again we come back to normal operations (here we chose Frequency “1”, Intensity “Low”)
One button menu for Arduino
Download the complete sketch of the one button menu for Arduino
Download