Friday 5 February 2021

i2c LCD & encoder - scrollable menu

I've been searching for a way to build arduino menus
They are a common feature in many synths.
 

 A huge thank you to Curious Scientist for this navigation menu post

Hopefully, I'll be able to incorporate some of these ideas into future projects. 


 I didn't have a KY-040 rotary encoder on hand so had to make do with a "naked one"

The KY-040 has a breakout board which adds a Vcc pin
The  virgin encoder doesn't have this.
 
It has 5 pins:
1. CLK - Clock (Out B)
2. DT - data (Out A)
3. SW - switch (when we press the shaft, the switch connects to GND. Otherwise it is floating.)
4. GND
5. GND
 

You most often will find encoders without the breakout board.
 
They still have 5 pins.
Here there are two GNDs
There is no Vcc
 
 
 
 
 
 
The bare Rotary Encoder is connected to the following Arduino Uno pins
//      o CLK (out B) --> Pin 2
//      o DT  (Out A) --> Pin 4
//      o SW  --> Pin 3
//      o GND both  --> GND on arduino/breadboard
 
I used tinkercad to make the wiring diagram
 
LEDs
short legs - cathode to gnd (connects to 220 ohm resistors )
Anode - to digital pins 
Normally, I connect the anode to the digital pins via resistors, and 
the cathode goes in straight to gnd, however this other way seems to work fine too.


ground it well.

 
 
 // *********************************************
//16x2 LCD
#include <LiquidCrystal_I2C.h> //SDA = A4, SCL = A5
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);


//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(16,2);                      // 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
}
 
//******************************


 Links
+https://www.youtube.com/watch?v=Q58mQFwWv7c 
https://www.youtube.com/watch?v=CnS0PuDJybA
+ https://www.youtube.com/watch?v=x2J4VAYQGh0


 ---------------------------------
-------------------------------------

No comments:

Post a Comment