Skip to content

Commit 4ab8adc

Browse files
committed
Add dynamic message type support
Adds ROS2 message type support to the rclcpy module. Creating a Publisher now requires a ROS message type - all future messages must adhere to that message type. A small group of test messages are available in the std_msgs submodule of rclcpy, including Int32 and Bool. Message type detection is done through a type registry, and the appropriate C level information passed to the Micro-ROS interface. Ideally, future messages and message packages will be supported with an automated class and registry generator. Due to MicroROS restrictions, custom user message types cannot be supported without rebuilding Circuitpython. API changes: Removes the test publisher function `publish_int32` and replaces it with the correct `publish` function, which accepts a message object. Adds the MsgType parameter to publisher creation. Adds the std_msgs module to the module root.
1 parent ec2a55a commit 4ab8adc

File tree

26 files changed

+413
-25
lines changed

26 files changed

+413
-25
lines changed

locale/circuitpython.pot

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,10 @@ msgstr ""
13001300
msgid "Invalid ROS domain ID"
13011301
msgstr ""
13021302

1303+
#: ports/espressif/common-hal/rclcpy/Publisher.c shared-bindings/rclcpy/Node.c
1304+
msgid "Invalid ROS message type"
1305+
msgstr ""
1306+
13031307
#: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c
13041308
msgid "Invalid argument"
13051309
msgstr ""
@@ -1330,6 +1334,10 @@ msgstr ""
13301334
msgid "Invalid hex password"
13311335
msgstr ""
13321336

1337+
#: ports/espressif/common-hal/rclcpy/Publisher.c
1338+
msgid "Invalid message type"
1339+
msgstr ""
1340+
13331341
#: ports/espressif/common-hal/wifi/Radio.c
13341342
#: ports/zephyr-cp/common-hal/wifi/Radio.c
13351343
msgid "Invalid multicast MAC address"
@@ -1854,6 +1862,10 @@ msgstr ""
18541862
msgid "Program too long"
18551863
msgstr ""
18561864

1865+
#: shared-bindings/rclcpy/Publisher.c
1866+
msgid "Publish message does not match topic"
1867+
msgstr ""
1868+
18571869
#: shared-bindings/rclcpy/Publisher.c
18581870
msgid "Publishers can only be created from a parent node"
18591871
msgstr ""
@@ -2341,6 +2353,10 @@ msgstr ""
23412353
msgid "Unsupported hash algorithm"
23422354
msgstr ""
23432355

2356+
#: ports/espressif/common-hal/rclcpy/Publisher.c
2357+
msgid "Unsupported message type"
2358+
msgstr ""
2359+
23442360
#: ports/espressif/common-hal/socketpool/Socket.c
23452361
#: ports/zephyr-cp/common-hal/socketpool/Socket.c
23462362
msgid "Unsupported socket type"

ports/espressif/common-hal/rclcpy/Publisher.c

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@
55
// SPDX-License-Identifier: MIT
66

77
#include "shared-bindings/rclcpy/Publisher.h"
8+
#include "shared-bindings/rclcpy/registry.h"
89

910
#include "esp_log.h"
1011

1112
void common_hal_rclcpy_publisher_construct(rclcpy_publisher_obj_t *self, rclcpy_node_obj_t *node,
12-
const char *topic_name) {
13+
const mp_obj_type_t *message_type, const char *topic_name) {
1314

14-
// Create Int32 type object
15-
// TODO: support other message types through class imports
16-
const rosidl_message_type_support_t *type_support = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32);
15+
const rosidl_message_type_support_t *type_support = common_hal_rclcpy_registry_get_msg_ros_typesupport(message_type);
16+
if (!type_support) {
17+
mp_raise_ValueError(MP_ERROR_TEXT("Invalid ROS message type"));
18+
}
1719

1820
// Creates a reliable Int32 publisher
1921
rcl_ret_t rc = rclc_publisher_init_default(
@@ -24,6 +26,7 @@ void common_hal_rclcpy_publisher_construct(rclcpy_publisher_obj_t *self, rclcpy_
2426
}
2527

2628
self->node = node;
29+
self->message_type = message_type;
2730
}
2831

2932
bool common_hal_rclcpy_publisher_deinited(rclcpy_publisher_obj_t *self) {
@@ -45,15 +48,36 @@ void common_hal_rclcpy_publisher_deinit(rclcpy_publisher_obj_t *self) {
4548
self->node = NULL;
4649
}
4750

48-
void common_hal_rclcpy_publisher_publish_int32(rclcpy_publisher_obj_t *self, int32_t data) {
49-
// Int32 message object
50-
std_msgs__msg__Int32 msg;
51+
void common_hal_rclcpy_publisher_publish(rclcpy_publisher_obj_t *self, mp_obj_t message_obj) {
52+
53+
const rclcpy_registry_msg_entry_t *entry = common_hal_rclcpy_registry_get_msg_entry(self->message_type);
54+
if (!entry) {
55+
mp_raise_ValueError(MP_ERROR_TEXT("Invalid message type"));
56+
}
5157

52-
// Set message value
53-
msg.data = data;
58+
rcl_ret_t rc = RCL_RET_ERROR;
59+
60+
// This will eventually be moved to an autogenerated dispatcher file.
61+
switch (entry->msg_kind) {
62+
case RCLCPY_MSG_TYPE_BOOL: {
63+
rclcpy_std_msgs_bool_obj_t *bool_msg = MP_OBJ_TO_PTR(message_obj);
64+
std_msgs__msg__Bool ros_msg;
65+
ros_msg.data = bool_msg->data;
66+
rc = rcl_publish(&self->rcl_publisher, &ros_msg, NULL);
67+
break;
68+
}
69+
case RCLCPY_MSG_TYPE_INT32: {
70+
rclcpy_std_msgs_int32_obj_t *int32_msg = MP_OBJ_TO_PTR(message_obj);
71+
std_msgs__msg__Int32 ros_msg;
72+
ros_msg.data = int32_msg->data;
73+
rc = rcl_publish(&self->rcl_publisher, &ros_msg, NULL);
74+
break;
75+
}
76+
default:
77+
mp_raise_ValueError(MP_ERROR_TEXT("Unsupported message type"));
78+
return;
79+
}
5480

55-
// Publish message
56-
rcl_ret_t rc = rcl_publish(&self->rcl_publisher, &msg, NULL);
5781
if (RCL_RET_OK != rc) {
5882
mp_raise_RuntimeError(MP_ERROR_TEXT("Could not publish to ROS topic"));
5983
}

ports/espressif/common-hal/rclcpy/Publisher.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,21 @@
1010

1111
#include "common-hal/rclcpy/Node.h"
1212
#include "common-hal/rclcpy/__init__.h"
13+
#include "shared-bindings/rclcpy/std_msgs/Int32.h"
14+
#include "shared-bindings/rclcpy/std_msgs/Bool.h"
1315

1416
#include <rcl/error_handling.h>
1517
#include <rcl/rcl.h>
1618
#include <rclc/executor.h>
1719
#include <rclc/rclc.h>
1820
#include <stdio.h>
1921
#include <std_msgs/msg/int32.h>
22+
#include <std_msgs/msg/bool.h>
2023

2124

2225
typedef struct {
2326
mp_obj_base_t base;
2427
rclcpy_node_obj_t *node;
2528
rcl_publisher_t rcl_publisher;
29+
const mp_obj_type_t *message_type;
2630
} rclcpy_publisher_obj_t;

ports/espressif/common-hal/rclcpy/__init__.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// SPDX-License-Identifier: MIT
66

77
#include "shared-bindings/rclcpy/__init__.h"
8+
#include "common-hal/rclcpy/registry.h"
89

910
#include "esp_log.h"
1011

@@ -59,6 +60,7 @@ void rclcpy_reset(void) {
5960
memset(&rclcpy_default_context, 0, sizeof(rclcpy_default_context));
6061
rclcpy_default_context.initialized = false;
6162
}
63+
deinitialize_registry();
6264
}
6365

6466
void common_hal_rclcpy_init(const char *agent_ip, const char *agent_port, int16_t domain_id) {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2025 Lucian Copeland
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#include "shared-bindings/rclcpy/registry.h"
8+
#include "shared-bindings/rclcpy/std_msgs/__init__.h"
9+
10+
static rclcpy_registry_msg_entry_t msg_registry[RCLCPY_MSG_TYPE_COUNT];
11+
static bool registry_initialized = false;
12+
13+
// static const rclcpy_registry_msg_entry_t msg_registry[] = {
14+
// // stg_msg
15+
// {
16+
// .cpy_type = &rclcpy_std_msgs_bool_type,
17+
// .ros_type_support = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Bool),
18+
// .msg_kind = RCLCPY_MSG_TYPE_BOOL
19+
// },
20+
// {
21+
// .cpy_type = &rclcpy_std_msgs_int32_type,
22+
// .ros_type_support = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32),
23+
// .msg_kind = RCLCPY_MSG_TYPE_INT32
24+
// },
25+
// };
26+
27+
// Called on demand, not at init
28+
static void initialize_registry(void) {
29+
if (registry_initialized) {
30+
return;
31+
}
32+
33+
msg_registry[0] = (rclcpy_registry_msg_entry_t) {
34+
.cpy_type = &rclcpy_std_msgs_bool_type,
35+
.ros_type_support = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Bool),
36+
.msg_kind = RCLCPY_MSG_TYPE_BOOL
37+
};
38+
39+
msg_registry[1] = (rclcpy_registry_msg_entry_t) {
40+
.cpy_type = &rclcpy_std_msgs_int32_type,
41+
.ros_type_support = ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32),
42+
.msg_kind = RCLCPY_MSG_TYPE_INT32
43+
};
44+
45+
registry_initialized = true;
46+
}
47+
48+
// Used at reset (just to be careful with ROS support pointers)
49+
void deinitialize_registry(void) {
50+
registry_initialized = false;
51+
}
52+
53+
const rclcpy_registry_msg_entry_t * common_hal_rclcpy_registry_get_msg_entry(const mp_obj_type_t *cpy_type) {
54+
initialize_registry();
55+
for (size_t i = 0; i < RCLCPY_MSG_TYPE_COUNT; i++) {
56+
if (msg_registry[i].cpy_type == cpy_type) {
57+
return &msg_registry[i];
58+
}
59+
}
60+
return NULL;
61+
}
62+
63+
const rosidl_message_type_support_t * common_hal_rclcpy_registry_get_msg_ros_typesupport(const mp_obj_type_t *cpy_type) {
64+
initialize_registry();
65+
const rclcpy_registry_msg_entry_t *entry = common_hal_rclcpy_registry_get_msg_entry(cpy_type);
66+
return entry ? entry->ros_type_support : NULL;
67+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2025 Lucian Copeland
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#pragma once
8+
#include "py/obj.h"
9+
10+
#include <std_msgs/msg/bool.h>
11+
#include <std_msgs/msg/int32.h>
12+
#include <std_msgs/msg/string.h>
13+
14+
typedef enum {
15+
RCLCPY_MSG_TYPE_BOOL,
16+
RCLCPY_MSG_TYPE_INT32,
17+
RCLCPY_MSG_TYPE_COUNT
18+
} rclcpy_msg_kind_t;
19+
20+
typedef struct {
21+
const mp_obj_type_t *cpy_type;
22+
const rosidl_message_type_support_t *ros_type_support;
23+
rclcpy_msg_kind_t msg_kind;
24+
} rclcpy_registry_msg_entry_t;
25+
26+
void deinitialize_registry(void);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2025 Lucian Copeland
4+
//
5+
// SPDX-License-Identifier: MIT
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2025 Lucian Copeland
4+
//
5+
// SPDX-License-Identifier: MIT
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2025 Lucian Copeland
4+
//
5+
// SPDX-License-Identifier: MIT
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2025 Lucian Copeland
4+
//
5+
// SPDX-License-Identifier: MIT

0 commit comments

Comments
 (0)