lcd keypad

I have 16x2 LCD but having trouble with a code snippet.

I want to display each key press on the second row of the lcd for example 1234, but what I am getting is the lcd displaying 1 then 2 then 3 then 4, with each key press on the second row but only in column "0".

Here is the original code with the code I have added commented out.

void readKeypad()
{


  char key = keypad.getKey();
  if (key)// != NO_KEY)   // only be bothered to do something if a key was pressed

  {   

  //   lcd.setCurser(0,1);
  //   lcd.print(key);

             switch(key)
         {
    case '*':
      z=0;
            break;
                case '#':
      delay(100); // for extra debounce
            checkPIN();
            lcd.clear();
      break;
    default:
        attempt[z]=key;
      z++; 

     // playKeyTone();// play a beep to acknowledge that key pressed    
    }
  }

}
  //   lcd.setCurser(0,1);

What does this code do? How does positioning the cursor on the second line, in the first column , correlate to the observations you are making?

my lcd is setup is this

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

most of the sketches I have seen start like this

LiquidCrystal lcd( 12, 11, 10, 9, 8, 7);

thefore I have to revese the typical code

lcd.setCursor(thisRow,thisCol);

to

lcd.setCursor(thisCol,thisRow);

what I'd expect from this code is that the keys that are pressed on the keypad will be shown on column "0" row "1".

what I'd expect from this code is that the keys that are pressed on the keypad will be shown on column "0" row "1".

Exactly. And that IS what you see.

So, what's the problem?

It should be fairly obvious that telling the LCD to display the next character in the 0th column every time is not what you want. Count how many characters have been received, and use that count to determine where to display the character just received.

how do i do this

Create a global or static variable:

byte numberOfCharactersReceivedFromKeypad = 0;

(of course, you could use a shorter name...)

Each time there is a character received, increment the counter:

numberOfCharactersReceivedFromKeypad++;

Position the cursor appropriately:

lcd.setCursor(numberOfCharactersReceivedFromKeypad-1, 1);

The -1 is needed because you want the 1st character in column 0, the 2nd character in column 1, etc.

I have include my sketch so you get a better idea of what i'm working with.

I have inlcuded you code snippets and called them "fromKeypad" and thay appear in the "void readKeypad()" section

#include <Keypad.h>
#include <LiquidCrystal.h>

int speakerPin= 5;//speaker

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
const int numRows = 3;
const int numCols = 16;


//--------------------KEYPAD SETUP-----------------------------------------------------------------------------------


const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = 
{
  {
    '1','2','3' }
  ,
  {
    '4','5','6' }
  ,
  {
    '7','8','9' }
  ,
  {
    '*','0','#' }
};

byte rowPins[ROWS] = {
  25, 24, 23, 22}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {
  28, 27, 26}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


char PIN[6]={
  '1','2','3','4','5','6'}; // our secret (!) number
char attempt[6]={
  0,0,0,0,0,0}; // used for comparison
int z=0;
byte fromKeypad = 0;
//------------------------------------------------------------------------------------------------------

void setup()

{
  lcd.begin (16,2);
  lcd.print("Initialising...");
  delay(3000); 
  lcd.clear();
  lcd.print("Ready");
  pinMode(speakerPin, OUTPUT);
}

//--------------------------PASSCODE SECTION-----------------------------------------------------------------------------


void correctPIN() // do this if correct PIN entered
{
  lcd.setCursor(0,0);
  playSuccessTone();
  lcd.print ("Correct");
  delay(1000);
  lcd.clear();
  lcd.print("Alarm Disabled.");

}
void incorrectPIN() // do this if incorrect PIN entered
{
  lcd.setCursor(0,0);
  playWarningTone();
  lcd.print ("Try again");
  delay(1000);
  lcd.clear();
  lcd.print("Alarm Enabled.");

}

void checkPIN()
{

  int correct=0;
  for (int q=0; q<6; q++)
  {
    if (attempt[q]==PIN[q])
    {
      correct++;
    }
  }
  if (correct==6)
  {
    correctPIN();
  }
  else
  {
    incorrectPIN();
  }
  for (int zz=0; zz<6; zz++) // wipe attempt
  {
    attempt[zz]=0;

  }
}

void readKeypad()
{


  char key = keypad.getKey();
  if (key != NO_KEY)   // only be bothered to do something if a key was pressed
  { 
    lcd.print(key);
    fromKeypad++;
    lcd.setCursor(fromKeypad-1, 1);

    switch(key)
    {
    case '*':
      z=0;
      break;
    case '#':
      delay(100); // for extra debounce
      lcd.clear();
      checkPIN();
      break;
    default:
      attempt[z]=key;
      z++;

      // playKeyTone();// play a beep to acknowledge that key pressed    
    }
  }

}
//--------------------------------SOUND SECTION----------------------------------------------

void playKeyTone(){ // beeping sound for key press
  int elapsedtime = 0;
  while (elapsedtime < 100)
  {
    digitalWrite(speakerPin,HIGH);
    delayMicroseconds(500);//time chajges pitch


    digitalWrite(speakerPin, LOW);
    delayMicroseconds(500);//time chajges pitch
    elapsedtime++;
  }
}

void playWarningTone(){ // long beep for wrong password
  int elapsedtime = 0;
  while (elapsedtime < 200)
  {
    digitalWrite(speakerPin,HIGH);
    delayMicroseconds(1000);//time changes pitch


    digitalWrite(speakerPin, LOW);
    delayMicroseconds(1000);//time changes pitch
    elapsedtime++;
  }
}

void playSuccessTone(){ //  beep for correct password
  int elapsedtime = 0;
  while (elapsedtime < 250)//time duration
  {
    digitalWrite(speakerPin,LOW);
    delayMicroseconds(500); //time changes pitch

    digitalWrite(speakerPin, HIGH);
    delayMicroseconds(250);//time changes pitch
    elapsedtime++;

  }
}

//---------------------------------------------------------------------------------------------
void loop()
{
  readKeypad();
}

with your code added heres what happens on the lcd from system reset.

lcd shows "Ready"
if I press #
I get "try again" then 1 sec after
I get "alarm enabled".

Now, if I press the keypad. the first number appears on the first line next to "alarm enebled". it looks like this.

alarm enabled.1
234

so the next time i try to enter a number it now looks like this

alarm enabled.1
234
next time it looks like this.

alarm enabled.1
234

I have noticed that by moving the code snippet around the "void readKeypad(); section affects how the lcd displays the characters?

hope you can make sense of this.

In the code you posted earlier, the lcd.setCursor() call was immediately followed by lcd.print() to print the character. In this code, that isn't the case.

It would appear that you are printing the character before the lcd.setCursor() call now.

At some point, of course, you need to reset fromKeypad to 0.

At some point, of course, you need to reset fromKeypad to 0.

is this my reset snippet and if not where should this go.

byte fromKeypad = 0;

if its not, what should it be?

I have redone the code

void readKeypad()
{


  char key = keypad.getKey();
  if (key != NO_KEY)   // only be bothered to do something if a key was pressed
  { 
  lcd.setCursor(fromKeypad-1, 1);
  lcd.print(key);
  fromKeypad++;

    switch(key)
    {
    case '*':
      z=0;
      break;
    case '#':
         delay(100); // for extra debounce
      lcd.clear();
      checkPIN();
      break;
      default:
      attempt[z]=key;
      z++;
     

      // playKeyTone();// play a beep to acknowledge that key pressed    
    }
  }

}

it no longer prints on the first row which is good, so i guess with the reset the code should be good.

thanks in advance

is this my reset snippet and if not where should this go.

The reset would simply be:

fromKeypad = 0;

If you put byte in front of it, you are defining a new (local) variable with the same name as a global variable, which is NOT what you want to do.

As to where to put it, that depends. The '*' case does something. The '#' case does something. Either or both of them are good candidates. More likely the '#' case.

mark7w:
my lcd is setup is this

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

most of the sketches I have seen start like this

LiquidCrystal lcd( 12, 11, 10, 9, 8, 7);

thefore I have to revese the typical code

lcd.setCursor(thisRow,thisCol);

to

lcd.setCursor(thisCol,thisRow);

Ahhh. I think there may be a disconnect here.

The numbers in the constructor are the Arduino pins used for LCD connections:
LiquidCrystal(rs_pin, enable_pin, d4_pin, d5_pin, d6_pin, d7_pin)

and have no relationship or affect on
the arguments to the setCursor() function are always in the same order:
setCursor(col, row)

The first argument to setCursor() is column and the second argument is row
and both start at zero. i.e. the upper left position is 0,0 not 1,1

--- bill

One thing is now baffling me and I cant see it in the code, but when I enter the charaters. I can only enter upto 9 characters despite having room for 16 characters.
Indeed if I enter the 10th character it goes into row "0" Column "0".

It has nothing to do with positioning the cursor. It has to do with running off the end of the attempt array, and stepping on something else.

char PIN[6]={
  '1','2','3','4','5','6'}; // our secret (!) number
char attempt[6]={
  0,0,0,0,0,0}; // used for comparison

If I wanted to have a password to unset, what code would I need.

To unset what? systemSet() doesn't do anything except print to the serial port. Why isn't that sufficient for systemUnset()?

How do i stop the password from "sliding"
if the password is 123456 and i enter 1234567 the password is still valid?

void checkPIN()
{
  int correct=0;
  for (int q=0; q<6; q++) 
  {
    if (attempt[q]==PIN[q])
    {
      correct++;
    }
  }
  if (correct==6)
  {
    systemSet();
    //correctPIN();
  }
  else
  {
    //incorrectPIN();
    systemUnSet();
  }
  for (int zz=0; zz<16; zz++) // wipe attempt
  {
    attempt[zz]=0;

  }
}


//------------------------------------------------------------------------------------


void readKeypad()
{


  char key = keypad.getKey();
  if (key != NO_KEY)   // only be bothered to do something if a key was pressed 

  { 
    fromKeypad++; //Each time there is a character received, increment the counter:
    lcd.setCursor(fromKeypad-1, 1);
    lcd.print(key);

    switch(key)// look for the special keys to initiate an event
    {
    case '*':
      z=0;
      fromKeypad = 0;
      break;
    case '#':
      delay(100); // for extra debounce
      fromKeypad = 0;
      lcd.clear();
      checkPIN();
      break;
    default:
      attempt[z]=key;
      z++;


      KeyPadTone();// play a beep to acknowledge that key pressed


    }
  }

}

How do i stop the password from "sliding"
if the password is 123456 and i enter 1234567 the password is still valid?

You really need to make some changes to the code. The password to check against should be a string, not an array of chars. Change

char PIN[6]={
  '1','2','3','4','5','6'}; // our secret (!) number

to

char PIN[] = "123456";

Then, in the readKeypad() function,

    default:
      attempt[z]=key;
      z++;
      attempt[z] = '\0';

The new line changes attempt from a char array to a string.

Finally, in checkPIN():

  int correct=0;
  for (int q=0; q<6; q++) 
  {
    if (attempt[q]==PIN[q])
    {
      correct++;
    }
  }

becomes

   int correct = strcmp(attempt, PIN);

You'll need to make some other changes, because correct will now be -1, 0, or +1. 0 means that the attempt and PIN strings are the same. +1 or -1 mean that they are not.

  int correct = strcmp(attempt, PIN);

If you enter 123456 on the keypad, correct will be 0;
If you enter 654321 on the keypad, correct will not be 0.

  for (int q=0; q<6; q++) 
  {
    if (attempt[q]==PIN[q])
    {
      correct++;
    }
  }

This is trying to do the same thing strcmp() is doing, except that strcmp() is smart enough to make sure that both strings are the same length. This code is NOT needed, and, having it, in fact, is wrong.

I think I have managed to sort it. I have included my code snippet. Does this look right, It seems to be working ok.

void checkPIN()
{
  int correct = strcmp(attempt, PIN);
  
if (correct < 0)        
        systemUnSet();
    
else if (correct == 0)
        systemSet();
    
else if (correct > 0)
        systemUnSet();
 
    return;
    
  for (int zz=0; zz<16; zz++) // wipe attempt
  {
    attempt[zz]=0;
  }
}

It certainly looks cleaner.

It certainly looks cleaner.

It would look even better if you used Tools + Auto Format.

    return;
    
  for (int zz=0; zz<16; zz++) // wipe attempt
  {
    attempt[zz]=0;
  }

We're done. Oh, and by the way do this. No. Doesn't work that way. Nothing after the return statement is executed. And, in a string, you only need one NULL.

It would look even better if you used Tools + Auto Format

.

Yes your right, I normally do.

We're done. Oh, and by the way do this. No. Doesn't work that way. Nothing after the return statement is executed. And, in a string, you only need one NULL.

Yes, I see.....how do you know so much! :blush:

how do you know so much!

I know how to debug my code. Good thing, too. Some of it is pretty buggy. I've been making a living doing computer programming for 33 years now.

PaulS:

how do you know so much!

I know how to debug my code. Good thing, too. Some of it is pretty buggy. I've been making a living doing computer programming for 33 years now.

+Karma to Paul. You are the man 8)