labgrid - remote infrastructure

In the previous post, we’ve explored the basic concepts by looking at labgrid as a library. Armed with the new concepts we take a step further. In this post, we explore labgrid as a provider of remote access to an embedded board.

What is labgrid?

As mentioned in a previous post I find this to be the best definition of labgrid:

The idea behind labgrid is to create an abstraction of the hardware control layer needed for testing of embedded systems, automatic software installation, and automation during development.

In this post, we’ll focus on how labgrid can assist us in coordinating remote access to a development board.


In the example, a new RPi (Exporter/Coordinator) serves as a remote connection point to the RPi (Development Board). Through the post, a shortened notaion will also be used RPi(E/C) and RPi(DB).

The diagram below shows the connections between the devices.

+-----------------+       +--------------------+     +-----------+
| Laptop          |       | RPi(E/C)           |     | RPi(DB)   |
|                 |       |                    |     |           |
|  +-------+     |     |           |
|                 |       | SSh server         |     |           |
|                 |       |                    |     |           |
|                 |       |        /dev/ttyUSB0+-----+ UART pins |
+-----------------+       +--------------------+     +-----------+


It is assumed you know how to set up the two RPis so that they:

  • boot Raspbian Buster Lite
  • RPi(E/C) is in the same subnet as the laptop
  • RPi(E/C) has SSH enabled with default credentials (pi, raspberry)
  • RPi(DB) has a UART connection with the RPi(E/C)
    • the UART connection is detected on the RPi(E/C) as /dev/ttyUSB0 or similar
    • the /dev/ttyUSB0 can be accessed without root permissions sudo chmod 777 /dev/ttyUSB0

For some help on how to get there check here and here.

Installation and config

We will have to set up two machines with labgrid. Laptop and the RPi(E/C).

RPi (Exporter/Coordinator)

The RPi(E/C) serves as a gateway to the RPi(DB). It provides remote access to the RPi(DB) for multiple developers. This example is a single RPi(DB) with a single UART connection (Resource) so the idea might be unclear. For better context imagine 10 RPi(DB)s with 20 resources and 5 developers who need to work with it.

The name Exporter/Coordinator comes from labgrid terminology. They are the services that need to run on the RPi(E/C) for remote access to work.

The Exporter exposes the resources of the RPi(DB) making them remotely accessible.

The Coordinator keeps track of the exported resources and manages access policies for clients who want to work with those resources.

Again the simple example doesn’t show the motivation behind Exporter and Coordinator being separate services. For context think about managing multiple Exporters on separate physically distant machines. This makes the benefits of the distributed nature of Exporter and Coordinator more apparent.

The code below will install all the requirements, create the necessary config files, and start the services. Please note two terminal screens will be used so you’ll need two ssh sessions to the RPi(E/C).


# ssh pi@
# password: raspberry
sudo apt-get install git python3 python3-virtualenv python3-pip virtualenv python-dev libatlas-base-dev ser2net

# Installing in a virtuaenv
virtualenv -p python3 labgrid-venv
source labgrid-venv/bin/activate

git clone
cd labgrid 
pip install -r crossbar-requirements.txt
python install

Configuration and start

Terminal one (Coordinator)

# Activate the venv if it isn't yet
# source labgrid-venv/bin/activate

cd ~/labgrid
crossbar start

Terminal two (Exporter)

The Exporter needs a configuration file to work. This configuration describes the resources RPi(DB) has available.

We know there is a UART connection with the RPi(DB) which is defined as the RawSerialPort resource. Also, the resource is described as belonging to a group of resources called arbitrary_group_name. This shows that on a conceptual level, the resources don’t need to be grouped according to the device they are a part of. Instead, every resource is an individual entity that can be put together in groups with arbitrary names with other resources.

For this example, it would make more sense to use “RPi(DB)” as the group name. I deliberately chose not to show that a group in labgrid isn’t the same thing as a target.

# ssh pi@
# password: raspberry
# Activate the venv if it isn't yet
# source labgrid-venv/bin/activate

cat > exporter_conf.yaml << EOF
    port: "/dev/ttyUSB0"

labgrid-exporter exporter_conf.yaml

These services need to be running continuously on the RPi.

Laptop setup

Now that the remote infrastructure is up and running, time to set up the client side. In this example, the client is someone attempting to interact with the RPi(DB) from the laptop.


apt-get install python3 python3-virtualenv python3-pip virtualenv microcom

# Installing in a virtualen
virtualenv -p python3 labgrid-venv
source labgrid-venv/bin/activate

git clone
cd labgrid 
pip install -r requirements.txt
pip install .
cd ..


The configuration process starts with identifying what resources are available on the remote setup.

labgrid-client -x ws:// resources
# raspberrypi/arbitrary_group_name/NetworkSerialPort

This list the resource we’ve set up with the Exporter. It’s formatted in a form of path <hostname>/<group defined in explorer config>/<resource>. It might be confusing to see NetworkSerialPort listed, given that in Explorer RawSerialPort was specified. This is an internal labgrid mechanism to automatically apply the network version of the resource when accessed from the client. Running the command with verbose shows that more explicitly.

labgrid-client -v -x ws:// resources
# Exporter 'raspberrypi':
#   Group 'arbitrary_group_name' (raspberrypi/arbitrary_group_name/*):
#     Resource 'RawSerialPort' (raspberrypi/arbitrary_group_name/NetworkSerialPort[/RawSerialPort]):
#       {'acquired': None,
#        'avail': True,
#        'cls': 'NetworkSerialPort',
#        'params': {'extra': {'path': '/dev/ttyUSB0',
#                             'proxy': 'raspberrypi',
#                             'proxy_required': False},
#                   'host': 'raspberrypi',
#                   'port': None,
#                   'speed': 115200}}

The goal of the labgrid-client is to act as the main interface for interacting with a place. A place is a grouping of resources that make sense from the perspective of the client. To be able to use the client a place needs to be defined.

labgrid-client -x ws:// --place rpi_db create

# Once created we can confirm it's listed
labgrid-client -x ws://  places

Now that the place is created, resources need to be added to it.

labgrid-client -x ws:// -p rpi_db add-match raspberrypi/arbitrary_group_name/NetworkSerialPort

# confirm the resources are added

labgrid-client -x ws:// -p rpi_db show
# Place 'rpi_db':
#   matches:
#     */example-group/*
#     raspberrypi/arbitrary_group_name/NetworkSerialPort
#   acquired: None
#   acquired resources:
#   created: 2020-11-24 01:49:37.271945
#   changed: 2020-12-08 17:57:37.543050
# Matching resource 'RawSerialPort' (raspberrypi/arbitrary_group_name/NetworkSerialPort/RawSerialPort):
#   {'acquired': None,
#    'avail': True,
#    'cls': 'NetworkSerialPort',
#    'params': {'extra': {'path': '/dev/ttyUSB0',
#                         'proxy': 'raspberrypi',
#                         'proxy_required': False},
#               'host': 'raspberrypi',
#               'port': None,
#               'speed': 115200}}

Having set everything up we’re now ready to start using the system.


The main goal of the setup is to allow multiple developers access to a single resource. This is achieved by the concept of acquiring a place. By acquiring the place, the client gets exclusive access to it. No other client can access it during that time and it’s transparent who is using it.

labgrid-client -x ws:// -p rpi_db acquire
# acquired place rpi_db

Once the place has been acquired we can perform actions on top of it. The number of labgrid-client subcommands that will work depends on the exported resources. For this particular example, we know only the UART related resource is exported.

Running the console subcommand will result in a UART connection with the RPi(DB).

labgrid-client -x ws:// -p rpi_db console
# Feel free to log in to the RPi(DB)
# user: raspberry pass: pi

# To exit press Ctrl+\ followed by Ctrl+c after reaching the -> prompt

Once we’ve done with the RPi(DB) it’s time to release it. This would allow others to access it while it’s idle.


The post explored how to use labgrid as an infrastructure for accessing an embedded device remotely. It was purposefully simple to introduce the major major components (Explorer, Coordinator, Client) and the motivation behind them.

The modular structure of labgrid remote infrastructure promises the possibility of a variety of use cases. On the Exporter side, you can categorize resources in different groups allowing for convenient combinations. An example that comes to mind is the RPi(DB) power control. That’s a device that controls if the RPi(DB) is connected to mains (i.e. a smart socket). It isn’t physically a part of the RPi(DB) but conceptually they belong together.

At the same time, the client side isn’t limited to follow the categories of the Exporter but can group the resources how they see fit with the concept of spaces.

While the flexibility allows for complex use cases, the simpler setup pays the price for that with an overhead of concepts and implementation steps. Thanks to the open source nature of the project, there is potential for this to improve.

Written on November 30, 2020