Need help setting up a countdown timer which you set using a keypad

Hi all, so this project is a first for me and I'm a little bit stumped on getting it going, I've never dealt with proper countdown timers much, except for basic ones that use the length of the loop for timing. I've looked at quite a few projects but haven't seen anything that's easy to use the code from.

I have a 6 section 7 segment display that uses a tm1637 chip, and found a library that makes using that easy, and a matrix keypad that I salvaged and got working, and the goal is to have the user set the timer in hours, minutes and seconds, updating the display with each keypress.

Then it will wait for a key press, probably the # key, and once that happens it counts down constantly updating the 7 segment display.

I want to have it so that if the timer is set with something one hour or over, it's formatted as such:

HH MM SS

but then when it drops below one hour, it switches to

HH MM MS

so that you get a nicely animated countdown. From other projects I've read threads on, it's not necessarily possible to accurately count every single milisecond, but if it was 5 miliseconds at a time that would be plenty

In the main loop I'll monitor a switch input, and if it's triggered then pause the timer and switch to a different section that alternates the display on and off with the remaining time, that should be easy enough to set, the timer code itself is where I'm really stuck, and hopefully once I have a basic set up for that I can make progress quickly.

Here's my code , nothing much really but shows the structure I want.

#include <Keypad.h>
#include <Arduino.h>
#include <TM1637TinyDisplay6.h>

// Module connection pins (Digital Pins)
#define CLK 10
#define DIO 11

const byte ROWS = 4; 
const byte COLS = 3; 

char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};

byte rowPins[ROWS] = {2, 3, 4, 5}; 
byte colPins[COLS] = {6, 7, 8}; 

Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); 
TM1637TinyDisplay6 display(CLK, DIO);

//timer stuff
int time_s = 0;
int time_m = 0;
int time_h = 0;

int set = 0;
int flag1=0, flag2=0;

//timer stuff



void setup(){
  Serial.begin(9600);
  display.setBrightness(BRIGHT_LOW);
  display.clear();
  display.showString("rEADY");
  delay(1000);
  settimer();
}
  
void loop(){
  //update time on display
  //check switch input, if low go to function that flashes remaining time.

}

void settimer(){
  //set hours
  //set minutes
  //set seconds

  //wait for keypress to go to loop
    char customKey = customKeypad.getKey();  
  if (customKey == #){
    Serial.println(customKey);
  }
}

void timerstopped(){
  //stop timer
  //trigger output
  //blink display on/off with a while statement
}


having it beep every 10 seconds with a peizo buzzer, and once per second when it's down to 30 seconds is also part of it but I think I should be able to figure that out once I have the timer set up and working.

Show us a good schematic of your circuit.
Show us a good image of your ‘actual’ wiring.
Give links to components.

The keypad and screen are already working so those are a non issue, but here's the display module I used.

and the library

The electrical side isn't what I need assistance with, reading inputs from switches or whatever else will be simple, it's just setting up the countdown timer which is the problem.

As in entering values for each unit?

That, but also how you actually set up the countdown timer to run, I've only used millis functions for timing without a delay, but I'm guessing that's the optimal way to count down a timer in this method too.

Unfortunately in all the projects I've found that are similar they seem to be going about it in a fairly messy manner or are triggering extra things which I don't need while counting down.

I'll have the screen say set time, and then get it to display 00---- for the hour entry, you enter whichever you want, and then have it change to (for a 2 hour timer) 0200-- when setting the minutes, then 020500 which would basically be two hours, five minutes, zero seconds.

So, I know I need integers for the hours, minutes, seconds and maybe milliseconds, for setting the timer, then in the mail loop I can take the desired timer, subtract currentmilis from that (or something similar), adjust the string that the display will show and just update that every time the loop repeats which should make a nice, fast countdown visible on it.

Clarification on the overcomplicated note, one example, though I can't find the project off hand, is that it was written so that in every section setting the time it had an if statement for the key presses, for every number 0 through 9 which was massively long and horribly messy to look through, and there surely is a better way to do it than that so you don't have 60 if statements to enter hours, minutes, and seconds in two digit format.

That would also use up a load of program space.

edit: Also totally fine just outputting to serial for now, that's the plan anyhow, and figuring out the display after testing the timer entry and other inputs.

Sorry but after reading all this, not sure which part you are having trouble with.

Do you know how to write a sketch which sends a count down time, every second to the serial monitor.

If I just wanted to have a preset time tick down every second yes, I'd just decrease the value each time the loop runs through with a one second delay with decrement, in otherwords have something along the line of int timer = 500 and once per loop just use timer--;.

I'm not sure how I can go about setting the timer in hours/minutes/seconds based on user input though.

For the sake of making it easy to have explained to me, having it all run in the serial monitor would be fine, and then I can probably hook the input from the keypad into that and instead of using serial.read use the keypad library to grab whatever button is pressed.

So basically I need to see how this would work

Serial.println("enter hours");
Serial.read();
however that gets entered to be hours (maybe time_h = Serial.Read();
Serial.println("enter minutes");
Serial.read();
however that gets entered to be minutes
Serial.println("enter seconds");
Serial.read();
however that gets entered to be seconds

and then the best way to count that total time down.

But when entering the numbers on the keypad, for 12 hours it's going to receive 1 and then 2, not 12 at the same time.

I suppose you could convert hours, minutes and seconds to a total number of milliseconds, then add them all together and decrement that figure, and use math to spit that back out in a format to output as a string, but that doesn't seem like the best solution, even though I don't have enough programming under my belt to know what the more elegant one is.

Edit: actually that would be way too long of a figure in miliseconds, you'd probably have to do it in whole seconds, but then you wouldn't be able to get the fractions of a second resolution on a timer to have that last section count down when the timer only has minutes left.

Yes

Use unsigned long counter;

counter = ((hours * 3600) + (minutes * 60) + seconds ) * 1000ul; //number of milliseconds

Ah, alright, unsigned long handles a pretty insanely large number, so I guess doing it in total miliseconds is feasible, and for the display I can reverse that to get my display readout hopefully in a not too messy code wise manner.

So, for entering the set time in HH MM SS format, I'm going to need to hold onto the first number in a variable, get the second, and combine them, but not in addition, but just place one after the other so entering 1 and then 2 ends up being 12 hours, not three. Hmm.

This is the line that gets the input from the keypad, the library handles debounce and everything
char customKey = customKeypad.getKey();

So I basically need (these variable names probably aren't what I'll use, just to help explaining)

firstdigit = customKeypad.getKey();
seconddigit = customKeypad.getKey();

and then combine firstdigit and seconddigit (in an array?) to get an output of the two, one after the other so pressing 1 and then 2 is 12 hours, pressing 0 and then 2 is two hours.

The same will be for minutes and seconds but that's easy to do once I get one working.

Now, the keypad does have asterisk and pound, but I can use an if statement to reject those so that's easy to handle.

Test this for the countdown portion:


byte hours         = 0;
byte minutes       = 1;
byte seconds       = 0;
byte tenthSecond   = 0;

unsigned long counter;
unsigned long timeMICROS;
unsigned long secondMICROS;


void setup()
{
  Serial.begin(115200);  //  <---------------------<<<<<<<<<<<<<<<<<

  counter = ((hours * 3600) + (minutes * 60) + seconds ) * 1000ul; //number of milliseconds

} //END of setup()

void loop()
{
  unsigned long timeNow = micros();

  //*****************************************
  //every 1ms do this
  if (timeNow - timeMICROS >= 1000)
  {
    //restart the TIMER
    timeMICROS = timeNow;

    counter = counter - 1;
  }

  //*****************************************
  //every 10ms
  if (timeNow - secondMICROS >= 10000)
  {
    //restart the TIMER
    secondMICROS = timeNow;

    unsigned long num = counter;
    unsigned long leftDecimal = num / 1000;
    unsigned int rightDecimal = (counter - leftDecimal * 1000) / 10;
    Serial.print(leftDecimal);
    Serial.print(".");
    Serial.println(rightDecimal);
  }

} //END of loop()

Alright, I'll fire that up, also came up with this as a possible solution, though you can't use ints like I wrote but you get the idea.

  int firstdigit();
  int seconddigit();
  int hours();

void settimer(){
  //set hour section
  firstdigit == customKeypad.getKey();
  hours == (firstdigit * 10);
  seconddigit == customKeypad.getKey();
  hours == (hours + seconddigit);
  
}

edit: That does count down properly, I'll try to figure out my input section and stitch that in

Watch your usage of = vs == :wink:

Ah, right. That's one of those things that I always miss and then notice after a few minutes lol.

Hmm, so this should work, but it's just spitting out zero for all the serial print lines, except for when it gets to the main loop. Maybe this keypad library doesn't wait for a state change after all and only does debounce, or I have something slightly wrong as in the main loop it debounces fine and is responsive, and doesn't spit out 0 when no key is pressed.

#include <Keypad.h>
#include <TM1637TinyDisplay6.h>

// Module connection pins (Digital Pins)
#define CLK 10
#define DIO 11

const byte ROWS = 4; 
const byte COLS = 3; 

char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};

byte rowPins[ROWS] = {2, 3, 4, 5}; 
byte colPins[COLS] = {6, 7, 8}; 

Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); 
char customKey;
TM1637TinyDisplay6 display(CLK, DIO);


//timer stuff
int hours;
int minutes;
int seconds;


int sethours1;
int sethours2;
int setminutes1;
int setminutes2;
int setseconds1;
int setseconds2;


void setup(){
  Serial.begin(9600);
  display.setBrightness(BRIGHT_LOW);
  display.clear();
  display.showString("rEADY");
  delay(1000);
  settimer();
}
  
void loop(){
  //update time on display
  //check switch input, if low go to function that flashes remaining time.
  customKey = customKeypad.getKey();
  if (customKey){
    Serial.println(customKey);
  }
  }

void settimer(){
  //set first hour section  
  Serial.println("set hours");

  sethours1 = customKeypad.getKey();
  Serial.println(sethours1);
  hours = (sethours1 * 10);
  Serial.println(hours);

  sethours2 = customKeypad.getKey();
  Serial.println(sethours2);
  hours = (hours + sethours2);
  Serial.println(hours);
}



void timerstopped(){
  //stop timer
  //trigger output
  //blink display on/off with a while statement
}

edit: fixed a quick rename mistake

Changing it to this stops it spitting zeroes out, but I guess that's because it's being skipped altogether since it's not getting an input.

void settimer(){
  //set first hour section  
  Serial.println("set hours");
  
  customKeypad.getKey();
  if (customKey){
  sethours1 = customKey;
  Serial.println(sethours1);
  hours = (sethours1 * 10);
  Serial.println(hours);
  }

  customKeypad.getKey();
  if (customKey){
  sethours2 = customKey();
  Serial.println(sethours2);
  hours = (hours + sethours2);
  Serial.println(hours);
  }
  Serial.println("end of set timer");
}

Found a function in the library that does wait for a keypress based on an educated guess

customKeypad.waitForKey();

But need to figure out how then use the key it gets to set the hour variables.

The set timer loop as of now below, when you press a key it spits out two zeroes, so some progress.

void settimer(){
  //set first hour section  
  Serial.println("set hours");

  customKeypad.waitForKey();
  sethours1 = customKeypad.getKey();
  Serial.println(sethours1);
  hours = (sethours1 * 10);
  Serial.println(hours);

  customKeypad.waitForKey();
  sethours2 = customKeypad.getKey();
  Serial.println(sethours2);
  hours = (hours + sethours2);
  Serial.println(hours);
}

edit: Oh, duh,
sethours1 = customKeypad.waitForKey();

But when I press 1 it outputs sethours1 as 49, and then 490 for the total hours. Huh...

It must be because of how the library detects a keypress, but the customKeypad.getKey function must change the detected press for 1 (which comes in as 49 for whatever reason) to a 1

That's because you have defined ASCII numbers in your matrix.

Subtract 0x30 or put in integers. :wink:

Try this:

#include <Keypad.h>


const byte ROWS = 4;
const byte COLS = 3;

char hexaKeys[ROWS][COLS] =
{
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};

byte rowPins[ROWS] = {2, 3, 4, 5};
byte colPins[COLS] = {6, 7, 8};

Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
char customKey;

//timer stuff
byte hours;
byte minutes;
byte seconds;


byte sethours1;
byte sethours2;
byte setminutes1;
byte setminutes2;
byte setseconds1;
byte setseconds2;


void setup()
{
  Serial.begin(9600);

  delay(1000);
  settimer();

}

void loop()
{
  customKey = customKeypad.getKey();
  if (customKey)
  {
    Serial.println(customKey);
  }

}

void settimer()
{
  //set first hour section
  Serial.println("set hours");

  sethours1 = customKeypad.waitForKey() - 0x30;
  hours = sethours1 * 10;
  Serial.println(hours);

  sethours2 = customKeypad.waitForKey() - 0x30;
  hours = (hours + sethours2);
  Serial.println(hours);
}

void timerstopped()
{
  //stop timer
  //trigger output
  //blink display on/off with a while statement
}