Rotary (incremental) Encoder Controlled Menu

I have found this question on different forums, but never did I find a straight and correct answer.
I hope it can be solved on this forum.
There are several device that use a Encoder to browse through a menu.
To me it seems logical that they use a Finite state machine in their code.
I have made a Finite Stae Machine that can be controlled through push buttons, but I cannot get it to work using a Rotary Encoder.

I have found a code for the encoder and I have a fsm code, but I do not know how to combine them to get it working.
The button on the encoder can be programmed like you would with a regular push button.
It would be great if it would work like this:
Main menu:
Leds off, push the encoder button → menu RED

from menu RED while turning encoder, flip through menus; RED, GREEN, BLUE.

With a push on the Encoder Button, back to main menu

The code of the FSM:

    //
    // Libraries
    //
    #include <LiquidCrystal.h> // Library voor de 16x2 LCD Display
    //
    // finite state machine state definitions
    //
    #define STATE_RED   0
    #define STATE_GREEN 1
    #define STATE_BLUE  2
    //
    // pin definitions
    //
    LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // LCD Display Pins D2, D3, D4, D5, D11, D12
    #define BLUELEDPIN   6                 // Blauwe Led aan Pin D 6
    #define REDLEDPIN    9                 // Rode Led aan Pin D 9
    #define GREENLEDPIN 10                 // Groene Led aan Pin D 10
    //
    // global variables
    //
    int fsm_state;                         // Switch variable
    /**
     * @name setup()
     * initialize the program
     */
    void setup() {
       //
       // LCD Display
       //
       lcd.begin(16, 2);                   // 16 kolommen en 2 rijen op de LCD Display
       lcd.print("Push encoder Button");   // Geeft tekst weer op de 1e rij van de LCD Display
       lcd.setCursor(0, 1);                // Zet de cursor op de 1 kolom van de 2e rij op de LCD Display
       lcd.print("for main menu");         // Geeft text weer op de 2e rij van de LCD Display
       //
       // open Seriele poort
       //
       Serial.begin(9600);                 // Baud snelheid is 9600
       //
       // outputs
       //
       pinMode(REDLEDPIN,   OUTPUT);
       pinMode(GREENLEDPIN, OUTPUT);
       pinMode(BLUELEDPIN,  OUTPUT);
       //
       // Zet de Leds standaard uit
       //
       digitalWrite(REDLEDPIN, LOW);
       digitalWrite(GREENLEDPIN, LOW);
       digitalWrite(BLUELEDPIN, LOW);

    }
    /**
     * @name loop()
     * main loop of program and runs endlessly
     */
    void loop() {
       lcd.setCursor(0, 1);                // zet de cursor op de eerste kolom op de 2e rij
       //
       // finite state machine 1
       //
       switch (fsm_state) {
       //
       // state RED
       //
       case STATE_RED:
       
        lcd.println("RED");               // Geeft RED weer op de Display
       
        digitalWrite(REDLEDPIN,   HIGH);  // Zet de Rode led aan
         
          break;
          //
          // state GREEN
          //
       case STATE_GREEN:
       
        lcd.println("GREEN");             // Geeft GREEN weer op de Display

        digitalWrite(GREENLEDPIN, HIGH);  // Zet de Groene led aan
         
          break;
          //
          // state BLUE
          //
       case STATE_BLUE:
       
         lcd.println("BLUE");             // Geeft BLUE weer op de Display

         digitalWrite(BLUELEDPIN,  HIGH); // Zet de Blauwe led aan
         
          break;
       }
    }

The Encoder code found on this website: http://www.hobbytronics.co.uk/arduino-tutorial6-rotary-encoder:

  /*
    ** Rotary Encoder Example
    ** Use the Sparkfun Rotary Encoder to vary brightness of LED
    **
    ** Sample the encoder at 200Hz using the millis() function
    */

    int brightness = 120;    // how bright the LED is, start at half brightness
    int fadeAmount = 10;    // how many points to fade the LED by
    unsigned long currentTime;
    unsigned long loopTime;
    const int pin_A = 12;  // pin 12
    const int pin_B = 11;  // pin 11
    unsigned char encoder_A;
    unsigned char encoder_B;
    unsigned char encoder_A_prev=0;

    void setup()  {
      // declare pin 9 to be an output:
      pinMode(9, OUTPUT);
      pinMode(pin_A, INPUT);
      pinMode(pin_B, INPUT);
      currentTime = millis();
      loopTime = currentTime;
    }

    void loop()  {
      // get the current elapsed time
      currentTime = millis();
      if(currentTime >= (loopTime + 5)){
        // 5ms since last check of encoder = 200Hz 
        encoder_A = digitalRead(pin_A);    // Read encoder pins
        encoder_B = digitalRead(pin_B);   
        if((!encoder_A) && (encoder_A_prev)){
          // A has gone from high to low
          if(encoder_B) {
            // B is high so clockwise
            // increase the brightness, dont go over 255
            if(brightness + fadeAmount <= 255) brightness += fadeAmount;               
          }   
          else {
            // B is low so counter-clockwise     
            // decrease the brightness, dont go below 0
            if(brightness - fadeAmount >= 0) brightness -= fadeAmount;               
          }   

        }   
        encoder_A_prev = encoder_A;     // Store value of A for next time   
       
        // set the brightness of pin 9:
        analogWrite(9, brightness);   
       
        loopTime = currentTime;  // Updates loopTime
      }
      // Other processing can be done here
                               
    }

Aansluitschema encoder:

Aansluitschema LCD Display:

Something happens when you rotate the encoder. What you do with that information depends on where you are in the menu, doesn't it?

Something happens when the press the switch on the encoder (I really doubt that it has a button. Where is the button hole that it goes through?). What you do with that information depends on where you are in the menu, doesn't it?

I think I might be wrong about the Finite State machine.
I have seen some people do this with while and do

I found this code on this website Tutorial: manage menu and LCD display with Arduino – Coagula – Giuseppe Di Cillo
it works great as menu with button control.
Does anyone know how to convert the button control to encoder control?

This code....See below.... works for the encoder. Values go up and down when turning.
How can I couple the values to the menubutton functions?

#define EncoderPinA 2
#define EncoderPinB 3

#include "pins_arduino.h"

byte portA;
byte portB;
byte bit_maskA;
byte bit_maskB;
volatile byte *registerA;
volatile byte *registerB;

volatile static int numberA;

volatile int* attached_val;


void setup()
{
  Serial.begin(9600);
  pinMode(EncoderPinA, INPUT);
  pinMode(EncoderPinB, INPUT);
  digitalWrite(EncoderPinA, HIGH);
  digitalWrite(EncoderPinB, HIGH);

  portA=digitalPinToPort(EncoderPinA);
  portB=digitalPinToPort(EncoderPinB);

  bit_maskA = digitalPinToBitMask(EncoderPinA);
  bit_maskB = digitalPinToBitMask(EncoderPinB);
  registerA = portInputRegister(portA);
  registerB = portInputRegister(portB);

  attached_val = &numberA;
  attachInterrupt(0, doEncoderA, FALLING); // for some reason the new mouser encoders only work on A falling and b rising The other ones don't read fast enough 

  //and always count the same way
}

void loop()
{

  Serial.print("numberA = ");
  Serial.println(numberA);

  delay(500);

}


void doEncoderA()
{

  ((((*registerA) & bit_maskA) && ((*registerB) & bit_maskB)) || ((!((*registerA) & bit_maskA)) && (!((*registerB) & bit_maskB))))? (*attached_val)++ : 

  (*attached_val)--;
}

I am a bit closer, but the menu gets stuck:

These are some pieces of code I adjusted:

   //Down button               
                  // read the state of the switch into a local variable:
                  //reading = digitalRead(buttonPinRight);
                  reading = encoder0Pos + 1;
    //Up button               
                  // read the state of the switch into a local variable:
                  //reading = digitalRead(buttonPinLeft);
                  reading = encoder0Pos - 1;

But its obvious I am doing something wrong.
Who can help?

Download the full code here:

I changed this piece of code and it works a bit better now. I figured if there is a 0 state for the buttons, there has to be a 0 state for the encoder too.

      }else{
                    if (reading = (encoder0Pos = 0)) {   // I added this and it works better now
                    lastButtonPushed=0;
                    }
                  }

I also changed this and now it is more stable. Encoder takes steps of 4 numbers up or down, but sometimes 3, so sometimes i turn one step and the menu stays the same, How can I fix this?

   //Down button               
                  // read the state of the switch into a local variable:
                  //reading = digitalRead(buttonPinRight);
                  reading = encoder0Pos + 1 / 4;

and

    //Up button               
                  // read the state of the switch into a local variable:
                  //reading = digitalRead(buttonPinLeft);
                  reading = encoder0Pos - 1 / 4;

There is another challenge, I can Scroll between Item1SubItem1 en Item1SubItem2, but further forward or backward doesnt work.

                    if (reading = (encoder0Pos = 0)) {   // I added this and it works better now

= != ==

What do you mean Paul?

Hanneman:
What do you mean Paul?

What do you think that line of code is doing?

What it ACTUALLY is doing is assigning the value of 0 to encoderPos and to reading, and then testing if 0 is true. It isn’t, so the body of the statement will never be executed.

Ah yes, what was I thinking?!?

I changed it to a while loop and it works better now.

Yet some other challenges

I adjusted the code to make it simpler for myself. It does what I want, but not good enough.
I sometimes have to make a lot of turns and sometimes it jumps a few menus ahead.
Also it doesnt go from menu 8 to menu 1 and vice versa.

http://pastebin.com/nGnhK12K

Latest working version:

This is the latest version of the Mood Light project. It has a working nested menu!
It works with an Incremental rotary encoder, a 74HC595 chip and a LCD Display.
The download from this link contains the libraries, a wiring instruction, some useful websites and the .ino file:

The full code:

check out the video here:

The libraries I used:
LiquidCrystal
MenuBackend
SPI

Help an absolute Novice out and give feedback! I need all the help I can get to build an even better Mood lamp.

I have contacted Alexander Brevig, the creator of the wonderfull menubackend library and the big has to do with the order.
If white.addleft(presetmode); is placed above and green3.addleft(presetmode) is placed on the bottom, it will go straight to green3 after the menu.moveRight function is used.
So placing white.addleft(presetmode) at the bottom will fix that problem.

Also all the .addBefore codes are unnecessary and can be removed.

Thank you Alexander!

The revised code can be found here:

The letters in the menu on the display became weak.
This is because the Arduino cannot deliver enough current on its own.
I disconnected the 5v from the Arduino to the breadboard and connected a 5v powersupply to the board.
Now it works like a charm, letters are really bright :smiley:

Thanks for sharing your code.

I also need to use a rotary encoder to navigate in a menu and this solution seems fine, except that I'm not sure if it can let the user change values of integer, float and boolean variables.

You use a nice "slider" trick to change the speed, is it possible to let the user change the value of a variable with predetermined minimum, maximum and step parameters?

You're welcome.

It should be possible, I tried that at first.
But it didnt work very well for my purpose.

} else if (encoderPos >= 30005 ) {
something like x = x + 1 and the value of the integer goes up by one.

a constrain will keep the values within limits

something like flashamount = constrain(flashamount, 30, 260);

Hanneman:
This is the latest version of the Mood Light project. It has a working nested menu!
It works with an Incremental rotary encoder, a 74HC595 chip and a LCD Display.
The download from this link contains the libraries, a wiring instruction, some useful websites and the .ino file:
Dropbox - Moodlight.zip - Simplify your life

The full code:
Arduino Mood Light English - Pastebin.com

check out the video here:
Arduino Mood light Encoder driven Menu - YouTube

The libraries I used:
LiquidCrystal
MenuBackend
SPI

Help an absolute Novice out and give feedback! I need all the help I can get to build an even better Mood lamp.

This is great, I was looking to on how to make a rotary encoder work with a menu, any pointers?