State Change Detection

Hi,
I have a sketch that reads a quadrature rotary encoder. the Rotary Encoder is hooked up to a Arduino Mega 2560 board. On top of this board is a DFRobot LCD Keypad Shield. I also have a SD Micro Card Module hooked up to the Mega Board. My problem is with the keypad press. I want limit the output to one line of data. When I press the key now it keeps printing out data until I release the key. I looked at the example in the Arduino IDE and also at the link below supplied by cattledog in the projects forum but I am still at a loss on how to incorporate this into my sketch. Any help in getting this accomplished would be greatly appreciated.
Bill

http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino

This is my sketch

#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;
}

There's lots of libraries out there to help simplify your code. Paul Stoffregen's encoder library (available from his Teensy site as well as other places) is excellent. It will make your code easier to read.

For up/down/left/right decoding, I'm sure there's libraries out there. Try to find something that exists before writing your own version.

I might be reading this wrong but I think you are asking how to make the serial print only run once during a key press.

If that's the case you will have to make a plan.

each print shows the code has done its thing and is now looping and repeating. Do you want the code to loop and repeat until you let your finger off the button?

something simple like recording lcd_key and checking to see if it has changed state would restrict the code from being repeated until the button was released.

if you want the code to repeat but the serial prints to only run once then a simple flag will work. This will not allow updates to the print so you may need to compare the last reading and if it changes remove the flag for one pass.

if you can explain what its doing and what you would like it to do im sure we can help (keeps printing isn't helpful as we don't know whats printing serial or lcd)

Thanks for the replies....With some help from cattledog, my sketch is working. Sorry if i wasn't clear in what I wanted, but the problem was when I clicked on the keypad it would keep printing the same output to the serial monitor and to the mirco sd card until the keypad was released. Now only one entry per keypad click is printed. One question i have though. I would like to draw a circle on a lcd screen that would represent one full rotation of the rotary encoder i'm using in my sketch. I have a nokia screen I would use for this. Are there any examples of such a thing?
Bill

Below is my working code.....

#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;
}