Skip to content

Commit cabd5ea

Browse files
pablogs9FranFin
andauthored
Ref #8285. Updates on Zephyr emulator (#154)
* Initial Zephyr emulator rework * Fix * Update _docs/tutorials/advanced/zephyr_emulator/index.md Co-authored-by: FranFin <58737168+FranFin@users.noreply.github.com> * Update _docs/tutorials/advanced/zephyr_emulator/index.md Co-authored-by: FranFin <58737168+FranFin@users.noreply.github.com> * Update _docs/tutorials/advanced/zephyr_emulator/index.md Co-authored-by: FranFin <58737168+FranFin@users.noreply.github.com> * Updates * Update links Co-authored-by: FranFin <58737168+FranFin@users.noreply.github.com>
1 parent 2d3446e commit cabd5ea

1 file changed

Lines changed: 16 additions & 218 deletions

File tree

  • _docs/tutorials/advanced/zephyr_emulator

_docs/tutorials/advanced/zephyr_emulator/index.md

Lines changed: 16 additions & 218 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ title: Zephyr Emulator
33
permalink: /docs/tutorials/advanced/zephyr_emulator/
44
---
55

6-
This tutorial aims to create a new micro-ROS application on with **[Zephyr RTOS](https://www.zephyrproject.org/)** emulator (also known as [Native POSIX](https://docs.zephyrproject.org/latest/boards/posix/native_posix/doc/index.html)).
6+
This tutorial aims at creating a new micro-ROS application on with **[Zephyr RTOS](https://www.zephyrproject.org/)** emulator (also known as [Native POSIX](https://docs.zephyrproject.org/latest/boards/posix/native_posix/doc/index.html)).
7+
8+
To follow this tutorial, it is assumed that the user is already familiar with the **[First micro-ROS Application on an RTOS](https://micro-ros.github.io/docs/tutorials/core/first_application_rtos/)** tutorial. The target app in this tutorial is the same ping pong app.
9+
Another requirement is that the user has a basic knowledge of micro-ROS and ROS 2.
710

811
<div>
912
<img width="300" style="padding-right: 25px;" src="imgs/4.jpg">
@@ -13,244 +16,39 @@ This tutorial aims to create a new micro-ROS application on with **[Zephyr RTOS]
1316

1417
This tutorial requires no hardware beyond a Linux host computer.
1518

16-
## Adding a new micro-ROS app
17-
18-
First of all, make sure that you have a **ROS 2** installation.
19-
20-
***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)*
21-
22-
On the **ROS 2** installation open a command line and follow these steps:
23-
24-
```bash
25-
# Source the ROS 2 installation
26-
source /opt/ros/$ROS_DISTRO/setup.bash
27-
28-
# Create a workspace and download the micro-ROS tools
29-
mkdir microros_ws
30-
cd microros_ws
31-
git clone -b $ROS_DISTRO https://github.com/micro-ROS/micro-ros-build.git src/micro-ros-build
32-
33-
# Update dependencies using rosdep
34-
sudo apt update && sudo apt install python3-colcon-metadata
35-
rosdep update
36-
rosdep install --from-path src --ignore-src -y
37-
38-
# Build micro-ROS tools and source them
39-
colcon build
40-
source install/local_setup.bash
41-
```
19+
## Building a Zephyr emulator application
4220

43-
Let's install the last version of CMake:
44-
45-
```bash
46-
sudo apt install wget
47-
wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add -
48-
sudo apt install software-properties-common
49-
sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main'
50-
sudo apt update
51-
sudo apt install cmake
52-
```
53-
54-
Now, let's create a firmware workspace that targets all the required code and tools for Zephyr emulator:
21+
Once the micro-ROS build system is ready, let's create a new Zephyr firmware for the host platform:
5522

5623
```bash
5724
# Create firmware step
5825
ros2 run micro_ros_setup create_firmware_ws.sh zephyr host
5926
```
6027

61-
Now you have all the required tools to compile micro-ROS and Zephyr. At this point, you must know that the micro-ROS build system is a four-step workflow:
62-
63-
<!-- TODO (pablogs9): Remove and link to build-system tutorial when done -->
64-
1. **Create**: retrieves all the required packages for a specific RTOS and hardware platform.
65-
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).
66-
3. **Build**: generates a binary file ready for being loaded in the hardware.
67-
4. **Flash**: load the micro-ROS software in the hardware.
68-
69-
micro-ROS apps for Zephyr emulator are located at `firmware/zephyr_apps/apps`. In order to create a new application, create a new folder containing two files: the app code (inside a `src` folder) and the RMW configuration. You will also need some other Zephyr related files: a `CMakeLists.txt` to define the building process and a `prj.conf` where Zephyr is configured. You have these two files [here](https://github.com/micro-ROS/zephyr_apps/tree/dashing/apps/host_ping_pong), for now, it is ok to copy them.
28+
micro-ROS apps for Zephyr emulator are located at `firmware/zephyr_apps/apps`. In order to create a new application, create a new folder containing two files: the app code (inside a `src` folder) and the RMW configuration. You will also need some other Zephyr related files: a `CMakeLists.txt` to define the building process and a `prj.conf` where Zephyr is configured. There is a sample proyect [here](https://github.com/micro-ROS/zephyr_apps/tree/dashing/apps/host_ping_pong), for now, it is ok to copy them.
7029

7130
```bash
7231
# Creating a new app
7332
pushd firmware/zephyr_apps/apps
74-
mkdir my_brand_new_app
75-
cd my_brand_new_app
33+
mkdir host_ping_pong
34+
cd host_ping_pong
7635
mkdir src
77-
touch src/app.c app-colcon.meta
7836

79-
# Copying CMakeLists.txt and prj.conf
80-
wget https://raw.githubusercontent.com/micro-ROS/zephyr_apps/dashing/apps/host_ping_pong/CMakeLists.txt
81-
wget https://raw.githubusercontent.com/micro-ROS/zephyr_apps/dashing/apps/host_ping_pong/prj.conf
37+
touch src/app.c
38+
touch app-colcon.meta
39+
touch CMakeLists.txt
40+
touch prj.conf
8241

8342
popd
8443
```
8544

86-
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 pings received from other nodes with a pong message:
87-
88-
![pingpong](http://www.plantuml.com/plantuml/png/ZOwnIWGn48RxFCNFzSkoUG2vqce5jHEHi1dtWZkPa6GByNntavZY10yknMJu-ORlFwPiOjvvK-d3-M2YOR1uMKvHc93ZJafvoMML07d7h1NAE-DPWblg_na8vnwEx9OeZmzFOt1-BK7AzetJciPxCfRYVw1S0SbRLBEg1IpXPIvpUWLCmZpXIm6BS3addt7uQpu0ZQlxT1MK2r0g-7sfqbsbRrVfMrMwgbev3CDTlsqJGtJhATUmSMrMg5TKwaZUxfcttuMt7m00)
89-
90-
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/core/microxrcedds_rmw_configuration/). The `app-colcon.meta` should look like:
91-
92-
```
93-
{
94-
"names": {
95-
"rmw_microxrcedds": {
96-
"cmake-args": [
97-
"-DRMW_UXRCE_MAX_NODES=1",
98-
"-DRMW_UXRCE_MAX_PUBLISHERS=2",
99-
"-DRMW_UXRCE_MAX_SUBSCRIPTIONS=2",
100-
"-DRMW_UXRCE_MAX_SERVICES=0",
101-
"-DRMW_UXRCE_MAX_CLIENTS=0",
102-
"-DRMW_UXRCE_MAX_HISTORY=4",
103-
]
104-
}
105-
}
106-
}
107-
```
108-
109-
Meanwhile `src/app.c` should look like the following code:
110-
111-
```c
112-
#include <rcl/rcl.h>
113-
#include <rcl_action/rcl_action.h>
114-
#include <rcl/error_handling.h>
115-
#include "rosidl_generator_c/string_functions.h"
116-
#include <std_msgs/msg/header.h>
117-
118-
#include <rmw_uros/options.h>
119-
120-
#include <stdio.h>
121-
#include <unistd.h>
122-
#include <time.h>
123-
124-
#include <zephyr.h>
125-
126-
#define STRING_BUFFER_LEN 100
127-
128-
// App main function
129-
void main(void)
130-
{
131-
//Init RCL options
132-
rcl_init_options_t options = rcl_get_zero_initialized_init_options();
133-
rcl_init_options_init(&options, rcl_get_default_allocator());
134-
135-
// Init RCL context
136-
rcl_context_t context = rcl_get_zero_initialized_context();
137-
rcl_init(0, NULL, &options, &context);
138-
139-
// Create a node
140-
rcl_node_options_t node_ops = rcl_node_get_default_options();
141-
rcl_node_t node = rcl_get_zero_initialized_node();
142-
rcl_node_init(&node, "pingpong_node", "", &context, &node_ops);
143-
144-
// Create a reliable ping publisher
145-
rcl_publisher_options_t ping_publisher_ops = rcl_publisher_get_default_options();
146-
rcl_publisher_t ping_publisher = rcl_get_zero_initialized_publisher();
147-
rcl_publisher_init(&ping_publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/ping", &ping_publisher_ops);
148-
149-
// Create a best effort pong publisher
150-
rcl_publisher_options_t pong_publisher_ops = rcl_publisher_get_default_options();
151-
pong_publisher_ops.qos.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
152-
rcl_publisher_t pong_publisher = rcl_get_zero_initialized_publisher();
153-
rcl_publisher_init(&pong_publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/pong", &pong_publisher_ops);
154-
155-
// Create a best effort pong subscriber
156-
rcl_subscription_options_t pong_subscription_ops = rcl_subscription_get_default_options();
157-
pong_subscription_ops.qos.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
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 wait set
168-
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
169-
rcl_wait_set_init(&wait_set, 2, 0, 0, 0, 0, 0, &context, rcl_get_default_allocator());
170-
171-
// Create and allocate the pingpong publication message
172-
std_msgs__msg__Header msg;
173-
char msg_buffer[STRING_BUFFER_LEN];
174-
msg.frame_id.data = msg_buffer;
175-
msg.frame_id.capacity = STRING_BUFFER_LEN;
176-
177-
// Create and allocate the pingpong subscription message
178-
std_msgs__msg__Header rcv_msg;
179-
char rcv_buffer[STRING_BUFFER_LEN];
180-
rcv_msg.frame_id.data = rcv_buffer;
181-
rcv_msg.frame_id.capacity = STRING_BUFFER_LEN;
182-
183-
// Set device id and sequence number;
184-
int device_id = rand();
185-
int seq_no;
186-
187-
int pong_count = 0;
188-
struct timespec ts;
189-
rcl_ret_t rc;
190-
191-
uint32_t iterations = 0;
192-
193-
do {
194-
// Clear and set the waitset
195-
rcl_wait_set_clear(&wait_set);
196-
197-
size_t index_pong_subscription;
198-
rcl_wait_set_add_subscription(&wait_set, &pong_subscription, &index_pong_subscription);
199-
200-
size_t index_ping_subscription;
201-
rcl_wait_set_add_subscription(&wait_set, &ping_subscription, &index_ping_subscription);
202-
203-
// Run session for 100 ms
204-
rcl_wait(&wait_set, RCL_MS_TO_NS(100));
205-
206-
// Check if some pong message is received
207-
if (wait_set.subscriptions[index_pong_subscription]) {
208-
rc = rcl_take(wait_set.subscriptions[index_pong_subscription], &rcv_msg, NULL, NULL);
209-
if(rc == RCL_RET_OK && strcmp(msg.frame_id.data,rcv_msg.frame_id.data) == 0) {
210-
pong_count++;
211-
printf("Pong for seq %s (%d)\n", rcv_msg.frame_id.data, pong_count);
212-
}
213-
}
214-
215-
// Check if some ping message is received and pong it
216-
if (wait_set.subscriptions[index_ping_subscription]) {
217-
rc = rcl_take(wait_set.subscriptions[index_ping_subscription], &rcv_msg, NULL, NULL);
218-
219-
// Dont pong my own pings
220-
if(rc == RCL_RET_OK && strcmp(msg.frame_id.data,rcv_msg.frame_id.data) != 0){
221-
printf("Ping received with seq %s. Answering.\n", rcv_msg.frame_id.data);
222-
rcl_publish(&pong_publisher, (const void*)&rcv_msg, NULL);
223-
}
224-
}
225-
226-
// Check if it is time to send a ping
227-
if (iterations++ % 50 == 0) {
228-
// Generate a new random sequence number
229-
seq_no = rand();
230-
sprintf(msg.frame_id.data, "%d_%d", seq_no, device_id);
231-
msg.frame_id.size = strlen(msg.frame_id.data);
232-
233-
// Fill the message timestamp
234-
clock_gettime(CLOCK_REALTIME, &ts);
235-
msg.stamp.sec = ts.tv_sec;
236-
msg.stamp.nanosec = ts.tv_nsec;
237-
238-
// Reset the pong count and publish the ping message
239-
pong_count = 0;
240-
rcl_publish(&ping_publisher, (const void*)&msg, NULL);
241-
printf("Ping send seq %s\n", msg.frame_id.data);
242-
}
243-
244-
usleep(10000);
245-
} while (true);
246-
}
247-
```
45+
The contents of the files can be found here: [app.c](https://github.com/micro-ROS/zephyr_apps/blob/dashing/apps/host_ping_pong/src/main.c), [app-colcon.meta](https://github.com/micro-ROS/zephyr_apps/blob/dashing/apps/host_ping_pong/app-colcon.meta), [CMakeLists.txt](https://github.com/micro-ROS/zephyr_apps/blob/dashing/apps/host_ping_pong/CMakeLists.txt) and [prj.conf](https://github.com/micro-ROS/zephyr_apps/blob/dashing/apps/host_ping_pong/prj.conf).
24846

249-
Once the new folder is created, let's configure our new app with a UDP transport that looks for the agent on the port UDP/8888 at localhost:
47+
Once the app folder is created, let's configure our new app with a UDP transport that looks for the agent on the port UDP/8888 at localhost:
25048

25149
```bash
25250
# Configure step
253-
ros2 run micro_ros_setup configure_firmware.sh my_brand_new_app --transport udp --ip 127.0.0.1 --port 8888
51+
ros2 run micro_ros_setup configure_firmware.sh host_ping_pong --transport udp --ip 127.0.0.1 --port 8888
25452
```
25553

25654
When the configuring step ends, just build the firmware:

0 commit comments

Comments
 (0)