Go Down

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

williamjcoates

Thanks Cattledog, That fixed it. Now I can read the encoder output on the lcd and then display the value on the serial monitor with a button press. You mentioned some characteristics I should look for when getting a sdcard module.  I am using the mega 2560 board with the dfrobot lcd keypad shield. Could you recommend a specific brand/ model that I could plug into the mega board and then have the lcd shield plug into that? Or would it better serve to hook up the module via a cable. And again what model/make.
Bill

cattledog

Quote
When I press the key now the output of the encoder is printed out to the serial monitor until I release the keypad. How can I modify the button code so that when I press the keypad only one line of data is sent to the serial monitor and the same value isn't repeated?
You are going to have to use something similar to the "state change detection" example found in the 02 digital examples section of the IDE. The following example from linksprite http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino may be confusing because it is not following the format of your original key shield code but it is in effect the same and will give you some ideas. It also uses a library LCDKeypad.h but I don't see where it is used in the sketch and I think it should work without it.

Code: [Select]
#include <LiquidCrystal.h>
#include <LCDKeypad.h>
LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7);
char msgs[5][16] = {"Right Key OK ",
                    "Up Key OK    ",              
                    "Down Key OK  ",
                    "Left Key OK  ",
                    "Select Key OK" };
int adc_key_val[5] ={50, 200, 400, 600, 800 };
int NUM_KEYS = 5;
int adc_key_in;
int key=-1;
int oldkey=-1;
 
void setup()
{
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("     helle! ");
  lcd.print("      welcome!");
  lcd.setCursor(0,1);
  lcd.print("   LinkSprite");
  lcd.print("    LCD Shield");
  delay(1000);
 
  lcd.setCursor(0,0);
  for (char k=0;k<26;k++)
  {
    lcd.scrollDisplayLeft();
    delay(400);
  }
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("ADC key testing");
}
void loop()
{
   adc_key_in = analogRead(0);    // read the value from the sensor
   key = get_key(adc_key_in);  // convert into key press
   if (key != oldkey)   // if keypress is detected
   {
     delay(50);  // wait for debounce time
     adc_key_in = analogRead(0);    // read the value from the sensor
     key = get_key(adc_key_in);    // convert into key press
     if (key != oldkey)    
     {  
       lcd.setCursor(0, 1);
       oldkey = key;
       if (key >=0)
       {
           lcd.print(msgs[key]);              
       }
     }
   }
   delay(100);
}
// Convert ADC value to key number
int get_key(unsigned int input)
{
    int k;
    for (k = 0; k < NUM_KEYS; k++)
    {
      if (input < adc_key_val[k])
      {
        return k;
      }
    }  
    if (k >= NUM_KEYS)k = -1;  // No valid key pressed
    return k;
}




Quote
I would also like to have it so that when I press the left button  and then the right button the data would appear in the same line like this in the serial monitor.
Your second question is easily solved with replacing the Serial.println()statement for button left with Serial.print() and then using the escape character  '\t' = tab  to control your formatting. You can do a google search on \t \n \r to learn about these escape characters.

williamjcoates

Thanks for getting back to me cattledog. I would have replied sooner but I was away for the week. I will look into the code you supplied and see what I can do for the button press. I do have one question concerning the second part about having the the "LCP" and "RCP" value on the same line. If i press the left button I see LCP: 45 which is fine....How would I go about also printing the value of the right button which would be 0. so when I press left button I would get
"LCP: 45     RCP: 0" or if I pressed the right button I would get
"LCP: 0       RCP: 45"
So i guess i need to include in each button press statement the value of the other button which should be zero.
Sorry if this sounds confusing.
Bill

williamjcoates

Hi All,
I didn't get to far on the problem of the output being repeated while holding down the button but figured it wouldn't be an issue. My end goal is to print the output to a sd card. I just recently bout a ethernet/sd card module and will be working with it soon. I have a couple of issues with my current sketch which I need to workout. When I press the left button I get the desired result "LCP:" plus the current output. As well I get "RCP:" plus the current output when pressing the right button. However I discovered that when I press either the up or down buttons I get the left button value"LCP:" plus the current output.? I haven't even initiated the up button. Why would this be happening?. My other question involves how to subtract the LCP: value from the RCP: value and display the result. I wanted to used the down button to initiate this and the display RESULT: plus the number. I started putting something together using some ideas from a sketch that added strings together but I didn't get very far.
Any ideas,,,
Bill





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);
String result;
// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnLEFT   3
#define btnRESULT  2
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;
 if (adc_key_in < 380)  return btnRESULT;
}

void setup() {
               Serial.begin(115200);
               result = String();
              //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_key = read_LCD_buttons();  // read the buttons
 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {
   case btnRIGHT:
     {
     Serial.print("RCP  ");
     Serial.println (map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));
     break;
     }
   case btnLEFT:
     {
     Serial.print("LCP");
     Serial.print("\t");
     Serial.println(map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));
     
     break;
     }

     case btnRESULT:
     {
     lcd.print("Result");
     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

Up or down are less than 555 and trigger the left response. You need to run through the conditional test in order of increasing resistance.

williamjcoates

Hi Cattledog,
That fixed it. Any ideas on how to subtract the "LCP output" from the "RCP output" Using the result button as the initiator? Basically I would rotate the encoder, click on the left button to display in the serial monitor, rotate the encoder and click on the right button to display in the serial monitor and then click on the result button to display the difference between the LCP and the RCP result.

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);
String result;
// 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 btnRESULT  2
#define btnLEFT    3
#define btnSELECT  4
#define btnNONE    5
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 < 195)  return btnUP;
 if (adc_key_in < 380)  return btnRESULT;
 if (adc_key_in < 555)  return btnLEFT;
 if (adc_key_in < 790)  return btnSELECT; 
}

void setup() {
               Serial.begin(115200);
               result = String();
              //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_key = read_LCD_buttons();  // read the buttons
 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {
   case btnRIGHT:
     {
     Serial.print("RCP  ");
     Serial.println (map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));
     break;
     }
   case btnLEFT:
     {
     Serial.print("LCP");
     Serial.print("\t");
     Serial.println(map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));
     
     break;
     }

     case btnRESULT:
     {
     Serial.println("Result:");
     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

Rather than just printing out the values you need to assign them to variables and do the math with the variables.

Also, please take another look at reply #35. You are not using the transfer variable copyCounter, but instead are directly using counter.

williamjcoates

Hi Cattledog,
Been working on my code. I now have the right and left buttons sending the encoder output to two separate txt files "dataRCP.txt, dataLCP.txt on a sd micro module. Still trying to implement the code that will reduce the multiple entries down to one when I press the button. If you have any other examples you could show me I'd appreciate it. As for subtracting the right button output from the left button output I have decided on a different approach. I will import the two .txt files i created into excel and use formulas and a the graphing utilities to achieve the final result. perhaps when I come more proficient in coding I will attempt to address the issue directing in arduino. If you could also look at what i have and see if there is anyway i could tweak what I have to run better i would appreciate this as well.
Code: [Select]
#include <SD.h>
#include <SPI.h>
#include <LiquidCrystal.h>
#define MAX_ENCODER_VALUE 4095
const int chipSelect = 53;
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);
String result;
// 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 btnRESULT  2
#define btnLEFT    3
#define btnSELECT  4
#define btnNONE    5
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 < 195)  return btnUP;
 if (adc_key_in < 380)  return btnRESULT;
 if (adc_key_in < 555)  return btnLEFT;
 if (adc_key_in < 790)  return btnSELECT; 
}

void setup() {
               Serial.begin(9600);
               Serial.println("Initializing SD card...");
  pinMode(chipSelect, OUTPUT);//set chip select PIN as output.
             // see if the card is present and can be initialized:
            if (!SD.begin(chipSelect)) {
            Serial.println("Card failed, or not present");
           // don't do anything more:
            return;
            }
          Serial.println("card initialized.");
         
              pinMode(18, INPUT_PULLUP);
              pinMode(19, INPUT_PULLUP);
             
               // set up the LCD's number of columns and rows:
                lcd.begin(numCols, numRows);
       
                attachInterrupt(4, encoder_interrupt, CHANGE);
                attachInterrupt(5, encoder_interrupt, CHANGE);
 
}

void loop() {
// make a string for assembling the data to log:
  String dataString = "";



 
 lcd_key = read_LCD_buttons();  // read the buttons
 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {
   case btnRIGHT:
     {
     {
   
    dataString =  String("RCP \t") + String(",") + String(map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));       
   
    File dataFile = SD.open("dataRCP.txt", FILE_WRITE);//open a file named datalog.txt.
   
   
    if (dataFile) {       // if the file is available, write to it ('datafile' is returned 1 if SD.open was successful.
      dataFile.println(dataString);//print the concatenated data string and finish the line with a carriage return (println adds the CR automatically after printing the string)
      dataFile.close();   //close the file. IT is a good idea to always open/close a file before and after writing to it. That way, if someone removes the card the file is most
                          //likely o.k. and can be read with the computer.
     
      Serial.println(dataString);// print the string also to the serial port, so we can see what is going on.
    } 
  // if SD.open is not successful it returns a 0, i.e. the else{} is executed if the file could not be opened/created successfully.
    else {
      Serial.println("error opening dataRCP.txt");//in that case print an error message
    }
  }
     break;
     }

     
   case btnLEFT:
     {
{
   
    dataString =  String("LCP \t") + String(",") + String(map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));     
    File dataFile = SD.open("dataLCP.txt", FILE_WRITE);//open a file named datalog.txt.
   
   
    if (dataFile) {       // if the file is available, write to it ('datafile' is returned 1 if SD.open was successful.
      dataFile.println(dataString);//print the concatenated data string and finish the line with a carriage return (println adds the CR automatically after printing the string)
      dataFile.close();   //close the file. IT is a good idea to always open/close a file before and after writing to it. That way, if someone removes the card the file is most
                          //likely o.k. and can be read with the computer.
     
      Serial.println(dataString);// print the string also to the serial port, so we can see what is going on.
    } 
  // if SD.open is not successful it returns a 0, i.e. the else{} is executed if the file could not be opened/created successfully.
    else {
      Serial.println("error opening dataLCP.txt");//in that case print an error message
    }
  }
     
   
     break;
     }

     case btnRESULT:
     {

     
   
 
     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

A problem that I still see with your code, is that you do not actually use the protected copy of the encoder counts in your code. You indeed pull the counts into a variable which will not change during a reading, but you don't actually use it. Everywhere in your code you are directly accessing the variable "counter" in the mapped values.

Code: [Select]
noInterrupts();
     int copyCounter = counter;
     interrupts();


You could handle the use of the protected copy with a function like
Code: [Select]
int getCount ()
{  
     noInterrupts();
     int copyCounter = counter;
     interrupts();
     return copyCounter;
}


Then, everywhere in your code where you are using "counter", substitute the function getCount().

Code: [Select]

 dataString =  String("RCP \t") + String(",") + String(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));
dataString =  String("LCP \t") + String(",") + String(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));
 lcd.print(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));

williamjcoates

Thanks again Cattledog,
I went over my code and I think I have it right now......Do you have any more examples for the "State Change Detection" I just cant seem to get my head around what needs to be done here.
Bill

Code: [Select]
#include <SD.h>
#include <SPI.h>
#include <LiquidCrystal.h>
#define MAX_ENCODER_VALUE 4095
const int chipSelect = 53;
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;
volatile int8_t direction = 0;


const int numRows = 2;
const int numCols = 16;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
String result;
// 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 btnRESULT  2
#define btnLEFT    3
#define btnSELECT  4
#define btnNONE    5
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 < 195)  return btnUP;
 if (adc_key_in < 380)  return btnRESULT;
 if (adc_key_in < 555)  return btnLEFT;
 if (adc_key_in < 790)  return btnSELECT; 
}

void setup() {
               Serial.begin(9600);
               Serial.println("Initializing SD card...");
  pinMode(chipSelect, OUTPUT);//set chip select PIN as output.
             // see if the card is present and can be initialized:
            if (!SD.begin(chipSelect)) {
            Serial.println("Card failed, or not present");
           // don't do anything more:
            return;
            }
          Serial.println("card initialized.");
         
              pinMode(18, INPUT_PULLUP);
              pinMode(19, INPUT_PULLUP);
             
               // set up the LCD's number of columns and rows:
                lcd.begin(numCols, numRows);
       
                attachInterrupt(4, encoder_interrupt, CHANGE);
                attachInterrupt(5, encoder_interrupt, CHANGE);
 
}

int getCount ()

     noInterrupts();
     int copyCounter = counter;
     interrupts();
     return copyCounter;
}

void loop() {
// make a string for assembling the data to log:
  String dataString = "";



 
 lcd_key = read_LCD_buttons();  // read the buttons
 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {
   case btnRIGHT:
     {
     {
   
    dataString =  String("RCP \t") + String(",") + String(map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));       
   
    File dataFile = SD.open("dataRCP.txt", FILE_WRITE);//open a file named datalog.txt.
   
   
    if (dataFile) {       // if the file is available, write to it ('datafile' is returned 1 if SD.open was successful.
      dataFile.println(dataString);//print the concatenated data string and finish the line with a carriage return (println adds the CR automatically after printing the string)
      dataFile.close();   //close the file. IT is a good idea to always open/close a file before and after writing to it. That way, if someone removes the card the file is most
                          //likely o.k. and can be read with the computer.
     
      Serial.println(dataString);// print the string also to the serial port, so we can see what is going on.
    } 
  // if SD.open is not successful it returns a 0, i.e. the else{} is executed if the file could not be opened/created successfully.
    else {
      Serial.println("error opening dataRCP.txt");//in that case print an error message
    }
  }
     break;
     }

     
   case btnLEFT:
     {
{
   
    dataString =  String("LCP \t") + String(",") + String(map(float(counter), 0, MAX_ENCODER_VALUE, 0, 99));     
    File dataFile = SD.open("dataLCP.txt", FILE_WRITE);//open a file named datalog.txt.
   
   
    if (dataFile) {       // if the file is available, write to it ('datafile' is returned 1 if SD.open was successful.
      dataFile.println(dataString);//print the concatenated data string and finish the line with a carriage return (println adds the CR automatically after printing the string)
      dataFile.close();   //close the file. IT is a good idea to always open/close a file before and after writing to it. That way, if someone removes the card the file is most
                          //likely o.k. and can be read with the computer.
     
      Serial.println(dataString);// print the string also to the serial port, so we can see what is going on.
    } 
  // if SD.open is not successful it returns a 0, i.e. the else{} is executed if the file could not be opened/created successfully.
    else {
      Serial.println("error opening dataLCP.txt");//in that case print an error message
    }
  }
     
   
     break;
     }

     case btnRESULT:
     {

     
   
 
     break;
     }

     
 }


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









cattledog

Quote
I went over my code and I think I have it right now.....
No. I explicitly pointed out all the places constructing your Strings where you used the unprotected variable "counter" which needed to be replaced with the function getCount(). You have not done that.

Your code appears to work, so you have not adopted the coding practice which says that when you access a volatile variable bigger than a byte from an isr you need to ensure an interrupt can not change it while it is being read.

You can also clean up the lcd print portion of the code now that you have the getCount() function
Code: [Select]

     lcd.setCursor(0, 0);
     lcd.print("Contact Points:  ");
     lcd.setCursor(0, 1);
    
     //noInterrupts();
     //int copyCounter = getCount();
     //interrupts();
     lcd.print(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));
}


Here's one possibility on how to manage the multiple records on a button press. Your program will need to read a no button pressed before it can service a button again. Create a global variable (a byte will be fine) called Flag. Start off with it =1.  You can start the code in each active button with if (Flag ==0 ){//do what follows} At the end of the case, set Flag = 1.

Add btnNONE to the resistance list, and create the case for button NONE which sets the flag to 0.

Code: [Select]
case btnNONE:
      Flag = 0;
     break;
    


Here is some code. I could not follow your bracket arrangements, and I changed them. Be aware that the bracket arrangement in this untested code may not be correct.
Code: [Select]
#include <SD.h>
#include <SPI.h>
#include <LiquidCrystal.h>
#define MAX_ENCODER_VALUE 4095
const int chipSelect = 53;
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;
volatile int8_t direction = 0;

byte Flag = 1;//add flag to control multiple records for same button press


const int numRows = 2;
const int numCols = 16;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
String result;
// 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 btnRESULT  2
#define btnLEFT    3
#define btnSELECT  4
#define btnNONE    5
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 < 195)  return btnUP;
  if (adc_key_in < 380)  return btnRESULT;
  if (adc_key_in < 555)  return btnLEFT;
  if (adc_key_in < 790)  return btnSELECT;
  if (adc_key_in < 1024) return btnNONE;//add
}

void setup() {
  Serial.begin(9600);
  Serial.println("Initializing SD card...");
  pinMode(chipSelect, OUTPUT);//set chip select PIN as output.
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");

  pinMode(18, INPUT_PULLUP);
  pinMode(19, INPUT_PULLUP);

  // set up the LCD's number of columns and rows:
  lcd.begin(numCols, numRows);

  attachInterrupt(4, encoder_interrupt, CHANGE);
  attachInterrupt(5, encoder_interrupt, CHANGE);

}


void loop() {
  // make a string for assembling the data to log:
  String dataString = "";

  lcd_key = read_LCD_buttons();  // read the buttons
  switch (lcd_key)               // depending on which button was pushed, we perform an action
  {
    case btnRIGHT:
      if (Flag == 0)
      {
        dataString =  String("RCP \t") + String(",") + String(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));

        File dataFile = SD.open("dataRCP.txt", FILE_WRITE);//open a file named datalog.txt.


        if (dataFile) {       // if the file is available, write to it ('datafile' is returned 1 if SD.open was successful.
          dataFile.println(dataString);//print the concatenated data string and finish the line with a carriage return (println adds the CR automatically after printing the string)
          dataFile.close();   //close the file. IT is a good idea to always open/close a file before and after writing to it. That way, if someone removes the card the file is most
          //likely o.k. and can be read with the computer.

          Serial.println(dataString);// print the string also to the serial port, so we can see what is going on.
        }

        // if SD.open is not successful it returns a 0, i.e. the else{} is executed if the file could not be opened/created successfully.
        else {
          Serial.println("error opening dataRCP.txt");//in that case print an error message
        }
        Flag = 1;
      }
      break;

    case btnLEFT:
      if (Flag == 0)
      {
        dataString =  String("LCP \t") + String(",") + String(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));
        File dataFile = SD.open("dataLCP.txt", FILE_WRITE);//open a file named datalog.txt.


        if (dataFile) {       // if the file is available, write to it ('datafile' is returned 1 if SD.open was successful.
          dataFile.println(dataString);//print the concatenated data string and finish the line with a carriage return (println adds the CR automatically after printing the string)
          dataFile.close();   //close the file. IT is a good idea to always open/close a file before and after writing to it. That way, if someone removes the card the file is most
          //likely o.k. and can be read with the computer.

          Serial.println(dataString);// print the string also to the serial port, so we can see what is going on.
        }
        // if SD.open is not successful it returns a 0, i.e. the else{} is executed if the file could not be opened/created successfully.
        else {
          Serial.println("error opening dataLCP.txt");//in that case print an error message
        }
        Flag = 1;
      }

      break;

    case btnRESULT:
      break;

    case btnNONE:
      Flag = 0;
      break;

  }//closes switch


  lcd.setCursor(0, 0);
  lcd.print("Contact Points:  ");
  lcd.setCursor(0, 1);

  //noInterrupts();
  //int copyCounter = getCount();
  //interrupts();
  lcd.print(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));

}//closes loop


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

int getCount ()
{
  noInterrupts();
  int copyCounter = counter;
  interrupts();
  return copyCounter;
}


williamjcoates

Thanks Cattledog,
It works great now. I will study what you did and work on interfacing the sketch and excel using GOBETWINO which looks interesting. I am also going to work on producing some graphics representing the the rotation of the encoder. Thanks again for your help and patience. Really appreciate it.
Bill

williamjcoates

Hi all,
I have been testing my sketch and have a couple of questions...
First off when I rotate the encoder clockwise(1 rotation) the lcd displays the count 1-99 fine. If i keep going clockwise the count should repeat 1-99. It does except for on exception. the second digit on the lcd display remains at 9 unti the count reaches 10.....so 1-20 looks like this
19,29,39,49,59,69,79,89,99,10,11,12,13,14,15,16,17,18,19,20. When I rotate the encoder CCW it reaches 10 and then the 0 remains like this count down from 20.
20,19,18,17,16,15,14,13,12,11,10,90,80,70,60,50,40,30,20,10. Anyone have an idea of why it would do this and how I could correct it. The other question involves refining the encoder output. I have included some pics of how am using the encoder which is mounted to a safe dial.
The safe dial is numbered 1-99 and my encoder measures 1-99 on 1 rotation. I would like to refine the measurement to includes 10ths. so when I turn the dial from 1 to 2 1 would get this on the display.
1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2.0. It just a matter of changing the output resolution but I am not sure how to do it........ Here is my code.....Special thanks to cattle dog and Jaakko for helping me get to this point....
Code: [Select]
#include <SD.h>
#include <SPI.h>
#include <LiquidCrystal.h>
#define MAX_ENCODER_VALUE 4095
const int chipSelect = 53;
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;
volatile int8_t direction = 0;

byte Flag = 1;//add flag to control multiple records for same button press


const int numRows = 2;
const int numCols = 16;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
String result;
// 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 btnRESULT  2
#define btnLEFT    3
#define btnSELECT  4
#define btnNONE    5
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 < 195)  return btnUP;
  if (adc_key_in < 380)  return btnRESULT;
  if (adc_key_in < 555)  return btnLEFT;
  if (adc_key_in < 790)  return btnSELECT;
  if (adc_key_in < 1024) return btnNONE;//add
}

void setup() {
  Serial.begin(9600);
  Serial.println("Initializing SD card...");
  pinMode(chipSelect, OUTPUT);//set chip select PIN as output.
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");

  pinMode(18, INPUT_PULLUP);
  pinMode(19, INPUT_PULLUP);

  // set up the LCD's number of columns and rows:
  lcd.begin(numCols, numRows);

  attachInterrupt(4, encoder_interrupt, CHANGE);
  attachInterrupt(5, encoder_interrupt, CHANGE);

}


void loop() {
  // make a string for assembling the data to log:
  String dataString = "";

  lcd_key = read_LCD_buttons();  // read the buttons
  switch (lcd_key)               // depending on which button was pushed, we perform an action
  {
    case btnRIGHT:
      if (Flag == 0)
      {
        dataString =  String("RCP \t") + String(",") + String(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));

        File dataFile = SD.open("dataRCP.txt", FILE_WRITE);//open a file named datalog.txt.


        if (dataFile) {       // if the file is available, write to it ('datafile' is returned 1 if SD.open was successful.
          dataFile.println(dataString);//print the concatenated data string and finish the line with a carriage return (println adds the CR automatically after printing the string)
          dataFile.close();   //close the file. IT is a good idea to always open/close a file before and after writing to it. That way, if someone removes the card the file is most
          //likely o.k. and can be read with the computer.

          Serial.println(dataString);// print the string also to the serial port, so we can see what is going on.
        }

        // if SD.open is not successful it returns a 0, i.e. the else{} is executed if the file could not be opened/created successfully.
        else {
          Serial.println("error opening dataRCP.txt");//in that case print an error message
        }
        Flag = 1;
      }
      break;

    case btnLEFT:
      if (Flag == 0)
      {
        dataString =  String("LCP \t") + String(",") + String(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));
        File dataFile = SD.open("dataLCP.txt", FILE_WRITE);//open a file named datalog.txt.


        if (dataFile) {       // if the file is available, write to it ('datafile' is returned 1 if SD.open was successful.
          dataFile.println(dataString);//print the concatenated data string and finish the line with a carriage return (println adds the CR automatically after printing the string)
          dataFile.close();   //close the file. IT is a good idea to always open/close a file before and after writing to it. That way, if someone removes the card the file is most
          //likely o.k. and can be read with the computer.

          Serial.println(dataString);// print the string also to the serial port, so we can see what is going on.
        }
        // if SD.open is not successful it returns a 0, i.e. the else{} is executed if the file could not be opened/created successfully.
        else {
          Serial.println("error opening dataLCP.txt");//in that case print an error message
        }
        Flag = 1;
      }

      break;

    case btnRESULT:
      break;

    case btnNONE:
      Flag = 0;
      break;

  }//closes switch


  lcd.setCursor(0, 0);
  lcd.print("Contact Points:  ");
  lcd.setCursor(0, 1);

  //noInterrupts();
  //int copyCounter = getCount();
  //interrupts();
  lcd.print(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));

}//closes loop


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

int getCount ()
{
  noInterrupts();
  int copyCounter = counter;
  interrupts();
  return copyCounter;
}

cattledog

#58
Oct 11, 2015, 01:22 am Last Edit: Oct 11, 2015, 01:23 am by cattledog
You need to read the documentation on the map function https://www.arduino.cc/en/Reference/Map
The map() function uses integer math so will not generate fractions, when the math might indicate that it should do so. Fractional remainders are truncated, and are not rounded or averaged.

Given the integer nature of the map function, you should remove the cast to float of getCount().
Code: [Select]
//lcd.print(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));
lcd.print(map(getCount(), 0, MAX_ENCODER_VALUE, 0, 99));


If you want to translate 0-4095 into 00.0 to 99.9 you are going to have to write your own function.

Regarding the management of the lcd print out, you were pointed at this in earlier posts #32 and #33
Quote
Will also work with the blank space to accommodate when the value decreases.
I would handle it this way
Code: [Select]
int value = map(getCount(), 0, MAX_ENCODER_VALUE, 0, 99)
if (value <10)
  lcd.print("0");
lcd.print(value);


This should print a leading zero for values less than 10 on the way up and down.

williamjcoates

Thanks Cattledog,
I tried adding this
Code: [Select]
int value = map(getCount(), 0, MAX_ENCODER_VALUE, 0, 99)
if (value <10)
  lcd.print("0");
lcd.print(value);
and recieved this complile error
Code: [Select]
error: expected ',' or ';' before 'if'
error: 'lcd' does not name a type
expected ',' or ';' before 'if'



Code: [Select]
#include <SD.h>
#include <SPI.h>
#include <LiquidCrystal.h>
#define MAX_ENCODER_VALUE 4095
const int chipSelect = 53;
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;
volatile int8_t direction = 0;

byte Flag = 1;//add flag to control multiple records for same button press


const int numRows = 2;
const int numCols = 16;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
String result;
// 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 btnRESULT  2
#define btnLEFT    3
#define btnSELECT  4
#define btnNONE    5
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 < 195)  return btnUP;
  if (adc_key_in < 380)  return btnRESULT;
  if (adc_key_in < 555)  return btnLEFT;
  if (adc_key_in < 790)  return btnSELECT;
  if (adc_key_in < 1024) return btnNONE;//add
}

void setup() {
  Serial.begin(9600);
  Serial.println("Initializing SD card...");
  pinMode(chipSelect, OUTPUT);//set chip select PIN as output.
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");

  pinMode(18, INPUT_PULLUP);
  pinMode(19, INPUT_PULLUP);

  // set up the LCD's number of columns and rows:
  lcd.begin(numCols, numRows);

  attachInterrupt(4, encoder_interrupt, CHANGE);
  attachInterrupt(5, encoder_interrupt, CHANGE);

}


void loop() {
  // make a string for assembling the data to log:
  String dataString = "";

  lcd_key = read_LCD_buttons();  // read the buttons
  switch (lcd_key)               // depending on which button was pushed, we perform an action
  {
    case btnRIGHT:
      if (Flag == 0)
      {
        dataString =  String("RCP \t") + String(",") + String(map(getCount(), 0, MAX_ENCODER_VALUE, 0, 99));

        File dataFile = SD.open("dataRCP.txt", FILE_WRITE);//open a file named datalog.txt.


        if (dataFile) {       // if the file is available, write to it ('datafile' is returned 1 if SD.open was successful.
          dataFile.println(dataString);//print the concatenated data string and finish the line with a carriage return (println adds the CR automatically after printing the string)
          dataFile.close();   //close the file. IT is a good idea to always open/close a file before and after writing to it. That way, if someone removes the card the file is most
          //likely o.k. and can be read with the computer.

          Serial.println(dataString);// print the string also to the serial port, so we can see what is going on.
        }

        // if SD.open is not successful it returns a 0, i.e. the else{} is executed if the file could not be opened/created successfully.
        else {
          Serial.println("error opening dataRCP.txt");//in that case print an error message
        }
        Flag = 1;
      }
      break;

    case btnLEFT:
      if (Flag == 0)
      {
        dataString =  String("LCP \t") + String(",") + String(map(getCount(), 0, MAX_ENCODER_VALUE, 0, 99));
        File dataFile = SD.open("dataLCP.txt", FILE_WRITE);//open a file named datalog.txt.


        if (dataFile) {       // if the file is available, write to it ('datafile' is returned 1 if SD.open was successful.
          dataFile.println(dataString);//print the concatenated data string and finish the line with a carriage return (println adds the CR automatically after printing the string)
          dataFile.close();   //close the file. IT is a good idea to always open/close a file before and after writing to it. That way, if someone removes the card the file is most
          //likely o.k. and can be read with the computer.

          Serial.println(dataString);// print the string also to the serial port, so we can see what is going on.
        }
        // if SD.open is not successful it returns a 0, i.e. the else{} is executed if the file could not be opened/created successfully.
        else {
          Serial.println("error opening dataLCP.txt");//in that case print an error message
        }
        Flag = 1;
      }

      break;

    case btnRESULT:
      break;

    case btnNONE:
      Flag = 0;
      break;

  }//closes switch


  lcd.setCursor(0, 0);
  lcd.print("Contact Points:  ");
  lcd.setCursor(0, 1);

  //noInterrupts();
  //int copyCounter = getCount();
  //interrupts();
  //lcd.print(map(float(getCount()), 0, MAX_ENCODER_VALUE, 0, 99));
lcd.print(map(getCount(), 0, MAX_ENCODER_VALUE, 0, 99));
}//closes loop


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

int value = map(getCount(), 0, MAX_ENCODER_VALUE, 0, 99)
if (value <10)
  lcd.print("0");
lcd.print(value);

int getCount ()
{
  noInterrupts();
  int copyCounter = counter;
  interrupts();
  return copyCounter;
}

Go Up