Reader small image

You're reading from  Practical Python Programming for IoT

Product typeBook
Published inNov 2020
Reading LevelIntermediate
PublisherPackt
ISBN-139781838982461
Edition1st Edition
Languages
Right arrow
Lights, Indicators, and Displaying Information

In the previous chapter, we explored and learned how to use an optocoupler, transistor, and relay circuit and how these three components work together to create a common relay control module. We also covered how to measure the current usage of a load using a multimeter so that you can make an informed decision on what method or component should be used to switch or control an external load.

In this chapter, we will cover two alternative ways of making color with RGB LEDs and create a simple application to monitor your Raspberry Pi's CPU temperature and display the result on an OLED display. We will conclude by seeing how we can combine PWM and buzzers to create sound.

After you complete this chapter, you will have the knowledge, experience, and code examples that you can adapt to your own projects for those situations you need...

Technical requirements

To perform the exercises in this chapter, you will need the following:

  • Raspberry Pi 4 Model B
  • Raspbian OS Buster (with desktop and recommended software)
  • A minimum of Python version 3.5

These requirements are what the code examples in this book are based on. It's reasonable to expect that the code examples should work without modification on a Raspberry Pi 3 Model B or a different version of Raspbian OS as long as your Python version is 3.5 or higher.

You will find this chapter's source code in the chapter08 folder in the GitHub repository available here: https://github.com/PacktPublishing/Practical-Python-Programming-for-IoT.

You will need to execute the following commands in a Terminal to set up a virtual environment and install the Python libraries required for the code in this chapter:

$ cd chapter08               # Change into this chapter's folder
$ python3 -m venv venv # Create Python Virtual Environment
$ source venv...

Making color with an RGB LED and PWM

In this section, we will learn how to use Pulse-Width Modulation (PWM) together with an RGB LED to create different colors. As a reminder, PWM is a technique to create a variable voltage, which when applied to an LED and resistor pair can be used to change the brightness of an LED. We first discussed PWM and used it to change the brightness of an LED back in Chapter 2, Getting Started with Python and IoT. We then covered PWM in greater depth in Chapter 5, Connecting your Raspberry Pi to the Physical World.

An RGB LED is three single-color LEDs (red, green, and blue) in a single package, as illustrated in Figure 8.1:

Figure 8.1 – RGB LED varieties

You will notice that two types are shown:

  • Common Cathode: The red, green, and blue LEDs share a common cathode leg, meaning that the common leg is what connects to the negative or ground voltage source—cathode = negative.
  • Common Anode: The red, green, and blue LEDs share...

Creating the RGB LED circuit

In this section, we will create a simple circuit to control an RGB LED, and we will be using a common cathode RGB LED (that is, the three individual LEDs share a common GND connection).

We will start by building the circuit as shown in Figure 8.2 on our breadboard:

Figure 8.2 – Common cathode RGB LED schematic

Following is the accompanying breadboard layout for this schematic that we are about to build:

Figure 8.3 – Common cathode RGB LED circuit

Here are the steps to follow, which match the numbered black circles in Figure 8.3:

  1. Start by placing the RGB LED into your breadboard, taking care to orientate the LED regarding the positioning of its cathode leg.
  2. Position the 200Ω resistor (R1). One end of this resistor connects to the red leg of the LED.
  3. Position the first 15Ω resistor (R2). One end of this resistor connects to the blue leg of the LED.
  4. Position the second 15Ω resistor (R3). One end of this resistor...

Running and exploring the RGB LED code

Now that you have your circuit ready, let's run our example code. Our example will light up the LED and make it alternate different colors. Here are the steps to follow:

  1. Run the code in the chapter08/rgbled_common_cathode.py file and you should observe the RGB LED cycling colors. Take note of the first three colors, which should be red, green, and then blue.
To use a common anode RGB LED, it needs to be wired differently than shown in Figure 8.2the common anode leg must go to the +3.3V pin on your Raspberry Pi, while the GPIO connections remain the same. The other change is in code where we need to invert the PWM signals—you will find a file called rgbled_common_anode.py in the chapter08 folder with the differences commented.
  1. If your first three colors are not red, green, and then blue, your RGB LED may have its legs in a different order than the RGB LED's pictured in Figure 8.1 and the circuit...

Controlling a multi-color APA102 LED strip with SPI

The APA102 is an addressable multi-color (RGB) LED that is controlled using a Serial Peripheral Interface (SPI). In simplistic terms, we send instructions to the LED asking it what color to display rather than individually controlling each of the three red-green-blue legs of the LED using PWM as we did in the previous example.

If you need a quick refresher on SPI, we covered it back in Chapter 5, Connecting Your Raspberry Pi to the Physical World. We will also discuss SPI further the context of the APA102, the Raspberry Pi, and Python after we explore APA102 specific code shortly.

APA102 LEDs can also be connected or chained together to create LED strips or LED matrices to create dynamic and multi-LED lighting and display solutions. Irrespective of how the LEDs are arranged, we control them using a common technique where we send multiple sets of instructions to a chain of APA102 LEDs. Each individual LED consumes...

Creating the APA102 circuit

In this section, we will create our APA102 circuit, as shown in the following diagram. We will do this on our breadboard in two parts:

Figure 8.4 – APA102 LED strip circuit schematic

Let's get started on the first part, which will be to place the components and wire up the low-voltage side of a logic level converter:

Figure 8.5 – APA102 LED circuit (part 1 of 2)

Here are the steps to follow. The step numbers match the numbered black circles in Figure 8.5:

  1. Place the logic level converter (logic level shifter) into the breadboard, positioning the low-voltage side toward your Raspberry Pi. Different logic level converters may have different labeling, however, it should be clear which is the low-voltage side. In our illustration, one side has an LV (Low Voltage) terminal while the other has an HV (High Voltage) terminal, which distinguishes the sides.
  2. Connect the negative rails on the left-hand side and right-hand...

Powering the APA102 circuit

In Chapter 7, Turning Things On and Off, we discussed the importance of knowing the current requirements of a "load" that you are using. Let's apply that learning to our APA102 LED strip so we can power it correctly. Our example is assuming a LED strip containing 60 LEDs, however, you will need to adjust the calculations based on the number of LEDs on your strip.

By the way of example, we have the following:

  • An APA102 LED strip with 60 LEDs.
  • Each LED uses (on average) a maximum of 25mA (from the datasheet and confirmed by measurement).
  • The LED strip consumes approximately 15mA when idle (no LED is lit).
A single RGB LED uses its maximum current when it is set to the color white, which is when each individual LED (red, green, and blue) are at their full brightness.

Using the preceding values, we can calculate our expected maximum current requirement for 60 LEDs, which is just over 1.5 amps:

If we work in the assumption that we are using...

Configuring and running the APA102 LED strip code

Now that you have your circuit ready and our LED strip's expected current usage, let's configure and light up our LED strip:

  1. Edit the chapter08/apa102_led_strip.py file and look for the following line near the top of the file. Adjust the number to be the number of safe LEDs you calculated previously, or the number of LEDs on your strip if it had a suitably capable power supply:
NUM_LEDS = 60     # (2)
  1. Save your edits and run the code. If everything is connected correctly, you should observe the LEDs on the strip cycle through the colors red, green, and blue and then perform a few different light sequences.
If your LED strip is not working, check out the APA102 LED strip troubleshooting tips later in the section.

If your strip does not show red, green, and blue in that order, then you would need to adjust code to set the correct order—I'll show you where in the code you can adjust...

APA102 LED strip code walkthrough

Starting at line (1) in the following code, we have the imports. We will be using a Python deque collection instance (I'll just refer to is as an array for simplicity) to model in-memory the APA102 LED stripwe will build up and manipulate the order of colors we want each individual LED on to display in this array before applying it to the LED strip. We then import the getrgb function from the PIL library for working with color formats (as we did in the preceding RGB LED example):

# ...truncated...
from collections import deque # (1)
from PIL.ImageColor import getrgb
from luma.core.render import canvas
from luma.led_matrix.device import apa102
from luma.core.interface.serial import spi, bitbang

Lastly, the three luma imports are for the APA102 LED strip control. Luma is a mature high-level library for working with a range of common display devices using Python. It has support for LCDs, LED strips...

Discussion of APA102 and the SPI interface

If you cast your mind back to Chapter 5, Connecting Your Raspberry Pi to the Physical World, where we discussed Serial Peripheral Interface (SPI), you may remember that we mentioned it uses four wires for data transfer. However, if you consider our circuit in Figure 8.6, we're only using two wires (DI and CI), not four. What's going on?

Here is the SPI mapping for the APA102:

  • Master-Out-Slave-In (MOSI) on your Raspberry Pi connects to Data In (DI) on the APA102. Here, your Raspberry Pi is the master sending data to the slave APA102 LEDs on the strip.
  • Master-In-Slave-Out (MISO) is not connected because the APA102 does not need to send data back to the Raspberry Pi.
  • SCLK on your Raspberry Pi connect to the Clock In (CI) on the APA102.
  •  Client Enable/Slave Select (CE/SS) is not connected.

The last line CE/SS of importance and worthy of further discussion. A CE/SS channel is used by a master device to tell a specific...

APA102 LED strip troubleshooting tips

If you cannot get your APA102 to light up or if you find that random LEDs are not turning on or off or they are displaying unexpected colors or random flickers, try the following:

  • The APA102 needs 5-volt logic: Make sure you are using a logic level converter and that is connected the correct way aroundHV to 5 volts and LV to 3.3 volts.
  • Ensure that the DI/CI side of the APA102 is connected to the logic level converter.
  • Make sure your power source can supply enough current. As an example, under-supply of current or voltage can make white look more like red.
  • Make sure the ground of your power supply is connected to a ground pin on your Raspberry Pi.
  • If you are using big banging, move to hardware SPI.
  • If using the hardware SPI (that is, creating an instance of the spi() class), try the following:
    • If you are receiving the error SPI device not found, make sure SPI has been enabled in the Raspbian OS. We covered this in Chapter 1, Setting Up Your...

Using an OLED display

An OLED or Organic LED display is a type of technology used to make screens. Our example will be using an SSD1306, which is a monochrome 128x64 pixel display, however, the information will apply to other OLED displays too. 

Our sample program will read your Raspberry Pi's CPU temperature and display it on the OLED display together with a thermometer icon. We will be assuming the OLED will connect using an I2C interface, however, an SPI interface device should also be compatible if you use an spi() instance (like in the APA102 example) for the serial object. The ability to change the interacting method used by the Luma library means you can reuse existing code with compatible display devices with minimal code changes.

We will commence by connecting the OLED display to the Raspberry Pi and verifying that it is connected.

Connecting the OLED display

Let's connect your OLED display to your Raspberry Pi, as shown in Figure 8.7:

Figure 8.7 – I2C OLED display circuit
IMPORTANT NOTE ON POWERING YOUR OLED: Our circuit, shown in Figure 8.6, and the associated discussion uses a 5-volt power supply. If you consult the SSD1306 OLED datasheet mentioned at the beginning of this chapter, you will discover that it mentions a minimum supply voltage of 7 volts. Furthermore, you will find other sources and SSD1306 OLED modules that indicate different voltage requirements. Please consult the documentation or place of purchase to obtain the correct operating voltage for your OLED and adjust the supply voltage as required (steps 7 and 8 in the following list).

You can connect the OLED with the following steps, which correspond to the numbered black circles in Figure 8.7:

  1. Connect the negative rails on the left-hand side and right-hand side power rails.
  2. Connect the SDA1 (Data) pin of your Raspberry...

Verifying whether the OLED display is connected

Previously, in Chapter 5, Connecting Your Raspberry Pi to the Physical World, we used the i2cdetect command-line tool to check whether an I2C device was connected and to verify its I2C address. Check that your Raspberry Pi can see your OLED display by running the following in a Terminal:

$ i2cdetect -y 1

If your OLED is connected, you will see the following output, which tells us that the OLED was detected and has the hex address, 0x3C:

# ...truncated...
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
# ...truncated...

If your address is different, that's okay, we just need to adjust the address in code which we will do next.

Configuring and running the OLED example

The code we are about to explore is contained in the chapter08/oled_cpu_temp.py file. Please review this file to get an overall view of what it contains before continuing:

  1. If the OLED I2C address you obtained in the preceding was different to 0x3C, find the following line in the source code and update the address parameter to match your OLED I2C address:
serial = i2c(port=1, address=0x3C)
  1. Run the program, and you should observe the CPU temperature and a thermometer icon drawn on the OLED display.

Once you have configured your OLED display address in code and confirmed the example works on your OLED, we are ready to review the code and learn how it works.

OLED code walkthrough

Commencing with the imports, in line (1), we import classes from the PIL (Pillow) module, which we use to create the image we want to render on the OLED display. We also import several other classes from the Luma module related to our SSD1306 OLED and its I2C interface (SPI is also imported for reference).

We see how to create an I2C instance in line (2) representing the interface that our OLED is connected to. Commented out is an SPI alternative. In line (3), we create an instance of ssd1306 that represents our OLED display and assign it to the device variable. If you are using a different OLED display than the SSD1306, you will need to identify and adjust the ssd1306 import line, and the device instance created in line (3):

from PIL import Image, ImageDraw, ImageFont         # (1)
from luma.core.interface.serial import i2c, spi
from luma.core.render import canvas
from luma.oled.device import ssd1306
#...truncated...

# OLED display is using I2C at address 0x3C...

Making sound with buzzers and PWM

In the final section of this chapter, we will walk through an example of how to make simple sound and music with PWM. Our sample program is going to play a musical scale on the buzzer, and we will be using a music score format called Ring Tone Text Transfer Language (RTTTL), which was developed by Nokia in the pre-smartphone era for creating ringtones. As we learn, we can use a simple Python library to parse an RTTTL music score and turn its notes into a PWM frequency and duration that can then be used to associate a buzzer to create an auditable tune.

To make a sound with PWM, we need a form of a speaker, and we will be using what is known as a passive buzzer. Buzzers come in two basic forms:

  • Active buzzers: These buzzers contain an internal oscillator that generates a single set tone. All you need to do us apply a DC voltage to an active buzzer and it will make a noise.
  • Passive buzzers: These do not contain any internal smarts to make...

Building the RTTTL circuit

In this section, we will be building a circuit to drive a passive buzzer. This circuit, shown in Figure 8.8 is very similar to the MOSFET circuit that we covered in Chapter 7, Turning Things On and Off, only this time with a buzzer connected as the load:

Figure 8.8 – Buzzer driver circuit Schematic

We will start our circuit build by placing the components onto our breadboard:

Figure 8.9 – Buzzer driver circuit (part 1 of 2)

The following step numbers match the numbered black circles in Figure 8.9:

  1. Place the MOSFET onto the breadboard, paying attention to the orientation of the component with regards to the legs. Please see Figure 7.7 in Chapter 7Turning Things On and Off, if you need help to identify the MOSFET's legs.
  2. Place the 100kΩ resistor (R2) into your breadboard. One end of this resistor shares the same row as the MOSFET's Gate (G) leg.
  3. Place the 1kΩ resistor (R1) into your breadboard....

Running the RTTTL music example

Run the code in the chapter08/passive_buzzer_rtttl.py file, and your buzzer will play a simple musical scale.

The code to perform this is quite simple. In line (1) in the following code, we are using the rtttl module to parse an RTTTL music score into a series of notes defined by frequency and duration. Our score is stored in the rtttl_score variable:

from rtttl import parse_rtttl
rtttl_score = parse_rtttl("Scale:d=4,o=4,b=125:8a,8b, # (1)
8c#,8d,8e,8f#,8g#,8f#,8e,8d,8c#,8b,8a"
)

Next, in line (2), we loop through the parsed notes in rtttl_score and extract the frequency and duration:

    for note in rtttl_score['notes']:                        # (2)
frequency = int(note['frequency'])
duration = note['duration'] # Milliseconds
pi.hardware_PWM(BUZZER_GPIO, frequency, duty_cycle) # (3)
sleep(duration/1000) # (4)

In line (3), we set the frequency...

Summary

In this chapter, we learned how to use PWM to set the color of an RGB LED and that a standalone single RGB LED requires three dedicated GPIO pins to work—one for each of the colors, red, green, and blue. We then explored another type of RGB LED, the APA102, which is a 2-wire SPI controllable device that can be chained together to create LED lighting strips. Next, we learned how to use an OLED display by creating an example application that displayed your Raspberry Pi's CPU temperature. We concluded with an example of using PWM together with a passive buzzer to make sound by parsing an RTTTL music score.

What you have learned in this chapter will allow you to add visual and auditable feedback to your own projects. You will also be able to extend your learning to other types of displays with relative ease, as the Luma library we have used is capable of working with a range of other display types and models in addition to the APA102 LED strip and SSD1306 ...

Questions

As we conclude, here is a list of questions for you to test your knowledge regarding this chapter's material. You will find the answers in the Assessments section of the book:

  1. Your APA102 LED strip is set to show all LEDs as white, but instead, all of the LEDs look reddish. What could be the problem?
  2. What limitation does the APA102 place on SPI?
  3. Your APA102 does not work when you use a logic level converter but appears to work when you connect it directly to the MOSI and SCK pins on your Raspberry Pi (hence bypassing the logic level converter). What are some possible causes of the problem?
  4. What is the basic process for creating and displaying an image on an OLED display using the Luma OLED library?
  5. What is RTTTL?

Further reading

An APA102 is a good choice to commence your learning on lower level data protocol and communication. After reviewing the APA102 datasheet for its data protocol (see the link under Technical requirements at the start of this chapter), the next logical step is to review some lower-level code. The APA102 example for PiGPIO is a one such starting point, but you'll find others on PyPi.org:

The Luma suite of libraries offers many high-level modules for integrating common display with a Raspberry Pi beyond the APA102 and SSD1306 OLED we covered in this chapter. Furthermore, Luma contains an extensive range of examples:

Luma uses a PIL (Python Imaging Library)/Pillow comparable API for drawing and manipulating displays. We specifically used ImageDraw...

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Practical Python Programming for IoT
Published in: Nov 2020Publisher: PacktISBN-13: 9781838982461
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime