Arduino menu navigation with rotary encoder and LCD

In this video I share some ideas about how you can navigate in a menu with a rotary encoder. With a simple setup such as an Arduino, an LCD and a rotary encoder, you can do a lot of things. I use a few LEDs to show you how you can navigate across a menu, and how you can activate/deactivate functions. The provided example can be extended or modified easily to your own requirements.



Wiring diagram

The five LEDs represent the different options selected within the menu. The color of the cable represents the color of the LED. The LCD uses the i2C protocol, so it uses 2 analog pins (A4: SDA, A5: SCK), plus the +5 V and GND connections. I added 15…

The five LEDs represent the different options selected within the menu. The color of the cable represents the color of the LED. The LCD uses the i2C protocol, so it uses 2 analog pins (A4: SDA, A5: SCK), plus the +5 V and GND connections. I added 150 Ohm resistor to all LEDs, however, some of them would run better with different values. Use some online calculators to get the optimal values for each LED.



Arduino source code

//16x2 LCD
#include <LiquidCrystal_I2C.h> //SDA = A4, SCL = A5
LiquidCrystal_I2C lcd(0x27, 16, 2);


//Defining pins for rotary encoder
const int RotaryCLK = 2; //CLK pin on the rotary encoder
const int RotaryDT = 4; //DT pin on the rotary encoder
const int RotarySW = 3; //SW pin on the rotary encoder (Button function)

//Defining variables for rotary encoder and button
int ButtonCounter = 0; //counts the button clicks
int RotateCounter = 0; //counts the rotation clicks
bool rotated = true; //info of the rotation
bool ButtonPressed = false; //info of the button

//Statuses
int CLKNow;
int CLKPrevious;
int DTNow;
int DTPrevious;

// Timers
float TimeNow1;
float TimeNow2;

//LED things
//digital pins
const int whiteLED = 8;
const int blueLED = 9;
const int greenLED = 10;
const int yellowLED = 11;
const int redLED = 12;
//statuses (1/true: ON, 0/false: OFF)
bool whiteLEDStatus = false;
bool blueLEDStatus = false;
bool greenLEDStatus = false;
bool yellowLEDStatus = false;
bool redLEDStatus = false;
//------------------------------

//Drawing of the LCD layout
//W  B  G  Y  R   CLK
//0  0  0  0  0    1


void setup()
{

 	 //Serial.begin(9600); //we don't use the serial in this example
  
	//------------------------------------------------------
	lcd.begin();                      // initialize the lcd 	
	lcd.backlight();
	//------------------------------------------------------
  lcd.setCursor(0,0); //Defining position to write from first row, first column .
  lcd.print("W B G Y R  CLK");
  lcd.setCursor(0,1); //second line, 1st block
  lcd.print("0 0 0 0 0   0"); //You can write 16 Characters per line .
  delay(3000); //wait 3 sec
  //------------------------------------------------------
   //setting up pins	
   pinMode(2, INPUT_PULLUP);
   pinMode(3, INPUT_PULLUP);
   pinMode(4, INPUT_PULLUP);

   pinMode(whiteLED, OUTPUT); //white LED
   pinMode(blueLED, OUTPUT); //blue LED
   pinMode(greenLED, OUTPUT); //green LED
   pinMode(yellowLED, OUTPUT); //yellow LED
   pinMode(redLED, OUTPUT); //red LED
	
	//LOW pins = LEDs are off. (LED + is connected to the digital pin)
   digitalWrite(whiteLED, LOW);
   digitalWrite(blueLED, LOW);
   digitalWrite(greenLED, LOW);
   digitalWrite(yellowLED, LOW);
   digitalWrite(redLED, LOW);
   

  //Store states
  CLKPrevious = digitalRead(RotaryCLK);
  DTPrevious = digitalRead(RotaryDT);
    
  attachInterrupt(digitalPinToInterrupt(RotaryCLK), rotate, CHANGE);
  attachInterrupt(digitalPinToInterrupt(RotarySW), buttonPressed, FALLING); //either falling or rising but never "change".

  TimeNow1 = millis(); //Start timer 1  
}


void loop()
{
  printLCD();
  ButtonChecker(); 
}

void buttonPressed()
{  
  //This timer is a "software debounce". It is not the most effective solution, but it works
  TimeNow2 = millis();
  if(TimeNow2 - TimeNow1 > 500)
  {    
    ButtonPressed = true;    
  }
  TimeNow1 = millis();  //"reset" timer; the next 500 ms is counted from this moment
}

void rotate()
{
  CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin

  // If last and current state of CLK are different, then a pulse occurred  
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
    // If the DT state is different than the CLK state then
    // the encoder is rotating CCW so increase
      if (digitalRead(RotaryDT) != CLKNow) 
      {        
      RotateCounter++; 

      if(RotateCounter > 4)
      {
       RotateCounter = 0;
      }

      }
      else
      {        
      RotateCounter--;
            
      if(RotateCounter < 0)
      {
        RotateCounter = 4;  
      } 	
        
      }       
    }   

  CLKPrevious = CLKNow;  // Store last CLK state
  rotated = true;
}


void printLCD()
{
    if(rotated == true) //refresh the CLK
    {
      lcd.setCursor(12,1); 
      lcd.print(RotateCounter);
      rotated = false;
    }
    
}


void ButtonChecker() //this is basically the menu part. keep track of the buttonpressed and rotatecounter for navigation
{
  if(ButtonPressed == true)
  {
    switch(RotateCounter)
    {
      case 0:      
      if(whiteLEDStatus == false)
      {
        whiteLEDStatus = true;
        digitalWrite(whiteLED, HIGH); //white LED is turned ON         
      }
      else
      {
        whiteLEDStatus = false;
        digitalWrite(whiteLED, LOW); //white LED is turned OFF           
      }

      lcd.setCursor(0,1); // Defining positon to write from second row, first column .
      lcd.print(whiteLEDStatus);
      
      break;
      
      case 1:
      if(blueLEDStatus == false)
      {
        blueLEDStatus = true;
        digitalWrite(blueLED, HIGH);  
        
      }
      else
      {
        blueLEDStatus = false;
        digitalWrite(blueLED, LOW);          
      }

      lcd.setCursor(2,1); // Defining positon to write from second row, first column .
      lcd.print(blueLEDStatus);
      break;
      
      case 2:
      if(greenLEDStatus == false)
      {
        greenLEDStatus = true;
        digitalWrite(greenLED, HIGH);  
        
      }
      else
      {
        greenLEDStatus = false;
        digitalWrite(greenLED, LOW);          
      }

      lcd.setCursor(4,1); // Defining positon to write from second row, first column .
      lcd.print(greenLEDStatus);
      break;
      
      case 3:
      if(yellowLEDStatus == false)
      {
        yellowLEDStatus = true;
        digitalWrite(yellowLED, HIGH);          
      }
      else
      {
        yellowLEDStatus = false;
        digitalWrite(yellowLED, LOW);          
      }
      lcd.setCursor(6,1); // Defining positon to write from second row, first column .
      lcd.print(yellowLEDStatus);
      
      break;
      
      case 4:
      if(redLEDStatus == false)
      {
        redLEDStatus = true;
        digitalWrite(redLED, HIGH);  
        
      }
      else
      {
        redLEDStatus = false;
        digitalWrite(redLED, LOW);          
      }

      lcd.setCursor(8,1); // Defining positon to write from second row, first column .
      lcd.print(redLEDStatus);
      break;
    }    
  }  
  ButtonPressed = false; //reset this variable
}

Previous
Previous

Arduino and joysticks - Part 2 - 3-axis joystick

Next
Next

Let's discuss about Peltier coolers #4 - Cooling the hot side - Part 2