Close
0%
0%

Audible notifications by a Grandfather's clock

Eight chimes can sound in different patterns whenever I receive a notification through IFTTT

Similar projects worth following

I fell in love with the sound of a grandfather's clock. But it was too expensive for me. And that clock has no alarm and snooze option. And it knows nothing about my emails or text messages.

So I bought the mechanism of a used grandfather's clock on. It has a simple structure: The chime rods tinkle, when the hammers hit, when the solenoids pull in, when the Raspberry Pi sets voltage on the GPIOs, when the IFTTT webpage calls the Raspberry Pi's web server, when I receive an important email.

The clock is working now, but it has some issues. So let's see what I have done already:

Special thanks to the Hatch (the makerspace of the Watertown Free Public Library) for the space, equipment, knowledge, and suggestions.

Standard Tesselated Geometry - 58.11 kB - 04/07/2016 at 04:59

Download

View all 6 components

  • Westminster Quarters

    borazslo04/23/2017 at 14:48 0 comments

    After I had changed from python to arduino, I added to the new arduino code the melodies of the Westminster Quarters.

    #define GIS D7
    #define FIS D6
    #define E D5
    #define B D2
    #define BIGBEN D1
    
    // https://en.wikipedia.org/wiki/Westminster_Quarters
    
    void WestminsterFirstQuarter(int speed = 900) {
      // 1 g♯4, f♯4, e4, b3
      dong(GIS);
      delay(speed);
      dong(FIS);
      delay(speed);
      dong(E);
      delay(speed);
      dong(B);
      delay(speed * 2);
    }
    
    void WestminsterHalfHour(int speed = 900) {
      // 2 e4, g♯4, f♯4, b3
      dong(E);
      delay(speed);
      dong(GIS);
      delay(speed);
      dong(FIS);
      delay(speed);
      dong(B);
      delay(speed * 2);
    
      // 3 e4, f♯4, g♯4, e4
      dong(E);
      delay(speed);
      dong(FIS);
      delay(speed);
      dong(GIS);
      delay(speed);
      dong(E);
      delay(speed * 2);
    
    }
    
    void WestminsterThirdQuarter(int speed = 900) {
      // 4 g♯4, e4, f♯4, b3
      dong(GIS);
      delay(speed);
      dong(E);
      delay(speed);
      dong(FIS);
      delay(speed);
      dong(B);
      delay(speed * 2);
    
      // 5 b3, f♯4, g♯4, e4
      dong(B);
      delay(speed);
      dong(FIS);
      delay(speed);
      dong(GIS);
      delay(speed);
      dong(E);
      delay(speed * 2);
    
      WestminsterFirstQuarter(speed);
    }
    
    void WestminsterFullHour(int hour = 0, int speed = 900) {
      WestminsterHalfHour();
    
      // 4 g♯4, e4, f♯4, b3
      dong(GIS);
      delay(speed);
      dong(E);
      delay(speed);
      dong(FIS);
      delay(speed);
      dong(B);
      delay(speed * 2);
    
      // 5 b3, f♯4, g♯4, e4
      dong(B);
      delay(speed);
      dong(FIS);
      delay(speed);
      dong(GIS);
      delay(speed);
      dong(E);
      delay(speed * 2);
    
      for (int i = 0; i < hour; i++) {
        dong(BIGBEN);
        delay(speed * 4);
      }
    }

  • From RaspberryPi (Python) to NodeMCU (Arduino)

    borazslo04/16/2017 at 18:36 0 comments

    After changing Balambér's system I also made the new version of this project. The new version of this clock works with NodeMcu ESP8266 V3 WIFI Internet Development Board.

    The Raspberry Pi worked well, but I prefer this smaller, more stable Arduino compatible wifi module. So the new hardware asks for new software. (I already know that I can use Python with NodeMCU, but I have no idea how. I need to make some research.)

    Hardware

    The circuit remained the same. But the five pins of the five MOSFET's has to be connected to the nodeMCU's following pins: D1, D2, D5, D6, D7. (We cannot use the following pins: D0 = USER ,D3 = FLASH, D4 = TXD1 . If we use them we cannot reset and/or update firmware without disconnecting the NodeMCU from the circuit.)

    Software

    The new software is written in the Arduino IDE. You can find the code here.
    • The serial_test code helps to debug the project. You can send command through the serial port. Like: "B1 T0.05" = Hit the FIRST Bell/Rod with 50 ms. The value of T is important, because adjusting that number you can make the hammer hits sound equally.
    • The webCmd is an ugly-written small library. That helps to build the mini-website for the project. (The serial_test does not need this library.)
    • The main file is the Audible_notifications_by_a_Grandfather_s_clock . Be aware of the 7th line where you can adjust the hit-time of the rods (BigBen, 1, 2, 3, 4).

    This software is in a very early stage. It cannot play melodies. Yet. I will work on it.

  • New mechanics - advise needed

    borazslo04/16/2017 at 13:47 0 comments

    I had to dismount the clock when I moved home to Europe. And when I rebuilt the project I encountered with a significant difficulty. The five solenoids needs significantly different power to move the hammers. It is because of the different home-made springs, and length of the lines, etc. (You can hear this difference even in the original video. ) This has became a huge problem.

    What can I do? Here are my possible solutions:

    a) Feed the solenoids much more power and with adjustable resistors I can adjust the power of the overpowered solenoids. (It needs new circuit board. And the solenoids are overpowered already.)

    b) Feed the solenoids much more power and I can adjust the power with the adjustment of hammer-time in the software (10-400 ms). (It needs more complex software design. But this adjustment has limited effect. This cannot resolve the entire problem.)

    c) Make new springs. (It is not easy to make springs with equal strength. And this alone is not a solution, even though it can help significantly.)

    d) Let's change the springs and use small weights like lead. (I do not have adjustable small weights. And does it really work?)

    What should I do? Do you have any suggestion?

  • Software Update

    borazslo05/18/2016 at 00:53 0 comments

    The recent Python code can be found in the Github repository. After the code v0.2, I cleaned out the code. I made a new mChimes.py file which is imported from all the other files. The main static settings are in this file as well. As a result all the other files became very short. Enjoy.

    chimeClock.py:

    import datetime, sys, zmq 
    import mChimes as chimes
    
    speed = 0.8
    
    firstQuarter  =  "1:%(speed)s-2:%(speed)s-3:%(speed)s-4:%(doubleSpeed)s" % {"speed": speed, "doubleSpeed": speed * 2}
    secondQuarter =  "3:%(speed)s-1:%(speed)s-2:%(speed)s-4:%(doubleSpeed)s-3:%(speed)s-2:%(speed)s-2:%(speed)s-3:%(doubleSpeed)s" % {"speed": speed, "doubleSpeed": speed * 2}
    thirdQuarter  =  "1:%(speed)s-3:%(speed)s-2:%(speed)s-4:%(doubleSpeed)s-4:%(speed)s-2:%(speed)s-1:%(speed)s-3:%(doubleSpeed)s-1:%(speed)s-2:%(speed)s-3:%(speed)s-4:%(doubleSpeed)s" % {"speed": speed, "doubleSpeed": speed * 2}
    fourthQuarter =  "3:%(speed)s-1:%(speed)s-2:%(speed)s-4:%(doubleSpeed)s-3:%(speed)s-2:%(speed)s-1:%(speed)s-3:%(doubleSpeed)s-1:%(speed)s-3:%(speed)s-2:%(speed)s-4:%(doubleSpeed)s-4:%(speed)s-2:%(speed)s-1:%(speed)s-3:%(doubleSpeed)s" % {"speed": speed, "doubleSpeed": speed * 2}
    Hour = "0:%(speed)s"% {"speed": speed * 4}
    
    now=datetime.datetime.now()
    if now.minute == 15:
        chimes.sound(firstQuarter)    
    elif now.minute == 30:
        chimes.sound(secondQuarter)
    elif now.minute == 45:
        chimes.sound(thirdQuarter)    
    elif now.minute == 0:
        if(now.hour > 12):
            hour = now.hour - 11
        elif(now.hour == 0 ):
            hour = 12
        else:
            hour = now.hour    
        chimes.sound(fourthQuarter+'-%s' % '-'.join(str(Hour) for x in xrange(1,hour)))

  • Reset Button

    borazslo05/14/2016 at 21:27 0 comments

    The RPi sometimes loses its internet-connection, and I need to reset to RPi in a safe way. So I added a small GPIO input push button:

    Some of the GPIO-s of the RPi has built in pull-up or pull-down, so I did not use external pull-up/down circuit. I put the button to HeaderPin 7 (GPIO 4) and HeaderPin 9 (GND) and I use a simple code with a special part for the GPIO.setup:

    import RPi.GPIO as GPIO
    import time
    
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(7, GPIO.IN, pull_up_down=GPIO.PUD_UP)
    
    while True:
        input_state = GPIO.input(7)
        if input_state == False:
            print('Button Pressed')
            time.sleep(0.2)
        
    
    Another advantage of this item that I can use it with both male and female head, and my GND pin remains accessible for other jumper wires.

  • How to fry your Pi

    borazslo04/22/2016 at 15:24 0 comments

    Have you seen my first Circuit? Well, I'm a beginner and I've made a very amateur mistake. Have you known that it was not a good idea to connect a GPIO out to the GND without any consumer? Well a MOSFET is not a consumer.

    I connected my GPIO to the MOSFET. I measured the running current, and it was 35mA. I thought it is not a lot and it should be fine. So I short (is this the right word?) my GPIOs even though for only a very short moment (0.01 sec) at a time. What has happened? I noticed nothing. It worked well, and the notifications system made beautiful sounds. But later I run into a problem: the Pi was not working as expected. There were more and more error messages. About corrupted files. I had to recreate my whole system.

    So shorting your GPIOs for a very short time does not necessarily kills the Pi, but destroys your system step by step. One easy solution is to put a 330 Ohm resistor between the MOSFET and the Pi.

  • Clock Add-On

    borazslo04/21/2016 at 01:03 0 comments

    This notification project can work as a normal grandfather's clock with famous Westminster Quarters melody. The easiest way is to use this Add-On file called chimeClock.py. You can invoke it regularly adding this line to your crontab (crontab -e):

    0,15,30,45 * * * * python /path/to/file/chimeClock.py
    Attention! This chimeClock.py works only if the chimeService.py is running, as it is written in the Program Code v0.2.

    chimeClock.py

    import datetime
    import sys, zmq 
    
    speed = 0.8
    
    #Mute it during night hours
    now=datetime.datetime.now()
    if ( ( now.hour > 22 ) and ( now.minute > 10 ) ) or ( now.hour < 9 ):
        sys.exit(0)
    
    port = "5555"
    
    #Westminster Quarters
    firstQuarter =  [1,speed,2,speed,3,speed,4,speed * 2]
    secondQuarter = [3,speed,1,speed,2,speed,4,speed * 2, 3,speed,2,speed,1,speed,3,speed * 2]
    thirdQuarter =  [1,speed,3,speed,2,speed,4,speed * 2, 4,speed,2,speed,1,speed,3,speed * 2, 1,speed,2,speed,3,speed,4,speed * 2]
    fourthQuarter = [3,speed,1,speed,2,speed,4,speed * 2, 3,speed,2,speed,1,speed,3,speed * 2, 1,speed,3,speed,2,speed,4,speed * 2, 4,speed,2,speed,1,speed,3,speed * 2]
    Hour = [0,speed * 4]
    
    context = zmq.Context()
    socket = context.socket(zmq.REQ)
    socket.connect ("tcp://localhost:%s" % port)
    
    def preparePattern( array ):
        pattern = ""
        for i in range(0, len(array), 2):
            pattern = pattern + str(array[i]) + ":" + str(array[i+1])
            if i < len(array) - 2 :
                pattern = pattern + "-"        
        return pattern
    
    def sound( array ):
        pattern = preparePattern(array)
        socket.send (pattern)  
    
    if now.minute == 15:
        sound(firstQuarter)    
    elif now.minute == 30:
        sound(secondQuarter)
    elif now.minute == 45:
        sound(thirdQuarter)    
    elif now.minute == 0: 
        sound(fourthQuarter)
        if(now.hour > 12):
            hour = now.hour - 11
        elif(now.hour == 0 ):
            hour = 12
        else:
            hour = now.hour
        for x in xrange(1,hour):
            sound(Hour)
            pass
    

  • Program Code v0.2

    borazslo04/20/2016 at 04:11 0 comments

    After the Program Code v0.1, I made a better but still simple code. There is no need for wsgi module anymore. But there is need of zmq module. See the new Preparing the Raspberry Pi.

    The new system works with three files. The chimeService.py runs continuously and it is waiting for calling on port 5555. The chimeClient.py can send a request for pattern to the service. This python file can be called from cron, from other scripts, etc. The chimes.php is the file on the webserver. It can call the chimeClient when that is needed.

    • You can send a pattern to make music. For example http://your_ip_address/chimes.php?pattern=1:0.5-1:0.5-4:1. That means: hit the rod 1, then wait 0.5 sec, then hit the rod 1, then wait 0.5 sec, then hit the rod 4, then wait 1 sec.
    • You can use the IFTTT's Maker channel to make a specific chime web request. So the chimes can be triggered when you receive in important email, or you wife comes home, or there is a new instagram photo, etc. etc.

    chimeService.py

    import sys, zmq
    import RPi.GPIO as GPIO
    import time, datetime, re
    
    port = "5555"
    GPIO.setmode(GPIO.BOARD)
    #Connect the RodNumbers to the GPIO board numbers.
    chimeRod2Pin = {0: 37, 1: 36, 2: 33, 3: 31, 4: 29}
    
    GPIO.setwarnings(False)
    for pin in chimeRod2Pin:
        GPIO.setup(chimeRod2Pin[pin], GPIO.OUT,initial=GPIO.LOW) 
    
    context = zmq.Context()
    socket = context.socket(zmq.REP)
    socket.bind("tcp://*:%s" % port)
    
    power = float
    
    def calculatePower():
        power = 0.06
        now = datetime.datetime.now()
        if ( now.hour > 21 ) or (now.hour < 9 ):
            power = 0.03
        else:
            power = 0.05
        return power
    
    def hitRod( number ):    
        GPIO.output(chimeRod2Pin[number], GPIO.HIGH)
        time.sleep(power)
        GPIO.output(chimeRod2Pin[number], GPIO.LOW)
    
    def hitPattern( pattern ):
        hit = map(str, pattern.split('-'))
        for i in range(0, len(hit)):
            if hit[i] != '':
                tmp = map(float, hit[i].split(':'))
                hitRod(tmp[0])
                time.sleep(tmp[1])
    
    def validatePattern( subject ):
        pattern = "^([0-4]{1}:\d{1}(\.\d{1,2}|)(-|$))+$"
        matchObj = re.match(pattern, subject)            
        if not matchObj:        
            return False
        else:
            return True
    
    while True:
        #  Wait for next request from client
        message = socket.recv()
        print "Received request: ", message,
        if not validatePattern(message):
            print "=> Invalid pattern!!"
            socket.send("Invalid pattern!")
        else:        
            power = calculatePower()
            hitPattern(message)
            print "=> Done."
            socket.send("Done.")
    

    chimeClient.py

    import sys, zmq
    
    port = "5555"
    
    context = zmq.Context()
    print "Connecting to server..."
    socket = context.socket(zmq.REQ)
    socket.connect ("tcp://localhost:%s" % port)
    
    if len(sys.argv) > 1:
        pattern = sys.argv[1]
        print "Sending pattern: ", pattern
        socket.send (pattern)  
        message = socket.recv()
        print "Received reply: ", message

    chimes.php

    <strong>Let's make some noise!</strong><br/><br/>
    Please send the request pattern in the url.<br/>
    See for example: <i>?pattern=4:0.5-3:0.5-2:0.5-1:0.5-0:1-0:1</i><br/><br/>
    
    <?php
    $pattern = $_REQUEST['pattern'];
    if(!validatePattern($pattern)) {
    	echo "<strong>Invalid pattern! Please see the example.</strong>";
    	exit;
    }
    
    echo "<strong>Current request:</strong> ".$pattern;
    
    /* Run the pattern without waiting for response */
    exec('python /path/to/file/chimeClient.py "'. $pattern .'" > /dev/null 2>/dev/null &');
    
    /* Run the pattern and wait for the response 
    exec('python /path/to/file/chimeClient.py "'. $pattern .'" ',$output,$return);
    echo "<pre>";
    foreach($output as $line)
    	echo $line."\n";
    echo "</pre>";
    */
    
    function validatePattern($pattern) {
    	if(preg_match('/^([0-4]{1}:\d{1}(\.\d{1,2}|)(-|$))+$/', $pattern))
    		return true;
    	else
    		return false;
    }
    ?> 

    Important notes:

    Development for the future:

    • The webpage could send the request with ajax so it could receive the answer.
    • Anybody...
    Read more »

  • Power Problems

    borazslo04/19/2016 at 04:46 3 comments

    Among the notes of the first version of the circuit I mentioned that still I am using two different power sources. I use a 5V/2A for the RPi3 and another 5V/5A for the solenoid circuit. Unfortunately the circuit consumes all the 5A. So if I connected the whole project to the 5V/5A source, the RPi would not have enough stable power. :(

    One solenoid needs 0.6A. So the step-up power converter needs to produce 0.6A (and 20V). For this efficiency the step-up converter consumes full 4.8A from the 5V/5A power adapter. Oh, and the step-up converter overheats fast, even though a solenoid works only for 0.1 sec / hit, so the consumption comes in strikes. (But what if I want to strike different rods in the same time?)

    I see two possible solutions with my very limited knowledge:

    • Should I change to capacitor from 1000 µF to something really big? With that I could buffer short notifications, but not longer ones. (1000 µF is enough for one strike.)
    • Or should I change the main 5V adapters? I could use a 18V or 20V AC DC adapter. So the step-up power converter could work much less (or it could be eliminated). Then the RPi would be powered through a simple LM7805 voltage regulator. Would this be safe enough for the RPi?

    Can you help me to find a solution, please.

  • Video

    borazslo04/18/2016 at 03:50 0 comments

    Would you like to listen how the clock works right now? Play it, it is only 40 sec.

View all 14 project logs

View all 5 instructions

Enjoy this project?

Share

Discussions

Ulysse wrote 04/14/2017 at 15:23 point

Excellent ! and so fun :-)

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates