We present a single-level, universal user menu
- Arduino in practice – how the sample menu works
- Arduino in practice – defining menu items
- Arduino in practice – use of the menu
- Arduino – Further information on creating menus
In the previous article, we presentedthe basic information on how to use the SHIELD-LCD16x2. In this section, we will focus on the preparation of a concrete, practical solution that may be helpful in future device designs – there are components mounted on the board that can be used to make a user menu. To begin with, we will prepare a single-level menu, i.e., a menu that immediately starts a certain function without expanding the sub-menu.
Just a quick reminder: the SHIELD-LCD16x2 is a kind of shield that extends Arduino capabilities, featuring an LCD module (2 lines of 16 characters each), 4 buttons, and 8 additional GPIO lines. There is a PIC microcontroller mounted on the board, which communicates with Arduino UNO via a TWI interface. The available function libraries make the whole module easy to use, even by inexperienced users. Let’s start by describing how our example program works.
Arduino in practice – how the sample menu works
The proposed solution is composed of several universal functions, which – similarly to Arduino libraries – are easy to customize. In this example we’ve used a ready-made SHIELD-LCD16x2, but by changing some definitions (we’ll talk about them later), we’ll be able to configure the menu functions for use with a character display of any resolution, e.g., 4 lines of 20 characters, 1 line of 16 characters, etc. When executing the menu program, we’ll have to turn to advanced C language elements such as pointers, structures, and arrays. However, let’s start by describing how the menu works.
In the example, a menu with 4 lines is implemented. It is displayed with 2 lines of 16 characters each and operated with four buttons.It is assumed that:
- The buttons are counted from the left and numbered from 1 to 4.
- The “1” button (first on the left) causes the menu line selection marker to move down.
- The “2” button moves the menu line selection marker up.
- The “3” button works as if you were resigning from an option. “As if” because if we are not dealing with a multi-level menu, it will not be useful. So let’s assume that this is a button that can be used freely by the application.
- The “4” button (first on the right) is the confirmation of the selection – the equivalent of the Enter key on a PC keyboard.
There are 2 lines of 16 characters each on the LCD module screen, and the menu in this example has 4 lines, so we cannot show the whole menu and have to divide it into 2-line sections. It is assumed that when the microcontroller is powered on or restarted, the first and second lines of the menu will be shown on the screen, and the checkmark will be shown in the first line. When the “down” button is pressed, the marker will be moved to menu line number 2. When the “down” button is pressed again, the marker will remain in display line number 2, but menu lines 2 and 3 will be shown on the display, so the marker will show menu line number 3. The next press of “down” will show menu lines number 3 and 4, and the marker will remain in display line 2, showing option number 4. The next press of “down” will return to the initial position.
The “up” button works in a similar way, but the marker is moved in the opposite direction. So, if you press the “up” button at the beginning of a program run, the checkmark will be moved to display line number 2, and menu lines 3 and 4 will be shown on the screen. Pressing “up” will move the checkmark to display line number 1, and menu lines 3 and 4 will still be shown on the LCD screen. Another pressing of “up” will leave the checkmark on the first display line, but menu lines 2 and 3 will be shown on the screen. And so on.
The display line allows you to show a string of 16 characters. Two characters are taken by the marker symbols (“>” and “<”), so our menu can have 14 characters. The length of the menu (the number of options) is theoretically unlimited, but in practice it is limited by the size of the program memory and the capacity of the variables.
After pressing button number 4, the User_Menu function returns the number of the selected option or 0 if no selection is made or is incorrect.
Arduino in practice – defining menu items
At the beginning of the sample program, you will find a number of definitions and variables that allow you to define the parameters of the display on which the menu is shown and the number of its options and message contents. The first one is the number of options in the menu. As mentioned, the menu in this example contains 4 lines, so the definition of menu options must be equal to 4.
#define menuoptions 4
In the next definition, it is important to note the property of the C language that the Arduino compiler is based on. In the C language, the definition of a string (text) always ends with byte 0. It instructs the compiler that this is the point where the string ends and that you should, for example, terminate the display or some other operation on the text. If we create a message that is 14 characters long, we define it as char message[14]. Some compilers will append a 0 at the end of such a definition, ending the string, and some will not, treating the definition as a string of numbers rather than a text. The Arduino compiler doesn’t append 0, so to reserve space for a string of this length, you either have to write your own function to display the specified number of characters without checking if the string ends with byte 0, or add one more character than the message needs. Therefore, the value of menuitemlen should be calculated as the maximum number of characters in the display minus 1, not 2 as you might think, because 2 characters are occupied by the “>” and “<” characters used to indicate the current menu line.
#define menuitemlen 15
The next definition of lcd_lines is simple – just provide the number of lines in the display that will show the menu. For the display mentioned before, the definition looks like this:
#define lcd_lines 2
Next, the menuitem element is defined, containing the parameters of a single menu line. It is a structure with fields:
- menu_message – description of the menu line (remember, it cannot be longer than 14 characters).
- whereisptr – a constant which specifies by what value the indication must be moved to the array with the menu definition in order to display the given two lines. As you can see, the first two lines fit on the screen, so the constant is 0 for them, and then it is increased by 1 for each subsequent line. If the display had 4 lines, then the first 4 message definitions would be 0, and then 1, 2, 3, etc.
- y0 – tells you which line the menu marker is on when a message is shown on the screen.
You can find more about structures in the C language literature. Here, we will focus only on the practical application of structure. In other languages, a structure is often called a record or tuple.
The defined data type was used to create an array in the program constants space (const) containing the complete menu definition. This array is called menu_lines and contains menuoptions elements, each of which is a structure that binds the definition boxes of a single menu line.
In this example, very general messages are defined for the menu lines. Thus, the first line displays the message fragment “First menu line”, the second line “Second menu line”, the third line “Third menu line”, and the fourth line “Fourth menu line”.
const menuitem menu_lines[menuoptions]={{“First menu lin”, 0, 1},{“Second menu li”, 0, lcd_lines},{“Third menu lin”, 1, lcd_lines},{“Fourth menu li”, 2, lcd_lines}};
We will also need a pointer to each structure in the menu, the number of the current menu item (where the tags are displayed), a variable with the user’s choice, and – as in the previous example – a variable with the button number.
menuitem*menu_lines_ptr;signed char menu_position;char user_choice;int pressed;
A pointer is a very useful type of variable and makes programming very easy. If, as in the example, the pointer indicates to the first element of an array and we add 1 to it, then without unnecessary byte counting and defining the size of its element, it will point us to position number 2 in this array.
Arduino in practice – use of the menu
After defining the menu items, we can call them in our program. First, in the void setup() function, we need to assign an initial value to the variable that defines the current menu item, that is, menu_position = 0; and display the menu with a call to theDisplay_Menu()function.
In the void loop() function, we read the number of the pressed button by calling.
pressed = Number_of_Button();
If the user presses a button with a number different from 0, we can call the function User_Menu(pressed); which takes the button number as a parameter and contains an adequate reaction. Thus, button “1” causes the markers to move down, “2” to move up, and “4” to select a menu option. The number of the selected option is returned by the function and can be dealt with using e.g. switch…case or if. In the example, for illustrating how the menu works, functions that change the brightness of the display background backlight are called, but of course these can be any other functions defined by the programmer.
void loop(){pressed = Number_of_Button();if (pressed > 0) user_choice = User_Menu(pressed);switch (user_choice){case 1://akcja 1break;case 2://akcja 2break;case 3://akcja 3break;case 4://akcja 4break;}}
Arduino – Further information on creating menus
A pointer is a variable that contains the address of another variable. Using pointers leads to obtaining a more efficient code than using other methods. Unfortunately, their incorrect use can contribute to the poor readability of the program. This happens especially when they are used chaotically, without comments, pointing to variables of unknown type. On the other hand, the possibility of direct pointing to areas in memory and performing actions on them is one of the strongest mechanisms of the C language. Let’s take a look at the example below:
char x = 1, y = 2, z[5];char* pointer;pointer =&x; //pointer variable indicates to xy =*pointer; //now the y variable is 1*pointer = 0; //now the variable x is 0pointer = &z[0] //pointer variable indicates to the first element of the array z
When declaring pointers, you use the symbol * (asterisk) which is the so-called indirect addressing operator. This C language operator, when applied to a pointer, provides the content of the object being pointed to. In the C language, there is another method of assigning pointers to a specific variable. The unary & (and symbol) operator returns the address of a variable (caution: it can only be used for objects occupying variable memory and arrays. Pointer’s declaration syntax should include the imitation of the element to which the pointer can occur.
Pointers can be used to simplify and speed up the program. For example, programmers are familiar with the need to provide parameters to functions. In many programming languages, parameters are provided in the form of a very long list. This takes up a lot of space in RAM. Of course, when we create a program for a PC, this is not a big problem, but in a microcontroller this memory is usually not very large. With variables indicating a list of parameters, including long arrays e.g. containing messages displayed by the program or data buffers, you can simply point to the memory address where the variable is located.
In the C language, function call parameters are provided by value. As a result, the function cannot access arguments that belong to the calling subroutine. The only way to operate on variables from another subroutine is to pass pointers to those variables as arguments to the function call.
void Replace (int*Tx, int *Ty){int temp;temp =*Tx;*Tx = *Ty;*Ty = temp;}void main(void){int A = 10, B = 20;Replace(&A,&B); // replacing the values of variables A and B}
In this example, actions inside the function are performed directly on the variables of the main program, without any “intermediaries” such as e.g. putting variables on a stack in memory, etc.
All operations on pointers are automatically adjusted to the size of the pointed objects. The rules of pointer arithmetic are very simple. If pointer T shows the beginning of an array of int-type elements and is itself also of *int type, then the operation T=T+1; (or T++;) will move the pointer to the next element of the array. Number 2 will be added to the pointer address, not 1, because an int type variable is 2 bytes long.
Valid pointer operations include: assigning pointers to objects of the same type, adding and subtracting a pointer and an integer, subtracting or comparing two pointers to elements of the same array, and assigning value 0 to a pointer or comparing a pointer to 0. All other pointer operations are invalid. You must not add two pointers to one another (even if they are of the same type), multiply, divide, shift them right or left, assemble with masks (AND & OR operations) or add floating-point numbers to them. It is not even allowed – except for a *void type pointer – to assign to a pointer to an object of one type without converting (casting) a pointer to objects of a different type.
In the example described here, the pointer indicates an array containing data structures (records). The individual fields of the structure have names and can be referenced directly, without having to specify an offset for the structure element. The arrow operator “->” is used to point to a box with a specific name. For example:
typedef struct parameters{int x;int y;int height;}parameters*pointer;pointer->x; enables reference to box xpointer->height; allows reference to the height field
enables reference to xpointer->height box; enables reference to height box
If we create an array from a parameter structure, the pointer changes by the number of bytes that is the size of a single array element, pointing to its individual elements without having to calculate the number of bytes.
parameters array_parameters[10]; // array definition – parameter listparameters*pointer = &tablica_parametrów[0]; // now the pointer indicates// to the beginning of the array(pointer+5)->x; //indicates box x of the fifth element of the array.// allowing you to download or modify the variable;
Pointers in the C language are a very broad topic. If you want to expand your knowledge and know more about practical uses for pointers, you can read books about C language and numerous tutorials available on the Internet.
Download resources
Text prepared by Transfer Multisort Elektronik Sp. z o.o.
The original source of text: tme.eu