Skip to content

Commit 2ac6cbd

Browse files
committed
Added FreeRTOS tutorial
Fix indentation Fix links Olimex + FreeRTOS tutorial Adding check procedure Updated plantUML Update code Minor fixes Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: FranFin <58737168+FranFin@users.noreply.github.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Update _docs/tutorials/basic/getting_started_olimex_freertos/index.md Co-Authored-By: Julián Bermúdez Ortega <julianbermudez@eprosima.com> Minor fixes Update structure
1 parent b0e67c8 commit 2ac6cbd

7 files changed

Lines changed: 386 additions & 1 deletion

File tree

_data/docs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,5 @@
7070
- title: Advanced Tutorials with Zephyr
7171
docs:
7272
- tutorials/advanced/zephyr/zephyr_getting_started
73+
7374

600 KB
Loading
3.66 MB
Loading
106 KB
Loading
82.2 KB
Loading
671 KB
Loading

_docs/tutorials/advanced/freertos/freertos_getting_started/index.md

Lines changed: 385 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,388 @@ title: FreeRTOS Getting Started
33
permalink: /docs/tutorials/advanced/freertos/freertos_getting_started/
44
---
55

6-
WIP
6+
This tutorial aims to create a new micro-ROS application on **[Olimex STM32-E407](https://www.olimex.com/Products/ARM/ST/STM32-E407/open-source-hardware)** evaluation board with **[FreeRTOS RTOS](https://www.freertos.org/)**
7+
8+
<div>
9+
<img width="400" style="padding-right: 25px;" src="imgs/3.jpg">
10+
11+
<img width="300" style="padding-right: 25px;" src="imgs/4.png">
12+
</div>
13+
14+
## Required hardware
15+
16+
This tutorial uses the following hardware:
17+
18+
| Item | |
19+
|---------------|----------------------------------------------------------|
20+
| Olimex STM32-E407 | [Link](https://www.olimex.com/Products/ARM/ST/STM32-E407/open-source-hardware) |
21+
| Olimex ARM-USB-TINY-H | [Link](https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/) |
22+
| USB-Serial Cable Female | [Link](https://www.olimex.com/Products/Components/Cables/USB-Serial-Cable/USB-Serial-Cable-F/) |
23+
24+
25+
## Adding a new micro-ROS app
26+
27+
First of all make sure that you have a **ROS 2** installation.
28+
29+
***TIP:** if you are familiar with Docker containers, this image may be useful: [ros:dashing](https://hub.docker.com/layers/ros/library/ros/dashing/images/sha256-b796c14ea663537129897769aa6c715a851ca08dffd4875ef2ecaa31a4dbd431?context=explore)*
30+
31+
On the **ROS 2** installation open a command line and follow these steps:
32+
33+
```bash
34+
# Source the ROS 2 installation
35+
source /opt/ros/$ROS_DISTRO/setup.bash
36+
37+
# Create a workspace and download the micro-ROS tools
38+
mkdir microros_ws
39+
cd microros_ws
40+
git clone -b $ROS_DISTRO https://github.com/micro-ROS/micro-ros-build.git src/micro-ros-build
41+
42+
# Update dependencies using rosdep
43+
rosdep update
44+
rosdep install --from-path src --ignore-src -y
45+
46+
# Build micro-ROS tools and source them
47+
colcon build
48+
source install/local_setup.bash
49+
```
50+
51+
52+
Now, let's create a firmware workspace that targets all the required code and tools for Olimex development board and FreeRTOS:
53+
54+
```bash
55+
# Create step
56+
ros2 run micro_ros_setup create_firmware_ws.sh freertos olimex-stm32-e407
57+
```
58+
59+
Now you have all the required tools to crosscompile micro-ROS and FreeRTOS for Olimex STM32-E407 development board. At this point, you must know that the micro-ROS build system is a four-step workflow:
60+
61+
<!-- TODO (pablogs9): Remove and link to build-system tutorial when done -->
62+
1. **Create**: retrieves all the required packages for a specific RTOS and hardware platform.
63+
2. **Configure**: configures the downloaded packages with options such as the micro-ROS application, the selected transport layer or the micro-ROS agent IP address (in network transports).
64+
3. **Build**: generates a binary file ready for being loaded in the hardware.
65+
4. **Flash**: load the micro-ROS software in the hardware.
66+
67+
micro-ROS apps for Olimex + FreeRTOS are located at `firmware/freertos_apps/apps`. In order to create a new application, create a new folder containing two files: the app code and the RMW configuration.
68+
69+
```bash
70+
# Creating a new app
71+
cd firmware/freertos_apps/apps
72+
mkdir my_brand_new_app
73+
cd my_brand_new_app
74+
touch app.c app-colcon.meta
75+
```
76+
77+
For this example we are going to create a ping pong app where a node sends a ping package with a unique identifier using a publisher and the same package is received by a pong subscriber. The node will also answer to:
78+
79+
![pingpong](http://www.plantuml.com/plantuml/png/ZOwnIWGn48RxFCNFzSkoUG2vqce5jHEHi1dtWZkPa6GByNntavZY10yknMJu-ORlFwPiOjvvK-d3-M2YOR1uMKvHc93ZJafvoMML07d7h1NAE-DPWblg_na8vnwEx9OeZmzFOt1-BK7AzetJciPxCfRYVw1S0SbRLBEg1IpXPIvpUWLCmZpXIm6BS3addt7uQpu0ZQlxT1MK2r0g-7sfqbsbRrVfMrMwgbev3CDTlsqJGtJhATUmSMrMg5TKwaZUxfcttuMt7m00)
80+
81+
To start creating this app, lets configure the RMW with the required static memory. You can read more about RMW and Micro XRCE-DDS Configuration [here](/docs/tutorials/basic/microxrcedds_rmw_configuration/) The `app-colcon.meta` should look like:
82+
83+
```
84+
{
85+
"names": {
86+
"rmw_microxrcedds": {
87+
"cmake-args": [
88+
"-DRMW_UXRCE_MAX_NODES=1",
89+
"-DRMW_UXRCE_MAX_PUBLISHERS=2",
90+
"-DRMW_UXRCE_MAX_SUBSCRIPTIONS=2",
91+
"-DRMW_UXRCE_MAX_SERVICES=0",
92+
"-DRMW_UXRCE_MAX_CLIENTS=0",
93+
"-DRMW_UXRCE_MAX_HISTORY=4",
94+
]
95+
}
96+
}
97+
}
98+
```
99+
100+
Meanwhile `app.c` should look like the following code:
101+
102+
```c
103+
#include <allocators.h>
104+
105+
#include <rcl/rcl.h>
106+
#include <rcl_action/rcl_action.h>
107+
#include <rcl/error_handling.h>
108+
#include "rosidl_generator_c/string_functions.h"
109+
#include <std_msgs/msg/header.h>
110+
111+
#include <rmw_uros/options.h>
112+
113+
#include <stdio.h>
114+
#include <unistd.h>
115+
#include <pthread.h>
116+
117+
#define STRING_BUFFER_LEN 100
118+
119+
// FreeRTOS thread for triggering a publication guard condition
120+
void * trigger_guard_condition(void *args){
121+
rcl_guard_condition_t * guard_condition = (rcl_guard_condition_t *)args;
122+
123+
while(true){
124+
rcl_trigger_guard_condition(guard_condition);
125+
sleep(5);
126+
}
127+
}
128+
129+
// App main function
130+
void appMain(void *argument)
131+
{
132+
//Init RCL options
133+
rcl_init_options_t options = rcl_get_zero_initialized_init_options();
134+
rcl_init_options_init(&options, rcl_get_default_allocator());
135+
136+
// Init RCL context
137+
rcl_context_t context = rcl_get_zero_initialized_context();
138+
rcl_init(0, NULL, &options, &context);
139+
140+
// Create a node
141+
rcl_node_options_t node_ops = rcl_node_get_default_options();
142+
rcl_node_t node = rcl_get_zero_initialized_node();
143+
rcl_node_init(&node, "pingpong_node", "", &context, &node_ops);
144+
145+
// Create a reliable ping publisher
146+
rcl_publisher_options_t ping_publisher_ops = rcl_publisher_get_default_options();
147+
rcl_publisher_t ping_publisher = rcl_get_zero_initialized_publisher();
148+
rcl_publisher_init(&ping_publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/ping", &ping_publisher_ops);
149+
150+
// Create a best effort pong publisher
151+
rcl_publisher_options_t pong_publisher_ops = rcl_publisher_get_default_options();
152+
pong_publisher_ops.qos.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
153+
rcl_publisher_t pong_publisher = rcl_get_zero_initialized_publisher();
154+
rcl_publisher_init(&pong_publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/pong", &pong_publisher_ops);
155+
156+
// Create a reliable pong subscriber
157+
rcl_subscription_options_t pong_subscription_ops = rcl_subscription_get_default_options();
158+
rcl_subscription_t pong_subscription = rcl_get_zero_initialized_subscription();
159+
rcl_subscription_init(&pong_subscription, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/pong", &pong_subscription_ops);
160+
161+
// Create a best effort ping subscriber
162+
rcl_subscription_options_t ping_subscription_ops = rcl_subscription_get_default_options();
163+
ping_subscription_ops.qos.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
164+
rcl_subscription_t ping_subscription = rcl_get_zero_initialized_subscription();
165+
rcl_subscription_init(&ping_subscription, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/ping", &ping_subscription_ops);
166+
167+
// Create a guard condition
168+
rcl_guard_condition_t guard_condition = rcl_get_zero_initialized_guard_condition();
169+
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
170+
rcl_guard_condition_init(&guard_condition, &context, guard_condition_options);
171+
172+
// Create a thread that triggers the guard condition
173+
pthread_t guard_condition_thread;
174+
pthread_create(&guard_condition_thread, NULL, trigger_guard_condition, &guard_condition);
175+
176+
// Create a wait set
177+
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
178+
rcl_wait_set_init(&wait_set, 2, 1, 0, 0, 0, 0, &context, rcl_get_default_allocator());
179+
180+
// Create and allocate the pingpong publication message
181+
std_msgs__msg__Header msg;
182+
char msg_buffer[STRING_BUFFER_LEN];
183+
msg.frame_id.data = msg_buffer;
184+
msg.frame_id.capacity = STRING_BUFFER_LEN;
185+
186+
// Create and allocate the pingpong subscription message
187+
std_msgs__msg__Header rcv_msg;
188+
char rcv_buffer[STRING_BUFFER_LEN];
189+
rcv_msg.frame_id.data = rcv_buffer;
190+
rcv_msg.frame_id.capacity = STRING_BUFFER_LEN;
191+
192+
// Set device id and sequence number;
193+
int device_id = rand();
194+
int seq_no;
195+
196+
int pong_count = 0;
197+
struct timespec ts;
198+
rcl_ret_t rc;
199+
200+
do {
201+
// Clear and set the waitset
202+
rcl_wait_set_clear(&wait_set);
203+
204+
size_t index_pong_subscription;
205+
rcl_wait_set_add_subscription(&wait_set, &pong_subscription, &index_pong_subscription);
206+
207+
size_t index_ping_subscription;
208+
rcl_wait_set_add_subscription(&wait_set, &ping_subscription, &index_ping_subscription);
209+
210+
size_t index_guardcondition;
211+
rcl_wait_set_add_guard_condition(&wait_set, &guard_condition, &index_guardcondition);
212+
213+
// Run session for 100 ms
214+
rcl_wait(&wait_set, RCL_MS_TO_NS(100));
215+
216+
// Check if it is time to send a ping
217+
if (wait_set.guard_conditions[index_guardcondition]) {
218+
// Generate a new random sequence number
219+
seq_no = rand();
220+
sprintf(msg.frame_id.data, "%d_%d", seq_no, device_id);
221+
msg.frame_id.size = strlen(msg.frame_id.data);
222+
223+
// Fill the message timestamp
224+
clock_gettime(CLOCK_REALTIME, &ts);
225+
msg.stamp.sec = ts.tv_sec;
226+
msg.stamp.nanosec = ts.tv_nsec;
227+
228+
// Reset the pong count and publish the ping message
229+
pong_count = 0;
230+
rcl_publish(&ping_publisher, (const void*)&msg, NULL);
231+
// printf("Ping send seq 0x%x\n", seq_no);
232+
}
233+
234+
// Check if some pong message is received
235+
if (wait_set.subscriptions[index_pong_subscription]) {
236+
rc = rcl_take(wait_set.subscriptions[index_pong_subscription], &rcv_msg, NULL, NULL);
237+
238+
if(rc == RCL_RET_OK && strcmp(msg.frame_id.data,rcv_msg.frame_id.data) == 0) {
239+
pong_count++;
240+
// printf("Pong for seq 0x%x (%d)\n", seq_no, pong_count);
241+
}
242+
}
243+
244+
// Check if some ping message is received and pong it
245+
if (wait_set.subscriptions[index_ping_subscription]) {
246+
rc = rcl_take(wait_set.subscriptions[index_ping_subscription], &rcv_msg, NULL, NULL);
247+
248+
// Dont pong my own pings
249+
if(rc == RCL_RET_OK && strcmp(msg.frame_id.data,rcv_msg.frame_id.data) != 0){
250+
// printf("Ping received with seq 0x%x (%d). Answering.\n", seq_no);
251+
rcl_publish(&pong_publisher, (const void*)&rcv_msg, NULL);
252+
}
253+
}
254+
255+
usleep(10000);
256+
} while (true);
257+
}
258+
```
259+
260+
Once the new folder is created, let's configure our new app with a serial transport on UART 3:
261+
262+
```bash
263+
# Configure step
264+
ros2 run micro_ros_setup configure_firmware.sh my_brand_new_app --transport serial --dev 3
265+
```
266+
267+
When the configuring step ends, just build the firmware:
268+
269+
```bash
270+
# Build step
271+
ros2 run micro_ros_setup build_firmware.sh
272+
```
273+
274+
Once the build has successfully ended, let's power and connect the board. First, connect Olimex ARM-USB-TINY-H JTAG programmer to the board's JTAG port:
275+
276+
<img width="400" style="padding-right: 25px;" src="imgs/2.jpg">
277+
278+
Make sure that the board power supply jumper (PWR_SEL) is in the 3-4 position in order to power the board from the JTAG connector:
279+
280+
<img width="400" style="padding-right: 25px;" src="imgs/1.jpg">
281+
282+
You should see the red LED lighting. It is time to flash the board:
283+
284+
```bash
285+
# Flash step
286+
ros2 run micro_ros_setup flash_firmware.sh
287+
```
288+
## Running the micro-ROS app
289+
290+
The micro-ROS app is ready to connect to a micro-ROS-Agent and start talking with the rest of the ROS 2 world.
291+
292+
First of all, create and build a micro-ROS agent:
293+
294+
```bash
295+
# Download micro-ROS-Agent packages
296+
ros2 run micro_ros_setup create_agent_ws.sh
297+
298+
# Build micro-ROS-Agent packages, this may take a while.
299+
colcon build
300+
```
301+
302+
Then connect the Olimex development board to the computer using the usb to serial cable:
303+
304+
<img width="400" style="padding-right: 25px;" src="imgs/5.jpg">
305+
306+
***TIP:** Color codes are applicable to [this cable](https://www.olimex.com/Products/Components/Cables/USB-Serial-Cable/USB-Serial-Cable-F/). Make sure to match Olimex Rx with Cable Tx and vice-versa. Remember GND!*
307+
308+
Then run the agent:
309+
310+
```bash
311+
# Run a micro-ROS agent
312+
ros2 run micro_ros_agent micro_ros_agent serial --dev [device]
313+
```
314+
315+
***TIP:** you can use this command to find your serial device name: `ls /dev/serial/by-id/*`*
316+
317+
And finally, let's check that everything is working. We are going to listen to ping topic to check whether the Ping Pong node is publishing its own pings
318+
319+
```bash
320+
# Subscribe to micro-ROS ping topic
321+
ros2 topic echo /microROS/ping
322+
```
323+
324+
You should see the topic messages published by the Ping Pong node every 5 seconds:
325+
326+
```
327+
pgarrido@pgarrido:~$ ros2 topic echo /microROS/ping
328+
stamp:
329+
sec: 20
330+
nanosec: 867000000
331+
frame_id: '1344887256_1085377743'
332+
---
333+
stamp:
334+
sec: 25
335+
nanosec: 942000000
336+
frame_id: '730417256_1085377743'
337+
---
338+
```
339+
340+
On another command line, let's subscribe to the pong topic
341+
342+
```bash
343+
# Subscribe to micro-ROS pong topic
344+
ros2 topic echo /microROS/pong
345+
```
346+
347+
At this point, we know that our app is publishing pings. Let's check if it also answers to someone else pings:
348+
349+
```bash
350+
# Send a fake ping
351+
ros2 topic pub --once /microROS/ping std_msgs/msg/Header '{frame_id: "fake_ping"}'
352+
```
353+
354+
Now, we should see on the ping subscriber our fake ping along with the board pings:
355+
356+
```
357+
pgarrido@pgarrido:~$ ros2 topic echo /microROS/ping
358+
stamp:
359+
sec: 0
360+
nanosec: 0
361+
frame_id: fake_ping
362+
---
363+
stamp:
364+
sec: 305
365+
nanosec: 973000000
366+
frame_id: '451230256_1085377743'
367+
---
368+
stamp:
369+
sec: 310
370+
nanosec: 957000000
371+
frame_id: '2084670932_1085377743'
372+
---
373+
```
374+
375+
And in the pong subscriber, we should see the board's answer to our fake ping:
376+
377+
```
378+
pgarrido@pgarrido:~$ ros2 topic echo /microROS/pong
379+
stamp:
380+
sec: 0
381+
nanosec: 0
382+
frame_id: fake_ping
383+
---
384+
```
385+
386+
387+
<!-- http://www.plantuml.com/plantuml/uml/bOwnIWGn48RxFCNFzSkoUG2vqce5jHEHi1dtWZkPa6GByNntavZY40yqnMJu-ORlFwPiOjvvK-dD-M2YOR1uMKvHc93ZJafvoMML07d5h1NAE-DHWblg_nu8vnwEx9Oem_tTmnYSNqkIidtXjARnbeob-2ifLqZo5jMjQWLBU9hBd9u1ap1F-5308bpEoMSSVhWFG0FhFbqdvOAKIdvUodINwN_8z9zbkifQFmp3JJzk4qDqwodNi75jLgYNLEf8tkwPj--5joy0 -->
388+
389+
390+
<!-- [![demo](https://asciinema.org/a/113463.svg)](https://asciinema.org/a/113463?autoplay=1) -->

0 commit comments

Comments
 (0)