Displays and Other Outputs#

In this chapter, you will learn how to control physical hardware via BeagleBone Black’s general-purpose input/output (GPIO) pins. The Bone has 65 GPIO pins that are brought out on two 46-pin headers, called P8 and P9, as shown in The P8 and P9 GPIO headers.

Note

All the examples in the book assume you have cloned the Cookbook repository on git.beagleboard.org. Go here Cloning the Cookbook Repository for instructions.

Headers P8 and P9

Fig. 578 The P8 and P9 GPIO headers#

The purpose of this chapter is to give simple examples that show how to use various methods of output. Most solutions require a breadboard and some jumper wires.

All these examples assume that you know how to edit a file (Editing Code Using Visual Studio Code) and run it, either within Visual Studio Code (VSC) integrated development environment (IDE) or from the command line (Getting to the Command Shell via SSH).

Toggling an Onboard LED#

Problem#

You want to know how to flash the four LEDs that are next to the Ethernet port on the Bone.

Solution#

Locate the four onboard LEDs shown in The four USER LEDs. They are labeled USR0 through USR3, but we’ll refer to them as the USER LEDs.

USER LEDs

Fig. 579 The four USER LEDs#

Place the code shown in Using an internal LED (internLED.py) in a file called internLED.py. You can do this using VSC to edit files (as shown in Editing Code Using Visual Studio Code) or with a more traditional editor (as shown in Editing a Text File from the GNU/Linux Command Shell).

Listing 34 Using an internal LED (internLED.py)#
 1#!/usr/bin/env python
 2# //////////////////////////////////////
 3# 	internLED.py
 4# 	Blinks A USR LED.
 5# 	Wiring:
 6# 	Setup:
 7# 	See:
 8# //////////////////////////////////////
 9import gpiod
10import time
11
12LED_CHIP = 'gpiochip1'
13LED_LINE_OFFSET = [21]  # USR0 run: gpioinfo | grep -i -e chip -e usr
14
15chip = gpiod.Chip(LED_CHIP)
16
17lines = chip.get_lines(LED_LINE_OFFSET)
18lines.request(consumer='internLED.py', type=gpiod.LINE_REQ_DIR_OUT)
19
20state =  0      # Start with LED off
21while True:
22    lines.set_values([state])
23    state = ~state      # Toggle the state
24    time.sleep(0.25)

internLED.py

Listing 35 Using an internal LED (internLED.c)#
 1// // //////////////////////////////////////
 2// # 	internLED.c
 3// # 	Blinks A USR LED.
 4// # 	Wiring:
 5// # 	Setup:
 6// # 	See:
 7// // //////////////////////////////////////
 8#include <gpiod.h>
 9#include <stdio.h>
10#include <unistd.h>
11
12#define	CONSUMER	"internLED.c"
13
14int main(int argc, char **argv)
15{
16	int chipnumber = 1;
17	unsigned int line_num = 21;	// usr0 LED, run: gpioinfo | grep -i -e chip -e usr
18	unsigned int val;
19	struct gpiod_chip *chip;
20	struct gpiod_line *line;
21	int i, ret;
22
23	chip = gpiod_chip_open_by_number(chipnumber);
24	line = gpiod_chip_get_line(chip, line_num);
25	ret = gpiod_line_request_output(line, CONSUMER, 0);
26
27	/* Blink */
28	val = 0;
29	while(1) {
30		ret = gpiod_line_set_value(line, val);
31		// printf("Output %u on line #%u\n", val, line_num);
32		usleep(100000);		// Number of microseconds to sleep
33		val = !val;
34	}
35}

internLED.c

In the bash command window, enter the following commands:

bone$ cd ~/beaglebone-cookbook-code/03displays
bone$ ./internLED.py

The USER0 LED should now be flashing.

Toggling an External LED#

Problem#

You want to connect your own external LED to the Bone.

Solution#

Connect an LED to one of the GPIO pins using a series resistor to limit the current. To make this recipe, you will need:

  • Breadboard and jumper wires.

  • 220 Ω to 470 Ω resistor.

  • LED

Warning

The value of the current limiting resistor depends on the LED you are using. The Bone can drive only 4 to 6 mA, so you might need a larger resistor to keep from pulling too much current. A 330 Ω or 470 Ω resistor might be better.

Diagram for using an external LED shows how you can wire the LED to pin 14 of the P9 header (P9_14). Every circuit in this book (Wiring a Breadboard) assumes you have already wired the rightmost bus to ground (P9_1) and the next bus to the left to the 3.3 V (P9_3) pins on the header. Be sure to get the polarity right on the LED. The _short_ lead always goes to ground.

External LED

Fig. 580 Diagram for using an external LED#

After you’ve wired it, start VSC (see Editing Code Using Visual Studio Code) and find the code shown in Code for using an external LED (externLED.py). Notice that it looks very similar to the internLED code, in fact it only differs in the line number (18 instead of 21). The built-in LEDs use the same GPIO interface as the GPIO pins.

Listing 36 Code for using an external LED (externLED.py)#
 1#!/usr/bin/env python
 2# //////////////////////////////////////
 3# 	externLED.py
 4# 	Blinks an external LED wired to P9_14.
 5# 	Wiring: P9_14 connects to the plus lead of an LED.  The negative lead of the
 6#		LED goes to a 220 Ohm resistor.  The other lead of the resistor goes
 7#		to ground
 8# 	Setup:
 9# 	See:
10# //////////////////////////////////////
11import gpiod
12import time
13
14LED_CHIP = 'gpiochip1'
15LED_LINE_OFFSET = [18]  # P9_14 run: gpioinfo | grep -i -e chip -e P9_14
16
17chip = gpiod.Chip(LED_CHIP)
18
19lines = chip.get_lines(LED_LINE_OFFSET)
20lines.request(consumer='internLED.py', type=gpiod.LINE_REQ_DIR_OUT)
21
22state =  0      # Start with LED off
23while True:
24    lines.set_values([state])
25    state = ~state      # Toggle the state
26    time.sleep(0.25)

externLED.py

Listing 37 Code for using an external LED (externLED.c)#
 1// // //////////////////////////////////////
 2// # 	externLED.c
 3// Blinks an external LED wired to P9_14.
 4// Wiring: P9_14 connects to the plus lead of an LED.  The negative lead of the
 5// 		LED goes to a 220 Ohm resistor.  The other lead of the resistor goes
 6// 		to ground
 7// 	Setup:
 8// 	See:
 9// // //////////////////////////////////////
10#include <gpiod.h>
11#include <stdio.h>
12#include <unistd.h>
13
14#define	CONSUMER	"internLED.c"
15
16int main(int argc, char **argv)
17{
18	int chipnumber = 1;
19	unsigned int line_num = 18;	// P9_14, run: gpioinfo | grep -i -e chip -e P9_14
20	unsigned int val;
21	struct gpiod_chip *chip;
22	struct gpiod_line *line;
23	int i, ret;
24
25	chip = gpiod_chip_open_by_number(chipnumber);
26	line = gpiod_chip_get_line(chip, line_num);
27	ret = gpiod_line_request_output(line, CONSUMER, 0);
28
29	/* Blink */
30	val = 0;
31	while(1) {
32		ret = gpiod_line_set_value(line, val);
33		// printf("Output %u on line #%u\n", val, line_num);
34		usleep(100000);		// Number of microseconds to sleep
35		val = !val;
36	}
37}

externLED.c

Save your file and run the code as before (Toggling an Onboard LED).

Toggling a High-Voltage External Device#

Problem#

You want to control a device that runs at 120 V.

Solution#

Working with 120 V can be tricky –even dangerous– if you aren’t careful. Here’s a safe way to do it.

To make this recipe, you will need:

  • PowerSwitch Tail II

Diagram for wiring PowerSwitch Tail II shows how you can wire the PowerSwitch Tail II to pin P9_14.

Power Switch Tail II

Fig. 581 Diagram for wiring PowerSwitch Tail II#

After you’ve wired it, because this uses the same output pin as Toggling an External LED, you can run the same code (Code for using an external LED (externLED.py)).

Fading an External LED#

Problem#

You want to change the brightness of an LED from the Bone.

Solution#

Use the Bone’s pulse width modulation (PWM) hardware to fade an LED. We’ll use the same circuit as before (Diagram for using an external LED). Find the code in Code for using an external LED (fadeLED.py) Next configure the pins. We are using P9_14 so run:

bone$ config-pin P9_14 pwm

Then run it as before.

Listing 38 Code for using an external LED (fadeLED.py)#
 1#!/usr/bin/env python
 2# ////////////////////////////////////////
 3# //	fadeLED.py
 4# //	Blinks the P9_14 pin
 5# //	Wiring:
 6# //	Setup:  config-pin P9_14 pwm
 7# //	See:
 8# ////////////////////////////////////////
 9import time
10ms = 20;   # Fade time in ms
11
12pwmPeriod = 1000000    # Period in ns
13pwm     = '1'  # pwm to use
14channel = 'a'  # channel to use
15PWMPATH='/dev/bone/pwm/'+pwm+'/'+channel
16step = 0.02    # Step size
17min = 0.02     # dimmest value
18max = 1        # brightest value
19brightness = min # Current brightness
20
21f = open(PWMPATH+'/period', 'w')
22f.write(str(pwmPeriod))
23f.close()
24
25f = open(PWMPATH+'/enable', 'w')
26f.write('1')
27f.close()
28
29f = open(PWMPATH+'/duty_cycle', 'w')
30while True:
31    f.seek(0)
32    f.write(str(round(pwmPeriod*brightness)))
33    brightness  += step
34    if(brightness >= max or brightness <= min):
35        step = -1 * step
36    time.sleep(ms/1000)
37
38# | Pin   | pwm | channel
39# | P9_31 | 0   | a
40# | P9_29 | 0   | b
41# | P9_14 | 1   | a
42# | P9_16 | 1   | b
43# | P8_19 | 2   | a
44# | P8_13 | 2   | b

fadeLED.py

Listing 39 Code for using an external LED (fadeLED.js)#
 1#!/usr/bin/env node
 2////////////////////////////////////////
 3//	fadeLED.js
 4//	Blinks the P9_14 pin
 5//	Wiring:
 6//	Setup:  config-pin P9_14 pwm
 7//	See:
 8////////////////////////////////////////
 9const fs = require("fs");
10const ms = '20';    // Fade time in ms
11
12const pwmPeriod = '1000000';    // Period in ns
13const pwm     = '1';  // pwm to use
14const channel = 'a';  // channel to use
15const PWMPATH='/dev/bone/pwm/'+pwm+'/'+channel;
16var   step = 0.02;  // Step size
17const min = 0.02,     // dimmest value
18    max = 1;        // brightest value
19var brightness = min; // Current brightness;
20
21
22// Set the period in ns
23fs.writeFileSync(PWMPATH+'/period', pwmPeriod);
24fs.writeFileSync(PWMPATH+'/duty_cycle', pwmPeriod/2);
25fs.writeFileSync(PWMPATH+'/enable', '1');
26
27setInterval(fade, ms);      // Step every  ms
28
29function fade() {
30     fs.writeFileSync(PWMPATH+'/duty_cycle', 
31        parseInt(pwmPeriod*brightness));
32    brightness += step;
33    if(brightness >= max || brightness <= min) {
34        step = -1 * step;
35    }
36}
37
38// | Pin   | pwm | channel
39// | P9_31 | 0   | a
40// | P9_29 | 0   | b
41// | P9_14 | 1   | a
42// | P9_16 | 1   | b
43// | P8_19 | 2   | a
44// | P8_13 | 2   | b

fadeLED.js

The Bone has several outputs that can be use as pwm’s as shown in Table of PWM outputs. There are three EHRPWM’s which each has a pair of pwm channels. Each pair must have the same period.

PWM outputs

Fig. 582 Table of PWM outputs#

The pwm’s are accessed through /dev/bone/pwm

bone$ cd /dev/bone/pwm
bone$ ls
0  1  2

Here we see three pwmchips that can be used, each has two channels. Explore one.

bone$ cd 1
bone$ ls
a  b
bone$ cd a
bone$ ls
capture  duty_cycle  enable  period  polarity  power  uevent

Here is where you can set the period and duty_cycle (in ns) and enable the pwm. Attach in LED to P9_14 and if you set the period long enough you can see the LED flash.

bone$ echo 1000000000 > period
bone$ echo  500000000 > duty_cycle
bone$ echo 1 > enable

Your LED should now be flashing.

Headers to pwm channel mapping are the mapping I’ve figured out so far. I don’t know how to get to the timers.

Table 136 Headers to pwm channel mapping#

Pin

pwm

channel

P9_31

0

a

P9_29

0

b

P9_14

1

a

P9_16

1

b

P8_19

2

a

P8_13

2

b

Writing to an LED Matrix#

Problem#

You have an I2C-based LED matrix to interface.

Solution#

There are a number of nice LED matrices that allow you to control several LEDs via one interface. This solution uses an Adafruit Bicolor 8x8 LED Square Pixel Matrix w/|I2C| Backpack.

To make this recipe, you will need:

  • Breadboard and jumper wires

  • Two 4.7 kΩ resistors.

  • I2C LED matrix

The LED matrix is a 5 V device, but you can drive it from 3.3 V. Wire, as shown in Wiring an I2C LED matrix.

|I2C| LED matrix

Fig. 583 Wiring an I2C LED matrix#

Measuring a Temperature shows how to use i2cdetect to discover the address of an I2C device.

Run the i2cdetect -y -r 2 command to discover the address of the display on I2C bus 2, as shown in Using I2C command-line tools to discover the address of the display.

Using I2C command-line tools to discover the address of the display#

bone$ i2cdetect -y -r 2
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- 49 -- -- -- -- -- --
50: -- -- -- -- UU UU UU UU -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 -- -- -- -- -- -- --

Here, you can see a device at 0x49 and 0x70. I know I have a temperature sensor at 0x49, so the LED matrix must be at 0x70.

Find the code in LED matrix display (matrixLEDi2c.py) and run it by using the following command:

bone$ pip install smbus  # (Do this only once.)
bone$ ./matrixLEDi2c.py

LED matrix display (matrixLEDi2c.py)#

Listing 40 LED matrix display (matrixLEDi2c.py)#
 1#!/usr/bin/env python
 2# ////////////////////////////////////////
 3# //	i2cTemp.py
 4# //      Write an 8x8 Red/Green LED matrix.
 5# //	Wiring:	Attach to i2c as shown in text.
 6# //	Setup:	echo tmp101 0x49 > /sys/class/i2c-adapter/i2c-2/new_device
 7# //	See:	https://www.adafruit.com/product/902
 8# ////////////////////////////////////////
 9import smbus
10import time
11
12bus = smbus.SMBus(2)  # Use i2c bus 2       ①
13matrix = 0x70         # Use address 0x70    ②
14ms = 1;               # Delay between images in ms
15
16# The first byte is GREEN, the second is RED.   ③
17smile = [0x00, 0x3c, 0x00, 0x42, 0x28, 0x89, 0x04, 0x85,
18    0x04, 0x85, 0x28, 0x89, 0x00, 0x42, 0x00, 0x3c
19]
20frown = [0x3c, 0x00, 0x42, 0x00, 0x85, 0x20, 0x89, 0x00,
21    0x89, 0x00, 0x85, 0x20, 0x42, 0x00, 0x3c, 0x00
22]
23neutral = [0x3c, 0x3c, 0x42, 0x42, 0xa9, 0xa9, 0x89, 0x89,
24    0x89, 0x89, 0xa9, 0xa9, 0x42, 0x42, 0x3c, 0x3c
25]
26
27bus.write_byte_data(matrix, 0x21, 0)   # Start oscillator (p10) ④
28bus.write_byte_data(matrix, 0x81, 0)   # Disp on, blink off (p11)
29bus.write_byte_data(matrix, 0xe7, 0)   # Full brightness (page 15)
30
31bus.write_i2c_block_data(matrix, 0, frown)      # ⑤
32for fade in range(0xef, 0xe0, -1):              # ⑥
33    bus.write_byte_data(matrix, fade, 0)
34    time.sleep(ms/10)
35
36bus.write_i2c_block_data(matrix, 0, neutral)
37for fade in range(0xe0, 0xef, 1):
38    bus.write_byte_data(matrix, fade, 0)
39    time.sleep(ms/10)
40
41bus.write_i2c_block_data(matrix, 0, smile)

matrixLEDi2c.py

① This line states which bus to use. The last digit gives the I2C bus number.

② This specifies the address of the LED matrix, 0x70 in our case.

③ This indicates which LEDs to turn on. The first byte is for the first column of green LEDs. In this case, all are turned off. The next byte is for the first column of red LEDs. The hex 0x3c number is 0b00111100 in binary. This means the first two red LEDs are off, the next four are on, and the last two are off. The next byte (0x00) says the second column of green LEDs are all off, the fourth byte (0x42 = 0b01000010) says just two red LEDs are on, and so on. Declarations define four different patterns to display on the LED matrix, the last being all turned off.

④ Send three commands to the matrix to get it ready to display.

⑤ Now, we are ready to display the various patterns. After each pattern is displayed, we sleep a certain amount of time so that the pattern can be seen.

⑥ Finally, send commands to the LED matrix to set the brightness. This makes the display fade out and back in again.

Driving a 5 V Device#

Problem#

You have a 5 V device to drive, and the Bone has 3.3 V outputs.

Solution#

If you are lucky, you might be able to drive a 5 V device from the Bone’s 3.3 V output. Try it and see if it works. If not, you need a level translator.

What you will need for this recipe:

  • A PCA9306 level translator

  • A 5 V power supply (if the Bone’s 5 V power supply isn’t enough)

The PCA9306 translates signals at 3.3 V to 5 V in both directions. It’s meant to work with I2C devices that have a pull-up resistor, but it can work with anything needing translation.

Wiring a PCA9306 level translator to an LED matrix shows how to wire a PCA9306 to an LED matrix. The left is the 3.3 V side and the right is the 5 V side. Notice that we are using the Bone’s built-in 5 V power supply.

PCA9306 level translator

Fig. 584 Wiring a PCA9306 level translator to an LED matrix#

Note

If your device needs more current than the Bone’s 5 V power supply provides, you can wire in an external power supply.

Writing to a NeoPixel LED String Using the PRUs#

Problem#

You have an Adafruit NeoPixel LED string or Adafruit NeoPixel LED matrix and want to light it up.

Solution#

The PRU Cookbook has a nice discussion (WS2812 (NeoPixel) driver) on driving NeoPixels.

NeoPixel Ring

Fig. 585 Wiring an Adafruit NeoPixel LED matrix to P9_29#

Writing to a NeoPixel LED String Using LEDscape#

Making Your Bone Speak#

Problem#

Your Bone wants to talk.

Solution#

Just install the flite text-to-speech program:

bone$ sudo apt install flite

Then add the code from A program that talks (speak.js) in a file called speak.js and run.

Listing 41 A program that talks (speak.js)#
 1#!/usr/bin/env node
 2
 3var exec = require('child_process').exec;
 4
 5function speakForSelf(phrase) {
 6{
 7	exec('flite -t "' + phrase + '"', function (error, stdout, stderr) {
 8        console.log(stdout);
 9        if(error) { 
10            console.log('error: ' + error); 
11        }
12        if(stderr) {
13            console.log('stderr: ' + stderr); 
14        }
15        });
16}
17
18speakForSelf("Hello, My name is Borris. " +
19    "I am a BeagleBone Black, " +
20    "a true open hardware, " +
21    "community-supported embedded computer for developers and hobbyists. " +
22    "I am powered by a 1 Giga Hertz Sitara™ ARM® Cortex-A8 processor. " +
23    "I boot Linux in under 10 seconds. " +
24    "You can get started on development in " +
25    "less than 5 minutes with just a single USB cable." +
26    "Bark, bark!"
27    );

speak.js

See Playing and Recording Audio to see how to use a USB audio dongle and set your default audio out.