In this example, I’m using a rotary encoder with a 20×4 LCD display in order to control a menu. This is a simple example that also uses the rotary library from Ben Buxton and modified by Phill Fisk, who included the push button feature. In this example, cursor moves up and down until the top or bottom of the page is encountered.
A rotary encoder is a position sensor that is used to determine angular position of the rotating shaft. Usually, a rotary encoder has 5 pins: 5V, GND, push button, DT and CLK that are used for Bit A and Bit B. The encoder uses Gray Code sequence where two successive values differ in only one bit (binary digit). The reflected binary code was originally designed to prevent spurious output from electromechanical switches.
The basic way of decoding these is to watch for which bits change. For example, a change from “00” to “10” indicates one direction, whereas a change from “00” to “01” indicates the other direction. The library uses a Finite State Machine (FSM) algorithm that follows a Gray Code sequence. The biggest advantage of using a state machine over other algorithms is that this has inherent debounce built in. Other algorithms emit spurious output with switch bounce, but this one will simply flip between sub-states until the bounce settles, then continue along the state machine.
The setup is very simple. First connect an 20×4 LCD to an Arduino as in the following picture:
Then connect a rotary encoder to A0 (pin1), A1(pin2) and A2 (push button) of the Arduino and upload the following sketch:
#include <LiquidCrystal.h> #include <rotary.h> // rotary handler LiquidCrystal lcd(12, 11, 5, 4, 3, 2); #define PINA A0 #define PINB A1 #define PUSHB A2 // Initialize the Rotary object // Rotary(Encoder Pin 1, Encoder Pin 2, Button Pin) Attach center to ground Rotary r = Rotary(PINA, PINB, PUSHB); // there is no must for using interrupt pins !! int CursorLine = 1; int DisplayFirstLine = 1; char* MenuLine[] = {" Option 1", " Option 2", " Option 3", " Option 4", " Option 5", " Option 6", " Option 7"}; int MenuItems = 7; void setup () { digitalWrite (PINA, HIGH); // enable pull-ups digitalWrite (PINB, HIGH); digitalWrite (PUSHB, HIGH); lcd.begin (20, 4); lcd.clear (); // go home lcd.print ("Menu Master"); lcd.setCursor(0, 1); lcd.print("Please Select"); delay(2000); print_menu(); } // end of setup void loop () { volatile unsigned char result = r.process(); if (result == DIR_CCW) { move_up(); print_menu(); } else if (result == DIR_CW) { move_down(); print_menu(); } if (r.buttonPressedReleased(25)) { lcd.clear(); lcd.setCursor(0, 1); lcd.print("You selected:"); lcd.setCursor(0, 2); selection(); print_menu(); } //endif buttonPressedReleased } //End loop() void print_menu() { int n = 4; //4 rows lcd.clear(); lcd.setCursor(0, 0); lcd.print(" Main Menu "); if (MenuItems == 1) { //if only 1 item n = 2; } else if (MenuItems == 2) { //if only 2 item n = 3; } for (int i = 1; i < n; i++) // row 0 is used for title Main Menu { lcd.setCursor(1, i); lcd.print(MenuLine[DisplayFirstLine + i - 2]); } lcd.setCursor(0, (CursorLine - DisplayFirstLine) + 1); lcd.print("<"); } //end print_menu void move_down() { if (CursorLine == (DisplayFirstLine + 3 - 1)) { DisplayFirstLine++; } //If reached last item...roll over to first item if (CursorLine == MenuItems) { CursorLine = 1; DisplayFirstLine = 1; } else { CursorLine = CursorLine + 1; } } //end move_down void move_up() { if ((DisplayFirstLine == 1) & (CursorLine == 1)) { if (MenuItems < 3) { //Do nothing } else { DisplayFirstLine = MenuItems - 2; } } else if (DisplayFirstLine == CursorLine) { DisplayFirstLine--; } if (CursorLine == 1) { if (MenuItems < 3) { //Do nothing } else { CursorLine = MenuItems; //roll over to last item } } else { CursorLine = CursorLine - 1; } } //end move_up void selection() { switch (CursorLine - 1) { case 0: lcd.print("Option 1 "); //set a flag or do something.... break; case 1: lcd.print("Option 2 "); //set a flag or do something.... break; case 2: lcd.print("Option 3 "); //set a flag or do something.... break; case 3: lcd.print("Option 4 "); //set a flag or do something.... break; case 4: lcd.print("Option 5 "); //set a flag or do something.... break; case 5: lcd.print("Option 6 "); //set a flag or do something.... break; case 6: lcd.print("Option 7 "); //set a flag or do something.... break; default: break; } //end switch delay(2000); DisplayFirstLine = 1; CursorLine = 1; } //End selection
This code let’s you move the cursor up and down. It uses polling but it can easily be modified to use with interrupts. I used ‘half-step’ mode which emits an event at both the 0-0 and 1-1 positions. This is useful since I wish to change the menu position at every click. In order to activate ‘half-step’ mode, go to “rotary.h” file which is located in your library within your sketchbook folder and uncomment line
//#define HALF_STEP
You are done!
Hallo Freund,
sehr gute arbeit,ich versuche Ihr Program auf
“#include
#include
LiquidCrystal_I2C lcd(0x27, 20, 4);”
anzuschliessen, schaffe es aber nicht.
LikeLike
You will need to change the line ” lcd.begin (20, 4);” to ” lcd.begin ();” also.
LikeLike
Dear friend,
I do not speak German but using Google translator it looks that I2C is not working for you. Two things that you should try:
1) You need to include the I2C library: Wire.h and LiquidCrystal_I2C.h
2) If it still doesn’t work, it might be that 0x27 is not the right code (usually it is the deafault). There are programs to detect the I2C code…try looking at this site…https://www.makerguides.com/character-i2c-lcd-arduino-tutorial/
Hope it helps!!
LikeLike
Hi,
I’m having issues with the polling mode, can you help on how I can switch to interrupt mode?
Thanks!!
LikeLike
Hi Arturo,
Look into original Buxtronix examples:
https://github.com/buxtronix/arduino/tree/master/libraries/Rotary/examples/interrupt
Here you will find an interrupt example. Hope it helps!
LikeLike
hi
how to change a own sub variable based on the selections in the submenu?
LikeLike
Hi Bjarne,
In the function “void selection()” you will find a switch selection.
Here where it says //set a flag or do something….
replace it with a variable.
I have a youtube video with a more complete example with a link to a Github where you can find the code.
I hope it helps.
LikeLike
Hi Carlos,
I use an I2C-LCD.
I made some changes (#include wire.h, #include LiquidCrystal_I2C.h) and ….
it works perfect!
Thank you!
Frank
LikeLike