Clock and timer with buttons

Hi! I'm programing an Arduino Leonardo in order to have a clock and a timer showed in a LCD (SainSmart 1.3" SPI Serial 128X64 Blue) I use the RTC DS1307. I want to change the our of the clock and the timer with 2 buttons. The problems I've got are:

  • Minutes don't stop at 60 and change into 00.
  • Hours don't stop at 24 and change into 00.
  • When I press the buttons they don't change the numbers one by one, they change them 6 by 6 or 7 by 7.
  • Also the time doesn't start with the time I set.

Any solutions? Thanks

Here is the code.

#include "U8glib.h"
U8GLIB_SH1106_128X64 u8g(7, 6, 4, 5, 9);
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 rtc;

int but_h = 12;
int but_m = 11;
int val;
int val1;

String texto;

void setup() {
Serial.begin(57600);
#ifdef AVR
Wire.begin();
#else
Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino Due
#endif
rtc.begin();

if (! rtc.isrunning()) {
Serial.println("RTC is NOT running!");
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(12,01,20));
}

pinMode (12, INPUT);
pinMode (11, INPUT);
}

void draw(void)
{
if (val != 1 && val1 != 1) { //mientras los botones no esten pulsados haz
DateTime now = rtc.now();
u8g.setFont(u8g_font_fur20);
u8g.setPrintPos(8, 50);
// u8g.print(texto);
if(now.hour() < 10) {u8g.print('0');} // si horas menor que 10, escribe 0 delante
u8g.print(now.hour(), DEC); //escribe horas
u8g.print(':');
if(now.minute() < 10) {u8g.print('0');} //si minutos menos que 10, escribe 0 delante
u8g.print(now.minute(), DEC);
u8g.print(':');
u8g.print(now.second(), DEC);
}
else buttons(); //si los botones estan pulsados, realiza la funcion botones
}

void loop() {
val = digitalRead(but_m);
val1 = digitalRead(but_h);
u8g.firstPage();
do {
draw();
} while( u8g.nextPage() );
delay(100);
}

void buttons() {
DateTime now = rtc.now();

if (val1 == 1) rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour () +1, now.minute() , 0));
if (val == 1) rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour() , now.minute () +1, 0));
}

now.hour () +1

It looks like the library will accept silly values so you will have to take responsibility for checking that they do not go out of bounds before writing to the RTC.

  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(12, 01, 20));
  }

Comments can be very helpful but only when they match what the code does and why are you setting the time only when the clock is not running ?

Thank you UKHeliBob, if I set the time when the clock is running it doesn't work neither.

Let's see your code that sets the date and time when the clock is running.

I don't really care too much about setting the time at the code because I'd like to set it using buttons.
My main problem is that the hours and minutes don't stop when they have to and I get the time like "34:72 h" for example. Also the buttons don't change the time 1 by 1.

Anyway here is the code. I've tried with "setTime(10, 29, 0, 14, 1, 2015);" and when I verify the code it gives to me " Error compiling".

#include <Time.h>
#include "U8glib.h"
U8GLIB_SH1106_128X64 u8g(7, 6, 4, 5, 9);
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 RTC;

int but_h = 12;
int but_m = 11;
int val;
int val1;

String texto;

void setup() {
Serial.begin(57600);
#ifdef AVR
Wire.begin();
#else
Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino Due
#endif
RTC.begin();
setTime(10, 29, 0, 14, 1, 2015);

pinMode (12, INPUT);
pinMode (11, INPUT);
}

void draw(void)
{
if (val != 1 && val1 != 1) { //mientras los botones no esten pulsados haz
DateTime now = RTC.now();
u8g.setFont(u8g_font_fur20);
u8g.setPrintPos(8, 50);
// u8g.print(texto);
if(now.hour() < 10) {u8g.print('0');} // si horas menor que 10, escribe 0 delante
u8g.print(now.hour(), DEC); //escribe horas
u8g.print(':');
if(now.minute() < 10) {u8g.print('0');} //si minutos menos que 10, escribe 0 delante
u8g.print(now.minute(), DEC);
u8g.print(':');
u8g.print(now.second(), DEC);
}
else buttons(); //si los botones estan pulsados, realiza la funcion botones
}

void loop() {
val = digitalRead(but_m);
val1 = digitalRead(but_h);
u8g.firstPage();
do {
draw();
} while( u8g.nextPage() );
delay(100);
}

void buttons() {
DateTime now = RTC.now();
if (val1 == 1) RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour () +1, now.minute() , 0));
if (val == 1) RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour() , now.minute () +1, 0));
}

My main problem is that the hours and minutes don't stop when they have to and I get the time like "34:72 h" for example.

As I said in reply #1

It looks like the library will accept silly values so you will have to take responsibility for checking that they do not go out of bounds before writing to the RTC.

As to the values not incrementing by 1 when the button is pressed it is because of this

 val = digitalRead(but_m);
  val1 = digitalRead(but_h);

Later on in the buttons() function you add 1 to the hour or the minute (without checking if the value has become silly) if val or val1 are set to 1 but loop() is repeating so fast, despite your attempt to slow it down by using delay(100), that next time round you read the buttons again and often they will still be pressed so val and/or val1 get set to 1 again and the time gets incremented again.

What you need to do is to detect when the buttons BECOME pressed, not when they ARE pressed. Look at the StateChangeDetection example in the IDE to see how to do this.

Anyway here is the code. I've tried with "setTime(10, 29, 0, 14, 1, 2015);" and when I verify the code it gives to me " Error compiling".

setTime is not valid syntax for RTClib.h.

The RTC.adjust is correct.

RTC.adjust(DateTime(2015,1,14,10,29,0));//year,month,day,hour,minute,second

Thank you cattledog and UKHeliBob for your replyes.

-UKHeliBob, I don't know how to check if the numbers go out of bounds or not before writing to the RTC. Also I've had a look at the StateChangeDetection example and I've tried to make something but finally I didn't succeed, the program returned me "Error compiling".

-cattledog even if I use "RTC.adjust(DateTime(2015,1,14,10,29,0));" it still doesn't work.

Any idea about how can I make it works?
It's getting hard to succeed. :slightly_frowning_face:

Here is the code with the modifications:

#include <Time.h>
#include "U8glib.h"
U8GLIB_SH1106_128X64 u8g(7, 6, 4, 5, 9);
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 RTC;

const int but_h = 12;
const int but_m = 11;

int buttonPushCounter_h = 0; // counter for the number of button presses
int buttonState_h = 0; // current state of the button
int lastButtonState_h = 0; // previous state of the button

int buttonPushCounter_m = 0; // counter for the number of button presses
int buttonState_m = 0; // current state of the button
int lastButtonState_m = 0; // previous state of the button

String texto;

void setup() {
Serial.begin(9600);
#ifdef AVR
Wire.begin();
#else
Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino Due
#endif
RTC.begin();
setTime(10, 29, 0, 14, 1, 2015);

// initialize button pins as a input:
pinMode(but_h, INPUT);
pinMode(but_m, INPUT);

}

void draw(void)
{
if (buttonState_h != 1 && buttonState_m != 1) { //mientras los botones no esten pulsados haz
DateTime now = RTC.now();
u8g.setFont(u8g_font_fur20);
u8g.setPrintPos(8, 50);
// u8g.print(texto);
if(now.hour() < 10) {u8g.print('0');} // si horas menor que 10, escribe 0 delante
u8g.print(now.hour(), DEC); //escribe horas
u8g.print(':');
if(now.minute() < 10) {u8g.print('0');} //si minutos menos que 10, escribe 0 delante
u8g.print(now.minute(), DEC);
u8g.print(':');
u8g.print(now.second(), DEC);
}
else buttons(); //si los botones estan pulsados, realiza la funcion botones
}

void loop() {
// read the pushbutton input pin:
buttonState_h = digitalRead(but_h);
buttonState_m = digitalRead(but_m);

if (buttonState_h != lastButtonState_h) {
// if the state has changed, increment the counter
if (buttonState_h == HIGH) {
// if the current state is HIGH then the button
// wend from off to on:
buttonPushCounter_h++;
}
}
lastButtonState_h = buttonState_h;

if (buttonState_m != lastButtonState_m) {
// if the state has changed, increment the counter
if (buttonState_m == HIGH) {
// if the current state is HIGH then the button
// wend from off to on:
buttonPushCounter_m++;
}
}
u8g.firstPage();
do {
draw();
} while( u8g.nextPage() );
delay(100);
}

void buttons() {
DateTime now = RTC.now();
if (buttonState_h == 1) RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour () +1, now.minute() , 0));
if (buttonState_m == 1) RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour() , now.minute () +1, 0));
}

I don't know how to check if the numbers go out of bounds or not before writing to the RTC

Don't add 1 to now.hour() or now.minute() in the parameter list for the RTC.adjust(). Copy it to a variable before the adjustment and add 1 to that. If the result is equal to 60 set the variable to zero and either way use the variable in RTC.adjust()

It is difficult to advise on the state change problem because I don't have all the libraries required. What errors did you get ? Was it just "Error compiling" ?

Finally I've decided to not use an RTC. Now I can set the time and it runs well.
The only problem I've got is to change the time with buttons. I use two buttons but when I press them numbers don't change.
Any solutions? Thank you

Here is the code:

#include "Time.h"
#include "TimeAlarms.h"
#include "U8glib.h"
U8GLIB_SH1106_128X64 u8g(7, 6, 4, 5, 9);

int h1=0;
int m1=0;
int but_h=11;
int but_m=12;
int led=10;

void setup(){
setTime(14,32,00,12,31,14); // set time to Saturday 8:29:00am Jan 1 2011
pinMode(but_h, INPUT);
pinMode(but_m, INPUT);
pinMode(led, OUTPUT);

Serial.begin(9600);
}

void draw(){
if (but_h != 1 && but_m != 1) {
u8g.setFont(u8g_font_fur20);
u8g.setPrintPos(8, 50);
if(hour() < 10) {u8g.print('0');}
u8g.print(hour());
u8g.print(":");
if(minute() < 10) {u8g.print('0');}
u8g.print(minute());
u8g.print(":");
if(second() < 10) {u8g.print('0');}
u8g.print(second());
}
else buttons();
}

void loop(){

u8g.firstPage();
do {
draw();
} while( u8g.nextPage() );
delay(100);
}

void buttons(){
if (but_h==1){
h1=hour();
u8g.print(h1++);}
if (but_m==1){
m1=minute();
u8g.print(m1++);}
}

if (but_h == 1) 
  {
    h1 = hour();
    u8g.print(h1++);
  }

So, if the hours button is pressed you copy the hour to the h1 variable, display it and increment it but do not actually update the hour so next time the button is pressed the same thing happens but you never actually update the hour.

I've modificated the code but the buttons are still not working. Any ideas of what's wrong?
Thank you

#include "Time.h"
#include "TimeAlarms.h"
#include "U8glib.h"
U8GLIB_SH1106_128X64 u8g(7, 6, 4, 5, 9);

int h1=0;
int m1=0;
int but_h=11;
int but_m=12;
int led=10;
int val1;
int val;

void setup(){
setTime(11,30,00,01,19,15); // set time to Saturday 8:29:00am Jan 1 2011
pinMode(but_h, INPUT);
pinMode(but_m, INPUT);
pinMode(led, OUTPUT);
Serial.begin(9600);
}

void draw(){
if (but_h != 1 && but_m != 1) {
u8g.setFont(u8g_font_fur20);
u8g.setPrintPos(8, 50);
if(hour() < 10) {u8g.print('0');}
u8g.print(hour());
u8g.print(":");
if(minute() < 10) {u8g.print('0');}
u8g.print(minute());
u8g.print(":");
if(second() < 10) {u8g.print('0');}
u8g.print(second());
}
else buttons();
}

void loop(){
val1 = digitalRead(but_h);
val = digitalRead(but_m);
u8g.firstPage();
do {
draw();
} while( u8g.nextPage() );
delay(100);
}

void buttons(){
m1=minute();
h1=hour();
if (val == 1 && m1 < 59) m1 = m1 + 1;
else if (val == 1 && m1 >= 59) m1 = 0;

if (val1 == 1 && h1 < 23) h1 = h1 + 1;
else if (val1 == 1 && h1 >= 23)h1 = 0;

}

In the buttons() function you update the value of m1 and h1 depending on which button was pressed, if any, but you never do anything with the updated values, so next time buttons() is called they go back to the values read from the RTC.

You need to update the RTC with the changed values before displaying the time again. This may give you an idea of how to do it even though the code does not match the comment.

setTime(11,30,00,01,19,15); // set time to Saturday 8:29:00am Jan 1 2011

I changed the code to have a multifunctional alarm clock with 3 buttons and a screen. The time is working well.
Button 1 is for changing the mode (set alarm (press once)/ set time (press twice)), button 2 for changing the hours and button 3 for changing the minutes. Button 1 works well, but the problem is that when I press once button 1, it prints at the screen " Set alarm", as I want, and also the current hour, one over the other one. I want to print the text and after 3 seconds clear the screen and print the current time when it's the alarm mode and 00:00 when it's the time set mode but I don't know how to do it. Also when I press button 2 and 3, it increases the numbers just 1 numbers while I press the button but when I release the button the numbers come back to the same hour it was. Could you help me please?

Thanks.
Here is the code:

#include "Time.h"
#include "TimeAlarms.h"
#include "U8glib.h"
U8GLIB_SH1106_128X64 u8g(7, 6, 4, 5, 9);

int ah=0; // hours of the alarm
int am=0; // minutes of the alarm
int th=0; // hours of the time set
int tm=0; // minutes of the time set
int led=10;
int but1=11; // change mode(Alarm/Timeset)
int but2=12; // change hours
int but3=13; // change minuts
int val1, val2, val3;
int cont=0;
int cont2=0;
String text;

void setup(){
setTime(16,48,00,01,19,15); // set time to Saturday 8:29:00am Jan 1 2011
pinMode(but1,INPUT);
pinMode(but2,INPUT);
pinMode(but3,INPUT);
pinMode(led,OUTPUT);
Serial.begin(9600);
}

void draw(String texto){
u8g.setFont(u8g_font_fur20);
u8g.setPrintPos(8, 50);
u8g.print("");
u8g.print(texto);
text = "";
}

void loop(){
u8g.firstPage();
do {
val1=digitalRead(but1);
val2=digitalRead(but2);
val3=digitalRead(but3);
if (val1==HIGH) {
cont++;
delay(100);
}
switch(cont) {
case 1:
setalarm();
cont2=1;
break;
case 2:
settime();
break;
default:
cont = 0;
time(int(hour()), int(minute()), int(second()));
}
draw(text);
} while(u8g.nextPage());
delay(100);
}

void time(int a, int b, int c){
if(a<10) text="0";
text = text + a;
text = text + ":";
if(b<10) text = text + '0';
text = text + b;
text = text + ":";
if(c<10) text = text + '0';
text = text + c;
}

void setalarm(){
draw("set alarm");
ah=hour();
am=minute();
time(ah,am,0);
draw(text);
if(cont2==1){
if (val2==HIGH){ // change alarm hours
if (ah<=23){
ah=ah++;
Alarm.alarmOnce(ah, minute(), 0, alarm);
time(ah, minute(), 0);
draw(text);
}
else if (ah>23){
ah=0;
Alarm.alarmOnce(ah, minute(), 0, alarm);
time(ah, minute(), 0);
draw(text);
}
}
if (val3==HIGH){ // change alarm minutes
if (am<=59){
am=am++;
Alarm.alarmOnce(hour(), am, 0, alarm);
time(hour(), am, 0);
draw(text);
}
if (am>59){
am=0;
Alarm.alarmOnce(hour(), am, 0, alarm);
time(hour(), am, 0);
draw(text);
}
}
}
}

void settime(){
text = "set time";
draw(text);
th=hour();
tm=minute();
time(0,0,0);
draw(text);
if (cont==2 && val2==HIGH){ // change hours of the time set
if (val2==HIGH && th<24){
th=th++;
setTime(th, minute(), second(), month(), day(), year());
}
if (val2==HIGH && th>0){
th=0;
setTime(th, minute(), second(), month(), day(), year());
}
}
if (cont==2 && val3==HIGH){ // change minutes of time set
if(val3==HIGH && tm<59){
tm=tm++;
setTime(hour(), tm, second(), month(), day(), year());
}
if (val3==HIGH && tm>59){
tm=0;
setTime(hour(), tm, second(), month(), day(), year());
}
}
}

void alarm(){
digitalWrite(led, HIGH);
delay(5000);
}

The first thing I would say is please put your code in code tags to make it easier to read and copy.

 { // change hours of the time set
    if (val2==HIGH && th<24)
    {
      th=th++;
      setTime(th, minute(), second(), month(), day(), year());
    }
    if (val2==HIGH && th>0){
      th=0;
      setTime(th, minute(), second(), month(), day(), year());
    }
  }

As I read this, if button 2 has been pressed and the hour is less than 24 increment the hour variable (more on this later) and set the time using the new value of th. Then, if button 2 has been pressed and the hour variable is greater than 0 set the hour variable to zero set the time with the hour equal to zero. Am I reading that right ?

Now, about incrementing variables. You have hit upon a strange amalgamation of

th = th + 1;

and

th++;

I think that it would be a good idea to use one or the other but not both at the same time.

Take a look at this. (Not tested)

#include "Time.h"
#include "TimeAlarms.h"
#include <U8glib.h>
U8GLIB_SH1106_128X64 u8g(7, 6, 4, 5, 9);

int ah = 0; // hours of the alarm
int am = 0; // minutes of the alarm
int th = 0; // hours of the time set
int tm = 0; // minutes of the time set

const byte led = 10;
const byte but1 = 11; // change mode(Alarm/Timeset)
const byte but2 = 12; // change hours
const byte but3 = 13; // change minuts
byte val1,val2, val3;

int cont = 0;
int cont2 = 0;
char text[15];

void setup() {
  //setTime(16, 48, 00, 01, 19, 15); // set time to Saturday 8:29:00am Jan 1 2011
  pinMode(but1, INPUT);
  pinMode(but2, INPUT);
  pinMode(but3, INPUT);
  pinMode(led, OUTPUT);
  Serial.begin(9600);
}

void draw(char * textOut)
{
  u8g.setFont(u8g_font_fur20);
  u8g.setPrintPos(8, 50);
  u8g.print(textOut);
}

void loop()
{
  u8g.firstPage();
  do
  {
     val1 = digitalRead(but1);
     val2 = digitalRead(but2);
     val3 = digitalRead(but3);

    if (val1 == HIGH)
    {
      cont++;
      delay(100);
    }
    switch (cont)
    {
      case 1:
        setalarm();
        break;
      case 2:
        settime();
        break;
      default:
        cont = 0;
        time(int(hour()), int(minute()), int(second()));
    }
    draw();
  } while (u8g.nextPage());
  delay(100);
}

void time(int a, int b, int c)
{
  sprintf(text, "%02d:%02d:%02d", a, b, c);
  draw(text);
}

void setalarm()
{
  draw("set alarm");
  ah = hour();
  am = minute();
  time(ah, am, 0);
  
  if (cont2 == 1) 
  {
    if (val2 == HIGH) 
    { // change alarm hours
      if (ah <= 23) 
      {
        ah++;
        Alarm.alarmOnce(ah, minute(), 0, alarm);
        time(ah, minute(), 0);
      }
      else if (ah > 23) 
      {
        ah = 0;
        Alarm.alarmOnce(ah, minute(), 0, alarm);
        time(ah, minute(), 0);
      }
    }
    if (val3 == HIGH) 
    { // change alarm minutes
      if (am <= 59) 
      {
        am++;
        Alarm.alarmOnce(hour(), am, 0, alarm);
        time(hour(), am, 0);
      }
      if (am > 59) 
      {
        am = 0;
        Alarm.alarmOnce(hour(), am, 0, alarm);
        time(hour(), am, 0);
      }
    }
  }
}

void settime() 
{
  draw("set time");
  th = hour();
  tm = minute();
  time(0, 0, 0);
  
  if (cont == 2 && val2 == HIGH) 
  { // change hours of the time set
    if (val2 == HIGH && th < 24) 
    {
      th++;
      setTime(th, minute(), second(), month(), day(), year());
    }
    if (val2 == HIGH && th > 0) 
    {
      th = 0;
      setTime(th, minute(), second(), month(), day(), year());
    }
  }
  if (cont == 2 && val3 == HIGH) 
  { // change minutes of time set
    if (val3 == HIGH && tm < 59) 
    {
      tm++;
      setTime(hour(), tm, second(), month(), day(), year());
    }
    if (val3 == HIGH && tm > 59) 
    {
      tm = 0;
      setTime(hour(), tm, second(), month(), day(), year());
    }
  }
}

void alarm() {
  digitalWrite(led, HIGH);
  delay(5000);
}