Chiming clock with big bell!

Hello all.

I am a total n00b to Arduino, I don't have any hardware yet, I am just asking for advice to see if this is the best way of doing what I want.

I have a church bell with an electric striker (with a controlling relay) and I have the ability to make other smaller tubular bells for other notes.

What I want to do is make a controller that keeps time and chimes a tune on small bells every quarter and chimes the hour on the big bell. I would like to be able to program tunes with a selection of different ones which I can change with an input. It would also be useful to have a digital readout of the time.

The clock would need either manual time setting, or preferably with a wifi connection to an online ntp time server.

The system would need also to be silent at night, so a settable on and off time.

Most of the time this would just be left to run with no inputs, just left to its own devices!

I have no idea at all what hardware I would need, and as this would be it's only function I don't want anything too fancy! I know very little of programming (but am willing to learn), so if there is something already existing that I can adapt for my needs that would be great.

Please say everything in ways that a complete idiot can understand!

Thanks

Tseb.

A RTC (Real Time Clock) module will keep very good track of time. It also has a battery so that the time stays through power failures and such. The RTC module is smart enough to know date, day of week, time for dawn, time for dusk, year, and it will also do daylight time changes. So that bit of your project is very do-able. No WiFi or other fancy stuff required - makes it simpler.

The "tune" part of your project could take some programming if it's elaborate. Dinging the big bell and a few smaller ones isn't a big deal. Playing a real tune like "Old Lang Syne" at midnight on New Year's would be harder.

Adding a display for the time is also something that's been done lots of times. Add a LCD display. Since you need to connect your computer's serial port to the RTC module to set/read it, a display might not get enough use to be worth the effort. Or it might be something you add later.

Buttons and indicator LEDs (so you know when you've successfully push a button) are easy enough. Good luck.

Please say everything in ways that a complete idiot can understand!

One of my grandchildren will say things slowly and with greater emphasis when I too don't quite understand! We find it so amusing! However, I will do my best not to offend you.

MrsCrossRoads has posted many good suggestions. I will expand on that.

...that keeps time and chimes a tune on small bells every quarter and chimes the hour on the big bell.

I have a church bell with an electric striker (with a controlling relay) and I have the ability to make other smaller tubular bells for other notes.

It would be critical to know how long the striker is activated... perhaps it is different for each bell? Maybe shorter for quiet rings?

How many notes in those tunes? Do you have some chime recordings that could be used to time the bell sequence? Most PC sound editors allow you to measure exactly when a sound starts.

I envision a 'tune' to be an array of numbers (integers) store on Arduino. Similar to a spreadsheet.
Each bell would have a time pair for each ring from start of tune; time striker is activated and then time striker is released.

I also suggest a simulation mode, where a simple beep close to the bell harmonic could be used to test your tunes. Otherwise the neighbours may not appreciate your tweaking!

[quote author=George Matthews date=1414891291 link=msg=1944894]

I also suggest a simulation mode, where a simple beep close to the bell harmonic could be used to test your tunes. Otherwise the neighbours may not appreciate your tweaking![/quote]
With a church bell, neighbours for several hundred yards around would not appreciate his tweaking. I also doubt if they would appreciate his bell ringing at all hours of the day and night. He hasn't told us where he's posting from. I hope he's not near me.

Thank you for all the replies. Very helpful.

I found some of it very funny! I am in the UK, and the bell is not set loud (I have a leather muffler on the hammer) and this is on my workshop miles from anywhere - and hundreds of miles from Henry_Best in London…

The main bell needs a powered strike length of about 1 second, with a 1/2 second gap between strikes. The other chimes (not yet made, to follow) are much smaller electromagnets and can repeat at least three times a second. In total there will be 8 tune chimes plus the main bell.

I agree with the idea of a simulation mode, with a beep instead of the bells, to help with initial testing.

The idea of the display is that it permanently displays the time as a clock for my workshop, which will also tell me when the bell is due to chime.

I agree with MrsCrossRoads that this would be much simpler without a wifi connection, as long as the clock part is accurate enough.

The problem for me is that I still don't know where to start. Which main board to use? The cheapest and easiest for me. I use a Mac, so needs to be programmable from Mac. Native USB the best? Which shield board for a RTC or is that built into the main board? What outputs will I need - can the main board handle outputting to small mechanical relays? If so, what voltage relays - or is there a better way of doing this? Any guidance to any posts or tutorials on programming at this level for n00bs?
I did BASIC programming back in the 70s/80s, a lot of which I still remember, and I do occasional other programming stuff for work, but not C based.

Thank you again for the help.

Tseb.

Tsebsietxis:
Thank you for all the replies. Very helpful.

I found some of it very funny! I am in the UK, and the bell is not set loud (I have a leather muffler on the hammer) and this is on my workshop miles from anywhere - and hundreds of miles from Henry_Best in London…

Thank goodness for that! I'd not want Little Ben in the next street. :slight_smile:

Hi, how about some more specific recomendations?

Rtc: a ds3231 based module. Temperature compensated and very accurate, so should not need adjusting more than once every couple of years.

Arduino: Nano 3 is my recomendation. Very similar to Uno but fits neatly on a breadboard for prototyping.

Display: a 16x2 lcd display with i2c interface would be a useful until/if ever you want to build something bigger.

Need to know the electrical specs of the striker mechanisms to advise on how to drive those.

Paul

Thank you PaulRB for your useful post.

Sounds good so far.

At the moment the relays I am using are electromechanical with 12v coils - I could change these, but I use these for work, so I have lots of them around! The coil takes 80mA - see http://uk.rs-online.com/web/p/non-latching-relays/0376385/?searchTerm=376-385

I also have some miniature PCB 80 mW Nominal operating power relays that I could use…

I decided to use relays because the main striker mechanism for the big bell takes 3A at 24v. The others are 24v 0.5A.

Tseb

I have just found these - could I use two of these to control what I want?

Tseb

Tsebsietxis:
At the moment the relays I am using are electromechanical with 12v coils - I could change these, but I use these for work, so I have lots of them around! The coil takes 80mA - see http://uk.rs-online.com/web/p/non-latching-relays/0376385/?searchTerm=376-385

I also have some miniature PCB 80 mW Nominal operating power relays that I could use…

I decided to use relays because the main striker mechanism for the big bell takes 3A at 24v. The others are 24v 0.5A.

Well, an Arduino output can't drive those relays directly. You would need a transistor to drive the relay, with the relay then driving the striker. Might as well cut out the middle-man!

As long as those 24V strikers are DC not AC, a logic-level power FET can drive them and be driven directly by the Arduino. Example:

After a steep learning curve I have finally managed to get this working as I want it.

I am using an Arduino Uno (initially I bought a Nano, but it was a cheap copy from eBay, and I couldn't get it to communicate with my Mac, so then I bought a Nano from a real shop, but this also wouldn't communicate. When I returned it to the shop a helpful employee suggested an UNO instead, as he said it didn't need a driver on a Mac - he was quite correct. Much easier to use an UNO on a Mac.
I bought a cheap eBay I2C display and a ds3231 RTC unit. Then buttons and a sounder and a few MOSFETs as suggested.
After a lot of messing around I got the hardware to work, put it neatly into a box, and then it was on to software.
I spent lots of time seeing how sketches are made, and I found some that I could cannibalise to help me.

So my sketch is below. It chimes on the hour (I don't yet have the hardware to chime a tune, so it is only an hourly chime). It has a 'silent' button when not wanted, and it is also a good clock for the workshop too. It has a daytime/night-time function so it is silent at night, and it also has a small section to chime midnight on new years' eve.

The only thing I couldn't get my head around (but I am not that worried, and I ran out of time) is that when it is chiming the display does not update. This is because I am using the delay() to time the chime rate. If I used millis() I could get it to display and chime. But this isn't important to me.

Now I need to find another project to do, as I really have got into Arduino. I am hooked! Any suggestions anyone?

Thank you again to all the previous posters for their help and guidance - especially to PaulRB for the specific stuff.

Separated into more than one post as there is a limit on the number of characters in a post

(in the code below I use the terms ‘strike’ and ‘chime’ interchangeably)

#include <Wire.h>  //Calls I2C serial library
 
#include <ds3231.h> // Calls RTC Clock library
 
#include <LiquidCrystal_I2C.h>  //Calls library for LCD Display

#define BUFF_MAX 128

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address

uint8_t time[8];
char recv[BUFF_MAX];
unsigned int recv_size = 0;
unsigned long prev, interval = 1000; //Variables for display/clock update rate - change variable 'interval' to change display time - I have found 1000 is best

//Variables for Bell//
int hourTest = 0; //flag to say if clock has chimed this past hour
int strike = 0; //variable used to count bell strikes
int hourValue = 0; //variable for hour
int startStrikeTime = 9; // time to start striking the bell for the day (24 hour clock, whole hours only, inclusive)***
int stopStrikeTime = 21; // time to stop striking the bell for the day (24 hour clock, whole hours only, inclusive)***
int quietTime = 1; // night-time setting

//Button variables//
int silent = 0; //variable for pressbutton to silence bell
int buttonState;             // the current reading from the input pin
int lastButtonState = HIGH;   // the previous reading from the input pin
long lastDebounceTime = 0;  // the last time the output was toggled
long debounceDelay = 100;    // the debounce time; increase if the output flickers***


///////////////////////////////////////////////////////////
// Setting Custom Characters (Bell character for silent) //
///////////////////////////////////////////////////////////

byte bell[8] = {
0x4,0xe,0xe,0xe,0x1f,0x1f,0x4 //This is a filled-in bell icon to show bell will strike
};

byte bellOutline[8] = {
0x4,0xa,0xa,0xa,0x11,0x1b,0x4 //This is an outline bell icon to show bell is not on silent, but the bell won't strike because it is evening
};
///////////////////////////////




void setup()
{
    Wire.begin();  //Initializw I2C communication library
    DS3231_init(DS3231_INTCN); //Starting RTC
    memset(recv, 0, BUFF_MAX);
   
//////////////////
// SETTING TIME //
//////////////////

   
  //  parse_cmd("T002211706122014",16); //Uncomment (and change the value in the quotes) to set the time and date in the format TssmmhhWDDMMYYYY (keep the 'T') 
                                        //but then reload with this out of use - otherwise will reset clock each time program started!
   
////////////////////
// Initialise LCD //
////////////////////

    lcd.begin(16,2);   // initialize the lcd for 16 chars 2 lines, clear and turn on backlight
    lcd.clear();
    lcd.backlight();
    
////////////////////////////////////////////////
// Outputting Custom Characters to LCD Memory //
////////////////////////////////////////////////
    
    // create a new character
lcd.createChar(1, bell);

 // create a new character
lcd.createChar(2, bellOutline);
   
    
///////////////////////////////////////
    
////////////////
// Pin Setups //
////////////////

    pinMode(0, OUTPUT); // initialize digital pin 0 as an output.
    digitalWrite(0, LOW);    // turn pin 0 off by making the voltage LOW
    
    pinMode(13, OUTPUT); // initialize digital pin 13 as an output.
    digitalWrite(13, LOW);    // turn pin 13 off by making the voltage LOW
    
     pinMode(7, OUTPUT); //Sets low to act as GND for BUZZER on pin 5
    digitalWrite(7, LOW);  
    
    pinMode(5, OUTPUT); //TEMP FOR BUZZER
    digitalWrite(5, LOW);  
    
     pinMode(3, OUTPUT); //Sets low to act as GND for button on pin 2
    digitalWrite(3, LOW);  
    
    pinMode(2, INPUT); //input for button for silent function - other side connected to ground.
  digitalWrite(2, HIGH); //Turn on pullup resistor

   
}

void loop()
{
  

   
    char tempC[6]; //Local variable to store converted temperature reading from Real Time Clock module
    float temperature; //Intermediate temperature variable
    char buff[BUFF_MAX];
    
//////////////////////////////////////////////
// Passing value of internal clock to 'now' //
//////////////////////////////////////////////     
    unsigned long now = millis(); //Local variable set to current value of Arduino internal millisecond run-time timer
    struct ts t; //Structure for retrieving and storing time and date data from real time clock

////////////////////////////////////////////////////////////////////////////////
// Button Section - changes state from striking to silent when button pressed //
////////////////////////////////////////////////////////////////////////////////

// read the state of the switch into a local variable:
  int reading = digitalRead(2);
// check to see if button pressed
   if (reading != lastButtonState) {
    // reset the debounce timer
    lastDebounceTime = millis();

 // toggles the 'silent' variable each time the button is pressed 
      if (reading == LOW) { 
      silent = !silent;
    } 
  } 
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    buttonState = reading;
  }
  
  //change the LCD bell display
  if(silent==0) {
    if(quietTime==0) {
    lcd.setCursor(15,1);
        lcd.write(1);
    }
    else {
      lcd.setCursor(15,1);
        lcd.write(2);
  }
  }
  else {
    lcd.setCursor(15,1);
        lcd.write(" ");
  }

  
  // save the reading.
 lastButtonState = reading;


////////////////////////////////////////////////////////////
// Display Time etc. after variable 'interval' has passed //
////////////////////////////////////////////////////////////

    if (now - prev > interval) {
        DS3231_get(&t); //Get time from RTC
        parse_cmd("C",1);
        temperature = DS3231_get_treg(); //Get temperature (treg = temperature register)
        dtostrf(temperature, 5, 1, tempC);

(Code continues on from last post)

////////////////
// Print Date //
////////////////  
        
         lcd.setCursor(0,1); //Move cursor to position 0, line 1
        if (t.mday < 10) { lcd.print("0"); } //adds 0 if it is single digit date
        lcd.print(t.mday); //prints date
        
        printMonth(t.mon); //Calls MONTH Subroutine below to display short name of month rather than a number
        
        lcd.print(t.year); //prints year
      
////////////////
// Print Time //
////////////////        
      
      
       //RTC is operated in 24-hour mode and conversion to 12-hour mode done here, in software
  if(t.hour == 0) {hourValue=12;} //Convert zero hour for 12-hour display
    else if(t.hour < 13) {hourValue=t.hour;} //Just copy hour if less than 13
        else if(t.hour >= 13) {hourValue=t.hour-12;} //If PM, convert 24 to 12 hour
       
      
      
        lcd.setCursor(0,0); //Move cursor to position 0, line 0 (upper line)
        if (hourValue < 10) { lcd.print("0"); } //adds 0 before hour if it is single-digit hour to show two digits
        lcd.print(hourValue); // Prints hour
        lcd.print(":");
        if(t.min<10) //adds 0 if it is single digit minute
        {
          lcd.print("0");
        }
        lcd.print(t.min); //prints minutes
        lcd.print(":");
        if(t.sec<10) //adds 0 if it is single digit second
        {
          lcd.print("0");
        }
        lcd.print(t.sec); // prints seconds
        
        
///////////////////////
// Print Temperature //
/////////////////////// 
    
       lcd.print(' '); //print space before temperature
       lcd.print(tempC);
       lcd.print((char)223); //print degrees sign
       lcd.print("C");
     

/////////////////////
// Test for Strike //
///////////////////// 

 //Tests to see if is daytime to allow striking
            if(t.hour >= startStrikeTime && t.hour <= stopStrikeTime) { 
              quietTime=0;
  }
          else { quietTime=1; }
          
        if(t.min==0) // Tests to see if minutes are 0 to start striking.
        {
          //Test to see if silent is set
            if(silent==0) {  
          if(quietTime==0) {
        hourStrike(); // call striking subroutine
          }
         }
        }
           
           
           
////////////////////////////////////////////////////////////////////
// New Year's Day Striking Midnight (unless strike set to silent) //
////////////////////////////////////////////////////////////////////   
           if (t.mon == 1) {
             if (t.mday == 1) {
               if (t.hour == 0) {
                 if (t.min == 0) {
                   if (silent == 0) {
                     hourStrike(); // call striking for midnight
                   }
                 }
               }
             }
           }
           
           
           
        prev = now; // Sets timing for loop repeat - This loop repeats many thousands of times a second
      
     
       
      
    }

//////////////////////////////////////////////////////////////
// END OF Display Time after variable 'interval' has passed //
//////////////////////////////////////////////////////////////


}



//////////////////////////////////////////////////
// END OF Void Loop - All below are subroutines //
//////////////////////////////////////////////////






/////////////////////////////////////////////
// Parse Time Subroutine to sort time etc. //
/////////////////////////////////////////////

void parse_cmd(char *cmd, int cmdsize)
{
    uint8_t i;
    uint8_t reg_val;
    char buff[BUFF_MAX];
    struct ts t;

//////////////////////
// SET TIME SECTION //
//////////////////////
    if (cmd[0] == 84 && cmdsize == 16) {
        //T355720619112011
        t.sec = inp2toi(cmd, 1);
        t.min = inp2toi(cmd, 3);
        t.hour = inp2toi(cmd, 5);
        t.wday = inp2toi(cmd, 7);
        t.mday = inp2toi(cmd, 8);
        t.mon = inp2toi(cmd, 10);
        t.year = inp2toi(cmd, 12) * 100 + inp2toi(cmd, 14);
        DS3231_set(t);
//End of time setting section


    }
    
    
}

//////////////////////
// MONTH Subroutine //
//////////////////////

void printMonth(int month)
{
  switch(month)
  {
    case 1: lcd.print(" Jan ");break;
    case 2: lcd.print(" Feb ");break;
    case 3: lcd.print(" Mar ");break;
    case 4: lcd.print(" Apr ");break;
    case 5: lcd.print(" May ");break;
    case 6: lcd.print(" Jun ");break;
    case 7: lcd.print(" Jul ");break;
    case 8: lcd.print(" Aug ");break;
    case 9: lcd.print(" Sep ");break;
    case 10: lcd.print(" Oct ");break;
    case 11: lcd.print(" Nov ");break;
    case 12: lcd.print(" Dec ");break;
    default: lcd.print(" --- ");break;
  } 
}

//////////////////////////////
// STRIKING BELL Subroutine //
//////////////////////////////
        
void hourStrike()        
{
if (hourTest != hourValue) { //test if flag is not current time - ie. clock has not chimed yet, then will chime
 
strike=0;

  while(strike < hourValue) { //tests to see end of strike routine
    digitalWrite(13, HIGH); //Output for BELL RELAY *****
    lcd.noBacklight();
    tone(5,1865);// BUZZER AT SAME TIME AS STRIKE          
     delay(500); 
            digitalWrite(13, LOW);
            lcd.backlight();
           noTone(5);// Turn off buzzer
            delay(1500);
            strike++; //add one to strike flag
  }
}

hourTest = hourValue; //set the flag to last chimed hour
}

I have made striking clocks before.
Instead of using delay(1500) or whatever between the strikes, use the seconds.

If the time is 13:00:00 (exactly 1 pm), then strike.

If the time is 14:00:00 (exactly 2 pm), then strike.
If the time is 14:00:02 (2 seconds after 2 pm), then strike.

If the time is 15:00:00 (exactly 3 pm), then strike.
If the time is 15:00:02 (2 seconds after 3 pm), then strike.
If the time is 15:00:04 (4 seconds after 3 pm), then strike.

Et cetera.

No delays necessary!

I changed your “print time” section a bit, to add something you might like.

////////////////
// Print Time //
////////////////        
      
      
       //RTC is operated in 24-hour mode and conversion to 12-hour mode done here, in software
  if(t.hour == 0) {hourValue=12;} //Convert zero hour for 12-hour display
    else if(t.hour < 13) {hourValue=t.hour;} //Just copy hour if less than 13
        else if(t.hour >= 13) {hourValue=t.hour-12;} //If PM, convert 24 to 12 hour
       
      
      
        lcd.setCursor(0,0); //Move cursor to position 0, line 0 (upper line)
        if (hourValue < 10) { lcd.print("0"); } //adds 0 before hour if it is single-digit hour to show two digits
        lcd.print(hourValue); // Prints hour
        lcd.print(":");
        if(t.min<10) //adds 0 if it is single digit minute
        {
          lcd.print("0");
        }
        lcd.print(t.min); //prints minutes
        
        if (t.sec>0) {
          // if it is not the exact minute, then print the seconds
          lcd.print(":");
          if(t.sec<10) //adds 0 if it is single digit second
          {
            lcd.print("0");
          }
          lcd.print(t.sec); // prints seconds
        }
        else {
          // on the exact minute, print "am" or "pm"
          if (t.hour<12) lcd.print(" am");
          else lcd.print(" pm");
        }

Thank you Odometer for that.

What a good way to do the chiming - I really had not thought of that.

I will give that a try. Hopefully the cycles are fast enough that it doesn't chime with odd timing - I have seen that occasionally the display either changes the seconds late or misses a second altogether presumably because it is trying to do so much. Otherwise I might hear
ding . . ding . . ding . . ding . . ding . . . ding . ding . . ding etc!
I can do it with a bit of calculation, rather than hard-coding it, so I can have variables to change the daylight hours settings for summer and winter, so it only chimes during daylight hours.

About your second post - it is late and I can't get my head around it yet - looks like the am or pm will print and then be immediately over-written by seconds - am I right? I will have a look if I have time tomorrow.

Now the system is up and running and I don't really want to disturb it yet. I have bought another UNO today - I just need to buy another display and another RTC for experiments. I just need to think what to make next…

Goodnight

T.

Instead of having the "interval" be 1000 milliseconds, have it be much shorter (say, 50 milliseconds). In order to avoid having to update the display more than once per second, check whether the "new" seconds are the same as the "old" seconds. (You will need to store the "old" seconds in a variable.) Yes, you will be reading the time from the RTC much more frequently this way, but you will not skip any seconds.