While receiving data from your microcontroller is great, what about sending data in the opposite direction?
First, we need to understand the difference between a publisher and a subscriber.
In order to achieve this goal, we'll need to use ROS 2 topics.
Each node has to send and receive data to and from a topic, that is publishing and subscribing to a topic.
In the ROS 2 world, a node is a primary entry point (a program on a device) from which we can create other entities (in our tutorial it will be publishers, subscribers and topics).
It’s in general a device that communicates within the ROS 2 network, sending data to other devices or receiving data from other devices.
In our tutorial, we have 2 publishers:
The publisher can publish on a topic without knowing if anyone is listening.
It’s like a radio broadcasting.
For example our microcontroller needs to update a value every 100 ms.
It can then publish the data directly on a topic.
In our tutorial, the topic is called badprog_topic_counter.
Each time a message is published, the program triggers a special function called a callback.
So every subscriber can listen to this topic by subscribing to the badprog_topic_counter topic.
We have also 2 subscribers in our tutorial (yes they are the same as the publishers):
The role of a subscriber is to read what happens on a topic in order to listen to data published on this topic.
And each time a data is broadcasted on this topic (by the publisher), all subscribers receive this data.
The topic has to have a strict type.
For example, you can’t send an Int32 on a topic set as a Bool.
It won’t work (or it may not the result expected).
Your message has to be the exact same type as the topic.
In order to communicate, devices need to understand each other.
In ROS 2, the main protocol used is DDS (Data Distribution Service).
This protocol requires a significant amount of RAM, which our STM32 board lacks.
So we need to find a solution to allow our microcontroller to communicate with other nodes on the ROS 2 network.
Here comes the XRCE (eXtremely Resource Constrained Environments), a ROS 2 extension made expressly for the microcontroller world.
Instead of using the complex ROS 2 network we delegate this task to a proxy, the micro-ROS Agent.
The STM32 can then send short messages with the XRCE protocol to the network.
Then the Agent transforms those XRCE messages into the DDS protocol in order to be fully understood by others nodes on the ROS 2 network.
To resume simply :
The micro-ROS Agent
In order to launch your micro-ROS Agent, use the following Docker command to install your container on your host:
docker run -it --rm --privileged --net=host -v /dev:/dev microros/micro-ros-agent:humble serial --dev /dev/ttyACM0 -v6
In this tutorial we use the HAL (Hardware Abstraction Layer) in order to manipulate our LEDs on the STM32F3Discovery board.
The handles are blocking.
It means that if a callback called by the executor lasts 50 ms, the next callback to be called won’t be called during these 50 ms.
There is no software interruption during the process of a callback.
The callbacks are indeed executed sequentially.
Only the hardware can interrupt the program.
So, each time a callback is executed, the loop() function is blocked.
A new loop cycle is started only at the end of each callback.
The Cortex M4, from the STM32F3, is single-tasked.
It means that it can handle only one task at a time.
So the code inside each callback must be as short as possible.
For the Python code we have a class inheriting from Node, a class of the RCL (ROS 2 Client Library).
Inside this constructor class we set the publisher and the subscriber in order to send and receive data to and from the STM32 program.
Every 500 ms, a timer triggers a function to send a new boolean value to the STM32.
Independently, whenever a message is received from the STM32 on the badprog_topic_counter topic, the executor triggers the listener_callback() to process that data.
This callback is triggered by the rclpy.spin() function which detects every message on topics and dispatch events as needed.
Let’s have a look at what happens for every kind of message.
First, we have to launch the micro-ROS Agent with a Docker image directly from our host.
So we should see something like this with of course a lot of other logs displayed by the debugger:
Then a lot of logs like:
The first element is displayed when the micro-ROS Agent sends data and the second when it receives data.
So it’s the opposite for the STM32 microcontroller.
The SER stands for Serial and it’s the XRCE protocol (discussed above in the tutorial) between the micro-ROS Agent and the microcontroller.
Then we have the following elements:
The DDS is when the micro-ROS Agent writes or reads information directly on or from the ROS 2 topics.
From your Dev Container (the container from ROS 2 humble image) you can see the counter increments by typing this command:
ros2 topic echo /badprog_topic_counter
You’ll see:
data: 0 --- data: 1 --- data: 2 --- ...
But you can also execute the Python script (from this Dev Container as well):
python3 python_node_pc/scripts/node_pc.py
And see:
[INFO [xxx] [badprog_node_pc]: The LED is turned: ON [INFO] [xxx] [badprog_node_pc]: The LED is turned: OFF [INFO] [xxx] [badprog_node_pc]: ---> Received from STM32: Counter = 0 [INFO] [xxx] [badprog_node_pc]: The LED is turned: ON [INFO] [xxx] [badprog_node_pc]: The LED is turned: OFF [INFO] [xxx] [badprog_node_pc]: ---> Received from STM32: Counter = 1 [INFO] [xxx] [badprog_node_pc]: The LED is turned: ON [INFO] [xxx] [badprog_node_pc]: The LED is turned: OFF [INFO] [xxx] [badprog_node_pc]: ---> Received from STM32: Counter = 2 ...
If this is happening, then good job, you did it! ![]()
Add new comment