micro-ROS / micro_ros_platformio

micro-ROS library for Platform.IO
Apache License 2.0
225 stars 80 forks source link

ESP32 multi-core panic #86

Closed BorealisJames closed 1 year ago

BorealisJames commented 1 year ago

Hello, I am trying to use the two ESP32 cores to do separate task, where core 1 handles robot controls and core 0 handles MicroROS. I made a test script to see if this is feasible, where core 0 runs a task that contains rclc_executor_spin() that has a timer that publishes to a counter topic and core1 runs a dummy serial print task.

Sample code

#include <Arduino.h>

#include <micro_ros_platformio.h>
#include <stdio.h>
#include <rcl/rcl.h>
#include <rcl/error_handling.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>

#include <std_msgs/msg/int32.h>
#include <geometry_msgs/msg/vector3.h>

#if !defined(ESP32) && !defined(TARGET_PORTENTA_H7_M7) && !defined(ARDUINO_NANO_RP2040_CONNECT)
#error This example is only avaible for Arduino Portenta, Arduino Nano RP2040 Connect and ESP32 Dev module
#endif

#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)){}}

rcl_publisher_t publisher;
std_msgs__msg__Int32 msg;
const char * publisher_topic_name = "/counter_test1";

rclc_executor_t executor;
rclc_support_t support;
rcl_allocator_t allocator;

rcl_node_t node;
rcl_timer_t timer;

/* End of MicroROS variables */
IPAddress agent_ip(192, 168, 1, 130);
size_t agent_port = 8888;
char ssid[] = "WIFI";
char psk[]= "PASS";

// Error handle loop
void error_loop() {
  while(1) {
  }
}

void rclc_executor_task(void *parameter)
{
    RCSOFTCHECK(rclc_executor_spin(&executor));
}

void dummy_task(void *parameter)
{
    Serial.println("Hello world");
}

void timer_callback(rcl_timer_t * timer, int64_t last_call_time)
{
  RCLC_UNUSED(last_call_time);
  if (timer != NULL) 
  {
    // display the data
    msg.data++;
    RCSOFTCHECK(rcl_publish(&publisher, &msg, NULL));
    Serial.println("Publishing and incrementing counter msg!!");
  }
}

void setup() {
    // serial to display data
    Serial.begin(115200);
    Serial.println("Begin simple test");
    Serial.println(xPortGetCoreID());

    /* MicroROS stuff */ 
    set_microros_wifi_transports(ssid, psk, agent_ip, agent_port);
    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, "micro_ros_platformio_wifi_node", "", &support));

    // create best effort publishers
    RCCHECK(rclc_publisher_init_best_effort(
    &publisher,
    &node,
    ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32),
    publisher_topic_name));

    // create a msg timer that is triggered every 1000ms
    RCCHECK(rclc_timer_init_default(
        &timer,
        &support,
        RCL_MS_TO_NS(1000),
        timer_callback));

    // create executor
    RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
    RCCHECK(rclc_executor_add_timer(&executor, &timer));    

    msg.data = 0;

    Serial.println("Done set up");
    xTaskCreatePinnedToCore(  // Use xTaskCreate() in vanilla FreeRTOS
                dummy_task,     // Function to be called
                "Test",  // Name of task
                1048,           // Stack size (bytes in ESP32, words in FreeRTOS)
                NULL,           // Parameter to pass
                1,              // Task priority (must be same to prevent lockup)
                NULL,           // Task handle
                1);       // Run on one core for demo purposes (ESP32 only)

    xTaskCreatePinnedToCore(  // Use xTaskCreate() in vanilla FreeRTOS
                rclc_executor_task,     // Function to be called
                "Test2",  // Name of task
                1048,           // Stack size (bytes in ESP32, words in FreeRTOS)
                NULL,           // Parameter to pass
                2,              // Task priority (must be same to prevent lockup)
                NULL,           // Task handle
                0);       // Run on one core for demo purposes (ESP32 only)
}

void loop() {
    // Run the executor 
    // RCSOFTCHECK(rclc_executor_spin(&executor));
}

Expected behavior

Able to see ROS2 counter topic and debug output from serial port.

Actual behavior

ESP32 crash and kept on restarting itself. Guru Meditation Error: Core 0 panic'ed (IllegalInstruction). Exception was unhandled, below is a screenshot of the ouput image

Thanks!

pablogs9 commented 1 year ago

Does it work if you spin in the loop?

Maybe the stack size is too small?

BorealisJames commented 1 year ago

Yes if I comment out the xTaskCreateFuncs and just spin it in the loop, it will work normally. I also tried increasing the stack space of the executor task from 1048 to 10000 but the issue still occurs.

pablogs9 commented 1 year ago

In that case, it seems to be related with your multithreading approach and not with micro-ROS

BorealisJames commented 1 year ago

Are there any resources/guides I can look into? By default ESP32 on arduino, the setup() and loop() runs on core 1. To use core0 we usually just use the xTaskCreatePinnedToCore() function and specify which core. But this time round it doesn't seem to be that straightforward.

pablogs9 commented 1 year ago

I guess that in ESP 32 documentation or IDF Arduino documentation.

I'm closing this, feel free to reopen if you find a micro-ROS issue.

n0sc3tipsum commented 5 months ago

Yes if I comment out the xTaskCreateFuncs and just spin it in the loop, it will work normally. I also tried increasing the stack space of the executor task from 1048 to 10000 but the issue still occurs.

Hey I am having the same iss

  • Hardware description: esp32-s3-devkitc-1
  • RTOS: FreeRTOS
  • Installation type: PlatformIO
  • Version or commit hash: humble

Hello, I am trying to use the two ESP32 cores to do separate task, where core 1 handles robot controls and core 0 handles MicroROS. I made a test script to see if this is feasible, where core 0 runs a task that contains rclc_executor_spin() that has a timer that publishes to a counter topic and core1 runs a dummy serial print task.

Sample code

#include <Arduino.h>

#include <micro_ros_platformio.h>
#include <stdio.h>
#include <rcl/rcl.h>
#include <rcl/error_handling.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>

#include <std_msgs/msg/int32.h>
#include <geometry_msgs/msg/vector3.h>

#if !defined(ESP32) && !defined(TARGET_PORTENTA_H7_M7) && !defined(ARDUINO_NANO_RP2040_CONNECT)
#error This example is only avaible for Arduino Portenta, Arduino Nano RP2040 Connect and ESP32 Dev module
#endif

#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)){}}

rcl_publisher_t publisher;
std_msgs__msg__Int32 msg;
const char * publisher_topic_name = "/counter_test1";

rclc_executor_t executor;
rclc_support_t support;
rcl_allocator_t allocator;

rcl_node_t node;
rcl_timer_t timer;

/* End of MicroROS variables */
IPAddress agent_ip(192, 168, 1, 130);
size_t agent_port = 8888;
char ssid[] = "WIFI";
char psk[]= "PASS";

// Error handle loop
void error_loop() {
  while(1) {
  }
}

void rclc_executor_task(void *parameter)
{
  RCSOFTCHECK(rclc_executor_spin(&executor));
}

void dummy_task(void *parameter)
{
  Serial.println("Hello world");
}

void timer_callback(rcl_timer_t * timer, int64_t last_call_time)
{
  RCLC_UNUSED(last_call_time);
  if (timer != NULL) 
  {
  // display the data
    msg.data++;
    RCSOFTCHECK(rcl_publish(&publisher, &msg, NULL));
  Serial.println("Publishing and incrementing counter msg!!");
  }
}

void setup() {
  // serial to display data
  Serial.begin(115200);
  Serial.println("Begin simple test");
  Serial.println(xPortGetCoreID());

  /* MicroROS stuff */ 
  set_microros_wifi_transports(ssid, psk, agent_ip, agent_port);
  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, "micro_ros_platformio_wifi_node", "", &support));

  // create best effort publishers
  RCCHECK(rclc_publisher_init_best_effort(
  &publisher,
  &node,
  ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32),
  publisher_topic_name));

  // create a msg timer that is triggered every 1000ms
  RCCHECK(rclc_timer_init_default(
      &timer,
      &support,
      RCL_MS_TO_NS(1000),
      timer_callback));

  // create executor
  RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
  RCCHECK(rclc_executor_add_timer(&executor, &timer));    

  msg.data = 0;

  Serial.println("Done set up");
  xTaskCreatePinnedToCore(  // Use xTaskCreate() in vanilla FreeRTOS
              dummy_task,     // Function to be called
              "Test",  // Name of task
              1048,           // Stack size (bytes in ESP32, words in FreeRTOS)
              NULL,           // Parameter to pass
              1,              // Task priority (must be same to prevent lockup)
              NULL,           // Task handle
              1);       // Run on one core for demo purposes (ESP32 only)

  xTaskCreatePinnedToCore(  // Use xTaskCreate() in vanilla FreeRTOS
              rclc_executor_task,     // Function to be called
              "Test2",  // Name of task
              1048,           // Stack size (bytes in ESP32, words in FreeRTOS)
              NULL,           // Parameter to pass
              2,              // Task priority (must be same to prevent lockup)
              NULL,           // Task handle
              0);       // Run on one core for demo purposes (ESP32 only)
}

void loop() {
  // Run the executor 
  // RCSOFTCHECK(rclc_executor_spin(&executor));
}

Expected behavior

Able to see ROS2 counter topic and debug output from serial port.

Actual behavior

ESP32 crash and kept on restarting itself. Guru Meditation Error: Core 0 panic'ed (IllegalInstruction). Exception was unhandled, below is a screenshot of the ouput image

Thanks!

Hey I am having the same issue, were you able to find any solutions?