Ringing massive church bell with Arduino and solenoid (and relay and hammer)

Hi all, I'm putting together something to ring a bell automatically. It's a massive bell at the top of a church with a diameter of about 40 inches.

I've got the hardware side of things good to go to. I'll basically going to trigger a relay with the Arduino every hour, on the hour for the hour specified.

I will ONLY ring it from 9:00 AM to 6:00 PM. So a total of 10 cycles per day. I'm using a DS3231 RTC.

I've got the board working correctly. I'm currently just testing it with the board's LED. (I do know the relay and solenoid that I'm using works, I just am using the LED currently for testing purposes.)

Can you guys please give a quick review of my code and give me any suggestions to clean it up or other ideas? Code is posted below. Also attached. Not sure which is the most appropriate way to share it.

Please be gentle! Not an expert coder here! I've spent hours on this and even though I could have just had my son (who's studying CS) just code it for me, I wanted to do it myself. Dusting off the cobwebs from that part of my brain was the biggest hurdle.

Okay, my MO:

  1. Check the "strike hours" (set up as an array) via a simple for loop.
  2. If current hour equals one of the predetermined "strike hours" then go to "strike" subroutine.
  3. After strike subroutine is complete, return to continue main loop.
  4. I will have a manual button so the end user (i.e. me) can ring the bell once at any time of the day by just simply pushing the button.

I haven't set an LCD yet. I know I need to include that at some point.

THANK YOU in advance!!!

#include "RTClib.h"
RTC_DS3231 rtc;

int buttonState = 0;         // variable for reading the pushbutton status
const int buttonPin = 2;     // the number of the pushbutton pin
char days[7][10] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
const int ledPin =  13; 
int strike_hours[10] = {9,10,11,12,13,14,15,16,17,18};
int number_of_strikes = 0;


void strike_bell() {
  number_of_strikes = 0;
  DateTime now = rtc.now();
  number_of_strikes = number_of_strikes + now.hour();
  if (now.hour() >= 13) {
    number_of_strikes = number_of_strikes - 12;
  }

  for(int y = 0; y < number_of_strikes; y++){
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      delay(2000);
  }

 
}


void setup () { 
  Serial.begin(9600);
  rtc.begin();
  pinMode(ledPin, OUTPUT);     // Initialize the LED_BUILTIN pin as an output
  pinMode(buttonPin, INPUT);  // initialize the pushbutton pin as an input:
  //rtc.adjust(DateTime(2020, 02, 19, 17, 9, 00));
  
}


void loop () {

DateTime now = rtc.now();
for(int x = 0; x < 10; x++) {
  if (now.hour() == strike_hours[x] && now.minute() == 00 && now.second() == 00) {
         strike_bell();   // strike the bell
         delay(1);  
         } else {
         digitalWrite(ledPin, LOW);  // keep LED off:
  }
  
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(" (");
    Serial.print(days[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();
    Serial.print("Temperature: ");
    Serial.print(rtc.getTemperature());
    Serial.println(" C");
    Serial.println();
    delay(100);

buttonState = digitalRead(buttonPin);
    if (buttonState == HIGH) {
        digitalWrite(ledPin, HIGH);
        delay(100);
        digitalWrite(ledPin, LOW);
        delay(2000);
             } else {
          digitalWrite(ledPin, LOW); // keep LED low
          delay(1);
  }


}
}

Clock_Code_for_Bell_Ringer_4.ino (2.12 KB)

1 Like

I find the scanning of the strike_hours[] array somewhat unintuitive. I would have used a 24 hour array of flags and just indexed it with the hour to see whether it should ring or not. The really simple way, which would work in your case, is to maintain a "first-hour" and "last-hour" variable or constant pair. Then the hour can be compared to make the determination

  if (now.hour() >= first-hour && now.hour() <= last-hour && now.minute() == 00 && now.second() == 00) {
//ring bell

Later on, if you want to add a feature to change the hours without re-compiling and uploading a new sketch, you would have the problem that the array is not long enough to add new hours.

Also you have delays in excess of one second in your loop(). Any time one of those branches is taken, you run the risk of missing your one second window for hour detection.
It's better to detect a change of hour using a state change variable, because no matter what delays, when they're finished the timing will catch up and complete the appointed hour tasks.

i can hear it now! The first hour arrives and your solenoid goes "click" against the massive bell. You must be kidding.

Paul

Paul_KD7HB:
i can hear it now! The first hour arrives and your solenoid goes "click" against the massive bell. You must be kidding.

Paul

That is going to depend on the solenoid - a search on google will bring up several companies selling such devices, including one that has a selection of 5, 10, and 20 pound (pull) solenoids.

you must check the response times of the solenoids, the travel distanse of the hammer, the force (hammer inertia) of the blow etc, so to deside the "pulse" . "100 ms " is not granded.

Many years ago I tried to "play" a bell using the manual buttons on the control, and I realised it needed spesific "on time" and a greater than "minimum off time", so to have 2 blows.

aarg:
I find the scanning of the strike_hours[] array somewhat unintuitive. I would have used a 24 hour array of flags and just indexed it with the hour to see whether it should ring or not. The really simple way, which would work in your case, is to maintain a "first-hour" and "last-hour" variable or constant pair. Then the hour can be compared to make the determination

Perfect. Thank you. I was stuck on reading the array with a loop and didn't think of this simple approach.

And you're right about the delays. I will address those.

Paul_KD7HB:
i can hear it now! The first hour arrives and your solenoid goes "click" against the massive bell. You must be kidding.

Paul

Paul, not kidding and David_2018 is correct. I think you might not realize what solenoid I'm talking about. I have a 20 pound pull solenoid that will drive a 4 pound hammer. See pictured. I only need to position the hammer about 2 inches away from the bell. If the report is not loud enough because I need more pull, I will put 2 solenoids in parallel. I'll share a video of the results back here when I get it running.

1 Like

GRuser:
you must check the response times of the solenoids, the travel distanse of the hammer, the force (hammer inertia) of the blow etc, so to deside the "pulse" . "100 ms " is not granded.

Many years ago I tried to "play" a bell using the manual buttons on the control, and I realised it needed spesific "on time" and a greater than "minimum off time", so to have 2 blows.

You're 100% right. I've tested the solenoid on my work bench and the 100ms triggers it properly (but that is with an artificial load I put on it which was only an 8 pound pull spring.) Even though this works great, I'm sure I'll have to make adjustments with the hammer apparatus and while I'm on the roof. I'll be taking my laptop with me!

GPIER:
Even though this works great, I'm sure I'll have to make adjustments with the hammer apparatus and while I'm on the roof. I'll be taking my laptop with me!

Be sure to have some hearing protection.

I would think the hammer would be adjusted so that it does not sit against the bell after striking it, but the inertia from the solenoid carries it to the bell then it swings back enough to allow free movement of the bell while it rings.

When your solenoid system fails, consider a much simpler system based on an electric motor with a cam to raise and release the hammer!

Paul

Paul_KD7HB:
When your solenoid system fails, consider a much simpler system based on an electric motor with a cam to raise and release the hammer!

Paul

I have seen some professional systems. None of them uses motor, ALL use solenoid.
The hammer is inside the bell, it does not drop, so to hit the bell, it goes up, hits the bell and returns with gravity.

ok, perhaps I go only to places with "solenoid" mechanisms.

GRuser:
I have seen some professional systems. None of them uses motor, ALL use solenoid.
The hammer is inside the bell, it does not drop, so to hit the bell, it goes up, hits the bell and returns with gravity.

ok, perhaps I go only to places with "solenoid" mechanisms.

Then, I guess, the "bell" part is stationary and there is some solid fixture that the solenoid is attached to. I was imagining the striking was from outside the "bell" part.

Paul

Paul_KD7HB:
Then, I guess, the "bell" part is stationary and there is some solid fixture that the solenoid is attached to. I was imagining the striking was from outside the "bell" part.

Paul

the solenoid part is inside and "stays" there (steady) attached from the "o" (where the "manual" hammer is hanging) but displaced so to be near the lip (always inside) and leave the manual hammer to be operated in case of "automatic" malfunction.

!!! I think I must mention that here in Greece the bells are hanged from the ceiling with chains from the upper-central outside point. In manual, they are operated by swinging the hammer and not the bell. Perhaps that is a difference

Sorry I dont have a photo to show to you.

So, the reaction of the solenoid armature moving to strike the bell also causes the solenoid to move in the opposite direction, pivoting on the "O" bell fixture?

Paul

my father did something like this decades back

washing machine water valve solenoids
screen door spring chains
a homemade keyboard

preload the clapper about an inch from the bell. the solenoid kicks it enough to impact the bell.

the organist could play a tune on the bells.

Paul_KD7HB:
.. pivoting on the "O" bell fixture?

Paul

Only the manual hammer is pivoting there , the solenoid is not , it is steady fixed. Can say that through the fixing "bar" to the "O", the solenoid is moved together with the nearest lip of the bell, keeping the same distance all the time.

For an "out of the system" observer , while the hammer is moving towards the lip, the lip also moves slightly towards the hammer (reaction).

hi, just some comments:

int buttonState = 0;         // byte or bool is large enough, no need for an int, not needed in global scope, put it to your loop.
const int buttonPin = 2;     // byte is large enough, no need for an int
char days[7][10] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};  // doesn't change, make it const
const int ledPin =  13; // byte  is large enough, no need for an int
int strike_hours[10] = {9, 10, 11, 12, 13, 14, 15, 16, 17, 18};  // byte is large enough, no need for an int, make it const
int number_of_strikes = 0;  // byte is large enough, not needed in global scope, put it to your function

Here it is up close.

noiasca:
hi, just some comments:

int buttonState = 0;         // byte or bool is large enough, no need for an int, not needed in global scope, put it to your loop.

const int buttonPin = 2;     // byte is large enough, no need for an int
char days[7][10] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};  // doesn't change, make it const
const int ledPin =  13; // byte  is large enough, no need for an int
int strike_hours[10] = {9, 10, 11, 12, 13, 14, 15, 16, 17, 18};  // byte is large enough, no need for an int, make it const
int number_of_strikes = 0;  // byte is large enough, not needed in global scope, put it to your function

Okay thanks. I'll work on these.

Here is a preliminary test of the RTC with arduino attached, from street level, just before 5:00.