Arduino Multiple Clock Project - USB clock synch?

I am following the following tutorial… Almost.

I have 2 Arduino Unos with an “Open Smart” brand “Clock Shield V1.1” (DS1307) on top.

I also have an Arduino Mega2560 connected to a PCF8523 RTC module.

So, I have 4 Arduino based clocks that function on their own using their respective RTC which has been set with the following code snippet:

rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

After loading the sketch to set the time initially, I comment it out. The clocks function fine on their own with their respective programs. The problem is that the clocks are about 30 seconds slow compared to my computer when first set and rapidly get further out of sync from there. Two of the clocks are 2 minutes away from each other currently.

How do I manually synchronize these clocks to a computer via USB like the tutorial that I found suggests?

I guess that can only happen when you fail to synchronise the RTCs when you first set them. A common method is here bildr Do You Have The Time? DS1307 RT Clock + Arduino - bildr

What you really want is far from clear. If Arduino is connected to PC, you may not need an RTC. Same applies but more so If several Arduinos are run together with PC,

Thank you for the reply.

I am looking for a quick and easy way to adjust the RTC back in synch with the computer time within the loop section of code. The goal is to plug the clock into the computer, update the time, and unplug it to run on its own for most of the time.

The current method sets the time to when the file was compiled which introduces an unknown time delay up to 30 seconds and requires loading a sketch to set the time and then loading another sketch for the clock to run.

This code from instructables works for the DS3231. What do I need to do to modify it for the DS1307 and the PCF8523?

#include "Wire.h"
#include <stdio.h>
#include <string.h>

#define DS3231_I2C_ADDRESS 0x68

byte year, month, day, hour, minute;
byte second;
int state = LOW;
int const sentenceSize = 20;
int sentenceIndex = 1;
char sentence[sentenceSize];

byte decToBcd(byte val)
{
  return ( (val / 10 * 16) + (val % 10) );
}
// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val / 16 * 10) + (val % 16) );
}

void setup() {
  Serial.begin(115200);
  Wire.begin();

  writeOnAddress(2, 3);// set day of week manually (1=Sunday, 7=Saturday)
}

void loop() {
  synchTime();
}

void synchTime(void) {
  if (Serial.available()) {
    char ch = Serial.read();
    if (sentenceIndex <= sentenceSize && ch != '\n' && ch != '\r' && ch != ',') {
      sentence [sentenceIndex] = ch;
      sentenceIndex++;
    } else {
      sentence[sentenceIndex] = '\0';
      applySentence(sentence, sentenceIndex - 1);
      sentenceIndex = 1;
    }
  }
}

void applySentence (char* sent, int leng) {
  switch (sent[1]) {
    case 83:    //S = second
      {
        char secondString[3];
        strcpy(secondString, &sent[2]);
        second = atoi(secondString);
        writeOnAddress(second , 0x00);
        break;
      }

    case 68:    //D = Minute  (Daghigheh in Persian)
      {
        char minuteString[3];
        strcpy(minuteString, &sent[2]);
        minute = atoi(minuteString);
        writeOnAddress(minute , 0x01);
        break;
      }
    case 72:    //H = Hour
      {
        char hourString[3];
        strcpy(hourString, &sent[2]);
        hour = atoi(hourString);
        writeOnAddress(hour , 0x02);
        break;
      }


    case 84:   //T = Day Of Month (Tag in German)
      {
        char dayString[3];
        strcpy(dayString, &sent[2]);
        day = atoi(dayString);
        writeOnAddress(day , 0x04);
        break;
      }

    case 77:  /// M = Month
      {
        char monthString[3];
        strcpy(monthString, &sent[2]);
        month = atoi(monthString);
        writeOnAddress(month , 0x05);
        break;
      }

    case 74:   /// J = Year (Jahr in German)
      {
        char yearString[3];
        strcpy(yearString, &sent[4]);
        year = atoi(yearString);
        writeOnAddress(year , 0x06);
        toggleLED();
        break;
      }

    case 66:  ///B Write Time on Serial: You should Write "B," on serial to get time
      displayTime();
      break;
  }
}

void writeOnAddress(byte value, int address)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(address);
  Wire.write(decToBcd(value));
  Wire.endTransmission();
}

void readDS3231time(byte *second,
                    byte *minute,
                    byte *hour,
                    byte *dayOfWeek,
                    byte *dayOfMonth,
                    byte *month,
                    byte *year)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set DS3231 register pointer to 00h
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  // request seven bytes of data from DS3231 starting from register 00h
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}


void displayTime()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  // retrieve data from DS3231
  readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  // send it to the serial monitor
  Serial.print(hour, DEC);
  // convert the byte variable to a decimal number when displayed
  Serial.print(":");
  if (minute < 10)
  {
    Serial.print("0");
  }
  Serial.print(minute, DEC);
  Serial.print(":");
  if (second < 10)
  {
    Serial.print("0");
  }
  Serial.print(second, DEC);
  Serial.print(" ");
  Serial.print(dayOfMonth, DEC);
  Serial.print("/");
  Serial.print(month, DEC);
  Serial.print("/");
  Serial.print(year, DEC);
  Serial.print(" Day of week: ");
  switch (dayOfWeek) {
    case 1:
      Serial.println("Sunday");
      break;
    case 2:
      Serial.println("Monday");
      break;
    case 3:
      Serial.println("Tuesday");
      break;
    case 4:
      Serial.println("Wednesday");
      break;
    case 5:
      Serial.println("Thursday");
      break;
    case 6:
      Serial.println("Friday");
      break;
    case 7:
      Serial.println("Saturday");
      break;
  }
}

void toggleLED () {
  if (state == LOW) {
    state = HIGH;
  } else {
    state = LOW;
  }
  digitalWrite(13, state);
}

This implies a regular time signal at the USB port - PC problem, not Arduino, and if you’ve got that, the RTC is redundant - hence my previous.

As I said, what you really want is unclear but what is certain is that, if you are serious about keeping time, you won’t be using the DS1307. I believe the DS3231 is a about the same price these days. That said, the DS1307 is adequate for most people’s needs.

I am making a wall clock and an alarm clock that will not always be connected to the computer. The computer will only be used as needed to re-synch the time when it is too far out of bounds.

The solution is the following program sketch (for the PCF8523). To use with the DS1307, a person just needs to change the declare instance to DS1307 instead of PCF8523. This sketch looks for 3 integers separated by commas and then a line return. I used Processing to create the matching computer signal.

/*
   This matches the processing Clock_Synch sketch
*/


// Date and time functions using a PCF8523 RTC connected via I2C and Wire lib
// Display is Adafruit 7seg with HT16K33 backpack.
// Arduino Uno compatible micro-controller
// V2 adds serial clock setting

#include <Wire.h>
#include "RTClib.h"
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"


// Set to false to display time in 12 hour format, or true to use 24 hour:
#define TIME_24_HOUR      true

// I2C address of the display.  Stick with the default address of 0x70
// unless you've changed the address jumpers on the back of the display.
#define DISPLAY_ADDRESS   0x70


// Create display and DS1307 objects.  These are global variables that
// can be accessed from both the setup and loop function below.
Adafruit_7segment clockDisplay = Adafruit_7segment();
RTC_PCF8523 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

// Keep track of the hours, minutes, seconds displayed by the clock.
// Start off at 0:00:00 as a signal that the time should be read from
// the DS1307 to initialize it.
int hours = 0;
int minutes = 0;
int seconds = 0;

// Remember if the colon was drawn on the display so it can be blinked
// on and off every second.
bool blinkColon = true;
String inputString = "";         // a String to hold incoming data
bool stringComplete = false;  // whether the string is complete
unsigned long previousMillis = 0;        // will store last time LED was updated
const long interval = 1000;           // interval
byte hr = hours;
byte mi = minutes;
byte sec = seconds;


void setup () {

  while (!Serial); // for Leonardo/Micro/Zero

  Serial.begin(115200);

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  // Setup the display.
  clockDisplay.begin(DISPLAY_ADDRESS);

  //if (! rtc.initialized()) {  //Commented out to stop the time from reseting
  //Serial.println("RTC is NOT running!");
  // following line sets the RTC to the date & time this sketch was compiled
  // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  //rtc.adjust(DateTime(2017, 4, 1, 12, 0, 0));
  //}
  inputString.reserve(20);
}

void loop () {
  recv();
  loopy();
}

void loopy() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    // This gets the time and sends it out via serial port (USB to computer)

    DateTime now = rtc.now();

    cerial();


    // Now set the hours and minutes.
    if (!stringComplete) {
      hours = now.hour();
      minutes = now.minute();
      seconds = now.second();


      // Show the time on the display by turning it into a numeric
      // value, like 3:30 turns into 330, by multiplying the hour by
      // 100 and then adding the minutes.
      int displayValue = hours * 100 + minutes;

      // Do 24 hour to 12 hour format conversion when required.
      if (!TIME_24_HOUR) {
        // Handle when hours are past 12 by subtracting 12 hours (1200 value).
        if (hours > 12) {
          displayValue -= 1200;
        }
        // Handle hour 0 (midnight) being shown as 12.
        else if (hours == 0) {
          displayValue += 1200;
        }
      }
      clockDisplay.setBrightness(2);
      // Now print the time value to the display.
      clockDisplay.print(displayValue, DEC);

      // Add zero padding when in 24 hour mode and it's midnight.
      // In this case the print function above won't have leading 0's
      // which can look confusing.  Go in and explicitly add these zeros.
      if (TIME_24_HOUR && hours == 0) {
        // Pad hour 0.
        clockDisplay.writeDigitNum(1, 0);
        // Also pad when the 10's minute is 0 and should be padded.
        if (minutes < 10) {
          clockDisplay.writeDigitNum(2, 0);
        }
      }

      // Blink the colon by flipping its value every loop iteration
      // (which happens every second).
      /* Um, no blink colon
        blinkColon = !blinkColon;
      */
      clockDisplay.drawColon(blinkColon);

      // Now push out to the display the new values that were set above.
      clockDisplay.writeDisplay();

      // Pause for a second for time to elapse.  This value is in milliseconds
      // so 1000 milliseconds = 1 second.
    }
  }
}


void recv() {
  // print the string when a newline arrives:
  if (stringComplete) {
    
    Serial.print(hr, DEC);
    Serial.print(mi, DEC);
    Serial.println(sec, DEC);
    rtc.adjust(DateTime(2021, 5, 6, hr, mi, sec));
    cerial();
    // clear the string:
    inputString = "";
    stringComplete = false;
  }
}

/*
  SerialEvent occurs whenever a new data comes in the hardware serial RX. This
  routine is run between each time loop() runs, so using delay inside loop can
  delay response. Multiple bytes of data may be available.
*/
void serialEvent() {
  while (Serial.available() > 0) {

    // look for the next valid integer in the incoming serial stream:
    hr = Serial.parseInt();
    // do it again:
    mi = Serial.parseInt();
    // do it again:
    sec = Serial.parseInt();

    // look for the newline. That's the end of your sentence:
    if (Serial.read() == '\n') {
      // print the three numbers in one string as hexadecimal:
      Serial.print(hr, DEC);
      Serial.print(mi, DEC);
      Serial.println(sec, DEC);

    }
    stringComplete = true;
  }
}


void cerial() {
  DateTime now = rtc.now();
  
  Serial.print(now.year(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.day(), DEC);
  Serial.print(" (");
  Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
  Serial.print(") ");
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.println();
}