Issues with Inconsistent Strike Timing on Clock Tower Project

Hey all

So, a friend and I are making a mini clock tower, and I'm handling the sound signals with an Arduino Uno R3, DS3231, SRD-05VDC-SL-C Relay, and a KK-0630B Strike Magnet.

The relay and strike magnet are powered through the Arduino. The current never exceeds the arduino limit, so I think it should be fine.

Every hour, I activate the strike magnet to strike the bell so you can hear what time it is. I also do this once every half hour so you know it's halfway through the hour.

Now, here's the issue: Not every hour does the bell strike the correct number of times. For example, if it's 10 AM, sometimes it will only strike 4 times, but other days it will strike all 10 times.

I don't know what the problem is. The only thing I can think of is that the millis are moving too fast, and it might cause an error in the loop?

Has anyone got any ideas?

Thank you!

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

RTC_DS3231 rtc;

unsigned long startMillis;
unsigned long currentMillis;
const unsigned long hammerMoveTime = 1000;  // This value is in milliseconds

const int relayPin = 8;
int totalStrikes = 0;
int strikeCount = 0;
bool isStriking = false;
bool halfHourStrike = false;
bool hammerRetracted = false;
bool hammerReleased = true;


void setup() {
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, LOW);
  Serial.begin(9600);

  startMillis = millis();  // Start the internal timer

  if (!rtc.begin()) { // Check if the external clock is found
    Serial.println("RTC not found!");
    while (1);
  }

  if (rtc.lostPower()) { // Check if the external clock has lost its time, otherwise set it
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
}

bool isDaylightSaving(DateTime now) { // Check if it is summer or winter time
  int month = now.month();
  int day = now.day();

  // Simple check: Daylight saving time from April 1st to October 31st
  return (month > 4 && month < 10) || 
         (month == 4 && day >= 1) || 
         (month == 10 && day <= 31);
}

void loop() {
  currentMillis = millis();  // Get the current milliseconds since the start of the program or last loop
  DateTime now = rtc.now(); // Get the current time from the RTC
  int hour = now.hour();
  int minute = now.minute();
  int second = now.second();

    //Serial.print(hour);
    //Serial.print(":");
    //Serial.print(minute);
    //Serial.print(":");
    //Serial.print(second);
    //Serial.println();

  bool isSummer = isDaylightSaving(now); // Check if it is summer or winter time

  // Strike window according to season
  int startHour = 7; // Time to start striking
  int endHour = isSummer ? 22 : 20; // Time to stop striking

  if (hour >= startHour && hour <= endHour && minute == 0 && second == 0 || isStriking) { // Strikes on every full hour

    if (hour > 12 && !isStriking) { // When a new hour starts, check if it's above or below 12
      totalStrikes = hour - 12; // If it's, for example, 13:00, subtract 12 to get 1 hour; otherwise, too many strikes will occur
    //        Serial.print("totalStrikes: ");
    //        Serial.print(totalStrikes);
    //        Serial.println();
    }
    else if (hour <= 12 && !isStriking){
      totalStrikes = hour;
    //        Serial.print("totalStrikes: ");
    //        Serial.print(totalStrikes);
    //        Serial.println();
    }

    if ((currentMillis - startMillis >= hammerMoveTime) && hammerRetracted == false && hammerReleased == true)  // Check if the time period has passed, check if the hammer is released
      {
        digitalWrite(relayPin, HIGH);
    //        Serial.print("Retracting");
    //        Serial.println();
        isStriking = true; // We have started a series of strikes
        hammerRetracted = true; // The hammer is retracted 
        hammerReleased = false; // The hammer is not released
        startMillis = currentMillis;  // IMPORTANT to keep track of the time

      }
      else if ((currentMillis - startMillis >= hammerMoveTime) && hammerRetracted == true && hammerReleased == false) // Check if the time period has passed, check if the hammer is retracted
      {
        digitalWrite(relayPin, LOW);
    //        Serial.print("Releasing");
    //        Serial.println();
        hammerRetracted = false; // The hammer is no longer retracted
        hammerReleased = true; // The hammer is released
        strikeCount++; // We count how many strikes we have done
    //        Serial.print("strikeCount: ");
    //        Serial.print(strikeCount);
    //        Serial.println();
        startMillis = currentMillis;  // IMPORTANT to keep track of the time
      }  

  } else   if (hour >= startHour && hour < endHour && minute == 30 && second == 0 || halfHourStrike) { // Strikes on every half hour

    if ((currentMillis - startMillis >= hammerMoveTime) && hammerRetracted == false && hammerReleased == true)  // Check if the time period has passed
      {
        digitalWrite(relayPin, HIGH);
    //        Serial.print("Retracting");
    //        Serial.println();
        halfHourStrike = true; // This is for 1 strike that we do every half hour
        hammerRetracted = true; // The hammer is retracted 
        hammerReleased = false; // The hammer is not released
        startMillis = currentMillis;  // IMPORTANT to save the start time of the current LED state

      }
      else if ((currentMillis - startMillis >= hammerMoveTime) && hammerRetracted == true && hammerReleased == false) // Check if the time period has passed
      {
        digitalWrite(relayPin, LOW);
    //        Serial.print("Releasing");
    //        Serial.println();
        hammerRetracted = false; // The hammer is no longer retracted
        hammerReleased = true; // The hammer is released
        halfHourStrike = false; // The 1 strike of the half hour has been completed
        startMillis = currentMillis;  // IMPORTANT to save the start time of the current LED state
      }  

  }

  if (strikeCount >= totalStrikes && totalStrikes != 0) { // Check if all the necessary strikes have been completed
    strikeCount = 0; // Reset the current number of strikes done
    isStriking = false; // All necessary strikes have been completed, this will be set to false
  //    Serial.print("isStriking = false ");
  //    Serial.println();
  }
}
  • Always show us a good schematic of your proposed circuit.
    Show us good images of your ‘actual’ wiring.
  • When you use true/false or HIGH/LOW it is difficult for us to grasp what you are doing.
    A Macro that documents what’s happening is what you should be using.

  • When you do diagnostic Serial prints on variables is anything out of the norm ?

  • Serial prints will confirm variables are what you think they are.

  • At 1st thought, a State Machine approach may be the way you should structure this sketch.

  • Explain what you think is happening here ?

Edit

Explain this too:

if (hour >= startHour && hour < endHour && minute == 30 && second == 0 || halfHourStrike)

We supply power to the board, relay, and hammer with a 12VDC/2A transformer. The USB cable isn't plugged into anything right now.The Arduino's digital pin 8 output only makes the relay switch, causing the hammer to swing in or out.When I serial print my steps, everything seems to work as it should. However, when not all swings are actually completed, the serial monitor appears to miss a println statement.

return (month > 4 && month < 10) || 
         (month == 4 && day >= 1) || 
         (month == 10 && day <= 31);

Looking at it now, I see that this code is more complex than what I actually need."

a relay cannot be reliably directly driven using an Arduino - so are you using a relay or a relay module, and if the latter, how do you have it wired? My concern is relay coil transients disrupting the Arduino’s processor, output circuit, etc.

1 Like
  • Always show us a good schematic.
  • And and what the relay is controlling too.
1 Like

The the conditional logic is so complex in your code that I can not really follow. I ageee with @LarryD suggestion of using a state machine with switch case contolling the process as the bsst way forward.

1 Like

Nice project, would be interresting to see how the Hammer mechanics works. Maybe you have a timing problem caused by your mechanics which is slower than "millis" and out of syncronizing with the program.
Your clock is digital or analog classic ?
My favorate projects are also clocks. On a Kuckuck clock project I implemented some similar funktionality as your project.
About your program, you are not defining any funktions to make the program more understandable and easier to debug. Put your Hammer operation into a funktion e.g. called void hammerStrike()
For the active operating hours of the bell define a bool variable, e.g. bellActive and define the active hour conditions once after getting the new hour values like this:

if(newHour >= nightLimit && newHour <= dayLimit) 
 {bellActive = HIGH;} 
else 
 {bellActive = LOW;}

dayLimit and nightLimit are the limit hours for the period you want to hear the bell.
I think the RTClib.h has a funktion to set the clock to 12 hour ore 24 hour mode. Check this, you don´t need to adapt 24 hour mode to 12 hour mode manually by rest 12 from the value the clock returns.
Make your program more compact then you can position debug messages on the serial monitor more efficient.

Yeth.

hammerRetracted looks like it is always the opposite of hammerReleased. If so, there is no need for two variables to track one state.

        hammerRetracted = true; // The hammer is retracted 
        hammerReleased = false; // The hammer is not released

//...

        hammerRetracted = false; // The hammer is no longer retracted
        hammerReleased = true; // The hammer is released

a7