LCD Screen and Rotary Encoder

Hello guys, new to Arduino and the forum.

I'm having a real hard time creating an LCD menu with a rotary encoder.

I've set up my basic code for the rotary encoder and switch button. It counts up and down fine and detects when the button is pressed. The counters and button have there own interrupt loops and aren't ran in the main loop.

I'm just looking for general advice on how to create a simple menu with sub menus.
such as ;

Menu 1 > Submenu 1 ; Option 1, Option 2
Menu 2 > Submenu 2 ; etc.
Menu 3 > Submenu 3 ; etc.

The main problem I'm having is differentiating the counted steps from one another. I can get the first layer to work where Menu 1 = counter = 1, Menu 2 = counter = 2 and so on but obviously with that logic the minute I go into another menu and rotate the encoder the menu will just go to menu 2 if I select menu 1 with the push button because the count is on going. If only there was a way to restart the count in each menu and ignore the rest the loop then this method might work.

Basically I'm just looking for general logic on how to set this up. The counter seems nice but it doesn't looks like I'll be able to get it to work this way. if there's a better way to go about this please let me know.

Any input is appreciated.

//const int: Read only, used if value never changes, saves memory
//int: Read and write, used if value changes, uses more memory

// include the library code:
#include <LiquidCrystal.h>

//Define Encoder Pins
#define outputA 19      // Generating interrupts using CLK signal, int.3
#define outputB 20      // Reading DT signal, int.4
#define SW 18          // Reading Push Button switch SW, int.5

//Define Encoder Variables
int counter = 0;
int counterLast = 0;
int maxCounter = 1;
int aState;
int aLastState;
int SWState;
int SWStateLast;
int pushed = 0;
int page = 1;
int menuPosition = 0;
int menuPositionLast = 0;

//initialize the library with the numbers of the interface pins
const int rs = 40, en = 41, d4 = 42, d5 = 43, d6 = 44, d7 = 45;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

//LCD Contrast
int Con = 100;

//Arrow right
byte arrow[8] = {

void setup() {

  Serial.println("Menu Test");

  //Reads the initial state of the outputA
  aLastState = digitalRead(outputA);

  //Activate Encoder Pins
  pinMode (outputA, INPUT_PULLUP);
  pinMode (outputB, INPUT_PULLUP);
  pinMode (SW, INPUT_PULLUP);

  //Call updateEncoder() when any high/low changed seen
  attachInterrupt(3, updateEncoder, CHANGE);    //outputA
  attachInterrupt(4, updateEncoder, CHANGE);    //outputB
  attachInterrupt(5, pushButton, CHANGE);       //SW

  //Create arrow char
  lcd.createChar(0, arrow);

  //LCD Contrast
  int Con = 100;

  //Controls LCD's contrast
  analogWrite(11, Con);

  lcd.begin(16, 2);

}//void setup

void loop() {

  if (counter > counterLast) {

    menuPosition ++;

  if (counter < counterLast) {

    menuPosition - 1;

  menuPositionLast = menuPosition;


  counterLast = counter; //Save the value of the last state


}//void loop

void updateEncoder() {

  //Reads the "current" state of the outputA
  aState = digitalRead(outputA);

  // If last and current state of CLK are different, then pulse occurred
  // React to only 1 state change to avoid double count
  if (aState != aLastState && aState == 1) {

    //If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
    if (digitalRead(outputB) != aState) {

      if (counter < maxCounter) {  //counter does not go above max

        counter ++;  //encoder is rotating CW


      else {

        counter = 0; //go back to 0 so the counter keeps looping, this way the
        //encoder can be rotated in only one direction for control

    } else {

      if (counter < 1) {  //counter does go below 0

        counter = maxCounter;


      else {

        counter --;  //encoder is rotating CCW





  //Updates the previous state of the outputA with the current state
  aLastState = aState;

  //Put in a slight delay to help debounce the reading

}//void updateEncoder

void pushButton () {

  // Read the button state
  SWState = digitalRead(SW);

  if (SWState == LOW) {

    pushed = 1;


  } else {

    pushed = 0;




You can make a separate variable to track the number of the menu item and reset it between each menu transition, named something like menu_position.
Instead of using the value of the encoder directly, consider using a variable to track where it was before the last turn, named something like last_position. Then, each time the encoder changes, compare it to last_postion.
greater than 0 then add one to menu_position
less than 0 then subtract one from menu_position.

This will free the menu_position to be adjusted or reset as you need it to be and removes the restriction of 4 items to a menu.

Post your sketch well formated and in code tags to see how we can help.

Wouldn't this give me the same issue as I’m having now? To my understanding every page would need its own unique number, I could make menuitem 1, 2, etc and then go into a submenu but if the sub menu’s is also 1,2 then it wouldn’t execute and would go out of the submenu back to the first menu

Try making it and see what it does. Your primary issue is that you have mapped the rotary position directly to the menu position and cannot change it when the menus start, at least as I understand it. My suggested method separates those concepts.

Code is posted.

Okay code is posted. I'm still trying to figure how to update the menu position properly when it detects an input from the rotary encoder

Hello and good morning
Take a view here.

I´m using this libary for my projects.

That library looks good but I don't see anything for a rotary encoder with a push button

Hello and good morning
Do you need a libary to read a button state, too?

I ended up just using another library.

What library did you have in mind though?

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.