Simple Peripherals

Note

This guide describes how to access peripherals using the Linux 5.2 kernel. Earlier versions of Linux used on the Librem 5 development board expose some peripherals to user space differently.

The Librem 5 development board has a number of peripherals that allow applications to interact with users and the outside world in a simple way. This guide covers these I/O peripherals and describes how to use them.

Peripherals that handle input are typically readable by any user but some that allow output, such as LEDs, can only be written by the root user.

Finding Device Files

Some of the peripherals are accessed via device files in the /dev/input directory. One way to determine which device file is used to access a particular peripheral is to open a console on the development board and run the evtest tool:

sudo evtest

This should produce a list of devices to monitor, like the following, though the paths to these files may change from one version of the kernel to the next:

/dev/input/event0:      gpio-keys
/dev/input/event1:      bd718xx-pwrkey
/dev/input/event2:      30370000.snvs:snvs-powerkey
/dev/input/event3:      gpio-vibrator
/dev/input/event4:      Goodix Capacitive TouchScreen

Press Ctrl-C to exit the tool unless you want to monitor a device. Make a note of the device file that corresponds to the peripheral you want to access.

Light Emitting Diodes (LEDs)

Only one of the LEDs can be controlled by applications. This red diode is mapped to the /sys/class/leds/LED 1 directory in the file system and its state can be changed by writing to the /sys/class/leds/LED 1/brightness file, as in this shell command:

echo 255 > /sys/class/leds/LED\ 1/brightness

Using the default system configuration, writing a value of 0 to the file will switch the LED off. Writing a value from 1 to 255 will switch it on. Reading from the file returns the value previously used to set the brightness of the LED. The maximum brightness can be read from the /sys/class/leds/LED 1/max_brightness file:

cat /sys/class/leds/LED\ 1/max_brightness

The LED can also be controlled using the python-periphery library: leds.py

#!/usr/bin/env python3

import periphery

# Obtain an object for the LED.
led = periphery.LED("LED 1")

# Read the current brightness.
brightness = led.read()

# Switch the LED on.
led.write(1)

# Switch the LED off.
led.write(0)

The lack of control over the brightness of the LED is because it is driven by a Pulse Width Modulator (PWM) that it shares with the haptic motor, and the default kernel device tree configures the motor to use the PWM. If a different device tree is used with the kernel, this behavior can be adjusted.

Haptic Motor

The haptic motor is treated as an input device that provides force feedback effects. Use the evtest tool as described earlier to find the path to the device file that is used to access the motor.

Test that the device is working by running the fftest tool that is provided as part of the joystick Debian package, passing the path to the device file that corresponds to the motor:

fftest <device file>

Another way to control the motor is to use the python-evdev package: haptic.py

#!/usr/bin/env python3

import evdev
import sys
import time


# Obtain an object for the motor.
for path in evdev.list_devices():
    device = evdev.InputDevice(path)
    if evdev.ecodes.EV_FF in device.capabilities():
        break
else:
    sys.stderr.write('Failed to find the haptic motor.\n')
    sys.exit(1)

# Define a rumble effect.
rumble = evdev.ff.Rumble(strong_magnitude=0xff, weak_magnitude=0xffff)

# Create an effect type and duration.
effect_type = evdev.ff.EffectType(ff_rumble_effect=rumble)
duration = 1000

# Create the effect itself.
effect = evdev.ff.Effect(evdev.ecodes.FF_RUMBLE, -1, 0,
                         evdev.ff.Trigger(0, 0),
                         evdev.ff.Replay(duration, 0),
                         effect_type)

# Upload the effect to the device and obtain an ID.
effect_id = device.upload_effect(effect)

# Write the event to the device to play the effect just once.
device.write(evdev.ecodes.EV_FF, effect_id, 1)

time.sleep(duration / 1000.0)

# Erase the effect once we are finished with it.
device.erase_effect(effect_id)

The haptic motor shares a PWM with the red LED. In the device tree used by the default kernel the PWM is used to control the amplitude of the haptic motor, restricting the state of the LED to being either on or off. If a different device tree is used with the kernel, this behavior can be adjusted.

More information about the different parameters used for force feedback effect, see the Force feedback for Linux documentation.

Push Buttons

User input from the volume up, volume down and power push buttons can be read. Events from these buttons are delivered to device files that can be found using the evtest tool as described earlier. Events from the volume buttons are delivered to a separate device file to those from the power button.

Events can also be read using the python-evdev package: buttons.py

#!/usr/bin/env python3

import evdev
import select
import sys


VOLUME_DOWN = 114
VOLUME_UP = 115
POWER = 116

# Obtain objects for the input devices.
devices = []

for path in evdev.list_devices():
    device = evdev.InputDevice(path)
    if evdev.ecodes.EV_KEY in device.capabilities():
        devices.append(device)

if not devices:
    sys.stderr.write('Failed to find any key input devices.\n')
    sys.exit(1)

fds = dict([(device.fd, device) for device in devices])

while True:

    # Wait for an event.
    r, w, x = select.select(fds.keys(), [], [])

    # Read the events for the device.
    for fd in r:
        device = fds[fd]

        for event in device.read():

            pressed = {0: "released", 1: "pressed"}[event.value]

            if event.code == VOLUME_UP:
                print("volume up", pressed)
            elif event.code == VOLUME_DOWN:
                print("volume down", pressed)
            elif event.code == POWER:
                print("power", pressed)

The python-evdev tutorial shows several ways of handling events that can be applied to various situations, using different techniques to read the event queue.

Headphone Connection

Note

This section requires revision.

Input events are generated when a headphone jack is inserted into or removed from the headphone socket. These events are delivered to the /dev/input/event0 file together with push button events. As with the buttons, the evtest command can be used to examine the events:

sudo evtest /dev/input/event0

This will listen for headphone connection events and write them in human-readable form to the console.

Events can also be read using the python-evdev package: headphones.py

#!/usr/bin/env python3

import evdev
import select

HEADPHONE_CONNECT = 211

# Obtain an object for the input device.
device = evdev.InputDevice('/dev/input/event0')

while True:

    # Wait for an event.
    r, w, x = select.select([device.fd], [], [])

    # Read the events for the device.
    for event in device.read():

        if event.code == HEADPHONE_CONNECT:
            connected = {0: "connected", 1: "disconnected"}[event.value]
            print("headphones", connected)

It may be necessary to combine reading of headphone connection events with those from the push buttons, even if they are handled separately.