From 28a5b702044bee55c51f97cab10d4fde96dfc98a Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Mon, 23 Mar 2020 10:01:02 +0100 Subject: [PATCH 01/15] Added Zephyr emulator section --- _data/docs.yml | 1 + .../advanced/freertos/freertos_getting_started/index.md | 3 ++- _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md | 6 ++++++ _docs/tutorials/core/programming_rcl_rclc/index.md | 2 -- 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md diff --git a/_data/docs.yml b/_data/docs.yml index dcf31168..71716ac8 100644 --- a/_data/docs.yml +++ b/_data/docs.yml @@ -69,6 +69,7 @@ - title: Advanced Tutorials with Zephyr docs: - tutorials/advanced/zephyr/zephyr_getting_started + - tutorials/advanced/zephyr/zephyr_emulator - title: Advanced Tutorials with Linux diff --git a/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md b/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md index 89ebd40f..40537c4f 100644 --- a/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md +++ b/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md @@ -153,8 +153,9 @@ void appMain(void *argument) 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 reliable pong subscriber + // 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); diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md new file mode 100644 index 00000000..8b080b70 --- /dev/null +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -0,0 +1,6 @@ +--- +title: Zephyr Emulator +permalink: /docs/tutorials/advanced/zephyr/zephyr_emulator/ +--- + +WIP \ No newline at end of file diff --git a/_docs/tutorials/core/programming_rcl_rclc/index.md b/_docs/tutorials/core/programming_rcl_rclc/index.md index 01bbd8f4..8a83e683 100644 --- a/_docs/tutorials/core/programming_rcl_rclc/index.md +++ b/_docs/tutorials/core/programming_rcl_rclc/index.md @@ -1,8 +1,6 @@ --- title: Programming with rcl and rclc permalink: /docs/tutorials/core/programming_rcl_rclc/ -redirect_from: /docs/tutorials/core/ -redirect_from: /docs/tutorials/ --- ## Programming client/service with rcl From 0f6b8ac7ae36e56e72cbec2bb8300030dcacc16e Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Mon, 23 Mar 2020 10:01:57 +0100 Subject: [PATCH 02/15] Copy content --- .../advanced/zephyr/zephyr_emulator/index.md | 362 +++++++++++++++++- 1 file changed, 361 insertions(+), 1 deletion(-) diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md index 8b080b70..2f9633fa 100644 --- a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -3,4 +3,364 @@ title: Zephyr Emulator permalink: /docs/tutorials/advanced/zephyr/zephyr_emulator/ --- -WIP \ No newline at end of file + +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 **[Zephyr RTOS](https://www.zephyrproject.org/)** + +
+ + + +
+ +## Required hardware + +This tutorial uses the following hardware: + +| Item | | +|---------------|----------------------------------------------------------| +| Olimex STM32-E407 | [Link](https://www.olimex.com/Products/ARM/ST/STM32-E407/open-source-hardware) | +| Olimex ARM-USB-TINY-H | [Link](https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/) | +| USB-Serial Cable Female | [Link](https://www.olimex.com/Products/Components/Cables/USB-Serial-Cable/USB-Serial-Cable-F/) | + + +## 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 +rosdep update +rosdep install --from-path src --ignore-src -y + +# Build micro-ROS tools and source them +colcon build +source install/local_setup.bash +``` + + +Now, let's create a firmware workspace that targets all the required code and tools for Olimex development board and Zephyr: + +```bash +# Create step +ros2 run micro_ros_setup create_firmware_ws.sh zephyr olimex-stm32-e407 +``` + +Now you have all the required tools to crosscompile micro-ROS and Zephyr for Olimex STM32-E407 development board. 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 Olimex + Zephyr 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. + +```bash +# Creating a new app +cd firmware/zephyr_apps/apps +mkdir my_brand_new_app +cd my_brand_new_app +mkdir src +touch src/app.c app-colcon.meta +``` + +You will also need some other Zephyr related files: a `CMakeLists.txt` in order 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/ping_pong), for now it is ok to copy them. + +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: + +![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 + +#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 efforts 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 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); + } + + // 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); + } + } + + usleep(10000); + } while (true); +} +``` + +Once the new folder is created, let's configure our new app with a serial transport on the USB: + +```bash +# Configure step +ros2 run micro_ros_setup configure_firmware.sh my_brand_new_app --transport serial-usb +``` + +When the configuring step ends, just build the firmware: + +```bash +# Build step +ros2 run micro_ros_setup build_firmware.sh +``` + +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: + + + +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: + + + +You should see the red LED lighting. It is time to flash the board: + +```bash +# Flash step +ros2 run micro_ros_setup flash_firmware.sh +``` +## Running the micro-ROS app + +The micro-ROS app is ready to connect to a micro-ROS-Agent and start talking with the rest of the ROS 2 world. + +First of all, create and build a micro-ROS agent: + +```bash +# Download micro-ROS-Agent packages +ros2 run micro_ros_setup create_agent_ws.sh + +# Build micro-ROS-Agent packages, this may take a while. +colcon build +``` + +Then connect the Olimex development board to the computer using the USB OTG 2 connector (the miniUSB connector that is furthest from the Ethernet port). + +***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!* + +Then run the agent: + +```bash +# Run a micro-ROS agent +ros2 run micro_ros_agent micro_ros_agent serial --dev [device] +``` + +***TIP:** you can use this command to find your serial device name: `ls /dev/serial/by-id/*`. Probably it will be something like `/dev/serial/by-id/usb-ZEPHYR_Zephyr_microROS_3536510100290035-if00`* + +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 + +```bash +# Subscribe to micro-ROS ping topic +ros2 topic echo /microROS/ping +``` + +You should see the topic messages published by the Ping Pong node every 5 seconds: + +``` +pgarrido@pgarrido:~$ ros2 topic echo /microROS/ping +stamp: + sec: 20 + nanosec: 867000000 +frame_id: '1344887256_1085377743' +--- +stamp: + sec: 25 + nanosec: 942000000 +frame_id: '730417256_1085377743' +--- +``` + +On another command line, let's subscribe to the pong topic + +```bash +# Subscribe to micro-ROS pong topic +ros2 topic echo /microROS/pong +``` + +At this point, we know that our app is publishing pings. Let's check if it also answers to someone else pings: + +```bash +# Send a fake ping +ros2 topic pub --once /microROS/ping std_msgs/msg/Header '{frame_id: "fake_ping"}' +``` + +Now, we should see on the ping subscriber our fake ping along with the board pings: + +``` +pgarrido@pgarrido:~$ ros2 topic echo /microROS/ping +stamp: + sec: 0 + nanosec: 0 +frame_id: fake_ping +--- +stamp: + sec: 305 + nanosec: 973000000 +frame_id: '451230256_1085377743' +--- +stamp: + sec: 310 + nanosec: 957000000 +frame_id: '2084670932_1085377743' +--- +``` + +And in the pong subscriber, we should see the board's answer to our fake ping: + +``` +pgarrido@pgarrido:~$ ros2 topic echo /microROS/pong +stamp: + sec: 0 + nanosec: 0 +frame_id: fake_ping +--- +``` From 7fd7264a9cb41204ceb603c8db817afe43240a51 Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Mon, 23 Mar 2020 10:23:27 +0100 Subject: [PATCH 03/15] Update tutorial --- .../advanced/zephyr/zephyr_emulator/index.md | 127 ++++++++++-------- 1 file changed, 71 insertions(+), 56 deletions(-) diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md index 2f9633fa..ea545e79 100644 --- a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -3,8 +3,7 @@ title: Zephyr Emulator permalink: /docs/tutorials/advanced/zephyr/zephyr_emulator/ --- - -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 **[Zephyr RTOS](https://www.zephyrproject.org/)** +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)).
@@ -14,14 +13,7 @@ This tutorial aims to create a new micro-ROS application on **[Olimex STM32-E407 ## Required hardware -This tutorial uses the following hardware: - -| Item | | -|---------------|----------------------------------------------------------| -| Olimex STM32-E407 | [Link](https://www.olimex.com/Products/ARM/ST/STM32-E407/open-source-hardware) | -| Olimex ARM-USB-TINY-H | [Link](https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/) | -| USB-Serial Cable Female | [Link](https://www.olimex.com/Products/Components/Cables/USB-Serial-Cable/USB-Serial-Cable-F/) | - +This tutorial requires no hardware beyond a Linux host computer. ## Adding a new micro-ROS app @@ -50,14 +42,14 @@ source install/local_setup.bash ``` -Now, let's create a firmware workspace that targets all the required code and tools for Olimex development board and Zephyr: +Now, let's create a firmware workspace that targets all the required code and tools for Zephyr emulator: ```bash # Create step -ros2 run micro_ros_setup create_firmware_ws.sh zephyr olimex-stm32-e407 +ros2 run micro_ros_setup create_firmware_ws.sh zephyr host ``` -Now you have all the required tools to crosscompile micro-ROS and Zephyr for Olimex STM32-E407 development board. At this point, you must know that the micro-ROS build system is a four-step workflow: +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. @@ -65,7 +57,7 @@ Now you have all the required tools to crosscompile micro-ROS and Zephyr for Oli 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 Olimex + Zephyr 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. +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. ```bash # Creating a new app @@ -76,7 +68,7 @@ mkdir src touch src/app.c app-colcon.meta ``` -You will also need some other Zephyr related files: a `CMakeLists.txt` in order 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/ping_pong), for now it is ok to copy them. +You will also need some other Zephyr related files: a `CMakeLists.txt` in order 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. 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: @@ -114,6 +106,7 @@ Meanwhile `src/app.c` should look like the following code: #include #include +#include #include @@ -146,7 +139,7 @@ void main(void) 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 efforts pong subscriber + // 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(); @@ -196,29 +189,10 @@ void main(void) // Run session for 100 ms rcl_wait(&wait_set, RCL_MS_TO_NS(100)); - - // 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); - } // 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); @@ -235,17 +209,35 @@ void main(void) 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); } ``` -Once the new folder is created, let's configure our new app with a serial transport on the USB: +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: ```bash # Configure step -ros2 run micro_ros_setup configure_firmware.sh my_brand_new_app --transport serial-usb +ros2 run micro_ros_setup configure_firmware.sh my_brand_new_app --transport udp --ip 127.0.0.1 --port 8888 ``` When the configuring step ends, just build the firmware: @@ -255,20 +247,8 @@ When the configuring step ends, just build the firmware: ros2 run micro_ros_setup build_firmware.sh ``` -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: - - +Now you have a Zephyr + micro-ROS app ready to run on your own computer. -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: - - - -You should see the red LED lighting. It is time to flash the board: - -```bash -# Flash step -ros2 run micro_ros_setup flash_firmware.sh -``` ## Running the micro-ROS app The micro-ROS app is ready to connect to a micro-ROS-Agent and start talking with the rest of the ROS 2 world. @@ -283,18 +263,19 @@ ros2 run micro_ros_setup create_agent_ws.sh colcon build ``` -Then connect the Olimex development board to the computer using the USB OTG 2 connector (the miniUSB connector that is furthest from the Ethernet port). - -***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!* - Then run the agent: ```bash # Run a micro-ROS agent -ros2 run micro_ros_agent micro_ros_agent serial --dev [device] +ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888 ``` -***TIP:** you can use this command to find your serial device name: `ls /dev/serial/by-id/*`. Probably it will be something like `/dev/serial/by-id/usb-ZEPHYR_Zephyr_microROS_3536510100290035-if00`* +And run the Zephyr app: + +```bash +# Flash/run step +ros2 run micro_ros_setup flash_firmware.sh +``` 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 @@ -364,3 +345,37 @@ stamp: frame_id: fake_ping --- ``` + +## Multiple Ping Pong nodes + +One of the advantages of having an emulator is that you don't need to buy a bunch of hardware in order to test some multi-node micro-ROS apps. So, with the same micro-ROS agent of the last section, let's open four different command lines and run thw following on each: + +```bash +cd microros_ws + +# This is an alternative way of executing the Zephyr emulator +./firmware/build/zephyr/zephyr.exe +``` + +As soon as all micro-ROS node are up and connected to the micro-ROS agent you will see them interacting: + +``` +pgarrido@pgarrido$ ./firmware/build/zephyr/zephyr.exe +*** Booting Zephyr OS build zephyr-v2.2.0-492-gc73cb85b4ae9 *** +UDP mode => ip: 127.0.0.1 - port: 8888 +Ping send seq 1711620172_1742614911 <---- This micro-ROS node sends a ping with ping ID "1711620172" and node ID "1742614911" +Pong for seq 1711620172_1742614911 (1) <---- The first mate pongs my ping +Pong for seq 1711620172_1742614911 (2) <---- The second mate pongs my ping +Pong for seq 1711620172_1742614911 (3) <---- The third mate pongs my ping +Ping received with seq 1845948271_546591567. Answering. <---- A ping is received from a mate identified as "546591567", let's pong it. +Ping received with seq 232977719_1681483056. Answering. <---- A ping is received from a mate identified as "1681483056", let's pong it. +Ping received with seq 1134264528_1107823050. Answering. <---- A ping is received from a mate identified as "1107823050", let's pong it. +Ping send seq 324239260_1742614911 +Pong for seq 324239260_1742614911 (1) +Pong for seq 324239260_1742614911 (2) +Pong for seq 324239260_1742614911 (3) +Ping received with seq 1435780593_546591567. Answering. +Ping received with seq 2034268578_1681483056. Answering. +``` + +***TIP:** use the help flag to discover some Zephyr emulation features `./firmware/build/zephyr/zephyr.exe -h`* \ No newline at end of file From cf1c45b02e8318b061dd4dae4facf76ee1649514 Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Mon, 23 Mar 2020 10:31:07 +0100 Subject: [PATCH 04/15] Added image --- .../advanced/zephyr/zephyr_emulator/imgs/4.jpg | Bin 0 -> 77092 bytes .../advanced/zephyr/zephyr_emulator/index.md | 2 -- 2 files changed, 2 deletions(-) create mode 100644 _docs/tutorials/advanced/zephyr/zephyr_emulator/imgs/4.jpg diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/imgs/4.jpg b/_docs/tutorials/advanced/zephyr/zephyr_emulator/imgs/4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6f3ad64332c2b9bfd7602d9867560de1de17b9be GIT binary patch literal 77092 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 - -
From 37e81728896cfe0ce93d5366ca72bc48aa6e30f1 Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Mon, 23 Mar 2020 11:29:34 +0100 Subject: [PATCH 05/15] Fix link --- _docs/concepts/rtos/comparison/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_docs/concepts/rtos/comparison/index.md b/_docs/concepts/rtos/comparison/index.md index a639495e..e8827773 100644 --- a/_docs/concepts/rtos/comparison/index.md +++ b/_docs/concepts/rtos/comparison/index.md @@ -46,7 +46,7 @@ Table: | | | | | | **Supported Hardware** | | | | | Olimex STM32-E407 (Cortex-M4) | yes | yes | yes, [explicitly](https://docs.zephyrproject.org/latest/reference/kernel/scheduling/index.html) | -| Bosch XDK 5 | not explicitly, but similar 6 | yes | yes, [explicitly](https://github.com/zephyrproject-rtos/zephyr/blob/master/ext/hal/README) | +| Bosch XDK 5 | not explicitly, but similar 6 | yes | yes | | MPC57xx | no | no | no | | **Scheduling** | | | | | Priority-based | FIFO | yes | yes | From 07093d0223f3f73cdf3a0afd498431f797b2ea53 Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Mon, 23 Mar 2020 11:31:05 +0100 Subject: [PATCH 06/15] FIx link --- _docs/concepts/rtos/comparison/index.md | 106 ++++++++++++------------ 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/_docs/concepts/rtos/comparison/index.md b/_docs/concepts/rtos/comparison/index.md index e8827773..70ef766a 100644 --- a/_docs/concepts/rtos/comparison/index.md +++ b/_docs/concepts/rtos/comparison/index.md @@ -28,59 +28,59 @@ Key questions: Table: -| **OS** | [NuttX](http://nuttx.org/) | [FreeRTOS](https://sourceforge.net/projects/freertos/) | [Zephyr](https://www.zephyrproject.org/) | -|--------------------------------------------------------------|---------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------| -| **Feature** | | | | -| **Standardization** | | | | -| POSIX | yes | partial | partial | -| POSIX.1 1 | [yes](http://nuttx.org/) | [wrapper](https://interactive.freertos.org/hc/en-us/community/posts/210029046-POSIX-Wrapper-for-FreeRTOS) | partial | -| POSIX.1b 2 | yes | partial | partial | -| POSIX.1c 3 | yes | yes | partial | -| | | | | -| OSEK/VDX | no | no | no | -| **Maturity** | | | | -| First release | 2007 | 2014 | 2016 | -| Last release | 2019 | 2019 | 2019 | -| Update rate | about 3 months | irregular | 3 months | -| Community | open-source | open-source | Linux Foundation Collaboration Project, (Intel, Linaro (ARM), nordic, NXP, Synopsys) | -| | | | | -| **Supported Hardware** | | | | -| Olimex STM32-E407 (Cortex-M4) | yes | yes | yes, [explicitly](https://docs.zephyrproject.org/latest/reference/kernel/scheduling/index.html) | -| Bosch XDK 5 | not explicitly, but similar 6 | yes | yes | -| MPC57xx | no | no | no | -| **Scheduling** | | | | -| Priority-based | FIFO | yes | yes | -| Round-Robin 4 | yes | yes 6 | [co-operative](https://docs.zephyrproject.org/latest/reference/kernel/scheduling/index.html) | -| Sporadic Server | yes | no | no | -| RBS | no | ? | no | -| Semaphore /Mutex Management | yes (Priority Inheritance) | yes | yes | -| **IO** | | | | -| I2C | yes | vendor-specific | yes | -| SPI | yes | vendor-specific | yes | -| UART | hw-specific | vendor-specific | yes | -| USB | yes | vendor-specific | yes | -| CAN | yes | vendor-specific | yes | -| CAnopen | no | vendor-specific | yes | -| Modbus | yes | vendor-specific | ? | -| **Networking** 7 | | | | -| BLE-Stack | unclear | no | yes | -| 6LoWPAN | yes | no | yes | -| TLS | | yes | yes | -| Thread | | ? | ? | -| Ethernet | yes | no | yes | -| Wifi | yes | no | yes | -| NFC | unclear | no | yes | -| RFID | yes | no | yes | -| **Storage & Display** 7 | | | | -| File System | yes | ? | yes | -| Graphical User Interface | | ? | ? | -| **Memory Footprint** | | | | -| RAM | "small footprint" | 236 B scheduler + 64 B / task | "small footprint" | -| ROM | "small footprint" | 5 - 10 kB | "small footprint" | -| **Safety Certification** | | | | -| Software Development Process DO178B Level A / EUROCAE ED-12B | no | [SafeRTOS: DO178C (Aerspace) by Wittenstein](https://www.highintegritysystems.com/safertos/certification-and-standards/) | no | -| Functional Safety IEC-61508 | no | [SafeRTOS (SIL 3)](https://www.freertos.org/FreeRTOS-Plus/Safety_Critical_Certified/SafeRTOS.shtml) | soon | -| **License** | BSD | MIT and Commercial | Apache 2 | +| **OS** | [NuttX](http://nuttx.org/) | [FreeRTOS](https://sourceforge.net/projects/freertos/) | [Zephyr](https://www.zephyrproject.org/) | +| ------------------------------------------------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | +| **Feature** | | | | +| **Standardization** | | | | +| POSIX | yes | partial | partial | +| POSIX.1 1 | [yes](http://nuttx.org/) | [wrapper](https://interactive.freertos.org/hc/en-us/community/posts/210029046-POSIX-Wrapper-for-FreeRTOS) | partial | +| POSIX.1b 2 | yes | partial | partial | +| POSIX.1c 3 | yes | yes | partial | +| | | | | +| OSEK/VDX | no | no | no | +| **Maturity** | | | ~~~~ | +| First release | 2007 | 2014 | 2016 | +| Last release | 2019 | 2019 | 2019 | +| Update rate | about 3 months | irregular | 3 months | +| Community | open-source | open-source | Linux Foundation Collaboration Project, (Intel, Linaro (ARM), nordic, NXP, Synopsys) | +| | | | | +| **Supported Hardware** | | | | +| Olimex STM32-E407 (Cortex-M4) | yes | yes | yes, [explicitly](https://docs.zephyrproject.org/latest/reference/kernel/scheduling/index.html) | +| Bosch XDK 5 | not explicitly, but similar 6 | yes | yes | +| MPC57xx | no | no | no | +| **Scheduling** | | | | +| Priority-based | FIFO | yes | yes | +| Round-Robin 4 | yes | yes 6 | [co-operative](https://docs.zephyrproject.org/latest/reference/kernel/scheduling/index.html) | +| Sporadic Server | yes | no | no | +| RBS | no | ? | no | +| Semaphore /Mutex Management | yes (Priority Inheritance) | yes | yes | +| **IO** | | | | +| I2C | yes | vendor-specific | yes | +| SPI | yes | vendor-specific | yes | +| UART | hw-specific | vendor-specific | yes | +| USB | yes | vendor-specific | yes | +| CAN | yes | vendor-specific | yes | +| CAnopen | no | vendor-specific | yes | +| Modbus | yes | vendor-specific | ? | +| **Networking** 7 | | | | +| BLE-Stack | unclear | no | yes | +| 6LoWPAN | yes | no | yes | +| TLS | | yes | yes | +| Thread | | ? | ? | +| Ethernet | yes | no | yes | +| Wifi | yes | no | yes | +| NFC | unclear | no | yes | +| RFID | yes | no | yes | +| **Storage & Display** 7 | | | | +| File System | yes | ? | yes | +| Graphical User Interface | | ? | ? | +| **Memory Footprint** | | | | +| RAM | "small footprint" | 236 B scheduler + 64 B / task | "small footprint" | +| ROM | "small footprint" | 5 - 10 kB | "small footprint" | +| **Safety Certification** | | | | +| Software Development Process DO178B Level A / EUROCAE ED-12B | no | [SafeRTOS: DO178C (Aerspace) by Wittenstein](https://www.highintegritysystems.com/safertos/certification-and-standards/) | no | +| Functional Safety IEC-61508 | no | [SafeRTOS (SIL 3)](https://www.freertos.org/FreeRTOS-Plus/Safety_Critical_Certified/SafeRTOS.shtml) | soon | +| **License** | BSD | MIT and Commercial | Apache 2 | 1 Processes, signals, fpe, segmentation, bus errors, timers, file and directory ops, pipes, c library, IO Port Interface From db7b4ec64dd90fccac229f916c964998ddef65bb Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Tue, 24 Mar 2020 11:38:52 +0100 Subject: [PATCH 07/15] Update agent install --- .../advanced/freertos/freertos_getting_started/index.md | 3 ++- _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md b/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md index 40537c4f..56032d70 100644 --- a/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md +++ b/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md @@ -297,7 +297,8 @@ First of all, create and build a micro-ROS agent: ros2 run micro_ros_setup create_agent_ws.sh # Build micro-ROS-Agent packages, this may take a while. -colcon build +colcon build --metas src +source install/local_setup.bash ``` Then connect the Olimex development board to the computer using the usb to serial cable: diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md index af62cd2c..148786a2 100644 --- a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -258,7 +258,8 @@ First of all, create and build a micro-ROS agent: ros2 run micro_ros_setup create_agent_ws.sh # Build micro-ROS-Agent packages, this may take a while. -colcon build +colcon build --metas src +source install/local_setup.bash ``` Then run the agent: From b8099b7b310f42a4b61a8c9f40fdd3e420d881da Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Tue, 24 Mar 2020 11:53:56 +0100 Subject: [PATCH 08/15] Update folder tree --- .../advanced/freertos/freertos_getting_started/index.md | 1 + _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md | 1 + 2 files changed, 2 insertions(+) diff --git a/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md b/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md index 56032d70..9d5bb8ca 100644 --- a/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md +++ b/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md @@ -72,6 +72,7 @@ cd firmware/freertos_apps/apps mkdir my_brand_new_app cd my_brand_new_app touch app.c app-colcon.meta +cd ../../../ ``` 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: diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md index 148786a2..210bad5d 100644 --- a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -64,6 +64,7 @@ mkdir my_brand_new_app cd my_brand_new_app mkdir src touch src/app.c app-colcon.meta +cd ../../../ ``` You will also need some other Zephyr related files: a `CMakeLists.txt` in order 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. From fbbc2604b13c19593c58a1a1fe316f2cac712805 Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Tue, 24 Mar 2020 13:14:03 +0100 Subject: [PATCH 09/15] Update tree --- _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md index 210bad5d..69d1e8c5 100644 --- a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -59,12 +59,12 @@ micro-ROS apps for Zephyr emulator are located at `firmware/zephyr_apps/apps`. I ```bash # Creating a new app -cd firmware/zephyr_apps/apps +pushd firmware/zephyr_apps/apps mkdir my_brand_new_app cd my_brand_new_app mkdir src touch src/app.c app-colcon.meta -cd ../../../ +popd ``` You will also need some other Zephyr related files: a `CMakeLists.txt` in order 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. From 4d076fe91c691917cf64a25e306fea2aad16a630 Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Tue, 24 Mar 2020 15:39:29 +0100 Subject: [PATCH 10/15] Minor fixes --- .../advanced/zephyr/zephyr_emulator/index.md | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md index 69d1e8c5..0edea0bb 100644 --- a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -39,11 +39,20 @@ colcon build source install/local_setup.bash ``` +Let's install last version of CMake: + +```bash +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 -y +``` Now, let's create a firmware workspace that targets all the required code and tools for Zephyr emulator: ```bash -# Create step +# Create firmware step ros2 run micro_ros_setup create_firmware_ws.sh zephyr host ``` @@ -55,7 +64,7 @@ Now you have all the required tools to compile micro-ROS and Zephyr. At this poi 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. +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. ```bash # Creating a new app @@ -64,12 +73,15 @@ mkdir my_brand_new_app cd my_brand_new_app 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 + popd ``` -You will also need some other Zephyr related files: a `CMakeLists.txt` in order 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. - -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: +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) From 8ea001f9f2d1506a8197a5d1e5b260720ef30a68 Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Tue, 24 Mar 2020 15:52:12 +0100 Subject: [PATCH 11/15] Update --- _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md index 0edea0bb..a8c16a81 100644 --- a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -282,9 +282,12 @@ Then run the agent: ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888 ``` -And run the Zephyr app: +And run the Zephyr app in another command line (remember sourcing ROS 2 and micro-ROS installation): ```bash +source /opt/ros/$ROS_DISTRO/setup.bash +source install/local_setup.bash + # Flash/run step ros2 run micro_ros_setup flash_firmware.sh ``` From 68bcf9ab5959fe77fc26dae34921aae21f4044c5 Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Tue, 24 Mar 2020 15:58:22 +0100 Subject: [PATCH 12/15] Update --- .../tutorials/advanced/zephyr/zephyr_emulator/index.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md index a8c16a81..7c4bc1ac 100644 --- a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -292,9 +292,11 @@ source install/local_setup.bash ros2 run micro_ros_setup flash_firmware.sh ``` -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 +And finally, let's check that everything is working in another command line. We are going to listen to ping topic to check whether the Ping Pong node is publishing its own pings ```bash +source /opt/ros/$ROS_DISTRO/setup.bash + # Subscribe to micro-ROS ping topic ros2 topic echo /microROS/ping ``` @@ -318,13 +320,17 @@ frame_id: '730417256_1085377743' On another command line, let's subscribe to the pong topic ```bash +source /opt/ros/$ROS_DISTRO/setup.bash + # Subscribe to micro-ROS pong topic ros2 topic echo /microROS/pong ``` -At this point, we know that our app is publishing pings. Let's check if it also answers to someone else pings: +At this point, we know that our app is publishing pings. Let's check if it also answers to someone else pings in a new command line: ```bash +source /opt/ros/$ROS_DISTRO/setup.bash + # Send a fake ping ros2 topic pub --once /microROS/ping std_msgs/msg/Header '{frame_id: "fake_ping"}' ``` From 41f75d8768e7b8a460c224abd2b1d10eb7b2e674 Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Tue, 24 Mar 2020 16:06:16 +0100 Subject: [PATCH 13/15] Update --- _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md index 7c4bc1ac..321cf810 100644 --- a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -292,7 +292,7 @@ source install/local_setup.bash ros2 run micro_ros_setup flash_firmware.sh ``` -And finally, let's check that everything is working in another command line. We are going to listen to ping topic to check whether the Ping Pong node is publishing its own pings +And finally, let's check that everything is working in another command line. We are going to listen to ping topic to check whether the Ping Pong node is publishing its own pings: ```bash source /opt/ros/$ROS_DISTRO/setup.bash From ccdf77e8b52415f85837c358e05c52d65a70f419 Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Fri, 27 Mar 2020 08:45:29 +0100 Subject: [PATCH 14/15] Minor fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Julián Bermúdez Ortega Update _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md Co-Authored-By: Julián Bermúdez Ortega Update _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md Co-Authored-By: Julián Bermúdez Ortega Update _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md Co-Authored-By: Julián Bermúdez Ortega Update _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md Co-Authored-By: Julián Bermúdez Ortega Update _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md Co-Authored-By: Julián Bermúdez Ortega Update _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md Co-Authored-By: Julián Bermúdez Ortega Update _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md Co-Authored-By: Julián Bermúdez Ortega Update _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md Co-Authored-By: Julián Bermúdez Ortega Update _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md Co-Authored-By: Julián Bermúdez Ortega Update pushd popd --- .../freertos_getting_started/index.md | 4 ++-- .../advanced/zephyr/zephyr_emulator/index.md | 20 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md b/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md index 9d5bb8ca..13f0ecf7 100644 --- a/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md +++ b/_docs/tutorials/advanced/freertos/freertos_getting_started/index.md @@ -68,11 +68,11 @@ micro-ROS apps for Olimex + FreeRTOS are located at `firmware/freertos_apps/apps ```bash # Creating a new app -cd firmware/freertos_apps/apps +pushd firmware/freertos_apps/apps mkdir my_brand_new_app cd my_brand_new_app touch app.c app-colcon.meta -cd ../../../ +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: diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md index 321cf810..b3195466 100644 --- a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -15,7 +15,7 @@ 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. +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)* @@ -31,6 +31,7 @@ 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 +apt update && apt install python3-colcon-metadata rosdep update rosdep install --from-path src --ignore-src -y @@ -39,14 +40,15 @@ colcon build source install/local_setup.bash ``` -Let's install last version of CMake: +Let's install the last version of CMake: ```bash +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 -y +sudo apt install cmake ``` Now, let's create a firmware workspace that targets all the required code and tools for Zephyr emulator: @@ -271,7 +273,7 @@ First of all, create and build a micro-ROS agent: ros2 run micro_ros_setup create_agent_ws.sh # Build micro-ROS-Agent packages, this may take a while. -colcon build --metas src +colcon build source install/local_setup.bash ``` @@ -286,7 +288,7 @@ And run the Zephyr app in another command line (remember sourcing ROS 2 and micr ```bash source /opt/ros/$ROS_DISTRO/setup.bash -source install/local_setup.bash +source microros_ws/install/local_setup.bash # Flash/run step ros2 run micro_ros_setup flash_firmware.sh @@ -304,7 +306,7 @@ ros2 topic echo /microROS/ping You should see the topic messages published by the Ping Pong node every 5 seconds: ``` -pgarrido@pgarrido:~$ ros2 topic echo /microROS/ping +user@user:~$ ros2 topic echo /microROS/ping stamp: sec: 20 nanosec: 867000000 @@ -338,7 +340,7 @@ ros2 topic pub --once /microROS/ping std_msgs/msg/Header '{frame_id: "fake_ping" Now, we should see on the ping subscriber our fake ping along with the board pings: ``` -pgarrido@pgarrido:~$ ros2 topic echo /microROS/ping +user@user:~$ ros2 topic echo /microROS/ping stamp: sec: 0 nanosec: 0 @@ -369,7 +371,7 @@ frame_id: fake_ping ## Multiple Ping Pong nodes -One of the advantages of having an emulator is that you don't need to buy a bunch of hardware in order to test some multi-node micro-ROS apps. So, with the same micro-ROS agent of the last section, let's open four different command lines and run thw following on each: +One of the advantages of having an emulator is that you don't need to buy a bunch of hardware in order to test some multi-node micro-ROS apps. So, with the same micro-ROS agent of the last section, let's open four different command lines and run the following on each: ```bash cd microros_ws @@ -399,4 +401,4 @@ Ping received with seq 1435780593_546591567. Answering. Ping received with seq 2034268578_1681483056. Answering. ``` -***TIP:** use the help flag to discover some Zephyr emulation features `./firmware/build/zephyr/zephyr.exe -h`* \ No newline at end of file +***TIP:** use the help flag to discover some Zephyr emulation features `./firmware/build/zephyr/zephyr.exe -h`* From cde514940461b79bc475438565f757e11c69933b Mon Sep 17 00:00:00 2001 From: Pablo Garrido Date: Fri, 27 Mar 2020 10:50:26 +0100 Subject: [PATCH 15/15] Update sudos --- _docs/tutorials/advanced/zephyr/zephyr_emulator/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md index b3195466..c9d6cb6f 100644 --- a/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md +++ b/_docs/tutorials/advanced/zephyr/zephyr_emulator/index.md @@ -31,7 +31,7 @@ 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 -apt update && apt install python3-colcon-metadata +sudo apt update && sydo apt install python3-colcon-metadata rosdep update rosdep install --from-path src --ignore-src -y @@ -43,7 +43,7 @@ source install/local_setup.bash Let's install the last version of CMake: ```bash -apt install wget +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'