LCD menu with encoder

Is this LCD menu missing code for the encoder?
https://forum.arduino.cc/index.php?topic=53265.0

I plan to integrate this code with the previous, if needed. I thought
#define MOVECURSOR 1
#define MOVELIST 2
were pins for the encoder...

The function read_encoder() is not declared--because it doesn't exist!

So I inserted this:

int8_t read_encoder(){ // By Oleg Mazurov
// returns change in encoder state (-1,0,1) 
static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; 
static uint8_t old_AB = 0; 
/**/ 
old_AB <<= 2; //remember previous state 
old_AB |= ( ENC_PORT & 0x03 ); //add current state 
return ( enc_states[( old_AB & 0x0f )]); 
}

Still working on other bugs, but the "Simple universal LCD menu code" is missing something involving the encoder, right?

Why not take an encoder example and expand that to do your LCD menu?

econjack:
Why not take an encoder example and expand that to do your LCD menu?

I plan to integrate this code with the previous, if needed. I thought
#define MOVECURSOR 1
#define MOVELIST 2
were pins for the encoder...

Well, I know there is something that needs to be taken out. The LCD menu seemed to have some code for the encoder. Wish someone would've commented on if all that is needed is the read_encoder() routine.

/*
  Mega 2560 (Tools → Board)
*/
#include <LiquidCrystal.h>
#include <OneWire.h> 
#include <DallasTemperature.h>

#define MOVECURSOR 9  // constants for indicating whether cursor should be redrawn
#define MOVELIST 10
#define ENC_PORT PINC // https://arduino.stackexchange.com/a/21296/47871

const byte totRows = 4;  // total rows of LCD
const byte totCols = 20;  // total columns of LCD
unsigned long timeoutTime = 0;  // this is set and compared to millis to see when the user last did something.
const int menuTimeout = 10000; // time to timeout in a menu when user doesn't do anything.
const int a = 30;  // Paragon // 30 mm = 1.2" = 0.1'
const int b = 61;  // 0.2 foot paragon
const int c = 91;  // 0.3 foot paragon        
const int d = 122;  // 0.4 foot paragon
const int e = 152;  // 0.5 foot paragon
const int f = 183;  // 0.6 foot paragon        
const int g = 213;  // 0.7 foot paragon

unsigned long startMillis;
unsigned long currentMillis;
unsigned long currentMillisA;
unsigned long currentMillisD;
unsigned long currentMillis1;
const unsigned long period = 1000;
const byte txPin3 = 14;
const byte rxPin3 = 15;
const byte txPin2 = 16;
const byte rxPin2 = 17;
const byte pinAnalog = 2;
const byte pinDigital = 3;  // slide switch switch allows user to select (input) test
const byte pinOneWire = 4;
const byte oneWireBus = 5;
const byte pinAnalogW = 6;    // write (output) to test datalogger
const byte pinDigitalW = 7;   //                                  ----------------
const byte pinOneWireW = 8;   //                                  ----------------
const byte pinA = 22; // 1st hardware interrupt: CLK
const byte pinB = 26; // 2nd hardware interrupt: DT
volatile int diffFromParagon = 0;
volatile int inch = 0;
// https://forum.arduino.cc/index.php?topic=418692.0 // volatile
volatile byte aFlag = 0; // indicates when expected rising edge: pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // (opposite direction to when aFlag is set for pinB)
volatile byte encoderPos = 0; // encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; // last encoder position value, compares to current reading (to know when to print to the serial monitor)
volatile byte reading = 0; // stores direct values read from interrupt pins before checking to see if moved a whole detent

LiquidCrystal lcd(52, 50, 49, 47, 43, 41); // Arduino pins. Creates object.
    // LCD pin (RS enable D4 D5 D6 D7) parameters
OneWire oneWire(oneWireBus);
    // Setup a oneWire instance to communicate with any OneWire devices  
    // (not just Maxim/Dallas temperature ICs) 
DallasTemperature sensors(&oneWire);
    // Passes oneWireBus reference to Dallas Temperature. 

void setup() {
  startMillis = millis();  // initial start time
  
// LCD code:
  Serial.begin(9600);
  lcd.begin(20, 4);
  
// pinMode not needed for INPUT (default) operator
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(14, OUTPUT);
  pinMode(15, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  
// encoder code:
  pinMode(pinA, INPUT_PULLUP); // pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  pinMode(pinB, INPUT_PULLUP);
  attachInterrupt(0,PinA,RISING); // interrupt, looking for rising edge signal and 
  attachInterrupt(1,PinB,RISING); // executing the "PinX" Interrupt Service Routine (below)
  Serial1.begin(115200); // pin 18 (TX) & 19 (RX)
  digitalWrite(pinB, HIGH);

// oneWire sensors:
  sensors.begin();
}

void loop() {
  currentMillis = millis(); // milliseconds since start
  
  lcd.setCursor(0, 0);
  lcd.print("Welcome.");
  lcd.setCursor(0, 1);
  lcd.print("Ready to make a selection.");
  
  byte analogSelection = digitalRead(pinAnalog);   // 2
  byte digitalSelection = digitalRead(pinDigital); // 3
  byte oneWireSelection = digitalRead(pinOneWire); // 4

  if(analogSelection == HIGH)
  {
    currentMillisA = millis();
    if(currentMillisA - startMillis > 1000*60) // one min.
    {
      lcd.clear();
      lcd.setCursor(0, 0);        // 1st line
      lcd.print("Testing...");
      lcd.setCursor(0, 1);        // 2nd
      lcd.print("Analog inputs");
      lcd.setCursor(0, 2);        // 3rd
      lcd.print("Please wait.");     
      for(int pin = 0; pin < 8; pin++)
        for(int s = 63; s <= 255; s += 64)
          {          // PWM: 255 = 5V
            analogWrite(pin, s);      // sweeps 1.24, 2.5, 3.75 & 5V
            delay(500);
         }
    }
  }
  else if(digitalSelection == HIGH)
  {
    currentMillisD = millis();
    if(currentMillisD - startMillis > 1000*60) // one min.
    basicMenu();    /////////////////////////////////////////////////// TEST CONTIUNITY FOR THE SENSOR ON PIN 4 TO CHEDFKCK!@!#@
  }  
  else if(oneWireSelection == HIGH)
  {
    currentMillis1 = millis();
    if(currentMillis1 - startMillis > 1000*60); // one min.
    lcd.print("Testing temperatures...");
    sensors.requestTemperatures(); // gets temperature readings
    lcd.print("Temperature is: "); 
    lcd.print(sensors.getTempCByIndex(0)); // Why "byIndex"?  
   // "0" refers to the first IC on the wire
   // (You can have more than one DS18B20 on the same bus.)
  }
  else  // multiple inputs toggled at once, error message:
    lcd.print("Only select one test at a time.");
  // https://forum.arduino.cc/index.php?topic=223286.0  Maybe multiple things can happen...

   
  // Original encoder code (but it was incomple: had to read_encoder() routine
 static byte counter = 0;      //this variable will be changed by encoder input
 char tmpdata;
 tmpdata = read_encoder();

  // New encoder code (might conflict with original above)
  if(oldEncPos != encoderPos) 
  {
    Serial1.println(encoderPos);
    oldEncPos = encoderPos;
  }    
}

The rest of the code--the function definitions--is in the complete sketch attached.

sketch_jul17.ino (16.1 KB)

Well there's obviously this section:

// encoder code:
  pinMode(pinA, INPUT_PULLUP); // pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  pinMode(pinB, INPUT_PULLUP);
  attachInterrupt(0,PinA,RISING); // interrupt, looking for rising edge signal and 
  attachInterrupt(1,PinB,RISING); // executing the "PinX" Interrupt Service Routine (below)
  Serial1.begin(115200); // pin 18 (TX) & 19 (RX)
  digitalWrite(pinB, HIGH);

Which mentions two interrupt routines that look like they probably go with the encoder.

And if you look they are a bit bloated but it looks like they are meant to read an encoder in a slightly over-thought way.

Reading an encoder is WAY easier than all this mess. All you need to do in the ISR is compare the two pins to see if they are in the same state or not. If they are in the same state then you're rotating one way and if not then the other way. For the other pin it will be exactly the opposite in direction.

Otherwise I'm not real sure what exactly you are asking here.

BTW: Big ole bug hiding here:

 if(currentMillisA - startMillis > 1000*60)

60000 doesn't fit in an int but 1000 and 60 both do, so the math overflows. Use:

 if(currentMillisA - startMillis > 1000ul*60)

I inserted the menu routine which seemed like it was for an encoder. I found it was incomplete (also asked about on that arduino forum's page).

So I added read_encoder(). I then integrated the "bloated" code because I think the menu routine is still missing things. So now I believe I have unnecessary code.

...Good this is my full-time job right now; it's taking forever for what I thought was adding a simple LCD menu functionality. I'll spend more hours trying to understand what is needed for it to work...

Here's one that I have been using. It might have a little more than you want, but it is at least something to look at. It is sort of advanced so if you don't know the basics of C++ coding it's probably going to go over your head. It works well and has a small footprint, but you need to have some C++ knowledge to work with it.

https://forum.arduino.cc/index.php?topic=358066.0

I took a C++ course about three years ago, and now mostly use MATLAB during the semester. (I'm a senior electrical engineering student.)

I just want to complete this summer project, whatever the means. I decided to see if the current program works, and I'm getting a timeout error after uploading to the Mega 2560.

adamaero:
I decided to see if the current program works, and I'm getting a timeout error after uploading to the Mega 2560.

That's a communication issue between your computer and your board. That has nothing to do with the code. You need to get that fixed first. Try uploading the blink example until you can get that to work to show that you have good communication and then come back to the larger code.

Thanks! You are right. (Took IT half-a-day to install the driver.) Blink is now blinking the LCD screen too...just trying to get the LCD examples to work...

Delta_G:
but you need to have some C++ knowledge to work with it.GitHub - delta-G/REBL_UI: Simple menu UI for Arduino with 16x2 LCD, rotary encoder, and a single button

Rotary Encoder, Button, and LCD User interface with Menu - Exhibition / Gallery - Arduino Forum

I do not know C++ classes that well. Although, I am still trying. Is Time.h built in? I couldn't find that on your GitHub.

OK, well I found a Time library: GitHub - PaulStoffregen/Time: Time library for Arduino

I'm just trying to get the REBL_UI_Example to work:

MenuItem PROGMEM menuItems[] = {
 { "display", fun1   }  ,
 {"press", pressToExit   }  ,
 {"enter number", pickNumber   }  ,
 {"enter string", fourLetterWord   }  ,
 {"set time", enterTime   }  ,
 {"show time", showTime   }
};

fun1, pressToExit, etc

Says they're not declared.

Maybe you need forward declarations to those functions.

I've seen a few people say that they got this error, but I have yet to see anyone post the ACTUAL COMPLETE error message or the code as they have it entered. So I don't know what else you want me to do without any information.

Today, I put the function definitions before void loop(). No change. I think I'll try to make the half-baked program work instead. That one compiles, at least for me. Although, I really hoped to use a menu + rotary encoder library; I will be using this HMI for future projects.

Again, I'd love to help you out with that but you'd have to give me the error messages first. Seriously, how do you expect me to tell you how to fix an error if you won't show me the error messages?

I thought you saw it in the other post. Here it is again:

Arduino: 1.8.5 (Windows 7), Board: "Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"

REBL_UI_Example:17: error: 'fun1' was not declared in this scope

    "display", fun1   }

               ^

REBL_UI_Example:20: error: 'pressToExit' was not declared in this scope

    "press", pressToExit   }

             ^

REBL_UI_Example:23: error: 'pickNumber' was not declared in this scope

    "enter number", pickNumber   }

                    ^

REBL_UI_Example:26: error: 'fourLetterWord' was not declared in this scope

    "enter string", fourLetterWord   }

                    ^

REBL_UI_Example:29: error: 'enterTime' was not declared in this scope

    "set time", enterTime   }

                ^

REBL_UI_Example:32: error: 'showTime' was not declared in this scope

    "show time", showTime   }

                 ^

C:\Users\uraynara\Documents\Arduino\REBL_UI_Example\REBL_UI_Example.ino: In function 'boolean enterTime()':

REBL_UI_Example:161: error: 'now' was not declared in this scope

      timeSetTime = now();

                        ^

REBL_UI_Example:169: error: 'setTime' was not declared in this scope

        setTime(timeSetTime);

                           ^

REBL_UI_Example:175: error: 'now' was not declared in this scope

      displayTime(now());

                      ^

C:\Users\uraynara\Documents\Arduino\REBL_UI_Example\REBL_UI_Example.ino: In function 'boolean showTime()':

REBL_UI_Example:192: error: 'now' was not declared in this scope

  displayTime(now());  // display the running time

                  ^

exit status 1
'fun1' was not declared in this scope

Ok thanks. I'll look at it this weekend when I'm home.

[Wish I could give thumbs-up like on AllAboutCircuits]

Why did I probably needed to know C++ stuff?
Isn't it just changing/having the correct pinout, and changing the menu list to my own functions?

I feel like it's something to do with the Time library. I left one function (fun1), and this is the error message:

Arduino: 1.8.5 (Windows 7), Board: "Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"

C:\Users\uraynara\Documents\Arduino\libraries\REBL_UI-master\REBL_Functions.cpp: In function 'boolean inputTime(time_t&)':

C:\Users\uraynara\Documents\Arduino\libraries\REBL_UI-master\REBL_Functions.cpp:81:9: error: 'tmElements_t' does not name a type

  static tmElements_t tmElem;

         ^

C:\Users\uraynara\Documents\Arduino\libraries\REBL_UI-master\REBL_Functions.cpp:82:17: error: 'tmElem' was not declared in this scope

  breakTime(var, tmElem);

                 ^

C:\Users\uraynara\Documents\Arduino\libraries\REBL_UI-master\REBL_Functions.cpp:82:23: error: 'breakTime' was not declared in this scope

  breakTime(var, tmElem);

                       ^

C:\Users\uraynara\Documents\Arduino\libraries\REBL_UI-master\REBL_Functions.cpp:157:23: error: 'makeTime' was not declared in this scope

  var = makeTime(tmElem);

                       ^

C:\Users\uraynara\Documents\Arduino\libraries\REBL_UI-master\REBL_Functions.cpp: In function 'void displayTime(time_t)':

C:\Users\uraynara\Documents\Arduino\libraries\REBL_UI-master\REBL_Functions.cpp:175:2: error: 'tmElements_t' was not declared in this scope

  tmElements_t tmElem;

  ^

C:\Users\uraynara\Documents\Arduino\libraries\REBL_UI-master\REBL_Functions.cpp:176:19: error: 'tmElem' was not declared in this scope

  breakTime(aTime, tmElem);

                   ^

C:\Users\uraynara\Documents\Arduino\libraries\REBL_UI-master\REBL_Functions.cpp:176:25: error: 'breakTime' was not declared in this scope

  breakTime(aTime, tmElem);

                         ^

exit status 1
Error compiling for board Arduino/Genuino Mega or Mega 2560.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

Also, attached is the (just fun1) program.

REBL_UI_Example.ino (1.11 KB)

Another contributor sent me a pull request he says fixes it. You can either download it again or go through and find the three files that include Time.h and change them to include TimeLib.h instead.

This was written with an older version of the IDE and it appears as if the name of the Time library has changed.

Let me know if that works. I am not where I can try to compile it.