Mar 222013
Circuit for 2 threaded callbacks and one wait interrupt

Multiple threaded callback interrupts in Python

We’ve been learning about interrupts this week because of the brand new interrupt capabilities of RPi.GPIO. We covered a simple “wait for” interrupt in part 1, threaded callback interrupt and button debouncing in part 2 and today we’re getting sophisticated with multiple threaded callbacks. “WoooooooooOOOOOOOOOOOOOOOooooooooooo”, I hear you say. ;)

Well actually, we’re not doing much that’s very different from last time, except, now there’s more of it. We’ll add another button and another threaded callback function the same as the first one (but on a different GPIO port). This is just to show that you can do multiple threaded callbacks in one program. After that, your imagination is the limit. (Well actually the number of GPIO ports is probably the limit.)

We’re just building on what we did before and this is exactly how programs are made. You do a bit at a time, test it, fix it, make sure it does what it ought to do, then go on to the next bit.

Here’s the Circuit

This circuit is a bit different from the previous one. The top two buttons connect port 17 and port 23 to GND when pressed. These are the two which trigger callbacks. The bottom button, connecting port 24 to 3V3 on button press is the “wait for” interrupt this time. So when you press button 3 it’s “game over”, but buttons 1 and 2 just report that they’ve been pressed until button 3 is eventually pressed.

Circuit for 2 threaded callbacks and one wait interrupt

We’ve used all the same building blocks we developed in parts 1 and 2, including button debouncing.

Do you need to update RPi.GPIO?

If you didn’t do it for the first or second examples, you will quite likely need to update your RPi.GPIO package. You can check what version of RPi.GPIO you have in the command line with…

sudo python
import RPi.GPIO as GPIO

This should show you what RPi.GPIO version you have. You need 0.5.1a or higher for this example.
You can exit the python environment with CTRL+D

Install RPi.GPIO version 0.5.2a for multiple threaded callback interrupts

If you need to, you can install 0.5.2a or later with
sudo apt-get update
sudo apt-get upgrade
(This will update all your Raspbian packages and may take up to an hour)

Update July 2014
The best way to get the latest RPi.GPIO (currently 0.5.5) is to flash a new SD card with the latest NOOBS or Raspbian. This will give you a clean start with the latest version of RPi.GPIO.

And now onto the code

I’ve put most of the explanations in the code, so that if you use it, you will still have them.

#!/usr/bin/env python2.7
# script by Alex Eames
import RPi.GPIO as GPIO

# GPIO 23 & 17 set up as inputs, pulled up to avoid false detection.
# Both ports are wired to connect to GND on button press.
# So we'll be setting up falling edge detection for both
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# GPIO 24 set up as an input, pulled down, connected to 3V3 on button press
GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

# now we'll define two threaded callback functions
# these will run in another thread when our events are detected
def my_callback(channel):
    print "falling edge detected on 17"

def my_callback2(channel):
    print "falling edge detected on 23"

print "Make sure you have a button connected so that when pressed"
print "it will connect GPIO port 23 (pin 16) to GND (pin 6)\n"
print "You will also need a second button connected so that when pressed"
print "it will connect GPIO port 24 (pin 18) to 3V3 (pin 1)\n"
print "You will also need a third button connected so that when pressed"
print "it will connect GPIO port 17 (pin 11) to GND (pin 14)"
raw_input("Press Enter when ready\n>")

# when a falling edge is detected on port 17, regardless of whatever 
# else is happening in the program, the function my_callback will be run
GPIO.add_event_detect(17, GPIO.FALLING, callback=my_callback, bouncetime=300)

# when a falling edge is detected on port 23, regardless of whatever 
# else is happening in the program, the function my_callback2 will be run
# 'bouncetime=300' includes the bounce control written into
GPIO.add_event_detect(23, GPIO.FALLING, callback=my_callback2, bouncetime=300)

    print "Waiting for rising edge on port 24"
    GPIO.wait_for_edge(24, GPIO.RISING)
    print "Rising edge detected on port 24. Here endeth the third lesson."

except KeyboardInterrupt:
    GPIO.cleanup()       # clean up GPIO on CTRL+C exit
GPIO.cleanup()           # clean up GPIO on normal exit


bouncetime=300 in lines 34 & 39 sets a time of 300 milliseconds during which time a second button press will be ignored. The code from example 2a has been incorporated into RPi.GPIO.

You can download this directly to your Pi using…

Then you can run it with…
sudo python

Can I switch off an event detection?

There’s just one more thing. If you want to stop an event detection on a particular port, you can use the following command…

You now know all you need to know about how to create and manage interrupts in RPi.GPIO in Python on the Raspberry Pi. The official documentation is here if you want to check it out.

Have fun with interrupts. I hope this mini-series has been useful. Let us know, in the comments below, what you’re planning to use it for and then come back and tell us how it went. :)

For extra credit, can you tell me why I chose those specific GPIO ports for these experiments?

RasPiO® Portsplus is really useful with RPi.GPIO

The RasPiO® Portsplus board labels the GPIO ports clearly for you so that you don't need to count pins. This helps you avoid wiring errors and damage. Click the photo for more information.

RasPiO Portsplus on Pi2

  140 Responses to “How to use interrupts with Python on the Raspberry Pi and RPi.GPIO – part 3”

  1. […] hope you are enjoying this series. Click here for Part 3. Share this:  Posted by alex at 8:00 am  Tagged with: interrupts in python with […]

  2. Thank you so much!

    I started fooling arround with the Raspberry Pi not long ago and that made me start learning Python. Your articles about interrupts were essencial.

    I made a little Python script that lights up a Green LED when I get new mail and a RED one when the temperature forecast to my city gets lower than 5ºC. But the forecast site free API only gives a limited number of updates a day. Because of that the temperature as well as the email are updated every 3 minutes.
    However, after reading this, I setted up a button that refreshes the e-mail status by pressing it using the callbacks.

    The next step will be making this information avaliable in a 16×2 LCD. (following one more of your posts :-D )

  3. This is a great tutorial; thanks for the example code as well. I am guessing that you have chosen the pins that you have due to them not having any special function, eg: UART, SPI etc.

    • Thanks John.

      Ref the port choices, that’s part of it, but not the whole story. :)

      • Mind sharing the details regarding your pin choices then, or should I stay tuned for a future tutorial :)

        As well, do we need to manually clear these interrupts under any circumstances, or is there a method to clear them that you are aware of? I ask because I am not sure if that may be my problem. I have written the attached code to allow an rotary incremental encoder (outputs 2 bit grey code changing with a change in angle) to control which stream in my mpd playlist is active, and the call backs are only triggered up until the if statement in the body of the while loop at the bottom is processed for the first time. If I cut the 2 If statements at the bottom and paste them into the call backs it works well. Any help would be greatly appreciated, I am still a bit green with Python and programming in general:

        import RPi.GPIO as GPIO
        import time
        import os



        GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.setup(16, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.setup(7, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

        counter = 5
        lastcounter = 5
        timestamp16 = time.time()
        timestamp18 = time.time()

        def callback16():
        global timestamp16
        global counter
        global lastcounter

        time_now = time.time()

        if (time_now – timestamp16) >= 0.1:
        current16 = GPIO.input(16)
        current18 = GPIO.input(18)

        if current16 == 0:
        if current18 == 0:
        counter = counter – 1
        counter = counter + 1
        if current18 == 0:
        counter = counter + 1
        counter = counter – 1

        print ‘callback 16’
        timestamp16 = time_now

        def callback18():
        global timestamp18
        global counter
        global lastcounter

        time_now = time.time()

        if (time_now – timestamp18) >= 0.1:

        current16 = GPIO.input(16)
        current18 = GPIO.input(18)

        if current18 == 0:
        if current16 == 0:
        counter = counter + 1
        counter = counter – 1
        if current16 == 0:
        counter = counter – 1
        counter = counter + 1

        print ‘callback 18’
        timestamp18 = time_now


        GPIO.add_event_detect(16, GPIO.BOTH, callback=callback16)
        GPIO.add_event_detect(18, GPIO.BOTH, callback=callback18)

        while GPIO.input(7) == 0:


        if counter > lastcounter + 4:
        os.system(‘mpc next’)
        lastcounter = counter

        if counter < lastcounter – 4:
        os.system('mpc prev')
        lastcounter = counter

        print str(GPIO.input(16)) + " " + str(GPIO.input(18))


        • Pin choices answer can be found in the comments for part 2 of this series. I’ll leave it there so as not to act as a spoiler in case anyone wants to have a guess. :)

          I’ll look at your code a little later as I have a headache right now. WordPress seems to have mangled it as well :(

          • Thanks for the offer to assist. I can upload the code somewhere if it’ll help you not have to rebuild the indentation.

          • You can zip it up and email it to me if you like. my_first_name @ mydomain ;) (a lot of email providers won’t pass .py files)

            I probably won’t be able to look until Tue/Wed though as I’m travelling tomorrow. I just might have a few minutes tomorrow am if things go well, or I feel like it on the plane :)

  4. Hi Alex, your description on how to use interrupts is very easy to understand. However I upgraded my gpio today to version 0.5.2a before I tried your lessons. Now lesson 2 and 3 have errors. When I change the sd card to one with version 0.5.1a it all works fine. My python skills are not that great yet, bit i’m learning. I had great fun getting a wii_mote controlling the robot used in the skutter project in the magpi.


    • I’m afraid it’s a known bug. Ben wrote to me about this at the weekend. 0.5.2a (a stands for alpha) has changed such that the callback functions will require channel number. Just add the channel number in your callback function definition and all should be good. I am going to fix the example code as soon as 0.5.2a goes into the Raspbian repo. So if you define a function for channel 25 it should look like this…
      def callback_25(25):

      I haven’t changed it yet because it will break the example for the previous versions.

      • Thanks Alex, I tried it but I keep getting an invalid syntax error on the (25): . I also tried the bouncetime function but can’t get past def callback_17(17):


        • I haven’t even had a chance to play with this version yet, so you’re ahead of me ;)

          In other news wiringpi2 for python just failed to install as well. I think I’m going to do something that actually works now. :-D

          • I’ve gone back to 0.5.1a now and wait until it’s in the repo.

          • It’s an awkward one because I will need to change the code, but don’t want to do it until the new version is the default (in the repo).

        • Try putting “self” instead of the channel number such as:

          def my_callback(self):

          This worked for me.

          • Yes using self instead of the channel number works for me too.

            I wonder if this is going to change in the near future? ?:-)

            I also notice that version 0.5.2a is now in the Raspbian repo. :yes:

          • Updated both this page and the previous examples and downloads to reflect the changes in RPi.GPIO from version 0.5.2a

            Basically you HAVE to include (channel) in your callback function definitions. Not channel number, but the variable called channel

            I didn’t want to do it until 0.5.2a was in the repo as it breaks the examples for previous version 0.5.1a.

            If you try to run the code from the old example (or the one in part 2) using 0.5.2, you will get this error when your callback function is called…

            TypeError: my_callback() takes no arguments (1 given)

          • Thanks Alex, I’m pleased to note that your new code is almost the same as what I did. I say almost as I have found that almost anything can be inserted between the () so long as it starts with a letter. You could use (raspberry) or even (raspi_tv) if you want and the two callbacks can even have different variables between the ().

          • That’s interesting, so any variable starting with a letter will do. :)

            Ben flagged up the changes to me about 10 days ago, but it seemed sensible to hold off updating until 0.5.2a was the “official” version. Didn’t want to get too many people stuck between versions, and didn’t want to have several different versions of these pages.

          • Alex I understand your dilemma, but this must be a bug and I’m sure that Ben will change it in the next release. Then you’ll have to change it again :silly:

          • We’ll see. Hopefully the code won’t be broken again. :-)

  5. […] is the third part of a series sharing about “interrupts” on the Raspberry Pi, from RasPi.TV: We’ve been learning about interrupts this week because of the brand new interrupt […]

  6. You chose GPIO18, GPIO23 and GPIO24 because of internal pull-up resistors. Am I right?

    • Good guess, but it’s not the whole story. :) It’s more to do with what’s next to each of those pins :)

      • They have interrupt vectors assigned at hardware level? I dare assume that not all pins have this capability.

        • They might do, but I wouldn’t have an idea. It’s simpler than that. ;)

          • Because those pins are not used by any of peripherals like UART, I2C, ADC? Or simply they are closer to the breadboard? :D

          • All good reasons, but not THE one. Big clue now. Look at the pins adjacent to the chosen ones and see if they have any useful properties. ;)

          • They have ground pins beside them, and therefore, if are configured to have internal pull-up resistors they can be connected to switches wired to simple 2 pin female connectors. ie: a switch (or button) between pin 9 and 11, one between 14 and 16, as well as 18 and 20.

          • There are those mysterious DNC pins (Do Not Connect). There must be some reason not to bother with them. May be some future functionality? Or may be my pinout chart is out of date already?

          • Two grounds and a 3V3 – just what we needed for this. The rest of it is as John says above. :) I didn’t use a breadboard for this, I used button switches connected to 2-way female connectors. Unfortunately the Fritzing Pi model won’t allow connections to the DNCs.

  7. Has anyone done any speed testing on this? I’ve been trying to work out whether I could turn a RPi into an ECU for my rather old car (bonkers I know). The stumbling block has been how to measure engine revs reasonably accurately.
    Getting one pulse per rev is pretty easy. Trigger an interrupt each time and log the timestamp. Subtract last timestamp from this timestamp and we have the time taken for 1 revolution but at 5000 RPM the pulses are arriving at 12 millisecond intervals.

    Am I being completely stupid or could Python on the Pi be fast enough?


    • It might be fast enough, but I doubt if Python would give you accurate enough timings for such a critical app. I think you’d be better off with a real-time microprocessor like ATMega or something else like it.

  8. Great tutorial! I wasn’t particularly fond of polling, so this was a handy find.

    I notice that you can call one function for both rising and falling edges using GPIO.BOTH, and you can call more than function using GPIO.add_event_callback, but can you call one function for rising and a different function for falling?

    When I try to do this I get an exception:
    RPi.GPIO.AddEventException: Edge detection already enabled for this GPIO channel.

    Is there a reason why this isn’t allowed?

    • You can do it on different channels. Surely there’s a workaround though? Do ‘both’ and the test for high or low in your callback function to determine what happens?

      Failing that, Ben’s the man to answer.

      • I had the same problem. I wanted to use this with two door sensors to detect if a chicken coop door is fully closed, fully open, or somewhere in between. For each sensor, I’d be able to tell if the door was arriving at that location or departing that location by the rising or falling edge.

        I tried your workaround… trigger the function on GPIO.BOTH and then do an if/else test in the callback function to see if the door was at that location (if it was, it had just arrived, if it was not, it had just left).

        Works great. Thanks for the tip! And thanks for the tutorials… I would not have gotten this working otherwise.

  9. Thank you for your great tutorial. it helped me alot for my little project.

  10. While the comments correctly indicate that port 24 is connected to 3v3, the diagram seems to indicate a connection to 5V. I don’t want anyone to smoke their RPi by mistake.

    • It doesn’t, but I can see how you could think that it did if you were using a small screen or something. I’ve found out how to “bend” wires in Fritzing now, so will try to avoid passing directly over ports in future.

  11. My RPi.GPIO package was not up to date so I followed your upgrade steps but every time I output my version it’s the old one. All updates run like they should without any errors. And in the logs it even shows the upgraded version.

    Preparing to replace python-rpi.gpio 0.5.2a-1 (using python-rpi.gpio_0.5.2a-1_mhf.deb) ...
    Unpacking replacement python-rpi.gpio ...
    Setting up python-rpi.gpio (0.5.2a-1) ...

    >>> import RPi.GPIO as GPIO

    Any idea what I’m missing or doing wrong?

    • It’s possible that if you’ve installed previous version of RPi.GPIO manually in the past, you might need to uninstall those before the newer version will “take”. Ben knows more about how to do this and is usually pretty quick to suggest how to test/do this in the Python section of the Pi forums.

  12. In your example about how to check the RPi.GPIO version, you show exiting with a ^Z.

    That puts the python task into the backround, without stopping it.

    root@mypi3:~/bin# python
    Python 2.7.3 (default, Jan 13 2013, 11:20:46)
    [GCC 4.6.3] on linux2
    Type “help”, “copyright”, “credits” or “license” for more information.
    >>> import RPi.GPIO as GPIO
    >>> (typed a ^Z)
    [1]+ Stopped python

    Checking the backround jobs:

    root@mypi3:~/bin# bg
    [1]+ python &

    Bringing python back to the foreground:

    root@mypi3:~/bin# fg 1

    Really exit and stop python

    >>> exit()

    This will avoid having extra python jobs running in the backround.

    • Actually someone already mentioned this a while back (may have been on another page) and said that CTRL-D is better (EOF).

      Thanks for bringing it up. I’ll change it.

  13. Hi,
    thank you for your great tutorial. it helped me a lot.
    I am trying to create a project for a museum: pi to stream video to a screen.
    We must select a movie from a console with 8 pushbuttons.

    The console is connected to the GPIO port 4 and 18 for testing, but my script does not quite work.
    os is “RASPBIAN”, so the versions are:
    python: 2.7.3
    RPi.GPIO: 0.5.2a

    the script:
    #!/usr/bin/env python2.7

    import RPi.GPIO as GPIO
    import os
    from time import sleep

    def ports_def():

    GPIO.setup(4, GPIO.IN) # button 1
    GPIO.setup(18, GPIO.IN) # button 5
    GPIO.setup(4, GPIO.IN, pull_up_down=GPIO.PUD_UP) # button 1
    GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP) # button 5
    GPIO.add_event_detect(4, GPIO.FALLING, callback=my_callback1, bouncetime=300)
    GPIO.add_event_detect(18, GPIO.FALLING, callback=my_callback2, bouncetime=300)

    def my_callback1(channel):
    os.system(“pkill omxplayer”)
    os.system(‘omxplayer ../videos/50001_court.mp4’)

    def my_callback2(channel):
    os.system(“pkill omxplayer”)
    os.system(‘omxplayer ../videos/coin_court.mp4’)

    def balise1():
    while 1:

    print “la forge”





    the problem is the following:
    video process can sometimes exist in duplicate:

    root 1890 1 0 05:51 tty1 00:00:00 /bin/login —
    pi 1975 1890 0 05:53 tty1 00:00:01 -bash
    root 1989 1975 0 05:53 tty1 00:00:00 sudo ./
    root 1990 1989 94 05:53 tty1 00:03:34 python2.7 ./
    root 2141 1 15 05:57 tty1 00:00:00 /usr/bin/omxplayer.bin ../videos/coin_court.mp4
    root 2142 1990 0 05:57 tty1 00:00:00 sh -c omxplayer ../videos/coin_court.mp4
    root 2143 2142 0 05:57 tty1 00:00:00 /bin/bash /usr/bin/omxplayer ../videos/coin_court.mp4
    root 2144 2143 15 05:57 tty1 00:00:00 /usr/bin/omxplayer.bin ../videos/coin_court.mp4
    pi 2160 2003 0 05:57 tty2 00:00:00 grep –color=auto tty1

    and after a while it can happen that you press a button does not detect…

    Thanks for your help.

    • Maybe a brief sleep() between killing omxplayer and starting the next instance may help things?
      Have you tried updating to the lastest RPi.GPIO ? (simply “sudo apt-get update && sudo apt-get upgrade”)
      Have you tried experimenting with different bouncetimes?
      Putting in some debug prints (i.e. each time you enter one of the callback functions) might be useful ;)
      Presumably you’ve got the buttons wired to ground?

      • Thank you for your reply.
        The buttons are wired to ground.
        I’ll test your ideas and give you the results.

        This other script works very well, but does not allow interruptions:

        #!/usr/bin/env python2.7

        import RPi.GPIO as GPIO
        import os
        from time import sleep

        # Ce programme est la propriete de l association La Forge

        def ports_def():

        GPIO.setup(4, GPIO.IN) # button 1
        GPIO.setup(18, GPIO.IN) # button 5

        def balise1():

        os.system(‘setterm -blank force’)

        while True:

        if ( GPIO.input(4) == False ):

        os.system(‘omxplayer ../videos/50001_court.mp4’)

        if ( GPIO.input(18) == False ):

        os.system(‘omxplayer ../videos/coin_court.mp4’)






        • It’s almost certainly a debounce issue then (or a bug if you have an earlier RPi.GPIO). Trying Andrew’s suggestions is the best way forward :)

      • I made several tests:
        I inserted a brief sleep() between killing omxplayer and starting the next instance, it was a little better, processes were not always two copies.
        I experienced different bouncetimes, without success.
        I would like to update the unit RPi.GPIO as you both recommend. The problem is that the Raspberry PI I use is the Model A, without ethernet jack for access to the Internet. So i can’t type “sudo apt-get update…”

  14. thank you for your help, I’ll try as soon as possible. Cordially. CM

  15. I’ve connected the INTA pin of a MCP23017 to the GPIO 23 via a 3.33V voltage divider to be able to use the interrupts generated by the MCP23017. However, the software debounce parameter doesn’t seem to work for me. When i include bouncetime=300 in the GPIO.add_event_detect the callback fires just once and then never again. any ideas why? Also, is there a workaround? Right now I do a sleep(0.3) in the callback, but I’d really like to go without any sleeps in my code…


  16. hello,
    I could load the package on a linux ubuntu pc and tranfer it to the sd card PI, the version is: RPi.GPIO > v 0.5.3a.
    The installation “dpkg -i package-name” was easy on PI.

    After many tests, no changes to the 0.5.2a version of … there are the same problems.

    I saw that there was a dedicated package in real time:
    i’ll try and inform you of the results.

  17. Here is the latest version of the program based on RPIO. After much testing, rather it seems to work. 8 GPIO inputs are each connected to a push button. At rest, the polarity is +3.3 v PI through resistance 10Kohms. When pushing the pin is grounded.

    #!/usr/bin/env python2.7

    # Association la forge Saint Martin La Plaine
    # station video basee sur PI Raspberry type A
    # Choix video sur pupitre equipe de 8 boutons poussoir

    import RPIO
    import os
    from time import sleep

    def ports_def():

    RPIO.setup(4, RPIO.IN)
    RPIO.setup(17, RPIO.IN)
    RPIO.setup(7, RPIO.IN)
    RPIO.setup(22, RPIO.IN)
    RPIO.setup(18, RPIO.IN)
    RPIO.setup(23, RPIO.IN)
    RPIO.setup(24, RPIO.IN)
    RPIO.setup(25, RPIO.IN)
    RPIO.add_interrupt_callback(4, my_callback1, edge=’falling’, debounce_timeout_ms=800)
    RPIO.add_interrupt_callback(17, my_callback2, edge=’falling’, debounce_timeout_ms=800)
    RPIO.add_interrupt_callback(7, my_callback3, edge=’falling’, debounce_timeout_ms=800)
    RPIO.add_interrupt_callback(22, my_callback4, edge=’falling’, debounce_timeout_ms=800)
    RPIO.add_interrupt_callback(18, my_callback5, edge=’falling’, debounce_timeout_ms=800)
    RPIO.add_interrupt_callback(23, my_callback6, edge=’falling’, debounce_timeout_ms=800)
    RPIO.add_interrupt_callback(24, my_callback7, edge=’falling’, debounce_timeout_ms=800)
    RPIO.add_interrupt_callback(25, my_callback8, edge=’falling’, debounce_timeout_ms=800)

    def my_callback1(gpio_id, val):
    # fonction appel film1 bouton 1
    os.system(“pkill omxplayer”)
    os.system(‘omxplayer ../videos/film1.mp4’)

    def my_callback2(gpio_id, val):
    # fonction appel film2 bouton 2
    os.system(“pkill omxplayer”)
    os.system(‘omxplayer ../videos/film2.mp4’)

    def my_callback3(gpio_id, val):
    # fonction appel film3
    os.system(“pkill omxplayer”)
    os.system(‘omxplayer ../videos/film3.mp4’)

    def my_callback4(gpio_id, val):
    # fonction appel film4
    os.system(“pkill omxplayer”)
    os.system(‘omxplayer ../videos/film4.mp4’)

    def my_callback5(gpio_id, val):
    # fonction appel film5
    os.system(“pkill omxplayer”)
    os.system(‘omxplayer ../videos/film5.mp4’)

    def my_callback6(gpio_id, val):
    # fonction appel film6
    os.system(“pkill omxplayer”)
    os.system(‘omxplayer ../videos/film6.mp4’)

    def my_callback7(gpio_id, val):
    # fonction appel film7
    os.system(“pkill omxplayer”)
    os.system(‘omxplayer ../videos/film7.mp4’)

    def my_callback8(gpio_id, val):
    # fonction appel film8
    os.system(“pkill omxplayer”)
    os.system(‘omxplayer ../videos/film8.mp4′)

    def main():

    while 1:

    # print “la forge”
    processname =’omxplayer’
    tmp = os.popen(“ps -Af”).read()
    proccount = tmp.count(processname)

    if proccount == 0:
    os.system(‘omxplayer ../videos/boucle.mp4’)




    Thanks to Alex and AndrewS…

  18. thank you for the example

    I have a question, is this is a SW or a HW interrupt ? I mean is the code checking for the button to be pushed ?

  19. Dear Alex
    Many thanks for your excellent tutorials. It is generous of you to devote so much time to helping others.
    I have been following your interrupt tutorials to help me detect rainfall using a tilting bucket rain gauge, and can print a message to screen saying “rain event detected”. I am however, unable, to return a value to accumulate total rainfall. I am sure that this is a very naive question, as I have yet to really get to grips with Python, but any help would be much appreciated.
    Best wishes

    • How are you measuring total rainfall? Have you got some kind of sensor?

      • Hi Alex

        I am using a spare Maplin rain gauge, which is a tilting bucket device that see-saws whenever 0.3 mm rain has fallen, when a small attached magnet briefly operates a reed switch. I have connected to the switch GPIO7, which is normally pulled high but earths briefly whenever the bucket tilts.

        This is all part of a weather-data-logger-RPi setup, still in bread-board phase. So, at the same time as rain, I am recording various temperatures and insolation. The program reads the sensors every 60 sec and determines means over a period defined by the raw-input value named ; I generally choose 15 min.

        Attached is a bit of the (probably badly written) program, with much irrelevant stuff deleted but its position indicated by comments.

        At the moment the function works and prints a message, so all is OK there. But, what I really want to do is add a command like:


        whenever the bucket tilts

        and print to to file after every averaging period of length .

        Any help you can give will be greatly appreciated.

        Best wishes, Harry

        Excerpt from logging program:

        # reading T & RH from DHT22 sensor, plus T frpm TMP36, thermistors and DS18B20; also, light from NORPS
        # version testing rain-guge (still indoors)
        # modified after A Eames & Adafruit tutorials

        import os
        import glob
        import subprocess
        import datetime
        import gspread
        import re
        import sys
        import time
        import datetime
        import time
        import RPi.GPIO as GPIO
        from math import exp,log
        from time import strftime

        os.system(‘modprobe w1-gpio’)
        os.system(‘modprobe w1-therm’)

        print ‘\nHi Harry, \nI need to know something … \n’
        interval=raw_input(“Input time between means in minutes: “)

        # …………………………………….
        # commands setting up GPIO channels for temp etc go in here
        # ditto setting accumulators to 0.0


        # prepare o/p file …………………..

        fo.write(“Date, Time, Drift, Temp1, Temp2, Temp3gmin, Temp4soil, Light mV, Thstor1mV, Thstor2mV, Tmp_DHT22, RH_DHT22, T_f8soil, T_0cgmin,rain,\n”)

        # —————————————————————————-
        # now set up thread for detecting rain
        def raindetected(channel):
        print “rain bucket tilted”

        GPIO.setup(7, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.add_event_detect(7, GPIO.FALLING, callback=raindetected, bouncetime=300) # raingauge on pin 7

        # end of rain detection

        # ========================Data Logging Starts ===============================
        # measures light & temp every minute, and calculates means over interval input
        # ===================================================================

        while True:
        print ‘start of big loop’
        # time2 sets no of secs between each cycle of readings i.e. +60 gives 1 min

        # commands reading light & temp in here
        # ………………………………………..

        datstr=(obstime+’, ‘+driftm+’, ‘+Tm[1]+’, ‘+Tm[2]+’, ‘+Tm[3]+’, ‘+VTh4m+’, ‘+VLm+’, ‘+VTh1m+’, ‘+VTh2m +’, ‘+temp +’, ‘+humidity+’, ‘+T_f8m +’, ‘+T_0cm+’, ‘+T_14m+’, ‘+raintotal + ‘\n’)
        print “means”
        print datstr
        print “****************************”
        # end of writing to file

        # reset accumulators etc

        # ……………….
        # irrelevant lines deleted
        # ………………..


        #print ‘go to start of while true loop’

        except KeyboardInterrupt:

        • You can put the

          in your callback function as long as you make raintotal a global variable in your function it will be remembered.

          global raintotal

          As for writing it to a file. Have a look at the code for the battery logging script. It’s on here somewhere.

          I’m deliberately not spoon-feeding you here. Tough love and all that :)

          • Thank you very much. I have tried it and it works! I’ve also managed to write to file and to a Google sheet. Now to fix a long cable to the gauge and put it outside and wait for rain.
            I am very grateful for your kind help.
            Best wishes

          • Excellent :)

            For extra credit, have a google about “scope of variables in Python functions” and you’ll hopefully find something to show you what you’ve just done with the “global” part. (Although I don’t know, maybe you know other languages already?)

            A lot of people frown on the use of global, but it has its uses.

  20. Do u know how to install RPIO?
    import RPIO

  21. Hi There

    I can seem to get sudo python myCode to see the add_event_detect module. when I run sudo python3 myCode I can.

    I have python3-rpi.gpio version 0.5.3 and python-rpi.gpio 0.5.3 installed.

    Does python-rpi.gpio not have the interrupt methods?



    • What is myCode? If you’re talking about any of the scripts I’ve written, they’re all for python 2.7

      • Hi Alex, when I say my code I mean my script and in it has the line add_event_detect module(). The script works fine when I run it using python 3 but not when I use python 2.7

        I get:

        GPIO.add_event_detect(4, GPIO.BOTH, callback=my_callback_A, bouncetime=0)
        AttributeError: ‘module’ object has no attribute ‘add_event_detect’

        When I query the RPi.GPIO module contents I get:

        >>> import RPi.GPIO as GPIO
        >>> dir(GPIO)
        [‘ALT0’, ‘BCM’, ‘BOARD’, ‘HIGH’, ‘IN’, ‘InvalidChannelException’, ‘InvalidDirectionException’, ‘InvalidModeException’, ‘InvalidPullException’, ‘LOW’, ‘ModeNotSetException’, ‘OUT’, ‘PUD_DOWN’, ‘PUD_OFF’, ‘PUD_UP’, ‘RPI_REVISION’, ‘SetupException’, ‘VERSION’, ‘WrongDirectionException’, ‘__doc__’, ‘__file__’, ‘__name__’, ‘__package__’, ‘cleanup’, ‘event_detected’, ‘gpio_function’, ‘input’, ‘output’, ‘set_falling_event’, ‘set_high_event’, ‘set_low_event’, ‘set_rising_event’, ‘setmode’, ‘setup’, ‘setwarnings’]

        The add_event_detect module part is not there but it is there when I run the same using python 3. Not sure how to fix this.



        • Ok, I see the RPI.GPIO version is 0.4.1 when I call GPIO.VERSION. I have the newer one installed though. How do I make python2.7 use the RPi.GPIO version I want?



          • Looks like you may have installed RPi.GPIO manually in the past. There is a way to get rid of it, but I’m not sure how.

            But if your Raspbian distro is that old, why not just back up your work and flash a new card and have everything bang up-to-date?

          • I guess I could do that but it’s a kind of shitty solution…..I may have to resort to it though!


          • I agree, in principle, but the improvements in Raspbian have been so great, it is worth updating frequently.

            There’s a thread in the Python section of the Raspberry Pi forums where your exact issue with old versions has been discussed before and Ben Croston showed how to get rid of it. You could try looking there. I don’t know exactly what the thread is called, but I saw it the other day. Have a look and see if you can find it, if you don’t want to update your OS. :)

            Found it here. Pasting command here for future reference…
            sudo rm -rf /usr/local/lib/python2.7/dist-packages/RPi.GPIO-0.1.0*
            obviously need to change it for the appropriate version number :)

          • I sorted this issue by deleting the following:

            sudo rm -r /usr/local/lib/python2.7/dist-packages/RPi.GPIO-0.4.1a-py2.7-linux-armv6l.egg

            python 2.7 now sees the correct version of RPi.GPIO………next problem…..

            GPIO.add_event_detect(17, GPIO.BOTH, callback=my_callback_B, bouncetime=0)
            ValueError: The channel sent is invalid on a Raspberry Pi

            DAMN U!!

  22. If the event callbacks are essentially the same function (differing only in data values) as in the example, you can use a higher-order function to define the callback (let’s see if the blog preserves indentation with <code> tags): (Ed. it doesn’t, but I edited it to use the same plugin I used above)

    def make_callback(port, msg=''):
        def callback(channel):
            print("falling edge detected on {0}; {1}".format(port, msg))
        return callback
    GPIO.add_event_detect(17, GPIO.FALLING, callback=make_callback(17, "it's crackers"), bouncetime=300) 
    GPIO.add_event_detect(23, GPIO.FALLING, callback=make_callback(23, "slip a rozzer"), bouncetime=300)
  23. I’ve been following your tutorials….but I’ve some doubts….can I give the dtmf coded bits to the raspberry pi directly…..could you please give an explanation on this as well as the code…please code it as soon as possible

  24. Hello I require some assistance :D

    The tutorial was great and I learned a lot, but my code does not want to work.

    Here is the code:

    import RPi.GPIO as gpio

    pulse = 0

    def turnEvent():
    pulse = pulse + 1

    def main():
    gpio.setup(23, gpio.IN)

    gpio.add_event_detect(23, gpio.BOTH)
    gpio.add_event_callback(23, turnEvent, 100)

    The error is:
    gpio.add_event_detect(23, gpio.BOTH)
    RuntimeError: Edge detection already enabled for this GPIO channel.

    All I want it to do is display a count for how many turns the rotary encoder goes through. I’m just keeping track of the distance of my motors.

    I will appreciate any help!!

    • I forgot to mention that I am using a rotary encoder to do this. The only change in edge I’m concerned about is with Pin A as it does not matter to me by which direction the encoder is turning.

  25. Hi, I need to add an event you want to detect the corresponding GPIO pin RXD of UART raspberry . The problem is that if I set the expectation of an event on the pins thereof, for each character received in the serial buffer will cause a call to the callback . Instead I want to make sure that once you start a sequence of characters , no longer detects the break even until it is over the sequence of characters that interest me . Maybe until I finish a readline from serial. And ‘ possible to do such a thing ? I had thought about doing a callback when any front was detected on RXD remove_event_detect made ​​on RXD and then started with the readline , only once did remove_event_detect readline does not work anymore . Any suggestions?

    • I wasn’t even aware you could set up an interrupt on a port which was being used for one of its alternative functions
      This might sound silly, but perhaps you’d actually be better off with polling for this application?

      • Hi, thanks for your response . Sorry I was not clear , in order to add add_event_detect on RXD , I first have to set it as INPUT and then I could do it, so I lost the functionality of ALT0 and therefore no longer worked readline . Unfortunately for the application that I need I can not do polling.

        • Could you cheat? Connect a different port to the RXD port and use an interrupt on that port?

          e.g. connect a wire from GPIO 25 to RXD, but leave RXD as ALT0. Then use 25 as an input with an interrupt?

          • Thanks again for the help, say virtually connect RXD as well as the device with which it must communicate to the pin 25 , so that I can set the GPIO 25 as input and put the event on that detect GPIO , and maybe nell’handler disable it until you ‘ve finished reading it ?
            Sorry if it annoys , but it is a problem that I carry on for a few days and wanted to be precise .
            Basically I should do
            Import RPi.GPIO as GPIO


            GPIO.setmode ( GPIO.BOARD )
            GPIO.setup (25, GPIO.IN )
            GPIO.add_event_detect (25, GPIO.RISING , handler )

            and nell’handler

            GPIO.remove_event_detect ( 25 )

            # Other operations
            # And the end
            GPIO.add_event_detect ( 25 ….. )

  26. Hey alex! this tutorial helped me a lot, but i’m having a problem in generating an interrupt with a timer. I wanted to create a program that could go to an interrupt routine when an a predetermined amount of time had passed. In other words, the raspberry pi should be running through its normal routine, and whenever an interval of time has passed (let’s say a second) it would go the interrupt routine. Can you help me?

  27. Hi Alex,
    Let me say thx for the useful posts at first.
    Anyway I had an idea to detect GPIO state changes even if the port is an output. Unfortunately the RPIO module drops an error, if I “miss” to setup the channel as an input…
    My plan is to have a python based Server Side Event source, so if my Web-based controller is opened in more than one browser, all the clients will alerted if a GPIO output is changed.
    In Your opinion, is it possible to do it only with software, or maybe I will have to connect parallel all my used outputs to an other GPIO input? That would be a solution, but it would be nice not to use twice more ports…
    Thx everyone in advance.

    • I don’t know about RPIO because I’ve never used it, but with RPi.GPIO you can check the status of an output in the same way you do an input, just by reading it as if it were an input without changing the port to input mode.

      • That is okay, but I cannot monitor an output change like an interrupt.
        I am confused now, I believed RPi.GPIO is the same as RPIO, so I meant RPi.GPIO…
        So if, with software I have to check the state with a small interval and put a data line to the SSE source, if a change is detected, but it is not as beautiful as an interrupt is. In this case I rather connect my outputs back to other GPIOs defined as inputs, because this way interrupts should work fine.

  28. Hi Alex,
    I enjoyed and learnt a lot from Your articles. I wanted to use interrupt in my script, and it works only in IDLE(3) but not in terminal. How can it be??? Is there any difference in the GPIO drivers. I send my problem in a forum, but I could not get an answer.

  29. These are great tutorials, thanks for the time you put into them! I’m using the LCD Pi plate from Adafruit ( which has five buttons on it. It uses the I2C pins instead of a plain old GPIO pin. How would I go about using python events to detect button presses from the plate so I don’t have to use while True: loops? Any input would be much appreciated!

    • Unless the buttons are wired directly to GPIO pins, you can’t use RPi.GPIO.

      I’m not aware of any i2c interrupt package, but there might be something you could use. We just don’t know. Sorry. :(

      • Hi
        I note that the Adafruit LCD plate uses the mcp23017 i2c bus expander chip which has on-chip interrupts.

        If you have a look at the code posted by AB Electronics on Github for their IOPi add-on you will see that they use the same chip(s) and have already developed the code you need.

        I modified this code to use a mcp2008 (same chip family but 8 i/o instead of 16) for the push-button inputs in my project, then made a simple wired connection (via a safety series resistor just in case the GPIO pin inadvertently became an output) between the INT output pin of the mcp2008 chip to a Pi GPIO pin.

        Then you CAN use the RPi.GPIO code explained on this web site to register the interrupt and respond accordingly – very many thanks for that Alex, I could not have solved my problem as to how to do this without your excellent tutorials (I am a Raspbian novice!).

        One important word of warning:

        The mcp2008 chip in my setup runs at 3.3v same as the Pi so no interface problems.
        I recollect that the mcp23017 is designed to run on 5v please check this out for the LCD Plate. If so you will need to ensure that you do not apply the full 5volts to a Pi GPIO input pin as this would likely cause damage to the Pi. i.e. you will need to buffer this, although a simple resistor divider might do it you could also have a look at the circuit of the AB module to see how they did it (more complex).

        I have had this set up working for several months now whilst continuing to developing other code for my project (I am using a Custard Pi 7 development board with a 4 line LCD plus RTC module to completely replace a swimming pool salt chlorination control unit which also requires analogue inputs).

        I hope you find this useful.

  30. Hi Alex

    Very pleased to see this article on Interrupts and all the discussion as I have recently bought a Raspberry Pi for a specific project, however some comments here and from other sources possibly indicate the Pi may not be the ideal solution and I should have instead gone for an Arduino. So I would welcome your views and those of anyone else who may be able to advise.

    Currently I have a vintage 286 pc running a computer controlled Scalextric system. This provides the means for a person to race against a car controlled by the pc; these are now called pace car systems and commercially available. So the car being controlled by the pc (pace car) is constantly monitored and its speed varied as required, while the challenger car is just lap counted.

    To achieve this I used Interrupts on the pc because it is vital that no signals are lost/missed. The pace car trips 29 sensors within the time of about 6 seconds per lap, the challenger car just one sensor per lap. Inevitably there are occasions when both the pace car and challenger car hit sensors at the same time, however as I understand it the arrangement within the 286 processor is that when one or more interrupts occur these are handled in order of priority and so the pace car is interrupt 3 and the challenger car is 4. The system works faultlessly, however one day the 286 pc is going to die and so I am looking for a modern replacement.

    With the huge improvement’s in technology since the days of 286 pc’s I would be surprised if the Pi is not up to the job, however that is the essence of my question; can the Pi provide me with two Interrupts that may be operated simultaneously without missing any events. This I imagine is going to come down to how the processor manages Interrupts’; do they latch until cleared and is there any internal order of priority?

    I would very much welcome your input; should I have bought an Arduino? If you would like to see a brief introduction and demonstration of the current system, have a look here:

  31. hi,
    Actually i wanted to find frequency through this falling edges can you please help me….

  32. Hello, thanks for this great post. I’ve been trying to make your sample code work for many hours, but nothing ever happens. I run the following code with sudo python, and I’ve simplified the code to this:

    import RPi.GPIO as GPIO
    GPIO.setup(0, GPIO.IN, pull_up_down=GPIO.PUD_UP)
    GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    def my_callback(channel):
        print "falling edge detected on 0"
    GPIO.add_event_detect(0, GPIO.FALLING, callback=my_callback)
        print "Waiting for rising edge on port 24"
        GPIO.wait_for_edge(24, GPIO.RISING)
        print "Rising edge detected on port 24. Here endeth the third lesson."
    except KeyboardInterrupt:
        GPIO.cleanup()       # clean up GPIO on CTRL+C exit
    GPIO.cleanup()           # clean up GPIO on normal exit

    My python version iz 2.7.3, my gpio version is 0.5.11. I’ve updated my packages with apt-get update and apt-get upgrade, I’ve checked every function on the documentation website to make sure they’re correct. There are no compile or run errors.
    I have a wire running from gpio 0 set to IN and UP, and I put this wire into the ground pin on the pi. I’ve checked with a different SSH session that the readout really does change from 1 to 0, yet nothing prints. Same deal with gpio 24 and sticking the wire in the 3,3 or 5V pins on the pi.

    I’ve spent many hours trying to debug this, but it’s such a simple program I don’t know what to do. I’m just gonna stick with polling for my project…

    • Why are you using GPIO 0? I think that’s one of the two out of 40 pins you’re not supposed to use. Try a different port e.g. GPIO25

      What kind of Pi are you using?

      It’s likely a wiring error.

      • Somehow, somewhere I finally realized that I was using the wrong numbering scheme. I finally linked the letters “BCM” from gpio readall shell command to the bloody GPIO.setmode(GPIO.BCM) line of code.
        Of course, I have thought of numbering schemes before, I just never used anything other than physical and wiringpi before.

        Get this: a minute after figuring out my mistake, my pi somehow broke down. I can’t ssh through my wireless module, yet the module works on windows. I need to connect through the wired connection.

        Someone does not want me to finish my project.

    • Connecting a GPIO pin directly to 5V is very inadvisable, and could (permanently) damage that particular GPIO.

  33. Dear Alex and readers,
    I have learned a ton from your post, but now I really need help. Thanks to you I have built my first python program, but it’s behaving very strangely.

    Long story short: threaded callback method somehow kills main method, and the program ends. The problem is 1 completely simple and CORRECTLY executed time.strft() command.

    It might be that many people reading your post will also stumble upon this same problem and get discouraged. Do you have any idea why this might be happening? I described the program here:

  34. Interesting tutorial.

    Is it possible to alter the code to call an interrupt only when two buttons are both pressed? I.e. run a callback thread when two inputs are activated.


    • Would it just be a case of, for example, adding ‘AND 23’ to the following line of code…

      GPIO.add_event_detect(17 AND 23, GPIO.FALLING, callback=my_callback, bouncetime=300)

      … to run the callback when both inputs 17 and 23 are activated?


      • I don’t think that’s possible. Rather than trying to do it “in software” could you instead do it “in hardware” i.e. connect the two buttons in series, so that the circuit only completes when both buttons are pressed?

        • Yes. I think that’s the answer.

          • Thanks Andrew and Alex.

            I am trying to design a custom keyboard using the Raspberry Pi as the controller. I need 55 buttons so was trying to work out a way of using an 5×11 x/y type matrix of inputs, where a combination of two input pins would represent each button. I was hoping to be able to get the rapsberry pi to then interrupt when a single button is pushed, discriminating between each button by the different combination of input pins, i.e. the pin at the top left would be represented by two inputs X1 and Y1, the next button along by X1 and Y2.

            As such using the series switch approach would not give me the level of discrimination required to distinguish between 55 buttons.

            Another thought I have had would be to have a callback/interrupt for each of the 11 y-axis inputs, and then have the callback poll each of the 5 x-axis inputs to identify the associated input pin that has been activated and therefore which of the 55 buttons has been pressed.

            Do you think is approach would be effective?

            Many thanks again for your help, I really appreciate it.

          • Ahhhh.

            Keyboard matrices don’t work the way they think you do – in your example of joining together input pins X1 and Y1, neither pin would get an interrupt, as both pins are operating in input mode, and so there’s nothing driving them high or low.
            AFAIK keyboard matrices always work by actively driving each row high (or low) one at a time, and seeing which columns then also get pulled high (or low) at the same time.
   should find you some useful background info, which you could always adapt to work with the RaspIODuino if the GPIOs on the Pi aren’t fast enough to handle keypresses as rapidly as you’d like.

  35. Hello, fantastic explanation but I have a problem with my project, I have a little python script to read my gas meter. Every revolution of digits a small magnet inside the gas meter close to the reed switch makes an input for my raspberry. The problem is when the magnet stops close to the sensor circuit leaving on a high level, which cause false inputs, here my script. Any suggestion? thanks to all

    #!/usr/bin/env python
    import time
    import datetime
    import os.path
    import pycurl
    import RPi.GPIO as GPIO
    from time import sleep
    GPIO.setup(10, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    def GetGas(arg):
        time.sleep(0.01)  # need to filter out the false positive of some power fluctuation
        if GPIO.input(10) != GPIO.HIGH:
        if os.path.isfile('/var/www/myscripts/gas/gastotale.txt'):
                file = open("/var/www/myscripts/gas/gastotale.txt","r")
                gastotal = float(
        gastotal = gastotal+0.01
        file = open("/var/www/myscripts/gas/gastotale.txt","w")
        now =
        fileday = '/var/www/myscripts/gas/'+now.strftime("%Y-%m-%d")+'.txt'
        gasday = 0
        if os.path.isfile(fileday):
                file = open(fileday,"r")
                gasday = float(
        gasday = gasday+0.01
        file = open(fileday,"w")
        oem = pycurl.Curl()
        oem.setopt(oem.URL, ';csv=0,'+str(gasday)+',0,0,'+str(gastotale)+'&amp;apikey=xxxx')
    GPIO.add_event_detect(10, GPIO.RISING, callback=GetGas, bouncetime=300)
    GPIO.wait_for_edge(10, GPIO.FALLING)
    • I think interrupts may not be suitable for this setup – unless you add some code to test for a falling edge as well and some sort of timing variable. It’s definitely possible, but will require some thought. If you use GPIO.BOTH and test for rising and falling you should be able to prevent it from registering a new high before it’s gone back down to low again.

  36. Hi ALex, thanks !!ok now i try as you suggest using GPIO.BOTH, let’s see whats happens :)


  37. Great articles, nicely written and easy to understand for all ages. Thanks Alex!

  38. Greetings Forum,

    I am, a python novice with little to no knowledge of coding in python, trying to program an application on the Raspberry pi.
    Just picked up python and bought a rasp pi a week ago. :)


    3 Actuators,
    1 Fan
    1 Rasp Pi
    1 16 channel relay
    3 mechanical switches (user input).
    Language: Python 3 on Raspbian.

    Application: The actuators/fan run on a signal from an external mechanical switch for a specific period of time. The application has 3 phases as explained in the ALGO below:


    Phase 1.

    On hitting switch 1 (each step happens in a succession):

    Actuator 1 turns on.
    Fan turns on for a specific period of time.
    After time up – Fan and actuator turns off.
    Phase 2 and 3 have the same function for respective actuators (common fan)


    At start:
    Go into sleep mode and Check for a switch signal:
    > Once signal received, run respective phase for given time and shut down after x minutes and go back to sleep.
    > If another signal is received while the phase is running:
    >>Shut down current phase.
    >>Start the new phase.
    *Same applies to all three phases.

    I could manage to code each phase in a respective python program and it works – separately.

    I am now writing a master program which calls the sub-processes(programs) on conditional basis(switch signal).

    I would really appreciate if anyone could help me with the structure(Logic) of the master program or how to go about it.

    I am currently learning State Machine Architecture in Python…

    The code is came up with is(only for 2 phases):

    import time
    import os
    full_path = os.path.abspath(‘’)
    full_path = os.path.abspath(‘’)
    full_path = os.path.abspath(‘’)
    full_path = os.path.abspath(‘’)
    import subprocess
    from subprocess import call
    import RPi.GPIO as GPIO

    A = 11 #Relay 1 – System Power
    B = 12 #Relay 3 – Actuator OFF
    C = 15 #Relay 2 – Actuator ON
    D = 16 #Relay 5 – Fan OFF
    E = 18 #Realy 4 – Fan ON
    X = 22
    A = 11 #power
    F = 19 #Relay 1 – System Power
    G = 23 #Relay 3 – Actuator OFF
    H = 21 #Relay 2 – Actuator ON
    I = 16 #Relay 5 – Fan OFF
    J = 18 #Realy 4 – Fan ON

    GPIO.setup(A, GPIO.OUT)
    GPIO.output(A, GPIO.HIGH)
    GPIO.setup(B, GPIO.OUT)
    GPIO.output(B, GPIO.HIGH)
    GPIO.output(C, GPIO.HIGH)
    GPIO.setup(D, GPIO.OUT)
    GPIO.output(D, GPIO.HIGH)
    GPIO.setup(E, GPIO.OUT)
    GPIO.output(E, GPIO.HIGH)
    GPIO.setup(F, GPIO.OUT)
    GPIO.output(F, GPIO.HIGH)
    GPIO.setup(G, GPIO.OUT)
    GPIO.output(G, GPIO.HIGH)
    GPIO.output(H, GPIO.HIGH)
    GPIO.setup(I, GPIO.OUT)
    GPIO.output(I, GPIO.HIGH)
    GPIO.setup(J, GPIO.OUT)
    GPIO.output(J, GPIO.HIGH)
    input_value1 = GPIO.input(C)
    input_value2 = GPIO.input(H)


    State = type(“State”, (object,),{})

    class PHASE1ON(State):
    def Execute(self):[“python”,””])

    class PHASEON(State):
    def Execute(self):[“python”,””])

    class PHASE1OFF(State):
    def Execute(self):[“python”,””])

    class PHASE2OFF(State):
    def Execute(self):[“python”,””])


    class Transition(object):
    def __init__(self, toState):
    self.toState = toState

    def Execute(self):
    print (“Transitioning…”)


    class FSM(object):
    def __init__(self,char):
    self.char = char
    self.states = {}
    self.transitions = {}
    self.curState = None
    self.trans = None

    def SetState(self,stateName):
    self.curState = self.states[stateName]

    def Transition(self,transName):
    self.trans = self.transitions[transName]

    def Execute(self):
    self.trans = None


    class Char(object):
    def __init__(self):
    self.FSM = FSM(self)
    self.PHASE1ON = True
    self.PHASE2ON = True
    self.PHASE1OFF = True
    self.PHASE2OFF = True


    if __name__ ==”__main__”:
    PHASE = Char()

    PHASE.FSM.states[“PHASE1ON”] = PHASE1ON()
    PHASE.FSM.states[“PHASE2ON”] = PHASE2ON()

    PHASE.FSM.transitions[“toPHASE1ON”] = Transition(“PHASE1ON”)
    PHASE.FSM.transitions[“toPHASE2ON”] = Transition(“PHASE2ON”)
    PHASE.FSM.transitions[“toPHASE1OFF”] = Transition(“PHASE1OFF”)
    PHASE.FSM.transitions[“toPHASE2OFF”] = Transition(“PHASE2OFF”)


    while True:
    input_value1 = GPIO.input(C)
    input_value2 = GPIO.input(H)
    if input_value1 == False:
    print (‘The Button1 is pressed… :)’)
    while input_value1 == False:
    PHASE.PHASE1ON = False

    if input_value2 == False:
    print (‘The Button2 is pressed… :)’)
    while input_value2 == False:
    PHASE.PHASE2ON = False
    PHASE.PHASE2ON = False

    I could manage this until i knew about callback functions.

    I would really really appreciate it if the experts would help me structure the code, i am missing the right logic.

    Thanks a ton!! Like a Zillion tons! :)

    • I think you may be overcomplicating things? For such a relatively simple program I’m not sure if I’d bother with setting up an FSM (unless I’ve misunderstood your requirements). And I think it’d be much simpler to just have everything in the one Python file, rather than trying to have a “master program” which makes external calls to separate “sub programs”.

      And you may find it easier to use more sensible variable names, e.g. replace “A = 11 #Relay 1 – System Power” with “relay_system_power = 11”.

      I also see you’ve got “input_value1 = GPIO.input(C)” which doesn’t really make sense, when you’ve earlier done “GPIO.setup(C,GPIO.OUT)”.

      As with any programming problem, I suggest starting small and working your way up, rather than trying to dive in at the deep end ;-)
      Alex has a great set of tutorials at and you may be able to get more detailed help at

      Best of luck!

      • Hello Andrew,

        Thank you for the awesome feedback.
        Yes, the GPIO.input(C) was a mistake, Has been corrected now. :)

        I still can’t figure out how to achieve this logic in a relatively simple program.

        Is there a way to check 3 if conditions in one while loop?

        something like:

        while True:    
            input_value1 = GPIO.input(C)
            input_value2 = GPIO.input(H) 
            input_value3 = GPIO.input(M)   
            if input_value1 == False:
                print ('The BUtton is pressed... :)')
                while input_value1 == False:
            Elif input_value1 == True: 
                 While input_value1 == True:
           if input_value2 == False:
                while input_value2 == False:
          Elif input_value2 == True: 
                 While input_value2 == True:
            if input_value3 == False:
                while input_value3 == False:
          Elif input_value3 == True: 
                 While input_value3 == True:

        I’ve tried calling subprocess(as shown above) and also including the subprocess code directly but what happens is, the program only responds to the first condition met (1, 2 or 3) any keeps responding to only that condition.

        That is, suppose i hit switch 3 first, it runs the STATE 3 logic and after executing the logic, it only responds to Switch 3 input only….

        I am getting a feeling, because of my rookie knowledge in python, i am over complicating things.

        Is there a smarter/simpler way to achieve the logic I am trying to achieve?

        Thanks a bunch again!

        I really appreciate the expert help!

        – RP.

  39. Hi! Thanks for this tutorial, I’ve use it as a model into making my own program to count pulses,switch off(soft off using os.system(sudo shutdown etc)) and copying files , now my problems start when I use crone to start it up at booting it does start BUT it doesn’t recognize my second and third interrupt , meaning it runs at booting up but it only counts pulses , pressing the other buttons to switch off or copy files don’t work. But and here is where it gets weird if I use The command line writing sudo python mypath/ works like a charm .I tried using another script to write sudo python etc but it doesn’t work I mean it calls the other script it works but the second and third interrupt don’t work . Do you have any clue as why? Could you try your script and see if you get the same problem. Thanks in advance!!

    • running things from cron can be a bit of a pig. One of the issues I’ve had in the past was using relative file paths instead of absolute paths.

      But I’m sure there are other pitfalls I’m unaware of. Andrew might have some ideas.

      • Wot, me? ;-)
        I’ll have a fiddle when I get back home, if I remember… (I’m currently visiting family, and so Pi-less).

        I assume you’ve already checked you’re running the latest version of RPi.GPIO? As Alex says, check you’re using absolute filepaths (e.g. /home/pi/subfolder/yourfile instead of subfolder/yourfile and /sbin/shutdown instead of shutdown). And if you’re still having problems, try pasting your script somewhere for us to have to look at.

      • And of course there’s always if you want “more eyes” looking at your problem ;-)

        • Hi and thanks for your reply, basically the problem i have is that I use the LTX terminal and it works my two interruts, but using cron my proggram runs because it reads my sensors, but only one inteLrrupt works, now if i change
          os.system(“echo $(sudo shutdown -h now)”) and wirte it in the ulse interrupt works so its not as andrew says and alex it works on crone but not fully detecting all my interrupts, but again using the LTX works fine booth interrupts, I’ve tried using another script to write sudo python Desktop/ and thesame problem aplies it runs but the second interrupt its not getting caught, so im guesing its a crone problems or smt

          import spidev
          import io # used to create file streams
          import fcntl # used to access I2C parameters like addresses
          import os
          from datetime import datetime
          import time # used for sleep delay and timestamps
          import string # helps parse strings
          import RPi.GPIO as GPIO # Importamos la libreria GPIO con sus modulos
          GPIO.setmode(GPIO.BCM) # Le decimos como queremos contar los pines
          #Ponemos el pin numero 23 como INPUT for pulses , al estar conectado a 3.3v necesitamos activar la resistencia de pull down de la raspberry pi
          GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
          #on off switch 
          GPIO.setup(4, GPIO.IN,pull_up_down=GPIO.PUD_UP)
          n = 0
          flag = False
          p = 0
          spi =spidev.SpiDev()
, 0)
          #checking is there is a pulse not relevant Definimos una funcion de paso para comprobar que ha habido una CAZOLETADA
          def comprobar(f):
              global flag, p
              if f == 1:
                  flag = True
                  flag = False
              return flag
          #Definimos una funcion de lectura de temperatura
          def leeradc(numadc):
              r = spi.xfer2([1, 8 + numadc &lt;&lt; 4, 0])
              adcout = ((r[1] &amp; 3 ) &lt;&gt; Atlas Scientific sample code")
              print("&gt;&gt; Any commands entered are passed to the board via I2C except:")
              print("&gt;&gt; Address,xx changes the I2C address the Raspberry Pi communicates with.") 
              print("&gt;&gt; Poll,xx.x command continuously polls the board every xx.x seconds")
              print(" where xx.x is longer than the %0.2f second timeout." %  atlas_i2c.long_timeout)
              print(" Pressing ctrl-c will stop the polling")
              global p
              # main loop
          • #off swicht       
            def apagar(channel):
                os.system("echo $(sudo shutdown -h now)")
            # Pulse interrupt Definimos nuestra interrupcion se supone que de mientras el codigo seguira haciendo mediciones de sensores y solo hasta que se de la interrupcion la cual esta en oparaleo al codigo principal
            def interrupcion(channel):
                #os.system("echo $(sudo shutdown -h now)")
                global n,p
                p = 1
                n = n + 1  
                return n
            def main():
                device = atlas_i2c() # creates the I2C port object, specify the address or bus if necessary
                GPIO.add_event_detect(23, GPIO.FALLING, callback = interrupcion, bouncetime = 800)
                GPIO.add_event_detect(4, GPIO.RISING, callback = apagar, bouncetime=600)
                global flag, n
                print("&gt;&gt; Atlas Scientific sample code")
                print("&gt;&gt; Any commands entered are passed to the board via I2C except:")
                print("&gt;&gt; Address,xx changes the I2C address the Raspberry Pi communicates with.") 
                print("&gt;&gt; Poll,xx.x command continuously polls the board every xx.x seconds")
                print(" where xx.x is longer than the %0.2f second timeout." %  atlas_i2c.long_timeout)
                print(" Pressing ctrl-c will stop the polling")
                global p
                # main loop
          • The indentation has got lost (WordPress problem, not yours), and it looks like your script has been truncated too?
            Try pasting it to and then giving us the link to it there :)


            See if it works now, basically my problem is that if I use crone it does run my script but only one interruption its detected, the pulse one , If I use the LTXterminal both interrupts are detected.

        • I’ve got no idea if it has anything to do with your problem or not, but just out of curiosity how come you’re using:
          os.system(“echo $(sudo shutdown -h now)”)
          rather than:
          os.system(“/sbin/shutdown -h now”)

          a) the “echo” isn’t necessary
          b) scripts run from cron are already running as root, so the “sudo” isn’t necessary
          c) using absolute paths rather than relative paths can’t hurt ;-)

 provides another method of running scripts at bootup, if you fancy doing some experimentation to see if that makes any difference?

          • Hello Andrew , I’ll give it a go to your suggestions and YES I tried rc.local but not working for me, I used the code and circuit that I think was featured in one of your post about a soft off ,hard off and On circuit for pi , I followed the instruction given in magpie magazine about it and not getting it to work the soft off . I’m gonna give it a go at your suggestions and trying rc.local again , I’ll get back ASAP . thanks for the help

          • I’m under the impression that rc.local doesn’t work on Jessie, as I’ve heard it uses systemd, although I don’t know much about it, I think Matt Hawkins had blogged about it over at raspberry pi spy.

  40. Hello and good news!! Now the problem I had, was the pin choosed it seemed that GPIO4 says GPIO_GCLK so it seems that when run with crone maybe it was waiting for a clock signal of some sort I honestly dont know what that pins does but after changing it to GPIO_24 it works. So it was something to do with the pins and not crone. Thank you for your help! and hopefully someone finds this helpfull

Leave a Reply