The Complete Magazine on Open Source

How to begin with Raspberry Pi GPIO programming using Python

This article uses the RPi.GPIO Python package to introduce Raspberry Pi GPIO programming. The GPIO package bundled with Raspbian is aimed at Raspberry Pi beginners who are familiar with Python and interested in designing IoT products.

Raspberry Pi is a sensational single-board computer (SBC) and development board, which is heavily used for the prototyping of IoT based products. It is the best-selling British computer ever, with more than 10 million pieces sold already. Contrary to common belief, Raspberry Pi is not entirely open source. Yet, it has been used in many open source projects. In this article, we will learn to use it as a development board. For all the programs and circuits discussed here, I have used Raspberry Pi 3. Unless explicitly specified otherwise, from now onwards, the name Pi refers to Raspberry Pi 2 Model B, 3 Model B or Zero.

Raspberry Pi pinout

What makes Raspberry Pi suitable for making IoT projects is its 40-pin expansion header. Pins are arranged in a 2×20 fashion. Figure 1 is a pinout diagram, taken from Wikipedia.

Numbering systems

Raspberry Pi pins are numbered in two different ways— physical numbering and Broadcom numbering (BCM). In the first case, the pins are numbered sequentially from one to 40. In the figure, this is shown as Pin#. And the image represents what is seen when the Pi is held with the USB ports facing downwards. That is, Pin 2 is the pin in the corner. The Broadcom numbering system is the default option for the SoC (System-on-Chip). Raspberry Pi uses a SoC developed by Broadcom Limited. Raspberry Pi 2 and Zero use BCM2836 and BCM2835, while the Pi 2 version 1.2 and 3 use BCM2837. This is also known as GPIO numbering and is shown as GPIO# in the figure.

As you have probably guessed already, all the pins are not programmable. There are eight ground pins and two +5V pins and three +3.3V pins, which are not programmable. There are other dedicated pins too. Most of the pins have alternative functions, as shown in the figure. A majority of the pins are directly connected to the SoC; so while connecting circuits or components, one should be careful to avoid wrong wiring and short circuits. It is always good to have a descriptive pinout diagram printed out for quick reference as well as a multimeter on the work desk.

Figure 1: Raspberry Pi pinout diagram

Programming the pins

Armed with some understanding about the pins, let us move to programming. The Python package used for Raspberry Pi GPIO programming is RPi.GPIO. It is already installed in Raspbian, the default operating system for Pi.
If you are using any other operating system, the package can be installed by using the following command:

$ sudo pip install RPi.GPIO

Now, fire up IDLE or the Python console and type the following commands:

import RPi.GPIO as GPIO
print(GPIO.RPI_INFO)

You will get some basic information about your Pi printed as the output. So the ‘Hello world’ of GPIO programming is done!

The GPIO programming workflow

Let us discuss the steps involved in writing a GPIO Python script, in general.

Step 1: Import the RPi.GPIO package.

Step 2: Set the numbering style to be used. We use the method GPIO.setmode() for this. It takes either GPIO.BOARD or GPIO.BCM as the parameter. GPIO.BOARD stands for physical numbering and GPIO.BCM stands for Broadcom numbering. In order to keep things simple, we will use only GPIO.BOARD here.

Step 3: Set up the necessary input and output pins.

Step 4: Read inputs and give outputs.

(Steps 3 and 4 are extensively explained in the following sections.)

Step 5: Clean up GPIO and exit.

You must clean up the pin set-ups before your program exits otherwise those pin settings will persist, and that might cause trouble when you use the same pins in another program. The Pi ‘expresses its displeasure’ with a warning. To clean up the entire set of pins, invoke GPIO.cleanup(). If you want only a few pins to be cleaned up, then the pin numbers should be provided as GPIO.cleanup(channel_list). Anyway, you can suppress the warning messages by calling GPIO.setwarnings(False).

Figure 2: Circuit 1

Setting up an output pin

To set a pin as an output pin, we have to call the method GPIO.setup(pin, GPIO.OUT). Here, pin is the pin number of the pin. You can set the initial state to be ON(1) or OFF(0) by using the initial parameter. So, to set Pin 8 as an output pin with the initial state as OFF, use the following code:

import RPi.GPIO as GPIO 
GPIO.setmode(GPIO.BOARD)
mypin = 8
GPIO.setup(mypin, GPIO.OUT, initial = 0)

Now, to change the state of the pin, type:

GPIO.output(pin, state)

The value of state can be either 1 or GPIO.HIGH or True for the ON state and 0 or GPIO.LOW or False for OFF.

Figure 3: Circuit 2

Practice circuit 1

For all the practice circuits, use a breadboard and jumper wires.

Now, take a regular 5mm LED, connect the anode (long leg) to any GPIO pin, say Pin 8, using a series resistor of 270Ω. Connect the cathode (short leg) to any ground pin, say Pin 6.

The series resistor makes sure that the LED will not draw too much current. The GPIO pins provide 3.3V, with the maximum permitted current draw of 50mA. Roughly speaking, for 3v3, any resistor less than or equal to 1K can be used as the series resistor, and lower the resistance to the appropriate value if the LED is too dim. The exact value can be calculated using the formula:

Resistor value = ( 3.3 – VLED ) / ILED.

After the connections have been made properly, power up the Pi and open IDLE or any text editor. Then type the following code:
import RPi.GPIO as GPIO
#for the sleep method
import time
led = 8
#set numbering mode for the program 
GPIO.setmode(GPIO.BOARD)
#setup led(pin 8) as output pin
GPIO.setup(led, GPIO.OUT,initial=0)
try:
#turn on and off the led in intervals of 1 second
while(True):
#turn on, set as HIGH or 1
GPIO.output(led,GPIO.HIGH)
print(“ON”)
time.sleep(1)
#turn off, set as LOW or 0
GPIO.output(led, GPIO.LOW)
print(“OFF”)
time.sleep(1)
except KeyboardInterrupt:
#cleanup GPIO settings before exiting
GPIO.cleanup()
print(“Exiting...”)

This code will print ON and OFF alternatively on the screen, in sync with when the LED is turned on and off. The Ctrl+C key combination can be used to terminate the execution of the program. The except KeyboardInterrupt: mechanism is used to detect the Ctrl+C keypress. The Sleep method will make the process wait for the given amount of time, which is one second here.

Setting up an input pin

Just like a ground pin was used to complete the circuit of the output pin, the circuit of an input pin should be completed using a ground pin or 3v3 pin. A lone input pin in a circuit is said to be ‘floating’. Since its voltage can be of any value between 0 and 3.3V, it cannot be used. That should be avoided by using a 3v3 pin or a ground pin and an in-built pull up or pull down resistor.

A pin can be set as input as follows:

GPIO.setup(channel, GPIO.IN, PUD)

Here, the channel is the pin number. PUD can be either GPIO.PUD_DOWN or GPIO.PUD_UP. It tells you whether to use the inbuilt pull up or the pull down resistor. If the 3v3 pin is used, we have to use GPIO.PUD_DOWN (pull down resistor), and for the ground pin, we need to use GPIO.PUD_UP (pull up resistor). The choice of 3v3 or ground is up to you. The only difference is the value read from an open circuit and closed circuit. For the 3v3 pin (GPIO.PUD_DOWN), the input value of the open circuit will be 0 and of a closed circuit will be 1. For a ground pin (GPIO.PUD_UP), open circuit is 1 and closed circuit is 0.

The value can be read from an input pin using the method GPIO.input(channel), usually read into a variable.

Practice circuit 2

Fix a two-legged button switch on the breadboard. Now, connect one leg of the switch to any GPIO pin, say Pin 7, and connect the other to any 3.3V pin.

#!/usr/bin/python3
import RPi.GPIO as GPIO
import time

cnl = 7
GPIO.setmode(GPIO.BOARD)

# PIN 7 AND 3.3V
# normally 0 when connected 1
GPIO.setup(cnl, GPIO.IN, GPIO.PUD_DOWN)
try:
while(True):
print(GPIO.input(cnl))
time.sleep(1)
except KeyboardInterrupt:
GPIO.cleanup()
print(“Exiting”)

When this program is run, it will print 0 continuously, and when the switch is pressed down (when the circuit is closed) the output will be 1.

Now change the circuit a little. Remove the connection from the 3v3 pin and connect it to any ground pin. Change the program by replacing GPIO.PUD_DOWN with GPIO.PUD_UP. Now run the program, and 1 will be printed continuously. When the switch is pressed down, the output will be 0.

Interrupts

There is a problem with simple input pins, which is that the programmer has to constantly check the input data. To overcome this, we have interrupts known as ‘event-detect’ and a pin can be made to listen to events by calling:

GPIO.add_event_detect(channel, event, callback = my_callback, bouncetime = timeinmilliseconds)

Channel should be an input pin. The various events available are GPIO.RISING for the rising edge (when the signal changes to HIGH from LOW), GPIO.FALLING for the falling edge (when the signal changes from HIGH to LOW), and GPIO.BOTH for both rising and falling edges. The my_callback method contains the interrupt handling code, and will be executed as a separate thread. bouncetime is the minimum amount of time required between two events. If two events occur in succession, within the bouncetime limit, then the second event will be ignored.

Practice circuit 3

This is just a combination of the previous circuits, without any change. The program will be changed in a way that when the switch is pressed, it will toggle the state of the LED.

#!/usr/bin/python3
import RPi.GPIO as GPIO
import time
led = 8
ledstate = True

#this method will be invoked when the event occurs
def switchledstate(channel):
global ledstate
global led
ledstate = not(ledstate)
GPIO.output(led,ledstate)

swtch = 7
GPIO.setmode(GPIO.BOARD)
GPIO.setup(swtch,GPIO.IN, GPIO.PUD_DOWN)
GPIO.setup(led,GPIO.OUT,initial=ledstate)
# adding event detect to the switch pin
GPIO.add_event_detect(swtch, GPIO.BOTH, switchledstate, 600)
try:
while(True):
#to avoid 100% CPU usage
time.sleep(1)
except KeyboardInterrupt:
#cleanup GPIO settings before exiting
GPIO.cleanup()

Pulse width modulation for analogue output

Pulse width modulation is a means to generate analogue signals from digital signal sources. In other words, PWM can fake analogue signals. PWM has two main characteristics – duty cycle and frequency. The duty cycle is the amount of time a signal is in the high state in one cycle. It is expressed in terms of a per cent measure. The frequency determines how fast the PWM completes a cycle. When the state of a digital signal toggles rapidly with changing duty cycle values, we feel that we get analogue signals.

There are two PWM pins in Raspberry Pi—PWM0 and PWM1. The onboard analogue audio output uses both PWM channels. So it is advised not to use both simultaneously. In any case, the RPi.GPIO package facilitates only software PWM, which can be implemented on any GPIO pin. For this, the desired pin should be set as an output pin first. Then use:

pwm = GPIO.PWM(channel, frequency)

…where channel is the output pin and frequency is the frequency of your choice. Then start pwm with pwm.start(dutycycle) (the duty cycle value should be provided here). The duty cycle value can be changed using pwm.ChangeDutyCycle(dutycycle) and the frequency can be changed using pwm.ChangeFrequency(frequency). At the end, stop pwm by using pwm.stop().

Practice circuit 4

Let’s brighten and dim an LED. The circuit is the same as Practice circuit 1.

import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
led=8
GPIO.setup(led, GPIO.OUT)
#starting with frequency 100
pwm = GPIO.PWM(led, 100)
#stating with 0, that is off state
pwm.start(0)
try:
while 1:
#duty cycle from 0% to 100%
for dc in range(0, 101, 5):
pwm.ChangeDutyCycle(dc)
time.sleep(0.1)
#duty cycle from 100% to 0%
for dc in range(100, -1, -5):
pwm.ChangeDutyCycle(dc)
time.sleep(0.1)
except KeyboardInterrupt:
pwm.stop()
GPIO.cleanup()

This article would not be complete without mentioning analog inputs. Since Raspberry Pi is a digital device without any built-in ADC (analogue-to-digital converter), reading analogue data from sensors or circuits requires an external ADC. The commonly used ADC is MCP3008. Discussing this ADC and its SPI communication protocol is beyond the scope of this article. Raspberry Pi supports SPI, I2C and UART protocols. I have found UART with Arduino as the ADC to be the simplest option of the three.

RPi.GPIO is just one package for GPIO programming. There are other packages like gpiozero available. Have fun making things!