RPi.GPIO basics 6 – Using inputs and outputs together with RPi.GPIO – pull-ups and pull-downs
Today, it’s time for us to combine inputs and outputs into the same script. It’s nothing scary and it’s not new either. It’s simply a case of doing what we’ve already done in the last two days’ of GPIO basics, but combining them.
To make it super-easy, we’ll even stick to the same port numbers we used for the last two days. We’re going to make a new program which takes parts from both the “read a button press” and the “flash an led every half second” programs.
We’re going to make a simple new program which switches the LED on when the button is pressed and switches it off again when the button is released. But before we do that…
Here’s a quick recap of inputs and outputs with RPi.GPIO
import RPi.GPIO as GPIO # import RPi.GPIO module GPIO.setmode(GPIO.BCM) # choose BCM or BOARD GPIO.setup(port_or_pin, GPIO.IN) # set a port/pin as an input GPIO.setup(port_or_pin, GPIO.OUT) # set a port/pin as an output GPIO.output(port_or_pin, 1) # set an output port/pin value to 1/HIGH/True GPIO.output(port_or_pin, 0) # set an output port/pin value to 0/LOW/False i = GPIO.input(port_or_pin) # read status of pin/port and assign to variable i
So now we need a combined circuit
This is simply a combination of the circuits from the last two exercises.
Here’s a simple Python program
This takes bits from each of the previous two programs.
import RPi.GPIO as GPIO from time import sleep # this lets us have a time delay (see line 15) GPIO.setmode(GPIO.BCM) # set up BCM GPIO numbering GPIO.setup(25, GPIO.IN) # set GPIO25 as input (button) GPIO.setup(24, GPIO.OUT) # set GPIO24 as an output (LED) try: while True: # this will carry on until you hit CTRL+C if GPIO.input(25): # if port 25 == 1 print "Port 25 is 1/HIGH/True - LED ON" GPIO.output(24, 1) # set port/pin value to 1/HIGH/True else: print "Port 25 is 0/LOW/False - LED OFF" GPIO.output(24, 0) # set port/pin value to 0/LOW/False sleep(0.1) # wait 0.1 seconds finally: # this block will run no matter how the try block exits GPIO.cleanup() # clean up after yourself
Every 0.1s, this program checks the button status…
- if pressed (input port 25 == 1), button status is displayed and the LED is switched on (output port 24 is set to 1)
- otherwise, if not pressed (input port 25 == 0), button status is displayed and the LED is switched off (output port 24 is set to 0)
- It keeps going until CTRL+C is pressed, then the ports are cleaned up before exit
Now YOU make it better
That’s the input/output (I/O) part sorted, now it’s over to you to improve the script. I can think of several improvements you could try to make.
- Fewer messages: Only display a message when the button status changes, rather than on every iteration of the loop.
- Light switch mode: Make it switch on the LED when the button is pressed and released once and leave it on until the button is pressed and released again. (You might need to investigate a little bit about “button debounce” if you run into difficulties)
- Flashing: Reinstate the flashing, while keeping the “on – off” light switch mode
Internal pull-ups and pull-downs
In the button circuit, we’re using resistors to “pull-down” the port. This gives it a “default” state of 0V (0, LOW, False). I mentioned in day 5 that we’d cover this in more detail, so that’s what we’re doing now.
Why are they necessary/useful?
If you have no pull-up or pull-down resistors attached to an input port, its status is not clearly defined. It is “floating”. It is susceptible to random electromagnetic radiation or static from you, from any devices near or far and from the environment. Any wires attached to the GPIO ports act as antennae for this radiation (it’s mostly radio waves).
So imagine the situation where you have a motor with a propellor on it, or one which controls Granny’s stairlift, which is controlled by a GPIO input port. If that port is susceptible to random changes of state, the propellor might spin when it shouldn’t and hurt someone. Or Granny might be sent back upstairs when she wants to be downstairs. It’s an out-of-control situation. You can easily get it under control by using pull-up or pull-down resistors, so that’s what we do.
What’s all this about internal ones then?
In our button circuit, we used resistors to pull down the voltage. This was to demonstrate the idea.
But, in fact, the Raspberry Pi has built-in pull-up and pull-down resistors which can be enabled in software. This means we can eliminate our pull-down resistors for the button – as long as we enable the internal ones.
How do you do that? RPi.GPIO to the rescue.
You enable these internal pull-ups/pull-downs at the time of setting up the port for input or output, by adding an extra, optional, argument to the GPIO.setup() function call.
We’re using pull-down, so it’s pull_up_down=GPIO.PUD_DOWN. If you want/need pull-up you can change the PUD_DOWN for PUD_UP. (It depends on how you’ve wired your circuit.)
GPIO.setup(port_or_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
How does that apply to our program?
We’ll need to change the circuit first, to this (you could also just remove the 10k resistor to GND from the previous circuit)…
…and then, once you’ve done that, line 4 needs changing to enable the pull-down on the button port (25).
Change line 4 from…
GPIO.setup(25, GPIO.IN) # set GPIO25 as input (button)
GPIO.setup(25, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # set GPIO25 as input (button)
The whole program should then look like this…
import RPi.GPIO as GPIO from time import sleep # this lets us have a time delay (see line 15) GPIO.setmode(GPIO.BCM) # set up BCM GPIO numbering GPIO.setup(25, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # set GPIO25 as input (button) GPIO.setup(24, GPIO.OUT) # set GPIO24 as an output (LED) try: while True: # this will carry on until you hit CTRL+C if GPIO.input(25): # if port 25 == 1 print "Port 25 is 1/HIGH/True - LED ON" GPIO.output(24, 1) # set port/pin value to 1/HIGH/True else: print "Port 25 is 0/LOW/False - LED OFF" GPIO.output(24, 0) # set port/pin value to 0/LOW/False sleep(0.1) # wait 0.1 seconds finally: # this block will run no matter how the try block exits GPIO.cleanup() # clean up after yourself
As you’ll see if you try it out, it works just the same as the previous version.
That was simultaneous inputs and outputs in RPi.GPIO, along with internal pull-ups/pull-downs
So now you know how to use inputs and outputs at the same time with RPi.GPIO in Python on the Raspberry Pi.
You also, hopefully understand a bit about pull-up and pull-down resistors and why they are used.
One final part to come
There’s just one more part to come on RPi.GPIO basics. It’ll be out in a day or two.
In the series so far, we’ve covered…
- How to check what RPi.GPIO version you have
- How to check what Pi board Revision you have
- How to Exit GPIO programs cleanly, avoid warnings and protect your Pi
- Setting up RPi.GPIO, numbering systems and inputs
- Setting up and using outputs with RPi.GPIO
- Using inputs and outputs at the same time with RPi.GPIO, and pull-ups/pull-downs
Part 7 can be found here…
RPi.GPIO cheat sheet
RasPiO® GPIO Reference Aids
Our sister site RasPiO has three really useful reference products for Raspberry Pi GPIO work...
[…] Alex continues his series on the GPIO by looking at using inputs and outputs together. His insight into granny stairlifts are particularly enlightening! Read the article here […]
Another great tutorial Alex! :-) Great to see someone going to the effort of explaining all the finer details (which usually get glossed over). Might even be worth suggesting to Ben Croston that he links to these in-depth tutorials from the RPi.GPIO wiki?
I might suggest removing the “Light switch mode: Make it switch on the LED when the button is pressed and released once and leave it on until the button is pressed and released again.” until you’ve covered button debouncing though, otherwise people will be getting very confused when their program doesn’t behave properly.
An alternative suggestion could be adding another LED to a different GPIO, and have one LED on while the other’s off and vice-versa.
Hmm – you might be right about the debounce. I think, rather than take it out, I’ll add a comment and link to where I covered debounce on the interrupts 2 tutorial. https://raspi.tv/2013/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio-part-2#bounce
I’ve just been given a Raspberry Pi, and am making first tentative steps to getting to grips with it (I date from the age of Algol and punched cards), and I wanted to thank you for your excellent series of tutorials. Very clear and logical. You have obviously put a lot of thought and hard work into them. There must be a book in there!
Thank you Harry. I expect there is. :)
[…]  – https://raspi.tv/2013/rpi-gpio-basics-6-using-inputs-and-outputs-together-with-rpi-gpio-pull-ups-and-… […]
[…] I completely forgot that the button on the HDMIPi driver board had its own pull-up resistor (explanation here) so I tried to use one of the i2c ports on the Pi. These have their own hardware pullups, so I […]
very good tutorial thanks
Just an FYI, this still doesn’t link to the last part – unless i completely missed it.. i used the sitemap though and found it
Side note: got anything planned for UART / serial stuff soon? – sorry I know it doesn’t belong here.
Thanks. FIxed. :)
No immediate plans for UART at the moment.
UART stuff is as simple as using http://www.raspberrypi.org/documentation/configuration/raspi-config.md to disable the default serial output, and then using picocom or pySerial to talk to /dev/ttyAMA0
(but don’t forget the serial port is also limited to 3V3 levels, just like the other GPIO pins, so can’t be connected directly to a RS232 port!)
If I do
GPIO.setup(24, GPIO.OUT, pull_up_down=GPIO.PUD_DOWN)
will then a current flow internally from the pin to ground even if it is not connected?
If I do
GPIO.setup(24, GPIO.OUT, pull_up_down=GPIO.PUD_UP)
will then a current flow internally from 3.3 V to the pin even if it is not connected?
Would these currents be 0.33 mA? It could be 5.61 mA for 17 GPIOs and 8.58 mA for 26 GPIOs, but then the internal current could be even higher for GPIO2 and GPIO3 because the internal pull-up is lower there: 1k8 corresponding to 1.83 mA per GPIO, i.e. in total 8.62 mA in the last case for 17 GPIOs, and 11.59 mA for 26 GPIOs.
Using pullups/downs with output ports is not usually done (although I believe it still works). I have slightly clarified the article to try and remove the ambiguity. There really shouldn’t be any need to use them on outputs because output ports are driven.
This is exactly my question. I have a continuous servo attached that for some reason starts spinning.
I’m guessing this is the floating state, hopefully this removes it.
Thanks for the explanation above, really clear.
Also is there any way to disable the power (5v from the rasp, pin 2) so the motor isn’t drawing any anymore?
Thanks in advance!
No it’s fixed. It’s physically connected to the 5V rail on the Pi.
If you’re using servos, you might want to try using https://gpiozero.readthedocs.io/en/stable/api_output.html#servo
If you want to “cut power” to the motor, you’d need to use a transistor or mosfet controlled by another GPIO pin. And I believe the recommendation is to always power a motor from an external power source, rather than taking 5V directly from the Pi, because if the motor suddenly takes a large current it may cause the Pi to brownout (reset). There’s a good example here https://raspi.tv/2013/how-to-use-soft-pwm-in-rpi-gpio-pt-2-led-dimming-and-motor-speed-control (but ignore the PWM stuff)
I think your code examples incorrectly reference Line 12 when explaining the SLEEP function – copied from the previous day’s example. Should this be ‘Line 15’ in this combined example?
Corrected. Thanks for spotting that :)
Hello, is it a good idea to connect 5V directly to GPIO?
As I read, GPIOs are mage for 3.3V, from long term perspection it could harm rPi.
But good tutorial anyway.
No it definitely isn’t.
If you need to connect a 5V signal to the Pi, you can use something like https://shop.pimoroni.com/products/sparkfun-logic-level-converter-bi-directional
If you want to apply a current from GPIO 24 to ground, shouldn’t the LED in the picture be reversed? Anode towards gpio and cathode towards and?
Hi Ola. That’s exactly how it is wired. The flat side of the LED is -ve (GND).
Thanks for the prompt reply…
But on your pic the long LED leg (anode) is not towards GPIO. This is the opposite of all other examples I have seen… Do a picture search on Google on:
“led example raspberry pi anode cathode”
In the real world you can often (but not always) use leg length as an indicator of anode/cathode, but you should not use that on cartoon-like Fritzing diagrams to determine LED polarity. If one leg is lengthened for the sake of the diagram, it does not suddenly make that leg the anode. Also, if both legs are pushed into the breadboard in parallel, they would be the same length, so how would you tell? (Actually, in Fritzing, you’d look at the leg with the bend at the top and that would be your anode.) You should ignore leg length in Fritzing diagrams. There is a more reliable way to tell…
Most (but not all) LEDs have a rounded side and a flat side at the point where the wires enter the resin casing (which you can also see in the Fritzing diagram). The flat side is always -ve.
This is shown on Fritzing and is 100% reliable.
Cool! You are right. Thanks for teaching me!
Wouldn’t be simpler to eliminate the pin 24 connection, tie the led to pin 25 in addition to the switch ?
pin 25 tied low
switch open, pin 25 low, led dark
switch closed, pin 25 high, led lit
You could do that with the 3v3 pin, but why bother with a Pi if you’ve going to do that?
The point here is to learn about inputs and outputs on the Pi. What you’re suggesting is a direct circuit that doesn’t even need a Pi.
Not intending to use pin 24 as an output, only a input, the tie low prevents the antenna afect, python program would be looking for the pin to go high, the switch provides the input, lighting the led is only a visual that the switch got closed, like in the use of a car bumper, bumping into something, the program would back the car away from the obstacle.
Yes – your way would work, but then would not meet the goal of this blog post which is to show how to use both inputs and outputs.
In computing and electronics there are usually lots of different and perfectly valid ways of doing things. You are perfectly correct. Your way would be simpler, but it wouldn’t be more ‘fit for purpose’ if you re-examine the title of this post.
It’s also worth bearing in mind that Alex is choosing a deliberately simple example here as a teaching exercise.
Once the switch is ‘readable’ by software, and the LED is ‘writable’ by software, you can do much more advanced things that would be difficult to do at a purely hardware level. e.g. press switch once to turn on LED, press switch again to turn off LED; or require the switch to be “double-clicked” to turn it on or off; or each time the switch is pressed turn the LED on for 10 seconds before automatically turning it off again; etc. etc.
This is great and I learned a lot. I was wondering if you can provide some insights as to how you can only display one message until the button status changes as opposed to every loop iteration.
I’d use a wait_for_edge interrupt as shown here…