Go Down

Topic: Set countdown time with Keypad? (Read 5 times) previous topic - next topic

TiboJ

Hello everyone,

I'm currently making a timed smokebomb for Airsoft games.
Now the problem is that I can't find out how to set the countdown time with my keypad, and display it on the 16x2 lcd.

I've already setup a case for the countdown(see my code), but it can only display the seconds.

I want to let it display the minutes and/or seconds.


For inputting the countdown time, one of the following input methods should be good:

Option 1) Input seconds and let the Arduino convert it to minutes & seconds.
for example: I input 90 seconds(I type 90 on the keypad) >>> it becomes 1 minute and 30 seconds(1:30 on the lcd).

Option 2) Make it possible to input the minutes and the seconds >>>  so no convert from seconds to minutes & seconds.

So it doesn't matter if I only can set the countdown time in seconds, and let the countdown clock only display seconds.
If one of this options work, then it's good.

So basically I'm stuck with two cases:

case SETTIME:

// set countdown time with keypad

break;

case ARMED:

// count to zero from the set countdown time

break;

Code: [Select]
case ARMED:
{
lcd.clear();
while(time > 0) {
lcd.setCursor(0,0);
   lcd.print("Time remaining:");
   time -= 1;
   delay(1000);
   lcd.clear();
   lcd.setCursor(7,1);
   lcd.print(time);
}
if(time == 0) {
     lcd.print("Bomb exploded!");

}
break;



Here is the full code that I currently have:

Code: [Select]
#include <LiquidCrystal.h>
#include <ShiftLCD.h>       // Include Library's
#include <Keypad.h>


#define SETTIME 0  //  case for setting time
#define ON 1       // case for arming bomb
#define ARMED 2    // case for countdown
#define DISARMED 3  // case for stopping countdown
#define DETONATED 4  // case for finished countdown

int bombState=0; // 0 = Set time, 1 = On, 2 = Armed ,3 = Disarmed, 4 = Detonated

const byte ROWS = 4; // Keypad has 4 rows
const byte COLS = 4; // Keypad has 4 columns
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},         // define which character is on a button
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {5,6,7,8}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {9,10,11,12}; //connect to the column pinouts of the keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );   

ShiftLCD lcd(2, 4, 3); // pins connected to LCD


char ArmCode[4] = {'7','8','9','1'};   // password to arm the bomb
char inputArray[4];   // array to gather user keypad presses
int i = 0; // store keypresses here



void setup()
{
  lcd.begin(16,2); // open the LCD
  // bootup screen
  lcd.print("AIRSOFT BOMB V1"); // print some text   
  delay(2500); // display text for 2500ms
  lcd.clear(); // clear display
}
void loop()
{
 
  // cases
  switch(bombState) {
   
////////////// SET COUNTDOWN TIME//////////////////////////   
    case SETTIME:

HERE I WANT THE CODE TO SET THE TIME WITH THE KEYPAD
       
        break;
   
///////////////////////////////////////////////////////////
//////////////////////ENTER ARM CODE/////////////////////////
    case ON: // enter the right code to arm the bomb
  {

    lcd.setCursor(0,0);
  lcd.print("Enter Code:");

char key = keypad.getKey();
//if a key is pressed
if(key)
{
  inputArray[i] = key; //store entry into array
  i++;
  lcd.setCursor(0,1); // set cursor at column 0 and row 1
lcd.print(key); // print keypad character entry to lcd
if (i == 4) // if 4 presses on the keypad have been made
{
   if (inputArray[0] == ArmCode[0] && // if first character pressed on keypad = first character in password for arming bomb
inputArray[1] == ArmCode[1] &&  // and if second character pressed on keypad = second character in password for arming bomb
inputArray[2] == ArmCode[2] && // and if third character pressed on keypad = third character in password for arming bomb
inputArray[3] == ArmCode[3]) // and if fourth charactet pressed on keypad = fourth character in password for arming bomb
{
  lcd.clear(); // clear display
  lcd.setCursor(0,0);
  lcd.print("Bomb Armed!"); // display some text
  delay(1000); // display text for 1000ms
  bombState = ARMED; // switch to ARMED case
 
}
}
}
  }
break;

////////////////////////////////////////////////////////////////////////

case ARMED:
{
lcd.clear();
while(time > 0) {
lcd.setCursor(0,0);
   lcd.print("Time remaining:");
   time -= 1;
   delay(1000);
   lcd.clear();
   lcd.setCursor(7,1);
   lcd.print(time);
}
if(time == 0) {
     lcd.print("Bomb exploded!");

}
break;


  }
}
}


 


How should I do this?
I would really appreciate help on this ;)

Regards,
Tibo

PaulS

Code: [Select]
    case SETTIME:

HERE I WANT THE CODE TO SET THE TIME WITH THE KEYPAD
       
        break;

What have you tried? Do you know how to detect that a key was pressed?Do you know how to detect which key was pressed?

TiboJ


Quote
What have you tried?

I tried to to use chars. But it was a mess and I didn't save the code.

Quote
Do you know how to detect that a key was pressed?
Do you know how to detect which key was pressed?


I can make a key do something like this:

char SetTimeButton[1] = {'*'};

   if(SetTimeButton[1]) {
     
      lcd.clear();
      bombState = ON;

PaulS

Quote
I can make a key do something like this:

Not without errors, you can't. The value in the [] on the declaration statement is the number of elements. Element indices start at 0.

Quote
But it was a mess and I didn't save the code.

Try something (that, preferably, isn't a mess) and show what you have.

Getting a key press is easy. Getting the key that was pressed is easy. Storing the character that corresponds to the key is easy. Incrementing the index after storing the character is easy. Adding a NULL after adding each character is easy. Converting the stored data to a number is easy. Doing all these things at the right place in the code, in the right order is the only challenging part.

TiboJ


Quote
I can make a key do something like this:

Not without errors, you can't. The value in the [] on the declaration statement is the number of elements. Element indices start at 0.


It did work, here is the full code:
(I was trying to set the time value with a potentiometer, and it worked but it was difficult to get the right value.)

Code: [Select]
include <LiquidCrystal.h>
#include <ShiftLCD.h>       // Include Library's
#include <Keypad.h>


#define SETTIME 0  //  case for setting time
#define ON 1       // case for arming bomb
#define ARMED 2    // case for countdown
#define DISARMED 3  // case for stopping countdown
#define DETONATED 4  // case for finished countdown

int bombState=0; // 0 = Set time, 1 = On, 2 = Armed ,3 = Disarmed, 4 = Detonated

int potPin = 0;
int time;
int setTime;

const byte ROWS = 4; // Keypad has 4 rows
const byte COLS = 4; // Keypad has 4 columns
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},         // define which character is on a button
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {5,6,7,8}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {9,10,11,12}; //connect to the column pinouts of the keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );   

ShiftLCD lcd(2, 4, 3); // pins connected to LCD

char SetTimeButton[1] = {'*'};
char ArmCode[4] = {'7','8','9','1'};   // password to arm the bomb
char inputArray[4];   // array to gather user keypad presses
int i = 0; // store keypresses here



void setup()
{
  lcd.begin(16,2); // open the LCD
  // bootup screen
  lcd.print("AIRSOFT BOMB V1"); // print some text   
  delay(2500); // display text for 2500ms
  lcd.clear(); // clear display
}
void loop()
{
 
  // cases
  switch(bombState) {
   
////////////// SET COUNTDOWN TIME//////////////////////////   
    case SETTIME: // set the countdown time
    {
     
     
      time = analogRead(potPin);
      setTime = (time / 1.024) * 3.6;
      lcd.setCursor(0,0);
      lcd.print("Set Time(s)");
      lcd.setCursor(0,1);
      lcd.print(setTime);

       char key = keypad.getKey();
//if a key is pressed
if(key)
{
 

    if(SetTimeButton[1]) {
     
      lcd.clear();
      bombState = ON;
     
     
     
    }
     
}
    }
        break;
   
   
///////////////////////////////////////////////////////////
//////////////////////ENTER ARM CODE/////////////////////////
    case ON: // enter the right code to arm the bomb
  {

    lcd.setCursor(0,0);
  lcd.print("Enter Code:");

char key = keypad.getKey();
//if a key is pressed
if(key)
{
  inputArray[i] = key; //store entry into array
  i++;
  lcd.setCursor(0,1); // set cursor at column 0 and row 1
lcd.print(key); // print keypad character entry to lcd
if (i == 4) // if 4 presses on the keypad have been made
{
   if (inputArray[0] == ArmCode[0] && // if first character pressed on keypad = first character in password for arming bomb
inputArray[1] == ArmCode[1] &&  // and if second character pressed on keypad = second character in password for arming bomb
inputArray[2] == ArmCode[2] && // and if third character pressed on keypad = third character in password for arming bomb
inputArray[3] == ArmCode[3]) // and if fourth charactet pressed on keypad = fourth character in password for arming bomb
{
  lcd.clear(); // clear display
  lcd.setCursor(0,0);
  lcd.print("Bomb Armed!"); // display some text
  delay(1000); // display text for 1000ms
  bombState = ARMED; // switch to ARMED case
  lcd.clear();
}
}
}
  }
break;

////////////////////////////////////////////////////////////////////////

case ARMED:
{

while(setTime > 0) {
lcd.setCursor(0,0);
   lcd.print("Time remaining:");
   setTime -= 1;
   delay(1000);
   lcd.clear();
   lcd.setCursor(7,1);
   lcd.print(setTime);

}

}
break;


  }
}



 


Quote
Try something (that, preferably, isn't a mess) and show what you have.

Getting a key press is easy. Getting the key that was pressed is easy. Storing the character that corresponds to the key is easy. Incrementing the index after storing the character is easy. Adding a NULL after adding each character is easy. Converting the stored data to a number is easy. Doing all these things at the right place in the code, in the right order is the only challenging part.


I will try it again.

PaulS

Code: [Select]
    if(SetTimeButton[1]) {
This references the second element in a one element array. It is NOT correct.

TiboJ


Code: [Select]
    if(SetTimeButton[1]) {
This references the second element in a one element array. It is NOT correct.


That's strange, it really works...
If I press the '*' button, then the lcd clears and switches to the countdown.
If I enter a '0' like you said, then I get this error:
too many initializers for 'char
  • '

PaulS

Quote
If I enter a '0' like you said, then I get this error:
too many initializers for 'char

    '

You don't change the declaration to declare a smaller array. You need to change the reference to refer to a valid element in the array.

TiboJ

#8
Apr 03, 2012, 06:48 pm Last Edit: Apr 03, 2012, 06:51 pm by TiboJ Reason: 1
Okay, here is what I've tried:
(I've leaved the ARMED case as it is)

-First attempt:

Errors:
If the 'Set Time' menu appears and I enter the time I want, then it redirects to the ARMED case when I press 1 number on the keypad.
It needs to have the possibility to enter more then 1 number.

Code: [Select]


char SetTimeButton[1] = {'*'};
int x = 0; // store the keypresses here

case SETTIME: // set the countdown time
    {
     
char TimeCode[4];

     
   lcd.setCursor(0,0);
  lcd.print("Enter Time");
 
       char key = keypad.getKey();
//if a key is pressed
if(key)
{

  TimeCode[x] = key;
  x++;
  x = setTime;
  lcd.setCursor(0,1); // set cursor at column 0 and row 1
lcd.print(setTime); // print keypad character entry to lcd
if(key)
{
    if(SetTimeButton[0]) {
     
      lcd.clear();
      bombState = ON;

    }
     
}
    }
}
   
        break;



-Second attempt:

Errors: - I can enter the time with 4 numbers this time, but I can't enter a time value with less then 4 numbers.
           - If the 4 numbers  are entered, then the countdown won't start >> just an empty screen.



Code: [Select]
char TimeCode[4];
int x = 0; // store the keypresses here

if(key)
{

  TimeCode[x] = key;
  x++;
  lcd.setCursor(0,1); // set cursor at column 0 and row 1
lcd.print(key); // print keypad character entry to lcd
if (x == 4) // if 4 presses on the keypad have been made
{
      x = setTime;
      lcd.clear();
      bombState = ON;

    }
     
}
    }
        break;



   

How should I create an array that doesn't have a minium of keys to be entered?

PaulS

Quote
How should I create an array that doesn't have a minium of keys to be entered?

It's not a matter of creating the array. It is a matter of how you populate the array.

What do you do when you come to the end of a sentence when typing a post? What do you do when you come to the end of a statement in the sketch? You use a special character to say "Hey, this sentence/statement is complete. Do something with it".

You need to do the same in your code. Allow up to 4 characters, with one of the special keys meaning backspace (oops, didn't want that number there) and one meaning enter/done/use this value, regardless of how many digits have been entered.

TiboJ

Quote
You need to do the same in your code. Allow up to 4 characters, with one of the special keys meaning backspace (oops, didn't want that number there) and one meaning enter/done/use this value, regardless of how many digits have been entered.


Do you mean something like this?:
(backspace key is not included)

Code: [Select]
////////////// SET COUNTDOWN TIME//////////////////////////   

char TimeCode[4];
char enterButton[1] = {'#'};

    case SETTIME: // set the countdown time
    {
     
     
   lcd.setCursor(0,0);
  lcd.print("Enter Time:");
 
       char key = keypad.getKey();
//if a key is pressed
if(key)
{

  TimeCode[x] = key;
  x++;
  lcd.setCursor(0,1); // set cursor at column 0 and row 1
lcd.print(key); // print keypad character entry to lcd
if(enterButton['#']);

{
      x = setTime;
      lcd.clear();
      bombState = ON;
     
   
}
     
}
    }
        break;


If I upload this code, then I get the error from my first attempt:
Quote

If the 'Set Time' menu appears and I enter the time I want, then it redirects to the ARMED case when I press 1 number on the keypad.


So maybe I'm understanding you wrong?



Sacman

#11
Apr 04, 2012, 01:47 am Last Edit: Apr 04, 2012, 01:49 am by Sacman Reason: 1
You have obviously used the example for the keypad so why not use the rest of the example:

Code: [Select]


if(key)
{
if (bombstate > 0)    //As long as you are not in settime mode, the following runs
{
  switch(key)
   {
     case '#':
       x = setTime;
       lcd.clear();
       bombState = ON;
       break;
     case '*':
       bombstate = 0;    //Use the * Key to put you in time set mode
       break;
     default:
       appendentry(key);    //Pass the key press to append it to your char[]
       break;
     }
 }
 else        //If you are in time set mode, you want to handle your button presses differently
 {
   switch(key)
   {
     case '#':
       //use this to accept your time entry once you have entered 4 numbers. I would pass this to a function that handles that.
       break;
     case 'A':
       break;    //Don't do anything on the Letters unless you want to
     case 'B':
       break;
     case 'C':
       break;
     case 'D':
       break;
     default:
       settime(key);    //Pass the key press to a settime function
       break;
   }
 }
}



This is not the only way but it will get you well on your way.

Since you are using single digits, it is simple to convert a byte to an int by subtracting '0'.

In other words:

Code: [Select]


int tempvalue = key - '0';



So everytime you call your settime function, you can use simple math to convert it to seconds for your counter and minutes and seconds for display. You want to limit your time entry to 4 digits (two for minutes, and two for seconds) so use an index counter each time you call you settime function. If you try to call it again before they have hit the '#' ignore it. You will have to stipulate the the time be entered as 4 digits. Otherwise someone will try to enter 3 minutes and 2 seconds as only 2 digits and it won't work. Another option is to use the '*' key as your colon and capture its input as well and format accordingly.

There is still a LOT of work to do even with this starting point. I just happen to be working with a keypad for the first time myself and it is fresh.

Luck,

Wade

TiboJ

#12
Apr 04, 2012, 10:09 am Last Edit: Apr 04, 2012, 10:11 am by TiboJ Reason: 1
Thanks for helping.

But were do I need to put this code?
Does it need to be inside the 'switch(bombState)' block?

And I assume that the following code has to be in the 'else' block because it's not possible to set the time if I'm not in the SETTIME case? :

Code: [Select]
if (bombstate > 0)    //As long as you are not in settime mode, the following runs
{
   switch(key)
    {
      case '#':
        x = setTime;
        lcd.clear();
        bombState = ON;
        break;

Sacman

Try to think of it this way.

When you turn the device on. It will do nothing until you enter a code that allows you to set it up. So accept a 4 digit input and once you press the "#" it will check that entered code against a setup code like 9999. If it is right then set your mode to setup that way the next entries you make will be for your time. If it is wrong, it will reset the entry index and clear your entered code allowing them to try again. Add a boolean variable that is false when your turn it on. Once you have set your time you turn it to true. Also add a second boolean that you can set if the code entered matches the setup password. Then you can check those in your loop. If setuppass == true then the next buttons you press will be for setting time. Once your time is completely set, set your setup to true.

After you have performed that, you will now accept input to arm and disarm. The code I included above doesn't quite handle that but it is close. You have to think about what you are trying to do. Use functions to handle tasks to keep your loop clean.

For example, you would have a function that handles a '#' call so you can check what code was entered. Only call that function if they have entered enough digits to satisfy your passcode. Use your incremented index to check for that. So if they enter 3 digits and press '#' it ignores it but if they enter 4 digits it calls it. Use a function to handle your time entry. 

I am doing something that is similar but doesn't include setting time. If I posted the code as it exists, it would be way too difficult to walk you through step by step since it is about 200 lines of code. I just tackled it one step at a time. For example, make sure you can check an entered code against a saved code. Make sure that you can only press 4 keys (if that is the number of digits for your code) and any further presses are ignored. Make sure that the '#' calls your function to check the code but only after you have entered enough digits. Use Serial to debug print. This will be a life saver. I have an RGB LED that I change to different colors as well to make sure the codes I am entering do the right thing.

If you work your way through methodically it will make a lot more sense than if you simply copy and paste code. That's what Paul is trying to get you to do and why I didn't include everything in the code above. What you are trying to do, like Paul has said is easy in its individual parts but much more difficult in making sure they happen when you want them to. This is where boolean flags, if statements and switch cases are your friends and are ultimately the heart and soul of any program you write.

One last note about the last thing you posted. Are you going to arm just by pressing the # key or is there an arming code? A lot of what I just typed assumes that you would be using a code to arm as well. Not necessary but what if you accidentally hit that # key when you aren't ready for it? The code you posted basically does that but I would encourage you to reconsider.
Luck,

Wade

TiboJ

#14
Apr 04, 2012, 07:08 pm Last Edit: Apr 04, 2012, 07:15 pm by TiboJ Reason: 1
You made your work of it, man.  :smiley-eek: :D Thank you!

Quote
For example, you would have a function that handles a '#' call so you can check what code was entered. Only call that function if they have entered enough digits to satisfy your passcode. Use your incremented index to check for that. So if they enter 3 digits and press '#' it ignores it but if they enter 4 digits it calls it. Use a function to handle your time entry. 


I also want the possibility to enter the time with less then 4 numbers. Otherwise I cannot enter 60 seconds for example.

Quote
One last note about the last thing you posted. Are you going to arm just by pressing the # key or is there an arming code? A lot of what I just typed assumes that you would be using a code to arm as well. Not necessary but what if you accidentally hit that # key when you aren't ready for it? The code you posted basically does that but I would encourage you to reconsider.


The user needs to type a code to arm the bomb, as in my code.

Quote
Use Serial to debug print.


What do you mean?


I will try to type the code like you said now. If I'm stuck again, then I'll post a message here.

Big thanks for help guys!


Go Up