How to create a new micro-ROS type

This tutorial starts in a previously created micro-ROS environment. Check First micro-ROS application on an RTOS for instructions about how to create a micro-ROS environment for embedded platforms.

Once your micro-ROS workspace is created, go to firmware/mcu_ws and run the package creating command:

cd firmware/mcu_ws
ros2 pkg create --build-type ament_cmake my_custom_message
cd my_custom_message
mkdir msg
touch msg/MyCustomMessage.msg

In the autogenerated CMakeLists.txt file you should add the following lines just before ament_package():

...
find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/MyCustomMessage.msg"
 )
...

In the autogenerated package.xml file you should add the following lines:

...
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
...

The content of the msg/MyCustomMessage.msg file contains your message defintion. For example, let’s include these fields:

bool bool_test
byte byte_test
char char_test
float32 float32_test
float64 double_test
int8 int8_test
uint8 uint8_test
int16 int16_test
uint16 uint16_test
int32 int32_test
uint32 uint32_test
int64 int64_test
uint64 uint64_test

Now, you can build your micro-ROS workspace as usual. As explained in First micro-ROS application on an RTOS, the ros2 run micro_ros_setup build_firmware.sh command will build all packages located inside mcu_ws.

In your micro-ROS application code, you can use your new message type as usual:

#include <my_custom_message/msg/my_custom_message.h>

...

my_custom_message__msg__MyCustomMessage msg;

msg.byte_test = 3;
msg.uint32_test = 42;

...

rclc_publisher_init_default(&publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(my_custom_message, msg, MyCustomMessage), "my_custom_publisher");
rcl_publish(&publisher, &msg, NULL);
 
...

You can find further information in the ROS 2 Create custom ROS 2 msg and srv files.

Using type composition

It is possible to create custom types that include members from another ROS 2 message types packages. For example let’s add a member with type Point32 from the ROS 2 package geometry_msgs.

First of all, you have to include the dependency in the CMakeLists.txt:

...
find_package(rosidl_default_generators REQUIRED)
find_package(geometry_msgs REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/MyCustomMessage.msg"
 )
...

Also, include the dependency in package.xml:

...
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
<depend>geometry_msgs</depend>
...

The message definition in msg/MyCustomMessage.msg can now include types from the geometry_msgs package:

...
int64 int64_test
uint64 uint64_test
geometry_msgs/Point32 point32_test

And finally, in your code you can access this new member of your custom type:

#include <my_custom_message/msg/my_custom_message.h>

...

my_custom_message__msg__MyCustomMessage msg;

msg.byte_test = 3;
msg.uint32_test = 42;

msg.point32_test.x = 1.23;
msg.point32_test.y = 2.31;
msg.point32_test.z = 3.12;

...

Note that in order for the micro_ros_agent to register these new types, the package with the custom types you’ve created above, should also be cloned to the host workspace, e.g. ~/uros_ws/src, and compiled there as well before running the agent.