changing clock/date setting on Real Time Clock pcf8563 [solved]

hello, I have a RTC working on a LCD but to set the time I have to use Serial monitor. You guess the question.

heres the code.

/*
 Program:      clock
 
 Description:  Reads the time from a PCF8563 RTC (Real Time
 Clock). Displays the time and date on a LCD
 and serial port. Allows the time to be set through the serial
 port.
 
 Date:         20 April 2012
 
 Author:       W.A. Smith, http://startingelectronics.com
 */
#include <Wire.h>
#include <LiquidCrystal.h>

#define RTC_ADDR  (0xA2 >> 1)

// Arduino pins used for LCD
LiquidCrystal lcd(7,8,9,10,11,12);

void setup() {
  Wire.begin();        // initialize the I2C/TWI interface
  Serial.begin(9600);  // initialize the serial port
  lcd.begin(16, 2);    // initialize the LCD display

}

void loop() {
  int rx_byte = 0;    // stores data byte received on serial port

  // check for data from the serial port
  if (Serial.available()) {
    rx_byte = Serial.read();
  }
  if ((rx_byte == 's') || (rx_byte == 'S')) {
    // set the time
    SetTime();
  }
  else {
    // print the time
    PrintTime();
  }
  rx_byte = 0;
}

// print the time to the serial port and LCD
void PrintTime(void)
{
  unsigned char time_date_raw[7];      // time/date read from RTC
  int index = 0;              // index into above array
  char time[] = "hh:mm:ss";   // time string
  char date[] = "dd-mm-20yy"; // date string
  static unsigned char raw_time = 0;   // stores old seconds value

  // point to the time registers in the RTC
  Wire.beginTransmission(RTC_ADDR);
  Wire.write(0x02);
  Wire.endTransmission();

  // get the time
  Wire.requestFrom(RTC_ADDR, 7);
  while (Wire.available()) {
    time_date_raw[index] = Wire.read();
    index++;
    if (index >= 7) {
      index = 0;
      break;
    }
  }

  // convert the time / date to a string only every second
  if (raw_time != time_date_raw[0]) {
    raw_time = time_date_raw[0];

    Serial.println("Send \"s\" to set the date and time.");

    // convert time to a string
    // hours
    time[0] = ((time_date_raw[2] >> 4) & 0x03) + '0';
    time[1] = (time_date_raw[2] & 0x0F) + '0';
    // minutes
    time[3] = ((time_date_raw[1] >> 4) & 0x07) + '0';
    time[4] = (time_date_raw[1] & 0x0F) + '0';
    // seconds
    time[6] = ((time_date_raw[0] >> 4) & 0x07) + '0';
    time[7] = (time_date_raw[0] & 0x0F) + '0';
    Serial.println(time);
    lcd.setCursor(0, 0);
    lcd.print(time);

    // convert date to a string
    // day
    date[0] = ((time_date_raw[3] >> 4) & 0x03) + '0';
    date[1] = (time_date_raw[3] & 0x0F) + '0';
    // month
    date[3] = ((time_date_raw[5] >> 4) & 0x01) + '0';
    date[4] = (time_date_raw[5] & 0x0F) + '0';
    //year
    date[8] = (time_date_raw[6] >> 4) + '0';
    date[9] = (time_date_raw[6] & 0x0F) + '0';
    Serial.println(date);
    Serial.print("\r\n");
    lcd.setCursor(0, 1);
    lcd.print(date);
  }
}

// allows the date and time to be set via the serial monitor window
void SetTime(void)
{
  char menu_option = 0;
  unsigned char new_date_time[7] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  };  // 0xFF to mark unchanged field
  char ret_val = 0;

  PrintMenu();

  while (menu_option != 8) {
    if (Serial.available()) {
      menu_option = Serial.read();
      menu_option = menu_option - '0';
      // check if a valid menu option was entered
      if ((menu_option < 0) || (menu_option > 8)) {
        // invalid option selected
        Serial.println("Only 0 to 8 are valid, send 8 to quit.");
      }
      else {  // valid menu option selected
        switch (menu_option) {
        case 1:  // set year
          Serial.println("Enter 2 digit year (00 to 99 valid) or \"q\" to quit.");
          Serial.print("Year: ");
          ret_val = GetNewTime(&new_date_time[6], 0, 99);
          break;

        case 2: // set month
          Serial.println("Enter 2 digit month (01 to 12 valid) or \"q\" to quit.");
          Serial.print("Month: ");
          ret_val = GetNewTime(&new_date_time[5], 1, 12);
          break;

        case 3: // set day
          Serial.println("Enter 2 digit day (01 to 31 valid) or \"q\" to quit.");
          Serial.print("Day: ");
          ret_val = GetNewTime(&new_date_time[3], 1, 31);
          break;

        case 4: // set hour
          Serial.println("Enter 2 digit hour (01 to 23 valid) or \"q\" to quit.");
          Serial.print("Hour: ");
          ret_val = GetNewTime(&new_date_time[2], 1, 23);
          break;

        case 5: // set minute
          Serial.println("Enter 2 digit minute (00 to 59 valid) or \"q\" to quit.");
          Serial.print("Minute: ");
          ret_val = GetNewTime(&new_date_time[1], 0, 59);
          break;

        case 6: // set second
          Serial.println("Enter 2 digit second (00 to 59 valid) or \"q\" to quit.");
          Serial.print("Second: ");
          ret_val = GetNewTime(&new_date_time[0], 0, 59);
          break;

        case 7: // write changes
          WriteDateTime(new_date_time);
          menu_option = 8;
          break;

        default:
          break;
        }
        if (ret_val == 1) {  // user enters invalid value
          Serial.println("Invalid value.");
        }
        else if (ret_val == 2) {  // user presses 'q' key to quit
          Serial.print("\r\n");
          PrintMenu();
        }
        ret_val = 0;
      }
    }
  }
}

// print the Set Date & Time menu on the serial port
void PrintMenu(void)
{
  Serial.println("------------------------");
  Serial.println("| Set Date & Time Menu |");
  Serial.println("------------------------");
  Serial.println("(Select 1 to 8)");
  Serial.println("1. Set year.");
  Serial.println("2. Set month.");
  Serial.println("3. Set day.");
  Serial.println("4. Set hour.");
  Serial.println("5. Set minute.");
  Serial.println("6. Set second.");
  Serial.println("7. Write changes and finish.");
  Serial.println("8. Exit without changing.");
}

// get new time/date from user via serial port
// returns 0 if success, 1 if invalid data entered, 2 if user quits
// *p_data = value entered by user converted to BCD
// lower = lowest valid value user can enter on serial port
// upper = highest valid value that user can enter on serial port
char GetNewTime(unsigned char *p_data, unsigned char lower, unsigned char upper)
{
  char rx_data[3];          // data received from serial port
  int index = 0;            // index into above array
  unsigned char data_val;   // stores converted data for limit checking

  // get 2 characters from the user over the serial port
  while (index < 2) {
    if (Serial.available()) {
      rx_data[index] = Serial.read();
      if (rx_data[index] == 'q') {
        return 2;    // user enters 'q' to quit
      }
      index++;
    }
    if (index > 1) {  // only proceed if 2 character entered
      data_val = ((rx_data[0] - '0') * 10) + (rx_data[1] - '0'); // convert ASCII BCD to binary
      // check for valid range and valid characters
      if ((data_val >= lower) && (data_val <= upper) && (rx_data[0] >= '0') &&
        (rx_data[0] <= '9') && (rx_data[1] >= '0') && (rx_data[1] <= '9')) {
        // send entered data over the serial port for feedback to user
        rx_data[2] = 0;  // terminate string
        Serial.println(rx_data);
        // convert 2 ASCII characters received to BCD
        rx_data[0] = rx_data[0] - '0';
        rx_data[1] = rx_data[1] - '0';
        *p_data = rx_data[1];
        *p_data |= (rx_data[0] << 4);

      }
      else {
        return 1;  // invalid data entered
      }
    }
  }
  return 0;    // success
}

// write all 7 time/date fields to the RTC registers
// new_d_t[] array contains 0xFF in date/time fields that are not to be changed
// and contains valid BCD data in fields that are to be changed 
void WriteDateTime(unsigned char new_d_t[])
{
  char time_date_raw[7];      // time/date read from RTC
  int index = 0;              // index into above array

  // point to the time registers in the RTC
  Wire.beginTransmission(RTC_ADDR);
  Wire.write(0x02);
  Wire.endTransmission();

  // get the time
  Wire.requestFrom(RTC_ADDR, 7);
  while (Wire.available()) {
    time_date_raw[index] = Wire.read();
    index++;
    if (index >= 7) {
      index = 0;
      break;
    }
  }

  // write the new time/date values to the array that contains values read from the RTC
  for (int i = 0; i < 7; i++) {
    if (new_d_t[i] != 0xFF) {         // skip unchanged fields
      time_date_raw[i] = new_d_t[i];  // copy value if changed
    }
  }

  // point to the time registers in the RTC and send the time and date
  // write new values and old unchanged values
  Wire.beginTransmission(RTC_ADDR);
  Wire.write(0x02);
  Wire.write((uint8_t*)time_date_raw, 7);
  Wire.endTransmission();
}

So can I Edit this code to use push buttons. How easy would it be. What about incrementing the units 0 to 59 on pushbuttons. Can anyone point me in the right direction. Even have code for the RTC pcf8563 chip. I have seen sketches for the DS1307 RTC, would it be eaiser to use one of those sketches?

You guess the question.

No. You ask the question.

So can I Edit this code to use push buttons.

To do what?

How easy would it be.

Somewhere between trivial and damn-near-impossible. Mostly depends on your coding skills and understanding of hardware and how the loop() function gets called over and over again.

What about incrementing the units 0 to 59 on pushbuttons.

Pushbuttons don't have units.

Can anyone point me in the right direction.

Without knowing which way that would be? Sure. Turn left. Again.

Even have code for the RTC pcf8563 chip.

No. But, the very last thing you need to do is tell the chip the new time. THAT part is trivial, and is exactly the same as when getting the new time from any other source.

I have seen sketches for the DS1307 RTC, would it be eaiser to use one of those sketches?

Sure. First, you get a DS1307...

I have an lcd and a Pcf8563 Real Time Clock. I have the following sketch which is used to set the time and date.

/* Demonstration of Rtc_Pcf8563 Clock on LCD. 
 *
 * I used a Arduino mega 2560,
 * SCL -21, SDA - 20,
 *
 * This sketch connects a lcd to display the clock output.
 *  written by                
 * Joe Robertson, jmr
 * orbitalair@bellsouth.net
 */

#include <Wire.h>
#include <Rtc_Pcf8563.h>
/* add the lcd support */ 
#include <LiquidCrystal.h>

//init the real time clock
Rtc_Pcf8563 rtc;

/* initialize the library objects */

LiquidCrystal lcd(7,8,9,10,11,12);

void setup()
{
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
  
  //clear out all the registers
  rtc.initClock();
  //set a time to start with.
  //day, weekday, month, century, year
  rtc.setDate(14, 6, 3, 0, 10);
  //hr, min, sec
  rtc.setTime(1, 15, 40);
}

void loop()
{
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 0);
  //lcd.print(rtc.formatTime(RTCC_TIME_HM));
  lcd.print(rtc.formatTime());
  lcd.setCursor(0, 1);
  //lcd.print(rtc.formatDate(RTCC_DATE_ASIA));
  lcd.print(rtc.formatDate());
  
  delay(1000);
    
}

The sketch allows for the time and date to be set before the sketch is compliled. But I want to SET both using push buttons to change time and date variables.
The Libray for this "Rtc_Pcf8563.h", has the code for changing the time and date. So would the answer lie in the library. Do I cut and past the code from the libray into the sketch above .

No, you use the setTime method. Lets say you want to set it to 11:30, then you'd do something like this...

//setTime(byte sec, byte minute, byte hour)
rtc.setTime(00, 30, 11)

Of course you'd need to work it into your code so your buttons increase/decrease each variable but that is another subject and if you aren't sure how to work with buttons then you might want to start back at square one.

There is more info on the library here: Arduino Playground - Rtc_Pcf8563 RTC Library

No, you use the setTime method. Lets say you want to set it to 11:30, then you'd do something like this...

//setTime(byte sec, byte minute, byte hour)
rtc.setTime(00, 30, 11)

I dont think it's as simple as that. It looks like I need to set some sort of pointer to each of the variables "hour", "min","sec".
I found this in the library

setTime(byte sec, byte minute, byte hour)

which I believe is

rtc.setTime(00, 30, 11)

So I think I need to interact with/point to, the "byte sec, byte minute, byte hour". If i can manipulate those at the byte level I think it might work?

If i can manipulate those at the byte level I think it might work?

What makes you think that? Have you tried anything? Where is your code, and a detailed explanation of what is/is not working, with serial output to prove it?

mark7w:
I dont think it's as simple as that. It looks like I need to set some sort of pointer to each of the variables "hour", "min","sec".

I don't see any pointers involved or needed here.

byte hr,min,sec;
void loop() {
   ... // warning untested code :)
   if (incrementMinuteButtonPressDectected) min = ++min % 60 // increment and rollover past 59
   if (decrementMinuteButtonPressDectected) min = (--min + 60) % 60 // decrement and rollunder past 0
   ...
   rtc.setTime( sec, min, hr);
   ...
}
rtc.setTime(00, 30, 11)

In the snippet above, if I change the variables 00,30,11 to the current time. Then compile the sketch, the time on the lcd is updated. I am trying to get away from compiling the sketch each time JUST to update the "Time".

but I dont know how to enter the variables from my sketch AFTER it has been compliled.

the original sketch is here

/* Demonstration of Rtc_Pcf8563 Clock on LCD. 
 *
 *  
 * This sketch connects a lcd to display the clock output.
 * 
 * setup:  see Pcf8563 data sheet.
 *         1x 10Kohm pullup on Pin3 INT
 *         No pullups on Pin5 or Pin6 (I2C internals used)
 *         1x 0.1pf on power
 *         1x 32khz chrystal
 *         1x h44780 LCD
 *
 * Joe Robertson, jmr
 * orbitalair@bellsouth.net
 */

#include <Wire.h>
#include <Rtc_Pcf8563.h>
/* add the lcd support */
#include <LiquidCrystal.h>

//init the real time clock
Rtc_Pcf8563 rtc;

/* initialize the library objects */
LiquidCrystal lcd(7,8,9,10,11,12);

void setup()
{
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);

  //clear out all the registers
  rtc.initClock();
  //set a time to start with.
  //day, weekday, month, century, year
  rtc.setDate(14, 6, 3, 0, 10);
  //hr, min, sec
  rtc.setTime(1, 15, 40);
}

void loop()
{
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 0);
  lcd.print(rtc.formatTime());
  lcd.setCursor(0, 1);
  lcd.print(rtc.formatDate());

  delay(1000);

}

I am going to use push buttons to change the "hour" "minute" and "sec". but "rtc.setTime(1, 15, 40);" how will the sketch know what 1,15,40 is. In the library these appear to be

setTime(byte sec, byte minute, byte hour)

so my thinking is byte sec, byte minute, byte hour tell ME what "(1, 15, 40)" are and therfore I would create a function to change byte sec, byte minute, byte hour byte

mark7w:

rtc.setTime(00, 30, 11)

I am going to use push buttons to change the "hour" "minute" and "sec". but "rtc.setTime(1, 15, 40);" how will the sketch know what 1,15,40 is. In the library these appear to be

so my thinking is byte sec, byte minute, byte hour tell ME what "(1, 15, 40)" are and therfore I would create a function to change byte sec, byte minute, byte hour byte

I think you have it backwards. setTime() is for "you to tell it" what the time is. For "it to tell you" what the time is, looks like you need to use rtc.getTime(); hr=rtc.getHour(); min=rtc.getMinute(); sec=rtc.getSecond(); [corrected]

Cheers,
John

Write some simple code, in setup():

byte hour = 5;
byte min = 10;
byte sec = 14;

hour += 3;
min +=27;
sec +=10;

rtc.setTime(hour, min, sec);

What time does the RTC get set to? Read the time in loop(), and print the values.

Incrementing hour, min, and sec by other values is what you need to make the switches do.

PaulS:
Write some simple code, in setup():

byte hour = 5;

byte min = 10;
byte sec = 14;

hour += 3;
min +=27;
sec +=10;

rtc.setTime(hour, min, sec);




What time does the RTC get set to? Read the time in loop(), and print the values.

Tme is set to 08:37:24. in which case I kinda understand.

mark7w:

PaulS:
Write some simple code, in setup():

byte hour = 5;

byte min = 10;
byte sec = 14;

hour += 3;
min +=27;
sec +=10;

rtc.setTime(hour, min, sec);




What time does the RTC get set to? Read the time in loop(), and print the values.

Tme is set to 08:37:24. in which case I kinda understand.

Ok, so then think of what would be the next step. There are a lot of ways of going about this but to make things easy you could have two buttons, one for hours and one for minutes. Each press of the button would increment the hour or min variable.

Something like this:

if(digitalRead(minButton) == HIGH)
   {
      min += 1;
   }

You would also have to make sure hours and minutes don't go too high.

if(digitalRead(minButton) == HIGH)
   {
      min += 1;
   }

I managed to reset the time back to the defaults but I cant get the minute to increase based in the snippet you gave.

I changed the code around since then and it does not do anything now.

#include <Wire.h>
#include <Rtc_Pcf8563.h>
#include <LiquidCrystal.h>

//init the real time clock
Rtc_Pcf8563 rtc;


LiquidCrystal lcd(7,8,9,10,11,12);

int minButton = 42;

void setup()
{
  lcd.begin(16, 2);
  pinMode (minButton,INPUT);
  //clear out all the registers
  rtc.initClock();
  //set a time to start with.
  //day, weekday, month, century, year
  byte day = 1;
  byte weekday= 1;
  byte month = 1;
  byte century =0;
  byte year = 13;

  day =20;
  weekday =7;
  month =1;
  century =20;
  year =13;
  rtc.setDate(day,weekday, month, century, year);
}

void loop()
{
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 0);
  lcd.print(rtc.formatTime());
  lcd.setCursor(0, 1);
  lcd.print(rtc.formatDate());
  delay(1000);
}

void changeTime(){

  byte hour = 5;
  byte min = digitalRead(minButton);
  byte sec = 14;

  hour = 18;
  min = 20;
  sec = 00;
  rtc.setTime(hour, min,sec);

  if(digitalRead(minButton) == HIGH)
  {
    min += 1;  
  }
}

I changed the code around since then and it does not do anything now.

Perhaps because you never call changeTime().

Even with the call, all that happens is the Time defaults to 18:20:00. I can't get the minutes to increase when the minButton goes HIGH.

#include <Wire.h>
#include <Rtc_Pcf8563.h>
#include <LiquidCrystal.h>

//init the real time clock
Rtc_Pcf8563 rtc;


LiquidCrystal lcd(7,8,9,10,11,12);

int minButton = 42;

void setup()
{
  lcd.begin(16, 2);
  pinMode (minButton,INPUT);
  //clear out all the registers
  rtc.initClock();
  //set a time to start with.
  //day, weekday, month, century, year
  byte day = 1;
  byte weekday= 1;
  byte month = 1;
  byte century =0;
  byte year = 13;

  day =20;
  weekday =7;
  month =1;
  century =20;
  year =13;
  rtc.setDate(day,weekday, month, century, year);
}

void loop()
{
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 0);
  lcd.print(rtc.formatTime());
  lcd.setCursor(0, 1);
  lcd.print(rtc.formatDate());
  delay(1000);

  if(digitalRead(minButton) == HIGH)
    changeTime();
}

void changeTime(){

  byte hour = 5;
  byte min = 30;
  byte sec = 14;

  hour = 18;
  min = 20;
  sec = 00;
  rtc.setTime(hour, min,sec);

  if(digitalRead(minButton) == HIGH)
  {
    min += 1;  
  }
}

Think about what value min has in the changeTime function just before rtc.setTime(hour, min,sec)
Looks to me that it will always be 20.
Is that what you want or do you want it to be the value you set it to by pressing the button ?

On each pass through loop(), you are testing whether the switch connected to minButton is pressed. If it is, you set the time to 5:30:14, then increment the min variable and return.

You need to move the local variables hour, min, and sec from the function to global space.

Then, you need to call setTime AFTER incrementing min.

Thanks to all. I have it working now.

This last bit solved the issue.

PaulS:
On each pass through loop(), you are testing whether the switch connected to minButton is pressed. If it is, you set the time to 5:30:14, then increment the min variable and return.

You need to move the local variables hour, min, and sec from the function to global space.

Then, you need to call setTime AFTER incrementing min.

Karma awarded.