LCD Timer, Buzzer, 30 Sec, With Increments & Interrupts

Ok, Im usually very quiet, but after about 20 hours of googling, red eyes and sore brain muscles, its time to ask for help...

I got a hold of an Arduino Uno and a 16x2 LCD Keypad Shield with buttons and a 12vdc buzzer from RShack.
The only 2 buttons used are "left [(analogread <600)] and RESET, which resets the entire arduino"

Im trying to create a display counter with 2 variables and a warning buzzer.

  1. incrementing counter number, starting from 1 in the top right corner of the display
  2. a countdown timer in the bottom left of the display starting at 30 seconds ending with a buzzer at 0

Proccess:
On power up, wait for 1st button press.
On 1st button press, Increment counter in top right, start 30 sec timer.
If 30 second expire, pull up and activate buzzer on pin 3.
If button press again before expire, increment top right counter by 1 and reset 30 second timer and reset buzzer. Repeat
If button press again before expire, increment top right counter by 1 and reset 30 second timer and reset buzzer. Repeat

Any Help appreciated.
Here is what I have worked out so far...

-Ki0s

#include <LiquidCrystal.h>
// TIME VARIABLES
int hours = 0; // start hours
int minutes = 0; //start min
int seconds = 30; //start seconds
int counter = 1; // initial amount of presses
int counternow = 0; //intitial countercalc
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

  
// ONE TIME RUN
void setup() {
  // put your setup code here, to run once:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.setCursor(0,0);
  lcd.print("30 Sec PRO");
  lcd.setCursor(0,1);
  lcd.print("Push To Start!");

}

// REPEATED RUN
void loop() {
  buttonprog();
  // put your main code here, to run repeatedly:

}


void buttonprog() {
  int x;
  x = analogRead (0);
  lcd.setCursor(0,1);
  if (x < 60) {
    lcd.print ("Right ");
  }
  else if (x < 200) {
    lcd.print ("Up    ");
  }
  else if (x < 400){
    lcd.print ("Down  ");
  }
  else if (x < 600){
    counterprog();
    lcd.print (counternow);
 countdown();
  }
  else if (x < 800){
    lcd.print ("Select");
  }
}

void countdown() {
    delay(150);   
           while (hours > 0 || minutes > 0 || seconds >= 0) {
           lcd.setCursor(0, 1);
           (hours < 10) ? lcd.print("0") : NULL;
           lcd.print(hours);
           lcd.print(":");
           (minutes < 10) ? lcd.print("0") : NULL;
           lcd.print(minutes);
           lcd.print(":");
           (seconds < 10) ? lcd.print("0") : NULL;
           lcd.print(seconds);

           lcd.display();
           stepDown();
           delay(1000);
          }
}

void counterprog() {
    lcd.setCursor(12,0);
    counternow = ( counter++ );
    delay(400);
}


void trigger() {
  
}

void stepDown() {
 if (seconds > 0) {
 seconds -= 1;
 } else {
 if (minutes > 0) {
 seconds = 59;
 minutes -= 1;
 } else {
 if (hours > 0) {
 seconds = 59;
 minutes = 59;
 hours -= 1;
 } else {
 trigger();
 }
 } } }

My first piece of advice would be to simplify your life and your code. You want a 30 second countdown right? So why is there all this stuff in your code about hours and minutes? No reason to add all that confusion, all you need to keep up with is 30 seconds. Arduino has this handy millis() function that returns the number of milliseconds since the board last started. If you know the value of that when your countdown started and a current value for it, then it is pretty easy to calculate how many seconds have passed since the countdown started.

This all boils down to checking a button and when appropriate getting a new starting time from millis.

Just discovered millis thanks to your advice.
Researching ways to implement a 30 sec countdown.

Do you by any chance have a code example of using millis to countdown from 30?

-ki0s

unsigned long prevMillis;
int count = 30;

void setup(){
  Serial.begin(9600);
  Serial.println("Starting Countdown");
  prevMillis = millis();  // get the time at the start
  Serial.println(count);
}

void loop(){
  
  unsigned long curMillis = millis();
  //  if one second has passed
  if(curMillis - prevMillis >= 1000){
    count -= 1;
    Serial.println(count);
    prevMillis += 1000;
    // normally prevMillis = curMillis
    // but by adding 1000 insead we prevent
    // accumulation of any mis-timing.
  }
  if(count == 0){
    Serial.println("Countdown ended");
    while(1); // lock up the board until reset
  }
}

I keep getting this error when I try to verify with your code...

exit status 1
'serial' was not declared in this scope

is it because im using your "serial.print" function instead of the previous "lcd.print" function?

I keep getting this error when I try to verify with your code...

exit status 1
'serial' was not declared in this scope

is it because im using your "serial.print" function instead of the previous "lcd.print" function?

Delta_G's code compiles and runs as intended.

Please post the exact code which produced your error. "Serial" should be capitalized, but there is nothing in Delta_G's code using "serial".

ki0s:
I keep getting this error when I try to verify with your code...

exit status 1
'serial' was not declared in this scope

is it because im using your "serial.print" function instead of the previous "lcd.print" function?

Perhaps you copied it wrong. I capitalized Serial everywhere it appears in my code and I don't get any errors.

hmm, I see, Serial needs to be capitalized, serial does not work.

I'm able to get the lcd to print the initial setup param, but nothing happens on button push..

Thanks Again!

#include <LiquidCrystal.h>
// TIME VARIABLES

unsigned long prevMillis;
int count = 30;
int counter = 1; // initial amount of presses
int counternow = 0; //intitial countercalc
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

  
// ONE TIME RUN
void setup() {
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.setCursor(0,0);
  lcd.print("30 Sec PRO");
  lcd.setCursor(0,1);
  lcd.print("Push To Start!");
  prevMillis = millis();  // get the time at the start
  lcd.print(count);
}

// REPEATED RUN
void loop() {
  buttonprog();
  // MAIN CODE INSTRUCTIONS
}

void buttonprog() {
  int x;
  x = analogRead (0);
  if (x < 60) {
  lcd.print ("Right ");
  }
  else if (x < 200) {
  lcd.print ("Up    ");
  }
  else if (x < 400){
  lcd.print ("Down  ");
  }
  else if (x < 600){
      countdown();
  }
  else if (x < 800){
  lcd.print ("Select");
  }
}

void countdown() {
    delay(150); 
    unsigned long curMillis = millis();
  //  if one second has passed
  if(curMillis - prevMillis >= 1000){
    count -= 1;
    lcd.print(count);
    prevMillis += 1000;
    // normally prevMillis = curMillis
    // but by adding 1000 insead we prevent
    // accumulation of any mis-timing.
  }
  if(count == 0){
    lcd.print("Countdown ended");
    while(1); // lock up the board until reset
  }    
}

I think im breaking the rules by changing all the Serial.print to lcd.print....

So I was able to get the serial monitor to display the initial Serial.print and the countdown, but when loading the code on to the arduino, I see nothing on the display....

Delta_G:

unsigned long prevMillis;

int count = 30;

void setup(){
  Serial.begin(9600);
  Serial.println("Starting Countdown");
  prevMillis = millis();  // get the time at the start
  Serial.println(count);
}

void loop(){
 
  unsigned long curMillis = millis();
  //  if one second has passed
  if(curMillis - prevMillis >= 1000){
    count -= 1;
    Serial.println(count);
    prevMillis += 1000;
    // normally prevMillis = curMillis
    // but by adding 1000 insead we prevent
    // accumulation of any mis-timing.
  }
  if(count == 0){
    Serial.println("Countdown ended");
    while(1); // lock up the board until reset
  }
}

You're only calling countdown once when the button is pressed. You want to set a flag so you can call countdown over and over if it needs to be called and not when it isn't needed.

else if (x < 600){
      countdown();
  }
else if (x < 600){
      someFlagVariable = true;
  }

And in loop:

void loop() {
  buttonprog();
  // MAIN CODE INSTRUCTIONS
  if(someFlagVariable){
     countdown();
  }
}

Be sure to add code to set your flag to false when you don't want the countdown running.

But really, instead of just copying my code into yours, it would work a lot better if you'd learn how that code works so you can write something that makes a little more sense with the code you are writing. What I wrote was merely intended as an example of how to use millis, not as a drop in addition to your code.

void countdown() {
    delay(150); 
    unsigned long curMillis = millis();
  //  if one second has passed
  if(curMillis - prevMillis >= 1000){
    count -= 1;
    lcd.print(count);
    prevMillis += 1000;
    // normally prevMillis = curMillis
    // but by adding 1000 insead we prevent
    // accumulation of any mis-timing.
  }

There is no cursor management in the countdown function. You last printed in setup, and your cursor position is now off the visible display. You need to reposition the cursor tell the display where to print count and you will have the issue of clearing one previously printed digit when the count drops from below 10.

Ok, So using your help, I was able to put this together,
I believe the countdownVariable is a boolean, because it is true / false.
Cattledog, I added a cursor position to the countdown variable.

I still get an error when trying to compile

C:\Users\kiro\Documents\Arduino\libraries\LiquidCrystal\I2CIO.cpp:35:26: fatal error: ../Wire/Wire.h: No such file or directory
#include <../Wire/Wire.h>
^
compilation terminated.
Multiple libraries were found for "LiquidCrystal.h"

Used: C:\Users\kiro\Documents\Arduino\libraries\LiquidCrystal

Not used: C:\Program Files (x86)\Arduino\libraries\LiquidCrystal

Error compiling.

#include <LiquidCrystal.h>
// TIME VARIABLES


unsigned long prevMillis;
int count = 30;
int counter = 1; // initial amount of presses
int counternow = 0; //intitial countercalc
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
boolean countdownVariable = false;

// ONE TIME RUN
void setup() {
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.setCursor(0,0);
  lcd.print("30 Sec PRO");
  lcd.setCursor(0,1);
  lcd.print("Push To Start!");
  prevMillis = millis();  // get the time at the start
  lcd.print(count);
}

// REPEATED RUN
void loop() {
  buttonprog();
  if(countdownVariable){
     countdown();
  }
}

void buttonprog() {
  int x;
  x = analogRead (0);
  if (x < 60) {
  lcd.print ("Right ");
  }
  //right
  else if (x < 200) {
  lcd.print ("Up    ");
  }
  //down
  else if (x < 400){
  lcd.print ("Down  ");
  }
  // left
  else if (x < 600){
      countdownVariable = true;
  }
  //select
  else if (x < 800){
  lcd.print ("Select");
  }
}

void countdown() {
    delay(150);
    unsigned long curMillis = millis();
  //  if one second has passed
  if(curMillis - prevMillis >= 1000){
    count -= 1;
    lcd.setCursor(0,1);
    lcd.print(count);
    prevMillis += 1000;
    // normally prevMillis = curMillis
    // but by adding 1000 insead we prevent
    // accumulation of any mis-timing.
  }
  if(count == 0){
    lcd.print("Countdown ended");
    while(1); // lock up the board until reset
  }    
}

Ok that was a bad local library, I renamed it and all compiled fine.
So far I'm here... Now im trying to figure out why the timer wont reset on each button push.
It lets the program run all the way through.

#include <LiquidCrystal.h>
// TIME VARIABLES


unsigned long prevMillis;
int count = 30;
int counter = 1; // initial amount of presses
int counternow = 0; //intitial countercalc
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
boolean countdownVariable = false;

// ONE TIME RUN
void setup() {
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.setCursor(0,0);
  lcd.print("30 Sec PRO");
  prevMillis = millis();  // get the time at the start
  lcd.setCursor(0,1);
  lcd.print(count);
  lcd.print(" sec ready");

}

// REPEATED RUN
void loop() {
  buttonprog();
  if(countdownVariable){
     countdown();
  }
}

void buttonprog() {
  int x;
  x = analogRead (0);
  if (x < 60) {
  lcd.print ("Right ");
  }
  //right
  else if (x < 200) {
  lcd.print ("Up    ");
  }
  //down
  else if (x < 400){
  lcd.print ("Down  ");
  }
  // left
  else if (x < 600){
      countdownVariable = true;
  }
  //select
  else if (x < 800){
  lcd.print ("Select");
  }
}

void countdown() {
    delay(150);
    unsigned long curMillis = millis();
  //  if one second has passed
  if(curMillis - prevMillis >= 1000){
    count -= 1;
    lcd.setCursor(0,1);
    lcd.print(count);
    prevMillis += 1000;
    // normally prevMillis = curMillis
    // but by adding 1000 insead we prevent
    // accumulation of any mis-timing.
  }
  if(count == 0){
    lcd.print("Countdown ended");
    while(1); // lock up the board until reset
  }    
}
else if (x < 600){
      countdownVariable = true;
  }

Well, there's what you told it to do on a button press. What part of that resets the timer? If you want it to reset there then you'll have to set your counter variable back to 30 and set prevMillis tot he current time.

Again, it is imperative that instead of just trying to plug in the code I give you that you try to understand how it works and what it does. Only then will you see how to make it do all that you want. I'm just giving you the bits to point you in the right direction. If you're not taking the time to understand how it works then you're just getting lost deeper and deeper down the rabbit hole.

I got my top right counter working,
I still can't get the counter to reset on button push,
is setting int count = 30; enough?

Look, I understand that I could spend 40 hours on youtube watching tutorials on every expression, but learning actively down a rabbit hole is learning.

In the span of 4 days I went from being handed an Arduino, knowing zero java. To writing sketches that add and subract integers via button pushes. I think i'm making leaps and bounds. Im not looking for anyone to write my sketch. I just dont know all these Arduino functions by heart. And im still learning what they do, how to use them as I discover them.

I very very greatly appreciate your guidance, and hope one day to be able to help someone else discovering too. Its a learning adventure I wasn't prepared for.
But how do you make it reset back to 30! lol!!! >:(

#include <LiquidCrystal.h>
// TIME VARIABLES


unsigned long prevMillis;
int count = 30;
int countertop = 0; // initial amount of presses
int counternow = 0; //intitial countercalc
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
boolean countdownVariable = false;

// ONE TIME RUN
void setup() {
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.setCursor(0,0);
  lcd.print("30 Sec PRO");
  prevMillis = millis();  // get the time at the start
  lcd.setCursor(0,1);
  lcd.print(count);
  lcd.print(" sec ready");

}

// REPEATED RUN
void loop() {

    unsigned long curMillis = millis();
       buttonprog();
     if(countdownVariable){
     countdown();
  }
}

void buttonprog() {
  int x;
  int count = 30;
  x = analogRead (0);
  if (x < 60) {
  lcd.print ("Right ");
  }
  //right
  else if (x < 200) {
  lcd.print ("Up    ");
  }
  //down
  else if (x < 400){
  lcd.print ("Down  ");
  }
  // left
  else if (x < 600){
      

      countdownVariable = true;
      countertopadd();
  }
  //select
  else if (x < 800){
  lcd.print ("Select");
  }
}

//countdown timer of 30 seconds, using millis and
void countdown() {
    delay(150);
    unsigned long curMillis = millis();
  //  if one second has passed
  if(curMillis - prevMillis >= 1000){
    count -= 1;
    lcd.setCursor(0,1);
    lcd.print(count);
    prevMillis += 1000;
    // normally prevMillis = curMillis
    // but by adding 1000 insead we prevent
    // accumulation of any mis-timing.
  }
  if(count == 0){
    lcd.print("Countdown ended");
    while(1); // lock up the board until reset
  }    
}

//top right incrementing counter on left button push
void countertopadd() {
delay(150);
  lcd.setCursor(12,0);
  lcd.print(countertop += 1);
  

}

Google "C++ scope" (Arduino is C++ not java BTW)

The count variable you have in your buttonprog function is NOT the same one you have at global scope at the top. Now you have two variables with the same name. A dangerous and messy situation. When you put the "int" in front of a variable name, you are creating a new int variable. If you want to use the one you've already got then lose the "int"

I thought you only wanted to reset it when the button was pressed though. buttonprog gets called every time the loop function repeats. So do you want to reset your counter to 30 every time buttonprog gets called? That doesn't sound like much of a countdown to me.

Also, this may work but it is very messy:

lcd.print(countertop += 1);

Either make this two lines, or use:

lcd.print(countertop++);

if you want to display the value and then add one to it (displaying the old value)
OR

lcd.print(++countertop);

if you want to add one and then display (displaying the new value)

So on button push, the countertop increases by 1
and
the 30 second counter resets back to 30

ill change it to lcd.print(countertop++);

and

resetting the 30 sec counter part, thats where im lost...

#include <LiquidCrystal.h>
// TIME VARIABLES


unsigned long prevMillis;
int count = 30;
int countertop = 0; // initial amount of presses
int counternow = 0; //intitial countercalc
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
boolean countdownVariable = false;

// ONE TIME RUN
void setup() {
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.setCursor(0,0);
  lcd.print("30 Sec PRO");
  prevMillis = millis();  // get the time at the start
  lcd.setCursor(0,1);
  lcd.print(count);
  lcd.print(" sec ready");

}

// REPEATED RUN
void loop() {

    unsigned long curMillis = millis();
       buttonprog();
     if(countdownVariable){
     countdown();
  }
}

void buttonprog() {
  int x;
  count = 30;
  x = analogRead (0);
  if (x < 60) {
  lcd.print ("Right ");
  }
  //right
  else if (x < 200) {
  lcd.print ("Up    ");
  }
  //down
  else if (x < 400){
  lcd.print ("Down  ");
  }
  // left
  else if (x < 600){
      

      countdownVariable = true;
      countertopadd();
  }
  //select
  else if (x < 800){
  lcd.print ("Select");
  }
}

//countdown timer of 30 seconds, using millis and
void countdown() {
    delay(150);
    unsigned long curMillis = millis();
  //  if one second has passed
  if(curMillis - prevMillis >= 1000){
    count -= 1;
    lcd.setCursor(0,1);
    lcd.print(count);
    prevMillis += 1000;
    // normally prevMillis = curMillis
    // but by adding 1000 insead we prevent
    // accumulation of any mis-timing.
  }
  if(count == 0){
    lcd.print("Countdown ended");
    while(1); // lock up the board until reset
  }    
}

//top right incrementing counter on left button push
void countertopadd() {
delay(150);
  lcd.setCursor(12,0);
  lcd.print(++countertop);
  

}

ki0s:
Look, I understand that I could spend 40 hours on youtube watching tutorials on every expression, but learning actively down a rabbit hole is learning.

You don't need a tutorial on each expression. Most of them are easy enough to understand. You DO need to know the basics of what each one does. And you DO need to try to learn to follow the logic. Otherwise this is all a waste of my time and I'll just stop. As will most others. Code isn't a magic black box where a certian combination of terms gets a result. It's nothing more than a list of instructions that are run one after another in order. Even if you don't know how to write code, you should be able to write out the instructions in plain English and understand why they need to be in a certain order to accomplish your stated goals.

ki0s:
So on button push, the countertop increases by 1
and
the 30 second counter resets back to 30

No, the way you have it written now (assuming you take the "int" off the new count variable in buttonprog) it gets reset back to 30 every few milliseconds when the loop function repeats. When the button is pressed you are setting a variable to true and adding one to countertop. But you don't use countertop anywhere else in the program so I'm not sure what that is supposed to accomplish.

Where is the section of code that only runs when the button is pressed? Can you identify the section that only runs when the button is pressed?