In this second article I will introduce “threaded callback” which opens up a lot of new possibilities.

Threaded callback – what the heck is that?

I know it sounds complicated. And it probably is complicated in the C code it’s written in, but we’re Pythonites and we don’t have to go there. ;)

If you remember the previous example program was just a simple “wait for port 23 to be connected to GND when we press the button and then print a message and exit the program”.

So, while it was waiting, the program wasn’t doing anything else. The program only had one thread, which means only one thing was being done at once. Python is capable of running more than one thread at once. It’s called multi-threading. It means that you can go through more than one piece of code simultaneously. This is where we can reap the benefit of interrupts because we can do something else while we wait for our “event” to happen. (Just like your “postman detector” allowed you to get on with something else instead of being distracted by waiting for the mail.)

So that covers the threading part of threaded callback. What’s a callback?
When an event is detected in the second thread, it communicates this back to the main thread (calls back). What we now have in RPi.GPIO is the ability to start a new thread for an interrupt and specify a set of instructions (function) that will run when the interrupt occurs in the second thread. This is a threaded callback function.

This is like your “postman detector” giving you a list of reminders of things you wanted to do when your delivery arrives AND doing them for you, so you can carry on with what you want to be doing.

So What are we going to do now?

We’ll keep most of what we did before and add another button and an event detect threaded callback that runs when the new button is pressed, even though we are still waiting for the first button to be pressed.

But this time, the new button will connect GPIO port 24 to 3.3V (3V3) when pressed. This will allow us to demonstrate a rising edge detection. So we’ll be setting up port 24 with the built in pulldown resistor enabled.

Circuit for second interrupt experiment

Later on we’ll have to modify the code to cope with ‘button bounce’, but we won’t say any more about that just yet.

Do you need to update RPi.GPIO?

If you didn’t do it for the first example, 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
GPIO.VERSION

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

Install RPi.GPIO version 0.5.2 for simple interrupts

If you need to, you can install 0.5.2 or later withsudo apt-get update
sudo apt-get dist-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 explanation in the code, so that if you use it, you will still have it.

#!/usr/bin/env python2.7
# script by Alex Eames http://RasPi.tv
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
# GPIO 23 & 24 set up as inputs. One pulled up, the other down.
# 23 will go to GND when button pressed and 24 will go to 3V3 (3.3V)
# this enables us to demonstrate both rising and falling edge detection
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
# now we'll define the threaded callback function
# this will run in another thread when our event is detected
def my_callback(channel):
print "Rising edge detected on port 24 - even though, in the main thread,"
print "we are still waiting for a falling edge - how cool?\n"
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)"
raw_input("Press Enter when ready\n>")
# The GPIO.add_event_detect() line below set things up so that
# when a rising edge is detected on port 24, regardless of whatever
# else is happening in the program, the function "my_callback" will be run
# It will happen even while the program is waiting for
# a falling edge on the other button.
GPIO.add_event_detect(24, GPIO.RISING, callback=my_callback)
try:
print "Waiting for falling edge on port 23"
GPIO.wait_for_edge(23, GPIO.FALLING)
print "Falling edge detected. Here endeth the second lesson."
except KeyboardInterrupt:
GPIO.cleanup() # clean up GPIO on CTRL+C exit
GPIO.cleanup() # clean up GPIO on normal exit

Two ways to get the above code on your Pi

If you are in the command line on your Pi, type…nano interrupt2.py
Then click “copy to clipboard” (above) and paste into the nano window. ThenCTRL+O
Enter
CTRL+X

Alternatively, you can download this directly to your Pi using…wget http://raspi.tv/download/interrupt2.py.gz
gunzip interrupt2.py.gz

Then you can run it with…sudo python interrupt2.py

What’s supposed to happen?

When you run the code it gives you a message “Waiting for falling edge on port 23″
If you press button 1, it will terminate the program as before and give you a message
“Falling edge detected.”

This will occur as many times as you press the button. The program is still waiting for the original falling edge on port 23, and it won’t terminate until it gets it. Because your second button press is detected in another thread, it doesn’t affect the main thread.

You may also notice, depending on how cleanly you press the second button, that sometimes you get more than one message for just one button press. This is called “switch bounce”.

Bouncy, bouncy, bouncy

When you press a button switch, the springy contacts may flex and rapidly make and break contact one or more times. This may cause more than one edge detection to trigger, so you may get more than one message for one button press. There is, of course, a way round it, in software.

Why didn’t this happen before?

I hear you ask. The answer is simple. Last time the program was simply waiting for a single button press. As soon as that button press was detected, it stopped waiting. So if the switch bounced, it was ignored. The program had already moved on. In our case, it had closed. But, when the event detection is running constantly in another thread, this is not the case and we actually need to slow things down a bit in what is called “software debouncing”.

How to do software debouncing

In order to debounce, we need to be able to measure time intervals. To do that, we use the time module. Near the top of the program we need to add a line…

import time. I add it immediately after the RPi.GPIO import

import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)

Then, also quite near the top of the program we need to set an intial value for the variable time_stamp that we will use to measure time intervals

time_stamp = time.time() I put this after the GPIO.setup commands. This sets time_stamp equal to the time in seconds right now.

And now, even if you deliberately press the button twice, as long as you do it within 0.3 seconds it will only register one button press. You have debounced the switch, by forcing the program to ignore a button press if it occurs less than 0.3 seconds after the previous one.

If you are IDLE (Python joke) you can get the amended code here…wget http://raspi.tv/download/interrupt2a.py.gz
gunzip interrupt2a.py.gz

How does this work?

time_now = time.time() stores the current time in seconds in the variable time_now.
Now look at the last line of the function.time_stamp = time_now This stores the time in seconds when the function was started in a global variable called time_stamp So next time this function is called, it will be able to check how much time has elapsed since the last time.

That’s what is happening in the if statement.if (time_now - time_stamp) >= 0.3: In English this means “If more than 0.3 seconds has elapsed since the last button press, execute the code in the indented block”.

So if more than 0.3 seconds have elapsed it would print the message…
“Rising edge detected on port 24 – even though, in the main thread we are still waiting for a falling edge – how cool?”

If less than 0.3 seconds has elapsed, it will do nothing. Job done. Button switch is debounced.

Update – RPi.GPIO 0.5.2 onwards includes this debounce algorithm

Ben has included the above debounce algorithm in 0.5.2 onwards. This article was originally written for 0.5.1. So the above debounce code has been superseded by adding bouncetime=xxx, where xxx is a time in milliseconds. e.g.

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

[…] button press” interrupt. There’s a lot more you can do with them, as I will show you in the next article, which will cover “threaded callback”, which allows us to use the spare capacity we’ve freed up by not polling […]

I like to use those 2 pins because there is a ground next to each of them. Plus they are right in front the way I have my RPi facing. By default they are not used for any of the communication interfaces, either.

For this test I used button switches scavenged from an old PC (same as in my reset switch video). Each switch is wired to a 2 pin female connector, so it makes it really easy to pick pins on the GPIO header that have a GND next to them (and aren’t used for anything else significant).

cleanup() at the top? GPIO.cleanup() only cleans up ports opened or used by THIS script. Putting it at the top does nothing.

Your problem appears to be that you’re waiting for a rising edge on 24 but you’ve pulled port 24 high. If it’s already high it can’t rise. It may well work if you change it to falling (but I don’t know how it’s wired).

The cleanup is at the top because I forgot to remove it after I added the keyboardinterrupt exception catch. :)

I know that the program is catching the rising button because of the print statement. It prints out the “buttonRising” each time I press and release the button (sometimes more than once). The sleep even happens (there’s a second pause before the “buttonRising” appears. So I know it’s going into the callback code. It’s just not lighting the LED.

Fascinating stuff! As a newbie, maybe you can help. Rather than printing to the screen, is there anyway to have the input added to an existing database to record the Time/Date and Duration of the button push?

Thanks for the great tutorial! I have it up and running on my pi with great success. I have two questions I was hoping to get help with:

1. Is there any way to detect whether it caught the rising or falling edge in the callback? I would like to either have a callback for falling and another for rising which doesnt seem to be allowed or to have one callback with an if statement to check for rising or falling

2. Is there a way to extend this feature to other buttons? Specifically I would like to use the buttons on an I2C adafruit pi plate with lcd.

1. http://code.google.com/p/raspberry-gpio-python/source/browse/source/py_gpio.c#377 would seem to indicate you can’t have separate callbacks for RISING and FALLING on a single GPIO. I haven’t tested this, but couldn’t you add the callback for BOTH and then just check the current value of the GPIO inside the callback? If the GPIO is now high, it must have been a rising edge; if the GPIO is low it must have been a falling edge… ? *shrug*

2. I guess edge-detection depends on the specifics of the I2C GPIO expander chip that’s being used – check the datasheet. But you won’t be able to use the specific library being used here, https://pypi.python.org/pypi/RPi.GPIO says “Note that the current release does not support SPI, I2C, hardware PWM or serial functionality on the RPi yet.”

One observation though. I have spent quite some time trying to handle Ctrl+C properly but I couldn’t figure it out.
Does handling CTRL+c in your code work fine ?
In my case it doesn’t and I suspect after a few tests it’s because the the ctrl+c takes place while we are not in the try: part.

Ok for the record I was wrong. Everything looked well. The system was hanging withing the try: so it should have worked. Not sure why it doesn’t.
After a bit of googling I added

sys.stdin.read()

in the try: part and it seems to work now. I don’t know why it doesn’t work with out it (the except part is not getting caught) but that’s more like a python question rather that RPi question. Seems like I am hitting something like the one described here

Nope, wait_for_edge is a blocking function (i.e. no other code can run at the same time). That’s what the callback functions are for (AFAIK there’s no problem having separate callback functions on different GPIO pins). I guess you could even have a while loop that did nothing but sleep(1).

I’m afraid “it doesn’t work properly” is never a very helpful comment – you’ll need to provide full details of your hardware setup (what connections have you wired to which GPIOs, using which resistors, which pullup settings, which Pi model/revision, etc.), your software setup (source code to your full script, which version of RPi.GPIO, which version of Python, etc.), and anything else relevant.http://catb.org/~esr/faqs/smart-questions.html

You could also try asking at http://www.raspberrypi.org/phpBB3/ – I don’t want to take anything away from Alex’s excellent blog, but there’s surely many more people reading the RaspberryPi Forums than read RasPI.TV

Thanks a lot for helping me, anyway i’m not sure to be able to use pull up resistor cause raspberry is far from the buttons and they are connected only to gnd and gpio.. There is no software way to eliminate this interference?

I did a test using this code
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(19, GPIO.IN, pull_up_down=GPIO.PUD_UP)
while True:
if GPIO.input(19):
print('1')
else:
print('0')

And i got something like ten 1 and then ten 0 then ten 1 and again ten 0 and so on…

Having long wires is a great way to pick up interference :-/ Unless your wires are shielded, they effectively act like aerials, so the longer the wire the more interference you’ll pick up. And this is just about where my knowledge on this subject runs out…
No, there’s no way to “fix this in software”.

I think a capacitor can sometimes be used to filter out “spikes” of interference (IIRC it’s the hardware equivalent of what software de-bouncing does), but this is all beyond my depth – I’m a software guy not a hardware guy ;-) Good luck!

I’ve seen with the digital multimeter that when the button is not pressed there is a very big resistance like 200Kohm maybe if i add a resistance in series i could delete any interference or not? (just like AndrewS i like software and i don’t know so much about hardware)

I think the 10k + 1k resistors limit the current enough to make it a non-fatal condition.
I seem to remember a post by Mah Jongh on the Pi forums where he said something like ‘you don’t need level converters as long as you have a >1k resistor in series with the port’. That’s not a quote though – it’s what I remember from the thread.

As AndrewS points out, THE DIAGRAM SHOWS 5V CONNECTED TO THE Pi’s GPIO INPUT (PORT 24) – WHEN THE BUTTON IS PUSHED. alex says “I think the 10k + 1k resistors limit the current enough” … BUT THERE ARE NO RESISTORS IN THE DIAGRAM. CORRECTION NEEDED.

Well are you using pin 21 or GPIO 21? Have you read the RPi.GPIO basics series on here this week? It’s all covered.
Pin 21 is GPIO9
GPIO21 is pin 13 if you have a Rev 1 Pi. If you have a Rev 2 it doesn’t exist.

There’s no reason that a switch shouldn’t work identically to the way a button works – electrically they’re either on (closed) or off (open). How have you got the switch wired up, what pull-up / pull-down settings (or resistors) are you using, etc. ?

Alex
Before my questions, can I just thank you for all the work you have done on your site. Without it I would be far more confused than I already am, and would never have got to grips with the Gertboard.

Now the questions:

1. Why have you used the function, e.g.,
GPIO.add_event_detect(24, GPIO.RISING, callback=my_callback)
when the manual on the raspberry-gpio-python/wiki page provides the function
GPIO.add_event_callback(….)
which appears to do the same job? Or does it?

2. How does one know when a “word” such as “channel” in a function has to be left as it is, as in
def my-callback(channel),
and when it has to be substituted with and actual value, such as 24 in
GPIO.add_event_detect(24, …) above?
Cheers!

1. GPIO.add_event_callback(….) is used if you want to run more than one callback function from a single interrupt event.

2. Simple answer, you rely on good documentation conventions and up to date documentation which is not always fool-proof. Sometimes you just have to “poke it and see” what happens. :)

I wasted some time yesterday trying to get tweepy working, only to realise that the official documentation was WAY out of date and what I was trying to do would never work again (they’ve changed the way twitter authentication works).

Aha! again. I’m afraid I come from the days of Algol and punched cards, so am v rusty, but I’m sure it will all gradually make sense. Thanks for taking the time to write the example code _ I’ll make a note of it.
H

Can you increment a variable within the interrupt ‘call back’ DEF function ?

I am trying to increment a variable every time an interrupt occurs. eg X=X+1 to count the number of interrupts that has happened. But when I include x=x+1 or similar into the DEF routine that is called by the interrupt, I keep getting ‘UnboundLocalError : local variable ‘x’ referenced before assignment. It appears not to like having ‘x’ declared twice for some reason. Is there a way around this so I can keep count of interrupts that has occurred ?

Please can anyone help, its so frustrating trying so many ways but keep failing.

FP:
define a higher-order function (HOF) with the value stored in a local variable. The HOF also defines the counter function. Before Python 3.2, an inner function couldn’t modify a variable from an outer function (assigning to a variable in the inner function creates a new variable of that name local to the inner function), so a mutable data structure (such as a dict) must be used to store the value.

Note that at a certain level, the two approaches are the same, as the 2nd is a simplified form of how you’d implement OOP in FP (define a higher order function, a “constructor”, that creates attributes as local variables & private methods as inner functions and returns a function that processes messages as the instance).

Thanks for this. You are of course right, and thank you for showing different ways around it.

But I think you can see, from the point of view of someone not very advanced with Python, the most important thing is “making it do what you want”. If it does what you want by making a variable global, using one line of code, it helps you to get the job done in a much simpler way.

I realise there is a danger that it becomes a habit. But there is also a danger that you can make things too hard for people when they’re at a low level of experience.

If you have to learn about classes, higher order functions and inner functions, in order to “make it do what I want”, you’re less likely to see it through.

It’s really good to have someone explain “how to make it do what you want in a more robust and scalable way” though, so thank you, and please keep adding your input. It’s most welcome. :)

AndrewS has emailed me to say he’s been unable to post this…
(If he manages to post it later on, I’ll delete this version)

Globals are bad and should generally be avoided

Ordinarily I’d agree with you, but even the page you linked to says
“In a very small or one-off programs …. using globals can be the
simplest thing that works.” and I believe Alex tries to keep his stuff
simple and aimed at beginners, not programming experts ;-)

Also, in this specific scenario you can’t even pass an object to the
callback function because RPi.GPIO provides no mechanism for doing sohttps://code.google.com/p/raspberry-gpio-python/wiki/Inputs#Threaded_callbacks
so you have to use a global variable. You might be able
to use a lambda function (I haven’t tried experimenting) but that’s
definitely beyond the scope of Alex’s beginner-focused blog.

Ordinarily I’d agree with you, but even the page you linked to says “In a very small or one-off programs …. using globals can be the simplest thing that works.” and I believe Alex tries to keep his stuff simple and aimed at beginners, not programming experts ;-)

The “generally” in my post was my out for this. My thinking when writing was:
1. If not exposed early to good practice, bad practices quickly become personal habit.
2. It’s not very helpful just to say “that’s bad; don’t do that”, so some sort of code sample is in order.
3. The code samples required for the better techniques aren’t very long. Even if all aspects of the techniques aren’t understood, they can still be used. The techniques then (hopefully) become a bridge to learning.
4. I briefly considered adding “but globals can be OK in very short programs that you’re going to discard”, but once someone has that sort of permission, they tend to put off learning the appropriate technique.

Also, what starts as one-off code all too often shows up in long-term projects, especially when posted as sample code on a such a useful website.

In the end, this is a debate about pedagogy, which (being more philosophy than science) doesn’t have hard answers. Either perspective may be the more effective for a given student.

Also, in this specific scenario you can’t even pass an object to the callback function because RPi.GPIO provides no mechanism for doing so […] so you have to use a global variable.

Python has a very nice feature that enables GPIO functions to take objects as callbacks. The GPIO functions take callables, and callables can be objects or functions (starting with Python 3, there is no longer any difference, as functions are objects). All it takes to make a callable object is for it to have a __call__ method.

You might be able to use a lambda function (I haven’t tried experimenting) but that’s definitely beyond the scope of Alex’s beginner-focused blog.

I disagree that lambdas (or higher-order functions) aren’t suitable for beginners. They’re taught day-one to underclassmen in any class on functional programming. Anonymous functions are no more surprising to a novice than anything else. They may seem that way to those that come across them later, but that’s only because it’s been so long that they’ve been surprised about some programming feature that they may not realize it’s due to the novelty of it, rather than any inherent difficulty. Essentially, that surprise is an emotional regression (but a very good one, because you get to experience the wonder and joy of discovery) to the novice state. The only real difficulty is when someone is so used to one programming paradigm that it’s hard to think within another. Because of the wide emphasis on imperative programming and the late introduction of FP into most programmers experience, FP gained an undeserved reputation of difficulty.

You could sum up (which is certainly called for, given how long-winded this post is becoming (including this comment (ack))) my beliefs on topic suitability with “the only concepts unsuitable for a novice are those that require learning other concepts first”.

In replying to a question of mine in Part 2 of this series, Alex suggested I used a global variable to access an interrupt event (accumulating output from a rain-gauge), and I understood it, and I did it, and it worked first go.

Outis has been very positive in his criticism of “global” by giving 3 ways in which “global” might be avoided. I have tried very hard, but I can’t see how these could be incorporated in GPIO callbacks. He mentions that these methods are introduced early on in Python classes.

I think that there is a different learning process if you are (a) a bright young student in a well-taught well-structured class (does Outis teach these?), or (b) an old bloke like me picking up bits as he goes along, often from this excellent blog.

I was going to suggest that, if Outis has the time and patience, I would love to see how he would incorporate one of his more “moral” solutions with the limitations of GPIO interrupts. However i notice below that AndrewS has already done this.

A footnote: I am always pleasantly surprised and encouraged by the generosity of the contributors to this blog. Thank you all.

I was going to suggest that, if Outis has the time and patience, I would love to see how he would incorporate one of his more “moral” solutions with the limitations of GPIO interrupts. However i notice below that AndrewS has already done this.

Um, I’m afraid I hadn’t! My code below was still using a global. Now that Outis has taught me about __call__ (even old programmers still learn new tricks!) I’d use his Counter class example from above, you’d just need to pass c as the RPi,GPIO callback ‘function’, and then the current value of c can be read elsewhere in the program with c.x

Although from a pedagogical point of view, single-letter variables are probably a worse habit than global variables ;-)

I tried to follow your suggestion to use Outis’ Counter example, by using a button to connect GPIO4 to 3V3, and running the short program attached at the foot of this comment.
The relevant commands are:

[I have checked the v simple circuit, and it works in a program that uses a function with a (deprecated) global variable.]
————————— The Program
#!/usr/bin/python
”’
The purpose of this trial:
to detect an event (in this case connecting GPIO 4 to 3V3 via a button)
using GPIO interrupts but without declaring a global variable
”’
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)

Outis has been very positive in his criticism of “global” by giving 3 ways in which “global” might be avoided. I have tried very hard, but I can’t see how these could be incorporated in GPIO callbacks. […] I was going to suggest that, if Outis has the time and patience, I would love to see how he would incorporate one of his more “moral” solutions with the limitations of GPIO interrupts.

There’s not too much to it. For the OOP approach, the plain callback basically becomes the __call__ method, and for FP it becomes the inner function returned by the make_* constructor. You can think of it in terms of code rewriting, where:def callback(args...):body...

In each case, the body requires the minor rewrite (signified by body’ rather than body) of renaming the variables that keep their values between calls.
* For OOP, simply prefix the variables with “self.” (and be sure to initialize them in __init__). For example, foo becomes self.foo
* For FP under Python < 3, replace the variables with a lookup in the dict you created that holds locals. For example, foo becomes locals[‘foo’]
* For FP under Python >= 3, add the variables to a “nonlocal” statement (the rest of the callback body remains unchanged).
Compare the samples in my previous post with what I could infer to be in Derek’s function:def counter():
x += 1
return x

If that’s not enough for you to write a solution, let me know and I’ll post a more complete example.

He mentions that these methods are introduced early on in Python classes.

<clarification mode=”pedant”>Actually, I was referring to anonymous functions and HOF being introduced to underclassmen in classes covering FP (though OOP similarly is taught early on). When Python makes an appearance in an class, it’s typically a teaching tool, rather than the subject of a class.</clarification>

Although from a pedagogical point of view, single-letter variables are probably a worse habit than global variables ;-)

Yeah, it seems like no matter how much time I spend editing, there’s always at least one mistake. Keeps the gods from punishing me, the Greeks might have said.

Ahhh, the problem here is that you’ve explicitly defined Counter.__call__ to only take the ‘self’ argument, but it looks like RPi.GPIO is trying to pass an additional ‘channel’ argument to the callback function. So you need to add an extra parameter to your __call__ function’s definition:def __call__(self, channel):

Alternatively you could say your function is allowed to ‘scoop up’ any number of arguments by doing:def __call__(self, *args, **kwargs):
– as you don’t have any need for these ‘extra arguments’ inside your function, it’s safe to just ignore their values. See this for more info.

Thanks to Outis for his examples and for getting me to look at OOP – I have now been reading up on it in a real (paper) book. Phew! Old dogs, new tricks.

Thanks to AndrewS for his suggestion about including the extra channel parameter – it now works!

Note to Alex: As the guys have helped me get this to work, I thought you might like to have the following functioning code on your blog for other confused beginners like me. Please edit or remove as you see fit – I’m not too confident about the formatting being maintained. HarryT.
———————————————————————————————–
#!/usr/bin/python
'''
The purpose of this demo program (in Python 2.7):
To detect an event using GPIO interrupts without declaring a global variable.
Based on suggestions by Outis and AndrewS on the RasPiTV forum.
The simple circuit is:
3V3 --- Button --- LED --- 68ohm resistor --- GPIO4.
Every press of the button increases the value of count by 1.
This program actually works!
'''

And the final call to GPIO.cleanup() is missing its brackets (I suspect that may have been a copy’n’paste typo?).

And instead of accessing the count.x variable directly from outside the Counter class, you could consider making it a read-only property (although again it’s not really worth it for such a small program).

It’s another stylistic thing, but personally when I use the same value more than once in a program (e.g. the 30 in your code above) I like to replace it with a variable instead (so that if I ever want to change its value later, I only need to change it in one place).

I hadn’t thought of string formatting, though I’ve been a bit wary of it as it seems quite different in Python 3. (My brain is still in Algol-land, I suppose.)

The little for loop was just a quick and dirty attempt to give the proof-of-principle program something to do. I really should input both the number and length of printing intervals using raw_input, as it runs too fast at the moment.

Gents
I have been following these comments with interest after my initial question which needed the ‘global’ command. However, I notice in the last comment the print statement contained ‘%d’. I have seen this before with other similar notations, but I do not understand what %d does or what is it for ?. I have read all my books on Raspberrypi & Python but can not find any reference to this or similar notations. Can anyone please explain why %d etc is used and point me in the right direction on where I can read up on this subject.
many thanks
Derek
ps I will still stick to ‘global’ as it is easier for me to understand.

Writing as one beginner to another, I sending this address of a page dealing with strings, on a python tutorial site that I have found more accessible than the official documentation and tutorials:http://www.tutorialspoint.com/python/python_strings.htm
(sorry, I don’t know how to paste this as a link.)

I also actually did find the “official” site useful (see link below): not the formal definitions (which are doubtless rigorous but opaque to me), but by working through the examples in section 6.1.3.2, and saving the resultant files containing my own efforts for future reference.

However I would like to know what does ‘global’ y actually do. and also in the interrupt detect statement you use the command ‘callback=’ this points to a DEF function, but is there any other commands/key words that can be used instead of callback as to do other things on an interrupt ?.

Derek you need to look up “scope of a variable in a function in python” or something like that.

As to your second question, not as far as I know. But you can do pretty much anything you want in a function (even call other functions, or, as you now know, globally set the value of variables defined in other functions) so it’s not really a handicap.

Can this method be used for GUI buttons using the tkinter library? I have a motor and a driver and can make the motor turn one way with pin 11 and turn the other way with pin 12 what I want to do is make a simple GUI with 2 buttons one for forward one for back.

When I click the forward button it works but I don’t know how to make it stop or use the back button to change direction, here is a sample of the code.

Thanks AndrewS, This looks perfect for what I need, I had before seeing this got the basic functions working with just turning the pins on and then created a 3rd button saying “STOP” which set all pins back to “False” which does the trick but is a bit clunky!

I will try and get your script working and see if it runs smoother, next step is I need to integrate it with webiopi :s

I love rpi gpio, very easy to use. But there is some confusion on what it does for event handling. To be clear, it does NOT use real interrupts. It does check the GPIO pin for a state change and then launch an event handler. Just know that this does not involve the cpu’s interrupt capabilities.