LCD Encoder Menu

Hi all,

I've been working with a sktch writen by Vladimir Ronamov found here;

The sketch was writen for 4 button swtches and I'm trying to re-write it to use an encoder.

So-far I've managed to get the encoder to scroll through the menu's, but I'm struggling to find a way to enter the values and change them.

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#include <Encoder.h>
Encoder myEnc(20, 21);

//Input & Button Logic
const int numOfInputs = 3;
const int inputPins[numOfInputs] = {20, 21, 53};
int inputState[numOfInputs];
int lastInputState[numOfInputs] = {LOW, LOW, LOW};
bool inputFlags[numOfInputs] = {LOW, LOW, LOW};
long lastDebounceTime[numOfInputs] = {0, 0, 0};
long debounceDelay = 5;
int backlight_pin10 = 10;

//LCD Menu Logic
const int numOfScreens = 10;
int currentScreen = 0;
const char* screens[numOfScreens][2] = {{"1Motor Voltage", "Volts"}, {"2Motor Current", "Amps"},
  {"3Motor Rated HP", "HP"}, {"4Overload Temp.", "degC"}, {"5Accel Time", "Secs"}, {"6Restart Time", "Mins"},
  {"7Analog Out. Curr.", "mA"}, {"8Input Temp.", "degC"}, {"9Run Time", "Hours"}, {"10Start Times", "times"}
};
int button = 53;
int led = 13;
int status = false;
int parameters[numOfScreens];

void setup() {
  pinMode(backlight_pin10, OUTPUT);
  analogWrite(10, 200);
  for (int i = 0; i < numOfInputs; i++) {
    pinMode(inputPins[i], INPUT);
    digitalWrite(inputPins[i], HIGH); // pull-up 20k
  }
  pinMode(led, OUTPUT);
  pinMode(button, INPUT_PULLUP); // set the internal pull up resistor, unpressed button is HIGH
  Serial.begin(9600);
  lcd.begin(20, 4);
}

long old = 0;

void loop() {
  setInputFlags();
  resolveInputFlags();

}

void setInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    int reading = digitalRead(inputPins[i]);
    if (reading != lastInputState[i]) {
      lastDebounceTime[i] = millis();
      //Serial.println(reading);
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != inputState[i]) {
        inputState[i] = reading;
        if (inputState[i] == HIGH) {
          inputFlags[i] = HIGH;
        }
      }
    }
    lastInputState[i] = reading;
  }
}

void resolveInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    if (inputFlags[i] == HIGH) {
      inputAction(i);
      inputFlags[i] = LOW;
      printScreen();
    }
  }
}

void inputAction(int input)
{
  if (digitalRead(button) == true)
  {
    status = !status;
    digitalWrite(led, status);
    Serial.print(status);
  }

  delay(50); // keeps a small delay
  long newP = myEnc.read() / 4;
  {
    if (newP < old)
    {
      old = newP;
      if (currentScreen == 0 && 1)
      {

        currentScreen = numOfScreens - 1;
      }
      else
      {
        currentScreen--;
      }
    }
    if (newP > old)
    {
      old = newP;
      if (currentScreen == numOfScreens - 1)
      {
        currentScreen = 0;
      }
      else
      {
        currentScreen++;
      }
    }
  }
  if (digitalRead(button) == false);
  {
    status = !status;
    delay(50); // keeps a small delay
    {
      if (newP < old)
        old = newP;
      {
        parameterChange(1);
      }
      if (newP > old)
        old = newP;
      {
        parameterChange(0);
      }
    }
  }
}

void parameterChange(int key) {
  if (key == 0) {
    parameters[currentScreen]++;
  } else if (key == 1) {
    parameters[currentScreen]--;
  }
}

void printScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(screens[currentScreen][0]);
  lcd.setCursor(0, 1);
  lcd.print(parameters[currentScreen]);
  lcd.print(" ");
  lcd.print(screens[currentScreen][1]);
}

I would like to ask for some guidance.

Dizzwold.

You need a variable to know which mode you're in: scrolling through the menu item names or, scrolling through a particular menu item's parameters/values. A pushbutton (possibly the one integrated into the encoder) set up to give a push on-push off function would work but any switch would do.

This mode flag then directs the code to execute or ignore one or the other of the two choices. Things get more complicated if you have multi-level menus.

Hi dougp,

Thank you for the reply.

Sorry I forgot to state that I wish to use the intergrated encoder switch and what you have suggested is exactly what I was trying to achieve.

I wrote the void inputactions to try to do that. Is this where I should have the variable to select which mode to change?

I've tried it in it's own void and called it in the void loop, but that didn't work either, it just went mad, as you would expect.

Please excuse my lack of coding knowledge.

Dizzwold.

dizzwold:
Sorry I forgot to state that I wish to use the intergrated encoder switch and what you have suggested is exactly what I was trying to achieve.

I wrote the void inputactions to try to do that. Is this where I should have the variable to select which mode to change?

I've tried it in it's own void and called it in the void loop, but that didn't work either, it just went mad, as you would expect.

First, let's clear up a terminology issue. 'void' is not the name of a function, it's a keyword - read about it here. The word 'loop' *after * void, as in 'void loop()' is the function name.

Secondly, this bit of code:

void inputAction(int input)
{
  if (digitalRead(button) == true)
  {
    status = !status;
    digitalWrite(led, status);
    Serial.print(status);
  }

The use of *digitalRead(button) == true *directly reads the button state from the input pin, after you have already debounced and determined its state in setInputFlags. Also, the state will follow the input, with a slight debouncing delay. Since the input is normally HIGH due to the pullups this code will be executed continuously until the button is pressed. What you want here is a one time action to change the state of status. Use instead:

inputFlags[2]  // the position of the button in the input flags array

It's clunky but this corresponds to the array location for the pushbutton which will be high for only one scan through the code when the button is released.

Also, in resolveInputs() you're passing the loop variable (i) as a parameter to inputActions but never using it. In order to distinguish what should happen in inputActions the basic structure should be something like:

if(input == 0){
// do some stuff 
}
else if(input == 1){
  // do other stuff
}
else if(input == 2){
  // do still more other stuff - like change the state of 'status', update the display, and so on
  }
// et cetera, et cetera, et cetera

This way the function can act appropriately on the passed value.

Hi dougp,

Thank you for your reply,

Also, in resolveInputs() you're passing the loop variable (i) as a parameter to inputActions but never using it.

I see what you mean, and i have corrected this.

inputFlags[2] // the position of the button in the input flags array

Is this pointing to the second part of the array "the values"? Then if it was "[1]" it would be pointing to the first part of the array "1motor voltage, 2 motor current etc"?

I'm not sure how to use this correctly, where to use it and how to relate it to the intergrated button / switch of the encoder, apart from "if (input == 2)"

I've tried this below, but another fail;

if (input == 0)
    {
      inputFlags[1];
 else if (input == 1)
    {
      inputFlags[1];
else if (input == 2);
    {
      inputFlags[2];

Here's what the sketch is currently;

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#include <Encoder.h>
Encoder myEnc(20, 21);

//Input & Button Logic
const int numOfInputs = 3;
const int inputPins[numOfInputs] = {20, 21, 53};
int inputState[numOfInputs];
int lastInputState[numOfInputs] = {LOW, LOW, LOW};
bool inputFlags[numOfInputs] = {LOW, LOW, LOW};
long lastDebounceTime[numOfInputs] = {0, 0, 0};
long debounceDelay = 10;
int backlight_pin10 = 10;

//LCD Menu Logic
const int numOfScreens = 10;
int currentScreen = 0;
const char* screens[numOfScreens][2] = {{"1Motor Voltage", "Volts"}, {"2Motor Current", "Amps"},
  {"3Motor Rated HP", "HP"}, {"4Overload Temp.", "degC"}, {"5Accel Time", "Secs"}, {"6Restart Time", "Mins"},
  {"7Analog Out. Curr.", "mA"}, {"8Input Temp.", "degC"}, {"9Run Time", "Hours"}, {"10Start Times", "times"}
};
int button = 53;
int led = 13;
int status = false;
int parameters[numOfScreens];

void setup() {
  pinMode(backlight_pin10, OUTPUT);
  analogWrite(10, 200);
  for (int i = 0; i < numOfInputs; i++) {
    pinMode(inputPins[i], INPUT);
    digitalWrite(inputPins[i], HIGH); // pull-up 20k
  }
  pinMode(led, OUTPUT);
  pinMode(button, INPUT_PULLUP); // set the internal pull up resistor, unpressed button is HIGH
  Serial.begin(9600);
  lcd.begin(20, 4);
}

long old = 0;

void loop() {
  setInputFlags();
  resolveInputFlags();

}

void setInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    int reading = digitalRead(inputPins[i]);
    if (reading != lastInputState[i]) {
      lastDebounceTime[i] = millis();
      //Serial.println(reading);
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != inputState[i]) {
        inputState[i] = reading;
        if (inputState[i] == HIGH) {
          inputFlags[i] = HIGH;
        }
      }
    }
    lastInputState[i] = reading;
  }
}

void resolveInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    if (inputFlags[i] == HIGH) {
      inputAction(i);
      inputFlags[i] = LOW;
      printScreen();
    }
  }
}

void inputAction(int input)
{
  long newP = myEnc.read() / 4;
  {
    if (input == 0)
    {
      if (newP < old)
      {
        old = newP;
        if (currentScreen == 0 && 1)
        {
          currentScreen = numOfScreens - 1;
        }
        else
        {
          currentScreen--;
        }
      }
    }
    else if (input == 1)
    {
      if (newP > old)
      {
        old = newP;
        if (currentScreen == numOfScreens - 1)
        {
          currentScreen = 0;
        }
        else
        {
          currentScreen++;
        }
      }
    }

    else if (input == 2);
    {
      if (newP < old)
        old = newP;
      {
        parameterChange(1);
      }
      if (newP > old)
        old = newP;
      {
        parameterChange(0);
      }
    }
  }
}




void parameterChange(int key) {
  if (key == 0) {
    parameters[currentScreen]++;
  } else if (key == 1) {
    parameters[currentScreen]--;
  }
}

void printScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(screens[currentScreen][0]);
  lcd.setCursor(0, 1);
  lcd.print(parameters[currentScreen]);
  lcd.print(" ");
  lcd.print(screens[currentScreen][1]);
}

Dizzwold.

inputFlags[2]  // the position of the button in the input flags array

Is this pointing to the second part of the array "the values"? Then if it was "[1]" it would be pointing to the first part of the array "1motor voltage, 2 motor current etc"?

No. Toss that, it was a poor example.

You have five arrays holding values dealing with the three inputs. Each array element zero has to do with some value or state pertaining to the input on pin 20. The others follow in succession. Element one deals with pin 21, etc.

Imagine that you refer to element zero with a name like 'encoderCLK' instead. Every place you would put a 0 inside square brackets to refer to the first element , substitute encoderCLK.

Now you can think of the various states of input 20 as;

inputState[encoderClk]
lastDebounceTime[encoderCLK]

// etc.

To toggle the menu variable the input of interest here is :

inputflags[encoderSW]

since this variable is true for only one scan - scan being a shorthand way of thinking of one pass through loop().

Now, releasing the encoder pushbutton (meaning 'toggle mode') sends a value of '2' to inputActions. There the pushbutton gets tangled up with the encoder values. Forget that. The only thing that a value of '2' sent to inputActions should do is toggle the state of a variable which determines whether you're manipulating the menu text or manipulating the values associated with a menu selection.

Create a variable to do this and prove it works as it should by printing its value on the serial monitor. Since, at this point, there are only two alternatives for the pushbutton action you could use a bool variable.

Hi dougp,

Thank you again for your reply.

I can only get the led to toggle on and off with the encoder switch, but not the values.

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#include <Encoder.h>
Encoder myEnc(20, 21);

//Input & Button Logic
const int numOfInputs = 3;
const int inputPins[numOfInputs] = {20, 21, 53};
int inputState[numOfInputs];
int lastInputState[numOfInputs] = {LOW, LOW, LOW};
bool inputFlags[numOfInputs] = {LOW, LOW, LOW};
long lastDebounceTime[numOfInputs] = {0, 0, 0};
long debounceDelay = 10;
int backlight_pin10 = 10;

//LCD Menu Logic
const int numOfScreens = 10;
int currentScreen = 0;
const char* screens[numOfScreens][2] = {{"1Motor Voltage", "Volts"}, {"2Motor Current", "Amps"},
  {"3Motor Rated HP", "HP"}, {"4Overload Temp.", "degC"}, {"5Accel Time", "Secs"}, {"6Restart Time", "Mins"},
  {"7Analog Out. Curr.", "mA"}, {"8Input Temp.", "degC"}, {"9Run Time", "Hours"}, {"10Start Times", "times"}
};
int button = 53;
int led = 13;
int status = false;
int parameters[numOfScreens];

void setup() {
  pinMode(backlight_pin10, OUTPUT);
  analogWrite(10, 200);
  for (int i = 0; i < numOfInputs; i++) {
    pinMode(inputPins[i], INPUT);
    digitalWrite(inputPins[i], HIGH); // pull-up 20k
  }
  pinMode(led, OUTPUT);
  pinMode(button, INPUT_PULLUP); // set the internal pull up resistor, unpressed button is HIGH
  Serial.begin(9600);
  lcd.begin(20, 4);
}

long old = 0;

void loop() {
  setInputFlags();
  resolveInputFlags();

}

void setInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    int reading = digitalRead(inputPins[i]);
    if (reading != lastInputState[i]) {
      lastDebounceTime[i] = millis();
      //Serial.println(reading);
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != inputState[i]) {
        inputState[i] = reading;
        if (inputState[i] == HIGH) {
          inputFlags[i] = HIGH;
        }
      }
    }
    lastInputState[i] = reading;
  }
}

void resolveInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    if (inputFlags[i] == HIGH) {
      inputAction(i);
      inputFlags[i] = LOW;
      printScreen();
    }
  }
}

void inputAction(int input)
{
  long newP = myEnc.read() / 4;
  {
    if (input == 0)
    {
      if (newP < old)
      {
        old = newP;
        if (currentScreen == 0 && 1)
        {
          currentScreen = numOfScreens - 1;
        }
        else
        {
          currentScreen--;
        }
      }
    }
    else if (input == 1)
    {
      if (newP > old)
      {
        old = newP;
        if (currentScreen == numOfScreens - 1)
        {
          currentScreen = 0;
        }
        else
        {
          currentScreen++;
        }
      }
    }

    else if (input == 2);
    {
      status = ! status;
      digitalWrite(led, status);
      inputFlags[2];
      if (newP < old)
        old = newP;
      {
        parameterChange(1);
      }
      if (newP > old)
        old = newP;
      {
        parameterChange(0);
      }
    }
  }
}




void parameterChange(int key) {
  if (key == 0) {
    parameters[currentScreen]++;
  } else if (key == 1) {
    parameters[currentScreen]--;
  }
}

void printScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(screens[currentScreen][0]);
  lcd.setCursor(0, 1);
  lcd.print(parameters[currentScreen]);
  lcd.print(" ");
  lcd.print(screens[currentScreen][1]);
}

Dizzwold.

Reference code:

else if (input == 2);
    {
      status = ! status;
      digitalWrite(led, status);
      inputFlags;
      if (newP < old)
        old = newP;
      {
        parameterChange(1);
      }
      if (newP > old)
        old = newP;
      {
        parameterChange(0);
      }

Where does the variable 'status' come from? The encoder? It's not a user-declared variable because I see it highlighted in red in my IDE. I'm not certain you want to be changing that.

And, as stated above, since the encoder pushbutton is associated with the value '2' in the arrays, and the only function of the pushbutton is to go on and off, the only activity in if(input == 2) should be toggling the menu select variable.

Once you can control this variable, let's call it menuToggle, the two options become:

  • if menuToggle is false the encoder direction signal(s) changes which menu option is displayed.
  • if menuToggle is true, the encoder direction signal(s) changes a parameter value

In both cases a decision will have to be made whether to stop the selection at the limits or to wrap around to the first/last item.

Hi,

I'm still not grasping this.

My understanding is, I'm checking for input 2 the encoder switch on pin 53, if that is toggled then check the encoder position for up or down movement if so change the value or parameter?

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#include <Encoder.h>
Encoder myEnc(20, 21);

//Input & Button Logic
const int numOfInputs = 3;
const int inputPins[numOfInputs] = {20, 21, 53};
int inputState[numOfInputs];
int lastInputState[numOfInputs] = {LOW, LOW, LOW};
bool inputFlags[numOfInputs] = {LOW, LOW, LOW};
long lastDebounceTime[numOfInputs] = {0, 0, 0};
long debounceDelay = 10;
int backlight_pin10 = 10;

//LCD Menu Logic
const int numOfScreens = 10;
int currentScreen = 0;
const char* screens[numOfScreens][2] = {{"1Motor Voltage", "Volts"}, {"2Motor Current", "Amps"},
  {"3Motor Rated HP", "HP"}, {"4Overload Temp.", "degC"}, {"5Accel Time", "Secs"}, {"6Restart Time", "Mins"},
  {"7Analog Out. Curr.", "mA"}, {"8Input Temp.", "degC"}, {"9Run Time", "Hours"}, {"10Start Times", "times"}
};
int button = 53;
int led = 13;
int status = false;
int parameters[numOfScreens];

void setup() {
  pinMode(backlight_pin10, OUTPUT);
  analogWrite(10, 200);
  for (int i = 0; i < numOfInputs; i++) {
    pinMode(inputPins[i], INPUT);
    digitalWrite(inputPins[i], HIGH); // pull-up 20k
  }
  pinMode(led, OUTPUT);
  pinMode(button, INPUT_PULLUP); // set the internal pull up resistor, unpressed button is HIGH
  Serial.begin(9600);
  lcd.begin(20, 4);
}

long old = 0;

void loop() {
  setInputFlags();
  resolveInputFlags();

}

void setInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    int reading = digitalRead(inputPins[i]);
    if (reading != lastInputState[i]) {
      lastDebounceTime[i] = millis();
      //Serial.println(reading);
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != inputState[i]) {
        inputState[i] = reading;
        if (inputState[i] == HIGH) {
          inputFlags[i] = HIGH;
        }
      }
    }
    lastInputState[i] = reading;
  }
}

void resolveInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    if (inputFlags[i] == HIGH) {
      inputAction(i);
      inputFlags[i] = LOW;
      printScreen();
    }
  }
}

void inputAction(int input)
{
  long newP = myEnc.read() / 4;
  {
    if (input == 0)
    {
      if (newP < old)
      {
        old = newP;
        if (currentScreen == 0 && 1)
        {
          currentScreen = numOfScreens - 1;
        }
        else
        {
          currentScreen--;
        }
      }
    }
    else if (input == 1)
    {
      if (newP > old)
      {
        old = newP;
        if (currentScreen == numOfScreens - 1)
        {
          currentScreen = 0;
        }
        else
        {
          currentScreen++;
        }
      }
    }

    else if (input == 2);
    {
      status = ! status;
      digitalWrite(led, status);
      if (newP < old)
        old = newP;
      {
        parameterChange(1);
      }
      if (newP > old)
        old = newP;
      {
        parameterChange(0);
      }
    }
  }
}




void parameterChange(int key) {
  if (key == 0) {
    parameters[currentScreen]++;
  } else if (key == 1) {
    parameters[currentScreen]--;
  }
}

void printScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(screens[currentScreen][0]);
  lcd.setCursor(0, 1);
  lcd.print(parameters[currentScreen]);
  lcd.print(" ");
  lcd.print(screens[currentScreen][1]);
}

Dizzwold.

 status = ! status;

What is status? Is it part of the encoder library? What does it indicate the status of?

Hi dougp,

No it's not part of the encoder library.

I cant remember where I found the example, but it is basically as follows for toggling an LED;

int button = 53;
int led = 13;
int status = false;

void setup()
{
  Serial.begin (9600);
  pinMode(led, OUTPUT);
  pinMode(button, INPUT_PULLUP); // set the internal pull up resistor, unpressed button is HIGH
}

void loop()
{
  //a) if the button is not pressed the false status is reversed by !status and the LED turns on
  //b) if the button is pressed the true status is reveresed by !status and the LED turns off

  if (digitalRead(button) == true)
  {
    status = !status;
    digitalWrite(led, status);
    Serial.print(status);
  }
  while (digitalRead(button) == true);
  delay(50); // keeps a small delay
}

Dizzwold.

Well, as I say, 'status' is colored red in my IDE. That means it's a keyword for *something *and you're changing that unknown something. To what effect who can say?

Give 'status' some different, meaningful name and make it a Boolean - since it's only going to have two states.

 else if (input == 2);
    {
      newNameForStatus = ! newNameForStatus;
      digitalWrite(led, status);
//      if (newP < old)   // disable all the code from here
//        old = newP;
//      {
//        parameterChange(1);
//      }
//      if (newP > old)
//        old = newP;
//      {
//        parameterChange(0);  //  to here
      }
    }

dougp:
Well, as I say, 'status' is colored red in my IDE. That means it's a keyword for *something * and you're changing that unknown something. To what effect who can say?

It's not a keyword. More likely that somebody decided to put it in their keywords.txt file for some library that the OP might not even be using.

Delta_G:
It's not a keyword. More likely that somebody decided to put it in their keywords.txt file for some library that the OP might not even be using.

Thanks. I don't know enough to have even considered that. In any case, the amorphous 'status' could still be improved, IMHO.

Hi,

Thank you for your comments;

Well, as I say, 'status' is colored red in my IDE. That means it's a keyword for something and you're changing that unknown something. To what effect who can say?

@ dougp, at first I didn't realise what you ment, but have now changed "status to dizzwold". Thank you for pointing this out clearly.

It's not a keyword. More likely that somebody decided to put it in their keywords.txt file for some library that the OP might not even be using.

@ Delta_G, Wow, you wouldn't think that some random word in a library txt file would effect a sketch that doesn't use said library! Thank you for this insight.

Before moving on to being able to change the values or parameters, one thing I have noticed is when loading the sketch /start /boot, the LED is high and on, push the encoder switch and the change of state works fine, on, off, on, off etc, but if I rotate the encoder really fast this also sometimes changes the state of the LED. Is this just because these are cheap encoders?

Here's the current sketch;

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#include <Encoder.h>
Encoder myEnc(20, 21);

//Input & Button Logic
const int numOfInputs = 3;
const int inputPins[numOfInputs] = {20, 21, 53};
int inputState[numOfInputs];
int lastInputState[numOfInputs] = {LOW, LOW, LOW};
bool inputFlags[numOfInputs] = {LOW, LOW, LOW};
long lastDebounceTime[numOfInputs] = {0, 0, 0};
long debounceDelay = 10;
int backlight_pin10 = 10;

//LCD Menu Logic
const int numOfScreens = 10;
int currentScreen = 0;
const char* screens[numOfScreens][2] = {{"1Motor Voltage", "Volts"}, {"2Motor Current", "Amps"},
  {"3Motor Rated HP", "HP"}, {"4Overload Temp.", "degC"}, {"5Accel Time", "Secs"}, {"6Restart Time", "Mins"},
  {"7Analog Out. Curr.", "mA"}, {"8Input Temp.", "degC"}, {"9Run Time", "Hours"}, {"10Start Times", "times"}
};
int button = 53;
int led = 13;
int dizzwold = false;
int parameters[numOfScreens];

void setup() {
  pinMode(backlight_pin10, OUTPUT);
  analogWrite(10, 200);
  for (int i = 0; i < numOfInputs; i++) {
    pinMode(inputPins[i], INPUT);
    digitalWrite(inputPins[i], HIGH); // pull-up 20k
  }
  pinMode(led, OUTPUT);
  pinMode(button, INPUT_PULLUP); // set the internal pull up resistor, unpressed button is HIGH
  Serial.begin(9600);
  lcd.begin(20, 4);
}

long old = 0;

void loop() {
  setInputFlags();
  resolveInputFlags();

}

void setInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    int reading = digitalRead(inputPins[i]);
    if (reading != lastInputState[i]) {
      lastDebounceTime[i] = millis();
      //Serial.println(reading);
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != inputState[i]) {
        inputState[i] = reading;
        if (inputState[i] == HIGH) {
          inputFlags[i] = HIGH;
        }
      }
    }
    lastInputState[i] = reading;
  }
}

void resolveInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    if (inputFlags[i] == HIGH) {
      inputAction(i);
      inputFlags[i] = LOW;
      printScreen();
    }
  }
}

void inputAction(int input)
{
  long newP = myEnc.read() / 4;
  {
    if (input == 0)
    {
      if (newP < old)
      {
        old = newP;
        if (currentScreen == 0 && 1)
        {
          currentScreen = numOfScreens - 1;
        }
        else
        {
          currentScreen--;
        }
      }
    }
    else if (input == 1)
    {
      if (newP > old)
      {
        old = newP;
        if (currentScreen == numOfScreens - 1)
        {
          currentScreen = 0;
        }
        else
        {
          currentScreen++;
        }
      }
    }

    else if (input == 2);
    {
      dizzwold = ! dizzwold;
      digitalWrite(led, dizzwold);
      /*if (newP < old)
        old = newP;
      {
        parameterChange(1);
      }
      if (newP > old)
        old = newP;
      {
        parameterChange(0);
      }*/
    }
  }
}




void parameterChange(int key) {
  if (key == 0) {
    parameters[currentScreen]++;
  } else if (key == 1) {
    parameters[currentScreen]--;
  }
}

void printScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(screens[currentScreen][0]);
  lcd.setCursor(0, 1);
  lcd.print(parameters[currentScreen]);
  lcd.print(" ");
  lcd.print(screens[currentScreen][1]);
}

Dizzwold.

Hi,

Something else I've noticed is from the serial print.

Can someone advise me a better way to read the reading's of the encoder, so the serial monitor will print the direction or the switch and then the reading "xxxx", for example;

CW xxxx
CCW xxxx
SW xx

What I've noticed with the current serial print "I know, not the best", it starts with "1111", on a CW rotation of 1 detent I get "0011", on a CCW rotation of 1 detent I get "0011" and on the push of the switch I get "01"
So I'm getting 4 reading's with each rotation, but only 2 reading's with a push of the switch. So I've tried the skatch by removing the divide by 4;

long newP = myEnc.read() / 4;

To;

long newP = myEnc.read();

This seem's to still work correctly for the rotation of the encoder and the switch to toggle the LED, allthough with rotating the encoder a speed still sometimes changes the LED state.

I think a better serial print would help.

I've also commented-out the button pin 53 and it's pin mode as this may also be causing conflicts;

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#include <Encoder.h>
Encoder myEnc(20, 21);

//Input & Button Logic
const int numOfInputs = 3;
const int inputPins[numOfInputs] = {20, 21, 53};
int inputState[numOfInputs];
int lastInputState[numOfInputs] = {LOW, LOW, LOW};
bool inputFlags[numOfInputs] = {LOW, LOW, LOW};
long lastDebounceTime[numOfInputs] = {0, 0, 0};
long debounceDelay = 10;
int backlight_pin10 = 10;

//LCD Menu Logic
const int numOfScreens = 10;
int currentScreen = 0;
const char* screens[numOfScreens][2] = {{"1Motor Voltage", "Volts"}, {"2Motor Current", "Amps"},
  {"3Motor Rated HP", "HP"}, {"4Overload Temp.", "degC"}, {"5Accel Time", "Secs"}, {"6Restart Time", "Mins"},
  {"7Analog Out. Curr.", "mA"}, {"8Input Temp.", "degC"}, {"9Run Time", "Hours"}, {"10Start Times", "times"}
};
//int button = 53;
int led = 13;
int dizzwold = false;
int parameters[numOfScreens];

void setup() {
  pinMode(backlight_pin10, OUTPUT);
  analogWrite(10, 200);
  for (int i = 0; i < numOfInputs; i++) {
    pinMode(inputPins[i], INPUT);
    digitalWrite(inputPins[i], HIGH); // pull-up 20k
  }
  pinMode(led, OUTPUT);
  //pinMode(button, INPUT_PULLUP); // set the internal pull up resistor, unpressed button is HIGH
  Serial.begin(9600);
  lcd.begin(20, 4);
}

long old = 0;

void loop() {
  setInputFlags();
  resolveInputFlags();

}

void setInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    int reading = digitalRead(inputPins[i]);
    if (reading != lastInputState[i]) {
      lastDebounceTime[i] = millis();
      Serial.println(reading);
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != inputState[i]) {
        inputState[i] = reading;
        if (inputState[i] == HIGH) {
          inputFlags[i] = HIGH;
        }
      }
    }
    lastInputState[i] = reading;
  }
}

void resolveInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    if (inputFlags[i] == HIGH) {
      inputAction(i);
      inputFlags[i] = LOW;
      printScreen();
    }
  }
}

void inputAction(int input)
{
  long newP = myEnc.read();
  {
    if (input == 0)
    {
      if (newP < old)
      {
        old = newP;
        if (currentScreen == 0 && 1)
        {
          currentScreen = numOfScreens - 1;
        }
        else
        {
          currentScreen--;
        }
      }
    }
    else if (input == 1)
    {
      if (newP > old)
      {
        old = newP;
        if (currentScreen == numOfScreens - 1)
        {
          currentScreen = 0;
        }
        else
        {
          currentScreen++;
        }
      }
    }

    else if (input == 2);
    {
      dizzwold = ! dizzwold;
      digitalWrite(led, dizzwold);
      if (newP < old)
        old = newP;
      {
        parameterChange(1);
      }
      if (newP > old)
        old = newP;
      {
        parameterChange(0);
      }
    }
  }
}




void parameterChange(int key) {
  if (key == 0) {
    parameters[currentScreen]++;
  } else if (key == 1) {
    parameters[currentScreen]--;
  }
}

void printScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(screens[currentScreen][0]);
  lcd.setCursor(0, 1);
  lcd.print(parameters[currentScreen]);
  lcd.print(" ");
  lcd.print(screens[currentScreen][1]);
}

Also once I'm in the input 2, I think I may need another "if or while statement for input 0 then check and input 1 then check?

Dizzwold.

dizzwold:
Before moving on to being able to change the values or parameters, one thing I have noticed is when loading the sketch /start /boot, the LED is high and on, push the encoder switch and the change of state works fine, on, off, on, off etc, but if I rotate the encoder really fast this also sometimes changes the state of the LED. Is this just because these are cheap encoders?

That's a new one! I suppose it's possible that the vibration set up by turning the knob very fast could cause the switch contacts to bounce without the button actually being pressed. Maybe it's a harmonic thing?

I used this one and have not seen that behavior. 'Course I wasn't testing for high RPM!

dizzwold:
Also once I'm in the input 2, I think I may need another "if or while statement for input 0 then check and input 1 then check?

To what end?

Hi dougp,

This is the unit I have;
http://www.everbuying.net/product695285.html

  • Edited Link

Also once I'm in the input 2, I think I may need another "if or while statement for input 0 then check and input 1 then check?

To what end?

I thought this may be needed once in the input 2 to state now expect an iput from 0 or 1 that would be an up or down.

Dizzwold.

Third time,

dougp:
the only activity in if(input == 2) should be toggling the menu select variable.

p.s. link above goes to a sign in screen