Go Down

Topic: Rotary encoder output to lcd 1602a (Read 15747 times) previous topic - next topic

williamjcoates

thanks Cattledog,
My confusion
Bill

williamjcoates

Okay, I have been doing some reading and looking at how the code works. I am now using different code which is behaving better than the previous. Its not exactly what I want but it will work until i learn how to modify it better. I added a lcd keypad shield to my mega 2560 board and configured the lcd display for my sketch. It works  as shown here in the serial monitor.
Start
Counter value: 0
Counter value: 1
Counter value: 2
Counter value: 3
Counter value: 4
Counter value: 5
Counter value: 6
Counter value: 7
Counter value: 8
Counter value: 9
Counter value: 10
Counter value: 11
Counter value: 12
Counter value: 13
Counter value: 14
Counter value: 15
Counter value: 16
Counter value: 17
Counter value: 18
Counter value: 19
Counter value: 20
Counter value: 21
Counter value: 22
Counter value: 23
Counter value: 24
But in my lcd the numbers are all together on the same line. and would look like this.
1011121314151617181920
I only have the numbers showing on the lcd display. How can I configure the lcd to display "Counter value" on the first line and only one number on the second. I think I have to put in a statement that instructs the lcd.print to add the next variable to the next line/new line. Are there sample sketches showing how to do this.
Bill

Paul__B

Three things:
  • If the words "Counter value" do not change, then write them to the display only once - possibly in setup().  Write them only if you need to change them.
  • There is no "carriage return" in the LCD.  Do not use println() - the fact that it actually compiles without error is a bug.  To return to the start of a line - or any other point - use setCursor.
  • There is no "erase line" function.  It is unwise to use clear() - it is too slow and if you use it often, you will generate annoying flicker.  You need to position the cursor where you need to change the value, write the new value and sufficient spaces to overwrite all of the previous value.  You may need to print leading spaces if the order of magnitude of the displayed value changes (reduces).



williamjcoates

Thanks Paul,
I will set the cursor to display the output more to the right. Will also work with the blank space to accommodate when the value decreases.
As for the Println(). Wasn't aware that was a bug. I left it in there to verify the output from the encoder when I was working on the lcd code and will remove it now.
Bill

williamjcoates

Hi,
I have solved the problems I was having with the sketch  for my rotary encoder. The counter now outputs 99 for one CW revolution. Thanks for all the input. My next task is to take the value of the counter display on the lcd screen and save this to a file on a button click. I would then take that stored number and place it op a graph.  I have a mega 2560 board with a df robot lcd keypad shield attached. There are left, right and up, down buttons plus a select and reset button. I was thinking of using the left and right buttons to do this. Any ideas on how to go about this?
Bill

Here is my working code
Code: [Select]

#include <LiquidCrystal.h>
#define MAX_ENCODER_VALUE 4095
const int8_t encoderDirections[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
int16_t counter = 0;
const int numRows = 2;
const int numCols = 16;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
void setup() {
               Serial.begin(115200);
              //pinMode(2, INPUT_PULLUP);arduino uno
              //pinMode(3, INPUT_PULLUP);arduino uno
              pinMode(18, INPUT_PULLUP);
              pinMode(19, INPUT_PULLUP);
             
               // set up the LCD's number of columns and rows:
                lcd.begin(numCols, numRows);
               

                //attachInterrupt(0, encoder_interrupt, CHANGE);arduino uno
                //attachInterrupt(1, encoder_interrupt, CHANGE);arduino uno
                attachInterrupt(4, encoder_interrupt, CHANGE);
                attachInterrupt(5, encoder_interrupt, CHANGE);
 
}

void loop() {
   
     lcd.setCursor(0, 0);
     lcd.print("Contact Point:  ");
     lcd.setCursor(0, 1);
     //lcd.print(map(counter, 0, 4095, 0, 99));
     lcd.print(map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));
}
             void encoder_interrupt() {
             static uint8_t oldEncoderState = 0;
                      oldEncoderState <<= 2;
                      oldEncoderState |= ((PIND >> 2) & 0x03);
                      counter += encoderDirections[(oldEncoderState & 0x0F)];
                      if (counter < 0) counter = MAX_ENCODER_VALUE;
                      else if (counter > MAX_ENCODER_VALUE) counter = 0;
}








 

cattledog

#35
Sep 06, 2015, 12:55 am Last Edit: Sep 06, 2015, 01:01 am by cattledog
You have made a lot of progress. There are a few modifications you should make to the code you currently have.

Code: [Select]
//int16_t counter = 0;
volatile int16_t counter = 0;


The value of counter is changed within the ISR, and declaring it volatile helps the compiler and program treat the variable correctly https://www.arduino.cc/en/Reference/Volatile

You should also turn interrupts off to make a protected copy of count to ensure that it doesn't change when you are reading it. This is important for "non-atomic" variables (larger than 1 byte) since the value can change while you are reading the multiple bytes.
Code: [Select]
void loop() {
   
     lcd.setCursor(0, 0);
     lcd.print("Contact Point:  ");
     lcd.setCursor(0, 1);
     //lcd.print(map(counter, 0, 4095, 0, 99));
     noInterrupts();
     int copyCounter = counter;
     interrupts();
    //lcd.print(map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));
     lcd.print(map(float(copyCounter), 0, MAX_ENCODER_VALUE, 0, 99));   
     
}


Quote
My next task is to take the value of the counter display on the lcd screen and save this to a file on a button click.
Do you presently know how to read button presses on the shield to do simple things like turn on an led or print a message on the screen. There is example code for these shields which should show you how to do that. That would be the first place to start. Learn how to display the value you want to save on the lcd or to Serial monitor with a button press.

You say you want to save this value to a file. Where? On an SD card? On your PC? On a webpage? What do do next will depend on the location of the file.

williamjcoates

#36
Sep 06, 2015, 03:06 am Last Edit: Sep 06, 2015, 05:48 am by williamjcoates
Hi Cattledog,
Thanks for getting back to me. I'll add your suggestions to my sketch. I just started searching about how to use the buttons on the shield, but haven't progressed very far as I wasn't sure how to word my search terms. Do you have the locations of these sample code that I could look at. As for where I want to save the encoder output. I think using a sd card module(shield) would work well. Don't know much about them either but am reading up on them.
Bill

cattledog

#37
Sep 06, 2015, 07:50 am Last Edit: Sep 06, 2015, 07:51 am by cattledog
Most lcd keypad shields are similar to this and there is a code example in the wiki.
http://www.dfrobot.com/wiki/index.php?title=Arduino_LCD_KeyPad_Shield_(SKU:_DFR0009)

Be aware that there is an issue with the backlight on many of the shields. Take a look at this sticky posting at the top of the "Displays" section of the forum, which is a warning about pin 10.
http://forum.arduino.cc/index.php?topic=96747.0


williamjcoates

Thanks again Cattledog,
I uploaded the sample sketch for the button press and have it working on my board. I will look closer at the code and see what I can do to incorporate some of the functions into my sketch.
Bill

williamjcoates

Hi Cattledog,
Well I uploaded the sketch included in the link and it worked fine once i commented out the v1.1 and went with v1.0.
I then started to implement the button code into my sketch. Of course it is not working the way I wanted it to and I know this is my lack of understanding to how the buttons are defines for what ever function you want to complete.

This part of the code is confusing to me [code// For V1.0 comment the other threshold and use the one below:

 if (adc_key_in < 50)   return btnRIGHT; 
 if (adc_key_in < 195)  return btnUP;
 if (adc_key_in < 380)  return btnDOWN;
 if (adc_key_in < 555)  return btnLEFT;
 if (adc_key_in < 790)  return btnSELECT;   ][/code]

I know if a certain condition is met the buttob selected will return what you define in the case statement.
Do you have any other examples that I could look at. I want to have the right or left button store the value of the encoder to file once depressed.
Bill




Code: [Select]
//Sample using LiquidCrystal library
#include <LiquidCrystal.h>
#define MAX_ENCODER_VALUE 4095
const int8_t encoderDirections[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
volatile int16_t counter = 0;
const int numRows = 2;
const int numCols = 16;

// select the pins used on the LCD panel
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

// read the buttons
int read_LCD_buttons()
{
 adc_key_in = analogRead(0);      // read the value from the sensor
 // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
 // we add approx 50 to those values and check to see if we are close
 if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
 // For V1.1 us this threshold
 /*
 if (adc_key_in < 50)   return btnRIGHT; 
 if (adc_key_in < 250)  return btnUP;
 if (adc_key_in < 450)  return btnDOWN;
 if (adc_key_in < 650)  return btnLEFT;
 if (adc_key_in < 850)  return btnSELECT; 
*/
 // For V1.0 comment the other threshold and use the one below:

 if (adc_key_in < 50)   return btnRIGHT; 
 if (adc_key_in < 195)  return btnUP;
 if (adc_key_in < 380)  return btnDOWN;
 if (adc_key_in < 555)  return btnLEFT;
 if (adc_key_in < 790)  return btnSELECT;   



 return btnNONE;  // when all others fail, return this...
}




void setup()
{
 Serial.begin(115200);
              //pinMode(2, INPUT_PULLUP);arduino uno
              //pinMode(3, INPUT_PULLUP);arduino uno
              pinMode(18, INPUT_PULLUP);
              pinMode(19, INPUT_PULLUP);
             
               // set up the LCD's number of columns and rows:
                lcd.begin(numCols, numRows);
               

                //attachInterrupt(0, encoder_interrupt, CHANGE);arduino uno
                //attachInterrupt(1, encoder_interrupt, CHANGE);arduino uno
                attachInterrupt(4, encoder_interrupt, CHANGE);
                attachInterrupt(5, encoder_interrupt, CHANGE);
}
 



void loop()
{

 lcd.setCursor(0, 0);
 
 noInterrupts();
 int copyCounter = counter;
 interrupts();
 
 
 lcd_key = read_LCD_buttons();  // read the buttons

 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {
   case btnRIGHT:
     {
     lcd.print("RCP  ");
     lcd.setCursor(0,1);            // move to the begining of the second line
     lcd.print(map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99)); 
     break;
     }
     
   case btnLEFT:
     {
     lcd.print("LCP   ");
     lcd.setCursor(0,1);            // move to the begining of the second line
     lcd.print(map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99)); 
     break;
     }
     
   case btnUP:
     {
     lcd.print("UP    ");
     break;
     }
     
   case btnDOWN:
     {
     lcd.print("DOWN  ");
     break;
     }
     
   case btnSELECT:
     {
     lcd.print("SELECT");
     break;
     }
     
     case btnNONE:
     {
     lcd.print("NONE  ");
     break;
     }
   
 }

 

}
void encoder_interrupt() {
      static uint8_t oldEncoderState = 0;
      oldEncoderState <<= 2;
      oldEncoderState |= ((PIND >> 2) & 0x03);
      counter += encoderDirections[(oldEncoderState & 0x0F)];
            if (counter < 0) counter = MAX_ENCODER_VALUE;
                  else if (counter > MAX_ENCODER_VALUE) counter = 0;
}

cattledog

Quote
This part of the code is confusing to me
There is a network of resistors configured such that when a button is pushed a different and distinct value (0-1024) will be read at A0. There are evidently two versions of the board which produce different readings for the buttons.

You should experiment with your shield and determine the analogRead() value for each button on your shield. Write a simple sketch to read A0 and print it on your lcd, or use the AnalogReadSerial in the 01Basic Examples of the IDE. That will help you tune the values used to determine which button has been pushed.

Quote
Of course it is not working the way I wanted it to and I know this is my lack of understanding to how the buttons are defines for what ever function you want to complete.
What don't you understand?

The buttons select different resistance values which produce different analogRead() values at A0. You read those values, test them against limit values, and act accordingly. You don't need all the complete switch case statements if you are just using a subset of buttons and you don't need to put the analogRead() in a function with a returned value. For the left and right button you can certainly have simple code in the loop which looks like this
Code: [Select]
adc_key_in = analogRead(0);  // read the lcd shield buttons
if(adc_key_in < 50)
{ Do what you want when right button pressed}
if (adc_key_in <= 555 && adc_key_in >=380)
{ Do what you want when left button is pressed}


Can you currently print on the lcd or in the Serial monitor what you want to save when the left or right button is pressed? If so, its time to integrate the SD module.

It may matter if you use the Uno or the Mega when you integrate the SD card. There are potential problems with the lcd shield and the Uno due to the pin 10 used for the backlight and pin 10 settings for the SPI/SD libraries. You are better off using this shield with the Mega as was indicated in your last posted code.

williamjcoates

Thanks again cattledog,
I used this sketch here to give me the values for each button
Code: [Select]
/* Yu Hin Hau
 * DFRobot LCD Keypad Shield Tutorial
 * 7/29/2012
 */
 
//Initialize LCD
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
 
void setup()
{
//Set LCD Display Size
lcd.begin(16,2);
lcd.clear();
 

}
 
void loop()
{
 
 
  //Read Shield Button from Analog 0 Pin
  double button = analogRead(0);
 
  //Set LCD Display Settings
  lcd.setCursor(0,0);
  lcd.println("Analog 0 Value:  ");
 
  lcd.setCursor(0,1);
  lcd.print(button);
  lcd.println("                ");
 
}

and came up with these values
None -  1023.00
Select  - 640.00
Left-      409.00
Up-         99.00
Down-    256.00
Right-    0
I understand now what happens when a button on the shield is depressed. And how this behavior is defined in the read statement.
The next part for me is to define the action produced based on the button pressed. For the moment I would like the button press to print the lcd display to the serial monitor. So if I rotate the encoder and stop on 56. I would press the right button and "RCP: 56" would display in the serial monitor. Or if I pressed the left button "LCP: 56" would display in the serial  monitor. Once I have that working I would then want to save the lcd display to a text file on the sd card. I am going to pickup a sd card module, just not sure whether to go with the SD / TF Card Shield V1.0 Expansion Board Module, or a SD Card Reader Module for Arduino/ARM Read and Write.
Bill

cattledog

Quote
I am going to pickup a sd card module, just not sure whether to go with the SD / TF Card Shield V1.0 Expansion Board Module, or a SD Card Reader Module for Arduino/ARM Read and Write.
My advice on the SD card module is to make sure that whatever you get can run at 5v,  and that there is logic level shifting with a IC rather than a resistance network. 

williamjcoates

Hi Cattledog,
Well I have been working on the keypad/button and I have progressed a bit. In this sketch I was trying to print the value of the encoder when the left or right button was pressed to the serial monitor. I used the serial monitor so I could see if the action worked or not. However its not working. Could you point me in the right direction with my button setup. I also tried your suggestion on the button set up which didn't work. I assume because I am missing something.
Code: [Select]

#include <LiquidCrystal.h>
#define MAX_ENCODER_VALUE 4095
const int8_t encoderDirections[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
volatile int16_t counter = 0;
const int numRows = 2;
const int numCols = 16;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnLEFT   3
int read_LCD_buttons()
{
 adc_key_in = analogRead(0);      // read the value from the sensor

 
 if (adc_key_in < 50)   return btnRIGHT; 
 if (adc_key_in < 555)  return btnLEFT;
}

void setup() {
               Serial.begin(115200);
              //pinMode(2, INPUT_PULLUP);arduino uno
              //pinMode(3, INPUT_PULLUP);arduino uno
              pinMode(18, INPUT_PULLUP);
              pinMode(19, INPUT_PULLUP);
             
               // set up the LCD's number of columns and rows:
                lcd.begin(numCols, numRows);
               

                //attachInterrupt(0, encoder_interrupt, CHANGE);arduino uno
                //attachInterrupt(1, encoder_interrupt, CHANGE);arduino uno
                attachInterrupt(4, encoder_interrupt, CHANGE);
                attachInterrupt(5, encoder_interrupt, CHANGE);
 
}

void loop() {

 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {
   case btnRIGHT:
     {
     Serial.println (map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));
     break;
     }
   case btnLEFT:
     {
     Serial.println (map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));
     break;
     }
 }


 
   
     lcd.setCursor(0, 0);
     lcd.print("Contact Points:  ");
     lcd.setCursor(0, 1);
     //lcd.print(map(counter, 0, 4095, 0, 99));
     noInterrupts();
     int copyCounter = counter;
     interrupts();
     lcd.print(map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));
}
      void encoder_interrupt() {
      static uint8_t oldEncoderState = 0;
      oldEncoderState <<= 2;
      oldEncoderState |= ((PIND >> 2) & 0x03);
      counter += encoderDirections[(oldEncoderState & 0x0F)];
            if (counter < 0) counter = MAX_ENCODER_VALUE;
                  else if (counter > MAX_ENCODER_VALUE) counter = 0;
}




cattledog

You are not calling the function int read_LCD_buttons() in the loop and assigning the return value of the function to the variable lcd_key which is used in the switch case tree.

This statement was in your earlier code, but does not appear in the latest version.

Code: [Select]
lcd_key = read_LCD_buttons();  // read the buttons

I may have confused you by suggesting code in a simplified snippet which did not have the analog read in a function or use a switch case on the return value, but rather had the analogRead and two conditional tests directly in the loop. It is a matter of style, and you should choose a path which makes the most sense for you and future uses of your code. For example, if you plan on implementing more buttons it my make more sense to maintain the structure of the original example code with the read_LCD_buttons() function, the return value, and the switch case.

Go Up