Is Luos for me? πŸ€”

Luos documentation and help

About Luos

Luos is designed with the conviction that the development of electronic systems should be made easier than it is today. Indeed, most of the time should be spent developing applications and behaviors rather than consuming time and money on over-complex boards' technicalities and communication.

For example, adding a new sensor β€”for instance, a distance sensorβ€” to a system you are conceiving should not take more than a few minutes, even on a fully developed and complex ready-to-produce electronic device.

Luos allows you to try, test, and iterate faster on any project to design what users really want.

Luos works like a microservices architecture β†— designed for the software world, and a containerization platform. It encapsulates any software or hardware functions to make them communicate and work with any other encapsulated service, no matter how they were developed, either on bare metal or on top of an embedded OS.

If you have questions about a specific topic, you can check out and post messages in our Luos' community on Reddit β†—. And if you have suggestions about this documentation, don't hesitate to create pull requests.

Luos revision: 2.0.0
Luos is under Apache 2.0 license β†—.

Get started

This page allows you to build, flash, run, and control your very first Luos code.

This Getting started is separated in 2 parts:

  • The embedded part: By following this part you will have all the tools you need to easily develop using Luos in your embedded target.
  • The remote control part: By following this part you will have all the tools you need to take control and easily test any Luos device.

The embedded part: Run your first embedded app!

This tutorial shows you how to quickly upload a Luos application on an MCU development kit.

Supported boards are listed below:

  • Arduino zero, MKRzero, MKR1000, or any SAMD21-based Arduino board
  • STM32L432KC Nucleo

Note: This list will grow longer with time.

Setup development environment

We will use PlatformIO β†— as development environment.

First, install the free coding editor Microsoft Visual Studio Code β†— (VSCode). PlatformIO's IDE is built on top of it.

Then, in PlatformIO:

  1. Open VSCode Extension Manager.
  2. Search for the official PlatformIO IDE extension.
  3. Install PlatformIO IDE.

Clone the project

Clone the getting started repository on your computer:

git clone

If you are not familiar with Git, you can consult their documentation β†—.

Flash your board depending on which one you have

Open VSCode and click on Open Folder in the project explorer on the left, then select a project depending on the board you have chosen. For example, for the STM32L432KC Nucleo, open L432KC_Nucleo in the folder explorer then click on ADD:

The project folder is opened in the explorer.

Note: Arduino users can select their board on the platformio.ini file by modifying the board = zero line.

You can now flash your board: make sure it's connected to your PC with a USB cable and click on Upload on the bottom left of the VSCode window:

PlatformIO will build the firmware and flash it. Take a look at the terminal to watch each step platformIO follows and a Success message at the end. If you have any trouble with your USB driver, you can consult this FAQ page. Once the board is programmed, you should see the LED blinking on your board.

Congratulations, your first Luos app is running!

What is going on

There are two services loaded in your board allowing to blink the LED.

  • Blinker sends a message at a fixed duration
    β•° located at the root of the getting_started repository (because the same app can run on any board)
  • Led receives this message and makes the LED blink
    β•° located on the lib folder of your project (because it is a driver which is specific to your board)

On top of it, we also added two other services allowing you to take control of your board:

  • Pipe, managing a serial interface
    β•° located on the lib folder of your project (because it is a driver which is specific to your board)
  • Gate, an app that translates Luos to JSON and sends it through Pipe
    β•° located in the cloud, (because it is a common cross-platform's Luos app) PlatformIO just downloaded it for you.

The remote control part: πŸ’Š You can control the Matrix.

The gate running on your board allows you to take control of any service loaded on your device.

Setup development environment

We will use Python with the default library of Luos called pyluos. To install it, run:

pip install pyluos

Connect and control your device

Pyluos provides a set of tools. To control you device, run:


This command will find the serial port of your device and mount it into a "device" object.

For example:

$ pyluos-shell
Searching for a gate available
Testing /dev/cu.usbserial-D308N885
Testing /dev/cu.usbmodem13102
Connected to "/dev/cu.usbmodem13102".
Sending detection signal.
Waiting for routing table...
Device setup.

 Hit Ctrl-D to exit this interpreter.

Your luos device have been successfully mounted into a "device" object:
  ┃  β•­node 1            /!\ Not certified            ┃
  ┃  β”‚  Type                Alias               ID   ┃
  ┃  β”œ> State               led                 2    ┃
  ┃  β”œ> Pipe                Pipe                3    ┃
  ┃  β”œ> Gate                gate                1    ┃
  ┃  β•°> Unknown             blinker             4    ┃

Now that you are on an IPython command line, you can run Python scripts in it. The device object is your real device and you can interact with it. For example, try to execute these lines one by one:

In [1]: device.blinker.time=0.25
In [2]: device.blinker.pause()
In [3]: device.led.state=True
In [4]: device.led.state=False
In [5]:

Next steps

Your development environment is now installed and you have a Luos app running on your MCU. Check the tutorials to learn how to use each feature of Luos technology. You can also read the Luos documentation to learn more about the core technology.

Luos Technology

The most for the developer

Luos provides a simple way to think your hardware products as a group of independant features. You can easily manage and share your hardware products' features with your team, external developers, or with the community. Luos is an open-source lightweight library that can be used on any MCU, leading to free and fast multi-electronic-boards products development. Choosing Luos to design a product will help you to develop, debug, validate, monitor, and manage it from the cloud.

The most for the community

Most of the embedded developments are made from scratch. By using Luos, you will be able to capitalize on the development you, your company, or the Luos community already did. The re-usability of features encapsulated in Luos services will fasten the time your products reach the market and reassure the robustness and the universality of your applications.

You can visit the Luos community on Reddit.

Need dedicated help with your project? Check out Luos' support packages.

Good practices with Luos

Luos proposes organized and effective development practices, guaranteeing development flexibility and evolutivity of your hardware product, from the idea to the maintenance of the industrialized product fleet.

Let's do this

This section details the features of Luos technology as an embedded development platform, following these subjects:

  • The Basics of Luos, explaining the general concepts and the project organization.
  • Definition of Nodes, and the relation between Luos and the physical world.
  • Definition of Packages, and how to make a portable and reusable development.
  • Definition of Services, how to create and declare features in your product.
  • Definition of Messages, when, why, and how to handle them, explaining the more advanced features of Luos.

Luos aims to end the dictatorship of electronics

Take most embedded systems software, you will find the same hardware oriented organization β€”the dictatorship of electronics 😈.

Sensors, actuators, and other hardware resources will be mixed into the application managing the behavior of the product into a unique and monolithic software. Modifying, adding, or reusing something into such an architecture forces you to rethink the entire project.

Sometimes, a design will have more than one microcontroller. In this case, you will need to implement an inter-device communication protocol using any bus available, and you will need to deal with instructions, messaging, error handling, collision, priority, latency, etc. In this case, the product will be even more difficult to maintain due to a significant portion of time spent on the network software development across all the boards.

Luos is here to back you up and keep your projects clean and smooth to develop, modify and reuse πŸ˜‡.

Introduction to Luos

Luos is a simple and lightweight containerization platform dedicated to embedded systems enabling a microservices β†— architecture for electronics. It is a powerful modularity tool to simplify and link any hardware component or application code together as a single system image β†—.

This guide contains all the basic notions you will need to use, create and understand Luos technology.

Luos is a low-level software technology uploaded into every board's (nodeHardware element (MCU) hosting and running Luos and hosting one or several services.) of a device. You can use Luos as a bare metal library, or as a driver into your embedded OS.

Luos is composed as well of code subdivisions called servicesSoftware element run by Luos that can communicate with other services. It can be a driver or an app.. Services are distributed into every node in a network.

Introduction to embedded containerized platforms

Luos aims to change the way you develop by containerizing your embedded features into services on your devices. A microcontroller can host a series of services, like data acquisition from sensors, actuators, or pieces of behavior for your devices. These features are placed inside servicesSoftware element run by Luos that can communicate with other services. It can be a driver or an app.. Prepare your services and deploy them anywhere in a Luos network, and you will be able to access them directly with your services, no matter where they are in the network. Services can be dynamically connected and disconnected and can be detected and find by your application.

Example: Imagine an industrial device. It might contain several temperature sensors that will monitor specific parts of the system, and our system will need to know exactly what they monitor. When we read the temperature of the industrial motor, it is more convenient to get the information from the motor itself. These sensors are placed into services and clearly state which components they monitor. If the device encounters a problem, we need to notify anyone around of the incident. This could be a flashing light, a siren, or any other way of notifying people. In this case, it doesn't matter where the alarm peripherals are placed or even how many there are, Luos can notify all the services that identify as an alarm to activate them. Alarms can even be added to the Luos network while operating, and they will be recognized and used. Alarms also can be hosted or controled by computers or cloud applications.

Finally, it doesn't matter where on the Luos network sensors or actuators are placed. You can wire the different elements together as you want, each element will be detected and you will be able to use all of your services, wherever they are.


What is a Node?

A node is a physical component (hardware) running Luos and hosting one or several services. In a Luos network, nodes are all connected together using RobusBus communication protocol used by Luos., the Luos network manager compatible with most of the existing communication bus.

In other words, a node is a microcontroller connected to other microcontrollers running Luos. In the Luos philosophy, each node has to carry the necessary programs (services), allowing it to manage its boards and features.


A package is a folder of code containing a pack of features in your project. A package can be composed of one or several services, but services in the same package share the same physical resources. For example, a dual DCmotor driver is developed into one package but exposes two services (one for each motor). The purpose of this package is to be simply copied and pasted across projects or shared as readyo-to-use apps.


A service is a "public" feature hosted in a package. It provides the API of your feature by exposing inputs and outputs. An input can be an angular target position of a motor, the color of an RGB LED, or an advanced custom command to control the behavior of your product. An output can be a temperature, the torque delivered by a motor, or the result of a complex home-made algorithm.

Services, much like their server-world counterparts, can be placed anywhere in your infrastructure, and you as a developer do not need to know where they are located to access them. Luos will detect the physical position of all the nodes of your product, list all the services among them, and share the result with all your services in a routing table. The network can physically change, Luos can update dynamically the routing table allowing your services to stay available.

Each service is hosted in a single node, but you can have the same service duplicated multiple times in your product.

For example, the Dynamixel example (available in the GitHub example repository) can dynamically create servomotors services depending on the number of Dynamixel motors linked to it. Any Dynamixel services are visible as independent servomotors similar to any other servomotor technology such as the stepper service example, or the controller motor service example. Then, you can use any of your Dynamixel from any other services or even from any computer, cloud program, or ecosystem such as ROS.

There are two categories of services, drivers or applications.

  • Drivers are services giving advanced access to a physical resource. Drivers cannot rely on any other services; they are independent. Drivers should comply to the adapted service profile provided by Luos and the community, allowing an universal access to any physical resource.
  • Applications are the behaviors of your product and don't rely on any hardware. Application services search for the driver services they need and use them to physically control the device.

Following the rules of these categories will help you to improve the maintainability and the re-usability of all your developments.

Go to the Services page for more information.

Service detection

Services in the network are automatically detected and being assigned IDs depending on their node's physical position in the network, and a routing table is generated.

IDs are assigned from the nearest to the furthest node branch by branch, from the point of view of the service running the detection. Following this logic, the service running the detection will have ID 1, the next one will have ID 2, etc.

Note: Multiple detections by different services at the same time is not allowed.

It is possible to execute a detection in the network frequently in order to dynamically discover included or excluded services while running. This allows to detect if hardware has been added or removed. Read the Routing table section for more information.

Routing table

A routing table is a data structure managed by the Luos network and available for any services on any nodes. This data structure lists all the services in the network and allows any services to access and use basic information of any other services or nodes. The routing table's data can be loaded or auto-generated during detection, and can be refreshed on demand.

Go to the Routing table page for more information.


Communication between services and apps is performed through messages. A message contains information on the destination service(s), the type of operation to be performed (a read or write operation and the type of message), as well as any additional data. The message will be sent in the network and will arrive at the destination, no matter where the service is placed in the network.

Go to the Messages handling page for more information.

Code organization

Because Luos allows you to manage your features as sharable blocks of code, we organized code in a specific way allowing to easily integrate and share packages from your projects with as little friction as possible.

Luos Levels

Different levels on the embedded code are defined, corresponding to the different levels of information needed across your entire product.

ProductA node or a set of nodes that communicate with each other. This is the place were you will have the configurations and information intended for all your boards.
NodeThis is the board's code. This is your actual Eclipse, PlatformIO, IAR, or any other project.
PackageA sharable folder containing code files managing one or more services.
ServiceLuos services can be drivers or apps. Each service can follow Luos profiles to be standard, but it can be custom too.

Product code organization with Luos

  β”œβ”€β”€β”€ product_config.h
  β”œβ”€β”€β”€ Node_1
  β”‚    β”œβ”€β”€β”€ node_config.h
  β”‚    β”œβ”€β”€β”€ main.c
  β”‚    β”œβ”€β”€β”€ main.h
  β”‚    β”œβ”€β”€β”€ Package_1
  β”‚    β”‚     β”œβ”€β”€β”€ package_1.c
  β”‚    β”‚     └─── package_1.h
  β”‚    └─── Package_2
  β”‚          β”œβ”€β”€β”€ package_2.c
  β”‚          └─── package_2.h
  β”œβ”€β”€β”€ Node_2
  β”‚    β”œβ”€β”€β”€ node_config.h
  β”‚    β”œβ”€β”€β”€ main.c
  β”‚    β”œβ”€β”€β”€ main.h
  β”‚    └─── Package_3
  β”‚          β”œβ”€β”€β”€ package_3.c
  β”‚          └─── package_3.h
  └─── Node_3
       β”œβ”€β”€β”€ node_config.h
       β”œβ”€β”€β”€ main.c
       β”œβ”€β”€β”€ main.h
       β”œβ”€β”€β”€ Package_4
       β”‚     β”œβ”€β”€β”€ package_4.c
       β”‚     └─── package_4.h
       β”œβ”€β”€β”€ Package_5
       β”‚     β”œβ”€β”€β”€ package_5.c
       β”‚     └─── package_5.h
       └─── Package_6
             β”œβ”€β”€β”€ package_6.c
             └─── package_6.h

  • Product level

    product_config.h: This file describes the general configuration for the product, such as baudrate, timeout duration, etc. It also groups the list of custom types, profiles, and commands that allow services to communicate together. This folder is the same for all the nodes of the same product and should be included at the node level.

  • Node level

    node_config.h: This file describes the configuration of the board's pinout, USART for communication, timer, and DMA. It also configures the buffer size for Luos communication and the number of tasks. main.c/.h: The main files for the node that calls luos.h, initializes and calls the package (all the different Service_Init, Service_Loop).

  • Package level

    package.c/.h: Contains the Service_Init, Service_Loop, and Service_Msg_Handler. In these files, you can create services dedicated to your functions with Luos API.

  • Service level

    Uses Luos API and Service Profile on your package's code to create either a standard or a custom service that anybody can use.

Where is Luos?

Luos Library is used at the node level. You will need to initialize Luos and call a loop function on your main program to be able to use the Luos API on your packages.


A Luos node is an hardware component connected to a Luos network. In other words, each MCU connected to the system is called a node. A node contains at least one package with one or more servicesSoftware element run by Luos that can communicate with other services. It can be a driver or an app..

The nodes are described by a set of unique characteristics:

  • node_id: node's unique id.
  • port_table: physical port connections.

Each node hosts the embedded Luos API, which allows services to communicate with others in the network. Nodes have specific network hardware access that have to be defined in the Luos's Hardware Abstraction Layer (HAL). They can host, manage, and run services.


The node's embedded code hosts the Luos's embedded code and the node's various functionalities, stored in services. Luos is responsible for creating each node's identity, integrating the node to a Luos network, locating it among the other nodes, communicating with each other, and managing all their services.

Luos Integration

Luos works as a code library running on nodes. To match the Luos library with your hardware, Luos offers a Hardware Abstraction Layer for various devices in LuosHALHardware Abstraction Layer used to fit Luos with various hardware designs..

  • LuosHAL β†—: This repository provides a list of family devices covered to match the Luos library with your hardware.
  • Luos β†—: The main library you will be working with.

To make it work in your environment, you have to:

  • Include the Luos lib folders in your project compilation;
  • Select the right LuosHAL for your device family in LuosHAL folder, and include luos_hal.c, luos_hal.h and luos_hal_config.h in your project;
  • If necessary, overload luos_hal_config.h with a node_config.h file describing specificities of your node. The default configuration created by Luos is an example of an MCU family that can be modified to fit with your design (e.g. match pins with your design);
  • Include luos.h on your main file.

The Luos functions need to be called only in one place for each node, but it should be run constantly.

Luos is like a task that has to be run regularly. The primary Luos functions that should be called to integrate Luos into the embedded code of a node are luos_init() and luos_loop(). They should be added in the main() of your program.

Basically, your main() function will look like this:

#include "luos.h"

int main(void)
    return 0;

Adding this code to a nodeHardware element (MCU) hosting and running Luos and hosting one or several services. makes it able to react to a Luos network. It is now ready to host your services by running packages on your main.

As a developer, you will always develop your functionalities into services and never into the main() program.

Note: The only information that should be put on the main() code are MCU's setup parameters and services' run functions.

A complete software node view

At the node level, communication is achieved by receiving and sending messages with the other components of a Luos network. The nodes can communicate with each other thanks to a specific part of Luos, called Robus.

Robus is the communication protocol provided by Luos and the low layer of Luos technology. It is responsible for functionalities like communication initialization between different nodes, messages' management (message format control, TX, and RX), memory allocation, topology detection, and attribution of messages to the suitable handling level.

Robus executes a format control, and store messages in the msg_buffer of your node. Depending on the specified destination and the type of each message, they are either treated automatically by Robus and Luos or sent to one or several services.

Node Parameters Configuration

Luos allows you to configure some parameters to optimize the memory usage and adapt it to fit your needs. To make it, we advise using a configuration file called node_config.h. Put the file at the root folder of your node project and add it in the compiling variables section of your IDE by adding the following line:

#include node_config.h

You can use it to set all your custom configurations:

  • for the services of your node
  • for Luos library of your node
  • to modify Luos HAL config to make it fit with your design
ParametersDefaults valueDescription
NBR_NAK_RETRY10Number of retries to send after a received NAK.
MAX_SERVICE_NUMBER5Number of services in the node (memory optimization).
MSG_BUFFER_SIZE3*size_msgMessage buffer size. Max size of a message (3 * (7 bytes header + 128 bytes data + 2 bytes CRC)).
MAX_MSG_NB2*MAX_SERVICE_NUMBERMax number of messages that can be referenced by Luos.
NBR_PORT2Number of PTP (port) on the node ( max 8). See electronic design page.

You will find the default configuration for Luos Library in the file config.h β†—,

Check the Luos_hal_config.h of your MCU family to see parameters that can be changed to fit your design.

Note: Every example provided by Luos has a node_config.h file that can be used as a base to fit your project's needs.

Luos Statistics

Into Luos embedded code, you are given the opportunity to obtain important information about different factors of the functioning of each node, as you can find stored several statistical values in the specific field of the structure that describes each node, like for example, the MCUs memory utilization, or timing information.

The statistics of a node can be occupied from any other node of the system, giving you the chance to explore the behavior of all your MCUs by having direct access to any of them.

More details of how to access the statistics are given in the Monitoring tools page.

Luos Hardware Abstraction Layer

Luos can work on a single node (localhost), or create a network to enable communication between services located on different nodes. This communication should be defined and hardware-configured to fit the chosen MCU. The files luos_hal.c and luos_hal.h define all the functions needed by the Luos library to send/receive messages through the bus and initialize all the MCU peripherals.

The file luos_hal_config.h file contains a default configuration for a MCU family and can be redefined in a node_config.h configuration file to fit your design. The configuration of Luos HAL is described here.

Luos HAL

Here are the hardware functions relative to protocol communication and physical bus in Luos:

  • PORT: Defines necessary pins, PTP lines, Rx/Tx, enable/disable.
  • TIMER: Defines timeouts for communication.
  • COM: Serial bus.
  • CRC: Validates the accuracy of the messages. CRC can be generated by hardware (Optional, can be software).
  • DMA: Used to save IRQ time (Optional, can be IT only).
  • FLASH: Stores services' aliases in the system, and used by Luos bootloader.

Network topology

The nodes can access the position of every other node in the network at any time, by accessing a specific structure called routing table. The routing table is designed and shared among the nodes after the execution of a devoted process called detection.

This routing table allows any service to find, locate, and use any other service on the entire network.

Routing Table

The routing table is a feature of Luos allowing every nodeHardware element (MCU) hosting and running Luos and hosting one or several services. to own a "map" (or topology) of the entire network of your device. This map allows nodes to know their physical position and different functionalities, as well as to easily search and interact with the other nodes.

The routing table is designed and shared among all the nodes after a process that is called detection.


PTP is the point-to-point connection between nodes used for topology detection. Every node should have between 2 to 8 PTP connections representing ports. At this time, these wires are mandatory.


With two PTP pins per board, you must chained your device as below:

Star Mounting

With at least three PTP pins per board, you can create a star mounting configuration:


What are packages used for

As explained in the architecture page, Luos is a platform that handles packages execution.

The traditional way of writing code is still "monolithic": all functionalities are used in one big main() function and are tightly dependent with each other. This leads to complex development, debug, and maintainability needs when the code base grows bigger. It also leads to complex collaboration with other developers. Luos tries to separate this monolithic architecture into independant and weak-coupled β†— blocks of code. It does so by giving to developers high-level APIs to create these blocks and make them communicate with each other.

Packages represent these blocks of code. They contain independent functionalities which will be run by Luos.

Relation with services

From a logical view, a package handles one or a group of functionalities, independent from the rest of the system. Still, we don't have any information on how thess functionalities should be executed: it can be run either as a single task or as smaller tasks talking to each other. Services, however, give this control level: a package can initialize as much services as it needs to run its functionalities.

Note: Each package has to run at least one service.

How to properly organize your Luos projects

How to add packages in your project

A Luos node can host multiple packages, and a package has to be as portable as possible. In order to do that, packages have to be independent code folders that can be easily copied and pasted in another project.

When designing projects at Luos, we always use the same way to organize our code: we put packages into a lib folder on a node project, and every packages have their own package folder allowing them to be easily copied and pasted into any other node project:

    β”œβ”€β”€β”€ lib
    β”‚    β”œβ”€β”€β”€ package_1
    β”‚    β”‚    β”œβ”€β”€β”€ package_1.c
    β”‚    β”‚    └─── package_1.h
    β”‚    └─── package_2
    β”‚         β”œβ”€β”€β”€ package_2.c
    β”‚         └─── package_2.h
    β”œβ”€β”€β”€ inc
    β”‚    β”œβ”€β”€β”€ luos
    β”‚    └─── luos_hal
    └─── src
         └─── main.c

Basic packages functions

We choose to put the public functions of our services in the package.h file. As said above, services are lightweight tasks that need to be run regularly. We choose to use the exact same strategy as presented for Luos functions by providing a Package_Init() and a Package_Loop(): services are created in Package_Init(), and the application code is placed in Package_Loop() (see service creation page for more information).

Then packages are initialized and run in the main() function:

#include "luos.h"
#include "package1.h"
#include "package2.h"

int main(void)
    return 0;

This way, it is easy to manage all of your services and add as many of them into main().


A service is a feature API that provides a functionality. Services can communicate with any other services present in the Luos network.

A service can be an application or a driver.

Each service provides a particular set of tasks such as managing a motor, handling a laser range finder, or more complex operations like computing inverse-kinematics. Services are stored into packages.

Each service is hosted in a single nodeHardware element (MCU) hosting and running Luos and hosting one or several services. (MCU). Still, a node can handle several services simultaneously and manage communication between them and other services hosted in different nodes, using the same network interface.

As a developer, you will always develop your functionalities as services into packages and never into the main() program. The only information that should be put on the main() code are MCU setup parameters and packages run functions.

Service properties

To properly work, each service has some properties allowing other services to recognize and access it:

IDThe ID is a unique number given to each service depending on its physical position. The system automatically assigns each ID during the detection phase. If you move a service from a microcontroller A to a microcontroller B on a given device, the ID will change. In the same way, if you change the wiring order of a microcontroller in the network on a given device, the ID will change too.Integer
e.g. id=1
TYPEThe type defines the service purpose. A few types are predefined and can be used, or new ones can be created. The service type can't be changed after service initialization. See the type page for more information.String
ALIASAlias is the name of the service. It's used to easily identify a service. Each service has a default alias which can be changed by the user. For example, a service with the default alias motor can be named left_knee_motor by the user. This new name will be stored in the non-volatile memory of the board. As we don't want to have multiple services with the same name, a duplicate name on your system will be automatically assigned with an incrementing number at its end in the network. You can go back to the default by setting a void name ("") to a service.String
e.g. alias="gate"

Create Luos services

As a developer, you will always develop your functionalities as services into packages and never into the main() program.

Warning: Make sure to read and understand the package section before reading this page.

How to create and initialize a service

To create a service, you have to call this function:

service_t* Luos_CreateService(void* callback, service_type_t type, char* default_alias, revision_t revision);

The returned service_t* is a service structure pointer that will be useful to make your service act in the network after this initialization.

callback is a pointer to a callback function called by Luos when a service receives messages from other services (see messages for more details). This function needs to have a specific format:

void Service_MsgHandler(service_t *service, msg_t *msg)
  • service is the service pointer of the service receiving the data (basically, it is your service).
  • msg is the message your service received.

type is the type of the new service represented by a number. Some basic types (e.g. DISTANCE_MOD, VOLTAGE_MOD, etc.) are already available in the service_type_t enum structure of Luos. You can also add your own and use them with Luos.

default alias is the alias by default for your new service (e.g. Myservice02). This alias is the one your service will use if no other service have the same and if no other alias is set by the user. Aliases have a maximum size of 16 characters.

revision is the revision number of the service you are creating and which will be accessible via Pyluos.

Following the packages rules, here is a code example for a button service:

service_t* service_btn;

static void Button_MsgHandler(service_t *service, msg_t *msg)
    // Manage received messages

void Button_Init(void)
    revision_t ButtonRevision = {.major = 0, .minor = 0, .build = 7};

    service_btn = Luos_CreateService(Button_MsgHandler, STATE_TYPE, "button", ButtonRevision);

void Button_Loop(void)

Services categories

To make your development as clean as possible, you have to understand into which category (Driver or App) each service of the project is located.

By following the categories guidelines, you will be able to make clean and reusable functionalities.

Drivers guidelines

A driver is a type of service that handles hardware. Motors, distance sensors, LEDs are all drivers.

By designing a driver, you have to keep the following rules in mind:

  • A driver service always uses a standard Luos type to be usable by any other services called profiles.
  • A driver service always uses standard object dictionarySet of objects based on SI metric system that can be transmitted through Luos messages. Any object can easily be converted in other units. structures to be usable by any other services.
  • A driver service never depends on or uses any other services (driver or app).
  • A driver service is "dumb", as it can't do anything else than manage its hardware feature (but it does it very well).

You can have multiple driver services on the same nodeHardware element (MCU) hosting and running Luos and hosting one or several services. managing different hardware functionalities of your board. It is up to you to sort them depending on your design.

Apps guidelines

An application or app is a type of service that only manages software items such as functions or algorithms. Apps use other services to make your device act, operate, and behave. Apps can be placed in any nodesHardware element (MCU) hosting and running Luos and hosting one or several services. on a Luos network without any hardware or code modifications. However, the choice of the hosting node can impact the global performance of the system.

By designing an app, you have to keep the following rules in mind:

  • An app can't have hardware dependencies.
  • An app can use custom service types (you can create your own profiles for that).
  • An app must use standard object dictionarySet of objects based on SI metric system that can be transmitted through Luos messages. Any object can easily be converted in other units. data structures (you can create your own object dictionary for that).

Warning: Iff the data structures used are not standard, the gate services could be completely unable to manage them.

Apps are the embedded smartness of your device, and at least one of them should run a network detection in order to map every service in every node in your device and make it work properly. Go to the Routing table page for more information.

Services accessibility

Luos can define and manage the accessibility of services.

This accessibility allows you to specify the access the services can deal with. For example, a STATE_TYPE service (which can handle a basic True/False state) can be used either for a button (read-only) or a relay (write-only).

By default, when you create a new service, it will be on READ_WRITE_ACCESS, telling any other services that they can "send to" or "receive from" this new service. You can change this configuration if you want to.

Services can have the following accessibility:


For example, from the previous initialization example function of the button service, we should specify the accessibility of the service as READ_ONLY_ACCESS:

service_t* service_btn;

void Button_Init(void)
    revision_t ButtonRevision = {.major = 0, .minor = 0, .build = 7};

    service_btn = Luos_CreateService(Button_MsgHandler, STATE_TYPE, "button", ButtonRevision);

    service_btn->access = READ_ONLY_ACCESS;

This doesn't change anything else on your service code as it allows external services to know the accessibility of your service.


The core of Luos technology enables sending and receiving messages from services with a straightforward API. More information about messages can be found in the dedicated page.

Luos APIs

The main Luos embedded technology includes the following tools to integrate more capabilities and functionalities to manage your messages:

Send a Luos messageLuos_SendMsg(service_t *service, msg_t *msg);error_return_t
Read a Luos messageLuos_ReadMsg(service_t *service, msg_t **returned_msg);error_return_t
Send the remaining data in case of long messagesLuos_SendData(service_t *service, msg_t *msg, void *bin_data, uint16_t size);void
Receive the remaining data in case of long messagesLuos_ReceiveData(service_t *service, msg_t *msg, void *bin_data);error_return_t
Send data stored in a streaming channelLuos_SendStreaming(service_t *service, msg_t *msg, streaming_channel_t *stream);void
Receive data from a streaming channelLuos_ReceiveStreaming(service_t *service, msg_t *msg, streaming_channel_t *stream);error_return_t
Share network's baudrateLuos_SendBaudrate(service_t *service, uint32_t baudrate);void
Get the total ticks number from the initialization of LuosLuos_GetSystick(void);uint32_t

Types of services

As explained in the service page, Luos enables message exchanges between services in a straightforward way for developers. Still, to easily use a service you need to know their interfaces or how to interact with them.

In your code, in order to know what are the capabilities and the purpose of any services of your product, you need to know their types.

Standard interface between services

Types allow to define the interface of a service. Types create a common API between services.

The following example uses the most straightforward type available in Luos: STATE_TYPE defines the capability to manage state messages format. With this information, each application that wants to send a message to the LED driver knows how to communicate with this service; it only has to put the state information, and the LED can then be controlled.

To define the capabilities of a specific type, you can use or create profiles. Profiles are API definitions that can be applied to a service.

How to use Luos services profiles

What is a service profile

Now that you understand what a service and a service type are, and how to create them, we can address the subject of profiles.

A service profile is a pre-made API management for a specific service type. Basically, a profile gives a data structure with all variables needed to handle a specific service type. The profile code knows how to deal with these variables and to how share them with any other services.

Thanks to this feature, the variables are shared and accessible by the entire system in real time, without explicit message handling in the service code.

Example: If you want to make a servo-motor service, you have to include the servo-motor profile for your service and use variables of the profile structure to set the rotor current position measurement, or to get the current motor's target in your code.

Profiles are convenient for making your code clean and straightforward, complying with your development into a standard API, or sharing your service type with the community. Luos provides some common profile models that you can use; feel free to contribute and to add your own to the standard profile bank with a pull request on Luos' GitHub page β†—.

How to use a profile in your service

To use this feature, you have to include the profile corresponding to your needs and instantiate the structure that will be brought:

#include "profile_servo_motor.h"

profile_servo_motor_t servo_motor; // create a motor profile structure

Now taht your structure exists, you can access all variables it embeds. Still, it won't be updated by Luos as described above, for this one step remains: you have to create a service and tell Luos that you're using a profile, and that you want the platform to handle it for you.

So far you learned how to create a bare service with the luos_CreateService() call, but not how to give information about the profile you instantiated to the platform. To do so, every profile comes with a Profile_CreateService() function that will make it for you. This function is very similar to the luos_CreateService() routine, except for the type field replaced by the profile you previously created.

To create a service with a motor profile, for example, the dedicated routine is shown here:

service_t *ProfileServo_CreateService(SERVICE_CB callback, profile_servo_motor_t *profile_servo_motor, const char *alias, revision_t revision);

The returned service_t* is a service structure pointer that will be useful to make your service act in the network.

  • callback is a pointer to the same callback function described in the service management section. The main difference between profile services and custom services is that you don't need to manage any message in this callback, because the profile handles it for you. You can use this callback to make your code react to an event or manage custom messages on top of the profile.
  • profile_servo_motor is the profile structure pointer you just created with a type depending on the profile you are using.
  • alias is the alias by default for your new service, e.g. Myprofile02. This alias is the one your service will use if no other alias is set by the user of the functionalities hosted in your service. Aliases have a maximum size of 16 characters.
  • revision is the revision number of the service you are creating.

Following the packages rules, here is a code example for a button service using a state profile:

#include "profile_state.h"

profile_state_t button;

void Button_Init(void)
    // service initialization
    revision_t revision = {.major = 1, .minor = 0, .build = 0};
    // Profile configuration
    button.access = READ_ONLY_ACCESS;
    // Service creation following state profile
    ProfileState_CreateService(&button, 0, "button", revision);

void Button_Loop(void) {
    button.state = (bool)HAL_GPIO_ReadPin(BTN_GPIO_Port, BTN_Pin);

You can notice that you don't send any Luos message to share the button state: if an application wants to access this information, the state profile will share it for you. You only have to update the button state value in your code. Supported profiles are available in this repository.

Routing Table

Warning: Make sure you have read and understood the network topoly section before reading this page.

The routing table is a feature of Luos allowing every serviceSoftware element run by Luos that can communicate with other services. It can be a driver or an app. to own a "map" (or topology) of the entire network of your device. This map enables services to know their physical position and to search and interact with other services quickly.

This feature is particularly used by app services to find other services they need to interact with. The routing table is shared by the service that launches the detection to other services.


The routing table is automatically generated when a service initiates a network detection. It is then shared with other services at the end of the detection. Any service can initiate a detection, but driver services should not run it; this features should be only used with app services by including routingTable.h and using this routing table API.

To run a detection, type:


where app is the service_t pointer running the detection.

A non-detected service (not in the routing table) has a specific ID of 0. At the beginning of the detection, Luos erases each service's ID in the network, so all of them will have ID 0 during this operation. You can use it on your services code to act consequently to this detection if you need it (for example, a service can monitor its ID to detect if a detection has been made and if it has to reconfigure its auto-update).

Then the service running the detection will have ID 1, and the other services will have an ID between 2 and 4096, depending on their position from the service detector. IDs are attributed to the services according to their position from the detector service, and to the branch they are in. ID attribution begins first to the PTPA port, then PTPB, etc. When each service in the network has an attributed ID, the detection algorithm proceeds to create the routing table and shares it with every service (saved only one time per node).

Sometimes, multiple services in the network can have the same alias, which is not allowed to prevent service confusion. In this case, the detection algorithm will add a number after each alias instance on the routing table.


  1. Be careful that a service can change ID during a detection depending on the service running this detection.
  2. Do not consider your service's ID fixed.
  3. Be aware that every service removes its auto-update configuration during the detection to prevent any ID movement.


NodesHardware element (MCU) hosting and running Luos and hosting one or several services. can host multiple services. To get the topology of your device, the routing table references physical connections between the nodes and lists all the services in each one of them.

The routing table is a table of a routing_table_t structure containing nodes or services information. The precompilation constant MAX_SERVICES_NUMBER manages the maximum number of services (set to 40 by default).

routing_table_t routing_table[MAX_SERVICES_NUMBER];

The routing table structure has two modes: service entry mode and node entry mode.

typedef struct __attribute__((__packed__))
    entry_mode_t mode;
        struct __attribute__((__packed__))// SERVICE mode entry
            uint16_t id;                // Service ID.
            uint16_t type;              // Service type.
            char alias[MAX_ALIAS_SIZE]; // Service alias.
        struct __attribute__((__packed__))// NODE mode entry
            // Watch out, this structure has a lot of similarities with the node_t struct.
            // It is similar to allow copy of a node_t struct directly in this one
            // but there is potentially a port_table size difference so
            // do not replace it with node_t struct.
            struct __attribute__((__packed__))
                uint16_t node_id : 12;  // Node id
                uint16_t certified : 4; // True if the node have a certificate
            uint16_t port_table[(MAX_ALIAS_SIZE + 2 + 2 - 2) / 2]; // Node link table
        uint8_t unmap_data[MAX_ALIAS_SIZE + 2 + 2];
} routing_table_t;

Service entry mode

Service entry mode allows the routing table to include information about a service. As a node can host one or more services, the routing table is able to obtain the specific information for each one of them:

  • id: service's unique id
  • type: service's type
  • alias: service's alias

You can read the services page for more information about what services are and how they are used.

Node entry mode

This mode gives physical information about your devices.

The node_id is the unique number that you can use to identify each one of your nodes. At the beginning (or when a reset detection is performed), all node IDs are set to 0. When the RoutingTB_DetectServices API is called, Luos assigns a unique ID to nodes and services in your system topology.

The certified Luos node can be certified for your system by including the Luos licencing number in your product (feature in progress).

The port_table allows sharing of topological information of your network. Each element of this table corresponds to a physical Luos port of the node and indicates which node is connected to it by sharing a node's id.

Here is an example:

As shown on this image, elements of the port_table indicate the first or last service id of the connected node through a given port.

Specific values can be taken by port_table:

  • 0: this port is waiting to discover which is connected with. You should never see this value.
  • 0x0FFF: this port is not connected to any other node.

Note: Routing tables can be easily displayed using Pyluos through a USB gate. Please refer to the Pyluos routing table section for more information.

Search tools

The routing table library provides the following search tools to find services and nodes' information into a Luos network:

Find a service's id from its aliasRoutingTB_IDFromAlias(char* alias);uint16_t
Find a service's id from its type (return the first of the list)RoutingTB_IDFromType(luos_type_t type);uint16_t
Find a service's id from a serviceRoutingTB_IDFromService(service_t *service);uint16_t
Find a service's alias from its id (return the first of the list)RoutingTB_AliasFromId(uint16_t id);char*
Find a service's type from its idRoutingTB_TypeFromID(uint16_t id);service_type_t
Find a service's type from its aliasRoutingTB_TypeFromAlias(char* alias);service_type_t
Find a service's string from its type (return the first of the list)RoutingTB_StringFromType(luos_type_t type);char*
Test if a service's type is a sensorRoutingTB_ServiceIsSensor(service_type_t type);uint8_t
Get the number of nodes in a Luos networkRoutingTB_GetNodeNB(void);uint16_t
Get a node's idRoutingTB_GetNodeID(unsigned short index);uint16_t

Management tools

Here are the management tools provided by the routing table library:

Compute the routing tableRoutingTB_ComputeRoutingTableEntryNB(void);void
Detect the services in a Luos networkRoutingTB_DetectServices(service_t* service);void
Convert a node to a routing table entryRoutingTB_ConvertNodeToRoutingTable(routing_table_t *entry, node_t *node);void
Convert a service to a routing table entryRoutingTB_ConvertServiceToRoutingTable(routing_table_t* entry, service_t* service);void
Remove an entry in the routing table (by id)RoutingTB_RemoveOnRoutingTable(uint16_t id);void
Erase routing tableRoutingTB_Erase(void);void
Get the routing tableRoutingTB_Get(void);routing_table_t*
Get the last service in a Luos networkRoutingTB_GetLastService(void);uint16_t
Get the last entry in a Luos networkRoutingTB_GetLastEntry(void);uint16_t
Get the last node in a Luos networkRoutingTB_GetLastNode(void);uint16_t*


As a developer, you will have to create and use Luos messages to exchange information between servicesSoftware element run by Luos that can communicate with other services. It can be a driver or an app.. In order to do that, you have to understand how messages work.

Message structure

Luos messages are managed by the msg_t structure:

typedef struct{
    header_t header;
    uint8_t data[MAX_DATA_MSG_SIZE];

All messages have a header. A header is a 7-byte field containing all information allowing services to understand the messages' context. All services in the network catch and decode the header of each sent and received message.

data is a table containing data.

Info: MAX_DATA_MSG_SIZE represents the maximum size of messages (default value is 128 bytes);

To send data to any services you want, you will have to first fill out some information on the header.

here is the header_t structure:

typedef struct{
    uint16_t protocol : 4;    /*!< Protocol version. */
    uint16_t target : 12;     /*!< Target address, it can be (ID, Multicast/Broadcast, Type). */
    uint16_t target_mode : 4; /*!< Select targeting mode (ID, ID+ACK, Multicast/Broadcast, Type). */
    uint16_t source : 12;     /*!< Source address, it can be (ID, Multicast/Broadcast, Type). */
    uint8_t cmd;              /*!< msg definition. */
    uint16_t size;            /*!< Size of the data field. */
  • Protocol (4 bits): This field provides the protocol revision. This field is automatically filled; you don't have to deal with it.
  • Target (12 bits): This field contains the target address. To understand the real destination of this field, you have to know the addressing mode contained on the Target_mode field.
  • Target_mode (4 bits): This field indicates the addressing mode and how to understand the Target field. It can take different values:
    • ID: This mode allows to communicate with a unique service using its ID without acknowledgment return.
    • IDACK: This mode allows to communicate with a unique service using its ID with acknowledgment return.
    • Type: This mode sends a message to all services with a given type, e.g. all "Sharp digital distance sensor".
    • Multicast/Broadcast: This mode allows multiple services to catch a message. In this case, the message contains a type of data used by multiple services.
    • NODEID: This mode allows to send a message to all the services of a specific node without acknowledgment return.
    • NODEIDACK: This mode allows to send a message to all the services of a specific node with acknowledgment return.
  • Source (12 bits): The unique ID of the transmitter service.
  • CMD (8 bits): The command defines the transmitted data's type.
  • Size (16 bits): Size of the incoming data.
  • ACK (8 bits): Acknowledgment signal demanded in target modes IDACK and NODEIDACK, after the good reception of the message.

Send a basic message

To send a message, you have to:

  1. Create a message variable
  2. Set the target_mode
  3. Set the target
  4. Set the cmd
  5. Set your data size
  6. Set your data
  7. Send it.

// Create and fill the message info
msg_t pub_msg;
pub_msg.header.target_mode = ID;      = msg->header.source;
pub_msg.header.cmd         = IO_STATE;
pub_msg.header.size        = sizeof(char);[0]            = 0x01;
Luos_SendMsg(service, &pub_msg);

Note: the function Luos_SendMsg returns an error_status that informs the user whether the message was stored in a buffer and is ready to be sent. User can monitor the returned value to be sure that the message will be sent.

Note: the Luos_SendMsg function is non blocking. This function loads the message into a Luos memory and Luos, leaded by your hardware, will send it as soon as possible.

How to handle and receive messages

Message callbacks of services can be difficult to use when a project has high real-time constraints.

Luos provides two different configurations allowing you to choose the best way for you to deal with messages. The message handling configuration is set during the initialization of a service.

Configurationexecution type
Callback (default)runtime callback
Pollingno callback

The following sections detail how the different configurations work.

Callback configuration

This configuration is the default and most common setup. In this configuration, Luos directly calls the service callback during runtime. The time between the physical reception of a message and the callback may vary depending on the luos_loop() function execution frequency.

With this configuration you have no real constraints on the callback's time of execution, you can then reply to a message directly on the callback.

To setup this configuration, you have to simply setup the callback at service creation.

Here is a code example with a button:

void Button_MsgHandler(service_t *service, msg_t *msg)
    if (msg->header.cmd == ASK_PUB_CMD)
        // The message is filled with global variables with proper data
        msg_t pub_msg;
        pub_msg.header.cmd         = IO_STATE;
        pub_msg.header.target_mode = ID;      = msg->header.source;
        pub_msg.header.size        = sizeof(char);[0]            = HAL_GPIO_ReadPin(BTN_GPIO_Port, BTN_Pin);
        // Sending the message
        Luos_SendMsg(service, &pub_msg);

void Button_Init(void)
    // service creation: (callback, service type, Default alias)
    service_t* service = Luos_CreateService(Button_MsgHandler, STATE_TYPE, "button");

void Button_Loop(void)

Polling configuration

This configuration is often used in Arduino libraries to receive information in a basic way. This method allows you to handle messages only when the user wants to do it in the loop of the service.

To set up this configuration, you have to create your service without any callbacks.

See the following code as an example, with a button:

service_t* service;
void Button_Init(void)
    service = Luos_CreateService(0, STATE_TYPE, "button");

void Button_Loop(void)
    if (Luos_NbrAvailableMsg())
        msg_t *msg = Luos_ReadMsg(service);
        if (msg->header.cmd == ASK_PUB_CMD)
            // The message is filled with global variables with proper data
            msg_t pub_msg;
            pub_msg.header.cmd         = IO_STATE;
            pub_msg.header.target_mode = ID;
        = msg->header.source;
            pub_msg.header.size        = sizeof(char);
  [0]            = HAL_GPIO_ReadPin(BTN_GPIO_Port, BTN_Pin);
            // Sending the message
            Luos_SendMsg(service, &pub_msg);

Command and object dictionary

Each message includes a command value that defines the content's type of the message's data.

Another feature included in Luos technology is the Object Dictionary (OD), which aims to maintain interoperability of data format and command's type between servicesSoftware element run by Luos that can communicate with other services. It can be a driver or an app..


Commands is a simple enum list from 0 to N allowing you to choose the data format you want to use on your message. Internally, Luos also uses some messages to manage and detect your system That's why Luos has some reserved commands at the begining of this list. User's commands start at LUOS_LAST_RESERVED_CMD.

Common registers for all services

GET_CMDasks a service to publish its data
SET_CMDsets some undefined data

Generic data

COLORcolor_t (R, G, B)
IO_STATEchar (True/False)
RATIOratio_t (percentage %)
PEDOMETERlong[2] (step number, step time millisecond)
ILLUMINANCEilluminance_t (lx)
VOLTAGEvoltage_t (Volt)
CURRENTcurrent_t (Ampere)
POWERpower_t (Watt)
TEMPERATUREtemperature_t (Β°C)
TIMEtime Second (float)
FORCEforce_t (Newton N)
MOMENTmoment_t (Newton meter N.m)
CONTROLcontrol_mode (control_mode_t)

Configuration commands

REGISTERa registered data [reg_add, data]
REINITchar (True/False)
PIDpid_t float[3] = {p, i, d}
RESOLUTIONresolution parameter for a sensor float
REDUCTIONreduction factor float (e.g. mechanical)
DIMENSIONdimension of an element m linear_position_t
OFFSETdecay float
SETIDsets Dynamixel ID

Space positioning

ANGULAR_POSITIONangular_position_t (deg)
ANGULAR_SPEEDangular_speed_t (deg/s)
LINEAR_POSITIONlinear_position_t (m)
LINEAR_SPEEDlinear_speed_t (m/s)
ACCEL_3Dlong[3](X, Y, Z axis linear acceleration data in Gees)
GYRO_3Dlong[3](X, Y, Z axis rotational acceleration data in degrees per second)
QUATERNIONlong[4] (sensor fused w, x, y, z rotational angles)
COMPASS_3Dlong[3](magnetic field data in micro tesla on each axis)
EULER_3Dlong[3](Pitch, roll, yaw based in degrees with frame reference)
ROT_MATshort[9] (linear math 9 element matrix representation)
LINEAR_ACCELfloat[3] (linear acceleration in body frame coordinates)
GRAVITY_VECTORfloat[3] (Which access gravity effects)
HEADINGlong (360 degrees from North with Y+ axis as the pointer)

Space positioning limits

ANGULAR_POSITION_LIMITmin angular_position_t (deg), max angular_position_t (deg)
LINEAR_POSITION_LIMITmin linear_position_t (m), max linear_position_t (m)
ANGULAR_SPEED_LIMITmin angular_speed_t (deg/s), max angular_speed_t (deg/s)
LINEAR_SPEED_LIMITmin linear_speed_t (m/s), max linear_speed_t (m/s)
TORQUE_LIMITmax moment_t (Nm)
TEMPERATURE_LIMITmax temperature_t (Β°C)

Specific register

PARAMETERSdepends on the service. It can be: servo_parameters_t, imu_report_t, motor_mode_t

You can find the complete list of commands here β†—.

If you want to create new commands for your custom services, you can create and add your own starting at LUOS_LAST_STD_CMD. See how.

Object dictionary

An object dictionary (OD) allows various developers of different services to make them interoperate regardless of the units they use on their code.

Example: If my_service1 uses an angle in radians and my_service2 uses degrees, what is the unit they should use to share the angle information?

An object dictionary defines a set of typical objects that can be transmitted through Luos messages. It allows to send these objects with a unit and to use it in any other units, in other services.

Luos defines objects based on physical values following the SI standard.

Objects and types

Each object in the Object Dictionary has a specific Type. For example:

// Define object angular_position as an angular_position_t type
angular_position_t angular_position; 

You can create your variables using these objects but never set OD variables directly with a value. Instead, you have to use functions available on the Luos OD:

// Set object angular_position
float deg = 12.0;
angular_position_t angular_position = AngularOD_PositionFrom_deg(deg); 

Following this rule, everybody will be able to use your values.

All the types are listed in the table summary at the end of this page.


As many units exist, many conversion functions are available. As a result, they follow logic naming rules in order to quickly find the desired function without having to search for it.

Unit conversions

There are two types of unit conversion: in one way (OD type from the desired unit), and in the other way (OD type to the desired unit):

  • from conversion: Converts a value with a defined unit into a desired OD data.

Format: [type_var] = [type]From_[unit]([value])

// save a linear_position from a mm value
linear_position_t linear_position =  LinearOD_PositionFrom_mm(float mm);
  • to conversion: Converts an OD data into a specific unit.

Format: [value] = [type]To_[unit]([type_var])

// convert the variable linear_position into mm
float mm = LinearOD_PositionTo_mm(linear_position_t linear_position);

Messages conversions

In the same way, both conversions are available for messages (OD type from message and OD type to message):

  • from conversion: Gets a OD data from a message.

Format: [type]FromMsg([type_var], msg)

// get the linear_position from the message msg
void LinearOD_PositionFromMsg(linear_position_t* linear_position, msg_t* msg); 
  • to conversion: Inserts a desired OD data into a message.

Format: [type]ToMsg(type_var], msg)

// insert the linear_position into the message msg
void LinearOD_PositionToMsg(linear_position_t* linear_position, msg_t* msg);

Types and units table summary

Here are listed the existing types:

TypeAvailable prefix and other units
linear_positionnm, ΞΌm, mm, cm, m, km, in, ft, mi
linear_speedmm/s, m/s, km/h, in/s, mi/h
angular_positiondeg, revolution, rad
angular_speeddeg/s, revolution/s, revolution/min, rad/s
forceN, kgf, ozf, lbf,, N.m,,, kgf.m,,
voltagemV, V
currentmA, A
powermW, W
temperaturedeg_c, deg_f, deg_k
color8bit_RGB unsigned char [3]
controlcontrol_t (play, pause, stop, record)
pidasserv_pid_t float [3] {proportional, integral, derivative}

Note: To find out what conversion function to use if you don't know it, replace the characters / or . in the units by the character _. The character Β΅ is replaced by u, and revolution is replaced by rev.

Examples: convert a linear speed to mm/s: LinearOD_SpeedTo_mm_s(); convert a value in ΞΌm to a linear position: LinearOD_PositionFrom_um(); convert a value in revolutions/s to an angular speed: AngularOD_SpeedFrom_rev_s();

Receive and Send Advanced messages

Luos provides advanced sending and receiving mechanisms allowing to deal with various data transmission needs or stategies.

Large data

You will sometimes have to deal with extensive data that could be larger than the maximum 128-byte data on a Luos message. Fortunately, Luos can automatically fragment and de-fragment the data above this side. To do that, you will have to use another send function that will set the messages' size and the data fields.

For example, here is how to send a picture:

// fill the large message info
msg_t msg;
color_t picture[300*300] = {/*Your favorite cat picture data*/};
msg.header.target_mode = ID_ACK; = 12;
msg.header.cmd = COLOR;
Luos_SendData(service, &msg, picture, sizeof(color_t)*300*300);

In the reception callback, here is the code for retrieve the message with the receiving service (the one with ID 12):

color_t picture[300*300];
void services_MsgHandler(service_t *service, msg_t *msg) {
    if (msg->header.cmd == COLOR) {
        if (Luos_ReceiveData(service, msg, (void*)picture) == SUCCEED)
            // The picture is completely received, you can enjoy your favorite cat picture!

Note: If you have to deal with high-frequency real-time data, please read the Streaming section.

Time-triggered update messages

Luos provides a standard command to ask a service to retrieve values from a sensor, called ASK_PUB_CMD. However, sometimes apps need to poll values from sensors. Still, the act of repeatedly retrieving a value using the ASK_PUB_CMD command may result in the use of a lot of bandwidth and take up valuable resources.

You can use the time-triggered auto-update features available from any Luos service in this kind of polling situation. This feature allows to ask a service to send an update of the ASK_PUB_CMD command each chosen amount of milliseconds.

To use it, you have to set up targeted service with a message containing a standard time object dictionarySet of objects based on SI metric system that can be transmitted through Luos messages. Any object can easily be converted in other units. but with a specific command.

For example, to update a service each 10 ms:

time_luos_t time = TimeOD_TimeFrom_ms(10);
msg_t msg; = id;
msg.header.target_mode = IDACK;
TimeOD_TimeToMsg(&time, &msg);
msg.header.cmd = UPDATE_PUB; // Overload the TIME command defined by TimeOD
Luos_SendMsg(app, &msg);

Info: services can handle only one time-triggered target, two services of the same network can't ask for a time-triggered value from the same service.

Warning: To prevent any ID movement, auto-update configuration is reset on all services on each detection (see the routing table page for more information).


On occasion, you will have to deal with small high-frequency data with strong time constraints.

To make it easy, Luos manages streaming channels through ring buffers.

A streaming channel allows you to drastically reduce the time constraints of your Luos network thanks to 2 factors:

  1. The first factor is a method that allows to have a no-real-time service dealing with a strict-real-time one. Both sides have their own loop frequency and time precision.

    • The real-time one is the service opening the streaming channel. It has a high-frequency function called at a precise sampling frequency.
    • The non-real-time one has a slower and unprecise timed function which is called at each chunk_time.
  2. By using streaming channels, you will be able to use big data chunks at low frequencies to optimize the data rate efficiency of the bus. The idea is to exchange big chunks of data between services instead of large amounts of time-constrained small messages flooding all services.


A motor-driver service has strict real-time constraints. If you want to have a smooth positioning movement or measurement, you must update the motor position at a high-frequency (sampling frequency).

First, you have to define the sampling frequency allowing to have a smooth movement or measurement on the motor. Let's take 200 Hz in this example.

On the non-real-time side (the service commanding the motor), you can't have a 200 Hz loop because it probably has other things to do and perhaps doesn't have sufficient time precision. To simplify it, you will have to send trajectory chunks regularly (chunk time), let's say approximately every 1 second.

Based on those numbers, your data chunk size will be:

chunk_size = chunk_time(s) x sampling_frequency(Hz)
chunk_size = 1 x 200 = 200 samples

In our configuration, data chunk needs to be 200 position samples each second, allowing to feed the streaming channel.

Following our example, if we want to send trajectory to the motor, we will have a ring buffer in the motor side managed by the streaming channel. Here are the different states of this ring_buffer:

  1. The service that sends the trajectory must ensure that the motor service always has data to consume. To do that, you have to bootstrap your streaming flux by sending 2 data chunks to start and then send a new data chunk each chunk_time. This way, the receiver always has at least one data chunk (1s in this example) ready to be consumed.
  2. When data chunks are received, the receiver can start consuming data at its sampling frequency.
  3. One data chunk later (1s in our example), the receiver has consumed the first data chunk, and the sender can start to compute the next one.
  4. At the end of the data chunk computation, the sender sends the chunk. Luos adds it to the ring buffer.
  5. The sender sends the next data chunk at each second, and Luos adds it to the ring buffer. At the end of the buffer, Luos puts extra data at the beginning. The consumer pointer also goes back to the beginning of the buffer when it reaches the end. This way, we have infinite data stream without any discontinuity.
  6. You can continue to do this indefinitely.

Note: You can play, pause, stop or record a stream flux with the standard CONTROL command using the control_type_t structure.

How to use it

A streaming channel is always created by the strict real-time service. The other service (the non-real-time one) will send or receive its data chunks using large data messages.

Streaming channel creation

Before starting to use the streaming method, you have to create a streaming channel linked to a buffer into the init function of your real-time service:

#define BUFFER_SIZE 1024
volatile angular_position_t trajectory_ring_buf[BUFFER_SIZE];
streaming_channel_t trajectory;

void Motor_Init(void) {
    trajectory = Stream_CreateStreamingChannel(trajectory_ring_buf, BUFFER_SIZE, sizeof(angular_position_t));
    Luos_CreateService(Motor_MsgHandler, CONTROLLER_MOTOR_MOD, "motor_mod");

Now you can use this channel to receive or transmit a streaming flux:

  • Reception is suited to make our motor move smoothly. A non-real-time service will send us parts of trajectory approximately each second, and our motor will consume angular position at 200 Hz.
  • Transmission is suited to measure precisely the movements of the motor. We can use it to send in a non-real-time way real-time data. In our motor it could be angular position measurement at 200 Hz, for example.

Streaming reception

The streaming reception is used to make a motor move.

When the streaming channel has been created, you can feed it with received messages on the reception callback:

void Motor_MsgHandler(service_t *service, msg_t *msg) {
    // check message command
    if (msg->header.cmd == ANGULAR_POSITION) {
        // this is our trajectory reception
        Luos_ReceiveStreaming(service, msg, &trajectory);

Now your service is able to receive trajectory chunks. For the next step, you need to have a real-time callback (using a timer, for example) which can manage the consumption of this trajectory at 200 Hz:

void 200hz_callback(void) {
    Stream_GetSample(&trajectory, &motor.target_angular_position);

Streaming transmission

The streaming transmission is used to measure the motor movements.

To go the other way and send a sampled signal such as a position measurement, you have to use your streaming channel in reception. First, you have to put values into your streaming channel at 200 Hz:

void 200hz_callback(void) {
    Stream_PutSample(&trajectory, &motor.angular_position);

This way, samples are buffered into your ring buffer, and then you can send this real-time information as you want:

void Motor_MsgHandler(service_t *service, msg_t *msg) {
    msg_t pub_msg;
    // check message command
    if (msg->header.cmd == ASK_PUB_CMD) {
        // prepare a reply message and send
        pub_msg.header.target_mode = ID; = msg->header.source;
        pub_msg.header.cmd = ANGULAR_POSITION;
        Luos_SendStreaming(service, &pub_msg, &measurement);

The Luos_SendStreaming function sends available data on your streaming channel. You can continue to feed your channel with samples at the same time.

Hardware consideration

When creating a Luos network, if you have a product with several nodes, it is mandatory to configure your MCU, to create the physical layer. In Luos's examples on GitHub β†—, Luos uses RS485 with a driver or one-wire communication for this physical layer (see Electronic design page), but other communication ways are possible.

Board examples and driver sources are available on GitHub β†—. We encourage you to use them in the way you want.

You can find the schematic of a Luos-ready board called L0 on Github β†— for a quick hardware example.

Minimum requirements

Luos Library uses hardware and software MCU resources. Here are the minimum requirements to make Luos Library work properly:

MCU consideration

FlashLuos Library uses less than 20 kB of Flash memory
RAMLuos Library uses at least 2 kB of RAM for Library usage and protocol message buffering
Stack512 B
Heap0 needed
GPIO4 in one-wire with 2 ports / 5 in RS485 with 2 ports
Resources1 USART, 1 Timer (option: DMA and CRC)

Network consideration

MCU frequencyBus frequency
>=48 MHz1 Mbps
>=32 MHz500 kbps
>=16 MHz250 kbps
>=8 MHz100 kbps

MCU with Luos

Compatible MCUs

Luos can manage any microcontrollers as long as they have a LuosHAL. If your microcontroller is not supported yet, you can create your own LuosHAL version or contact us:

Check the list of the MCU families Luos covers:Hardware Abstraction Layers for MCU Families β†—,

Default Configuration

Luos libraries are made to run on MCU and use hardware peripheral of the MCU to complete communication between services. In order to configure this low-level part, Luos provides for many MCU families a default configuration that can lead to plug-and-play the Luos library with the chosen MCU family. The peripheral configuration is described in files luos_hal_config.h in the repository Luos HAL β†—, and can be redefined in the node_config.h file to fit with your design.

Luos HAL configuration

To match the pinout and functionality with your design, you can create or use the file node_config.h (see Luos example) As you can see on the default configuration, defined in luos_hal_config.h, you are able to define in the file node_config.h, in the section "LUOS HAL LIBRARY DEFINITION", parameters like pinout, usart, timer, etc.

In this way, you will be able to change the default hardware configuration and use it by calling it in the preprossessor variable section of your IDE, so that it is taken into consideration for your project.

Note: Every example provided by Luos has a node_config.h file, which is included by the PlatformIO project's initialization file, called platformio.ini.

Example of PTPA redefinition:

In luos_hal_config.h this is defined as followed:

#ifndef PTPA_PIN
#define PTPA_PIN                    GPIO_PIN_8

In node_config.h this should be redefined as followed:

#define PTPA_PIN                    GPIO_PIN_11

There are many possible configurations that you can change, but not all of them may be necessary for your design:


PORT_CLOCK_ENABLEActivates clock for GPIODepends on port
RX_EN_PIN/TX_EN_PINChooses pinout to activate Rx/Tx commsNecessary for special comms
COM_TX_PIN/COM_RX_PINChooses pinout for Rx/Tx commsAdapts to the chosen serial bus
PTPX_PIN/PTPX_IRQ/PINOUT_IRQHANDLERChooses pinout, IRQ and callback for the PTP lineNecessary for topology detection


LUOS_COM_CLOCK_ENABLEActivates clock for serialDepends on serial bus
LUOS_COM/LUOS_COM_IRQ/LUOS_COM_IRQHANDLERChooses serial bus, IRQ and callbackAdapts to the serial bus chosen
LUOS_DMA_CLOCK_ENABLEActivates clock for DMANecessary for for Tx
LUOS_DMA_CHANNELChooses ChannelSend Tx


LUOS_TIMER_CLOCK_ENABLEActivates clock for TimeoutNecessary for Timeout
LUOS_TIMER/LUOS_TIMER_IRQ/LUOS_TIMER_IRQHANDLERChooses Timer, IRQ and callbackNecessary for Timeout


PAGE_SIZE/ADRESS_LAST_PAGEDefines space in flash for aliasNecessary to rename service alias

Integrating Luos into an electronic board

To create and match with a default reference design, electronic boards must respect some design rules to work in a Luos network properly.

Electronic design

A Luos-friendly electronic board must contain at least the following elements:

  • 1 MCU β†— (microcontroller unit): It hosts, as a node, the Luos firmware along with the different servicesSoftware element run by Luos that can communicate with other services. It can be a driver or an app. (drivers and apps).
  • At least two connectors: They allow to link boards together into a Luos network and link them as a daisy-chain or star mounting. Through PTP pins, nodes know if there is another node connected to the connector. This is used when the user wants to make a topology detection of the system.

One-wire reference design

Luos' One-wire official connector is DF11-4DP-2DS β†—.

RS485 reference design

Luos' RS485 official connector is DF11-8DP-2DS β†—.

See the default pinout configuration in luos_hal_config.h β†— file for chosen the MCU family.


Luos provides several tools to interface control or monitor a product developed with Luos technology.

GateThe entry point between your device and any cellphone, computer, or cloud application. This is a Luos app that can be placed on any MCU.
PyluosA Python library using JSON API to communicate with the gate.
BootLoderUpdate a node through Luos Network.
MonitoringAll the tools that allow you to monitor a Luos network.


The gate is a central tool of the Luos eco-system. It's allowing the translation of any Luos architecture into a more convenient format for standard software (JSON most of the time) and to stream and receive this formatted information using any means of communication, such as serial interface, Wifi, Bluetooth, Lora, ...

You can use it to take control of any embedded service with any languages on any machine. For example, we use it in Pyluos or ROS.

The gate is a simple embedded app service, so that it can work on any MCU running Luos without any modification.

The gate service must be used with a driver service called pipe that can be hosted into different kinds of nodesHardware element (MCU) hosting and running Luos and hosting one or several services. allowing you to choose the communication way fitting with your project (USB, Wifi, Bluetooth, etc.).

Default gate Process

The default behavior of the gate is optimized for systems that are composed only of drivers and control the entire behavior through a remote machine.

  1. At power-up, the gate makes a network detection to find a pipe service. (Optional).
  2. The gate waits to receive a detection message from a pipe.
  3. At detection command, the gate performs a new detection and generates a formatted routing table to send it back to the pipe.
  4. Then the gate evaluates the time needed to convert the entire network values into the selected format. (Optional)
  5. the gate sets up all the network services to send back their values at the optimal frequency. (Optional)
  6. At this optimal frequency, the gate generates formatted data and sends commands comming from a pipe.

Warning: The gate service refreshes sensors information as fast as possible, so it can be intensive to Luos bandwidth.

The gate and the pipe are two separate services; they can be put on the same node or a separate node.

Putting a gate and a pipe on the same node

In that configuration, you put two services in the node like below:

#include "luos.h"
#include "pipe.h"
#include "gate.h"

int main(void)

  while (1)

In that configuration formatted messages don't pass through the Luos network and stay in localhost.

A gate and a pipe on separate node

When the gate and the pipe are on separate nodes, formatted messages transit into the network using even more bandwidth and adding latency.

The gate configurations

The default process described above can be changed using different configurations that you can use on node_config.h file.

You may need to change it if you have apps on your Luos embedded systems.

ParametersDefaults valueDescription
GATE_BUFF_SIZE1024Maximum size of 1 formatted Data.
GATE_POLLINGNOT DEFINEDNo autorefresh always ask data (more intensive to Luos bandwidth.)
NODETECTIONNOT DEFINEDThe gate do not make detection a power up

If you have an app service on your device managing detections, you should define NODETECTION avoiding useless detection from the gate at boot.

If you have an app service on your device using auto-update, you should define GATE_POLLING avoiding the gate to take the lead on the services your app is using.


/ A Pyluos guide

Pyluos is the standard Python library to manage a Luos system with a computer. In this tutorial, you will learn how to install Pyluos in order to use Luos with Python on a computer through a gate service.


Required: Installing Python and Pip

Warning: In order to use Pyluos library, Python and the Pip package manager must be installed on your computer.

Β« Python is a programming language that lets you work more quickly and integrate your systems more effectively. Β» (Source β†—)

Β« Pip β†— is the standard package manager for Python. It allows you to install and manage additional packages that are not part of the Python standard library. Β» (Source β†—)

If Python is not installed on your computer, download and run the last release according to your computer's OS: β†—.

To install Pip, type the following commands in a console:

curl -o

Installing Jupyter Notebook

The tool Jupyter Notebook is needed for this tutorial. Jupyter Notebook will allow you to type Python commands in an internet browser to communicate with a Luos system, via Pyluos.

Β« The Jupyter Notebook App is a server-client application that allows editing and running notebook documents via a web browser. The Jupyter Notebook App can be executed on a local desktop requiring no internet access (...) or can be installed on a remote server and accessed through the internet. Β» (Source β†—)

Type the following command in the console to install Jupyter:

pip install jupyter

Note: Feel free to consult Jupyter Notebook's β†— documentation.

Installing or updating Pyluos library

You are now ready to install Pyluos. The last Pyluos version is 2.0.0.

In a console, the following command will install the Pyluos library using the Pip package manager:

pip install pyluos

If Pyluos is already installed, it may only need to be updated:

pip install --upgrade pyluos

Pyluos also provides auto-generated pre-releases for advanced developers. You can get it using:

pip install --pre pyluos

Start using Jupyter Notebook and Pyluos

Jupyter Notebook can be launched through a console:

jupyter notebook

In the browser page that opened, the New button creates a new Python file:

Note: In the previous picture, Jupyter uses Python 3, but you also can use Python 2.7 depending on your computer configuration.

The Jupyter work space looks like the following image. On the keyboard, Shift+Enter executes any selected part of the code.

Now you are ready to code using Python.

Import Pyluos

The first thing to do is to call the Pyluos library along with the Device tool inside that library:

from pyluos import Device

This line is always used while programming behaviors and should be called before any connection with the device is made.

Device connection

Connect your device to your computer through a gate with a USB cable.

Configuring USB transfer sizes and latency timer

Some devices may not work correctly with the default USB transfer sizes and latency timer for COM ports on Windows. These parameters can be set to lower values in order to use your device correctly while connected to your computer from a gate.

USB Transfer Sizes: The default value is 4096 Bytes; however if you have issues using your connected device, you should try the minimum possible values both for Receive and Transmit.

Latency Timer: The default value is 16 msec, but you can rise lower it to the minimal value of 1 msec.

To access to these parameters, open the Device Manager in Windows, right-click on the USB Serial Port (COMX) where your device is connected, and click on Properties.

Click on Port Settings tab and click on Advanced... button.

Change the desired values.

These values can give you better results, for example if your device has motors to control.

Connection to the device

Now you should be ready to use the Pyluos library and connect to your device. To do that, you have to create a device object with your device address as an argument.

Your device address can be an IP address ( or my_device.local, for example) or a serial port (COM13 on Windows or /dev/cu.usbserial-DN2YEFLN on Mac).

device = Device('address of the device')

This line makes the connexion between the computer and the device. Python should answer with this kind of message:

Connected to "address to the device".
Sending detection signal.
Waiting for first state...
Device setup.

Only once the connection is set is it possible to start programming behaviors.

Routing table display

Routing table can be readily displayed using Pyluos.

Pyluos can display a list of all the services and their associated characteristics (type, alias, and ID) by filtering the routing table. To display it, use the following command:

Note: device is the name of the network.

Pyluos will give you a list of all services without any topological information:

Type                Alias               ID
Gate                gate                1
Pipe                pipe                2
Voltage             analog_read_P1      3
Voltage             analog_read_P7      4
Voltage             analog_read_P8      5
Voltage             analog_read_P9      6
State               digit_read_P5       7
State               digit_read_P6       8
State               digit_write_P2      9
State               digit_write_P3      10
State               digit_write_P4      11
Angle               potentiometer_m     12

Pyluos also can interpret the routing table and transform it into a tree. This way, we can display a lot more complete information using the following command:


Note: device is the name of the network.

Based on the previous example, Pyluos will give you all information about services and topological information:

  ┃  β•­node 1                Certified            ┃
  ┃  β”‚  Type                Alias           ID   ┃
  ┃  β”œ> Gate                gate            1    ┃
  ┃  β•°> Pipe                pipe            2    ┃
β•‘     ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
β•šβ•β• 0>┃1 β•­node 2                Certified            ┃
      ┃  β”‚  Type                Alias           ID   ┃
      ┃  β”œ> Voltage             analog_read_P1  3    ┃
      ┃  β”œ> Voltage             analog_read_P7  4    ┃
      ┃  β”œ> Voltage             analog_read_P8  5    ┃
      ┃  β”œ> Voltage             analog_read_P9  6    ┃
      ┃  β”œ> State               digit_read_P5   7    ┃
      ┃  β”œ> State               digit_read_P6   8    ┃
      ┃  β”œ> State               digit_write_P2  9    ┃
      ┃  β”œ> State               digit_write_P3  10   ┃
      ┃  β•°> State               digit_write_P4  11   ┃
    β•‘     ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
    β•šβ•β• 0>┃1 β•­node 3                Certified            ┃
          ┃  β”‚  Type                Alias           ID   ┃
          ┃  β•°> Angle               potentiometer_m 12   ┃

In this example, three nodes (MCUs) and their associated UUID are listed, along with their services and related characteristics (type, alias, and ID). The characters after each set of node's services and before the UUID's next node specify which connector is used. For example, 1<=>0 means the first node is connected from its second connector (1) to the first connector (0) of the next node.

Service type

Each service has a type (e.g. Button, Led, etc.). You can either retrieve the service's type from the previous code or with the following line:


service_alias is the alias you got from the previous listing.

Note: Unknown service types are defaulty set for custom service types such as some Luos apps.

Get and set services information

Once you have detected your services, you can use this information like variables.

To access values, you have to address them in the device object following these rules:


For example:

device.rgb_led_mod.color = [50,80,5] # Changes the color of the LED in the "rgb_led_mod" service

device.button_mod.state # Returns the status of the push-button

device.button_mod.type # Returns the service type of the service "button_mod"

device.button_mod.luos_revision # Returns the version of Luos

device.button_mod.robus_revision # Returns the version of Robus

If you use IPython or Jupyter Notebook, you can use auto-completion using the Tab key to find every available object and variable.

Change a service name

The name of any service can be changed by using this code:


For example:


Note: You should restart your device and reconnect to it after this operation.

Note: To get back to the service default name, set a void name ("").

Get a node's statistics

Nodes can send back some values representing the sanity of a node. You can use it to evaluate the Luos needs depending on your particular configuration. The RAM usage of Luos depends on the number of messages the node has to treat and the max Luos loop delay.


For example:

gate statistics :
.luos allocated RAM occupation  = 53%
  .Message stack                = 50%
  .Luos stack                   = 53%
.Dropped messages number        = 0
.Max Luos loop delay            = 16ms
.Msg fail ratio                 = 0%
.Nak msg max number             = 1
.Collision msg max number       = 5
  • luos allocated RAM occupation represents the global Luos RAM usage based on Message stack and Luos stack. You can use this value to know if you need to expand or reduce the amount of RAM dedicated to Luos through the MAX_MSG_NB configuration flag (equals to 2 * MAX_SERVICE_NUMBER where MAX_SERVICE_NUMBER = 5 by default ).

  • Dropped messages number represents the number of messages dropped by Luos. Luos is able to drop messages if they are too old and consume too much memory. If you experience message drops, you should increase the MSG_BUFFER_SIZE configuration flag (equals to 3 * sizeof(msg_t) by default. sizeof(msg_t) -> 7 bytes Header + 128 bytes data).

  • Unlike Message stack, Luos stack, and Max Luos loop delay which are all nodes' related statistics, Msg fail ratio and NAK msg max number are services' statistics. Msg fail ratio gives a ratio of the failed sent messages based on all the messages that the service has sent. NAK msg max number gives the maximum number of NAK received when a message has been sent.

  • The RAM occupation and message drop number are also related to Max Luos loop delay. If Max Luos loop delay is too big, Luos has to buffer more messages between loop executions and consumes more RAM. You can reduce the RAM consumption and messages dropping by reducing the Max Luos loop delay. To do that, you have to call the Luos_Loop() function more frequently.

Full script

from pyluos import Device
device = Device('address of the device')

device.rgb_led_mod.color = [50,80,5]



The bootloader feature allows updating the firmware of any node in a Luos network. It's useful for quickly upgrading your application software without using specific programming tools or physically accessing your boards.

This page first explains how the bootloader works from a high-level perspective, and how to use it on already supported targets (STM32 L4/F4/G4/F0, ATSAMD21).

A third section details the requested changes to make a Luos application compatible with this bootloader.

How it works

The bootloader feature consists of three elements:

  • A CLI on your computer, used to monitor the network and manage the update
  • A gate service connected to your computer that is able to convey commands from the CLI to the Luos network
  • A bootloader code, which is flashed in each node in the network

When you want to update the firmware of node 2 (for example), the CLI tool sends commands through JSON files to the gate, which converts them into Luos commands. During the update, if the node needs to send information to the CLI tool, it sends information to the gate, converting it into JSON files.

The feature has been designed to be as simple as possible, and only four steps can describe the process:

  1. The CLI reboots the entire Luos network (except the gate) on bootloader mode, so that all nodes are ready to receive and save a new firmware.
  2. The CLI sends binary files to the nodes you want to update.
  3. The CLI checks the CRC of the binary files saved by the nodes.
  4. The CLI reboots the entire network in application mode.

From the user's point of view, the CLI only passes through these four steps.

How to use it

In a network with a bootloader flashed in each node, an application running on top of the bootloader, and a gate allowing you to connect the Luos network to a computer, you can send a new binary file to node 2 (for example) by using the CLI tool. First, you can use a command to detect each node in the network:

pyluos-bootloader detect <SERIAL_PORT>

In this example, you will use COM3:

pyluos-bootloader detect COM3

This leads to the following result:

We can see two nodes in the network:

  • node 1 containing the gate app
  • node 2 containing one service

Your goal is to update the button_old service. To do that, you need to flash a new firmware in node 2 (we suppose it is called firmware_new.bin and has been saved in a known location on the user's computer). You can then call the following command:

pyluos-bootloader flash <SERIAL_PORT> -t <target1 target2 ...> -b <file.bin>

We use the port COM3, our target has the ID's number 2, and our binary file is called firmware_new.bin:

pyluos-bootloader flash COM3 -t 2 -b firmware_new.bin

Type this command to see the following text on your screen:

You can see the four steps described in the previous section, plus a few log information. First, the CLI prints the parameters used to program the network:

  • The gate id used to access the network (this option is not active yet).
  • The list of target nodes to program.
  • The binary file used to program nodes.
  • The port on which the gate is connected to the user's computer.

Note: There is a default parameter for the target list: if nothing is set, the node with the ID's number 2 (first node after the gate) is flashed.

Note: If you need help to use the tool, you can type the following command:

pyluos-bootloader flash --help

After the CLI launches the programming process, it checks if the node is ready (or alive) before programming it. Once the process is finished, you can re-run the network detection and see the following:

You can program more than one node by giving an ID list with the option -t:

pyluos-bootloader flash COM3 -t 2 3 4 -b firmware_new.bin

In this example, we programmed nodes with ID numbers 2, 3, and 4.


If the connection with the network or with a node is lost during the update, the bootloader allows you to re-run the process without the need to use specific programming tools (such as a JTAG debugger). The following image shows what happens in case of a loss of connection during the update:

The CLI tells you that you have lost the connection. Now by powering off and on your network and re-running a detection, you should see the following:

The boot_service tells the node is in bootloader mode. You have to re-run the flashing process with the CLI:

pyluos-bootloader flash COM3 -b firmware_new.bin

Note: No matter what problem you encounter during the loading process, you must power off and power back on your network to see all the nodes running in bootloader mode. Once it is done, you have to use pyluos-bootloader detect / flash tools to load the applications and make it work again.

How to add the bootloader feature in your project

CLI tool

The pyluos-bootloader CLI is included in pyluos since v2.0.0, packed with the package when installed with pip.


Since Luos v2.0.0, the gate application handles bootloader commands.

Bootloader binary

The bootloader runs in its own flash partition, which is totally isolated from the application. You have to flash it on your target before you can use the feature. Depending on your hardware, you can use an already available project or make your own bootloader:

Luos already supports your target:

The Luos bootloader is available for the following targets:

  • STM32 F0 / F4 / G4 / L4 families
  • Microchip ATSAMD21J18

Several projects for each of these targets can be found in this repository: You can clone this repository and use the projects for your application or use them as examples to build your own bootloader for your specific target.

Note: Examples are available for several IDEs: L4 / F4 / F0 use platformIO, G4 uses SW4STM32 (an Eclipse-based IDE) and SAMD21 uses MPLAB.

Luos does not support your target yet:

First, you have to enable the bootloader feature in the Luos library. To do so, you must add the -D BOOTLOADER_CONFIG parameter when you invoke your compiler. Then you have to run the library in your main() function as you would do for any project:

Warning: Luos will now run the bootloader application. Be careful not to initialize any package with the ADD_PACKAGE() macro. Luos can only run the bootloader app in bootloader mode, and your package will not be executed.

Now you have to adjust the linker settings: your bootloader has to reserve a portion of the flash, and the remaining memory will be dedicated to the Luos application. You can find the memory layout of the flash summarized in the following picture:

This figure shows a third section called shared_flash, which exchanges information between the bootloader and the application. When you want to port the bootloader on a specific target, you have to specify this layout (e.g. the amount of flash you save for the bootloader, the shared section, and the application). Here is an example for the STM32L432: We choose to dedicate 48 kB for the bootloader, 2 kB (one flash page) for the shared section, and all remaining memory for the application. The translation in the linker file can be seen here:


As for the bootloader, you have to modify the linker file in the application to make it compatible with this feature. Now that we defined the memory layout, the modification is straightforward:

You also have to set up the VTOR register to the APP_ADDRESS. This feature exists in most modern ARM CPUs and allows to jump to applications that are saved at any address in flash. On STM32L4, this register is set in the SystemInit() function:

#define VECT_TAB_OFFSET  0xC800

void SystemInit(void)
    SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */

Note: You can find application examples in

How to deal with no VTOR

Some CPUs don't have a VTOR register (such as all the CPUs based on cortex-m0), so you will have to emulate one. The most convenient solution consists in using a section in RAM to save the application's vectors table. Then you have to find a way for the CPU to look to this section when it has to check the vectors table (by default, it will always look at the first address of the flash memory).

To do so, we modify the application linker to add a dedicated section in RAM:

Then, we initialize an empty vector table in this section:

#define RSVD_SECTION ",\"aw\",%nobits//"
#define _RSVD __attribute__((used, section(RSVD_SECTION)))

static volatile _RSVD uint32_t VectorTable[48];

Now we can copy the application vector table in RAM in the main() function:

for (i = 0; i < 48; i++)
    VectorTable[i] = *(__IO uint32_t *)(0x0800C800 + (i << 2));

/* Enable the SYSCFG peripheral clock*/
/* Remap SRAM at 0x00000000 */

The last line remaps the CPU to the RAM, allowing the CPU to check the vector table at the first address of RAM instead of FLASH.


As a developer, you will encounter bugs. 😲

To help you in your development journey, Luos provides monitoring and debugging mechanisms that can give you the opportunity of having clearer visibility of your network.

Luos self-healing capabilities

Warning: Make sure to read and understand how to create Luos services(./ before reading this page.

Finding, understanding, and managing bugs on multiple boards running multiple services can be hard. To make your life easier, Luos allows you to get some basic information about any problems in your system, allowing you to adapt to them.

Service exclusion

Luos includes an acknowledgment management using the ID_ACK target_mode. This mode guarantees the proper reception of critical messages.

If Luos fails to reach its target using ID_ACK, it will retry 10 times. If the acknowledgment still fails, the targeted service is declared excluded. Excluded services are removed from the routing table to avoid messaging from any services, preserving bandwidth for the rest of the system.

Note: Gates services can report service exclusion through JSON.

Note: Pyluos can report service exclusion through gates.

Luos statistics

Luos monitors some values representing the sanity of your nodes and services.

Note: Gates services can report statistics through JSON.

Note: Pyluos can display statistics through gates.

Node statistics

Inside any service, you can access the host node's statistics values using the luos_stats_t structure. This structure gives you access to several values:

  • memory: Memory statisctics information.
    • rx_msg_stack_ratio: Percentage of memory occupation of Rx message management tasks.
    • luos_stack_ratio: Percentage of memory occupation of Luos tasks.
    • tx_msg_stack_ratio: Percentage of memory occupation of Tx message management tasks.
    • buffer_occupation_ratio: Percentage of memory occupation of the message buffer.
    • msg_drop_number: Number of messages dropped due to a lack of memory (older messages are dropped to be replaced by new ones).
  • max_loop_time_ms: Maximum time in ms between luos_loop executions.

You can access to node statistics by using service.node_statistics.

Service statistics

In any service, you have access to statistics values using the service_stats_t structure. This structure gives you access to a specific service's statistic value:

  • max_retry: Maximum number of sent retries due to a NAK or collision with another service.

You can access node statistics by using service.statistics.


Luos allows you to declare to an entire network a critical failure on a service. To handle it, Luos exposes a LUOS_ASSERT macro that will enable you to test some conditions on it to prevent wrong values. for example:

 LUOS_ASSERT(arg_ptr != NULL);

In this case, if arg_ptr is not initialized, Luos will crash the entire node and send a message to all other services with the file and line where the crash occurred. All other nodes will remove all the services from the crashed node from the routing table.

Note: Gates services can report asserting of other nodes through JSON.

Note: Pyluos can display assert through gates.


An additional monitoring mechanism provided by Luos is the integration of a sniffer MCU into the network. The sniffer is responsible for gathering all the messages that are transferred into a Luos network, transmitting them serially to your computer, and displaying them in a logger, allowing you to examine the behavior of your nodes and services.

The sniffer, which consists of an application and a driver, can be easily ported on a simple MCU in the same way as a serial gate, and it can be connected to your network as any other node. The reception of the messages from the computer is achieved by transmitting the messages serially using a USB cable, while these messages are handled by pyluos (link).

All you have to do is to connect the sniffer to your MCU network and to the computer, initialize the connection using Pyluos after you spot the name of the USB port that the sniffer is connected (for example, COM13)

import pyluos
from pyluos import Sniffer
sniffer = Sniffer('COM13')

and send the command:


Note: Do you need more information on how to debug your application using a sniffer? Contact us at

/ Using Luos with ROS1 and ROS2

Luos comes with a package for the Robot Operating System β†—.

ROS 2 is the default version, but backward compatibility with ROS 1 is ensured via the official ROS 1 bridge. You can get an example of an application using Luos services in ROS 2 with the shared-bike example β†—.

In this tutorial, we will assume you're using ROS 2. If you want to communicate with a ROS 1 ecosystem, begin with this quickstart since ROS 2 needs to be installed, and then refer to the retro-compatibility with ROS 1 tutorial.

Basics: a few ROS-applied-to-Luos concepts

Here is a summary of core concepts in ROS:

  • ROS workspace: This is the directory where you download all the ROS software built from sourcecode. You will likely set up your own projects here, but it is also common to download dependencies at this location, such as luos_ros2. Here we assume your workspace is ~/ros2_ws.
  • ROS node: This is the name given to a single program or script being executed.
  • ROS launchfile: This is a file that launches multiple nodes at once. Several nodes communicate with each other exchanging information in structured pieces of data called messages, being exchanged through named communication channels called topics. Just like the filesystem of your hard drive, topics are hierarchized. E.g. /something/into/something/else/data.
  • ROS package: A package is a folder that contains resources such as nodes, launchfiles, messages, etc. Here, we have two packages named luos_msgs and luos_interface.

With Luos, data coming from/to Luos services are messages sent on topics. Some messages representing popular data types comply to existing message formats, e.g. sensor_msgs/Imu representing IMU data: this message is provided by the sensor_msgs package. Some other messages are Luos-specific and are provided by the luos_msgs package.

There is a particular node whose role is to connect the Luos ecosystem with the ROS ecosystem: the broker, provided by the luos_interface package. The broker is strongly associated with a Luos gate since it connects to one (and only one) gate and seamlessly ensures communication. A broker can be launched with the command ros2 launch luos_interface, which attempts to connect to a serial Luos gate.

With Luos, topics names all start with a prefix being the service's alias and end with suffixes:

  • .../read: this topic is read-only, and a .../write topic exists.
  • .../write: this topic is write-only, and a .../read topic exists. It is often a boolean type that (de)activates some source of data publishing.
  • no suffix: this topic is read-only, and no other related topic exists

For example, /Imu_mod/variables/pedometer/read allows to read-only the current pedometer variable from the IMU service named Imu_mod.


This section details the APIs used by Luos. As of today, the only API is the JSON API. However, new APIs will complete the list in the future.


The JSON formatted data is common and widely used by many programming languages. Luos allows you to convert low-level Luos information into JSON objects, enabling conventional programming languages to interact with your device easily.

To do that, you must add a specific app service called a gate on your device.

How to start using the JSON API

Before using your device through JSON, you have to be connected to the communication flow depending on the node type hosting your gate service.

Then you can start the gate by sending:

{"detection": {}}\r

This command asks the gate to start a topological detection, create a routing table, convert it into JSON and send it back to you.

Routing table messages

Warning: Make sure to read and understand how the routing table works before reading this part.

After the gate starts, the first message you receive is a routing table.

This first message is essential because it contains all the information allowing you to create a code object for your device, including all its features.

Routing table structure

A routing table in JSON consists of a list of the nodes present in the Luos network:

         // node 1
         // node 2
         // node 3
      // ...

Nodes information

Each listed node of the network has basic node information and a list of hosted services:

{ // node 1
   "port_table":[1, 2],
         // service 1
         // service 2
      // ...

Note: To understand the meanings of uuid and port_table, please refer to the routing table page.


Each listed service of a node has basic services information:

{ // service 1

Note: To understand the meanings of type, id and alias, please refer to the service page.

Full routing table example

         "port_table":[2, 65535],
         "port_table":[4, 1],
         "port_table":[5, 3],
         "port_table":[65535, 4],

Below is a visual representation of this routing table:

Service's information messages

When the JSON routing table is transmitted, the gate updates and streams your network data with services information.

This JSON is a "service" object listing all the services by their alias and the values they send:

         "value2":[1, 2, 3, 4]

You can use the same JSON object structure to send data to services.

Here is the list of all values that services can use:

Value nameDefinition
power_ratioPercentage of power of an actuator (-100% to 100%)
target_rot_positionActuator's target angular position (can be a number or an array)
limit_rot_positionActuator's limit angular position
limit_trans_positionActuator's limit angular position
limit_powerLimit ratio of an actuator's reduction
limit_currentLimit current value
target_rot_speedActuator's target rotation speed
target_trans_positionActuator's target linear position (can be a number or an array)
target_trans_speedActuator's target linear speed
timeTime value
compliantActuator's compliance status
pidSet of PID values (proportional, integral, derivative)
resolutionSensor's resolution value
offsetOffset value
reductionRatio of an actuator's reduction
dimensionDimension value
voltVoltage value
currentElectric current value
reinitReinitialisation command
controlControl command (play, pause, stop, rec)
colorColor value
io_stateIO state
uuidService's uuid
renameRenaming an alias
revisionFirmware revision
trans_positionTranslation position value
trans_speedTranslation speed value
rot_positionRotation position value
rot_speedRotation speed value
luxLux (light intensity) value
temperatureTemperature value
forceForce value
momentTorque value
powerPower value
linear_accelLinear acceleration value
gravity_vectorGravity vector value
compassCompass value
gyroGyroscope value
accelAcceleration value
eulerEuler angle value
quaternionQuaternion values
rotational_matrixRotational matrix values
pedometerSteps number value
walk_timeWalk time value
luos_revisionLuos's version
luos_statisticsLuos's memory usage statistics [Rx stack, Luos stack, Tx stack, Dropped messages, Loop delay, Send retry max number]

Here is an example of a message sent by a potentiometer service containing the rotation angle of the associated potentiometer:


Here is an example of a message sent by a gate service about Luos statistics:


Custom parameters and specific messages

Some messages are specifically handled:

Custom parameters can be defined and sent to services through the JSON API, either with Python (Pyluos) or any other programming language on a computer side. Here is an example of a C function that can be implemented in order to send commands to services in a Luos Network, through a gate:

def sendCmd(s, cmd, sleep_time=0.5):
    cmd = cmd + '\r'
s = serial.Serial(sys.argv[1], 1000000)
# detect Luos network
sendCmd(s, '{"detection": {}}')
# set speed mode and compliant mode
sendCmd(s, '{"services": {"controller_moto": {"parameters": 2441}}}')
# set pid parameters
sendCmd(s, '{"services": {"controller_moto": { "pid": [20, 0.02, 90]}}}')
# set speed mode and non compliant mode
sendCmd(s, '{"services": {"controller_moto": {"parameters": 2440}}}')

Parameters are defined by a 16-bit bitfield.

parametersenabling or disabling some measurementLink to structure (GitHub)Stepper, Controller-motor, Servo
parametersenabling or disabling some measurementLink to structure (GitHub)Imu

Other specific messages:

registerMotor memory register filed with [register_number, value]Dynamixel, void
set_idA set id commandDynamixel, void
wheel_modeThe wheel mode parameter for Dynamixel servomotors True or FalseDynamixel, void
delayreduce services refresh rateGate

Services exclusion messages

Services can be excluded from the network if a problem occurs (see self-healing for more information). In this case, the gate sends an exclusion message indicating that this service is no longer available:

{"dead_service": "service_alias"}

Node assert messages

Nodes can assert if a critical issue occurs (see self-healing for more information). In this case, the gate sends an assertion message indicating that this node is no longer available and some details about the crash:


Sending large binary data

Binary data such as, for example, a motor trajectory can't be included in a JSON file if it is too large. In order to allow this type of transmission, the size of the binary data is sent through the JSON, followed by the actual data in binary format.

  • If the data is short, it can be displayed inside the JSON as a regular value (see the different values in service's information messages section), or as a table of several values (for example a motor trajectory).

  • If the data is large, the defined value must be a table of one element, containing only the size of the binary data to be transfered in bytes.

The following example shows a transfer of binary data of 1024 bytes.



This section includes step-by-steps tutorials to help you understand how to use Luos' features. You can also find some project examples to help you start your own projects on the dedicated GitHub page.

Luos tutorials

Tutorials about Luos and its associated tools.

Demo boards tutorials

How to use Luos on existing electronic demonstration boards.

Luos integration tutorials

Tutorials about the integration of Luos in external technologies.

Luos and tools tutorials

This section contains tutorials about Luos and its associated tools.

  • Bootloader: Learn how to use the bootloader feature.

More tutorials to come soon.


In this tutorial, you will learn to use the bootloader feature offered by Luos technology.

The setup is simple and is composed of two l0 boards (see boards for further information on boards we are using). These boards are chained following rules described in hardware considerations.

We will use one of these nodes as a gate and the other as an application node. The second node will host a bootloader, and you will be able to update its firmware through the gate. You need an USB shield to connect to the first node to complete this tutorial.

Note: Tt is not possible to update the firmware on a gate node.

Step 1: Setup development environment

The first thing to do is to install pyluos and PlatformIO if not already done.

Then we will create a working folder on our desktop machine and clone three repositories containing: a gate project, the bootloader, and an application example using the bootloader.

cd home/workspace/ 
mkdir tutorial
cd tutorial/
mkdir bootloader
cd bootloader/

git clone
git clone
git clone

Your folder should then look like this:

If not, please follow the commands listed above.

Step 2: Load gate program

Open Visual Studio Code. On the top left corner, in the Explorer section, click on Add Folder. Search Gate_SerialCom project in Examples/Projects/l0:

Click on Add, and the folder should appear in the explorer section:

Take your first node with the USB shield and connect a USB cable from your PC to the port used for dfu programming (the USB port on the l0 board).

Click on Upload on the bottom left corner of the IDE. Wait for the compilation and flashing to be completed. If successful, you should see the following:

If not, please verify you are properly connected to the correct port of the device.

Step 3: Detect gate node with the CLI

You can now detect your Luos node with the CLI: connect your USB cable on the USB shield port instead of the dfu port and type the following command in a terminal:

pyluos-bootloader detect COM6

COM6 is the identifier of the serial port used to communicate with your node. It is unique for each shield; you can check yours with the Device Manager in Windows or ls -l /dev/ | grep ttyUSB on Linux.

If the command is not recognized by the PC, verify your pyluos installation (see Step 1). After execution, you should see the following:

Step 4: Load the bootloader

Now, you will load the bootloader in the second node. Follow the same process as described in Step 2, but this time open Luos_bootloader/luos_bootloader_f072RB project.

Connect the second node with the dfu port and click on Upload; you should see the following:

Step 5: Detection

Connect your two nodes through the Luos network and connect your USB shield to your PC. Type the same command used on Step 3, and you should see the following:

boot_service indicates there is a bootloader running on your node. You are now able to communicate with it through the gate and load any application.

Step 6: Compile an app compatible with the bootloader

Some applications examples are available with the bootloader feature, and they can be used as a template for your custom application. They can be found in luos_bootloader_app/. As we are using l0 boards, open luos_bootloader_app/luos_bootloader_application_f072RB in VSCode.

This time, click on Build instead of Upload as you don't want to download it to the node: the bootloader will do it for you.

The generated binary file can be found in luos_bootloader_app/luos_bootloader_application_f072rb/.pio/build/l0, its name is firmware.bin. You can use the bootloader to load it:

cd luos_bootloader_app/luos_bootloader_application_f072rb/.pio/build/l0

pyluos-bootloader flash COM6 -t 2 -b firmware.bin

For further information on options used with the CLI, please read the dedicated documentation page or type the following command:

pyluos-bootloader --help

The following lines should appear after typing the command:

If any problem occurs during the loading process, please reboot your system and retry to type the command (you can also find information here).

Then relaunch a detection (as done in Step 3):

You can see that boot_service has been replaced by button_old, which is the name of the service running in your app: the bootloader switched to application mode and launched your app.

Step 7: Update your app

You also can update your app and reload it in the node. As an example, change the name of your service from button_old to button_new:

Load the app with the bootloader and relaunch a detection:

pyluos-bootloader flash COM6 -t 2 -b firmware.bin

pyluos-bootloader detect COM6

You should see your updated service running in your node:

You have reached the end of this tutorial. You are now able to use the bootloader feature included in Luos. You know the basics, but you can find some helpful information on the dedicated documentation page.

Demo boards tutorials

Luos can be quickly demonstrated using demonstration boards, such as Arduino boards. This section contains the tutorials about different electronic demonstration boards from various technologies.

More tutorials to come soon.

A general guide to Luos electronic boards

Luos library has been designed to run on low-cost hardware. It works with all Arm microcontrollers, starting with the smallest and cheapest one: the Cortex-M0.

The demonstration boards are a set of small electronic boards examples, each hosting Luos and providing an electronic function (motor, distance sensor, battery, LED, potentiometer, etc.). These boards can be used to test the technology or to quickly develop an electronic device prototype in order to prove a concept without any knowledge in electronics: demonstration boards are connected with cables, behaviors can be programmed through a gate board on a computer, and the device can be tested in a matter of minutes!

Luos provides simple electronic boards examples to build in order to demonstrate the Luos modular technology. These examples are available on Github and contain a schematic file and a Kicad file so that they can be easily reproduced to test Luos.

Boards general specifications

Almost every demonstration board in the provided examples is composed of a motherboard and a shield board. The motherboard, called L0, has a nodeHardware element (MCU) hosting and running Luos and hosting one or several services. that hosts Luos. The shield board is added to an L0 to type it with an electronic function.

Note: Power category boards don't include an L0 motherboard as they provide only power functions and don't need communication. However. The communication data passes through their connectors to other communicating boards.

Here are the specifications of this motherboard:

  • Board name: L0
  • MCU: STM32f0
  • Dimensions: 20 x 26 mm
  • Supply Voltage: 5 V to 24 V
  • Output Voltage: 5 V
  • Connectors: 2x Robus connectors (DF11-8DP-2DS(24) β†—)
  • Sockets: 2x 6 connectors (826926-3 β†—)
  • Other Output: 1x micro-USB
  • USB Serial Speed: 1 Mbaud/s

see Example demonstration board β†—.

Plugging boards together

Luos boards have at least two connection ports in their design. All connectors are the same so that any board can be connected to another using any of these ports. Just avoid making a loop circuit, otherwise you will inhibit communication between services.

There is a right side to plug a cable's connector to a board. The small tab on the connector must face upward to plug correctly, as shown on the following pictures:

Wrong side imgRight side img
Wrong side, the upper surface is flatRight side, the tab is visible on the upper surface

Power management

Luos boards can share their power inputs through the network connection, allowing you to feed other boards. These boards belong to the power category. All the Luos boards can manage a voltage between 5 V and 24 V, up to 7 A.

In a Luos network, you can have multiple power category boards. In this case, the power board with the highest voltage takes over and shares its power with other boards.

For example, for a device using a 12 V motor and a USB board, the USB board belongs to the power category; so it shares its 5 V into the network's wires. Still, you need 12 V for your motor, so you will have to add a 12 V AC plugboard in your network to supply the motor. In this case, the USB board doesn't share its power; only the AC plugboard does, because 5 V is inferior to 12 V.

Some components need a specific voltage to work correctly. For example, to use a standard servomotor, you have to feed the Luos network with 5 V or 7 V. If you need to combine 7 V and 12 V motors in a system, for example, you can manage multiple voltages on the same network using a power isolator board.

External communication management

The boards from the Communication category allow you to control a Luos network easily. These boards host a service called "gate", they can communicate using different kinds of technologies and reach devices outside the device.

To start using Luos technology, you have to use at least one of these gates to be able to program your machine's behaviors.

The "gate" service's task is to stream the Luos network activity into a standard JSON format file, and on the opposite to allow an external device to easily interact with any device in the network.

This way, it is easy to use your favorite device and language to interact and control your device.

We created an open-source Python library managing this JSON API called Pyluos. Feel free to use it, copy it, and convert it into your favorite language. We are open to review your contributions in any programming languages. You can suggest any change or new API on the Luos' community on Reddit β†—.

Get Pyluos on GitHub β†—.

Basic button-LED device tutorial

In the following steps, you will learn to make a simple behavior with a Luos RGB LED demo board and a Luos button demo board step-by-step.

What you will need

In the following example, we will make a LED turn on and off by pushing and releasing a button. You will need the following boards and accessories:

  • 1x Luos LED board
  • 1x Luos Button board
  • 1x Luos USB board and a USB cable
  • 2x Luos cables


1. Configure your computer

The default tool we use to control a Luos network is a board hosting a Gate service with a Python lib called Pyluos.

To begin, you have to install Python and Pyluos library, following the Pyluos documentation page.

2. Plug the boards together

Plug together all the boards with cables. You can plug them into any of the two connectors of each board, in any order.

Warning: Don't close a loop with the boards at each extremity.


From left to right: LED, Button, and USB. The plug order doesn't matter.

3. Connect the device to a computer

Plug the USB board to a computer with micro-USB to USB cable.

In this particular example, there is no high consumption component, so we can use the power given by USB.

Your device is now powered and connected. All the low-level code and electronics are ready to use to help you program your behaviors.

USB board

4. Interact with the device

The USB node handles a specific service called "gate". Other boards are hosting a "gate" service and using a different connection than USB. These particular services convert Luos services data into something easier to understand and manage, using JSON API.

Interacting with the Luos system and program behaviors will require spotting the USB connection on your computer. In the following example, the associated port is COM13.

Once you know the port, you can connect using:

import pyluos
from pyluos import Device
device = Device('COM13')

When Pyluos establishes the connection with a gate service, it asks to run a network detection. This detection allows discovering all boards wired together in the network.

To list the discovered boards, you can run:


In this tutorial, Python should find three boards, the gate (USB), the LED, and the Button boards. You can check that all are detected:

Type                Alias               ID
Gate                gate                1
State               button              2
Color               rgb_led             3

Knowing the alias of the boards, you can use them in your code. To read values from a sensor, you just have to read a variable. For example, you can see the button state using this code:


Python will answer True if you execute this line by pressing the button and False if you don't.

In the same way, you can control a board by setting variables. In the following example, we can control the LED color using RGB values. Type and execute the following line:

device.rgb_led_mod.color = [50,80,5]

The LED turns on.

Changing to the value [0, 0, 0] will turn it off.

5. Write a simple beahvior

You can now write a simple behavior that makes the LED turn on when pushing the button and turn off when releasing it.

# Import libraries
from pyluos import Device
# Establish connection with the Luos network
device = Device('COM13')

# Use an infinite loop to put the behavior inside
while 1:
    if (device.button.state == True): # if the button is pushed
        device.rgb_led.color = [0,15,15] # Assigns a color to the LED
    else: # If the button is released or idle
        device.rgb_led.color = [0,0,0] # Turns the LED off

Test your behavior by executing the code.

LED board

Don't hesitate to share your projects with the Luos community β†—.

Servomotor-LED-potentiometer device video tutorial

The following video shows a basic tutorial explaining how to make a LED and a servomotor responsive to a potentiometer.

Don't hesitate to share your projects with the Luos community β†—.

Set up a Luos-Arduino project tutorial

Introduction to Arduino

The Luos library can be used with PlatformIO, the IDE Luos company uses, but you can also use Arduino IDE.

For now, only Arduino SAM Board 32-bits ARM cortex-M0+ are compatible with Luos Library.

You can download Arduino IDE compatible example and Luos Library with Luos Release β†—.

Arduino β†— is an open-source electronic platform based on easy-to-use hardware and software. The Luos library can be used in Arduino IDE and examples are provided to test it on Arduino hardware quickly. For now, only Arduino SAM Board 32-bits ARM cortex-M0+ are compatible with Luos Library (Arduino Zero, Arduino MKR Wifi, Arduino MKR FOX, Arduino MKR WAN, Arduino MKR GSM, Arduino NB, etc.).

Creating a Luos Network with an Arduino board is very easy. Use D0 and D1 for Tx and Rx, D2 and D3 for Rx_En and Tx_En (RS485 configuration), D5 and D6 for PTPA and PTPB. See the hardware topics page for more information.

Getting Started

  1. Install the Ardiuno IDE from the Arduino β†— website.
  2. Copy and paste the following URL into the File > Preferences > "Additional Boards Manager" textbox.

  1. Install the Luos adapted Arduino SAMD Library in Boards > "Add board definition" > Search for "Luos" > Install "Luos adapted Arduino SAMD (32-bits ARM Cortex-M0+) Boards"

  2. Install the official Arduino SAMD Library in Boards > "Add board definition" > Search for "SAMD" > Install Arduino SAMD Library:

  1. Download Luos Library for Arduino environment with an example on GitHub β†—.

  2. Include Luos Library for Arduino to your Arduino IDE:

  1. Use provided Luos example, then compile and upload it to your Arduino board:

Luos integration tutorials

Luos can be integrated with other technologies and work along with them to expand development possibilities. This section lists the tutorials about the integration of Luos in external technologies, such as ROS 2.

More tutorials to come soon.

Install ROS 2 tutorial

Install ROS 2 and Luos

First, install ROS 2 Foxy β†— for your OS with FastRTPS. Also, install colcon β†— as advised in the guidelines. If you are not familiar with ROS, you should go on with a couple of ROS 2 tutorials to get started.

Then clone luos_ros2 to your workspace and compile it with colcon:

cd ros2_ws/src/    # Or replace by your ROS 2 workspace name, maybe also dev_ws/src
git clone
cd luos_ros2/luos_interface
pip3 install --no-warn-script-location .
cd ~/ros2_ws && colcon build --symlink-install
source ~/ros2_ws/install/setup.bash

Get started with Luos in ROS 2

Plug your Luos gate in and other services, such as IMU, Color or State, and run the broker. It will return the detected services:

~$ ros2 launch luos_interface
[INFO] [luos_broker]: Connecting to /dev/ttyUSB0...
[INFO] [luos_broker]: Found services:
Type                Alias               ID
Gate                gate                1
Imu                 Imu_mod             2
State               button_mod          3
Color               rgb_led_mod         5

Note: If you have several Luos gates, you need several brokers. Specify a port and a unique name for each of them:

ros2 launch luos_interface device:=/dev/ttyUSB1 name:=brokerUSB1

According to the services you have plugged-in, the broker will automatically publish the relevant topics in the namespace of your services' aliases. Here we have plugged a State service (alias button_mod), a Imu service (alias Imu_mod) and a Color service (alias rgb_led_mod) to the gate; thus the broker publishes the following topics:

~$ ros2 topic list

Most variables advertise both /read and /write topics to get data, write data, or (de)activate this data source. Other data types might be aggregates of Luos variables (such as the IMU) or Luos events.

In order to echo messages from the terminal, use a regular ROS subscriber. For instance, here is the current IMU data:

~$ ros2 topic echo /Imu_mod/imu
    sec: 1581267954
    nanosec: 5449
  frame_id: Imu_mod
  x: 0.97814
  y: 0.052695
  z: -0.042831
  w: 0.196548
orientation_covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
  x: 0.0
  y: 0.0
  z: 0.0
angular_velocity_covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
  x: 0.177171
  y: 0.04968
  z: 0.176722
linear_acceleration_covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

In order to publish messages to the Luos services, use a regular ROS publisher. For instance, here is how to light up the Luos RGB service, in a pink color:

ros2 topic pub /rgb_led_mod/variables/color/write std_msgs/msg/ColorRGBA "{r: 64, g: 0, b: 64}" --once

Get started with my own ROS2 package using Luos in Python

These command lines will create a new package my_luos_ros2_package relying on luos_interface:

cd ~/ros2_ws/src
ros2 pkg create my_luos_ros2_package --build-type ament_python --dependencies luos_interface

You can then add your ROS Python scripts by taking examples on the shared-bike example page.

ROS 2 package example: Shared-bike example tutorial

This is a Luos example using ROS 2, the shared-bike application, that works this way:

  • The bike's LED pops up in steady green when it is idle.
  • Shake the IMU while it is idle, meaning the bike is being stolen; the RGB alarm then flashes in red.
  • Press the state button to acknowledge the alarm.
  • Press again to start riding the bike, the bike's LED slightly blinks in green.
  • Press again when you stopped riding the bike, it becomes idle again.

It relies on the 3D visualizer embedded in ROS 2, named RViz 2:

Shared-bike example

Test the example

This example package relies on luos_interface. Make sure you first installed it by following its own procedure.

Then, download the example package and build your workspace with colcon:

~/ros2_ws/src/$ git clone

~/ros2_ws/$ colcon build --symlink-install    # Build the ROS workspace
~/ros2_ws/$ source ~/.bashrc                  # Source all new launches messages and resources

Plug at least a Luos Imu node and a gate to your computer, as well as optional RGB and State services. The expected Luos services' aliases are the default. If they are not, update the topic names β†— with your custom aliases.

Then, start the bike example from its launchfile:

~/ros2_ws/$ ros2 launch luos_bike_alarm_example

RViz2 will pop up and show a 3D bike. Shake the Luos Imu node in order to update the RViz2 view in real-time. If the bike is displayed but does not actuate, make sure that Imu data comes from the expected topic /Imu_mod/imu, or try to change the topic name.

ROS 1 retro-compatibility with Luos tutorial

Luos with ROS 1

The ROS 1 Bridge allows ROS 2 packages such as luos_ros2 to communicate with a ROS 1 ecosystem. However, both ROS 1 and 2 need to be installed, and the bridge takes care of the translation. This procedure has been tested with ROS 1 Noetic + ROS 2 Foxy and Python 3.8.2 in Ubuntu. It might work with older distributions, although it has not been tested.

1. Install ROS 2 and Luos

Make sure you have first installed ROS 2 and managed to run the broker in ROS 2 with the command ros2 launch luos_interface

We assume your ROS 2 workspace is ~/ros2_ws.

2. Install ROS 1 and the ROS 1 bridge

Then install ROS 1 Noetic on Ubuntu 20.04 β†—. We assume your ROS 1 workspace is ~/ros_ws.

3. Initialize the bridge

The bridge has its own workspace that is to be compiled with colcon (ROS 2):

mkdir -p ~/ros1_bridge_ws/src
cd ~/ros1_bridge_ws/src
git clone -b foxy
source ~/ros_ws/devel/setup.bash
source ~/ros2_ws/install/setup.bash
cd ~/ros1_bridge_ws
colcon build --packages-select ros1_bridge --cmake-force-configure --cmake-args -DBUILD_TESTING=FALSE

4. Start Luos in ROS 1

In terminal 1: ROS Core in workspace ~/ros_ws

source ~/ros_ws/devel/setup.bash

In terminal 2: ROS 1 Bridge in workspace ~/ros1_bridge_ws

source ~/ros1_bridge_ws/install/setup.bash
ros2 run ros1_bridge dynamic_bridge --bridge-all-2to1-topics

In terminal 3: Luos broker in workspace ~/ros2_ws Plug some Luos services before starting the broker.

source ~/ros2_ws/install/setup.bash
ros2 launch luos_interface

In terminal 1: Your ROS 1 app in workspace ~/ros_ws Let us consider that rostopic is the ROS 1 app you want to run with Luos services.

source ~/ros_ws/devel/setup.bash
rostopic list

You can then publish and subscribe to the available topics in ROS 1.

Technical FAQ

Here is a list of the issues our users sometimes encountered while using Luos and how to solve them.

What is your issue?

Do you encounter an issue that is not on the list? >> Submit it!

Application doesn't retrieve driver's data after pluging in a gate to my system.


An application can retrieve data to a certain rate from a driver in a Luos system. However, after pluging in a gate to the system to use Pyluos, the application doesn't seem to retrieve data anymore. Pyluos can still detect the packages of the system and can retrieve the data from the driver, as well as see the application running.

Possible explanation

When you plug a gate in a Luos system, Pyluos automatically asks it to perform a detection, making the drivers' IDs to change. The application is not able to retrieve data from the driver because it doesn't know its new ID.


To make it resistant to detection, you need to reconfigure the driver's auto-update from the application that calls it, after each detection. You have to manage this configuration in a loop, and catch the application's ID changes.


A button-LED application that turns the LED on and off with a button. It contains:

  • 1 LED package (Driver)
  • 1 button package (Driver)
  • 1 led_control package (Application)

The application called led_control gets the state of the button and turns on the LED. To do it, the button's driver sends information on its state every 100ms to the application. Here is the initialization of this application:

Initial code

uint16_t id_led, id_but;
void LedControl_MsgHandler(container_t *container, msg_t *msg)
    if (msg->header.cmd == IO_STATE)
        // This is a message from the button, update the LED state
        msg_t pub_msg;      = id_led;
        pub_msg.header.target_mode = IDACK;
        pub_msg.header.cmd         = IO_STATE;
        pub_msg.header.size        = 1;[0]     = msg->[0];
        Luos_SendMsg(container, &pub_msg);
 void LedControl_Init(void)
    revision_t revision = {.unmap = [0.0.1]};
    container_t* app = Luos_CreateContainer(LedControl_MsgHandler, LEDCONTROL_APP, "App_LedControl", revision);
    //Detect all containers of the network and create a routing_table
    //Get the ID of our LED from the routing table
    id_led = RoutingTB_IDFromAlias("myled");
    id_but = RoutingTB_IDFromAlias("mybutton");
    //Auto-update messages
    msg_t msg;      = id_but;
    msg.header.target_mode = IDACK;
    time_luos_t time       = TimeOD_TimeFrom_ms(100.0);
    TimeOD_TimeToMsg(&time, &msg);
    msg.header.cmd = UPDATE_PUB;
    Luos_SendMsg(app, &msg);

Here is how to reconfigure after a detection:


 void LedControl_Loop(container_t *container, msg_t *msg)
        static uint16_t previous_id = -1; allowing us to monitor the detection state
        // Check the detection status.
    if (RoutingTB_IDFromContainer(app) == 0)
        // No ID, meaning either no detection occured, or a detection is occuring right now.
        if (previous_id == -1)
            // This is the start period, we have to make a detection.
            // Be sure the network is powered-up 20 ms before starting a detection
            if (Luos_GetSystick() > 20)
                // No detection occured, do the detection
            // A detection is being made, we let it finish.
            // reset the previous_id state to be ready to setup container at the end of detection:
            previous_id = 0;
            if (RoutingTB_IDFromContainer(app) != previous_id)
                // This is the first loop execution after a detection, here goes the initial configuration:
            //Get the ID of our LED from the routing table
            id_led = RoutingTB_IDFromAlias("myled");
            id_but = RoutingTB_IDFromAlias("mybutton");
            //Auto-update messages
            msg_t msg;
        = id_but;
            msg.header.target_mode = IDACK;
            time_luos_t time       = TimeOD_TimeFrom_ms(100.0);
            TimeOD_TimeToMsg(&time, &msg);
            msg.header.cmd = UPDATE_PUB;
            Luos_SendMsg(app, &msg);

Associated documentation page(s):

How to update my USB drivers


When connecting to a Luos system through a gate, an error message appears on PlatformiO:

Possible explanation

Your USB drivers are not up-to-date on your computer.


Update your USB driver

You may need to update you USB driver. The process depends on your OS:


You need to install and run Zadig by following this tutorial on GitHub.

Installing the drivers:

PlatformIO's screen after successful driver installation:


You need permissions to access the device for non-root users:

echo 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", GROUP="plugdev", MODE="0666"' > /etc/udev/rules.d/60-luos.rules


You need to install libusb:

ruby -e "$(curl -fsSL" < /dev/null 2> /dev/null
brew install libusb

Associated documentation page(s):

The default Luos example does not compile


Luos provides examples for different MCUs, and a HAL for MCU families. You may change Luos HAL definition in node_config.h to fit with your design and your MCU. After the setup of your environment, building Luos example may create an error with CRC or IrqHandler definition

Possible explanation

  • CRC case: You may not have a CRC hardware peripheral on your MCU. If your MCU has hardware CRC, you may have chosen #define USE_HW_CRC 1 in node_config.h.

If you have this peripheral in your MCU, it may not support polynome 16 (the polynome Luos chooses for CRC calculation).

  • IrqHandler case: It is possible that you want to use the default configuration while the IrqHandler chosen by Luos does not exist in your MCU.


  • CRC case: If you don't have a CRC hardware peripheral on your MCU, or if your MCU does not support polynome 16, you must change to #define USE_HW_CRC 0 in node_config.h.

  • IrqHandler case: Check your startup file for the default MCU definition of IrqHandler in you MCU. If Luos default configuration does not match with your MCU, you must redefine the used peripherial in node_config.h.

Example and Solution

Below is an example of the file node_config.h in your project:

 *    Define                  | Description
 *    :-----------------------|-----------------------------------------------
 *    MCUFREQ                 | Put your the MCU frequency (value in Hz)
 *    TIMERDIV                | Timer divider clock (see your clock configuration)
 *    USE_CRC_HW              | define to 0 if there is no Module CRC in your MCU
 *    USE_TX_IT               | define to 1 to not use DMA transfert for Luos Tx
 *    PORT_CLOCK_ENABLE       | Enable clock for port
 *    PTPx                    | A,B,C,D etc. PTP Branch Pin/Port/IRQ
 *    TX_LOCK_DETECT          | Disable by default use if not busy flag in USART Pin/Port/IRQ
 *    RX_EN                   | Rx enable for driver RS485 always on Pin/Port
 *    TX_EN                   | Tx enable for driver RS485 Pin/Port
 *    COM_TX                  | Tx USART Com Pin/Port/Alternate
 *    COM_RX                  | Rx USART Com Pin/Port/Alternate
 *    PINOUT_IRQHANDLER       | Callback function for Pin IRQ handler

 *    LUOS_COM_CLOCK_ENABLE   | Enable clock for USART
 *    LUOS_COM                | USART number
 *    LUOS_COM_IRQ            | USART IRQ number
 *    LUOS_COM_IRQHANDLER     | Callback function for USART IRQ handler

 *    LUOS_DMA_CLOCK_ENABLE   | Enable clock for DMA
 *    LUOS_DMA                | DMA number
 *    LUOS_DMA_CHANNEL        | DMA channel (depending on MCU DMA may need special config)

 *    LUOS_TIMER_CLOCK_ENABLE | Enable clock for Timer
 *    LUOS_TIMER              | Timer number
 *    LUOS_TIMER_IRQ          | Timer IRQ number
 *    LUOS_TIMER_IRQHANDLER   | Callback function for Timer IRQ handler

 *    FLASH_SECTOR               | FLASH page size
 *    PAGE_SIZE               | FLASH page size
 *    ADDRESS_LAST_PAGE_FLASH | Page to write alias

#define USE_HW_CRC 0



#define LUOS_TIMER                TIM3
#define LUOS_TIMER_IRQ            TIM3_IRQn

Associated page(s):

Application blocked in an infinite loop (default handler)


No PTP detection. When an IRQ happens and no IRQ handler is defined, the MCU goes into a Default_Handler:Infinite_Loop.

Possible explanation

You may change the definition of Luos HAL in node_config.h to fit with your design and your MCU. Luos HAL uses macro to define IRQ handler. However, in the case of pinout in some MCUs, the declarations of IRQ handler are in the a special file IT.c.


Luos uses IRQ for the PTP pin. If you redefine the PTP pin to fit with your design, check if there is an IT.c file and fit the redefinition in the IRQ Handler.

Example and Solution

Example for STM32 in the file STM32f0xx_it.c:

default configuration :

void EXTI4_15_IRQHandler(void)

after redefinition:

void EXTI3_IRQHandler(void)


void EXTI4_15_IRQHandler(void)

Associated documentation page(s):

Add an issue

If you encounter an issue that should appear in the FAQ, you can describe as precisely as possible and contact us by email, ask your question on the Luos community Reddit page β†—, or add an issue on Luos GitHub page β†—.