diff --git a/_docs/tutorials/advanced/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr_emulator/index.md index 47158d46..c73bcf05 100644 --- a/_docs/tutorials/advanced/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr_emulator/index.md @@ -3,7 +3,10 @@ title: Zephyr Emulator permalink: /docs/tutorials/advanced/zephyr_emulator/ --- -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)). +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)). + +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. +Another requirement is that the user has a basic knowledge of micro-ROS and ROS 2.
@@ -13,244 +16,39 @@ This tutorial aims to create a new micro-ROS application on with **[Zephyr RTOS] This tutorial requires no hardware beyond a Linux host computer. -## Adding a new micro-ROS app - -First of all, make sure that you have a **ROS 2** installation. - -***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)* - -On the **ROS 2** installation open a command line and follow these steps: - -```bash -# Source the ROS 2 installation -source /opt/ros/$ROS_DISTRO/setup.bash - -# Create a workspace and download the micro-ROS tools -mkdir microros_ws -cd microros_ws -git clone -b $ROS_DISTRO https://github.com/micro-ROS/micro-ros-build.git src/micro-ros-build - -# Update dependencies using rosdep -sudo apt update && sudo apt install python3-colcon-metadata -rosdep update -rosdep install --from-path src --ignore-src -y - -# Build micro-ROS tools and source them -colcon build -source install/local_setup.bash -``` +## Building a Zephyr emulator application -Let's install the last version of CMake: - -```bash -sudo apt install wget -wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add - -sudo apt install software-properties-common -sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main' -sudo apt update -sudo apt install cmake -``` - -Now, let's create a firmware workspace that targets all the required code and tools for Zephyr emulator: +Once the micro-ROS build system is ready, let's create a new Zephyr firmware for the host platform: ```bash # Create firmware step ros2 run micro_ros_setup create_firmware_ws.sh zephyr host ``` -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: - - -1. **Create**: retrieves all the required packages for a specific RTOS and hardware platform. -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). -3. **Build**: generates a binary file ready for being loaded in the hardware. -4. **Flash**: load the micro-ROS software in the hardware. - -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. +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. ```bash # Creating a new app pushd firmware/zephyr_apps/apps -mkdir my_brand_new_app -cd my_brand_new_app +mkdir host_ping_pong +cd host_ping_pong mkdir src -touch src/app.c app-colcon.meta -# Copying CMakeLists.txt and prj.conf -wget https://raw.githubusercontent.com/micro-ROS/zephyr_apps/dashing/apps/host_ping_pong/CMakeLists.txt -wget https://raw.githubusercontent.com/micro-ROS/zephyr_apps/dashing/apps/host_ping_pong/prj.conf +touch src/app.c +touch app-colcon.meta +touch CMakeLists.txt +touch prj.conf popd ``` -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: - -![pingpong](http://www.plantuml.com/plantuml/png/ZOwnIWGn48RxFCNFzSkoUG2vqce5jHEHi1dtWZkPa6GByNntavZY10yknMJu-ORlFwPiOjvvK-d3-M2YOR1uMKvHc93ZJafvoMML07d7h1NAE-DPWblg_na8vnwEx9OeZmzFOt1-BK7AzetJciPxCfRYVw1S0SbRLBEg1IpXPIvpUWLCmZpXIm6BS3addt7uQpu0ZQlxT1MK2r0g-7sfqbsbRrVfMrMwgbev3CDTlsqJGtJhATUmSMrMg5TKwaZUxfcttuMt7m00) - -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: - -``` -{ - "names": { - "rmw_microxrcedds": { - "cmake-args": [ - "-DRMW_UXRCE_MAX_NODES=1", - "-DRMW_UXRCE_MAX_PUBLISHERS=2", - "-DRMW_UXRCE_MAX_SUBSCRIPTIONS=2", - "-DRMW_UXRCE_MAX_SERVICES=0", - "-DRMW_UXRCE_MAX_CLIENTS=0", - "-DRMW_UXRCE_MAX_HISTORY=4", - ] - } - } -} -``` - -Meanwhile `src/app.c` should look like the following code: - -```c -#include -#include -#include -#include "rosidl_generator_c/string_functions.h" -#include - -#include - -#include -#include -#include - -#include - -#define STRING_BUFFER_LEN 100 - -// App main function -void main(void) -{ - //Init RCL options - rcl_init_options_t options = rcl_get_zero_initialized_init_options(); - rcl_init_options_init(&options, rcl_get_default_allocator()); - - // Init RCL context - rcl_context_t context = rcl_get_zero_initialized_context(); - rcl_init(0, NULL, &options, &context); - - // Create a node - rcl_node_options_t node_ops = rcl_node_get_default_options(); - rcl_node_t node = rcl_get_zero_initialized_node(); - rcl_node_init(&node, "pingpong_node", "", &context, &node_ops); - - // Create a reliable ping publisher - rcl_publisher_options_t ping_publisher_ops = rcl_publisher_get_default_options(); - rcl_publisher_t ping_publisher = rcl_get_zero_initialized_publisher(); - rcl_publisher_init(&ping_publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/ping", &ping_publisher_ops); - - // Create a best effort pong publisher - rcl_publisher_options_t pong_publisher_ops = rcl_publisher_get_default_options(); - pong_publisher_ops.qos.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; - rcl_publisher_t pong_publisher = rcl_get_zero_initialized_publisher(); - rcl_publisher_init(&pong_publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/pong", &pong_publisher_ops); - - // Create a best effort pong subscriber - rcl_subscription_options_t pong_subscription_ops = rcl_subscription_get_default_options(); - pong_subscription_ops.qos.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; - rcl_subscription_t pong_subscription = rcl_get_zero_initialized_subscription(); - rcl_subscription_init(&pong_subscription, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/pong", &pong_subscription_ops); - - // Create a best effort ping subscriber - rcl_subscription_options_t ping_subscription_ops = rcl_subscription_get_default_options(); - ping_subscription_ops.qos.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; - rcl_subscription_t ping_subscription = rcl_get_zero_initialized_subscription(); - rcl_subscription_init(&ping_subscription, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/ping", &ping_subscription_ops); - - // Create a wait set - rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set(); - rcl_wait_set_init(&wait_set, 2, 0, 0, 0, 0, 0, &context, rcl_get_default_allocator()); - - // Create and allocate the pingpong publication message - std_msgs__msg__Header msg; - char msg_buffer[STRING_BUFFER_LEN]; - msg.frame_id.data = msg_buffer; - msg.frame_id.capacity = STRING_BUFFER_LEN; - - // Create and allocate the pingpong subscription message - std_msgs__msg__Header rcv_msg; - char rcv_buffer[STRING_BUFFER_LEN]; - rcv_msg.frame_id.data = rcv_buffer; - rcv_msg.frame_id.capacity = STRING_BUFFER_LEN; - - // Set device id and sequence number; - int device_id = rand(); - int seq_no; - - int pong_count = 0; - struct timespec ts; - rcl_ret_t rc; - - uint32_t iterations = 0; - - do { - // Clear and set the waitset - rcl_wait_set_clear(&wait_set); - - size_t index_pong_subscription; - rcl_wait_set_add_subscription(&wait_set, &pong_subscription, &index_pong_subscription); - - size_t index_ping_subscription; - rcl_wait_set_add_subscription(&wait_set, &ping_subscription, &index_ping_subscription); - - // Run session for 100 ms - rcl_wait(&wait_set, RCL_MS_TO_NS(100)); - - // Check if some pong message is received - if (wait_set.subscriptions[index_pong_subscription]) { - rc = rcl_take(wait_set.subscriptions[index_pong_subscription], &rcv_msg, NULL, NULL); - if(rc == RCL_RET_OK && strcmp(msg.frame_id.data,rcv_msg.frame_id.data) == 0) { - pong_count++; - printf("Pong for seq %s (%d)\n", rcv_msg.frame_id.data, pong_count); - } - } - - // Check if some ping message is received and pong it - if (wait_set.subscriptions[index_ping_subscription]) { - rc = rcl_take(wait_set.subscriptions[index_ping_subscription], &rcv_msg, NULL, NULL); - - // Dont pong my own pings - if(rc == RCL_RET_OK && strcmp(msg.frame_id.data,rcv_msg.frame_id.data) != 0){ - printf("Ping received with seq %s. Answering.\n", rcv_msg.frame_id.data); - rcl_publish(&pong_publisher, (const void*)&rcv_msg, NULL); - } - } - - // Check if it is time to send a ping - if (iterations++ % 50 == 0) { - // Generate a new random sequence number - seq_no = rand(); - sprintf(msg.frame_id.data, "%d_%d", seq_no, device_id); - msg.frame_id.size = strlen(msg.frame_id.data); - - // Fill the message timestamp - clock_gettime(CLOCK_REALTIME, &ts); - msg.stamp.sec = ts.tv_sec; - msg.stamp.nanosec = ts.tv_nsec; - - // Reset the pong count and publish the ping message - pong_count = 0; - rcl_publish(&ping_publisher, (const void*)&msg, NULL); - printf("Ping send seq %s\n", msg.frame_id.data); - } - - usleep(10000); - } while (true); -} -``` +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). -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: +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: ```bash # Configure step -ros2 run micro_ros_setup configure_firmware.sh my_brand_new_app --transport udp --ip 127.0.0.1 --port 8888 +ros2 run micro_ros_setup configure_firmware.sh host_ping_pong --transport udp --ip 127.0.0.1 --port 8888 ``` When the configuring step ends, just build the firmware: