LanguagesPythonUsing Python for Basic Raspberry Pi Electronic Controls

Using Python for Basic Raspberry Pi Electronic Controls

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

In this second part of our embedded programming tutorial, we will be learning how to use Python and Python libraries to work with electronic components and the Raspberry Pi. We will start by looking at GPIO output and move on from there.

Before moving on, make sure you have read the first part in this embedded programming guide: Python Basic Electronics Control with the Raspberry Pi.

Python and GPIO Output with Raspberry Pi

Python provides a module exclusive to the Raspberry Pi OS that enables direct communication to and from the Raspberry Pi’s GPIO header. This demonstration presumes that Python 3 is installed on the Raspberry Pi, and the code used here runs on Python 3.9.2:

$ pip3 install gpiozero

# If pip3 is not accessible in the current path.
$ /usr/bin/pip3 install gpiozero    

Below is a basic program which will light up all of the LEDs for 1 and 2 seconds, with a pause in-between, written in Python using the GPIO library:

# demo.py
import sys

import RPi.GPIO as GPIO

from time import sleep

def main(argv):
	# Suppresses warning messages from output.
	GPIO.setwarnings(False)
	
	# This tells the library to use the GPIO numbers and not the
	# header pin numbers. If you want to use the pin numbers in
	# the code below, use GPIO.setmode(GPIO.BOARD)
	GPIO.setmode(GPIO.BCM)

	# Sets up GPIO 25 (Pin 22 as an output) 
	GPIO.setup(25, GPIO.OUT)

	# Sets GPIO 25 to active (a 3.3v DC output)
	GPIO.output(25, True)
	sleep(1)

	# Deactivates GPIO 25 (sets to 0v DC output)
	GPIO.output(25, False)
	sleep(1)
	GPIO.output(25, True)
	sleep(2)

	# You must turn off the GPIO before exiting or it will stay on.
	GPIO.output(25, False)

	# Standard cleanup routines for GPIO Library
	GPIO.cleanup()

	return 0

if __name__ == "__main__":
	main(sys.argv[1:])
   

Listing 1 - Lighting up all 3 LEDs at a time.

Note, always run the GPIO.cleanup() before exiting. This will ensure that all GPIO header pins used during this program are properly reset.

Danger warning: Depending on the quality of the components used, you may run into smoking components if you keep these LEDs on for more than 2-3 seconds.

Read: Using a Raspberry Pi Device as an OpenVPN Server

Randomized LEDs with Python and Raspberry Pi

The following example makes use of the following buildout. This buildout features the 3 LEDs, but they are on independent circuits and are each connected to a separate relay. The relays are then connected to other GPIO Header pins. Note that in the diagram below, wiring and connections not related to the assembly (e.g., power, video, networking, keyboard, mouse, etc.) have been covered up in order to reduce distraction from the design.

Raspberry Pi Relay Leads

Figure 1 – 3 LEDs with 3 Separate Relays

Below is a closeup of the connections to the GPIO Header:

Raspberry Pi GPIO Header Connections

Figure 2 – GPIO Header with Separate Connections

Below is a closeup of the 3 relays and their connections. Note how the power and ground for the relays are shared, but the controlling connections from the GPIO Header are separated. The wires are color-coded individually. The red +3.3 Volts DC connections for the top 2 relays are sharing the bottom DC+ terminal. The black Ground connections for the top 2 relays are sharing the middle DC- terminal.

Relay Connections Raspberry Pi

Figure 3 – The 3 Relay Connections

Lastly, below is a closeup of the new circuit:

Raspberry Pi and LED Connections

Figure 4 – Individual LED Connections

The following Python code example will light up each of the LEDs at random intervals:

# demo2.py
import sys

import RPi.GPIO as GPIO

import datetime

from time import sleep

import random

def main(argv):
    # Suppresses warning messages from output.
    GPIO.setwarnings(False)
    
    # This tells the library to use the GPIO numbers and not the
    # header pin numbers. If you want to use the pin numbers in
    # the code below, use GPIO.setmode(GPIO.BOARD)
    GPIO.setmode(GPIO.BCM)

    # Sets up GPIO 25 (Pin 22 as an output) 
    GPIO.setup(25, GPIO.OUT)

    # Sets up GPIO 13 (Pin 33 as an output)
    GPIO.setup(13, GPIO.OUT)

    # Sets up GPIO 26 (Pin 37 as an output)
    GPIO.setup(26, GPIO.OUT)

    # Turn off everything just to be safe:
    GPIO.output(25, False)
    GPIO.output(13, False)
    GPIO.output(26, False)

    startTime = datetime.datetime.now()
    endTime = startTime + datetime.timedelta(seconds=20)
    # Use the following tracking variables to tell us when to turn
    # each LED off in the loop.
    gpio25OffTime = startTime
    gpio13OffTime = startTime
    gpio26OffTime = startTime

    # Use the following tracking variables to tell us when the last
    # time we turned on each output was. To make things easy, set
    # this to some time in the past.
    gpio25OnTime = startTime - datetime.timedelta(seconds=10)
    gpio13OnTime = startTime - datetime.timedelta(seconds=10)
    gpio26OnTime = startTime - datetime.timedelta(seconds=10)

    # Create an array from which we will randomly choose a GPIO:
    outputList = [25, 13, 26]

    while datetime.datetime.now() <= endTime:
        # Pick a random GPIO output:
        chosenOutput = random.sample(outputList, 1)[0]

        # Now get the last time the GPIO was switched on:
        lastSwitchedOnTime = datetime.datetime.now()
        if 25 == chosenOutput:
            lastSwitchedOnTime = gpio25OnTime
            print ("GPIO 25 Last on at [" + str(lastSwitchedOnTime) + "]")
        elif 13 == chosenOutput:
            lastSwitchedOnTime = gpio13OnTime
            print ("GPIO 13 Last on at [" + str(lastSwitchedOnTime) + "]")
        elif 26 == chosenOutput:
            lastSwitchedOnTime = gpio26OnTime
            print ("GPIO 26 Last on at [" + str(lastSwitchedOnTime) + "]")


        # Get the current state of the output. If it is on, or if the LED
        # was on not that long ago, do nothing.
        currentOutputState = GPIO.input(chosenOutput)
        startingDT = datetime.datetime.now()
        if (not currentOutputState) and (lastSwitchedOnTime < startingDT - datetime.timedelta(seconds=1.5)):
            GPIO.output(chosenOutput, True)
            # As Python lacks a switch/case statement, must use if-elif...
            if 25 == chosenOutput:
                gpio25OnTime = startingDT
                gpio25OffTime = startingDT + datetime.timedelta(seconds=1.5)
                print ("Turned on GPIO 25 at [" + str(gpio25OnTime) + "] Must turn off at [" + str(gpio25OffTime) + "]")
            elif 13 == chosenOutput:
                gpio13OnTime = startingDT
                gpio13OffTime = startingDT + datetime.timedelta(seconds=1.5)
                print ("Turned on GPIO 13 at [" + str(gpio13OnTime) + "] Must turn off at [" + str(gpio13OffTime) + "]")
            elif 26 == chosenOutput:
                gpio26OnTime = startingDT
                gpio26OffTime = startingDT + datetime.timedelta(seconds=1.5)
                print ("Turned on GPIO 26 at [" + str(gpio26OnTime) + "] Must turn off at [" + str(gpio26OffTime) + "]")

            # Needed because all 3 LEDs will turn on and stay on because of how
            # fast the loop iterates.
            sleep(0.5)

        # Turn off anything that needs to be turned off. Note that this
        # will happen on a different looping iteration than the previous
        # block of statements. Not using if... elif here because we want
        # all these conditionals to be evaluated. 
        # As with turning on the outputs, a forced delay must be applied
        # because of how fast the loop iterates. All outputs will never
        # have any time to be off otherwise.
        endingDT = datetime.datetime.now()
        if gpio25OffTime <= endingDT:
            print ("Must turn off GPIO 25")
            GPIO.output(25, False)
            sleep(0.5)
        if gpio13OffTime <= endingDT:
            print ("Must turn off GPIO 13")
            GPIO.output(13, False)
            sleep(0.5)
        if gpio26OffTime <= endingDT:
            print ("Must turn off GPIO 26")
            GPIO.output(26, False)
            sleep(0.5)

    # You must turn off the GPIO before exiting or it will stay on.
    GPIO.output(25, False)
    GPIO.output(13, False)
    GPIO.output(26, False)

    # Standard cleanup routines for GPIO Library
    GPIO.cleanup()
    return 0

if __name__ == "__main__":
    main(sys.argv[1:])
  

Listing 2 - Lighting up all 3 LEDs at a time.

A few important things to note. First, electronic components such as relays, cannot and do not move as fast as a computer. If there were no calls to the sleep command in the code above, all of the LEDs would stay on for as long as the program was running, simply because “all” of them would end up being chose after any number of loop iterations. Second, this code example DOES NOT provide exception handling for the purposes of canceling execution via Ctrl-C. Any exception handling will need to ensure that GPIO.cleanup() is called.

Read: CUPS and RaspBerry Pi AirPrinting

GPIO Input in Python: Example

Just as the Raspberry Pi raises the output voltage on a header pin to 3.3 Volts DC, it can also detect when an external source has applied 3.3 Volts DC to a header pin. The diagram below shows two momentary switches connected to GPIO Headers 13 and 26. In this case, the 3.3 Volts DC input source for each switch comes from the Raspberry Pi device itself. Pins 1 and 17 both provide “always on” 3.3 Volts DC and will never exceed the current limitations of the Raspberry Pi.

Raspberry Pi GPIO Electrical Switches

Figure 5 – Electrical Switches Connected to GPIO Header

Below is a closeup of the GPIO Header Connections:

GPIO Header Connections

Figure 6 – GPIO Connections for Electrical Switches

In the above photo, the connections for Pins 20 and 22, which are Ground and GPIO 25 respectively, are retained but they are not used here. However, if you wish to test the voltage level of a GPIO Header Pin, you will need to connect the ground side of the testing circuit to the Ground pin.

Note that reading input from the GPIO Header requires slightly more setup in the Python code than generating output. This is because the Raspberry Pi internally sets GPIO pins to read as being activated even when there is no load applied to them. This process is known as “GPIO pull up.” This can be temporarily corrected at the start of the code, but it is vital that the GPIO.cleanup() command be invoked to reset the pull up back to its original state. If this is not done, then the next execution of the program, or another program which reads GPIO input, may not work correctly.

# demo3.py
import sys

import RPi.GPIO as GPIO

import datetime

from time import sleep

import random

def main(argv):
    # Suppresses warning messages from output.
    GPIO.setwarnings(False)
    
    # This tells the library to use the GPIO numbers and not the
    # header pin numbers. If you want to use the pin numbers in
    # the code below, use GPIO.setmode(GPIO.BOARD)
    GPIO.setmode(GPIO.BCM)

    # Sets up GPIO 13 (Pin 33 as an input and pulls down the default high reading to low)
    GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

    # Sets up GPIO 26 (Pin 37 as an input and pulls down the default high reading to low)
    GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

    startTime = datetime.datetime.now()
    endTime = startTime + datetime.timedelta(seconds=20)

    while datetime.datetime.now() <= endTime:
        gpio13State = GPIO.input(13)
        gpio26State = GPIO.input(26)

        if gpio13State:
            print ("GPIO 13 is active at [" + str(datetime.datetime.now()) + "]")

        if gpio26State:
            print ("GPIO 26 is active at [" + str(datetime.datetime.now()) + "]")

    GPIO.cleanup()
    return 0

if __name__ == "__main__":
    main(sys.argv[1:])
    

Listing 3 - Reading GPIO Input

The messages are printed as the momentary switches are held down. Once the information is programmatically available, it can be used or manipulated in any manner necessary for the successful operation of a larger system.

Danger warning: The maximum input current for the Raspberry Pi GPIO Header can vary from 0.25 mA to 0.5 mA. Do not exceed this level or else the Raspberry Pi may be damaged.

Also note, this example DOES NOT provide exception handling for the purposes of canceling execution via Ctrl-C. Any exception handling will need to ensure that GPIO.cleanup() is called.

Final Thoughts on Raspberry Pi Programming with Python

This Python embedded programming tutorial series does not even begin to scratch the surface of what the Raspberry Pi can do in the role of a microcontroller. Through the use of simple electronic devices such as relays, switches and resistors, nearly any physical device that uses electrical power can be modified to be controllable via Python code on the Raspberry Pi. This can range to simple examples like robotic arms, to more complex systems like alarm systems or other factory machinery which depend on numerous interdependent components. Custom Python code can serve as an orchestrator for any system that depends on inputs or outputs of external electronic components.

Read more Python programming and software development tutorials.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories