Jul132013

 

RasPiTV-RPi.GPIO-EXIT

You might think I’m going about this series in a funny way. You’d be right! I am. That’s because I’m trying to highlight the bits that people don’t read about in other tutorials/documentation/manuals/books. These bits are useful, important and often overlooked.

If I covered inputs and outputs first, you might not get to the rest because you’d already “know all I need”. That’s only partially true though. Although you can get away with not knowing or using this stuff, it’s muchbetter and safer if you know it and use it.

So today we’re focussing on how to exit cleanly from a program that uses RPi.GPIO
I know – I’m bonkers! I haven’t even told you how to use RPi.GPIO yet and I’m already showing you how to exit cleanly. Bear with me! It makes sense. You need to know this stuff.

Correct use of GPIO.cleanup()

RPi.GPIO provides a built-in function GPIO.cleanup() to clean up all the ports you’ve used. But be very clear what this does. It only affects any ports you have set in the current program. It resets any ports you have used in this program back to input mode. This prevents damage from, say, a situation where you have a port set HIGH as an output and you accidentally connect it to GND (LOW), which would short-circuit the port and possibly fry it. Inputs can handle either 0V (LOW) or 3.3V (HIGH), so it’s safer to leave ports as inputs.

Here’s a simple example of how to use GPIO.cleanup()

  1. import RPi.GPIO as GPIO  
  2.   
  3. # the rest of your code would go here  
  4.   
  5. # when your code ends, the last line before the program exits would be...  
  6. GPIO.cleanup()  
  7.   
  8. # remember, a program doesn't necessarily exit at the last line!  

If you use this, and the program exits normally, all the ports you’ve used will be cleaned up and ready for use straight away. But life’s not often as simple as that is it? More on that in a minute. First I want to show you…

Incorrect use of GPIO.cleanup()

I have seen people using GPIO.cleanup() wrongly at the start of a program , thinking it will clean up all the ports on the Pi. It does no such thing.1 If you put it at the top of your script, it’s just a wasted line of code. It does nothing until you’ve set up some ports for input or output (which we’re covering soon).

Use GPIO.cleanup() on exit, as I’ve shown above, and in slightly more complex situations, as I will show below…

Reasons for a program exit

There are at least three reasons, that I can think of, for exiting a program…

  1. Natural, controlled end – the program finished doing what it was written to do. Hurrah! It worked :)
  2. Error – something went wrong and the program “throws a wobbly” and “errors out”. Either a process failed in an unforeseen way, or there was a coding error in the program.
  3. Keyboard Interrupt – you pressed CTRL+C to stop the program. This is called a Keyboard Interrupt.

Both 2 & 3 are types of “exceptions”.

So what?

The problem is that, unless you code for these situations, any ports which are in use at the time of an error or Keyboard Interrupt will stay set exactly as they were, even after the program exits.

That’s an out of control situation – unacceptable, when you’re trying to take over the world with GPIO programming! Everything has to be under control.

If you then try to run the program again, when you try to set up the ports that are “still set” you’ll get warnings that the ports are “already in use”.

Switch off the warnings?

You can switch off the warnings, but that’s the “chicken’s way out”. It hides the problem, but doesn’t solve it. Still, you may choose to do it, so here’s how you do. Just place…

GPIO.setwarnings(False)

…near the top of your script, before you start setting up any ports.

Let’s try: a better way

My preferred method to catch errors and Keyboard Interrupts uses a try: except: block.
If you put your main piece of code in a try: block, it won’t “bomb straight out” of the program if an exception occurs (something doesn’t go according to plan).

You can specify different types of exceptions, including KeyboardInterrupt, and/or have a general one to catch every exception. There’s a list of Python Exceptions here . I’ve only ever used a couple of them.

Have a look at the code below to see how the exceptions are coded…

  1. import RPi.GPIO as GPIO  
  2.   
  3. # here you would put all your code for setting up GPIO,  
  4. # we'll cover that tomorrow  
  5. # initial values of variables etc...  
  6. counter = 0  
  7.   
  8. try:  
  9.     # here you put your main loop or block of code  
  10.     while counter < 9000000:  
  11.         # count up to 9000000 - takes ~20s  
  12.         counter += 1  
  13.     print "Target reached: %d" % counter  
  14.   
  15. except KeyboardInterrupt:  
  16.     # here you put any code you want to run before the program   
  17.     # exits when you press CTRL+C  
  18.     print "\n", counter # print value of counter  
  19.   
  20. except:  
  21.     # this catches ALL other exceptions including errors.  
  22.     # You won't get any error messages for debugging  
  23.     # so only use it once your code is working  
  24.     print "Other error or exception occurred!"  
  25.   
  26. finally:  
  27.     GPIO.cleanup() # this ensures a clean exit  

If you let the program run for ~22 seconds, it will count up to 9 million, tell you it reached its target, clean up any GPIO ports you've used and exit normally. This is the code within the try: block (lines 8-13).

The code in the except KeyboardInterrupt: block (lines 15-18) covers the CTRL+C situation. So when you press CTRL+C, it prints the current value of counter, cleans up any GPIO ports you've used, and exits.

The code in the except: block (lines 20-24) covers all other exceptions - including program errors. So if another exeption occurs, it warns you, cleans up any GPIO ports you've used, and exits.

This program will run until it finishes, or a keyboard interrupt or other exception occurs. Whichever of these occurs, the finally:block (lines 26-27) will run, cleaning up the GPIO ports on exit.

Also note, that we haven't actually used any GPIO ports here, so the cleanup isn't really doing anything yet, but this is a template you can use when we do start using ports.

If you want to try it out, it's a bit long for a live Python session, so you could retype it, or cut and paste it into a program as we did in the first post in this series...

Now you know how to exit an RPi.GPIO program cleanly, without leaving a trail of uncontrolled ports behind you.

That's about it for the preliminaries

So now we've covered...

  1. How to check what RPi.GPIO version you have
  2. How to check what Pi board Revision you have
  3. How to Exit GPIO programs cleanly, avoid warnings and protect your Pi

What next?

Tomorrow, we're actually going to set up the GPIO, talk about different pin/port numbering schemes and take a look at reading inputs.


 
_____________________________
1 If it did clean up all the ports, this would mean you could have major conflicts between different programs, which might not even be trying to use the same ports. Clearly, not a desirable situation!


+ Recent posts