Accurate output pulse every 15 seconds for mechanical clock

Hi, I want to drive a clock. The clock uses a solenoid to drive the mechanics. The solenoid needs to be activated every 15 seconds. What is the best way to provide that activation every 15 seconds accurately within the software? Just a regular Arduino with RTC and using the TimeAlarms library with an Alarm.timerRepeat?

The clock itself doesn't have to know the absolute time. There will be a button to adjust the displayed time forwards to allow the initial setting to the correct time.

Any other thoughts?

Just a regular Arduino with RTC. No need for a library.

Many RTC modules provide an output at accurate 1 second intervals. Count them and every 15 seconds output your required pulse

Which RTC do you have ?

Thanks. An MH D3231.

The 3231 has a square wave output that you can use as I suggested

The clock itself doesn't have to know the absolute time. There will be a button to adjust the displayed time forwards to allow the initial setting to the correct time.

As a suggestion, and only that, something I once built for a friend was a driver for his collection of mechanical clocks. The driver knew the correct time and it knew the time on the mechanical clocks. The meant that at night the drive to the mechanical clocks could be turned off, meaning my friend could get to sleep without the KERCLUNK! they made every 30 seconds. In the morning he turned the drive back on and it sent pulses to the clocks to bring them to the correct time.

siddyboy:
There will be a button to adjust the displayed time forwards to allow the initial setting to the correct time.

Much more fun project to attach some sensor so that the Arduino can tell what time the mechanical clock is showing. Like maybe an optical sensor which can tell when the clock is showing exactly midday/midnight. Then, as suggested, it could set the time correctly itself, even adjust for DST.

I would use a Wemos Mini as the Arduino so it can fetch time from a network time server over WiFi once per day. No RTC needed - its internal clock will be accurate enough to keep time in-between.

You probably already know but I will mention that you will need a transistor driver (MOSFET) and flyback diode to drive the solenoid as an Arduino output most likely does not supply sufficient current.

Thanks for all the replies and ideas.
I have the hardware side of things covered - or rather I will have - just gathering ideas for the approach at the moment.
Adding some kind of sensor to the mechanism to feedback the displayed time will be an extension once things are up and running. Turning it off at night sounds like a sensible idea. I wouldn't mind a pointer to the best way to count the RTC 1 Hz signal in the s/w.

While the 1 Hz output seems cool, it actually requires an extra Arduino pin, since it is not enabled on DS3231 power up. To enable it, requires accessing and programming the DS3231 through the 2 I2C pins. So I doubt that it's worth it - you can just check for seconds changing instead. The 32kHz output is enabled by default so you could use that without any other connections to the DS3231 except ground of course.

I wouldn't mind a pointer to the best way to count the RTC 1 Hz signal in the s/w.

Here is some demonstration code which uses an external interrupt to read the rising edge of the square wave rtc output.

Jumper the Square Wave output pin of the RTC to pin2 of the Arduino. The millis() print outs show the inaccuracy of the Arduino to the rtc.

#include <Wire.h>
const unsigned long interval = 15; //15 seconds
volatile boolean driveClock = false;
volatile byte tick;
volatile boolean firstTick = true; //sychronization for first tick
unsigned long currentMillis = millis();
unsigned long previousMillis = currentMillis;

void setup() {
  Serial.begin(115200);  
  Wire.begin();
  enable1HzTick();
  //jumper SQW/INT pin of DS3231 to pin2 of Arduino
  pinMode(2, INPUT_PULLUP); // may require external pullup
  noInterrupts();
  attachInterrupt(digitalPinToInterrupt(2), addTick, RISING); //jumper sqw out to pin 2
  EIFR = bit (INTF0);// clear any existing interrupt flag
  interrupts();
}

void loop() {
  if (driveClock) {
    driveClock = false;
    clockTick();
  }
}

void enable1HzTick() {
  Wire.beginTransmission(0x68); //RTC i2c address
  Wire.write(0x0E);//register for square wave output
  Wire.write(B00000000);//enable 1hz see data sheet
  Wire.endTransmission();
}

void addTick()
{
  if (!firstTick)
  {
    tick++;
    if (tick >= interval) {
      driveClock = true;
      tick = 0;
    }
  }
  else //first tick edge synchronization code
  {
    firstTick = false;
    previousMillis = millis();//start timing from first edge
    driveClock = true;
  }
}
void clockTick() {
  //function to drive mechanical clock solenoid goes here
  //replaces timing demonstration Serial output 
  currentMillis = millis();
  Serial.print("update clock interval ");
  Serial.println(currentMillis - previousMillis);
  previousMillis = currentMillis;
}

There is no need to use interrupts, the DS3231 with TimeLib and TimeAlarms is the easiest and best option by far in my opinion.

Koepel:
There is no need to use interrupts, the DS3231 with TimeLib and TimeAlarms is the easiest and best option by far in my opinion.

Yes, but is it the most accurate method? It depends the Arduino resonator/crystal.

Using the libraries and adjusting the internal reference time to the external time of the RTC is best for time keeping, but the OP's issue was driving a mechanical clock every 15 seconds. He appeared to want something more accurate than a millis() or hardware timer solution which depended on the accuracy of the processor clock.

The DS3231 is better than other common RTCs, because its crystal is internal and it compensates for changes by temperature.
The TimeLib synchronizes itself with the RTC. It synchronizes every 5 minutes. The interval can be changed if needed. When a RTC is available then it can be 1 minutes or less. Just a quick I2C communication is easier than a network NTP call. When a Arduino board with a crystal is used, then the interval can perhaps be one hour without problems.

PaulRB mentioned a ESP board and network NTP. I think that is also no problem. The ESP32 has a simple example: SimpleTime.ino.

Variations of microseconds for a mechanical clock don't make sense to me.

For just a silly millis() with the accuracy of the Arduino resonator or crystal I would have given a link to my millis_clock.ino :wink: which is not so bad when a Leonardo is used (crystal) or a clone Uno with a crystal instead of a resonator.

I understand how the TimeLib synchronizes to the RTC every 5 minutes.

However, it is my understanding that a 15 second tick controlled by the TimeLib/Time Alarms would still be dependent upon the accuracy of the internal oscillator and millis().

The internal time keeping with reference to the outside world will be accurate dependent upon the period synchronization, but the interval measurement dependent on the internal clock will not be.

Yes, the TimeLib uses millis() inbetween the synchronization.
When the seconds are written to the DS321, then it restarts the clock divider, so it starts with a full second. I have not had a problem with a second that is shorter or longer yet, but now I think it about it, that could give a glitch.

@cattledog Thanks for explaining. You are right. I stand corrected.

OP here. Thanks for the interesting discussions. Having though about this a bit more I think I can clarify the problem. There needs to be a pulse every 15 seconds to drive the clock. The accuracy of any particular 15 s interval is not particularly important as long as the average over time is accurate so the clock keeps time.

Sorry if that changes the problem slightly...

If you have the DS3231 and the free pins to connect SDA/SCL/SQW I would try the square wave interrupt counting approach.

Making the fully automated mechanical clock where the Arduino drives the clock and also knows both the correct time and position of the hands, as @PerryBebbington describes, is maybe where you are ultimately headed.

siddyboy:
OP here. Thanks for the interesting discussions. Having though about this a bit more I think I can clarify the problem. There needs to be a pulse every 15 seconds to drive the clock. The accuracy of any particular 15 s interval is not particularly important as long as the average over time is accurate so the clock keeps time.

Sorry if that changes the problem slightly...

You do not need the Time library and you do not need to use the millis() counter to keep track of clock time. You do not need an input pulse every 15 seconds. You only need a regular input pulse that you can count to make up 15 seconds. If you have a 1Hz pulse, you just count each pulse up to 15 and reset the counter before doing your clock routine. If you use the 32,768Hz output from the RTC, you just count up to 32768*15.

You haven't given any feedback about any choices or directions you have taken on this project. The RTC is a must have, so I suggest you investigate how you will connect and drive the DS3231. Your case is very simple, since you can do the whole thing without any initialization of the RTC. All you need to do is read the seconds register. Keep a variable with the last value of the register. Read the register often in loop(). If the register value changes, set the variable to the current value, increment the counter and if it reaches the 15 count, reset and actuate as I described above. You can do that with an RTC library, or even using the wire library I2C functions directly.

Although using the SQW output is "neat", it's not really necessary, requires an extra processor pin, and requires much more complicated software to accomplish (however, not too hard with some libraries), than this method. Alternatively, the connection that requires the least number of processor pins, is one processor input pin to read the 32kHz RTC pin. With that you also don't need any RTC library or custom code at all - the RTC will happily produce 32kHz all by itself when it is powered up. You can count the 32kHz as easily with an interrupt, as with almost any frequency.

1 Like

@aarg has given you an alternate architecture (monitor the seconds register) to use instead of the sqw interrupt.

Read the register often in loop(). If the register value changes, set the variable to the current value, increment the counter and if it reaches the 15 count, reset and actuate as I described above. You can do that with an RTC library, or even using the wire library I2C functions directly.

In my opinion/experience the frequent reading of the rtc on the i2c bus at loop speed with the Wire library can cause reliability problems with a sketch. That's one of the reasons why the TimeLib and periodic synch to the rtc is a good approach when just keeping time.

Enabling and counting the square wave at 1Hz would appear to cost 1 pin, but I'm not certain I see any added software complications over counting the transitions of the seconds register.

aarg:
You haven't given any feedback about any choices or directions you have taken on this project.

I haven't made any choices yet. I thought I'd ask the best way to go before launching into anything. I already have the RTC which I bought for another project but didn't need in the end. Sounds like I might get to use it after all.