Anton Bondarev
7 min readMar 17, 2021

Embox: the RTOS that can use Linux software. This article is showing how to do this with ‘libmodbus’ on an STM32

Let’s develop a device that will include a modbus server. Our device will be simple. After all, it is intended only for demonstration of ‘modbus’. This device will allow controlling LEDs using the ‘modbus’ protocol. To communicate with the device, we will use an ethernet connection.

Modbus’ is an open communication protocol. It is widely used in industrial applications to communicate between electronic devices. It can be used to transfer data via serial communication lines RS-485, RS-422, RS-232 and TCP/IP networks (Modbus TCP).

The ‘modbus’ protocol is simple enough to implement yourself. However, any new implementation of a functionality may contain bugs, so let’s use something ready-made.

One of the most popular implementations of the modbus protocol is the open source project ‘libmodbus’. We will use it. That will allow us to reduce development time and bugs. At the same time, we will be able to focus on the implementation of business logic, and not on studying the protocol.

We created a separate repository (https://github.com/embox/embox_project_modbus_iocontrol) special for our demo. If you wish, you can download and play everything yourself.

Design a prototype on Linux

Let’s start with developing a prototype on the host. In order to be able to use libmodbus as a library, we need to download, configure and build it. For this purpose, I sketched a Makefile:

Actually, from the configuration parameters, we only use ‘prefix’ to build the library locally. And since we want to use the library not only on the host, but also on the microcontroller, we will build a static version of it.

Now we need a modbus server. There are examples in the libmodbus project, let’s make our implementation based on some simple server.

Everything is usual here. A couple of places of interest are the mb_mapping_getstates and leddrv_updatestates functions. This is exactly the functionality that our device implements.

Thus, we need leddrv_updatestates() which sets the state of the LEDs, and leddrv_getstates() which gets the state of the LEDs.

Since we want our software to work both on the board and on the host, we need different implementations of the functions for setting and getting the state of the LEDs. Let’s store the state for the host in a regular file. This will allow the status of the LEDs to be obtained in other processes.

For example, if we want to check the LEDs states via a website, we will launch the website and specify this file as the data source of the LEDs states.

We need to specify the file where the initial state of the LEDs will be saved. The file format is simple. The states of the LEDs are listed separated by commas, ‘1’ — LED is on, and ‘0’ — LED is off. Our device has 80 LEDs, more precisely 40 pairs of LEDs. Let’s assume that by default the even LEDs will be off and the odd ones will be on. File contents

0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1

Launching the server

./led-server
led(000)=0
led(001)=1
...
led(078)=0
led(079)=1

Now we need a client to manage our device. It also can be developed based on one of the examples from ‘libmodbus’

Set up LED 78 which is off by default

./led-client set 78
Connecting to 127.0.0.1:1502
[00][01][00][00][00][06][FF][05][00][4E][FF][00]
Waiting for a confirmation...
<00><01><00><00><00><06><FF><05><00><4E><FF><00>
OK

We see in the server console

...
led(076)=0
led(077)=1
led(078)=1
led(079)=1
Waiting for an indication...
ERROR Connection reset by peer: read

That is, the LED is on. Let’s turn it off.

./led-client clr 78
Connecting to 127.0.0.1:1502
[00][01][00][00][00][06][FF][05][00][4E][00][00]
Waiting for a confirmation...
<00><01><00><00><00><06><FF><05><00><4E><00><00>
OK

In the server console we notice messages about the change

...
led(076)=0
led(077)=1
led(078)=0
led(079)=1
Waiting for an indication...
ERROR Connection reset by peer: read

Let’s launch the HTTP server. We talked about website development in the article. In addition, we only need the website for a more convenient demonstration of how modbus works. Therefore, I will not go into much detail. I will immediately give a CGI script

Let me remind you that you can start using any HTTP server with CGI support. We are using python’s built-in server. Run with the following command

python3 -m http.server — cgi -d .

Open the site in a browser

Turning on 78 LED using the modbus client

./led-client -a 127.0.0.1 set 78
Connecting to 127.0.0.1:1502
[00][01][00][00][00][06][FF][05][00][4E][FF][00]
Waiting for a confirmation...
<00><01><00><00><00><06><FF><05><00><4E><FF><00>
OK

Turning off 79 LED

./led-client -a 127.0.0.1 clr 79
Connecting to 127.0.0.1:1502
[00][01][00][00][00][06][FF][05][00][4F][00][00]
Waiting for a confirmation...
<00><01><00><00><00><06><FF><05><00><4F><00><00>
OK

We can see a difference on the site

That’s all, the modbus server based on libmodbus works great on Linux.

Port on Embox and launch on QEMU

libmodbus

Now we need to move the code to Embox. Let’s start with the ‘libmodbus’ project.

It’s simple. We need a description of a module ( in a Mybuild file)

An ‘@Build(script = “$ (EXTERNAL_MAKE)”)’ annotation indicates that a Makefile is used to work with an external project.

@BuildArtifactPath adds include paths to modules which will depend on this module .

source “libmodbus.a” means that libmodbus.a library is required.

The build makefile is also simple. I notice only that we use the internal Embox compiler ($(EMBOX_GCC) ) and the platform ( — host) which is specified in Embox ($(AUTOCONF_TARGET_TRIPLET)).

Add the project to Embox as external repo

Let me remind you that for the convenience of development, we have created a separate repository. In order to connect it to Embox, it is enough to tell Embox where the external project is located.

Using the command in the root Embxo directory:

make ext_conf EXT_PROJECT_PATH=<path to project>

For example

make ext_conf EXT_PROJECT_PATH=~/git/embox_project_modbus_iocontrol

modbus-server

The modbus server source code does not require any changes. That is, we use the same code that we developed on the host. We need to add Mybuild

With the annotations, we indicate that it is a command, and also that it depends on the ‘libmodbus’ module.

It needs emulation libraries. I will not give Mybuild files, they are trivial. Note that the sources are also used unchanged.

We also need to build our system with the modbus server. Add our modules to ‘mods.conf’

include iocontrol.modbus.http_admin
include iocontrol.modbus.cmd.flash_settings
include iocontrol.modbus.cmd.led_names
include third_party.lib.libmodbus
include iocontrol.modbus.cmd.modbus_server
include iocontrol.modbus.cmd.led_driver
include embox.service.cgi_cmd_wrapper(cmds_check=true, allowed_cmds="led_driver led_names flash_settings")include iocontrol.modbus.lib.libleddrv_ll_stub

We put our ‘leds.txt’ file with LED statuses into the root file system. But since we need a changeable file, let’s add a RAM disk and copy our file to this disk. ‘system start.inc’ content:

"export PWD=/",
"export HOME=/",
"netmanager",
"service telnetd",
"service httpd http_admin",
"ntpdate 0.europe.pool.ntp.org",
"mkdir -v /conf",
"mount -t ramfs /dev/static_ramdisk /conf",
"cp leds.txt /conf/leds.txt",
"led_driver init",
"service modbus_server",
"tish",

It is enough. Let’s launch Embox на qemu:

./scripts/qemu/auto_qemu

‘modbus’ and ‘httpd’ servers are run automatically during booting the system . Let’s control LEDs with the modbus client. We need to use QEMU IP address (10.0.2.16)

Set LED 78

./led-client -a 10.0.2.16 set 78
Connecting to 10.0.2.16:1502
[00][01][00][00][00][06][FF][05][00][4E][FF][00]
Waiting for a confirmation...
<00><01><00><00><00><06><FF><05><00><4E><FF><00>
OK

Clear LED 79

./led-client -a 10.0.2.16 clr 79
Connecting to 10.0.2.16:1502
[00][01][00][00][00][06][FF][05][00][4F][00][00]
Waiting for a confirmation...
<00><01><00><00><00><06><FF><05><00><4F><00><00>
OK

Open the site

As expected, everything is the same as on the host. We can control the device via modbus protocol already on Embox.

Launch on MCUs

We will use STM32F4-discovery to run on a microcontroller. In the above screenshots of the browser pages, you can see that 80 LEDs are used, combined in pairs. You can also notice that these pairs have other properties. For example, you can set a name, or the pair can be highlighted. In fact, we use the code from a real project and we removed unnecessary parts from it for simplicity.

But there are only 4 LEDs on the STM32F4-discovery board. It would be convenient to set the number of LEDs in order not to modify the source code. Embox has a mechanism that allows you to parameterize modules. You need to add an option in the module description (Mybuild)

And then you can use in code follow

In this case, you can change this parameter by specifying it in the mods.conf file

include  iocontrol.modbus.lib.libleddrv(leds_quantity=4)

if the parameter is not specified, then the one specified in the module by default is used, that is, 80.

We also need to manage the real output lines. The code is as follows

we need the configuration for our board In the mods.conf file. We add our modules to it :

include iocontrol.modbus.http_admin
include iocontrol.modbus.cmd.flash_settings
include iocontrol.modbus.cmd.led_names
include third_party.lib.libmodbus
include iocontrol.modbus.cmd.modbus_server
include iocontrol.modbus.cmd.led_driver
include embox.service.cgi_cmd_wrapper(cmds_check=true, allowed_cmds="led_driver led_names flash_settings")include iocontrol.modbus.lib.libleddrv(leds_quantity=4)
include iocontrol.modbus.lib.libleddrv_ll_stm32_f4_demo

In fact, these are the same modules as for ARM QEMU, with the exception of the driver, of course.

We build , flash, launch. And with the same modbus client, we control the LEDs. You just need to put the correct address and do not forget that there are only 4 LEDs on the board.

You can see how it works on this short video

Conclusion

Modbus is an open protocol widely used in industrial devices. Based on the modbus example, we showed that it is the development process that is the main difference between Embox and other operating systems for microcontrollers. Including those that have a POSIX compliant layer. After all, we essentially took the ready-made modules and developed the business logic on Linux. Then we ran it all on the target platform. Thus, the development is significantly simplifying and speeding up.

The ‘modbus’ protocol is simple enough to implement yourself. Since any new implementation of a functionality may contain bugs, we used something ready-made. With our approach, it allows each specialist to focus on their part. And of course, most issues are solved on the host, which is much more convenient than developing directly on the board.