Arduino Uno & 4x4 keypad: Code not functioning as expected.

#include <Wire.h>
#include <hd44780.h>                       // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header

hd44780_I2Cexp lcd; // declare lcd object: auto locate & config exapander chip

// LCD geometry
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char keys[ROWS][COLS] =
{
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {6, 7, 8, 9}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 12, 11}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
bool a = true;
const unsigned long LongPress = 300;
bool j = true;

void setup()
{
  Serial.begin(115200);
  lcd.begin(LCD_COLS, LCD_ROWS);
  lcd.setCursor(3, 0);
  lcd.print("  PRIYA");
  lcd.setCursor(3, 1);
  lcd.print("ELECTRONICS");
  delay(100);
  for (int positionCounter = 0; positionCounter < 40; positionCounter++)
  {
    lcd.scrollDisplayLeft();
    delay(120);
  }
  delay(800);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set Count:");
}

void SetCount()
{
  const byte entryMaxSize = 5;
  static char digits[entryMaxSize + 1];
  static byte x; // index

  char key = keypad.getKey();

  if (key != NO_KEY) // Check for a valid key.
  {
    switch (key)
    {
      case 'A':
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Set Count:");
        x = 0; // reset the counter
        a = true;
        break;

      case 'D':
        if (a)
        {
          if (x == 0)
          {
            invalidCount();
          }
          else
          {
            lcd.clear();
            lcd.print("Count Set:");
            for (byte i = 0; i < x; i++)
            {
              lcd.print(digits[i]);
              a = false;
            }
            unsigned long count = atol(digits);
            Serial.print(count);
          }
        }
        break;

      default:
        if (a)
        {
          // if not 5 characters yet
          if (x < entryMaxSize)
          {
            // add key to userinput array and increment counter
            if ( key >= '0' && key <= '9' ) // key is of type char and has a value between 0 and 9 so do something with it.
            {
              digits[x++] = key;
              digits[x] = '\0';
              lcd.print( key ) ;  // new print statement <<<<<<<<<<<<<<<<
              //total = ((count[ 0 ] - '0') * 10000UL  + (count[ 1 ] - '0') * 1000 + (count[ 2 ] - '0') * 100 + (count[3] - '0') * 10 + (count[4] - '0'));
            }
          }
          else
          {
            countWarning();
          }
        }
        break;
    }
  }
}

void invalidCount()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Invalid Count!!");
  lcd.setCursor(0, 1);
  lcd.print("Press A"); // suggesting the user to enter the count again
  a = false;
}

void countWarning()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("5 Digits Only!!"); // warning for the user if more than permitted digits are entered
  lcd.setCursor(0, 1);
  lcd.print("Press A"); // suggesting the user to enter the count again
  a = false;
}

void loop()
{
  if (keypad.getKeys()) // check for keypad activity
  {
    // we won't handle multiple keypresses, just single ones, so just key index 0
    const byte key = keypad.key[0].kchar;
    const byte state = keypad.key[0].kstate; // IDLE, PRESSED, HOLD, RELEASED

    switch (key)
    {
      case '#':
        {
          static unsigned long pressedTime; // static so the value is remembered like a global
          if (state == PRESSED)
          {
            pressedTime = millis();
          }
          else if (state == RELEASED)
          {
            if (millis() - pressedTime > LongPress)
            {
              handleLongPress();
            }
            else
            {
              handleShortPress();
            }
          }
        }
        break;

      case 'D':
        {
          if (!j)
          {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Timer Mode Set!");
          }
          else
          {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Sensor Mode Set!");
          }
        }
        break;
    }
  }
}

void handleShortPress()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Mode:");
  lcd.setCursor(5, 0);
  lcd.print("TimerBased");
  j = false;
  //Serial.println("short");
}

void handleLongPress()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Mode:");
  lcd.setCursor(5, 0);
  lcd.print("SensorBased");
  j = true;
  //Serial.println("long");
}

I'm not able to set the count by entering the digits, the 'A' key is not working. The '#' key allows me to set the modes. The enter key 'D' displays the mode to be set. Nothing from the void SetCount is working.

Nothing from the void SetCount is working.

Where in the code is the SetCount () function (it is not a "void") called ?

UKHeliBob:
Where in the code is the SetCount () function (it is not a "void") called ?

Sorry, I didn't get you.

You have a function named SetCount()

Where in the code is it called ?

Yea I've not called it, that's a mistake. After the set up(), first I want to be able to set the count and then the mode. How do I do that?

How do I do that?

Put the code for each in separate functions and call them from loop() would be the obvious answer, but I am not clear as to what the program is intended to do.

If the count and mode settings are to be done only once in the program then call the functions from setup() instead of loop()

Hi,
Did you write code JUST for the keyboard and test that it works through the serial monitor?

Tom.... :slight_smile:

UKHeliBob:
Put the code for each in separate functions and call them from loop() would be the obvious answer, but I am not clear as to what the program is intended to do.

I want to "set a count" which will be an unsigned long value(max value= 99,999). After the count is set I want to set a mode, which could be a sensor based i.e. an IR sensor will be used. The other mode is the Timer based.

The pulses from the IR sensor will be compared to the "set count continuously" and until they become equal some operation will be performed.

In the timer based mode, there will be no need of an IR mechanism, since auto-counting will be done based on the time set by the user. The max value that can be set will be 99,999.

Throughout the day if user wishes to change the " set count" he must be able to do that.
Also if he wishes to use auto-counting i.e. Timer based mode instead of the Sensor based mode, he must be able to do that.

TomGeorge:
Hi,
Did you write code JUST for the keyboard and test that it works through the serial monitor?

Tom.... :slight_smile:

Didn't get you.

Only able to set the count not able to set the mode.

#include <Wire.h>
#include <hd44780.h>                       // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header

hd44780_I2Cexp lcd; // declare lcd object: auto locate & config exapander chip

// LCD geometry
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char keys[ROWS][COLS] =
{
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {6, 7, 8, 9}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 12, 11}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
bool a = true;
const unsigned long LongPress = 300;
bool j = true;

void setup()
{
  Serial.begin(115200);
  lcd.begin(LCD_COLS, LCD_ROWS);
  lcd.setCursor(3, 0);
  lcd.print("  PRIYA");
  lcd.setCursor(3, 1);
  lcd.print("ELECTRONICS");
  delay(100);
  for (int positionCounter = 0; positionCounter < 40; positionCounter++)
  {
    lcd.scrollDisplayLeft();
    delay(120);
  }
  delay(800);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set Count:");
}

void loop()
{
setCount();
}


void setCount()
{
  const byte entryMaxSize = 5;
  static char digits[entryMaxSize + 1];
  static byte x; // index

  char key = keypad.getKey();

  if (key != NO_KEY) // Check for a valid key.
  {
    switch (key)
    {
      case 'A':
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Set Count:");
        x = 0; // reset the counter
        a = true;
        break;

      case 'D':
        if (a)
        {
          if (x == 0)
          {
            invalidCount();
          }
          else
          {
            lcd.clear();
            lcd.print("Count Set:");
            for (byte i = 0; i < x; i++)
            {
              lcd.print(digits[i]);
              a = false;
            }
            unsigned long count = atol(digits);
            Serial.print(count);
            }     
        }
        break;

      default:
        if (a)
        {
          // if not 5 characters yet
          if (x < entryMaxSize)
          {
            // add key to userinput array and increment counter
            if ( key >= '0' && key <= '9' ) // key is of type char and has a value between 0 and 9 so do something with it.
            {
              digits[x++] = key;
              digits[x] = '\0';
              lcd.print( key ) ;  // new print statement <<<<<<<<<<<<<<<<
              //total = ((count[ 0 ] - '0') * 10000UL  + (count[ 1 ] - '0') * 1000 + (count[ 2 ] - '0') * 100 + (count[3] - '0') * 10 + (count[4] - '0'));
            }
          }
          else
          {
            countWarning();
          }
        }
        break;
    }
  }
}

void invalidCount()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Invalid Count!!");
  lcd.setCursor(0, 1);
  lcd.print("Press A"); // suggesting the user to enter the count again
  a = false;
}

void countWarning()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("5 Digits Only!!"); // warning for the user if more than permitted digits are entered
  lcd.setCursor(0, 1);
  lcd.print("Press A"); // suggesting the user to enter the count again
  a = false;
}

void setMode()
{
  if (keypad.getKeys()) // check for keypad activity
  {
    // we won't handle multiple keypresses, just single ones, so just key index 0
    const byte key = keypad.key[0].kchar;
    const byte state = keypad.key[0].kstate; // IDLE, PRESSED, HOLD, RELEASED

    switch (key)
    {
      case '#':
        {
          static unsigned long pressedTime; // static so the value is remembered like a global
          if (state == PRESSED)
          {
            pressedTime = millis();
          }
          else if (state == RELEASED)
          {
            if (millis() - pressedTime > LongPress)
            {
              handleLongPress();
            }
            else
            {
              handleShortPress();
            }
          }
        }
        break;

      case 'D':
        {
          if (!j)
          {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Timer Mode Set!");
          }
          else
          {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Sensor Mode Set!");
          }
        }
        break;
    }
  }
}

void handleShortPress()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Mode:");
  lcd.setCursor(5, 0);
  lcd.print("TimerBased");
  j = false;
  //Serial.println("short");
}

void handleLongPress()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Mode:");
  lcd.setCursor(5, 0);
  lcd.print("SensorBased");
  j = true;
  //Serial.println("long");
}

after Count set : ... has been displayed, sensor based mode is to be displayed as default mode with following code:

lcd.setCursor(0, 0);
  lcd.print("Mode:");
  lcd.setCursor(5, 0);
  lcd.print("SensorBased");    //default mode
            unsigned long count = atol(digits);
            Serial.print(count);

All that you are doing with count is printing it to the serial port. The variable then goes out of scope. That is almost certainly NOT what you want to be doing.

loop() should, apparently, NOT be doing anything until setCount() has executed this code, and it should not call setCount() again until something has happened count times. But, all it does is call setCount() over and over.

after Count set : ... has been displayed, sensor based mode is to be displayed as default mode with following code

That snippet of code is from handleLongPress() which is called, maybe, by setMode(). But, where is setMode() called? Nowhere that I can see.

GautamD:
Didn't get you.

Did you write your code in stages, each stage you get working, before coding the next.
So you must have some code that is only code for interpreting the keyboard presses.
Does this code work.
What I am trying to get at is, does your code read the keypad correctly.
Tom... :slight_smile:

TomGeorge:
Did you write your code in stages, each stage you get working, before coding the next.
So you must have some code that is only code for interpreting the keyboard presses.
Does this code work.
What I am trying to get at is, does your code read the keypad correctly.
Tom... :slight_smile:

Hi Tom. I've written the code in stages. What you are seeing is a combination of 2 codes which work very well separately, which if you want to see I can post the 2 separate codes here. My code reads keyboard presses correctly.

Hi,

GautamD:
Hi Tom. I've written the code in stages. What you are seeing is a combination of 2 codes which work very well separately, which if you want to see I can post the 2 separate codes here. My code reads keyboard presses correctly.

Thats good mate, a lot of the time we get code that has been written on one go and has all sorts of problems.
Doing it in stages as you have, means there is some config or coding clash that has to be found.
Tom.. :slight_smile:

GautamD:
I want to "set a count" which will be an unsigned long value(max value= 99,999). After the count is set I want to set a mode, which could be a sensor based i.e. an IR sensor will be used. The other mode is the Timer based.

The pulses from the IR sensor will be compared to the "set count continuously" and until they become equal some operation will be performed.

In the timer based mode, there will be no need of an IR mechanism, since auto-counting will be done based on the time set by the user. The max value that can be set will be 99,999.

Throughout the day if user wishes to change the " set count" he must be able to do that.
Also if he wishes to use auto-counting i.e. Timer based mode instead of the Sensor based mode, he must be able to do that.

Hello Tom, this is what I actually wanna implement but all I am able to do till now is set the count but not able to set the mode. Could you help me out? Thanks.

all I am able to do till now is set the count but not able to set the mode.

You can't even do that. What you are currently doing is converting the key text into a number that you discard immediately.

You have then written some code you never call.

When do you plan to actually call that code?

PaulS:
You can't even do that. What you are currently doing is converting the key text into a number that you discard immediately.

I don't mean to discard the unsigned long value but store it in the internal EEPROM. Right now I'm not sure how to do it.

PaulS:
You have then written some code you never call.

When do you plan to actually call that code?

After storing the number I mean to call the code for setting the mode.

Right now I'm not sure how to do it.

Step 1. Read the EEPROM reference page.
Step 2. Try something.
Step 3. Post your code if you have problems.

Is it possible to use a ring buffer while storing the unsigned long value in EEPROM. More importantly is it necessary in my case?

PaulS:
Step 1. Read the EEPROM reference page.
Step 2. Try something.
Step 3. Post your code if you have problems.

PaulS:
Step 1. Read the EEPROM reference page.
Step 2. Try something.
Step 3. Post your code if you have problems.

I've checked the EEPROM.h library as well as the EEPROM extended library. Not sure which to use..

Is it possible to use a ring buffer while storing the unsigned long value in EEPROM.

Anything's possible.

More importantly is it necessary in my case?

No. The EEPROM.put() method knows how to store a long, and the EEPROM.get() method knows how to read one.