Hi everyone,
I've gotten myself into another (programming) pickle here and would kindly ask for your expert advise.
Attempting to continue building the examples library, I have successfully created the following examples, which all work subscriber publisher and subscriber service (addTwoInts) publisher, subscriber, service (addTwoInts)
Now I'm attempting to add another service, but for some reason it fails, upon attempting to initialise the second service. Instead of creating the service, it gets stuck inside the error_handler and flashes the LED for ever.
This is the line that fails.
RCCHECK(rclc_service_init_default(&service_setBool, &node, ROSIDL_GET_SRV_TYPE_SUPPORT(example_interfaces, srv, SetBool), "/setbool"));
Callback, service instance and executor have all been added as it should.
Would you kindly take a look at the source code below and let me know what I am missing. Would appreciate your help.
Thank you!
Complete source code:
#include <Arduino.h>
#include <Wire.h>
#include <micro_ros_platformio.h>
#include <example_interfaces/srv/add_two_ints.h>
#include <example_interfaces/srv/set_bool.h>
#include <stdio.h>
#include <rcl/error_handling.h>
#include <rcl/rcl.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>
#include <std_msgs/msg/int32.h>
#include <std_msgs/msg/int64.h>
#include <std_msgs/msg/bool.h>
#if !defined(MICRO_ROS_TRANSPORT_ARDUINO_SERIAL)
#error This example is only avaliable for Arduino framework with serial transport.
#endif
// ============================================================================
// INSTANCES, OBJECTS
// ============================================================================
// publisher and subscriber common
rcl_node_t node;
rclc_support_t support;
rcl_allocator_t allocator;
rclc_executor_t executor;
rcl_timer_t timer;
unsigned int num_handles = 4; // 1 subscriber, 1 publisher, 2 service
// service addTwoInts
rcl_service_t service_addTwoInts;
// service setBool
rcl_service_t service_setBool;
rcl_wait_set_t wait_set;
//publisher
rcl_publisher_t publisher;
std_msgs__msg__Int32 msg_pub;
// subscriber
rcl_subscription_t subscriber;
std_msgs__msg__Int32 msg_sub;
example_interfaces__srv__AddTwoInts_Response res;
example_interfaces__srv__AddTwoInts_Request req;
example_interfaces__srv__SetBool_Response res_setBool;
example_interfaces__srv__SetBool_Request req_setBool;
// ============================================================================
// DEFINES
// ============================================================================
// I/O - Definitions
#define LED_PIN 13
// cleartext substitutions
// debug
#ifndef DebugMonitor
#define DebugMonitor Serial6
#endif
#define debug(x) DebugMonitor.print(x)
#define debugln(x) DebugMonitor.println(x)
// ============================================================================
// FUNCTION PROTOTYPES
// ============================================================================
#define RCCHECK(fn){rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){error_loop();}}
#define RCSOFTCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){}}
// Error handle loop
void error_loop() {
while(1) {
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
delay(500);
}
}
// --------------------------------------------------------
// timer callback (currently just increments msg_pub.data)
void timer_callback(rcl_timer_t * timer, int64_t last_call_time) {
RCLC_UNUSED(last_call_time);
if (timer != NULL) {
RCSOFTCHECK(rcl_publish(&publisher, &msg_pub, NULL));
msg_pub.data++;
}
}
// --------------------------------------------------------
// subscriber callback
void subscription_callback(const void * msgin) {
const std_msgs__msg__Int32 * msg = (const std_msgs__msg__Int32 *)msgin;
digitalWrite(LED_PIN, (msg->data == 0) ? LOW : HIGH);
}
// --------------------------------------------------------
// srv.addTwoInts callback
void service_addTwoInts_callback(const void * req, void * res) {
example_interfaces__srv__AddTwoInts_Request * req_in = (example_interfaces__srv__AddTwoInts_Request *) req;
example_interfaces__srv__AddTwoInts_Response * res_in = (example_interfaces__srv__AddTwoInts_Response *) res;
//printf("Service request value: %d + %d.\n", (int) req_in->a, (int) req_in->b);
res_in->sum = req_in->a + req_in->b;
}
// --------------------------------------------------------
// srv.setBool callback
void service_setBool_callback(const void * req_setBool, void * res_setBool) {
bool previousState = digitalRead(LED_PIN);
example_interfaces__srv__SetBool_Request * req_in = (example_interfaces__srv__SetBool_Request *) req_setBool;
example_interfaces__srv__SetBool_Response * res_in = (example_interfaces__srv__SetBool_Response *) res_setBool;
digitalWrite(LED_PIN, (req_in->data == 0) ? LOW:HIGH);
if (previousState != digitalRead(LED_PIN)) {
res_in->success = true;
}
else {
res_in->success = false;
}
}
// ============================================================================
// SETUP
// ============================================================================
void setup() {
set_microros_serial_transports(Serial);
// Debug
DebugMonitor.begin(115200);
// I/O setup
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
delay(1000);
allocator = rcl_get_default_allocator();
// create init_options
RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));
// create node
RCCHECK(rclc_node_init_default(&node, "uros_platformio_node", "", &support));
// create service addTwoInts
RCCHECK(rclc_service_init_default(&service_addTwoInts, &node, ROSIDL_GET_SRV_TYPE_SUPPORT(example_interfaces, srv, AddTwoInts), "/addtwoints"));
// create service setBool
RCCHECK(rclc_service_init_default(&service_setBool, &node, ROSIDL_GET_SRV_TYPE_SUPPORT(example_interfaces, srv, SetBool), "/setbool"));
// create publisher
RCCHECK(rclc_publisher_init_default(&publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32), "micro_ros_platformio_node_publisher"));
// create subscriber
RCCHECK(rclc_subscription_init_default(&subscriber, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32), "micro_ros_platformio_node_subscriber"));
// create timer,
const unsigned int timer_timeout = 1000;
RCCHECK(rclc_timer_init_default(&timer, &support, RCL_MS_TO_NS(timer_timeout), timer_callback));
// create executor
//RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
RCCHECK(rclc_executor_init(&executor, &support.context, num_handles, &allocator));
RCCHECK(rclc_executor_add_service(&executor, &service_addTwoInts, &req, &res, service_addTwoInts_callback));
RCCHECK(rclc_executor_add_service(&executor, &service_setBool, &req_setBool, &res_setBool, service_setBool_callback));
RCCHECK(rclc_executor_add_subscription(&executor, &subscriber, &msg_sub, &subscription_callback, ON_NEW_DATA));
RCCHECK(rclc_executor_add_timer(&executor, &timer));
msg_pub.data = 0;
}
// ============================================================================
// MAIN LOOP
// ============================================================================
void loop() {
delay(100);
RCSOFTCHECK(rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100)));
}
EDIT:
I have added some debug code to get to the bottom of this problem, as per the RCCHECK
. See below for details:
#define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){debugln(temp_rc); error_loop();}}
The output I receive in the debug console is
1
Tracing RCL_RET_OK back to where it is defined, I found the following in file types.h:
/// Success return code.
#define RCL_RET_OK RMW_RET_OK
/// Unspecified error return code.
#define RCL_RET_ERROR RMW_RET_ERROR
/// Timeout occurred return code.
#define RCL_RET_TIMEOUT RMW_RET_TIMEOUT
/// Failed to allocate memory return code.
#define RCL_RET_BAD_ALLOC RMW_RET_BAD_ALLOC
/// Invalid argument return code.
#define RCL_RET_INVALID_ARGUMENT RMW_RET_INVALID_ARGUMENT
/// Unsupported return code.
#define RCL_RET_UNSUPPORTED RMW_RET_UNSUPPORTED
Hovering with the mouse over the individual #defines, the tooltip revealed the "1" seen in my debug output resolves to
RMW_RET_ERROR
with the comment above:
/// Unspecified error return code.
Not really helpful, but with some luck somebody in the community has been across this and is willing to provide some insight.
Much appreciated!
Originally posted by PointCloud on ROS Answers with karma: 120 on 2022-10-20
Post score: 1
Original comments
Comment by PointCloud on 2022-10-21:
Added an edit to post, too long for comment here.
Basically traced RCCHECK back to
/// Unspecified error return code.
RMW_RET_ERROR
Anyone kind enough here to provide some insight why this fails? Thank you!
Comment by PointCloud on 2022-10-21:
Just did a test to verify that both services are able to run. Commenting one or the other out and compiling / downloading the code works without issues.
So I'm guessing there is an issue with the way the second service is initialized.
Nothing in the source code tells me that these functions need to be unique, or that they would interfere with one another. Here is, how both services are initialized:
RCCHECK(rclc_service_init_default(&service_addTwoInts, &node, ROSIDL_GET_SRV_TYPE_SUPPORT(example_interfaces, srv, AddTwoInts), "/addtwoints"));
RCCHECK(rclc_service_init_default(&service_setBool, &node, ROSIDL_GET_SRV_TYPE_SUPPORT(example_interfaces, srv, SetBool), "/setbool"));
Any thoughts?