Welcome to Luos documentation
Luos revision: 1.1.0
Introduction
We started designing Luos with the conviction that building electronic systems should be made easier than it is today. Most of the time should be spent on designing the applications and behaviors instead of on complex and time-and-money-eating technicalities. To give a simple example, adding a new sensor —for instance a distance sensor— to an electronic device in conception should not take more than a few minutes. So you can try, test and iterate fast on a project to truly design what users want.
Luos works like a microservices architecture in the software world, and a containerization platform: it encapsulates any software or hardware function to make it communicate and work with any other encapsulated container, however it was developed, either on bare metal or on top of an embedded OS.
You are not familiar with Luos operations? Follow this flowchart:
What do you want to do?- Begin with the basics
- ①
Build a Luos-ready board - ②
Make drivers and apps for your hardware- Choose and configure your development environment
- Read about containers and how they work
- Create a project
and
start creating containers- Learn more about the tools and configurations available with Luos:
→ Object dictionary
→ Routing table
→ Messages handling
→ Self-healing
→ Streaming
→ Real-time configuration
- Learn more about the tools and configurations available with Luos:
- Create a project
- Read about containers and how they work
- Choose and configure your development environment
- ③
Use Luos-ready boards and containers
- ①
- Begin with the basics
If you have questions about a specific topic, you can refer or ask it on the Luos' Forum. And if you have suggestions about this documentation don't hesitate to create pull requests.
Watch this video for additional details:
Luos is free for non-commercial use. It's protected by a license that you can consult here.
General guide to Luos technology
Luos is a simple and lightweight containerization platform dedicated to embedded systems enabling microservices architecture for electronics. It's a powerful tool using modularity 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 containers.) of a device. You can use Luos as a bare metal lib or as a driver into your embedded OS.
Luos is composed as well of code subdivisions called containersSoftware element run by Luos that can communicate with other containers. It can be a driver or an app. (Initially called a module). Containers are distributed into every nodes in a network.
What is a Node
A node is a physical component (hardware) running Luos and hosting one or several containers. In a Luos network, nodes are all connected together using RobusBus communication protocol used by Luos., the Luos communication technology.
In other words, a node is a microcontroler connected to other microcontrolers running Luos.
In the Luos philosophy, each node has to carry the necessary programs (containers) allowing it to manage its boards and features.

Nodes can have capacities such as measuring the core temperature, sending the processor's unique ID or input voltage. A node's capacities are commonly shared by all the containers hosted into it and are accessible through each of them.
Container
A container is a block of code which is able to communicate with any other containers through the Luos network. Each container provides an API allowing to manage a motor, handle a laser range finder, or compute an inverse-kinematics, for example. Each container is hosted in a single node, but a node can handle several containers at the same time and manage communication between them and between other containers hosted in other nodes, using the same interface.
For example, the Dynamixel board provided by Luos can dynamically create and manage Dynamixel containers depending on the number of Dynamixel motors linked to it. Any Dynamixel containers can get or set values to other Dynamixel containers on the same node or to any other containers in any other nodes in the network.
Messages
All containers can share and receive datas using messages.
Routing table
A routing table is a "service" managed by the Luos network and available for any containers in any nodes. This service lists all the containers and allows to any containers to get and use basic information of any other containers. The routing table's data can be loaded or auto-generated during detection.
Container detection
The container detection assigns IDs to containers depending on their node's physical position in the network, and generates a routing table.
IDs are assigned from the nearest to the furthest node branch by branch, from the point of view of the container running the detection. Following this logic, the container running the detection will have the ID 1, the next one will have the ID 2, etc.
Note: Multiple detection by different containers at the same time is not allowed.
It's possible to detect the network frequently in order to dynamically discover included or excluded containers while running. Go to Routing table page for more informations.
Low level
This part of the Luos documentation is dedicated to people designing their own electronic boards or their specific embedded code.
Let's start by setting up your development environment, then you can start your electronic design and your containers creation.
Setup your development environment
Before starting developing with Luos, you need to have an operational development environment. At Luos, we use PlatformIO to share all our examples and to make our lib integration easy, but of course you can use your favorite IDE and integrate our libs by yourself.
Setup a Luos PlatformIO project
PlatformIO is a cross-platform, cross-architecture, multiple framework, professional tool for embedded systems engineers and for software developers who write applications for embedded products. You can put it as a plug-in in a lot of different editors.
Getting Started
- Install Platform IO on VSCode by following the instructions on this page.
- Create a new projet on PlatformIO
- Add Luos as dependancy and select HAL on your
platformio.ini
file:
lib_deps = Luos
board = <board name>
Replace <board name>
with the name of the board you're using, eg. board = l0
for the L0 board.
Note: More information about how Luos libs are managed into PlatformIO is available by following this post on our forum.
Project examples
Luos shares a lot of code examples, feel free to use and modify them as you want.
Demonstration boards
Luos created a sets of boards allowing to easily test the technology.
General integration consideration
Luos works as a code library running on nodes. To match Luos library with your hardware, Luos offers a Hardware Abstraction Layer for a lot of devices in LuosHALHardware Abstraction Layer used to fit Luos with various hardware designs..
- LuosHAL: This repository gives you a list of family device covers to match Luos library with your hardware.
- Luos: This is the main library you will be working with.
To make it work on your environment, you have to:
- Include Luos lib folders in your project compilation;
- Select the right LuosHAL from your family device in LuosHAL folder, and include
luos_hal.c
,luos_hal.h
andluos_hal_config.h
in your project; - Change, if necessary,
luos_hal_config.h
in you project, the default configuration created by Luos (before includingluos.h
) in order to match LuosHAL with your hardware (eg: match pins with your design); - Include
luos.h
on your source file.
Integrating Luos into an electronic board
Luos uses a communication protocol call RobusBus communication protocol used by Luos. to communicate with other containers. Luos creates a network for the communication between containers located on different nodes. This communication goes through a physical layer, which must be adapted to your own design (through the file luos_hal_config.h
).
Electronic boards must respect some design rules in order to properly work in a Luos network.
Electronic design
Board examples and electronic sources are available on GitHub. You are free to use them as you want.
The basic physical layer to create a Luos network is based on RS485, but you can use any half duplex support allowing to check transmitted data.
Here is the example of the schematic of L0 boards (available on GitHub).
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 containersSoftware element run by Luos that can communicate with other containers. It can be a driver or an app. (Initially called a module) (drivers and apps).
- At least 2 connectors: They allow to link boards together into a Luos network (Luos' official connector is: DF11-8DP-2DS(24)).
Compatible MCUs
Luos manages any type of microcontrollers, but they need to be added manually to the library. If your microcontroller is not managed yet, please contact us:
- by mail: hello@luos.io
- on GitHub
Containers
A container is a block of code which is able to communicate with any other containers in the Luos network.
A container can be an application or a driver.
Each container provides a particular set of tasks such as managing a motor, handling a laser range finder, or compute an inverse-kinematics. Each container is hosted in a single nodeHardware element (MCU) hosting and running Luos and hosting one or several containers. (MCU), but a node can handle several containers at the same time and manage communication between them and between other containers hosted in other nodes, using the same interface.
As a developer you will always develop your functionalities into containers, and never into the main()
program. The only information that should be put on the main()
code are MCU setup parameters and containers' run functions.
Container properties
To properly work, each container owns some properties allowing to other containers to recognize and access it:
Name | Description | Format |
---|---|---|
ID | The ID is a unique number given to each container depending on their physical position. The system automatically assigns each ID during the detection phase. If you move a container 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 microcontroler on the network on a given device, the ID will change too. | Integer e.g. Id=1 |
TYPE | The type defines the container purpose. A few types are predefined and can be used, or new ones can be created. The container type can't be changed after container initialization. | String e.g. type=DISTANCE_MOD |
ALIAS | Alias is the name of the container. It's used to easily identify a container. Each container has a default alias which can be changed by users. For example, a container with the default alias motor_mod can be named left_knee_motor by user. This new name will be stored in the non-volatile memory of the board. As we don't want to have multiple containers 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 name by setting a void name ("" ) to a container. | String e.g. alias="gate" |
Create Luos Projects
How to properly organize your Luos projects
How to run Luos
Luos is like a task that has to be run regularly. So you will have to run it by adding luos_init()
and luos_loop()
in the main()
of your program.
Basically, your main()
will look like this:
#include "luos.h"
int main(void)
{
Luos_Init();
while(1)
{
Luos_Loop();
}
return 0;
}
Putting this code into a nodeHardware element (MCU) hosting and running Luos and hosting one or several containers. makes it able to react to a Luos network. It's now ready to host your containers.
As a developer you will always develop your functionalities into containers and never into the main()
program.
Note: The only information that should be put on the
main()
code are MCU setup parameters and containers' run functions.
How to add containers in your project
A node can host multiple containers, and a container has to be as portable as possible. In order to do that, containers have to be independent code folders that can be easily copied and pasted in another project.
To make it at Luos we always use the same way to organize our projects: we put the containers into a containers
folder and name the containers' code files with the name of each container:
Project
│
├─── containers
│ ├─── container_1
│ │ ├─── container_1.c
│ │ └─── container_1.h
│ └─── container_2
│ ├─── container_2.c
│ └─── container_2.h
│
├─── Inc
│ ├─── Luos
│ └─── luos_hal
│
└─── Src
└─── Main.c
Basic containers functions
We choose to put the public functions of our containers in the container.h
file. Like Luos, containers are similar to tasks that need to be run regularly, so we choose to use the exact same stategy as presented for Luos functions by providing a Container_Init()
and a Container_Loop()
functions and to add them in the main()
.
Following the previous folder organization, the main()
code looks like this:
#include "luos.h"
#include "container1.h"
#include "container2.h"
int main(void)
{
Luos_Init();
Container1_Init();
Container2_Init();
while(1)
{
Luos_Loop();
Container1_Loop();
Container2_Loop();
}
return 0;
}
This way, it is easy to manage all your containers and to add as many of them in the main()
file as you want.
Create Luos containers
As a developer you will always develop your functionalities into containers and never into the main()
program.
Warning: Make sure to read and understand how to Create Luos projects before reading this page.
How to create and initialize a container
To create a container, you have to call this function:
container_t* Luos_CreateContainer(void* callback, container_type_t type, char* default_alias, revision_t revision);
The returned container_t*
is a container structure pointer that will be useful to make your container act in the network after this initialization.
callback is a pointer to a callback function called by Luos when your container receive messages from other containers (see Message Handling configuration page for more details). This function needs to have a specific format:
void Container_MsgHandler(container_t *container, msg_t *msg)
- container is the container pointer of the container receiving the data (basically, it is your container).
- msg is the message your container received.
type is the type of the your new container represented by a number. Some basic types (e.g. DISTANCE_MOD
, VOLTAGE_MOD
, etc.) are already available in the container_type_t
enum structure of Luos. You can also create your own on top of the luos one.
default alias is the alias by default for your new container. e.g. Mycontainer02
. This alias is the one your container will take if no other alias is set by the user of your functionality hosted in your container. Aliases have a maximum size of 16 characters.
revision is the revision number of the container you are creating and which will be accessible via pyluos.
Following the project rules, here is a code example for a button container:
revision_t ButtonRevision = {.unmap = {0,0,7}};
container_t* container_btn;
static void Button_MsgHandler(container_t *container, msg_t *msg){
// Manage received messages
}
void Button_Init(void) {
container_t* container_btn = Luos_CreateContainer(Button_MsgHandler, STATE_MOD, "button_mod", ButtonRevision);
}
void Button_Loop(void) {
}
Note: According to the real-time configuration you chose, an additional line of code may be necessary. See Message Handling configuration page for more details.
Containers categories
To make your development as clean as possible, you have to understand in which category (Driver or App) each container of the project is.
By following the categories guidelines, you will be able to make clean and reusable functionalities.
Drivers guidelines
A driver is a type of container that drives hardware. Motors, distance sensors, LEDs are all drivers.
By designing a driver, you have to keep the following rules in mind:
- A driver container always uses a standard Luos type to be usable by any other containers.
- A driver container 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 containers.
- A driver container never depends or uses any other containers (driver or app).
- A driver container 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 containers on the same nodeHardware element (MCU) hosting and running Luos and hosting one or several containers. managing different hardware functionalities of your board, it is your call to sort them depending on your design.
Apps guidelines
An applications or app is a type of container that only manages software items such as functions or algorithms. Apps use other containers 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 containers. on a Luos network without any hardware or code modifications. However, the choice of the hosting node can impact global performances 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 container types.
- 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. structures. If the structures used are not standard, Gate containers 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 containers in every nodes in your device and make it work properly. Go to Routing table page for more informations.
Object Dictionary
To keep interoperability between containersSoftware element run by Luos that can communicate with other containers. It can be a driver or an app. (Initially called a module), Luos provides an Object Dictionary (OD).
What is OD?
An Object Dictionary (OD) allows different developers of different containers to make them interoperate regardless of the unit they uses on the container.
Let's take an example: If container1 uses an angle as radians and container2 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 predefined type and to use it in the units the user want.
How is it managed in Luos?
Luos defines objects based on physical values following the SI standard.
Object 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
angular_position_t angular_position = AngularOD_PositionTo_deg (12.0);
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.
Conversions
As many units exist, many conversion functions are available. As a result, they follow a logic naming rules in order to easily find a desired function without having to search for it.
Unit conversions
There are two types of unit conversion: in one way (OD type from desired unit), and in the other way (OD type to desired unit):
from
conversion: Converts a value with a defined unit into a desired OD type. Format:[type_var] = [type]From_[unit]([value])
// save a linear_position from a mm value
linear_position_t LinearOD_PositionFrom_mm(float mm);
to
conversion: Converts a type to a unit. Format:[value] = [type]To_[unit]([type_var])
// convert the variable linear_position into a mm
float LinearOD_PositionTo_mm(linear_position_t linear_position);
Messages conversions
The same both way of conversion are available for messages (OD type from message and OD type to message):
from
conversion: Gets a type 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 type 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:
Type | Available prefix and other units |
---|---|
linear_position | nm, μm, mm, cm, m, km, in, ft, mi |
linear_speed | mm/s, m/s, km/h, in/s, mi/h |
angular_position | deg, revolution, rad |
angular_speed | deg/s, revolution/s, revolution/min, rad/s |
force | N, kgf, ozf, lbf |
moment | N.mm, N.cm, N.m, kgf.mm, kgf.cm, kgf.m, ozf.in, lbf.in |
voltage | mV, V |
current | mA, A |
power | mW, W |
ratio | percentage |
temperature | deg_c, deg_f, deg_k |
color | 8bit_RGB unsigned char [3] |
Note: to find out a conversion function, replace the characters
/
or.
in the units by the character_
. The characterµ
is replaced byu
, andrevolution
is replaced byrev
.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()
;
Routing Table
Warning: Make sure to read and understand how to Create Luos containers before reading this page.
The routing table is a feature of Luos allowing every containersSoftware element run by Luos that can communicate with other containers. It can be a driver or an app. (Initially called a module) to own a "map" (or topology) of the entire network of your device. This map allows containers to know their physical position and to search and interact with other containers easily.
This feature is particularly used by apps containers to find other containers they need to interact with. The routing table is shared by the container which launches the detection to other containers, but only apps containers store the routing table internaly.
Detection
Routing table is automatically generated when a network detections is initiate by a container and shared with other containers at the end of the detection. A detection can be initiated by any container, but driver containers should not be able to run it and this kind of features should be only used on app containers by including routingTable.h an using this routing table API.
To run a detection, type:
RoutingTB_DetectContainers(app);
where app is the container_t
pointer running the detection.
A non-detected container (not in the routing table) has a specific ID of 0
. At the beginning of the detection, Luos erases each container's ID in the network, so all of them will have the ID 0
during this operation. You can use it on your containers code to act consequently to this detection if you need it (for example, a container can monitor its ID to detect if a detection has been made and if it has to reconfigure its auto-update).
Then the container running the detection will have the ID 1
and the other containers will have an ID between 2
and 4096
, depending on their position from the container detector. The IDs are attributed to the containers according to their position from the detector container and to the branch they are in. The ID attribution begins first to the PTPA port, then PTPB, etc.
When each container in the network has an attributed ID, the detection algorithm proceeds to the creation of the routing table and shares it with every containers (saved only one time per node).
Sometimes, multiple containers in the network can have the same alias, which is not allowed to prevent container confusion. In this case, detection algorithm will add a number after each instance of this alias on the routing table.
Warning: Be careful that during a detection, a container can change ID depending on the container running this detection. Do not consider your container's ID fixed. Also, be aware that every containers remove their auto-update configuration during the detection to prevent any ID movement.
Modes
As explained in this page, nodesHardware element (MCU) hosting and running Luos and hosting one or several containers. can host multiple containers. To get the topology of your device, the routing table references physical connexions between your nodes and lists all the containers in each one of them.
The routing table is a table of a routing_table_t
structure containing nodes or containers information.
The maximum number of containers and nodes are managed by the precompilation constant MAX_containerS_NUMBER
(set to 40 by default).
routing_table_t routing_table[MAX_CONTAINERS_NUMBER];
The routing table structure has two modes: container entry mode and node entry mode.
typedef struct __attribute__((__packed__))
{
entry_mode_t mode;
union
{
struct __attribute__((__packed__))// CONTAINER mode entry
{
uint16_t id; // Container ID.
uint16_t type; // Container type.
char alias[MAX_ALIAS_SIZE]; // Container 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;
container entry mode
This mode allows routing_table
to contain:
- id: container's unique id
- type: container's type
- alias: container's alias
For more information, please refer to the containers page of this documentation.
Node entry mode
This mode gives physical information of 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 perfomed, all node ID are set to 0. When RoutingTB_DetectContainers API is called, Luos gives to nodes and containers a unique ID according to your system topology.
The certified Luos node can be certified for your system by including Luos licencing number in your product (feature in progress).
The port_table allows to share 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 container id of the connected node through a given port.
Specific values taken by port_table
:
- 0: this port is waiting to discover who 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 containers and nodes' information into a Luos network:
Description | Function | Return |
---|---|---|
Find a container's id from its alias | RoutingTB_IDFromAlias(char* alias); | uint16_t |
Find a container's id from its type (return the first of the list) | RoutingTB_IDFromType(luos_type_t type); | uint16_t |
Find a container's id from a container | RoutingTB_IDFromContainer(container_t *container); | uint16_t |
Find a container's alias from its id (return the first of the list) | RoutingTB_AliasFromId(uint16_t id); | char* |
Find a container's type from its id | RoutingTB_TypeFromID(uint16_t id); | container_type_t |
Find a container's type from its alias | RoutingTB_TypeFromAlias(char* alias); | container_type_t |
Find a container's string from its type (return the first of the list) | RoutingTB_StringFromType(luos_type_t type); | char* |
Test if a container's type is a sensor | RoutingTB_ContainerIsSensor(container_type_t type); | uint8_t |
Get the number of nodes in a Luos network | RoutingTB_GetNodeNB(void); | uint16_t |
Get a node's id | RoutingTB_GetNodeID(unsigned short index); | uint16_t |
Management tools
Here are the management tools provided by the routing table library:
Description | Function | Return |
---|---|---|
Compute the rooting table | RoutingTB_ComputeRoutingTableEntryNB(void); | void |
Detect the containers in a Luos network | RoutingTB_DetectContainers(container_t* container); | void |
Convert a node to a routing table entry | RoutingTB_ConvertNodeToRoutingTable(routing_table_t *entry, node_t *node); | void |
Convert a container to a routing table entry | RoutingTB_ConvertContainerToRoutingTable(routing_table_t* entry, container_t* container); | void |
Remove an entry in the routing table (by id) | RoutingTB_RemoveOnRoutingTable(uint16_t id); | void |
Erase routing table | RoutingTB_Erase(void); | void |
Get the routing table | RoutingTB_Get(void); | routing_table_t* |
Get the last container in a Luos network | RoutingTB_GetLastContainer(void); | uint16_t |
Get the last entry in a Luos network | RoutingTB_GetLastEntry(void); | uint16_t |
Get the last node in a Luos network | RoutingTB_GetLastNode(void); | uint16_t* |
Containers communication handling messages
Warning: Make sure to read and understand how to Create Luos containers before reading this page.
As a developer, you will have to create and use Luos messages to exchange informations between containersSoftware element run by Luos that can communicate with other containers. It can be a driver or an app. (Initially called a module). In order to do that, you have to understand how messages works.
Message structure
Luos messages are managed by the msg_t
structure:
typedef struct{
header_t header;
uint8_t data[MAX_DATA_MSG_SIZE];
}msg_t;
All messages have a header. A header is a 7-byte field containing all information allowing containers to understand messages context. All containers on the network catch and decode the header of each sent and received message.
data
is a table containing informations.
Info: MAX_DATA_MSG_SIZE represenst the maximum size of messages (default value is 128 bytes);
Header
To send data to any containers you want, you will have to fill 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. */
}header_t;
- 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. Make sure 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 container using its ID without acknowledgment return.
- ID_ACK: This mode allows to communicate with a unique container using its ID with acknowledgment return.
- Multicast/Broadcast: This mode allows multiple containers to catch a message. In this case, the message contains a type of data used by multiple containers.
- Type: This mode sends a message to all containers with a given type, for example all "Sharp digital distance sensor".
- Source (12 bits): The unique ID of the transmitter container.
- CMD (8 bits): The command defines the transmitted data's type.
- Size (16 bits): Size of the incoming data.
Receive and send a basic message
To send a message you have to:
- Create a message variable
- Set the target_mode
- Set the target
- Set the cmd
- Set your data size
- Set your data
- Send it.
Here is a basic reply example that you can find in a container reception callback:
void containers_MsgHandler(container_t *container, msg_t *msg) {
if (msg->header.cmd == ASK_PUB_CMD) {
// fill the message info
msg_t pub_msg;
pub_msg.header.target_mode = ID;
pub_msg.header.target = msg->header.source;
pub_msg.header.cmd = IO_STATE;
pub_msg.header.size = sizeof(char);
pub_msg.data[0] = 0x01;
Luos_SendMsg(container, &pub_msg);
return;
}
}
container exclusion
Luos includes an acknowledgement management using the ID_ACK target_mode. This mode guaranties the proper reception of critical messages.
If Luos fails to reach its target using ID_ACK, it will retries 10 times. If the acknowledgement still fails, the targeted container is declared excluded. Excluded containers are removed from the routing table to avoid any messaging by any containers, preserving bandwidth for the rest of the system.
Large data
You will sometimes have to deal with large data that could be larger than the maximum 128-byte data on a Luos message. Fortunately, Luos is able to automatically fragment and de-fragment the data above this side. To do that, you will have to use another send function that will take care of setting 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;
msg.header.target = 12;
msg.header.cmd = COLOR;
Luos_SendData(container, &msg, picture, sizeof(color_t)*300*300);
return;
In the reception callback, here is the code for retrieve the message with the receiving container (the one with ID 12):
color_t picture[300*300];
void containers_MsgHandler(container_t *container, msg_t *msg) {
if (msg->header.cmd == COLOR) {
Luos_ReceiveData(container, msg, (void*)picture);
}
}
Note: If you have to deal with high-frequency real-time data, please read the Streaming page.
Time-triggered update messages
Luos provides a standard command to ask a container to retrieve values from a sensor, called ASK_PUB_CMD
. However, sometimes apps need to poll values from sensors, but the act of repeatedly retriving a value using the ASK_PUB_CMD
command may result in the use of a lot bandwidth and take useless resources.
In this kind of polling situation, you can use the time-triggered auto-update features available from any Luos container. This feature allows you to ask a container to send you an update of any value each X milliseconds.
To use it, you have to setup targeted container 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 associated to it.
For example, to update a container each 10ms:
time_luos_t time = TimeOD_TimeFrom_ms(10);
msg_t msg;
msg.header.target = id;
msg.header.target_mode = IDACK;
TimeOD_TimeToMsg(&time, &msg);
msg.header.cmd = UPDATE_PUB;
Luos_SendMsg(app, &msg);
Info: containers can handle only one time-triggered target, 2 containers of the same network can't ask a time-triggered value from the same container.
Warning: To prevent any ID movement, auto-update configuration is reseted on all containers at each detection (see Routing table page for more information).
Luos self-healing capabilities
Warning: Make sure to read and understand how to create Luos containers before reading this page.
As a developer, you will have bugs. 😲
Finding, understanding, and managing bugs on multiple boards running multiple containers can be really hard. To make your life easier, Luos allows you to get some basic information about the defaults of your system allowing you to react to it.
Container exclusion
Luos includes an acknowledgement management using the ID_ACK target_mode. This mode guaranties the proper reception of critical messages.
If Luos fails to reach its target using ID_ACK, it will retry 10 times. If the acknowledgement still fails, the targeted container is declared excluded. Excluded containers are removed from the routing table to avoid any messaging from any containers, preserving bandwidth for the rest of the system.
Note: Gates containers can report container exclusion through Json.
Note: Pyluos can report container exclusion through Gates.
Luos statistics
Luos monitors some values representing the sanity of your nodes and containers.
Note: Gates containers can report statistics trough Json.
Note: Pyluos can display statistics trough Gates.
Node statistics
In any container you can have access to the host node's statistics values using the luos_stats_t
structure.
This structure gives you access to several values:
- memory: Mmory statisctics information
- msg_stack_ratio: Prcentage of memory occupation of message management tasks
- luos_stack_ratio: pPercentage of memory occupation of luos tasks
- 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 container.node_statistics
.
Container statistics
In any container you have access to statistics values using the container_stats_t
structure.
This structure gives you access to several values:
- msg_fail_ratio: Percentage of failed messages transmission
- max_collision_retry: Maximum number of retries due to collision on the network
- max_nak_retry: Maximum number of retries due to a NAK of another container
You can access node statistics by using container.statistics
.
Assert
Luos allows you to declare to an entire network a critical failure on a container.
To manage it, Luos exposes a LUOS_ASSERT
macro that allows 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 containers with the file and line were the crash occured. All other nodes will remove all the containers from the crashed node from the routing table.
Note: Gates containers can report assert of other nodes trough Json.
Note: Pyluos can display assert trough Gates.
Streaming
In occasion, you will have to deal with high-frequency small data with strong time constraints.
To make it easy, Luos manages streaming channels trough ring buffers.
A streaming channel allows you to reduce drastically the time constraints of your Luos network thanks to 2 effects:
-
The first effect is a method which allows you to have a no-real-time container dealing with a strict-real-time one. Both side have their own loop frequency and time precision.
- The real-time one is the container opening the streaming channel. It has a high-frequency function called at a precise sampling frequency.
- The no-real-time one has a slower and unprecise timed function which is called at each chunk_time.
-
By using streaming channels, you will be able to use big data chunks at low frequency to optimize the data rate efficiency of the bus. The idea is to exchange big chunks of data between containers instead of a tons of time-constrained small messages flooding all containers.
Example
A motor-driver container has strict real-time constraints. If you want to have a smooth positioning movement or measurement you have to update the motor position at high-frequency (sampling frequency).
First, you have to define the sampling frequency allowing you to have a smooth movement or measurement on the motor. Let's take 200Hz in this example.
In the no-real-time side (the container commanding the motor), you can't have a 200Hz loop because it probably has other things to do and perhaps doesn't have a sufficient time precision. To simplify it you will have to send trajectory chunks regularly (chunk time), let's say approximately each 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:
- The container who sends the trajectory has to make sure that the motor container 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, receiver always has at least one data chunk (1s in this example) ready to be consumed.
- When data chunks are received, receiver can start consuming data at its sampling frequency.
- One data chunk later (1s in our example), receiver has consumed the first data chunk, and the sender can start to compute the next one.
- At the end of the data chunk computation, sender sends the chunk. Luos adds it to the ring buffer.
- At each second, the sender sends the next data chunk and Luos add it to the ring buffer. At the end of the buffer, Luos puts extra data at the begining. The consumer pointer also goes back to the begining of the buffer when it reaches the end. This way we have infinite data stream without any discontinuity.
- You can continue this way indefinitely.
Note: You can play pause, stop or record 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 container. The other container (the no-real-time one) will just send or receive its data chunks using large data messages.
Streaming channel creation
Before starting using streaming method, you have to create a streaming channel linked to a buffer into the init function of your real-time container:
#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_CreateContainer(Motor_MsgHandler, CONTROLLER_MOTOR_MOD, "motor_mod");
}
Now you can use this channel to receive or transmit a streaming flux:
- reception is adapted to make our motor move smoothly. A no-real-time container will send us parts of trajectory approximately each second and our motor will consume angular position at 200Hz.
- transmission is adapted to measure precisely the movements of the motor. We can use it to send in a no-real-time way real-time data. In our motor it could be angular position measurement at 200Hz for example.
Streaming reception
This is used to make the motor move.
When your streaming channel has been created, you can feed it with received messages on your reception callback:
void Motor_MsgHandler(container_t *container, msg_t *msg) {
// check message command
if (msg->header.cmd == ANGULAR_POSITION) {
// this is our trajectory reception
Luos_ReceiveStreaming(container, msg, &trajectory);
}
}
Now your container is able to receive trajectory chunks. For the next step, you need to have a real-time callback (using a timer for example) which is able to manage the consumption of this trajectory at 200hz:
void 200hz_callback(void) {
Stream_GetSample(&trajectory, &motor.target_angular_position);
}
Streaming transmission
This 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 200Hz:
void 200hz_callback(void) {
Stream_PutSample(&trajectory, &motor.angular_position);
}
This way, samples are buffered into your ring buffer, and you can send this real-time information as you want. For example only when someone ask you to:
void Motor_MsgHandler(container_t *container, 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;
pub_msg.header.target = msg->header.source;
pub_msg.header.cmd = ANGULAR_POSITION;
Luos_SendStreaming(container, &pub_msg, &measurement);
}
}
The Luos_SendStreaming
function sends data available on your streaming channel. You can continue to feed your channel with samples at the same time.
Warning: This example doesn't work if your container is configured as real-time. Please read Message Handling configuration page for more informations.
Message Handling configuration
Warning: Make sure to read and understand how to Create Luos containers before reading this page.
Message callbacks of containers can be really difficult to use when a project include 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 container.
Configuration | execution type |
---|---|
Callback (default) | runtime callback |
Polling | no 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 container callback during runtime. The time between the physical reception of a message and the callback may vary depending on the luos_loop()
function call frequency.
With this configuration, you have no real constraints on the callback's time of execution, you can reply to a message directly on the callback.
To setup this configuration you have to simply setup the callback at container creation.
Here is a code example with a button:
void Button_MsgHandler(container_t *container, msg_t *msg) {
if (msg->header.cmd == ASK_PUB_CMD) {
// The message is filled with global variable with proper data
msg_t pub_msg;
pub_msg.header.cmd = IO_STATE;
pub_msg.header.target_mode = ID;
pub_msg.header.target = msg->header.source;
pub_msg.header.size = sizeof(char);
pub_msg.data[0] = HAL_GPIO_ReadPin(BTN_GPIO_Port, BTN_Pin);
// Sending the message
Luos_SendMsg(container, &pub_msg);
return;
}
}
void Button_Init(void) {
// container creation: (callback, container type, Default alias)
container_t* container = Luos_CreateContainer(Button_MsgHandler, STATE_MOD, "button_mod");
}
void Button_Loop(void) {
}
Polling configuration
This configuration is often used into Arduino libraries to receive information in a basic way. This method allows to manage the messages only when the user wants to do it on the loop of the container.
To setup this configuration, you have to create your container without any callback.
See the following code as an example, with a button:
container_t* container;
void Button_Init(void) {
container = Luos_CreateContainer(0, STATE_MOD, "button_mod");
}
void Button_Loop(void) {
if (Luos_NbrAvailableMsg()) {
msg_t *msg = Luos_ReadMsg(container);
if (msg->header.cmd == ASK_PUB_CMD) {
// The message is filled with global variable with proper data
msg_t pub_msg;
pub_msg.header.cmd = IO_STATE;
pub_msg.header.target_mode = ID;
pub_msg.header.target = msg->header.source;
pub_msg.header.size = sizeof(char);
pub_msg.data[0] = HAL_GPIO_ReadPin(BTN_GPIO_Port, BTN_Pin);
// Sending the message
Luos_SendMsg(container, &pub_msg);
}
}
}
Code Examples
Here is a list of project example you can use to understand different concepts used in this documentation. You can use this table to find interesting project regarding specific features example you are looking for.
Projects | Dynamic containers allocation | Large data | Streaming | Object Dictionnary | Routing table |
---|---|---|---|---|---|
Gate | ✓ | ✓ | ✓ | ||
Potentiometer | ✓ | ||||
Gpio | ✓ | ||||
Distance | ✓ | ||||
Servo | ✓ | ||||
Dxl | ✓ | ✓ | |||
Load | ✓ | ||||
Stepper | ✓ | ||||
IMU | ✓ | ||||
Button | ✓ | ||||
LED | ✓ | ||||
DC motor | ✓ | ||||
Controller motor | ✓ | ✓ | |||
LED strip | ✓ | ✓ | |||
Light sensor | ✓ | ||||
Power switch | ✓ |
High level
This part of the Luos documentation is dedicated to people aiming to control or monitor a device trough a computer or a Raspberry Pi using classic programming ways such as Python, JavaScript, or Rust.
Luos provides a Python library, Pyluos and also works with ROS. If you want to use another language, you should start reading about our JSON API.

JSON API
The JSON formated data is very 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 easily interact with your device.
To do that, you must add a specific app container called Gate on your device.
The Gate container is an app that converts Luos messages from a device's network into JSON data format, and the other way from JSON to Luos messages.
The Gate container can be hosted into different kinds of nodesHardware element (MCU) hosting and running Luos and hosting one or several containers. allowing you to choose the communication way fitting with your project (USB, Wifi, Bluetooth, etc.)
Warning: The Gate container refreshes sensors information as fast as it can, so that can be intensive to Luos bandwidth.
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 container.
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 routing table works before reading this part.
After the Gate starts, the first message you receive is a routing table.
This first message is really important, because it contains all the information allowing you to create a code object for your device, containing all its features.
Routing table structure
A routing table in JSON consists in a list of the nodes present in the Luos network:
{
"routing_table":[
{
// node 1
},
{
// node 2
},
{
// node 3
}
// ...
]
}
Nodes information
Each listed node of the network has basic node information and a list of hosted containers:
{ // node 1
"node_id":1,
"certified":true,
"port_table":[1, 2],
"containers":[
{
// container 1
},
{
// container 2
}
// ...
]
}
Note: To understand the meanings of uuid and port_table, please refer to the routing table page.
Containers
Each listed container of a node has basics containers information:
{ // container 1
"type":"Type",
"id":1,
"alias":"Alias"
}
Note: To understand the meanings of type, id and alias, please refer to the container page.
Full routing table example
{
"routing_table":[
{
"node_id":1,
"certified":true,
"port_table":[2, 65535],
"containers":[
{
"type":"Gate",
"id":1,
"alias":"r_right_arm"
}
]
},
{
"node_id":2,
"certified":true,
"port_table":[4, 1],
"containers":[
{
"type":"State",
"id":2,
"alias":"lock"
},
{
"type":"Unknown",
"id":3,
"alias":"start_control"
}
]
},
{
"node_id":3,
"certified":true,
"port_table":[5, 3],
"containers":[
{
"type":"Imu",
"id":4,
"alias":"gps"
}
]
},
{
"node_id":4,
"certified":true,
"port_table":[65535, 4],
"containers":[
{
"type":"Color",
"id":5,
"alias":"alarm"
},
{
"type":"Unknown",
"id":6,
"alias":"alarm_control"
}
]
}
]
}
Below is a visual representation of this routing table:
Container's information messages
When the JSON routing table is transmitted, the Gate starts to update and stream your network data with containers information.
This JSON is a "container" object listing all the containers by their alias and the values they send:
{
"containers":{
"container_alias1":{
"value1":12.5
},
"container_alias2":{
"value1":13.6,
"value2":[1, 2, 3, 4]
}
}
}
You can use the exact same JSON object structure to send data to containers.
Here is the list of all values that can be used by containers:
Value name | Definition |
---|---|
power_ratio | Percentage of power of an actuator (-100% to 100%) |
target_rot_position | Actuator's target angular position (can be a number or an array) |
limit_rot_position | Actuator's limit angular position |
limit_trans_position | Actuator's limit angular position |
limit_power | Limit ratio of an actuator's reduction |
limit_current | Limit current value |
target_rot_speed | Actuator's target rotation speed |
target_trans_position | Actuator's target linear position (can be a number or an array) |
target_trans_speed | Actuator's target linear speed |
time | Time value |
compliant | Actuator's compliance status |
pid | Set of PID values (proportionnal, integral, derivative) |
resolution | Sensor's resolution value |
offset | Offset value |
reduction | Ratio of an actuator's reduction |
dimension | Dimension value |
volt | Voltage value |
current | Electric current value |
reinit | Reinitialisation command |
control | Control command (play, pause, stop, rec) |
color | Color value |
io_state | IO state |
uuid | Container's uuid |
rename | Renaming an alias |
revision | Firmware revision |
trans_position | Translation position value |
trans_speed | Translation speed value |
rot_position | Rotation position value |
rot_speed | Rotation speed value |
lux | Lux (light intensity) value |
temperature | Temperature value |
force | Force value |
moment | Torque value |
power | Power value |
linear_accel | Linear acceleration value |
gravity_vector | Gravity vector value |
compass | Compass value |
gyro | Gyroscope value |
accel | Acceleration value |
euler | Euler angle value |
quaternion | Quaternion values |
rotational_matrix | Rotational matrix values |
heading | Heading |
pedometer | Steps number value |
walk_time | Walk time value |
luos_revision | Luos's version |
luos_statistics | Luos's memory usage statistics [Message stack, Luos stack, Dropped messages, Loop delay, Fail ratio, Nak max number, Collision max number] |
Here is an exemple of a message sent by a Potentiometer container about the rotation angle of the associated potentiometer:
{
"containers":{
"potentiometer_m":{
"rot_position":12.5
}
}
}
Here is an exemple of a message sent by a gate container about Luos statistic:
{
"containers":{
"gate":{
"luos_statistics":{
"msg_stack":60,
"luos_stack":53,
"msg_drop":0,
"loop_ms":16,
"fail_ratio":0,
"nak_max":1,
"collision_max":5,
}
}
}
}
Custom parameters and specific messages
Some messages are specifically handled:
Custom parameters can be defined and sent to containers 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 containers in a Luos Network, through a gate:
def sendCmd(s, cmd, sleep_time=0.5):
cmd = cmd + '\r'
print(cmd)
s.write(cmd.encode())
time.sleep(sleep_time)
s = serial.Serial(sys.argv[1], 1000000)
# detect Luos network
sendCmd(s, '{"detection": {}}')
# set speed mode and compliant mode
sendCmd(s, '{"containers": {"controller_moto": {"parameters": 2441}}}')
# set pid parameters
sendCmd(s, '{"containers": {"controller_moto": { "pid": [20, 0.02, 90]}}}')
# set speed mode and non compliant mode
sendCmd(s, '{"containers": {"controller_moto": {"parameters": 2440}}}')
Parameters are defined by a 16-bit bitfield.
Object | Definition | Structure | Container(s) |
---|---|---|---|
parameters | enabling or disabling some measurement | Link to structure (GitHub) | Stepper, Controller-motor, Servo |
parameters | enabling or disabling some measurement | Link to structure (GitHub) | Imu |
Other specific messages:
Object | Definition | Container(s) |
---|---|---|
register | Motor memory register filed with [register_number, value] | Dynamixel, void |
set_id | A set id command | Dynamixel, void |
wheel_mode | The wheel mode parameter for Dynamixel servomotors True or False | Dynamixel, void |
delay | reduce containers refresh rate | Gate |
Containers exclusion messages
Containers can be excluded of the network if a problem occurs (see self-healing for more information). In this case, the Gate sends an exclusion message indicating that this container is no longer available:
{"dead_container": "container_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 detail about the crash:
{
"assert":{
"node_id":3,
"file":"/Users/foo/luos/Examples/Drivers/Button/button.c",
"line":75
}
}
Sending large binary data
Binary data such as, for example, a motor tarjectory can't be included into 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, then 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 Container'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 transfert of a binary data of 1024 bytes.
{
"containers":{
"container_alias1":{
"rot_position":[1024]
}
}
}
###BINARY_DATA###

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 container.
Installation
Required: Installing Python and Pip
Warning: In order to use Pyluos library, Python and the Pip packet 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 you computer, download and run the last release according to your computer's OS: https://www.python.org/downloads/.
To install Pip, type the following commands in a console:
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py
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 1.4.0
.
In a console, the following command will install the Pyluos library using the Pip packet 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 user. 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 use 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, Maj+Enter
executes any selected part of 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 properly 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 properly while connected to your computer from a Gate.
USB Transfer Sizes: Default value is 4096 Bytes, however if you have issues to use your connected device, you should try the minimum possible values both for Receive
and Transmit
.
Latency Timer: Default value is 16 msec, but you can rise lower it to the minimal value of 1 msc.
To access to these parameters, open the Device Manager in Windows, and right-click on the USB Serial Port (COMX) where your device is connected, then 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 (192.168.0.6
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 it is possible to start programming behaviors.
Routing table display
Routing table can be easily displayed using Pyluos.
Pyluos can displays a list of all the containers by filtering the routing table, and their associated characteristics (type, alias and ID). To display it, use the following command:
device.containers
Note:
device
is the name of the network.
Pyluos will give you a list of all containers without any topological informations :
-------------------------------------------------
Type Alias ID
-------------------------------------------------
Gate gate 1
Voltage analog_read_P1 2
Voltage analog_read_P7 3
Voltage analog_read_P8 4
Voltage analog_read_P9 5
State digit_read_P5 6
State digit_read_P6 7
State digit_write_P2 8
State digit_write_P3 9
State digit_write_P4 10
Angle potentiometer_m 11
Pyluos also can interpreate routing_table and transform it into a tree. This way we can display a lot more complete information usinig the following command :
device.nodes
Note:
device
is the name of the network.
Based on the previous example Pyluos will give you all informations about containers and topological informations :
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ╭node 1 Certified ┃
┃ │ Type Alias ID ┃
┃ ╰> Gate gate 1 ┃
╔>┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
╚══ 0>┃1 ╭node 2 Certified ┃
┃ │ Type Alias ID ┃
┃ ├> Voltage analog_read_P1 2 ┃
┃ ├> Voltage analog_read_P7 3 ┃
┃ ├> Voltage analog_read_P8 4 ┃
┃ ├> Voltage analog_read_P9 5 ┃
┃ ├> State digit_read_P5 6 ┃
┃ ├> State digit_read_P6 7 ┃
┃ ├> State digit_write_P2 8 ┃
┃ ├> State digit_write_P3 9 ┃
┃ ╰> State digit_write_P4 10 ┃
╔>┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
╚══ 0>┃1 ╭node 3 Certified ┃
┃ │ Type Alias ID ┃
┃ ╰> Angle potentiometer_m 11 ┃
>┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
In this example, 3 nodes (MCU) and their associated UUID are listed, along with their containers and associated characteristics (type, alias and ID).
The characters after each set of node's containers 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.
Container type
Each container has a type (eg. Button
, Led
, ...).
You can either retrieve your container's type from the previous code, or with the following line:
device.container_alias.type
container_alias
being the alias you got from the previous listing.
Note: Unknown container types are defaulty set for custom container types such as some Luos apps.
Get and set containers informations
Once you have detected your containers, you can use these information like variables.
To access values you have to address them in the device object following this rules :
device.container_alias.variable
For example :
device.rgb_led_mod.color = [50,80,5] # Change the color of the LED in "rgb_led_mod" container
device.button_mod.state # Returns the status of the push button
device.button_mod.type # Returns the container type of the container "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 objects and variables.
Change a container name
The name of any container can be changed following this code.
device.container_alias.rename("new_name")
For example:
device.rgb_led_mod.rename("myLED")
Note: You should restart your device and reconnect to it after this operation.
Note: To get back to the container default name, set a void name (
""
).
Get a node statistics
Nodes are able to 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.
device.container_alias.luos_statistics
For example:
device.gate.luos_statistics
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 to2 * MAX_CONTAINER_NUMBER
where MAX_CONTAINER_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 to3 * sizeof(msg_t)
by default. sizeof(msg_t) -> 7 bytes Header + 128 bytes data). -
Contrary to Message stack, Luos stack, Max luos loop delay which are Node relatif statistics, Msg fail ratio and NAK msg max number are container's statistic. Msg fail ratio give a ratio of msg send fail base a all the msg that the container has sent. NAK msg max number give the max number of NAK receive when a message has been sent.
-
The RAM occupation and message drop number is 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. So 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.containers
device.rgb_led_mod.color = [50,80,5]
device.button_mod.state
device.button_mod.type
device.rgb_led_mod.rename("myLED")
Angle container type
The Angle container handles a rotation position value in degree.
Its type has access to all common capabilities.
Variables
Variable name | Action | Type |
---|---|---|
rot_position | Reads the rotation position in degree | Read only: Float |
threshold | Thresholds position variation before filter_changed event triggers. Default value 10 °. | Read / write: Float |
Events
Event name | Trigger |
---|---|
changed | Any movement on the position measurement |
filter_changed | Movement bigger than threshold |
ROS topics
Topic name | Message type |
---|---|
/potentiometer_m/variables/rot_position/read | std_msgs/msg/Float32 |
/potentiometer_m/variables/threshold/read | std_msgs/msg/Float32 |
/potentiometer_m/variables/threshold/write | std_msgs/msg/Float32 |
/potentiometer_m/events/changed | luos_msgs/msg/FloatChange |
/potentiometer_m/events/filter_changed | luos_msgs/msg/FloatChange |
Color container type
The Color container handles an RGB color data.
Its type has access to all common capabilities.
Functions
Function name and parameters | Action | Comment |
---|---|---|
control(self) | Displays container type graphical interface | Only available using Jupyter notebook |
Variables
Variable name | Action | Type |
---|---|---|
color | RGB color | read / write: [Char, Char, Char] |
time | Transition time between color command | read / write: Float |
ROS topics
Topic name | Message type |
---|---|
/rgb_led_mod/variables/color/read | std_msgs/msg/ColorRGBA |
/rgb_led_mod/variables/color/write | std_msgs/msg/ColorRGBA |
/rgb_led_mod/variables/time/read | std_msgs/msg/Float32 |
/rgb_led_mod/variables/time/write | std_msgs/msg/Float32 |
Controller-motor container type
This container type allows to control a motor with a reduction and a sensor (usually called motor-reducer or speed-reducer). This container computes PID for speed, position and motion planning.
You can find basic information about PID control here: An introduction to PID control with DC motor, and a code example to tune your PID at the end of this page.
Its type has access to all common capabilities.
containers’s type settings:
Warning: This container doesn't save any of the following parameters, they must be set each time your container reboots.
Before using your Controller-motor container, you have to setup the resolution, motor reduction and eventually the wheel size, if you plan to use translation modes. To check the configuration, just make a complete turn on the motor shaft with your hand and check if the rotation position value is OK.
Both PID’s values have to be set accordingly to the motor-reducer plugged to the board. Each different motor-reducer will have different PID’s values for position and speed control, and you have to define them by yourself.
The default values [0, 0, 0]
won’t have any effect on the motor, and must be changed if you plan to use any position or speed control mode.
To setup your PID please refer to the example at the end of this page.
Warning: PID for position and speed must be set in your code as an initialization before starting to use your container with position or speed control.
Now that everything is configured, you can enable the control modes you want to use. You can use position and speed mode simultaneously. Power mode is only usable alone. The Controller-motor is now ready to use, you can disable compliance to start moving the motor.
Functions
Function name and parameters | Action | Comment |
---|---|---|
setToZero(self) | Resets current position of the motor to 0 | You can use it to initialize the position of the motor |
control(self) | Displays container type graphical interface | Only available using Jupyter notebook |
Variables
Motor settings
Variable name | Action | Type |
---|---|---|
positionPid | Sets position PID used for rotation position mode and translation position mode | read / write: [float P, float I, float D] |
speedPid | Sets speed PID used for rotation speed mode and translation speed mode | read / write: [float P, float I, float D] |
encoder_res | Defines the motor sensor resolution, in steps by rotation. This container considers that the sensor is placed before the reduction. If it is not your case, just setup a reduction ratio of 1. | read / write: float |
reduction | Defines the motor reduction. Set this value to 1 if your sensor measures after the reduction. | read / write: float |
wheel_size | Defines wheel size used for translation mode, in mm. | read / write: float |
Motor control modes
Variable name | Action | Type |
---|---|---|
compliant | - True: disables the motor driver, you can use it to move the motor by hand. - False: Enables the motor driver. | read / write: Boolean (True or False) |
power_mode | Enables/Disables the power control mode. Disables any other control mode if enabled. | read / write: Boolean (True or False) |
rot_position_mode | Enables/Disables the motor rotation position control mode. Disables power mode and translation position mode if enabled. Doesn't work if no position PID is configured. | read / write: Boolean (True or False) |
rot_speed_mode | Enables/Disables the motor rotation speed control mode. Disables power mode and translation speed mode if enabled. Doesn't work if no speed PID configured. | read / write: Boolean (True or False) |
trans_position_mode | Enables/Disables the motor translation position control mode. Disables power mode and rotation position mode if enabled. Doesn't work if no position PID configured. | read / write: Boolean (True or False) |
trans_speed_mode | Enables/Disables the motor translation speed control mode. Disables power mode and rotation speed mode if enabled. Doesn't work if no speed PID configured. | read / write: Boolean (True or False) |
Motor sensing
Variable name | Action | Type |
---|---|---|
rot_position | Reads rotation position in ° Reading it auto-Enables actualization. | read only: float |
rot_position | Starts/Stops rotation position measurement actualization | write only: Boolean (True or False) |
rot_speed | Reads rotation speed in °/s Reading it auto-Enables actualization. | read only: float |
rot_speed | Starts/Stops rotation speed measurement actualization | write only: Boolean (True or False) |
trans_position | Reads translation position in mm Reading it auto-Enables actualization. | read only: float |
trans_position | Starts/Stops translation position measurement actualization | write only: Boolean (True or False) |
trans_speed | Reads translation speed in mm/s Reading it auto-enables actualization. | read only: float |
trans_speed | Starts/Stops translation speed measurement actualization | write only: Boolean (True or False) |
current | Reads the current consumption in A Reading it auto-enables actualization. | read only: float |
current | Starts/Stops current measurement actualization | write only: Boolean (True or False) |
Motor commands
Variable name | Action | Type |
---|---|---|
power_ratio | Sets the power quantity send to the motor between -100% and 100%. | read / write: float |
target_rot_position | Sets the target rotation position to reach in °. | read / write: float |
target_rot_speed | Sets the target rotation speed to reach in °/s. | read / write: float |
target_trans_position | Sets the target translation position to reach in mm. | read / write: float |
target_trans_speed | Sets the target translation speed to reach in mm/s. | read / write: float |
ROS topics
Topic name | Message type | Comment |
---|---|---|
/mod/variables/positionPid/read | geometry_msgs/msg/Vector3 | |
/mod/variables/positionPid/write | geometry_msgs/msg/Vector3 | |
/mod/variables/speedPid/read | geometry_msgs/msg/Vector3 | |
/mod/variables/speedPid/write | geometry_msgs/msg/Vector3 | |
/mod/variables/encoder_res/read | std_msgs/msg/Float32 | |
/mod/variables/encoder_res/write | std_msgs/msg/Float32 | |
/mod/variables/reduction/read | std_msgs/msg/Float32 | |
/mod/variables/reduction/write | std_msgs/msg/Bool | |
/mod/variables/wheel_size/read | std_msgs/msg/Float32 | |
/mod/variables/wheel_size/write | std_msgs/msg/Float32 | |
/mod/variables/compliant/read | std_msgs/msg/Bool | |
/mod/variables/compliant/write | std_msgs/msg/Bool | |
/mod/variables/power_mode/read | std_msgs/msg/Bool | |
/mod/variables/power_mode/write | std_msgs/msg/Bool | |
/mod/variables/rot_position_mode/read | std_msgs/msg/Bool | |
/mod/variables/rot_position_mode/write | std_msgs/msg/Bool | |
/mod/variables/rot_speed_mode/read | std_msgs/msg/Bool | |
/mod/variables/rot_speed_mode/write | std_msgs/msg/Bool | |
/mod/variables/trans_position_mode/read | std_msgs/msg/Bool | |
/mod/variables/trans_position_mode/write | std_msgs/msg/Bool | |
/mod/variables/trans_speed_mode/read | std_msgs/msg/Bool | |
/mod/variables/trans_speed_mode/write | std_msgs/msg/Bool | |
/mod/variables/rot_position/read | std_msgs/msg/Float32 | |
/mod/variables/rot_position/write | std_msgs/msg/Bool | |
/mod/variables/rot_speed/read | std_msgs/msg/Float32 | |
/mod/variables/rot_speed/write | std_msgs/msg/Bool | |
/mod/variables/trans_position/read | std_msgs/msg/Float32 | |
/mod/variables/trans_position/write | std_msgs/msg/Bool | |
/mod/variables/trans_speed/read | std_msgs/msg/Float32 | |
/mod/variables/trans_speed/write | std_msgs/msg/Bool | |
/mod/variables/current/read | std_msgs/msg/Float32 | |
/mod/variables/current/write | std_msgs/msg/Bool | |
/mod/variables/power_ratio/read | std_msgs/msg/Float32 | |
/mod/variables/power_ratio/write | std_msgs/msg/Float32 | |
/mod/variables/target_rot_position/read | std_msgs/msg/Float32 | value in radians |
/mod/variables/target_rot_position/write | std_msgs/msg/Float32 | value in radians |
/mod/variables/target_rot_speed/read | std_msgs/msg/Float32 | value in radians |
/mod/variables/target_rot_speed/write | std_msgs/msg/Float32 | value in radians |
/mod/variables/target_trans_position/read | std_msgs/msg/Float32 | |
/mod/variables/target_trans_position/write | std_msgs/msg/Float32 | |
/mod/variables/target_trans_speed/read | std_msgs/msg/Float32 | |
/mod/variables/target_trans_speed/write | std_msgs/msg/Float32 |
Example code
PID Setting example code
The PID values allow your motor to stick to the target command as fast as possible. The quality of a set of PID values depends on time to reach the target position and position precision. Tuning a PID is something difficult and takes a lot of practice. It's really important to have simple ways to evaluate PID values impact on your motor before starting to tune these values. Here is the code we use at Luos to tune a PID by ourself. Use it with Jupyter notebook to get your plot instantly.
The main code:
%matplotlib inline
from pyluos import Device
from IPython.display import clear_output
import time
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
# 1. Connect your Luos network (here using an USB container for example)
r = Device('/dev/cu.usbserial-DN2AAOVK')
r.containers
# 2. Select the container of your network you need to configure
container = r.controller_moto
# 3. Setup container basic settings
container.encoder_res = 48
container.reduction = 26.851
def run_speed_test(velocity_target):
container.rot_position = False
container.rot_speed = True
container.rot_position_mode = False
container.rot_speed_mode = True
container.target_rot_speed = 0.0
container.compliant = False
target = []
real = []
test_time_vector = []
test_start_time = time.time()
target.append(container.target_rot_speed)
real.append(container.rot_speed)
test_time = time.time()
test_time_vector.append(0.0)
while (test_time < test_start_time + 0.5):
target.append(container.target_rot_speed)
real.append(container.rot_speed)
test_time_vector.append(test_time - test_start_time)
test_time = time.time()
container.target_rot_speed = velocity_target
while (test_time < test_start_time + 2.5):
target.append(container.target_rot_speed)
real.append(container.rot_speed)
test_time_vector.append(test_time - test_start_time)
test_time = time.time()
container.compliant = True
plot_test(test_time_vector, target, real)
def run_pos_test(pos_target):
container.rot_speed = False
container.rot_position = True
container.rot_speed_mode = False
container.rot_position_mode = True
container.target_rot_position = 0.0
container.compliant = False
target = []
real = []
test_time_vector = []
test_start_time = time.time()
target.append(container.target_rot_position)
real.append(container.rot_position)
test_time = time.time()
test_time_vector.append(0.0)
while (test_time < test_start_time + 1):
target.append(container.target_rot_position)
real.append(container.rot_position)
test_time_vector.append(test_time - test_start_time)
test_time = time.time()
container.target_rot_position = pos_target
while (test_time < test_start_time + 2.5):
target.append(container.target_rot_position)
real.append(container.rot_position)
test_time_vector.append(test_time - test_start_time)
test_time = time.time()
container.compliant = True
plot_test(test_time_vector, target, real)
def plot_test(test_time_vector, target, real):
fig = plt.figure()
ax = plt.subplot(111)
ax.plot(test_time_vector,target,'r')
ax.plot(test_time_vector,real,'b')
plt.show()
plt.close(fig)
Now, you are ready to tune the PID values for position and speed control modes. To do that, you have to try values to get best the result possible. In order to succeed, we advise you to do it step by step:
- Set P, I, and D values to 0.
- Increase the P value until you have a small oscillation around the target.
- Increase the D value until you have a fast and stable position.
- Increase with really small figures the I value to improve the motor precision.
The code you can use to tune your speed PID:
# Speed PID settings
container.speedPid = [0.1,0.1,0] # speed PID [P, I, D]
run_speed_test(100.0)
The code you can use to tune your position PID:
# Position PID settings
container.positionPid = [4.0,0.02,100] # position PID [P, I, D]
run_pos_test(100.0)
Example command from ROS topics
By publishing on 3 topics you will take control over the Controller-motor named controller_moto
to a velocity command of 1.57 rad/s:
# Launch the broker. Note: warnings will be displayed, please ignore them
ros2 launch luos_interface broker.launch.py
# Start the command in velocity mode
ros2 topic pub -1 /controller_moto/variables/rot_speed_mode/write std_msgs/msg/Bool data:\ true\
ros2 topic pub -1 /controller_moto/variables/target_rot_speed/write std_msgs/msg/Float32 data:\ 90.0\
ros2 topic pub -1 /controller_moto/variables/compliant/write std_msgs/msg/Bool data:\ false\
Then publish true
to the /controller_moto/variables/compliant/write
topic to stop the driver.
DC-motor container type
The DC-motor container allows to drive a DC motor using only power mode.
Its type has access to all common capabilities.
Functions
Function name and parameters | Action | Comment |
---|---|---|
control(self) | Displays container type graphical interface | Only available using Jupyter notebook |
Variables
Variable name | Action | Type |
---|---|---|
power_ratio | Sets the power quantity send to the motor between -100% and 100%. | read / write: float |
ROS topics
Topic name | Message type |
---|---|
/DC_motor1_mod/variables/power_ratio/read | std_msgs/msg/Float32 |
/DC_motor1_mod/variables/power_ratio/write | std_msgs/msg/Float32 |
Distance container type
The Distance container handles a sensor measuring a distance in mm.
Its type has access to all common capabilities.
Variables
Variable name | Action | Type |
---|---|---|
distance | Reads the measured distance in mm | read only: Float |
threshold | Thresholds distance variation before filter_changed event trigers. Default value 10 mm. | read / write: Float |
Events
Event name | Trigger |
---|---|
changed | Any movement on the distance measurement |
filter_changed | Movement bigger than threshold |
ROS topics
Topic name | Message type |
---|---|
/mod/variables/distance/read | std_msgs/msg/Float32 |
/mod/variables/threshold/read | std_msgs/msg/Float32 |
/mod/variables/threshold/write | std_msgs/msg/Float32 |
/mod/events/changed | luos_msgs/msg/FloatChange |
/mod/events/filter_changed | luos_msgs/msg/FloatChange |
Dynamixel container type
The Dynamixel container allows to control Dynamixel motors.
Its type has access to all common capabilities.
Functions
Function name and parameters | Action | Comment |
---|---|---|
set_id(self, id) | Changes motor ID | This new Id will be saved by the Dynamixel motor. You have to detect motors again to make it work after this change. |
detect(self) | Launches a motor detection | You have to run a luos detection to include or exclude new motors. |
register(self, register, val) | Sets a Dynamixel register value. | This register only manage word size register. Use it only if you know what you do. |
control(self) | Displays container type graphical interface | Only available using Jupyter notebook |
Variables
Variable name | Action | Type |
---|---|---|
compliant | - True: disables the motor power, you can use it to move the motor by hand. - False: Enables the motor power. | read / write: Boolean (True or False) |
target_rot_position | Sets the target rotation position to reach in °. | read / write: Float |
target_rot_speed | Sets the target rotation speed to reach in °/s. | read / write: Float |
wheel_mode | Enables or disables wheel mode on motor | read / write: Boolean (True or False) |
rot_position | Measured position of the motor in °. | read / write: Float |
temperature | Measured temperature of the motor in °C. | read / write: Float |
positionPid | Sets position PID used for rotation position mode and translation position mode | read / write: [float P, float I, float D] |
power_ratio_limit | Max power limit in %. | read / write: Float |
rot_position_limit | Min and Max rotation position limit in °. | read / write: [Float(min), Float(max)] |
ROS topics
Topic name | Message type | Comment |
---|---|---|
/dxl_1/variables/compliant/read | std_msgs/msg/Boolean | |
/dxl_1/variables/compliant/write | std_msgs/msg/Boolean | |
/dxl_1/variables/target_rot_position/read | std_msgs/msg/Float32 | |
/dxl_1/variables/target_rot_position/write | std_msgs/msg/Float32 | |
/dxl_1/variables/target_rot_speed/read | std_msgs/msg/Float32 | |
/dxl_1/variables/target_rot_speed/write | std_msgs/msg/Float32 | |
/dxl_1/variables/wheel_mode/read | std_msgs/msg/Boolean | |
/dxl_1/variables/wheel_mode/write | std_msgs/msg/Boolean | |
/dxl_1/variables/rot_position/read | std_msgs/msg/Float32 | |
/dxl_1/variables/rot_position/write | std_msgs/msg/Float32 | |
/dxl_1/variables/temperature/read | std_msgs/msg/Float32 | |
/dxl_1/variables/temperature/write | std_msgs/msg/Float32 | |
/dxl_1/variables/positionPid/read | geometry_msgs/msg/Vector3 | (x=P, y=I, z=D) |
/dxl_1/variables/positionPid/write | geometry_msgs/msg/Vector3 | (x=P, y=I, z=D) |
/dxl_1/variables/power_ratio_limit/read | std_msgs/msg/Float32 | |
/dxl_1/variables/power_ratio_limit/write | std_msgs/msg/Float32 | |
/dxl_1/variables/rot_position_limit/read | geometry_msgs/msg/Vector3 | (x=min, y=max, z= |
/dxl_1/variables/rot_position_limit/write | geometry_msgs/msg/Vector3 | (x=min, y=max, z= |
Example code
Example commands from ROS topics
Plot the real time angular position with RQT plot:
ros2 run rqt_plot rqt_plot /dxl_1/variables/rot_position/read
Disable compliance:
ros2 topic pub -1 /dxl_1/variables/compliant/write std_msgs/msg/Bool data:\ false\
The motor will move to its saved target.
Switch to wheel mode and set a rotational speed target:
ros2 topic pub -1 /dxl_1/variables/wheel_mode/write std_msgs/msg/Bool data:\ true\
ros2 topic pub -1 /dxl_1/variables/target_rot_speed/write std_msgs/msg/Float32 data:\ 3.14\
The motor will move at 3.14 rad/s
Switch to position mode and set a rotational position target:
ros2 topic pub -1 /dxl_1/variables/wheel_mode/write std_msgs/msg/Bool data:\ false\
ros2 topic pub -1 /dxl_1/variables/target_rot_position/write std_msgs/msg/Float32 data:\ 0.0\
The motor will go to angle zero.
Note: If no motor is connected to the Dynamixel board, the ROS broker will display a warning message and ignore the board.
Gate container type
The Gate container allows to translate Json to Luos and Luos to Json constantly. This container continuously pulls data from the sensors detected on the network and streams it into a Json format. Also, it can receive Json data and convert it into a container command.
You need to have at least one of these containers in one of your nodes to use the Luos network with a computer using Pyluos or any other lib.
Json is a really mainstream standard allowing you to use your favorite language easily trough a Json library.
Its type has access to all common capabilities.
Functions
Function name and parameters | Action | Comment |
---|---|---|
control(self) | Displays container type graphical interface | Only available using Jupyter notebook |
Variables
Variable name | Action | Type |
---|---|---|
delay | Sets or reads the network refresh delay in ms. | read / write: Integer |
ROS topics
The gate has no topic but all topics names of all containers are prefixed with the name given to the broker associated to this gate at runtime.
Default is luos_broker
but can be changed by passing an argument to the launchfile, e.g.: ros2 launch luos_interface broker.launch.py name:=luos_broker_2
. This name must be unique to prevent conflicts.
Imu container type
The Imu container handles an inertial sensor.
Its type has access to all common capabilities.
Imu containers can measure:
- Compass – Magnetic field data in micro-tesla on each axis
- Gyro – X, Y, Z axis rotational acceleration data in degrees per second
- Accel – X, Y, Z axis linear acceleration data in G
- Heading – 360 degrees from North with Y+ axis as the pointer
- Rotational Matrix – linear math 9 element matrix representation
- Euler Angles – Pitch, roll, yaw based in degrees with frame reference
- Quaternions – Sensor fused w, x, y, z rotational angles
- Linear Acceleration – Linear acceleration in body frame coordinates
- Gravity Vector – Which access gravity effects
- Pedometer – Step number
- walk time – Duration (second) of the walk
By default, the container will send only quaternions to keep a low number of data and avoid bus congestion. Retrieving any other types of measures requires to enable them first.
The easiest way to enable a measure is by using it, as pyluos automatically enables a called measure. For example, to retrieve the linear acceleration value when it’s disabled, you can execute:
device.Imu_mod.linear_acceleration
This command doesn’t allow you to disable the value after using it in order to keep your device responsive. Another way to enable or disable a value is to set it to True
or False
. For example, if you want to disable the linear acceleration measure previously activated, you can execute:
device.Imu_mod.linear_acceleration = False
Functions
Function name and parameters | Action | Comment |
---|---|---|
control(self) | Displays container type graphical interface | Only available using Jupyter notebook |
Variables
Variable name | Action | Type |
---|---|---|
compass | Magnetic field data in micro-tesla on each axis | read only: [Float, Float, Float] |
compass | Starts/Stops compass measurement actualization | write only: Boolean (True or False) |
gyro | X, Y, Z axis rotational acceleration data in degrees per second | read only: [Float, Float, Float] |
gyro | Starts/Stops gyro measurement actualization | write only: Boolean (True or False) |
acceleration | X, Y, Z axis linear acceleration data in G | read only: [Float, Float, Float] |
acceleration | Starts/Stops acceleration measurement actualization | write only: Boolean (True or False) |
heading | 360 degrees from North with Y+ axis as the pointer | read only: [Float, Float, Float] |
heading | Starts/Stops heading measurement actualization | write only: Boolean (True or False) |
rotational_matrix | Linear math 9 element matrix representation | read only: [Float, Float, Float, Float, Float, Float, Float, Float, Float] |
rotational_matrix | Starts/Stops rotational_matrix measurement actualization | write only: Boolean (True or False) |
euler | Pitch, roll, yaw based in degrees with frame reference | read only: [Float, Float, Float] |
euler | Starts/Stops euler measurement actualization | write only: Boolean (True or False) |
quaternion | Sensor fused w, x, y, z rotational angles | read only: [Float, Float, Float, Float] |
quaternion | Starts/Stops quaternion measurement actualization | write only: Boolean (True or False) |
linear_acceleration | Linear acceleration in body frame coordinates | read only: [Float, Float, Float] |
linear_acceleration | Starts/Stops linear_acceleration measurement actualization | write only: Boolean (True or False) |
gravity_vector | Which access gravity effects | read only: [Float, Float, Float] |
gravity_vector | Starts/Stops gravity_vector measurement actualization | write only: Boolean (True or False) |
pedometer | Step number | read only: int |
pedometer | Starts/Stops pedometer measurement actualization | write only: Boolean (True or False) |
walk_time | Duration (second) of the walk | read only: Float |
walk_time | Starts/Stops walk_time measurement actualization | write only: Boolean (True or False) |
ROS topics
Topic name | Message type |
---|---|
/Imu_mod/variables/pedometer/read | std_msgs/msg/UInt32 |
/Imu_mod/variables/pedometer/write | std_msgs/msg/Bool |
/Imu_mod/variables/walk_time/read | std_msgs/msg/Float32 |
/Imu_mod/variables/walk_time/write | std_msgs/msg/Bool |
/Imu_mod/variables/gravity_vector/read | geometry_msgs/msg/Vector3 |
/Imu_mod/variables/gravity_vector/write | std_msgs/msg/Bool |
/Imu_mod/variables/heading/read | std_msgs/msg/UInt32 |
/Imu_mod/variables/heading/write | std_msgs/msg/Bool |
/Imu_mod/acceleration | geometry_msgs/msg/Accel |
/Imu_mod/imu | sensor_msgs/msg/Imu |
/Imu_mod/compass | sensor_msgs/msg/MagneticField |
Example of use of your IMU container using Jupyter notebook
In this example, we will display in 3D the rotation sensor by using quaternions. In order to do that, we will use the pythreejs lib and jupyter notebook.
First, install and enable this library by typing these commands in a terminal:
pip install pythreejs
jupyter nbextension install --py --symlink --sys-prefix pythreejs
jupyter nbextension enable --py --sys-prefix pythreejs
Now, restart jupyter notebook and add this code to a new python script:
from pyluos import Device
import time
from pythreejs import *
# Create a cube to move following our sensor
cube = Mesh(
BoxBufferGeometry(3, 3, 3),
MeshPhysicalMaterial(color='green'),
position=[0, 0, 0],
castShadow = True
)
# Create a floor
plane = Mesh(
PlaneBufferGeometry(100, 100),
MeshPhysicalMaterial(color='gray'),
position=[0, -1.5, 0], receiveShadow = True)
plane.rotation = (-3.14/2, 0, 0, 'XYZ')
# Create a directional light following our cube
key_light = SpotLight(position=[0, 10, 10], angle = 0.3, penumbra = 0.1, target = cube, castShadow = True)
key_light.shadow.mapSize = (2048, 2048)
# Create a camera
c = PerspectiveCamera(position=[4, 12, 10], up=[0, 1, 0],
aspect=800/400)
# Create a scene
scene = Scene(children=[plane, cube, c, key_light, AmbientLight()])
# Display the scene with shadow and everything.
renderer = Renderer(camera=c,
scene=scene,
controls=[OrbitControls(controlling=c)],
width=800, height=400,
)
renderer.shadowMap.enabled = True
renderer.shadowMap.type = 'PCFSoftShadowMap'
display(renderer)
# Connect your Luos network (here using an USB container)
r = Device('/dev/cu.usbserial-DN38OIYT')
# Control the rotation of the cube with the rotation of the Imu sensor
while(True):
cube.quaternion = r.Imu_mod.quaternion
time.sleep(0.05)
You should obtain a result like this:
Light container type
The Light container handles a sensor measuring a light intensity in lux.
Its type has access to all common capabilities.
Variables
Variable name | Action | Type |
---|---|---|
lux | Reads the measured light intensity in lux | read only: Float |
threshold | Thresholds light intensity variation before filter_changed event trigers. Default value 10 lux. | read / write: Float |
Events
Event name | Trigger |
---|---|
changed | Any movement on the light intensity measurement |
filter_changed | Movement bigger than threshold |
ROS topics
Topic name | Message type |
---|---|
/mod/variables/lux/read | std_msgs/msg/Float32 |
/mod/variables/threshold/read | std_msgs/msg/Float32 |
/mod/variables/threshold/write | std_msgs/msg/Float32 |
/mod/events/changed | luos_msgs/msg/FloatChange |
/mod/events/filter_changed | luos_msgs/msg/FloatChange |
Servo container type
The Servo container allows to drive RC elements like servomotor
Its type has access to all common capabilities.
Functions
Function name and parameters | Action | Comment |
---|---|---|
control(self) | Displays container type graphical interface | Only available using Jupyter notebook |
Variables
Variable name | Action | Type |
---|---|---|
rot_position | Rotation position in °. | read / write: float |
max_angle | Sets max_angle value, in degrees (° ) (180.0 by default) | read / write: Float |
min_pulse | Sets PWM minimum pulse value, in seconds (s ) (0.0005 by default) | read / write: Float |
max_pulse | Sets PWM maximum pulse value, in seconds (s ) (0.0015 by default) | read / write: Float |
ROS topics
Topic name | Message type | Comment |
---|---|---|
/servo1_mod/variables/rot_position/read | std_msgs/msg/Float32 | value in radians |
/servo1_mod/variables/rot_position/write | std_msgs/msg/Float32 | value in radians |
/servo1_mod/variables/max_angle/read | std_msgs/msg/Float32 | value in radians |
/servo1_mod/variables/max_angle/write | std_msgs/msg/Float32 | value in radians |
/servo1_mod/variables/min_pulse/read | std_msgs/msg/Float32 | |
/servo1_mod/variables/min_pulse/write | std_msgs/msg/Float32 | |
/servo1_mod/variables/max_pulse/read | std_msgs/msg/Float32 | |
/servo1_mod/variables/max_pulse/write | std_msgs/msg/Float32 |
Example code
Example command from ROS topics
Move the servo1 to 1.57 radians:
ros2 topic pub -1 /servo1_mod/variables/rot_position/write std_msgs/msg/Float32 data:\ 1.57\
State container type
The State container can handles a sensor (Button board for example), or an actuator (Power Switch board for example). Generally, this type of containers allows to manage bi-state elements such as on/off, pushed/release, 0/1, ...
Its type has access to all common capabilities.
Functions
Function name and parameters | Action | Comment |
---|---|---|
control(self) | Displays container type graphical interface | Only available using Jupyter notebook |
Variables
Variable name | Action | Type |
---|---|---|
state | Sets or reads the container state | read / write: Boolean (True or False) |
Events
Event name | Trigger |
---|---|
changed | Any state modification pressed or released |
pressed | State modification from True to False |
released | State modification from False to True |
ROS topics
Topic name | Message type |
---|---|
/button_mod/variables/state/read | std_msgs/msg/Bool |
/button_mod/variables/state/write | std_msgs/msg/Bool |
/button_mod/events/released | luos_msgs/msg/BoolChange |
/button_mod/events/pressed | luos_msgs/msg/BoolChange |
/button_mod/events/changed | luos_msgs/msg/BoolChange |
Stepper container type
This container type allows to control a stepper motor. It computes micro-stepping and motion planning.
Its type has access to all common capabilities.
containers’s type settings:
Warning: This container doesn't save any of the following parameters, they must be set each time your container reboots.
The number of steps per turn must be defined, as well as the wheel diameter at the output of the motor if you wahnt to use translation. These specs may figure in your motor’s datasheet.
Functions
Function name and parameters | Action | Comment |
---|---|---|
setToZero(self) | Resets current position of the motor to 0 | You can use it to initialize the position of the motor |
control(self) | Displays container type graphical interface | Only available using Jupyter notebook |
Variables
Motor settings
Variable name | Action | Type |
---|---|---|
stepPerTurn | Defines the stepper resolution | read / write: float |
wheel_size | Defines wheel size used for translation mode | read / write: float |
Motor control modes
Variable name | Action | Type |
---|---|---|
compliant | - True: disables the motor driver, you can use it to move the motor by hand. - False: Enables the motor driver. | read / write: Boolean (True or False) |
rot_position_mode | Enables/Disables the motor rotation position control mode. Disables power mode and translation position mode if enabled. Doesn't work if no position PID is configured. | read / write: Boolean (True or False) |
rot_speed_mode | Enables/Disables the motor rotation speed control mode. Disables power mode and translation speed mode if enabled. Doesn't work if no speed PID configured. | read / write: Boolean (True or False) |
trans_position_mode | Enables/Disables the motor translation position control mode. Disables power mode and rotation position mode if enabled. Doesn't work if no position PID configured. | read / write: Boolean (True or False) |
trans_speed_mode | Enables/Disables the motor translation speed control mode. Disables power mode and rotation speed mode if enabled. Doesn't work if no speed PID configured. | read / write: Boolean (True or False) |
Motor commands
Variable name | Action | Type |
---|---|---|
target_rot_position | Sets the target rotation position to reach in °. | read / write: float |
target_rot_speed | Sets the target rotation speed to reach in °/s. | read / write: float |
target_trans_position | Sets the target translation position to reach in mm. | read / write: float |
target_trans_speed | Sets the target translation speed to reach in mm/s. | read / write: float |
ROS topics
Topic name | Message type | Comment |
---|---|---|
/stepper_1/variables/stepPerTurn/read | std_msgs/msg/Float32 | |
/stepper_1/variables/stepPerTurn/write | std_msgs/msg/Float32 | |
/stepper_1/variables/wheel_size/read | std_msgs/msg/Float32 | |
/stepper_1/variables/wheel_size/write | std_msgs/msg/Float32 | |
/stepper_1/variables/compliant/read | std_msgs/msg/Bool | |
/stepper_1/variables/compliant/write | std_msgs/msg/Bool | |
/stepper_1/variables/rot_position_mode/read | std_msgs/msg/Bool | |
/stepper_1/variables/rot_position_mode/write | std_msgs/msg/Bool | |
/stepper_1/variables/rot_speed_mode/read | std_msgs/msg/Bool | |
/stepper_1/variables/rot_speed_mode/write | std_msgs/msg/Bool | |
/stepper_1/variables/trans_position_mode/read | std_msgs/msg/Bool | |
/stepper_1/variables/trans_position_mode/write | std_msgs/msg/Bool | |
/stepper_1/variables/trans_speed_mode/read | std_msgs/msg/Bool | |
/stepper_1/variables/trans_speed_mode/write | std_msgs/msg/Bool | |
/stepper_1/variables/target_rot_position/read | std_msgs/msg/Float32 | value in radians |
/stepper_1/variables/target_rot_position/write | std_msgs/msg/Float32 | value in radians |
/stepper_1/variables/target_rot_speed/read | std_msgs/msg/Float32 | value in radians |
/stepper_1/variables/target_rot_speed/write | std_msgs/msg/Float32 | value in radians |
/stepper_1/variables/target_trans_position/read | std_msgs/msg/Float32 | |
/stepper_1/variables/target_trans_position/write | std_msgs/msg/Float32 | |
/stepper_1/variables/target_trans_speed/read | std_msgs/msg/Float32 | |
/stepper_1/variables/target_trans_speed/write | std_msgs/msg/Float32 |
Voltage container type
The Voltage container handles a sensor measuring voltage.
Its type has access to all common capabilities.
Functions
Function name and parameters | Action | Comment |
---|---|---|
control(self) | Displays container type graphical interface | Only available using Jupyter notebook |
Variables
Variable name | Action | Type |
---|---|---|
volt | Reads or writes voltage in V | read / write: Float |
threshold | Thresholds voltage variation before filter_changed event trigger. Default value 1.0 V. | read / write: Float |
Events
Event name | Trigger |
---|---|
changed | Any state modification falling or raising |
filter_changed | Voltage variation bigger than threshold |
ROS topics
Topic name | Message type |
---|---|
/mod/variables/volt/read | std_msgs/msg/Float32 |
/mod/variables/volt/write | std_msgs/msg/Float32 |
/mod/variables/threshold/read | std_msgs/msg/Float32 |
/mod/variables/threshold/write | std_msgs/msg/Float32 |
/mod/events/changed | luos_msgs/msg/FloatChange |
/mod/events/filter_changed | luos_msgs/msg/FloatChange |

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 containers in ROS 2 with the bike sharing example.
In this tutorial, we will assume you're using ROS 2. If you want to communicate with a ROS 1 ecosystem, follow this quickstart anyway since ROS 2 needs to be installed and then refer to the Retrocompatibility with ROS 1 page.
Basics: a few ROS-applied-to-Luos concepts
Here is a summary of core concepts in ROS:
- ROS workspace: this is the directory in which you download all the ROS software that is built from sourcecode. You will likely setup your own projects here but it is also common to download dependencies here, 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 the form of structured pieces of data called messages, being exchanged through named communication channels called topics. Just like the filesystem of your hardrive, topics are hierarchised. E.g.
/something/into/something/else/data
- ROS package: a package is a folder that contains resources such as nodes, launchfiles, messages, ... Here we have 2 packages named
luos_msgs
andluos_interface
.
With Luos, data coming from/to Luos containers are messages sent on topics. Some messages representing popular types of data 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 to a Luos gate since it connects to one (and only one) gate, and ensures the communication seamlessly. A broker can be launched with the command ros2 launch luos_interface broker.launch.py
, which then attempts to connect to a serial Luos gate.
With Luos, topics names all start with a prefix being the container'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
As an example, /Imu_mod/variables/pedometer/read
allows to read-only the current pedometer variable from the IMU container named Imu_mod
.
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 https://github.com/aubrune/luos_ros2.git
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 in your Luos gate and other containers, such as Imu, Color or State, and run the broker. It will return the detected containers:
~$ ros2 launch luos_interface broker.launch.py
[INFO] [luos_broker]: Connecting to /dev/ttyUSB0...
[INFO] [luos_broker]: Found containers:
-------------------------------------------------
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 broker.launch.py device:=/dev/ttyUSB1 name:=brokerUSB1
According to the containers you have plugged-in, the broker will automatically publish the relevant topics in the namespace of your containers' aliases.
Here we have plugged a State
container (alias button_mod
), a Imu
container (alias Imu_mod
) and a Color
container (alias rgb_led_mod
) to the gate; thus the broker publishes the following topics:
~$ ros2 topic list
/Imu_mod/acceleration
/Imu_mod/compass
/Imu_mod/imu
/Imu_mod/variables/gravity_vector/read
/Imu_mod/variables/gravity_vector/write
/Imu_mod/variables/heading/read
/Imu_mod/variables/heading/write
/Imu_mod/variables/pedometer/read
/Imu_mod/variables/pedometer/write
/Imu_mod/variables/walk_time/read
/Imu_mod/variables/walk_time/write
/button_mod/events/changed
/button_mod/events/pressed
/button_mod/events/released
/button_mod/variables/state/read
/button_mod/variables/state/write
/parameter_events
/rgb_led_mod/variables/color/read
/rgb_led_mod/variables/color/write
/rgb_led_mod/variables/time/read
/rgb_led_mod/variables/time/write
/rosout
Most variables advertise both /read
and /write
topics to get data, write data, or (de)activate this data source. Other types of data 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
header:
stamp:
sec: 1581267954
nanosec: 5449
frame_id: Imu_mod
orientation:
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]
angular_velocity:
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]
linear_acceleration:
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 containers, use a regular ROS publisher. For instance, here is how to light up the Luos RGB container, 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 example on the bike sharing example page.
ROS package example: Bike sharing example
This is a Luos example using ROS 2, the bike sharing application, that works this way:
- The bike pops up in steady green when it is idle
- Shake the Imu when 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 slightly blinks in green
- Press again to stop riding, it becomes idle again
It relies on the 3D vizualizer embedded in ROS 2, named RViz 2
:
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 https://github.com/aubrune/luos_bike_alarm_example.git
~/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 containers. The expected Luos containers' 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 example.launch.py
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.
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 broker.launch.py
.
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 https://github.com/ros2/ros1_bridge.git
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
roscore
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 containers before starting the broker.
source ~/ros2_ws/install/setup.bash
ros2 launch luos_interface broker.launch.py
In terminal 1: Your ROS 1 app in workspace ~/ros_ws
Let us consider here that rostopic
is the ROS 1 app you want to run with Luos containers.
source ~/ros_ws/devel/setup.bash
rostopic list
You can then publish and subscribe to the available topics in ROS 1.
Luos demonstration boards
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.
Start reading the board's general use page, then you can read the quick start page to start using your demonstration boards. You can consult the list of available Luos boards examples.
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 one hosting Luos and providing with 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 together with cables, behaviors can be programmed through a gate board on a computer, and the device can be tested in a matter of minutes!
Warning: All examples codes of this documentation use the pyluos Python library and are adapted to be used with Jupyter Notebook.
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 containers. that hosts Luos. The shield board is added to a L0 to type it with an electronic function.

Note: Power category boards don't include L0 motherboard as they provide only with power functions and don't need communication. However. he communication data pass 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
Boards categories
Luos boards examples are organized in 6 categories. Each board belongs to at least one of these categories. Understanding every categories will help to understand how to connect the Luos boards together in order to achieve any type of electronic system.

Below is the list of the six categories:
Sensor | Actuator | Communication |
---|---|---|
![]() | ![]() | ![]() |
Sensor boards are able to measure physical world environment. | Actuation boards are able to act on the physical world. | Communication boards (also called gates) are able to share your system’s inputs, outputs and configurations outside of your device, using a JSON API. |
Cognition | Interface | Power |
---|---|---|
![]() | ![]() | ![]() |
Cognition are boards dedicated to execute your code or host your AI. | These boards are built to interact with the user of the machine. | Power boards are able to share their input power source into the RobusBus communication protocol used by Luos. wire to feed other boards. |
Plugging boards together
Luos boards have at least 2 connection ports in their design. All connectors are the same, so that any board can be connected to another one using any of these ports. Just avoid to make a loop circuit, otherwise you will damage the communication between containers.
There is a correct 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, the upper surface is flat | Right 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 5V and 24V, up to 7A.
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 12V motor and an USB board: The USB board belongs to the power category, so it can share its 5V into the network's wires. But you need 12V for your motor, so you will have to add a 12V AC plug board in your network to supply the motor. In this case, the USB board doesn’t share its power, only the AC plug board does, because 5V < 12V.
Some component needs specific voltage to work properly. For example, in order to use standard servomotor, you have to feed the Luos network with 5V or 7V. If you need to combine 7V and 12V 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 easily control a Luos network. These boards host a container 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" container's task is to stream the Luos network activity into a standard Json format file, and on the oposite to allow an external device to easily interact with any device in the network.
This way, it’s 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 contribution for any programing languages. You can suggest any change or new API on the Luos' forum.
Get pyluos on github.
Update Luos, Robus and board's firmware
Instal PlatformIO if you don't have it yet and clone the Examples repository.
To update Luos, RobusBus communication protocol used by Luos. or your containers' code, you must open the librairies tab on PlatformIO in Visual Studio Code:
To update the board's firmware you need to follow 3 steps:
1. Open the container folder that you want to update in Visual Studio Code
To do this, you must go to File -> Open the folder and go to the folder of the container you want from Examples/Projects.
2. Compile it to ensure there is no error
To compile your code, you must click on the Compile button at the bottom of your window:
If your terminal goes like this, it means that your download was successful:
If the compilation goes wrong in spite of your code having been perfectly compiled, do a cleaning by clicking on the ant button and choosing Clean in the list of project tasks:
3. Upload the new code into the board
To upload the new code into the board you must click on the Upload button :
If your terminal goes like this, it means that your download was successful:
Troubleshooting
If the upload goes wrong, it may be because your driver is not up-to-date. The fix depends on your OS:
Windows
You need to install and run Zadig by following this tutorial on github.
Linux
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
Getting started
This page provides quick and easy tutorials to get started with Luos demonstration boards.
Tutorial #1
On the following steps, you will learn how to make a simple behavior with a RGB LED board and a Button 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 an USB cable
- 2x Luos cables
STEPS
1. Configure your computer
The default tool we use to control a Luos network is a board hosting a Gate container, 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 to 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 is ready to use to help you program your own behaviors.
4. Interact with the device
The USB node handle a specific container called "Gate". There are other boards hosting "Gate" container and using different connection than USB. These particular containers convert Luos containers data into something easier to understand and manage, using JSON API.
Interacting with the Luos system and program behaviors will require to spot the USB connection on your computer. The following steps are explained on the General board use page with more details. 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 container, it asks to run a network detection. This detection allows to discover all boards wired together on the network.
To list the discovered boards you can run:
print(device.containers)
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
Button button_mod 2
Led rgb_led_mod 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:
print(device.button_mod.pressed)
Python will answer True
if you execute this line by pressing the button and False
if you don't.
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.
More details are provided on the page Luos boards general use.
5. Write a simple beahvior
You can now write a simple behavior that makes the LED to 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_mod.pressed == True): # if the button is pushed
device.rgb_led_mod.color = [0,15,15] # Assigns a color to the LED
else: # If the button is released or idle
device.rgb_led_mod.color = [0,0,0] # Turns the LED off
Test your behavior by executing the code.
Tutorial #2
The following video shows a basic tutorial explaining how to make a LED and a servomotor be responsive to a potentiometer.
What’s next?
These were simple tutorials to get you on tracks.
Now you just have to create awesome projects and share them with the community.
Demonstration boards list
List of demonstration boards available on GitHub:
- Battery power input
- Button
- Controller motor
- DC motor
- Distance sensor
- Dynamixel motor
- GPIO
- IMU
- Jack power input
- Led strip
- Light sensor
- Potentiometer
- Power isolator
- Power Pi
- Power switch
- RGB LED
- Servo motor
- Stepper motor
- USB
- Wifi BLE
- Wire power input
- Accessory: Cables
Battery power input board
Default Alias: N/A
Type: N/A
Number of container(s): 0
Image
Category(-ies)
Project source
Battery power inputHow to use the Battery power input board
The Battery power input board allows to power your Luos Network using XT60 or JST battery interface. This board is not active, you can't detect it. The Battery power input board can provide 5V to 24V DC.
You can manage multiple voltage in the same network following Luos power rules defined in Luos boards general use or by using a power isolator board.
Button board
Default Alias: button_mod
Type: State
Number of container(s): 1
Image
Category(-ies)
Project source
ButtonButton board functions
The Button board provides a push-button that can be used as an interface human-machine or as a end-of-course sensor, for example. It returns the state of the button (pushed or idle).
Power considerations
The Button board supports 5V to 24V DC.
Controller-motor board
Default Alias: controller_moto
Type: Controller motor
Number of container(s): 1
Image
Category(-ies)
Project source
Controller_motorHow to connect your motor-reducer to your boards
The Controller-motor board is designed to control motors with a reducer and a sensor. It provides PH connector with 6 pins, where the motor can be plugged.
Connector's reference
Male connector's reference (on the board): B6B-PH-K-S(LF)(SN))
Female connector's reference (on the wire): PHR-6
Crimp's reference (on the wire): BPH-002T-P0.5S
Pinout and characteristics
PHR-6 connector pinout.
This board accepts supply voltage from 7V to 24V.
To control regular DC motors (without reduction neither sensor), please refer to DC motor board’s documentation.
Warning: The USB board provides too weak power to drive a motor-reducer with this board. A power board such as Battery board or Power plug board shall be used.
This board is able to control DC motors with a reduction and a sensor (usually called motor-reducer or speed-reducer).
The Controller-motor board provides a PID control on the output position, and PID control on the output speed, taking into account the reducer and the encoder.
You can find basic information about PID control here: An introduction to PID control with DC motor and a example code to tune your PID on the Controller motor container page of this documentation.
DC-motor board
Default Alias: DC_motor1_mod, DC_motor2_mod
Type: DC-motor
Number of container(s): 2
Image
Category(-ies)
Project source
DC motorDC-motor board functions
The DC-motor board allows to drive up to 2 low-power DC motors. This board is useful to easily drive a small and simple rover.
Power considerations
The DC-motor board supports 5V to 12V DC input to drive 5V to 12V DC motors up to 2 x 1.5 A (2 A peak).
Distance board
Default Alias: distance_mod
Type: Distance
Number of container(s): 1
Image
Category(-ies)
Project source
DistanceDistance board functions
The Distance board measures a distance using a time-of-flight laser range finder. This sensor is able to measure precise distances between 20 to 2000 mm.
Power considerations
The Distance board supports 5V to 24V DC input.
Dynamixel board
Default Alias: dxl_*id*
Type: N x Dynamixel motor
Number of container(s): N
Image
Category(-ies)
Project source
DxlVersions of Dynamixel board
There are two versions of this board. One is the version for XL320 Dynamixel, the other is for other types of Dynamixel (eg. AX12). Both boards have a different connector.
Except for this connection, both versions work exactly the same way.
How to connect and start the motors to the board
The Dynamixel board is special because it has a dynamic number of visible containers, depending on the number of motors plugged to it. If you have 5 motors on your board, you will see 5 DynamixelMotor containers.
Note: If you don’t plug any motor to the board, it will create a special container called
void_dxl
.
This board creates containers dynamically upon motor detection. So in order to create containers, this board has to detect motors, and to detect them each motor needs to have a proper power supply.
Indeed, if you power the Luos network with an unadapted voltage, the motors won’t reply to the board requests and you won’t be able to see any Dynamixel container on your network.
To be detected, the Dynamixel motors need to use a baudrate of 1 000 000 baud
and to have an ID between 1 and 30.
When your Dynamixel motors are properly configured, you can connect them to the Luos network. Be sure to respect the following order to have a proper start-up:
- Connect the board to the Luos network and to the Dynamixel motors.
- Connect the correct power supply to the luos network.
- Connect the USB board to your computer.
- Wait for the blue LED at the back of the board to turn off.
Note: The blue LED is ON when the network is busy detecting Dynamixel motors.
In order to begin using this board, you must disable the compliant mode, and you can then use the functions and variables of the Dynamixel container.
Warning: Dynamixel boards don’t belong to the power category. Thus, do not power your motors on the Robotis side, you won’t be able to share this power with others boards.
GPIO board
Default Alias: analog_read_P1, analog_read_P7, analog_read_P8, analog_read_P9, digit_read_P5, digit_read_P6, digit_write_P2, digit_write_P3, digit_write_P4
Number of container(s): 9
Image
Category(-ies)
Project source
GpioGPIO pinout and power consideration
The GPIO board allows you to use the pins of the L0 board through the Luos system. You can use Digital Write
, Digital Read
, or Analog Read
pins.
This board creates a container for each available pin.
Power considerations
The GPIO board supports 5V to 24V DC input.
Warning: The pins only support 3.3V.
IMU board
IMU board functions
The IMU board measures a wide set of position data and return values in several units. Refer to the IMU container page for more details.
Power considerations
The IMU board supports 5V to 24V DC.
Jack power input board
Default Alias: N/A
Type: N/A
Number of container(s): 0
Image
Category(-ies)
Project source
Jack power inputJack power input board functions
The Jack power input board allows to power your Luos Network using a power Jack.
- plug inner diameter : 2 mm
- plug outer diameter : 5.5 mm
See the DC power jack datasheet for more information.
Warning: For your choice of power supply adapter, the max Ampere value should not exceed 5A.
This board is not active, you can't detect it in a network.
You can manage multiple voltage in the same network following Luos power rules defined in Luos boards general use or by using a Power isolator board.
Power considerations
The Jack power input board can provide 5V to 24V DC.
Light board
Default Alias: light_sensor_mod
Type: Light
Number of container(s): 1
Image
Category(-ies)
Project source
Light sensorLight board functions
The Light board measures human visible light intensity and returns a value in lux.
Power considerations
The Light board supports 5V to 24V DC input.
RGB LED strip board
Default Alias: led_strip_mod
Type: Color
Number of container(s): 1
Image
N/A
Category(-ies)
Project source
Led stripBoard function
This board controls a strip of several RGB LED. Refer to the Color container page for more details.
Power considerations
The RGB LED strip board supports 5V to 24V DC.
Potentiometer board
Default Alias: potentiometer_mod
Type: Angle
Number of container(s): 1
Image
Category(-ies)
Project source
PotentiometerPotentiometer board functions
The Potentiometer board measures the board potentiometer's rotation in degree.
Power considerations
The board supports 5V to 24V DC input.
Power isolator board
Default Alias: N/A
Type: N/A
Number of container(s): 0
Image
Category(-ies)
Project source
Power isolatorPower isolator board
The Power isolator board allows to manage multiple voltages into the same Luos network. As this board is not active, you can't detect it in your network. This board isolates the voltage between each plugged-in side. You can use it to link components with different voltage needs. For example, to connect an XL320 motor (7V) and a MX28 motor (12V) on the same network, you can use a Dynamixel V2 board with a 7V power container for XL320, a Dynamixel V1 board with a 12V power container, and a Power isolator board to link both side together. You can connect a Gate like a USB board in the side you want without any functioning trouble.

Power Pi board
Power Pi board
The Power Pi board links any Raspberry Pi-like board to a Luos network. This board allows you to convert the Luos network power into 5V and up to 2A in order to power your Raspberry Pi or Odroid without any other power source. This board hosts a Gate container allowing to your Raspberry Pi to control your entire Luos network using Pyluos or any other language as a single system image. The Power Pi board supports 5V to 24V DC input.
Connection of Power Pi board to a Raspberry Pi
The connection of the Power Pi board to an ODrive board or to a Raspberry Pi board is made according to the following images.
Warning: Be sure to plug the board on the right pins of the Raspberry Pi, and facing the right side. A bad connection may damage both boards.
Red rectangles show where to plug the Power Pi board on an ODrive board (left) and on a Raspberry Pi board (right).
On the left, a Power Pi board connected to an ODrive board; on the right, a Power Pi board connected to a Raspberry Pi board.
How to easily start to create your code using this board
Coding on a Raspberry Pi in a device can be quite boring. Generally, you can't connect any screen and keyboard to work properly. That's why we created a small piece of code allowing to convert the Gate container stream into Web Socket messages. By using this Web Socket, you can connect pyluos or any other lib you created to your Raspberry Pi. This way you can create and execute your device's behaviors directly on your computer. When your behavior is complete and tested on your device, you just have to copy it into your Raspberry Pi to obtain an autonomous device.
To setup this pipe on your Raspberry Pi, please follow the tutorial on our forum.
How to setup your Power Pi's Gate
You need to setup the Power Pi board before to start using it.
First, you have to connect your Raspberry Pi to the Gate network.
Several solution exist to configure the Raspberry Pi’s Gate, we provide you with two of them according to your setup:
First solution: with a computer and the Raspberry Pi’s SD card
You will need the following parts:
- A micro SD to SD card adapter
- A computer with Bonjour installed on it You can download Bonjour by Apple here. Install it on your computer if you don’t already have it.
Plug the micro SD card to the micro-SD-to-SD adapter, and plug it to your computer. Ignore the messages that ask you if you want to format, and locate the SD card directory, named Boot. In Windows, it appears as a drive; in MacOS or Linux, go to
cd /Volumes/boot
Create a new file in this directory called wpa_supplicant.conf
. The file should contain the following code:
country=fr
update_config=1
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
network={
scan_ssid=1
ssid="SSID-Internet-box"
psk="Secured-key"
}
Choose the country according to where you live, and replace SSID-Internet-box
by the SSID of your internet device, and Secured-key
by the password.
Save the file and eject the SD card. Replace it into the Raspberry Pi’s slot.
The Raspberry Pi can be located with the expression raspberrypi.local
, thanks to the software Bonjour.
Second solution: with a screen and a keyboard
In order to establish a connection, you will need:
- A QWERTY keyboard
- An HDMI screen
- An USB charger or USB to micro-USB cable to power up the Raspberry Pi
- A micro USB to female USB adapter
- A micro-HDMI to HDMI adapter
- You can find the adapters you need in the Raspberry Pi zero toolkit, for example.
Plug all these elements and the Power Pi board on your Raspberry Pi (do not plug anything to the Power Pi board), and power it up. You should see on the screen the boot sequence and the file system expanding. After a few seconds, you should have a prompt asking you for username and password.
Usually, the Raspberry Pi has the default Raspberry username and password:
Username: pi
Password: raspberry
As you can see at the bottom of the boot screen, the SSH port is now open, so you should start by changing the password of your board to avoid any security issue.
To do that, use the following command:
sudo raspi-config
Choose option 1 to change your password and hostname, and choose option 2 to connect your board to your wifi.
You can check your Gate connection and retrieve the IP address using
ifconfig
and halt your system using
sudo halt
Your raspberry is now ready to be used, you can start setting your Luos network up.
Warning: The Power Pi board doesn’t belong to the Power category. Using the power input of your Raspberry Pi doesn’t allow you to supply the others boards in the Luos network. In order to make it work properly, please use a power board on your system.
How to use your Power Pi board
Power
Please note that the Power Pi board connected to the Raspberry Pi is already powered by the Luos network, through the power boards you use (Power board or Battery board).
However, the USB board can’t power the Raspberry Pi board, because several voltage transformations are applied along the network. You can also use an universal power supply (+5.1V micro USB) directly plugged to the Raspberry Pi.
Communication mode
By default, your Raspberry Pi starts a Luos service at boot called pyluos-usb2ws. This service creates a pipe between a websocket opened on port 9342, and the Luos system. If you send standard Luos Json data into this web socket, it is directly sent into the Luos network.
This way, you can control your device from your computer even if it is moving or dispatched. For example, if you are using pyluos to control your device, you can start your program with:
from pyluos import Device
device = Device("raspberrypi.local")
device.containers
In this example, you can replace raspberrypi.local
by your Raspberry Pi’s IP or hostname.
You should see the list of containers connected to the Power Pi board.
Cognition mode
Also, you can use your Raspberry Pi like an embedded computer for your device.
To send your Json data to your network, please use the serial port /dev/ttyAMA0
of your Raspberry Pi, as you can do it with the USB board.
Power-switch board
Default Alias: switch_mod
Type: State
Number of container(s): 1
Image
Category(-ies)
Project source
Power switchBoard function
The Power-switch board allows you to interrupt another circuit up to 10A on 230V AC or 30V DC. The blue LED on the container indicates when the link between 2 entries on the screw connector is closed.
Power considerations
The Power-switch board supports 5V to 24V DC input.
RGB LED board
Default Alias: rgb_led_mod
Type: Color
Number of container(s): 1
Image
Category(-ies)
Project source
LedBoard function
This board contains an RGB LED that can be controlled to make a light of any chosen color. Refer to the Color container page for more details.
Power considerations
The RGB LED board supports 5V to 24V DC.
Servo board
Default Alias: servo1_mod, servo2_mod, servo3_mod, servo4_mod
Type: Servo
Number of container(s): 4
Image
Category(-ies)
Project source
ServoHow to connect your servo-motors to your containers
The Servo board has 4 servo-motor ports ordered as shown on the following picture, from S1
to S4
.
Power considerations
The Servo board accepts supply voltage from 5V
to 7V
. Watch out to always power your motor with an appropriate voltage.
Warning: A USB board generally provides too weak power to drive a servomotor properly. It must be plugged to a Power plug board, for example.
Stepper board
Default Alias: stepper_mod
Type: Stepper
Number of container(s): 1
Image
Category(-ies)
Project source
StepperHow to connect a stepper motor to the Stepper board
The Stepper board has one 4-pin PH connector where a stepper motor can be plugged.
Current limitations
The current allowed in the stepper motor must be limited in order to avoid overheat into the board. The trimming potentiometer situated at the left of the radiator on the board is used to control the output current.

The trimming potentiometer is used as followed:
- Turning the trimming potentiometer clockwise (towards pin 3) has the effect of reducing the output current.
- Turning the trimming potentiometer counterclockwise (towards pin 1) has the effect of rising the output current (with overheating risks).

Warning: Considering these specifications, be sure to have the trimming potentiometer fully turned clockwise to the pin 3 in order to limit the current and avoid overheat. Then you can set the right amount of output current by slowly turning the trimming potentiometer counterclockwise.
Power considerations
This board accepts supply voltage from 7V
to 24V
.
Warning: USB Gate board doesn't provide enough voltage through USB cable to power the Stepper board nor drive a stepper motor. A power board such as Battery board or Power plug board shall be used.
USB board
Driver installation
With Windows, you must install VCP
and D2XX
drivers first. They are available for download on these pages:
https://www.ftdichip.com/Drivers/VCP.htm
https://www.ftdichip.com/Drivers/D2XX.htm
Select the files to download according to your system (x86 or x64) and install them on your computer.
How to connect the USB board to your computer
There are 2 micro-USB ports, but one of them is only used to manually update the board. The other one is the one we will use in this page.
The right USB port used on this page is the one at the opposite of the 2 Luos connectors.
How to use the USB board
Luos' USB board acts like a serial port on your system. To control your device, you have to get and set Json data into the serial port opened by the USB board. In order do that with pyluos, you can use the following python code:
from pyluos import Device
device = Device('COM13')
device.containers
On Windows
On Windows, a COM port is usually used (like COM1
, COM2
, COM3
, etc.). It can be found in Device Manager (right-click on Start button), after it’s plugged:
Once the port is known, the connexion can be set on pyluos with python:
device = Device('COM27')
On MacOS
To list the available serial ports, use the following command line:
ls /dev/cu.usbserial-*
Once the port is known, the connexion can be set on pyluos with python:
device = Device('/dev/cu.usbserial-DN30VKKB')
On Linux
To list the available serial ports, type the following in a terminal:
dmesg | grep tty
Once the port is known, the connexion can be set on pyluos with python:
device = Device('/dev/ttyS0')
Serial connectivity with other languages
In order to communicate from a computer to a Luos network through a gate, a serial connection can be established. This serial connectivity must have the following parameters:
- Baudrate: 1,000,000 Bits/s
- 8 Bits per transfer
- 1 stop bit
- No parity bit
- Least significant bit sent first
- Non inverted
Although the connection can be made with Pyluos, the Python library, other languages can be used on the computer side. Here is a C Linux serial connection example:
void init_luos_comm(luos_t* m)
{
m->serial_fd = open(SERIAL_PORT, O_RDWR);
// Check for errors
if (m->serial_fd < 0)
{
printf("Error %i from open: %s\n", errno, strerror(errno));
return -1;
}
struct termios tty;
memset(&tty, 0, sizeof tty);
if (tcgetattr(m->serial_fd, &tty) != 0)
{
printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
return -1;
}
tty.c_cflag &= ~PARENB; // Disable parity
tty.c_cflag &= ~CSTOPB; // Clear stop field
tty.c_cflag |= CS8; // 8 bits per byte
tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)
tty.c_lflag &= ~ICANON; // Disable canonical mode
tty.c_lflag &= ~ECHO; // Disable echo
tty.c_lflag &= ~ECHOE; // Disable erasure
tty.c_lflag &= ~ECHONL; // Disable new-line echo
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
// Turn off s/w flow ctrl
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
// Disable any special handling of received bytes
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);
// Prevent special interpretation of output bytes (e.g. newline chars)
tty.c_oflag &= ~OPOST;
// Prevent conversion of newline to carriage return/line feed
tty.c_oflag &= ~ONLCR;
// Wait for up to 1s (10 deciseconds), returning as soon as any data is received.
tty.c_cc[VTIME] = 10;
tty.c_cc[VMIN] = 0;
// Set in/out baud rate to be 1000000
cfsetispeed(&tty, B1000000);
cfsetospeed(&tty, B1000000);
// Save tty settings
if (tcsetattr(m->serial_fd, TCSANOW, &tty) != 0)
{
printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
return -1;
}
fcntl(m->serial_fd, F_SETFL, FNDELAY);
usleep(500000);
}
USB board power delivery
The USB board power-delivery in the Luos network is limited to 500 mA
. This board can’t power too many boards and power-demanding ones like, for example, a DC-motor board with one or two motors connected. If you experiment power issues, feel free to add a power category board like a Jack power input board.
Wifi-BLE board
Default Alias: gate
Type: Gate
Number of container(s): 1
Image
N/A
Category(-ies)
Project source
GateHow to configure the Wi-Fi network
This board allows to enable Wi-Fi communication into a Luos network.
- Plug the board to a powered Luos network or to a power source. It automatically turns the Wi-Fi on.
- Connect your computer to the board's Wi-Fi. A page automatically opens in your default browser.
- On the page, choose the name of the Wi-Fi network or choose an existing network to connect to.
Power considerations
This board accepts supply voltage from 7V
to 24V
.
Wire power input board
Default Alias: N/A
Type: N/A
Number of container(s): 0
Image
Category(ies)
Project source
Wire power inputHow to use the Wire power input board
The Wire power input board allows to power your Luos Network using a wire-screw interface. As this board is not active, you can't detect it into a network.
Power considerations
The Wire power input board can provide 5V to 24V DC.
You can manage multiple voltage in the same network following Luos power rules defined in Luos boards general use or by using a Power isolator board.
Cables
Default Alias: N/A
Type: N/A
Number of container(s): N/A
Image
Category(-ies)
N/A
Project source
CablesHow do the cables work?
The cables are used to link the Luos bords together. There are two lengths of cables : 10 cm (3.9 in) cables and 20 cm (7.9 in) cables. However, it is possible to build a cable from any disired length (see the next section).
Maximal current value: The Luos cable can handle up to 7 A.
Boards connection: The connectors on the board side and on the cable side have a foolproof so that they can plug together in one way only. Fore more information about plugging boards together with cables, please follow this link.
How to buid a Luos compatible-cable?
If you need a cable with a length not available, you can build one, provided you have electrical wire and two connectors:
- Electrical wire:
AWG 22
- Connectors:
DF11-8DS-2C, 2mm
The datasheet connector is available here.
The board connector associated to the cables is DF11-8DP-2DS
. this connector's pinout on Luos' boards is shown in the following picture:
The routing of the board connector is as shown below:
Click on the image to display it in full size.
On a Luos cable, the wires are organized in this order:
- Pin 1 (first connector end) on pin 1 (second connector end)
- Pin 2 (first connector end) on pin 2 (second connector end)
- Pin 3 (first connector end) on pin 3 (second connector end)
- ...
- Pin 8 (first connector end) on pin 8 (second connector end)
Note: If you wish to use only 4 wires instead of 8 (data only, no power), this is possible with using the pins 4 (
B_RS_485_P
), 5 (A_RS_485_N
), 3 or 6 (PTP
), andGND
.