Hello! Watching this video will help you write
write better Python programs for Raspberry Pi GPIO.
It is my experience that not enough attention is paid to what happens when a program exits.
As a result often GPIO pins remain active
after the program is terminated, leading to hard to trace, unexpected behaviour
of the Raspberry Pi
and even possible damage to the GPIO pins.
In this video I will demonstrate a number of problem scenarios around this subject
and provide practical solutions to minimize them.
Most of the solutions revolve around correct handling of various Linux operating system signals
which is applicable not just to the Raspberry Pi but programming with Python on Linux in general.
My examples use a very simple circuit
consisting of a resistor and an LED and I use the gpiozero library
but the same issues and solutions are applicable to other circuits and GPIO libraries.
Let's get started!
Have a look at my program.
All it does, it lights up the LED and then it waits for a signal.
Let's run it and see it in action.
The pause() statement in line 10 of the program will wait until the program receives a signal
which will tell the program what to do next.
As pause is the last line, it is assumed that the program will end once a signal is received.
The signal to terminate an interactive program under Linux
is keyboard interrupt otherwise known as SIGINT,
invoked by pressing the "Ctrl c" key sequence.
Notice what happens when I do that.
As you see, the program ends, LED turns off and Python displays a diagnostic traceback message.
The gpiozero library is sophisticated enough
that under normal circumstances simple components such as LEDs are
deactivated automatically and the associated GPIO pins are
reverted to default state.
The traceback message is useful in debugging but when a program is finished, it may be a good idea to turn it off.
This is controlled by amending the code to specify what happens when the
keyboard interrupt signal is received.
Once this is done, Python will not display the diagnostic message anymore.
Keyboard interrupt is handled through exceptions.
All you need to do is add a simple "try:" and "except..." structure to your program.
I typically use one large try block surrounding the main program section
and have one exception handler at the end.
Only a couple of lines of code need to be added...
Let's test that!
As you see, the LED is back on. Everything works as before,
but now when I press Ctrl c, I do not see the traceback message anymore.
When you are writing interactive programs, you should always designate a way
to normally turn off the program
that would be straightforward and follow a standard.
Using Ctrl c key sequence is a good way to implement that under Linux.
Of course there may be additional or different ways to terminate your programs,
depending on your specific situation.
You should also provide for abnormal program termination,
which would happen when your program is terminated through another signal.
For example, a program can be terminated using the kill command,
which sends the SIGTERM signal to the program.
Let's have a look at how this scenario would play out right now.
This time I will run my program in the background.
The syntax is similar to how I started my program before
with the addition of the & sign at the end.
That will run my signals.py program in the background.
As you see, the LED is on as before.
I could bring the program back into foreground
and use the Ctrl c keyboard shortcut to shut it down as before,
but this time I will use the kill command to terminate it.
As you see, the program has been terminated, but looking at the breadboard,
the LED remained on and that is a problem!
Without extra code to handle that signal, the program terminated immediately
without shutting down the active GPIO pin.
As much as it is within your control,
you should never allow your program to end that way,
with GPIO pins active.
I would like to emphasize that this is about the GPIO pins
and not about a specific component such as an LED.
I only use an LED because it is easy to see what is happening.
The same situation will arise regardless of what is connected to the GPIO port.
So, how would you prevent that from happening?
What you want here is to have the ability for your program to exit cleanly when killed
and shut down the same way as with the keyboard interrupt signal.
As it turns out, this cannot be handled with exceptions.
But there is a solution through the signal library
and it's not that difficult to implement.
Let me show you how to do that!
We need some additional imports from the signal library...
The SIGTERM signal is what we are attempting to handle.
You need to say up front in your program
what happens when a specific signal is received.
There is a callback function attached (you can pick any name you like),
which will handle that situation. This callback will only be executed
when the specific signal is received.
You could put any code you like inside of the safe_exit function,
including de-activation of the GPIO ports,
but generally such code would be better placed in a finally block
at the end of the program
which I will demonstrate at the end of this video.
All that is really needed within the handler function is an exit
and you can provide it with an exit code, for example 1.
The LED is still on from the last run, although my program isn't running anymore.
I need a way to shut it off before I start the program again.
to test the updated code.
This demonstrates the problem which we are trying to fix here right now.
I have a few options to reset the GPIO. I could use a couple of shell commands
or run a dedicated port reset program.
Since my circuit is very simple I can actually run my program again
and terminate it correctly using Ctrl c and deactivate the GPIO pin.
All this extra (and complicated) work will not be necessary
once the SIGTERM signal is handled correctly.
Let's start the program again in the background.
The LED is on. Now I will use the kill command
to terminate the program to see if it shuts off correctly.
There you have it!
Notice, I no longer have the "Terminated" message
and the LED has turned off correctly.
The kill command is capable of producing another signal - SIGKILL.
This signal is special in the way that it cannot be trapped or handled by a program.
Any program that receives it will terminate immediately
and we cannot add any code to prevent that.
The operating system is designed that way because it cannot have programs
that are impossible to terminate. We use this kind of signal as a last resort,
for example when a program stops responding.
Under these circumstances the pins would remain energized.
You would have to reset them manually or even restart the Raspberry Pi to fix that.
There is another signal which is easy
and worthwhile to handle,
received by a program when its parent process is terminated.
With interactive programs such as this one the parent is the shell.
Whenever a shell is terminated,
all programs running in it are terminated at the same time.
A normal process cannot exist without a parent process attached.
Let me demonstrate this scenario...
I'll start the program again.
Whether I start it in the foreground or background it doesn't matter.
Now I will terminate the shell by closing my Putty window.
I am connected remotely which means the shell will be closed,
my program will be terminated and I will get disconnected.
Putty gives me a warning. I choose to ignore it...
The shell and my program are terminated and I am disconnected.
As you can see, yet again, the LED remained on.
You can take my word for it that my Python program is no longer running.
Let me reconnect...
As with the previous signal, I need a way to deactivate the GPIO pins when the shell is closed.
A pin reset to default is needed, as working with the pins, whether connecting components or executing programs,
should always happen with the pins at there default state.
Just like before, I will simply re-run my program.
and terminate it through Ctrl c.
I would like to emphasize that this is not something I recommend.
This is just a temporary workaround
until proper shutdown procedures are in place.
You cannot rely on this method because you will have programs and circuits
which are a lot more complicated
and starting the program again and shutting off safely may not be an option.
nor it is typically recommended or safe to do so.
This only works for me because I only have a trivial circuit and program.
You should always provide a way for your programs to shut off
cleanly and automatically, without the need to perform manual steps,
possibly quite complicatedi, to reset GPIO pins.
You want to anticipate as much as possible and program that ahead of time.
This is what we are doing.
The signal produced when I've closed my Putty window is SIGHUP
and it can be handled in the same fashion as the standard kill signal.
In fact, I will reuse my safe exit function here.
Of course you can code a different solution for your specific situation.
Only small changes to the code are needed...
An extra import and an added line to handle the additional signal.
Let's test it!
As before, I will run my program and close the Putty window.
Notice that now the LED has turned off as soon as I got disconnected.
The GPIO pin used has been reset to default correctly.
One last thing!
I have mentioned earlier the finally block, which is used with exception handling.
The finally block can be added after all exceptions
to contain code to be executed under all circumstances,
including the three signals I talked about, as long as they are handled correctly
Code under finally will be always executed
and if you place the finally at the end of your program,
it is the perfect place for any GPIO cleanup and shutdown procedures.
Placing the shutdown procedure inside of finally
will be consistent, regardless of the way your program shuts off.
If I used any additional pins or any other components,
they would be deactivated here as well using appropriate code.
I hope you enjoyed this video and have learned something!
Please subscribe and thanks for watching!