20,4 Lcd with Encoder Menu Scroll.

Hi,

I’m trying to create a menu on a 20,4 Lcd display using the built-in normal Lcd Library on a Mega. I’m also wishing to scroll a cursor “a custom char”, up and down the menu using a Rotary Encoder and the PJRC Encoder Library.

The menu starts with like a welcome, then when you click the switch on the Encoder it takes you to the menu I wish to scroll through. With the current sketch this all works except for the cursor scrolling.

/* Encoder Library - Basic Example
   http://www.pjrc.com/teensy/td_libs_Encoder.html

   This example code is in the public domain.
*/

#include <LiquidCrystal.h>
#include <Encoder.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
Encoder myEnc(20, 21);
int button = 53;
int sw_val = 0;

char* menu[] =
{
  "Set Temp",
  "Set Humid",
  "Item 3",
  "Item 4",
  "Item 5",
};

const int numMenuItems = sizeof menu / sizeof * menu;
const int numLcdRows = 4;
int newPosition = 0;

byte menuCursor[8] =
{
  B01000, //  *
  B00100, //   *
  B00010, //    *
  B00001, //     *
  B00010, //    *
  B00100, //   *
  B01000, //  *
  B00000  //
};

void setup()
{
  Serial.begin(9600);

  lcd.begin(20, 4);
  lcd.createChar(0, menuCursor);
  lcd.clear();
  pinMode(button, INPUT);
  digitalWrite(button, HIGH);
  lcd.setCursor(4, 0);
  lcd.print("Temperature");
  lcd.setCursor(8, 1);
  lcd.print("and");
  lcd.setCursor(6, 2);
  lcd.print("Humidity");
}

long oldPosition = -999;

void loop()
{
  sw_val = digitalRead(button);
  //Serial.println(sw_val);
  if (sw_val == LOW)
  {
    mainMenu();
  }
}

void mainMenu()
{
  for (int i = 0; i < numLcdRows; ++i)
  {
    lcd.setCursor(1, i);
    lcd.print(menu[i + newPosition]);    
  }
  long newPosition = myEnc.read();
  if (newPosition > oldPosition)
  {
    oldPosition = newPosition;
   
    lcd.clear();
    lcd.setCursor(0, newPosition);
    lcd.write(byte(0));
    //lcd.setCursor(1, 0);
    //lcd.print(menu[newPosition]);
  }
}

What I would like to happen is;

Set Temp
Set Humid
Item 3
Item 4

Then with 1 detent click of the Encoder;
Set Temp

Set Humid
Item 3
Item 4

After the cursor gets to Item 4 I then wish for the char menu to scroll up 1 so you have;
Set Humid
Item 3
Item 4

Item 5

I’ve done some experimenting with the mainMenu with mixed results;

void mainMenu()
{
  for (int i = 0; i < numLcdRows; ++i)
  {
    lcd.setCursor(1, i);
    lcd.print(menu[i + newPosition]);
  }
  for (int i = 0; i < numLcdRows; ++i)
  {
   i = myEnc.read();
  if (i > oldPosition)
  {
    oldPosition = i;

    lcd.clear();
    lcd.setCursor(0, i);
    lcd.write(byte(0));
    //lcd.setCursor(1, 0);
    //lcd.print(menu[newPosition]);
  }
  }
}

This will show the “welcome msg”, click the switch I only get the cursor @ 0,0. Rotate the Encoder and it moves to 0,3. click the switch and then the char menu appears with the cursor @ 0,3.

Another effort;

void mainMenu()
{
  for (int i = 0; i < numLcdRows; ++i)
  {
    lcd.setCursor(1, i);
    lcd.print(menu[i + newPosition]);
  }
  for (int i = 0; i < numLcdRows; ++i)
  {
   i = myEnc.read();
  if (i > oldPosition)
  {
    oldPosition = i;

    lcd.clear();
    lcd.setCursor(0, i);
    lcd.write(byte(0));
    //lcd.setCursor(1, 0);
    //lcd.print(menu[newPosition]);
  }
  }
}

This does the same as above.

Another effort;

void mainMenu()
{
  for (int i = 0; i < numLcdRows; ++i)
  {
    lcd.setCursor(1, i);
    lcd.print(menu[i + newPosition]);
  }
  for (int j = 0; j < numLcdRows; ++j)
  {
   j = myEnc.read();
  if (j > oldPosition)
  {
    oldPosition = j;

    lcd.clear();
    lcd.setCursor(0, j++);
    lcd.write(byte(0));
    //lcd.setCursor(1, 0);
    //lcd.print(menu[newPosition]);
  }
  }
}

Now when I rotate the Encoder the cursor goes from 0,0 to 0,2 with no char menu. Click the switch the char menu appears and the cursor moves to 0,3.

Can I ask for some advice.

Dizzwold.

  for (int j = 0; j < numLcdRows; ++j)
  {
   j = myEnc.read();
  if (j > oldPosition)
  {
    oldPosition = j;

    lcd.clear();

Why do you need to do this more than once? Why do you need to clear the LCD to get rid of one character?

You have two problems. One is to determine which lines to display on the LCD. The other is to determine which row to print the custom character on.

Separate those two problems.

Determine which item in the menu to display on the top line. Determine whether it gets a space or a custom character as the prefix. Display the line.

Repeat for the other three rows.

Hi PaulS,

Thank you for your reply and comments.

I've actually found a sketch/Library that does more than I would want by MARK VAN VEGGEL and currently digesting it to understand its workings.

For myself as a novice it's quite a big sketch and somewhat complicated. With reading through it I understand most of it, but would like to ask some questions.

I'll post the complete sketch below in a seperate post.

void singleClick() {

  // IF current layer number is the same as redirect layer number, we stay on the same layer
  // AND no item is selected in the menu
  // AND the selected item has a redirect to another layer
  if(currentLayer == menu[currentPosition + currentSelect].substring(0,1).toInt() && menu[currentPosition + currentSelect].substring(1,3) != "__" && currentPress == ""){

I'm guessing that substring(1,3) is looking at the first line of the stringmenu; "1_Rotation","056Active","3Watches","4_Item4"

So what is substring(0,1) looking at, I'm getting a bit lost?

Dizzwold.

/*
 * MARK VAN VEGGEL
 * ARDUINO LCD MENU V0.1
 * YOUTUBE.COM/AMPSOURCE
 * 
 */

#include <OneButton.h>
#include <Encoder.h>
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C  lcd(0x27,2,1,0,4,5,6,7);

// ROTARY ENCODER
long oldPosition  = 0;
int encoderDTpin = 2;
int encoderCLKpin = 3;
Encoder myEnc(encoderDTpin, encoderCLKpin);

int buttonPin = 8;
OneButton button0(buttonPin, true);


// MENU ARRAY 
// First number in array = amount of menu items in root layer. Same goes for next layer. (Example: first value in array is 4. This means Layer 0 has 4 menu items. Layer 1 has only 2 menu items.)
// First number infront of menu text is the layer number that item points to. (Example: 1_Rotation points to Layer 1)
// Second number infront of menu text is the number of the value connected to that menu item in the values array. (Example: 15Direction points to position 5 in the values array)

String menu[] = {"4","1__Rotation","056Active","3__Watches","4__Item4",       // Layer 0
                 "3","100Rotations","105Direction","109Speed",                // Layer 1
                 "3","256Active","2__Item2.2","2__Item2.3",                    // Layer 2
                 "4","4__Watch 1","3__Watch 2","3__Watch 3","3__Watch 4",     // Layer 3
                 "2","4__Item3.1.1","4__Item3.1.2"};                          // Layer 4

// VALUES ARRAY
// TYPES, 1 numeric / 2 character value range
// 1 - type,value,increments,min,max
// 2 - type,starting value in options array,lowest value options array,max value options array
int values[] = {1,11,25,50,1450, //0-4 WATCH1
                2,2,0,2,         //5-8
                1,600,25,75,600, //9-13
                
                1,11,25,50,1450, //14-18 WATCH2
                2,2,0,2,         //19-22
                1,600,25,75,600, //23-27
                
                1,11,25,50,1450, //28-32 WATCH3
                2,2,0,2,         //33-36
                1,600,25,75,600, //37-41
                 
                1,11,25,50,1450, //42-46 WATCH4
                2,2,0,2,         //47-50
                1,600,25,75,600, //51-55
                
                2,4,3,4,         //56-59 WATCH1 ACTIVE
                2,4,3,4,         //60-63 WATCH2 ACTIVE
                2,4,3,4,         //64-67 WATCH3 ACTIVE
                2,4,3,4};        //68-71 WATCH4 ACTIVE
                
                
String options[] = {"CW","CCW","MIX",
                    "No","Yes"};                 

// Custom character for LCD.
byte cursor[8] = {
  0b10000,
  0b10000,
  0b01000,
  0b00110,
  0b01000,
  0b10000,
  0b10000,
  0b00000
};

byte watch[8] = {
  0b01110,
  0b01110,
  0b11011,
  0b10001,
  0b10001,
  0b11011,
  0b01110,
  0b01110
};


int currentType = 0;
int value = 0;
int numIncr = 0;
int Min = 0;
int Max = 0;

int currentLayer = 0;
int lastLayer = 99;
int currentLength = menu[0].toInt();
int currentPosition = 0;
int currentSelect = 1;
int currentChange = 0;
String currentPress = "";
float currentlcdLayerFloat = 0;
int currentlcdLayer = 0;
int lastlcdLayer = 0;
int lcdUpdated = 0;
int currentLine = 0;
int cursorLayer = 0;
int cursorPosition = 0;
int valueLength = 0;

/*
int homeScreen = 1;

String currentTime = "00:00:00";
String lastTime = "00:00:00";
String temptime = "00";
String currentDate = "";

int watch1active = values[56] - 3;
int watch2active = values[60] - 3;
int watch3active = values[64] - 3;
int watch4active = values[68] - 3;
int watch1rotations = 0;
int watch2rotations = 0;
int watch3rotations = 0;
int watch4rotations = 0;
int watch1left = values[1];
int watch2left = values[15];
int watch3left = values[29];
int watch4left = values[43];
String temprotationsleft = "";
*/


void setup() {
  Serial.begin(9600);

  // Map rotary button to actions single and doubleclick.
  button0.attachClick(singleClick);
  button0.attachDoubleClick(doubleClick);

  lcd.begin (16,2);
  lcd.setBacklightPin(3,POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home();

  // Create the custom character.
  lcd.createChar(0, cursor);
  lcd.createChar(1, watch);

  lcd.setCursor (2,0);  
  lcd.print("Watch Winder");
  delay(1000);
  clearLine(0);

  // Set the select cursor on the first line.
  cursorLayer = 0;
  lcd.setCursor (0,cursorLayer);  
  writeCursor();

}

void loop() {
    
    // Listen to button presses.
    button0.tick();

    // Listen if the rotary encoder moves.
    rotary_check();
    
    // Print the LCD menu.
    lcdMenu();

}

void singleClick() {

  // IF current layer number is the same as redirect layer number, we stay on the same layer
  // AND no item is selected in the menu
  // AND the selected item has a redirect to another layer
  if(currentLayer == menu[currentPosition + currentSelect].substring(0,1).toInt() && menu[currentPosition + currentSelect].substring(1,3) != "__" && currentPress == ""){
    
    currentPress = menu[currentPosition + currentSelect].substring(3);
    currentChange = menu[currentPosition + currentSelect].substring(1,3).toInt();
    
    Serial.println("Currentpress: " + currentPress);
    
    currentType = values[currentChange];
    value = values[currentChange + 1];

    // Type select change is numeric
    if(currentType == 1){
      Min = values[currentChange + 3];
      Max = values[currentChange + 4];
      numIncr = values[currentChange + 2];
      
      Serial.println("Currentvalue: " + String(value));
      Serial.println("Increments: " + String(numIncr));
      Serial.println("Min: " + String(Min));
      Serial.println("Max: " + String(Max));

      
      valueLength = String(value).length();
      
    // Type select change is character range
    } else if(currentType == 2){
      Min = values[currentChange + 2];
      Max = values[currentChange + 3];

      Serial.println("Currentvalue: " + options[value]);
      Serial.println("Options: ");
      
      for (int i=Min; i <= Max; i++){
        Serial.println(options[i]);
      }

      valueLength = options[value].length();

    }

    clearSelect();
    cursorPosition = 15 - valueLength;
    lcd.setCursor(cursorPosition,cursorLayer);
    writeCursor();

  // ELSE IF something is selected in the menu, and the button is pressed again. We deselect the item.  
  } else if(currentPress != ""){
    
    currentValues();
    currentPress = "";
    currentChange = 0;

    clearChar(cursorPosition,cursorLayer);
    lcd.setCursor(0,cursorLayer);
    writeCursor();
    
  // IF nothing is selected and nothing will be deselected, move to the redirect layer listed in the first position of the array.  
  } else {
    
    currentLayer = menu[currentPosition + currentSelect].substring(0,1).toInt();
    currentSelect = 1;
    currentlcdLayerFloat = 0;
    currentlcdLayer = 0;
    lastlcdLayer = 0;

    clearSelect();
    cursorLayer = 0;
    lcd.setCursor (0,cursorLayer);  
    writeCursor();
    
  }
  
}
// Reset to layer 0.
void doubleClick() {
  if(currentLayer == 0){
    currentLayer = 0;
    currentSelect = 1;
    currentlcdLayerFloat = 0;
    currentlcdLayer = 0;
    lastlcdLayer = 0;
  
    clearSelect();
    cursorLayer = 0;
    
    lcd.clear();
    lcdUpdated = 0;
  }
  if(currentLayer != 0){
    currentLayer = 0;
    currentSelect = 1;
    currentlcdLayerFloat = 0;
    currentlcdLayer = 0;
    lastlcdLayer = 0;
  
    clearSelect();
    cursorLayer = 0;
    lcd.setCursor (0,cursorLayer);  
    writeCursor();
  }
}

void rotary_check(){

  // Constantly read the position of the rotary encoder
  long newPosition = myEnc.read() / 4;

  // IF the new position of the encoder is different then the old position
  if (newPosition != oldPosition) {
       
    // IF nothing has been selected in the menu
    if(currentPress == ""){

      if(newPosition > oldPosition && currentSelect < currentLength){
        clearSelect();
        currentSelect++;
        currentlcdLayerFloat = currentlcdLayerFloat + 0.5;
        currentlcdLayer = currentlcdLayerFloat;

        cursorSelect();
        
        lcd.setCursor (0,cursorLayer);  
        writeCursor();
        Serial.println("> " + menu[currentPosition + currentSelect].substring(3));

      } else if (newPosition < oldPosition && currentSelect != 1){
        clearSelect();
        currentSelect--;
        currentlcdLayerFloat = currentlcdLayerFloat - 0.5;
        currentlcdLayer = currentlcdLayerFloat;

        cursorSelect();

        lcd.setCursor (0,cursorLayer);  
        writeCursor();
        Serial.println("> " + menu[currentPosition + currentSelect].substring(3));
  
      }
      
      if(lastlcdLayer != currentlcdLayer){
        lcdUpdated = 0;
      }
      
      lastlcdLayer = currentlcdLayer;      
      oldPosition = newPosition;



    // IF something has been selected in the menu, we are going to change the value of a menu item.
    } else if(currentPress != ""){

      
      lcd.setCursor(cursorPosition + 1,cursorLayer);
      // IF the new position is HIGHER then the old position
      if(newPosition > oldPosition){
        clearChar(cursorPosition,cursorLayer);
        
        // IF the selected item has a change type that is NUMERIC.
        // AND the MAXIMUM of the selected item has not been reached. 
        // We change the value UPWARDS with the icrements listed in the array.
        // SAME applies for next ELSE IF, just for the change type character. And the next ELSE IF we just go down.
        if(currentType == 1 && value < Max){
            value = value + numIncr;
            values[currentChange + 1] = value;
            Serial.println(value);

            clearValue();
            valueLength = String(value).length();
            cursorPosition = 15 - valueLength;
            lcd.setCursor(cursorPosition + 1,cursorLayer);
            lcd.print(value);
          }

        if(currentType == 2 && value < Max){
            value++;
            values[currentChange + 1] = value;
            Serial.println(options[value]);

            clearValue();
            valueLength = options[value].length();
            cursorPosition = 15 - valueLength;
            lcd.setCursor(cursorPosition + 1,cursorLayer);
            lcd.print(options[value]);
          }
          lcd.setCursor(cursorPosition,cursorLayer);
          writeCursor();
        
      } else {
        clearChar(cursorPosition,cursorLayer);
        
        if(currentType == 1 && value > Min){
          value = value - numIncr;
          values[currentChange + 1] = value;
          Serial.println(value);

          clearValue();
          valueLength = String(value).length();
          cursorPosition = 15 - valueLength;
          lcd.setCursor(cursorPosition + 1,cursorLayer);
          lcd.print(value);
        }

        if(currentType == 2 && value > Min){
            value--;
            values[currentChange + 1] = value;
            Serial.println(options[value]);

            clearValue();
            valueLength = options[value].length();
            cursorPosition = 15 - valueLength;
            lcd.setCursor(cursorPosition + 1,cursorLayer);
            lcd.print(options[value]);
          }
          lcd.setCursor(cursorPosition,cursorLayer);
          writeCursor();
        
      }

      
        
      oldPosition = newPosition;
    } else {
      oldPosition = newPosition;
    }
    
    
    
  }
}

void lcdMenu(){
  if (lastLayer != currentLayer || lcdUpdated == 0){

    currentLength = menu[0].toInt();
    currentPosition = 0;
    
    for (int i=0; i < currentLayer; i++){
      currentPosition = (menu[currentPosition].toInt() + 1) + currentPosition;
      currentLength = menu[currentPosition].toInt();
    }

    clearMenu();
    int tempPosition = currentPosition;
    
    if(currentlcdLayer != 0){
      
      currentPosition = currentPosition + (currentlcdLayer * 2);
    }
    
    Serial.println("LCD-------------");

    for (int i=1; i <= 2; i++){
      lcd.setCursor (1,i - 1);  
      lcd.print(menu[currentPosition + i].substring(3));
      
      currentType = 0;
      value = values[menu[currentPosition + i].substring(1,3).toInt() + 1];
      String tempvalue = "";
      
      if(menu[currentPosition + i].substring(1,3) != "__" && menu[currentPosition + i].substring(1,3) != ""){
        currentType = values[menu[currentPosition + i].substring(1,3).toInt()];
        if(currentType == 1){
          lcd.setCursor (16 - String(value).length(),i - 1);
          lcd.print(value);
          tempvalue = " "+ String(value);
        }else if(currentType == 2){
          lcd.setCursor (16 - options[value].length(),i - 1);
          lcd.print(options[value]);
          tempvalue = " "+ options[value];
        } 
        
      }
      
      Serial.println(menu[currentPosition + i].substring(3) + tempvalue);
    }
    Serial.println("----------------");
    
    currentPosition = tempPosition;
    lastLayer = currentLayer;
    lcdUpdated = 1;
    
    /**
    Serial.println("Layer " + String(currentLayer + 1));
    Serial.println("Layer length " + String(currentLength));
    Serial.println("Layer position " + String(currentPosition));
    **/
    
  }
}


void currentValues(){

  Serial.println("Currentpress: " + currentPress);
  
  if(currentType == 1){
      Serial.println("Currentvalue: " + String(value));
      Serial.println("Increments: " + String(numIncr));
      Serial.println("Min: " + String(Min));
      Serial.println("Max: " + String(Max));
      
    } else if(currentType == 2){
      Serial.println("Currentvalue: " + options[value]);
      Serial.println("Options: ");
      
      for (int i=Min; i <= Max; i++){
        Serial.println(options[i]);
      }
    }
}

void clearChar(int charPosition, int line){
  lcd.setCursor (charPosition,line);
  lcd.print(" ");
}

void clearLine(int line){
  lcd.setCursor (0,line);
  lcd.print("                ");
}

void clearMenu(){
  lcd.setCursor (1,0);
  lcd.print("               ");
  lcd.setCursor (1,1);
  lcd.print("               ");
}

void clearSelect(){
  lcd.setCursor (0,0);
  lcd.print(" ");
  lcd.setCursor (0,1);
  lcd.print(" ");
}

void cursorSelect(){
  switch (currentSelect){
          case 1:
            cursorLayer = 0;
            break;
          case 2:
            cursorLayer = 1;
            break;
          case 3:
            cursorLayer = 0;
            break;
          case 4:
            cursorLayer = 1;
            break;
          case 5:
            cursorLayer = 0;
            break;
          case 6:
            cursorLayer = 1;
            break;
        }
}

void writeCursor(){
  lcd.write(byte(0));
}

void clearValue(){
  for (int i = 16 - valueLength; i <= 16; i++){
        lcd.setCursor (i,cursorLayer);
        lcd.print(" ");
      }
      
  
}

So what is substring(0,1) looking at, I'm getting a bit lost?

The first character in the array starts at 0. The number is 1 digit long. The last number in the parens is the first character to not include.

The 1,3 part is looking for the characters at positions 1 and 2 (the two underscores).

Hi PaulS,

Thank you again for your reply and correcting me. It actually makes sense to me now, with how that part of the sketch works.

Dizzwold.

Hi,

I’ve now attempted to convert the sketch for a 20,4 Lcd display with some success.

The only problem I’ve found is if I go to the 4th string in Layer 0 and select “Item4”, It displays the 2 items “Item3.1.1” and “Item3.1.2”, but I seem to get some text wrapping on the 3rd line. I get 3 lots of double = symbols “double meaning one on top of another in one segment”, then to the far right of the display the #11. I believe this is the 1st string in Layer 1 “Rotations”, as it is the only item with the #11 as a starting value.

=== 11

Can someone offer some advice on how to stop this.

I think I need to change or add someting to the void lcdmenu.

/*
 * MARK VAN VEGGEL
 * ARDUINO LCD MENU V0.1
 * YOUTUBE.COM/AMPSOURCE
 * 
 */

#include <OneButton.h>
#include <Encoder.h>
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal.h>

LiquidCrystal  lcd(12, 11, 5, 4, 3, 2);

// ROTARY ENCODER
long oldPosition  = 0;
int encoderDTpin = 20;
int encoderCLKpin = 21;
Encoder myEnc(encoderDTpin, encoderCLKpin);

int buttonPin = 53;
OneButton button0(buttonPin, true);


// MENU ARRAY 
// First number in array = amount of menu items in root layer. Same goes for next layer. (Example: first value in array is 4. This means Layer 0 has 4 menu items. Layer 1 has only 2 menu items.)
// First number infront of menu text is the layer number that item points to. (Example: 1_Rotation points to Layer 1)
// Second number infront of menu text is the number of the value connected to that menu item in the values array. (Example: 15Direction points to position 5 in the values array)

String menu[] = {"4","1__Rotation","056Active","3__Watches","4__Item4",       // Layer 0
                 "3","10_Rotations","2__Direction","109Speed",                // Layer 1
                 "3","256Active","2__Item2.2","2__Item2.3",                    // Layer 2
                 "4","4__Watch 1","3__Watch 2","3__Watch 3","3__Watch 4",     // Layer 3
                 "2","4__Item3.1.1","4__Item3.1.2"};                          // Layer 4

// VALUES ARRAY
// TYPES, 1 numeric / 2 character value range
// 1 - type,value,increments,min,max
// 2 - type,starting value in options array,lowest value options array,max value options array
int values[] = {1,11,25,50,1450, //0-4 WATCH1
                2,2,0,2,         //5-8
                1,600,25,75,600, //9-13
                
                1,11,25,50,1450, //14-18 WATCH2
                2,2,0,2,         //19-22
                1,600,25,75,600, //23-27
                
                1,11,25,50,1450, //28-32 WATCH3
                2,2,0,2,         //33-36
                1,600,25,75,600, //37-41
                 
                1,11,25,50,1450, //42-46 WATCH4
                2,2,0,2,         //47-50
                1,600,25,75,600, //51-55
                
                2,4,3,4,         //56-59 WATCH1 ACTIVE
                2,4,3,4,         //60-63 WATCH2 ACTIVE
                2,4,3,4,         //64-67 WATCH3 ACTIVE
                2,4,3,4};        //68-71 WATCH4 ACTIVE
                
                
String options[] = {"CW","CCW","MIX",
                    "No","Yes"};                 

// Custom character for LCD.
byte cursor[8] = {
  0b10000,
  0b10000,
  0b01000,
  0b00110,
  0b01000,
  0b10000,
  0b10000,
  0b00000
};

byte watch[8] = {
  0b01110,
  0b01110,
  0b11011,
  0b10001,
  0b10001,
  0b11011,
  0b01110,
  0b01110
};


int currentType = 0;
int value = 0;
int numIncr = 0;
int Min = 0;
int Max = 0;

int currentLayer = 0;
int lastLayer = 99;
int currentLength = menu[0].toInt();
int currentPosition = 0;
int currentSelect = 1;
int currentChange = 0;
String currentPress = "";
float currentlcdLayerFloat = 0;
int currentlcdLayer = 0;
int lastlcdLayer = 0;
int lcdUpdated = 0;
int currentLine = 0;
int cursorLayer = 0;
int cursorPosition = 0;
int valueLength = 0;

/*
int homeScreen = 1;

String currentTime = "00:00:00";
String lastTime = "00:00:00";
String temptime = "00";
String currentDate = "";

int watch1active = values[56] - 3;
int watch2active = values[60] - 3;
int watch3active = values[64] - 3;
int watch4active = values[68] - 3;
int watch1rotations = 0;
int watch2rotations = 0;
int watch3rotations = 0;
int watch4rotations = 0;
int watch1left = values[1];
int watch2left = values[15];
int watch3left = values[29];
int watch4left = values[43];
String temprotationsleft = "";
*/


void setup() {
  Serial.begin(9600);

  // Map rotary button to actions single and doubleclick.
  button0.attachClick(singleClick);
  button0.attachDoubleClick(doubleClick);

  lcd.begin (20,4);
  lcd.home();

  // Create the custom character.
  lcd.createChar(0, cursor);
  lcd.createChar(1, watch);

  lcd.setCursor (2,0);  
  lcd.print("Watch Winder");
  delay(1000);
  clearLine(0);

  // Set the select cursor on the first line.
  cursorLayer = 0;
  lcd.setCursor (0,cursorLayer);  
  writeCursor();

}

void loop() {
    
    // Listen to button presses.
    button0.tick();

    // Listen if the rotary encoder moves.
    rotary_check();
    
    // Print the LCD menu.
    lcdMenu();

}

void singleClick() {

  // IF current layer number is the same as redirect layer number, we stay on the same layer
  // AND no item is selected in the menu
  // AND the selected item has a redirect to another layer
  if(currentLayer == menu[currentPosition + currentSelect].substring(0,1).toInt() && menu[currentPosition + currentSelect].substring(1,3) != "__" && currentPress == ""){
    
    currentPress = menu[currentPosition + currentSelect].substring(3);
    currentChange = menu[currentPosition + currentSelect].substring(1,3).toInt();
    
    Serial.println("Currentpress: " + currentPress);
    
    currentType = values[currentChange];
    value = values[currentChange + 1];

    // Type select change is numeric
    if(currentType == 1){
      Min = values[currentChange + 3];
      Max = values[currentChange + 4];
      numIncr = values[currentChange + 2];
      
      Serial.println("Currentvalue: " + String(value));
      Serial.println("Increments: " + String(numIncr));
      Serial.println("Min: " + String(Min));
      Serial.println("Max: " + String(Max));

      
      valueLength = String(value).length();
      
    // Type select change is character range
    } else if(currentType == 2){
      Min = values[currentChange + 2];
      Max = values[currentChange + 3];

      Serial.println("Currentvalue: " + options[value]);
      Serial.println("Options: ");
      
      for (int i=Min; i <= Max; i++){
        Serial.println(options[i]);
      }

      valueLength = options[value].length();

    }

    clearSelect();
    cursorPosition = 19 - valueLength;
    lcd.setCursor(cursorPosition,cursorLayer);
    writeCursor();

  // ELSE IF something is selected in the menu, and the button is pressed again. We deselect the item.  
  } else if(currentPress != ""){
    
    currentValues();
    currentPress = "";
    currentChange = 0;

    clearChar(cursorPosition,cursorLayer);
    lcd.setCursor(0,cursorLayer);
    writeCursor();
    
  // IF nothing is selected and nothing will be deselected, move to the redirect layer listed in the first position of the array.  
  } else {
    
    currentLayer = menu[currentPosition + currentSelect].substring(0,1).toInt();
    currentSelect = 1;
    currentlcdLayerFloat = 0;
    currentlcdLayer = 0;
    lastlcdLayer = 0;

    clearSelect();
    cursorLayer = 0;
    lcd.setCursor (0,cursorLayer);  
    writeCursor();
    
  }
  
}

Dizzwold.

// Reset to layer 0.
void doubleClick() {
  if(currentLayer == 0){
    currentLayer = 0;
    currentSelect = 1;
    currentlcdLayerFloat = 0;
    currentlcdLayer = 0;
    lastlcdLayer = 0;
  
    clearSelect();
    cursorLayer = 0;
    
    lcd.clear();
    lcdUpdated = 0;
  }
  if(currentLayer != 0){
    currentLayer = 0;
    currentSelect = 1;
    currentlcdLayerFloat = 0;
    currentlcdLayer = 0;
    lastlcdLayer = 0;
  
    clearSelect();
    cursorLayer = 0;
    lcd.setCursor (0,cursorLayer);  
    writeCursor();
  }
}

void rotary_check(){

  // Constantly read the position of the rotary encoder
  long newPosition = myEnc.read() / 4;

  // IF the new position of the encoder is different then the old position
  if (newPosition != oldPosition) {
       
    // IF nothing has been selected in the menu
    if(currentPress == ""){

      if(newPosition > oldPosition && currentSelect < currentLength){
        clearSelect();
        currentSelect++;
        currentlcdLayerFloat = currentlcdLayerFloat + 0.5;
        currentlcdLayer = currentlcdLayerFloat;

        cursorSelect();
        
        lcd.setCursor (0,cursorLayer);  
        writeCursor();
        Serial.println("> " + menu[currentPosition + currentSelect].substring(3));

      } else if (newPosition < oldPosition && currentSelect != 1){
        clearSelect();
        currentSelect--;
        currentlcdLayerFloat = currentlcdLayerFloat - 0.5;
        currentlcdLayer = currentlcdLayerFloat;

        cursorSelect();

        lcd.setCursor (0,cursorLayer);  
        writeCursor();
        Serial.println("> " + menu[currentPosition + currentSelect].substring(3));
  
      }
      
      if(lastlcdLayer != currentlcdLayer){
        lcdUpdated = 0;
      }
      
      lastlcdLayer = currentlcdLayer;      
      oldPosition = newPosition;



    // IF something has been selected in the menu, we are going to change the value of a menu item.
    } else if(currentPress != ""){

      
      lcd.setCursor(cursorPosition + 1,cursorLayer);
      // IF the new position is HIGHER then the old position
      if(newPosition > oldPosition){
        clearChar(cursorPosition,cursorLayer);
        
        // IF the selected item has a change type that is NUMERIC.
        // AND the MAXIMUM of the selected item has not been reached. 
        // We change the value UPWARDS with the icrements listed in the array.
        // SAME applies for next ELSE IF, just for the change type character. And the next ELSE IF we just go down.
        if(currentType == 1 && value < Max){
            value = value + numIncr;
            values[currentChange + 1] = value;
            Serial.println(value);

            clearValue();
            valueLength = String(value).length();
            cursorPosition = 19 - valueLength;
            lcd.setCursor(cursorPosition + 1,cursorLayer);
            lcd.print(value);
          }

        if(currentType == 2 && value < Max){
            value++;
            values[currentChange + 1] = value;
            Serial.println(options[value]);

            clearValue();
            valueLength = options[value].length();
            cursorPosition = 19 - valueLength;
            lcd.setCursor(cursorPosition + 1,cursorLayer);
            lcd.print(options[value]);
          }
          lcd.setCursor(cursorPosition,cursorLayer);
          writeCursor();
        
      } else {
        clearChar(cursorPosition,cursorLayer);
        
        if(currentType == 1 && value > Min){
          value = value - numIncr;
          values[currentChange + 1] = value;
          Serial.println(value);

          clearValue();
          valueLength = String(value).length();
          cursorPosition = 19 - valueLength;
          lcd.setCursor(cursorPosition + 1,cursorLayer);
          lcd.print(value);
        }

        if(currentType == 2 && value > Min){
            value--;
            values[currentChange + 1] = value;
            Serial.println(options[value]);

            clearValue();
            valueLength = options[value].length();
            cursorPosition = 19 - valueLength;
            lcd.setCursor(cursorPosition + 1,cursorLayer);
            lcd.print(options[value]);
          }
          lcd.setCursor(cursorPosition,cursorLayer);
          writeCursor();
        
      }

      
        
      oldPosition = newPosition;
    } else {
      oldPosition = newPosition;
    }
    
    
    
  }
}

void lcdMenu(){
  if (lastLayer != currentLayer || lcdUpdated == 0){

    currentLength = menu[0].toInt();
    currentPosition = 0;
    
    for (int i=0; i < currentLayer; i++){
      currentPosition = (menu[currentPosition].toInt() + 1) + currentPosition;
      currentLength = menu[currentPosition].toInt();
    }

    clearMenu();
    int tempPosition = currentPosition;
    
    if(currentlcdLayer != 0){
      
      currentPosition = currentPosition + (currentlcdLayer * 0.5);
    }
    
    Serial.println("LCD-------------");

    for (int i=1; i <= 4; i++){
      lcd.setCursor (1,i - 1);  
      lcd.print(menu[currentPosition + i].substring(3));
      
      currentType = 0;
      value = values[menu[currentPosition + i].substring(1,3).toInt() + 1];
      String tempvalue = "";
      
      if(menu[currentPosition + i].substring(1,3) != "__" && menu[currentPosition + i].substring(1,3) != ""){
        currentType = values[menu[currentPosition + i].substring(1,3).toInt()];
        if(currentType == 1){
          lcd.setCursor (20 - String(value).length(),i - 1);
          lcd.print(value);
          tempvalue = " "+ String(value);
        }else if(currentType == 2){
          lcd.setCursor (20 - options[value].length(),i - 1);
          lcd.print(options[value]);
          tempvalue = " "+ options[value];
        } 
        
      }
      
      Serial.println(menu[currentPosition + i].substring(3) + tempvalue);
    }
    Serial.println("----------------");
    
    currentPosition = tempPosition;
    lastLayer = currentLayer;
    lcdUpdated = 1;
    
    /**
    Serial.println("Layer " + String(currentLayer + 1));
    Serial.println("Layer length " + String(currentLength));
    Serial.println("Layer position " + String(currentPosition));
    **/
    
  }
}


void currentValues(){

  Serial.println("Currentpress: " + currentPress);
  
  if(currentType == 1){
      Serial.println("Currentvalue: " + String(value));
      Serial.println("Increments: " + String(numIncr));
      Serial.println("Min: " + String(Min));
      Serial.println("Max: " + String(Max));
      
    } else if(currentType == 2){
      Serial.println("Currentvalue: " + options[value]);
      Serial.println("Options: ");
      
      for (int i=Min; i <= Max; i++){
        Serial.println(options[i]);
      }
    }
}

void clearChar(int charPosition, int line){
  lcd.setCursor (charPosition,line);
  lcd.print(" ");
}

void clearLine(int line){
  lcd.setCursor (0,line);
  lcd.print("                    ");
}

void clearMenu(){
  lcd.setCursor (1,0);
  lcd.print("                   ");
  lcd.setCursor (1,1);
  lcd.print("                   ");
  lcd.setCursor (1,2);
  lcd.print("                   ");
  lcd.setCursor (1,3);
  lcd.print("                   ");
}

void clearSelect(){
  lcd.setCursor (0,0);
  lcd.print(" ");
  lcd.setCursor (0,1);
  lcd.print(" ");
  lcd.setCursor (0,2);
  lcd.print(" ");
  lcd.setCursor (0,3);
  lcd.print(" ");
}

void cursorSelect(){
  switch (currentSelect){
          case 1:
            cursorLayer = 0;
            break;
          case 2:
            cursorLayer = 1;
            break;
          case 3:
            cursorLayer = 2;
            break;
          case 4:
            cursorLayer = 4;
            break;
          case 5:
            cursorLayer = 0;
            break;
          case 6:
            cursorLayer = 1;
            break;
        }
}

void writeCursor(){
  lcd.write(byte(0));
}

void clearValue(){
  for (int i = 20 - valueLength; i <= 20; i++){
        lcd.setCursor (i,cursorLayer);
        lcd.print(" ");
      }
      
  
}

Dizzwold.

dizzwold: String menu[] = {"4","1__Rotation","056Active","3__Watches","4__Item4",      // Layer 0

Dizzwold.

you've been around a while... at this point, do you really want to rely on using the String class?

Hi BulldogLowell,

Thank you for your reply.

Are your suggesting using char instead of string?

Dizzwold.

dizzwold: Are your suggesting using char arrays instead of string?

yes.

Have you not seen the advice here regarding the perils of using the String class?

Hi BulldogLowell,

So string menu[], would become char menu[]?

What about substring, would that become char*()?

The toint(), can these now be removed?

Dizzwold.

dizzwold: Hi BulldogLowell,

So string menu[], would become char menu[]?

What about substring, would that become char*()?

The toint(), can these now be removed?

Dizzwold.

If it were me, I'd use pointers to strings, and not have to over-size any of the arrays.

const char* menu[] = {
  "String1",
  "String2",
  "StringEct...",
};

Hi BulldogLowell,

Would I be correct to get rid of the toint()'s, and instead of using substring(), use char*() to point to the string. Just trying to find what I need to substitute with what.

Dizzwold.

dizzwold: Hi BulldogLowell,

Would I be correct to get rid of the toint()'s, and instead of using substring(), use char*() to point to the string. Just trying to find what I need to substitute with what.

Dizzwold.

I don't know what you mean...

why would you want to convert a string constant to an integer?

It is probably well past your time to read and understand all about C strings. There is plenty of information out their on the inter-webs.

if you have a specific question... ask away!