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.

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:

#!/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. You can test that the device is working by running the fftest tool that is provided as part of the joystick Debian package:

fftest /dev/input/event2

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

#!/usr/bin/env python3

import evdev
import time

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

# 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 the /dev/input/event0 file in the case of the volume buttons and to /dev/input/event3 file in the case of the power button.

One way to examine these events is to run the evtest command – this will need to be executed using sudo unless the user is in the input group:

sudo evtest /dev/input/event0

This will listen for events from the volume buttons and write them in human-readable form to the console. Use /dev/input/event3 to do the same for the power button.

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

#!/usr/bin/env python3

import evdev
import select

VOLUME_DOWN = 114
VOLUME_UP = 115
POWER = 116

# Obtain an object for the input devices.
devices = [evdev.InputDevice('/dev/input/event0'),
           evdev.InputDevice('/dev/input/event3')]

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

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:

#!/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.