How to use ESP32 Dual Core with Arduino IDE

The ESP32 comes with 2 Xtensa 32-bit LX6 microprocessors: core 0 and core 1. So, it is dual core. When we run code on Arduino IDE, by default, it runs on core 1. In this post we’ll show you how to run code on the ESP32 second core by creating tasks. You can run pieces of code simultaneously on both cores, and make your ESP32 multitasking.

Note: you don’t necessarily need to run dual core to achieve multitasking.

Introduction

The ESP32 comes with 2 Xtensa 32-bit LX6 microprocessors, so it’s dual core:

  • Core 0
  • Core 1

When we upload code to the ESP32 using the Arduino IDE, it just runs – we don’t have to worry which core executes the code.

There’s a function that you can use to identify in which core the code is running:

xPortGetCoreID()

If you use that function in an Arduino sketch, you’ll see that both the setup() and loop() are running on core 1. Test it yourself by uploading the following sketch to your ESP32.

/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com  
*********/

void setup() {
  Serial.begin(115200);
  Serial.print("setup() running on core ");
  Serial.println(xPortGetCoreID());
}

void loop() {
  Serial.print("loop() running on core ");
  Serial.println(xPortGetCoreID());
}

View raw code

Open the Serial Monitor at a baud rate of 115200 and check the core the Arduino sketch is running on.

Create Tasks

The Arduino IDE supports FreeRTOS for the ESP32, which is a Real Time Operating system. This allows us to handle several tasks in parallel that run independently.

Tasks are pieces of code that execute something. For example, it can be blinking an LED, making a network request, measuring sensor readings, publishing sensor readings, etc…

To assign specific parts of code to a specific core, you need to create tasks. When creating a task you can chose in which core it will run, as well as its priority. Priority values start at 0, in which 0 is the lowest priority. The processor will run the tasks with higher priority first.

To create tasks you need to follow the next steps:

1. Create a task handle. An example for Task1:

TaskHandle_t Task1;

2. In the setup() create a a task assigned to a specific core using the xTaskCreatePinnedToCore function. That function takes several arguments, including the priority and the core where the task should run (the last parameter).

xTaskCreatePinnedToCore(
      Task1code, /* Function to implement the task */
      "Task1", /* Name of the task */
      10000,  /* Stack size in words */
      NULL,  /* Task input parameter */
      0,  /* Priority of the task */
      &Task1,  /* Task handle. */
      0); /* Core where the task should run */

3. After creating the task, you should create a function that contains the code for the created task. In this example you need to create the Task1code() function. Here’s how the task function looks like:

Void Task1code( void * parameter) {
  for(;;) {
    Code for task 1 - infinite loop
    (...)
  }
}

The for(;;) creates an infinite loop. So, this function runs similarly to the loop() function. You can use it as a second loop in your code, for example.

If during your code execution you want to delete the created task, you can use the vTaskDelete()function, that accepts the task handle (Task1) as argument:

vTaskDelete(Task1);

Let’s see how these concepts work with a simple example.

Create Tasks in Different Cores – Example

To follow this example, you need the following parts:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

To create different tasks running on different cores we’ll create two tasks that blink LEDs with different delay times. Start by wiring two LEDs to the ESP32 as shown in the following diagram:

We’ll create two tasks running on different cores:

  • Task1 runs on core 0;
  • Task2 runs on core 1;

Upload the next sketch to your ESP32 to blink each LED in a different core:

/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com  
*********/

TaskHandle_t Task1;
TaskHandle_t Task2;

// LED pins
const int led1 = 2;
const int led2 = 4;

void setup() {
  Serial.begin(115200); 
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);

  //create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0
  xTaskCreatePinnedToCore(
                    Task1code,   /* Task function. */
                    "Task1",     /* name of task. */
                    10000,       /* Stack size of task */
                    NULL,        /* parameter of the task */
                    1,           /* priority of the task */
                    &Task1,      /* Task handle to keep track of created task */
                    0);          /* pin task to core 0 */                  
  delay(500); 

  //create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1
  xTaskCreatePinnedToCore(
                    Task2code,   /* Task function. */
                    "Task2",     /* name of task. */
                    10000,       /* Stack size of task */
                    NULL,        /* parameter of the task */
                    1,           /* priority of the task */
                    &Task2,      /* Task handle to keep track of created task */
                    1);          /* pin task to core 1 */
    delay(500); 
}

//Task1code: blinks an LED every 1000 ms
void Task1code( void * pvParameters ){
  Serial.print("Task1 running on core ");
  Serial.println(xPortGetCoreID());

  for(;;){
    digitalWrite(led1, HIGH);
    delay(1000);
    digitalWrite(led1, LOW);
    delay(1000);
  } 
}

//Task2code: blinks an LED every 700 ms
void Task2code( void * pvParameters ){
  Serial.print("Task2 running on core ");
  Serial.println(xPortGetCoreID());

  for(;;){
    digitalWrite(led2, HIGH);
    delay(700);
    digitalWrite(led2, LOW);
    delay(700);
  }
}

void loop() {
  
}

View raw code

How the Code Works

Note: in the code we create two tasks and assign one task to core 0 and another to core 1. Arduino sketches run on core 1 by default. So, you could write the code for Task2 in the loop() (there was no need to create another task). In this case we create two different tasks for learning purposes.

However, depending on your project requirements, it may be more practical to organize your code in tasks as demonstrated in this example.

The code starts by creating a task handle for Task1 and Task2 called Task1 and Task2.

TaskHandle_t Task1;
TaskHandle_t Task2;

Assign GPIO 2 and GPIO 4 to the LEDs:

const int led1 = 2; 
const int led2 = 4;

In the setup(), initialize the Serial Monitor at a baud rate of 115200:

Serial.begin(115200);

Declare the LEDs as outputs:

pinMode(led1, OUTPUT); 
pinMode(led2, OUTPUT);

Then, create Task1 using the xTaskCreatePinnedToCore() function:

xTaskCreatePinnedToCore(
             Task1code, /* Task function. */
             "Task1",   /* name of task. */
             10000,     /* Stack size of task */
             NULL,      /* parameter of the task */
             1,         /* priority of the task */
             &Task1,    /* Task handle to keep track of created task */
             0);        /* pin task to core 0 */

Task1 will be implemented with the Task1code() function. So, we need to create that function later on the code. We give the task priority 1, and pinned it to core 0.

We create Task2 using the same method:

xTaskCreatePinnedToCore(
             Task2code,  /* Task function. */
             "Task2",    /* name of task. */
             10000,      /* Stack size of task */
             NULL,       /* parameter of the task */
             1,          /* priority of the task */
             &Task2,     /* Task handle to keep track of created task */
             1);         /* pin task to core 0 */

After creating the tasks, we need to create the functions that will execute those tasks.

void Task1code( void * pvParameters ){
  Serial.print("Task1 running on core ");
  Serial.println(xPortGetCoreID());

  for(;;){
    digitalWrite(led1, HIGH);
    delay(1000);
    digitalWrite(led1, LOW);
    delay(1000);
  }
}

The function to Task1 is called Task1code() (you can call it whatever you want). For debugging purposes, we first print the core in which the task is running:

Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());

Then, we have an infinite loop similar to the loop() on the Arduino sketch. In that loop, we blink LED1 every one second.

The same thing happens for Task2, but we blink the LED with a different delay time.

void Task2code( void * pvParameters ){
  Serial.print("Task2 running on core ");
  Serial.println(xPortGetCoreID());

  for(;;){
    digitalWrite(led2, HIGH);
    delay(700);
    digitalWrite(led2, LOW);
    delay(700);
  }
}

Finally, the loop() function is empty:

void loop() { }

Note: as mentioned previously, the Arduino loop() runs on core 1. So, instead of creating a task to run on core 1, you can simply write your code inside the loop().

Demonstration

Upload the code to your ESP32. Make sure you have the right board and COM port selected.

Open the Serial Monitor at a baud rate of 115200. You should get the following messages:

As expected Task1 is running on core 0, while Task2 is running on core 1.

In your circuit, one LED should be blinking every 1 second, and the other should be blinking every 700 milliseconds.

Wrapping Up

In summary:

  • The ESP32 is dual core;
  • Arduino sketches run on core 1 by default;
  • To use core 0 you need to create tasks;
  • You can use the xTaskCreatePinnedToCore() function to pin a specific task to a specific core;
  • Using this method you can run two different tasks independently and simultaneously using the two cores.

In this tutorial we’ve provided a simple example with LEDs. The idea is to use this method with more advanced projects with real world applications. For example, it may be useful to use one core to take sensor readings and other to publish those readings on a home automation system.

If you want to learn more about ESP32, make sure you take a look at our course: Learn ESP32 with Arduino IDE.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

103 thoughts on “How to use ESP32 Dual Core with Arduino IDE”

      • hello can you wtite the code for to plot the graphs in between two cores
        TaskHandle_t Task1;
        TaskHandle_t Task2;

        // LED pins
        const int led1 = 2;
        const int led2 = 4;

        void setup() {
        Serial.begin(115200);
        pinMode(led1, OUTPUT);
        pinMode(led2, OUTPUT);

        //create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0
        xTaskCreatePinnedToCore(
        Task1code, /* Task function. /
        “Task1”, /
        name of task. /
        10000, /
        Stack size of task /
        NULL, /
        parameter of the task /
        1, /
        priority of the task /
        &Task1, /
        Task handle to keep track of created task /
        0); /
        pin task to core 0 */
        //delay(50);

        //create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1
        xTaskCreatePinnedToCore(
        Task2code, /* Task function. /
        “Task2”, /
        name of task. /
        10000, /
        Stack size of task /
        NULL, /
        parameter of the task /
        1, /
        priority of the task /
        &Task2, /
        Task handle to keep track of created task /
        1); /
        pin task to core 1 */
        // delay(50);
        }

        //Task1code: blinks an LED every 1000 ms
        void Task1code( void * pvParameters ){
        Serial.print(“Task1 running on core “);
        Serial.println(xPortGetCoreID());
        unsigned long cnt=0;
        while(1){
        /digitalWrite(led1, HIGH);
        delay(1000);
        digitalWrite(led1, LOW);
        /
        // delay(10);
        Serial.println(“intask1”);
        //Serial.println(cnt++);
        }
        }

        //Task2code: blinks an LED every 700 ms
        void Task2code( void * pvParameters ){
        Serial.print(“Task2 running on core “);
        Serial.println(xPortGetCoreID());
        unsigned long cntt =0;
        while(1){
        /digitalWrite(led2, HIGH);
        delay(700);
        digitalWrite(led2, LOW);
        delay(700);
        /
        Serial.println(“intask2”);
        //Serial.println(cntt++);
        }
        }

        void loop() {

        }//////only task one printing on the sceen not working on the task2

        Reply
      • sometimes error also coming
        E (31208) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
        E (31208) task_wdt: – IDLE (CPU 0)
        E (31208) task_wdt: Tasks currently running:
        E (31208) task_wdt: CPU 0: Task1
        E (31208) task_wdt: CPU 1: IDLE
        E (31208) task_wdt: Aborting.

        Reply
        • Shankar,

          At the very end of your endless while() loop, add a vTaskDelay(xDelay); and define your xDelay as something like:-

          const TickType_t xDelay = (10 / portTICK_PERIOD_MS);

          The delay will stop your task without blocking other processes and thus give the system a chance to run something else (in this case, WiFi, which runs on core-0 by default).

          Reply
  1. Hi,
    Thank you for the post.
    At first I did not see the ‘why would you want to’ , but …
    If I would make a simple robot car, could one core be involved with the sensors and actions while the other core handles my commands, either directly or for instance via MQTT?

    Reply
  2. Sir I request you to write a code for Controlling relays with both classic Bluetooth and Wi-Fi of ESP32. By using these two different Tasks assigning in a single ESP32. It will be helpful for us sir to build a project like Home automation. Please give us some of your precious time to help us. Thank you sir.

    Reply
  3. Hi,
    Brilliant article and really well explained. Thank you.
    Like Henry Smith & Murray Bold above I want to use one core for sensors, LCD, controls, etc. and the other for Wifi connections. Does your course show the coding for the links between the two cores? For my project I currently use Arduino (controls etc) and ESP8266 (Wifi) with RX/TX links but would like to investigate the 2 x core ESP32 option

    Reply
    • Hi Bob.
      We don’t go deep into that subject in our course.
      As Tony mentioned in a previous comment “both tasks can use the same variables declared in the sketch, so this is the easiest way to communicate between the cores.”.
      I hope this helps.
      Regards,
      Sara

      Reply
      • Sharing global variables between multiple threads of execution of course is possible. But the caveat is that you should be careful to use mutex’es or semaphores when accessing the shared resources.

        Reply
  4. Hi, dear Santos couple,
    I am just happy you find all these features in mostly undocumented ESP jungle and make them actually run.
    My question is, what is the best way for two tasks in different cores to communicate with each other. Say you have a web server task that collects what sequence the user wishes to run on WS8212B LED-strip and the other task, that actually executes the LED sequence. Can these two tasks access the same variable?
    What is this “tasks send messages to each other in a queue” about?
    Thank you for your answer.

    Reply
    • Hi Tony.
      Thank you for your nice words.
      I’m not familiar with queues and semaphores, but these are important concepts to achieve what you are asking.
      We don’t have any tutorials about those subjects. But a good way to get started is watching this video from Andreas Spiess: youtu.be/k_D_Qu0cgu8?t=497
      Regards,
      Sara

      Reply
  5. Hi Sara,
    I have actually solved it. I used your tutorial and I run my web-server sketch in the default core 1. I created a task in core 0 to run the LED-strip sequences. What I discovered (and you could probably mention it in your tutorial), that both tasks can use the same variables declared in the sketch, so this is the easiest way to communicate between the cores.
    Andreas Spiess is awesome, thank you for the tip.

    Reply
    • Hi Tony.
      I agree, Andreas Spiess videos are great and very informative.
      Thank you for sharing that. It will definitely be useful for other of our readers and also for us.
      Regards,
      Sara

      Reply
  6. Rui & Sara,
    You two are a godsend to people like myself that “Program thru example”. Keep up the great work !
    Thank you, so much
    Steve

    Reply
  7. Thank you so much Sara, First of all I don’t know how to use freeRTOS, just I searched on google freeRTOS on esp32, then I found treasure, that you created and it is well explained, once again thank you so much

    Reply
  8. Hi,

    Great article and thanks for sharing.

    Re: “Note: you don’t necessarily need to run dual core to achieve multitasking.”

    How is this achieved because I would like to try it as I am having issues running both cores with LoRa on core 0 and the display on core 1.

    Thanks.

    Reply
  9. Hi Sara,

    great article, I tried two create two independent tasks for a couple of WS2812B-Leds. But delays in the original library seems to stop both tasks…

    And now sketches which I upload (with only one taslk) behave weird. Is there a way to rest my ESP32 in an initial state? Something like stopping processing for a while and restarting after some delay…

    Regards,
    Max

    Reply
  10. I am following the same procedure for my ESP32 via PlatformIO but getting errors like……
    ‘Task1code’ was not declared in this scope
    ‘Task2code’ was not declared in this scope

    can you help me with this?

    Reply
    • Hello Ace,

      In PlatformIO you need to include Arduino.h and use function prototypes.
      Use this in the begining of the code:

      #include “Arduino.h”

      void Task1code( void * pvParameters );
      void Task2code( void * pvParameters );

      Reply
  11. Excellent article, I hadn’t appreciated you could use tasks with Arduino on the ESP32. So far I had been using the ESP32 library and the FreeRTOS directly via a complicated make. It works but there is less available code to drive all the peripherals, so using the Arduino is far better.
    As mentioned in previous posts, you can communicate between tasks using the same variables, this will work for a simple integer, however you can get obscure problems with anything more complicated. For instance, what happens if one task is updating a variable, then the other task kicks in and also tries to update it. You need to use either semaphores or mutexs to stop this, both of which the ESP32 system provides. You might want to cover this in future articles.

    Reply
    • You’re right, and i’m interested too, but i don’t think they will go deeper into multithreading.
      They tend to keep articles simple for beginners.
      But if they could explore communications between threads with simple examples, i’m in !

      Reply
  12. Nice tutorial, thank you.
    Question:
    Is it necessary to specify the two handlers?
    If everything runs on core 1 as defaults, can we not just specify a handler for core 0 and run everything else in the regular Arduino loop statement?

    Reply
  13. Hi Sara,

    Does the program need to run a continuous loop when running in specific cores as shown here? What if we still want the program run on the separate core restricted, ie run once?

    Thanks for another great tutorial!

    Reply
    • Hi.
      I think that you don’t necessarily need to run a loop.
      You can put code that only runs once.
      Regards,
      Sara

      Reply
  14. Hi, I just searched FreeRTOS and I found this article.It’s very helpful but can you please provide a sketch where Bluetooth and Wi-Fi both can be used simultaneously for controlling 2 relay switched.

    Reply
  15. Great write up! I have it working fine for me GPS info gathering on one core, and WIFI on the other.
    I can’t seem to find a way to define functions within the TaskxCode block. If I call a function that was declared external to the TaskxCode block, does it run within the calling block? If not, how do you declare functions with the TaskxCode block?
    Thanks!

    Reply
    • Hi Bob, I want to do something similar to what you did, use the two cores of the esp32 to have one in charge only of the internet connection, either by GSM or Wifi. could you share your code to have a base to start from? it would be very helpful

      Reply
      • Hello,
        I can, but there are some caveats.
        -C is not my long suit! I struggle with strings. I suppose it’ll be obvious 🙂
        -I got sidetracked on this project, this code is functional*, but not very clean.
        -It’s very lightly commented.
        -It still has debug code info embedded.
        -It’s about 600 lines of code.
        -The WIFI portion is unduly complex, as I stripped it from an ESP32 web Server experiment I had around. There are provisions for scanning multiple APs, a single AP, fixed, DHCP…
        -There are routines for GPS data collection, a ring buffer to hold the data till an AP can be found, data pruning if the buffer fills, GMT to local time conversions, etc. In short, a lot of stuff to confuse what’s going on!
        -*I believe there’s still a leak in the dB posting routine, it does run for 10s of thousands of dB posts without error, which is longer than I ever needed in my use.

        So if you’re still interested, let me know. I’ll need to sanitize it a bit before posting or emailing.

        Reply
  16. Hi, when a task is deleted using vTaskDelete, how can I restart the task? does it simply just call the xTaskCreate again? or there another method?
    Thanks

    Reply
  17. Hi, I am facing an issue with Multitasking on Esp32. The Sensor data is collected through Task 1 on Core Zero, with priority four, and the Void loop() performs all Wifi related functionalities. But after 3 hrs or so, the Task1 running on Core Zero stops working completely and only the void loop() is executed. Not sure why this is happening .It would be really useful if someone can guide me on this.

    Reply
    • Like @Alberto Rubinelli mentioned, your stack might be too small, but if it is a case of stack overflow, you are probably having a memory leak, so increasing the stack size would just delay the failure.
      Look at your code, if there are some memory structures that are allocated and never released.

      Reply
  18. Hi Sara,
    something to be careful of when setting up tasks in free RTOS with ESP32 it that the stack size is in bytes for the ESP32 and not words.
    This is one of the differences between the Xtensa build of free RTOS and stock free RTOS.
    Each task requires a stack of at least 768 bytes to run, plus a bit for any local variables being used and a little bit of head room for loading and unloading doesn’t do any harm.
    Best regards
    Ben

    Reply
  19. I want to run an attachinterrupt() in core 0 continuously.
    Do I set the pinmode and attachinterrupt inside the task function? Where do I put the IRQ function?
    Or do I set the pinmode and attachinterrupt normally and just have the IRQ function in core 0?

    Reply
  20. That is a great article, simple and complete answer to a relevant question.
    Though you never can square the circle and be complete 🙂

    The questions that the article rises are, what happens if there are multiple tasks on a core. Do they share time? In random order? I the order of priority?
    Will tasks interrupt the loop() on core 0?
    Is there a way to delay() without locking the core for the duration on the delay?
    Will tasks coexist with AsyncWebServer?

    Reply
  21. I purchased several of your courses and I am extremely pleased with how well you write your tutorials. I have some prior experience with the regular Arduino family of computers and have just started working with the ESP32. You have really helped me get up to speed quickly. Can’t recommend your tutorials any stronger. Anyone wanting to learn the EPS32, or even the basic electronics course, should absolutely buy your courses. Highly recommended.

    I tried your example to blink two leds with this tutorial and it works amazing. I added some additional code in each task to try to do some more things and it compiles cleanly, but after two or three loops thru a task it executes the setup code over again, effectively rebooting the program. Any ideas why this might be happening?

    Reply
    • Hi Bruce.
      Thanks for your comment.

      Usually, it means that something is not programmed properly and it triggers a watchdog that makes the ESP32 reboot over and over again.
      Regards,
      Sara

      Reply
    • You could try to look at the boot information that ESP sends to console when it boots.
      There might be a hint on what caused the restart.
      Another usual approach is disable parts of the code that you have added to find the exact place that causes the restart.
      I won’t bet money on it, but often it is an accidental infinite loop that causes such a behaviour.

      Reply
    • The WDT is reset by the IDLE task which runs at priority 0. If you’re running a continuous loop at a higher priority, the IDLE never gets to do the reset hence the WDT is not reset and the dog barks 🙂
      You can avoid this problem by running your task also at priority 0. The RTOS scheduler will then be able to give the IDLE task some time.
      Alternatively, you can run your task at a higher priority but include a delay such as xTaskDelay(pdMS_TO_TICKS(10)) which will cause a 10ms delay in your loop during which time a lower priority task (such as IDLE) can run.

      Reply
      • Hi John, Hi comunity,
        in March 22 I put the split of the tasks on Ice, now I try again and it’s not realy clear to me what I can do to avoid a WDT Reset without breaking down my whole System.
        (Priority 0 for Task1, Priority 1 for Task2 is set up)
        I need the timing as I have a Time machine that executes every ms and a delay – ether delay(10) or xTaskDelay(pdMS_TO_TICKS(10)) delay’s too much the functionality.
        By the way, we have a real fast controller and stop it by a delay????
        Without using core 1 and 2 my appication is running fine (I know, without taskdefinition it’s running on core 1) so I assume there is a problem with the setup of the Tasks.
        I also tried to disable the WDT but it didn’t work
        esp_task_wdt_init(10,false);
        esp_task_wdt_add(&Task1);
        esp_task_wdt_add(&Task2);
        disableCore0WDT();
        disableCore1WDT();
        disableLoopWDT();

        I tried to reset the task WDT (esp_task_wdt_reset();) but it didn’t work.

        So the question would be what I can do …..

        Reply
  22. I’m not an expert, but I had messed around with esp32 a lot, idk if it’s the same situation that happened to me but sometimes the esp is restarted by the watchdog timer, you can disable this by the ‘disablecore0wdt();’ line before at the setup, before task configuration lines.

    Reply
    • “Something is wrong, watchdog timer triggers, let’s disable a watchdog” is not a healthiest approach.
      Like totally wrong thing to do, IMHO 🙂

      Reply
      • If you have a code that takes more time that the WDT can support, it will reset the uC, so that was my case, I have a code that may take more time that the WDT can support, I disabled the WDT, and currently the uC has been running for months without issues (I register the uptime and other indicators of the health of the uC), making measurements, control and sending data to the cloud.

        So it does not seems to be wrong at all for me, so you can share your solution or a path to find one, because with your humble opinion problems doesn’t get solved.

        Reply
        • I say it is rare occasion where your code really takes more than WDT can support.
          Pretty much any algorithm can be coded in a way that it would fit into the watchdog constraints. Or you could explicitly reset watchdog timer.
          Anyway, plugging the safety valve because your boiler has higher pressure than expected might sound like a good idea, but it is usually not. Even when it works for you.
          Regarding solutions, I see no problem in this post that would allow for solutions in a form of code samples. The question is general, so is the answer.

          Reply
          • WOW! I am amazed at all the responses to my question and I am very grateful for all the information that has been sent to me. All of you are fantastic! I’m under a NDA with this project, so I can’t say too much about the application or the code. Basically, it is like some that you have mentioned where one cpu monitors multiple sensors and the other cpu performs a critical task, as long as the sensors are reading ok. It was working just running the basic code that Rui presented in this tutorial. I even added some GPS code to the sensor cpu and that was working fine. But once I added a few more sensors to cpu 1 and made the code on cpu 2 closer to the actual application, it began to fail. In the past, I have used the procedure of removing code, a little at a time, to find where the failure point occurs. I like the idea of leaving the watchdog timer alone to make sure it can do its job. I have written over 100 sketches for the Arduino family from AT85s to the MEGA. I have only written 3 apps using the ESP32 so far. I really like the ESP and learning from Rui’s tutorials has been amazing. This is really the first problem that threw me for a loop. Frankly I didn’t realize how much influence the RTOS had over my apps. I can see how I’ll have to learn more about it to make sure things are running properly. Again, many thanks for all the advice and time you have taken to respond to me. I really appreciate it. Bruce

          • Hear Ye! Hear Ye! Hear Ye!
            My dual CPU code is now working thanks to all the wonderful support the readers have sent me! I flip flopped the code from one CPU to the other. And it is working great. I have more code to added to the first CPU, but it is already running several functions perfectly. I’ll continue to let you all know what happens when I add all the additional code. Again, many thanks to everyone that responded.
            Merry Christmas to all! Have a Blessed New Year.
            Bruce

  23. There is a section on the watchdog timer at https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/wdts.html. Normally you would get a watchdog trigger if your code enters a loop and doesn’t call any functions to yield to another process. The software then thinks the system has crashed and so reboots. An example would be if you try to implement a simple delay loop in your code by repeatedly executing a for loop.
    There are several ways out of this:
    1. Alter your code so it doesn’t just execute tight loops but does call some FreeRTOS functions as well
    2. Alter the watchdog timer window, so that it will go longer before timing out. However if you have a fault in your code it will still fail at some point
    3. Reset the watchdog timer in your loop by calling esp_task_wdt_reset. I have never tried this so you may have to set various things up first. See the documentation referenced above.
    4. As mentioned above, disable the watchdog timer, though as also mentioned above this is not normally desirable.

    Reply
  24. First of all, I want to apologize, my previous answer was rude.

    In the device I coded, I need to run a while loop for about 10 mins, I double checked to avoid get stuck in that loop, and I found pretty particular that in main loop the same routine did not trigger the WDT, so basically it watches the tasks, not the main loop, and it worked good there, so that’s why I disabled it.

    I find other solution that resets the timer with some lines, but with this routine I coded, I don’t see the need to have the WDT enabled.

    I may say that is ok to disable it if you know why you are doing it, it is not a must to have it running.

    Reply
    • No apology necessary. That fact that you responded to help me, in what ever way you thought best at the time, is greatly appreciated. It would only be rude to have some information for me and not bother to send it. Even negative comments and sarcasm have there place with me because I’m asking for help and I may not know the best or approved way to do that. So any remarks help me conform to be better at asking for help. Believe me when I say I try everything I can possibly think of before asking for help and I often see that is not always the case with others. So again, thank you for trying to help me in any way you think best. I do appreciate it.
      Bruce

      Reply
    • The loop() is just part of a rtos task and before running loop() the watchdog timer is reset using this code: if(loopTaskWDTEnabled){esp_task_wdt_reset()};
      That is why you did not see the problem when running your code in loop.

      Reply
    • Hi Erik. The purpose of the delay(500); between the creation of tasks is merely to allow time for the xTaskCreatePinnedToCore function to start up properly. The delay is necessary as without it the tasks may not be created correctly and you would possibly end up with a corrupted program. Regards, George

      Reply
    • Hi Erik,
      There is no need for the delay’s, I suspect an earlier / test version of the code may of had a printf. To send text to terminal stating that each task had been created.
      Adding a little delay after the message ensures the message has time to reach the terminal, as sometimes it can overwrite the UART buffer before the first message is sent, depending on the message size and system clock speed. 500ms seems excessive for this though.
      I think that the delays have just been left in, after the printf has been removed.
      Ben

      Reply
  25. Hello everyone, I am using the ESP32 in my Mechatronics classes, this article is really great, I would like to know more about the use of external and internal interrupts (timers) distributed in the two cores for PID control of motors and sensors, currently we are doing without dividing the tasks in the two cores, so any possible use orientation is welcome especially in the Arduino IDE but if you know better ways to do it and how to learn it is welcome too

    Reply
  26. So I had an issue with a previous tutorial where sending data to InfluxDB hangs the code while data is being transmitted. For 10+ sensors this can hang the code for 30 seconds and there’s no way around this, even using the standard millis() timing trick as it’s a single write command that causes the delay.

    Anyway, long story short, this tutorial removes all that pain as one can just set up a looping task on core1 that handles the data transmission.

    Fantastic tutorial, thanks!

    Reply
  27. Hello Sara,
    How do I apply tasks with callback functions if I want to run for example ESP Mesh on Core 0. How do I place the callback functions inside a task function?

    Reply
  28. Hi. I have followed up many of your articles and they came up to be very useful.
    I would suggest please to add date of publication when writing these tutorials. Technology updates and changes every time, so it would be better to know how old is the article.
    cheers, and thanks for your tutorials!

    Reply
  29. Hi,
    Thank you for the article!

    I was wondering – is there a problem with having an empty loop() function? Would it cause the CPU to spin and use more energy?

    Thanks, Amos

    Reply
    • no problem,
      a microcontroller is working all the time.
      The emptly loop is only a problem for CPUs with variable clocks because it will prevent them from clocking down.
      Since the ESP32 has a fixed clock, it is no problem, it consums the same energy independent of its job.

      Reply
  30. hi, many thanks for your wonderfull article
    my question is : how many tasks can I create in each task ?
    in your article there is core 0 with task 1. may i create (for example) in core 0 task2, 3 ,4 … etc ?
    I tried but esp32 reset itself. I’m going to do something wrong
    may you explain it with a minimal code ?

    many thanks

    Reply
  31. unfortunately this code won’t work in viscose because of these errors:
    src/main.cpp:22:21: error: ‘Task1code’ was not declared in this scope
    src/main.cpp:33:21: error: ‘Task2code’ was not declared in this scope

    how to fix??

    TIA

    Reply
  32. its also broken in Arduino 2:

    /private/var/folders/w3/rzctjzp952nb7qxljdy7rw600000gn/T/.arduinoIDE-unsaved2023218-17843-wr64y9.qxpk/sketch_mar18a/sketch_mar18a.ino: In function ‘void setup()’:
    /private/var/folders/w3/rzctjzp952nb7qxljdy7rw600000gn/T/.arduinoIDE-unsaved2023218-17843-wr64y9.qxpk/sketch_mar18a/sketch_mar18a.ino:10:3: error: ‘xTaskCreatePinnedToCore’ was not declared in this scope
    10 | xTaskCreatePinnedToCore(
    | ^~~~~~~~~~~~~~~~~~~~~~~
    /private/var/folders/w3/rzctjzp952nb7qxljdy7rw600000gn/T/.arduinoIDE-unsaved2023218-17843-wr64y9.qxpk/sketch_mar18a/sketch_mar18a.ino: In function ‘void Task1code(void)’:
    /private/var/folders/w3/rzctjzp952nb7qxljdy7rw600000gn/T/.arduinoIDE-unsaved2023218-17843-wr64y9.qxpk/sketch_mar18a/sketch_mar18a.ino:35:18: error: ‘xPortGetCoreID’ was not declared in this scope
    35 | Serial.println(xPortGetCoreID());
    | ^~~~~~~~~~~~~~
    /private/var/folders/w3/rzctjzp952nb7qxljdy7rw600000gn/T/.arduinoIDE-unsaved2023218-17843-wr64y9.qxpk/sketch_mar18a/sketch_mar18a.ino: In function ‘void Task2code(void
    )’:
    /private/var/folders/w3/rzctjzp952nb7qxljdy7rw600000gn/T/.arduinoIDE-unsaved2023218-17843-wr64y9.qxpk/sketch_mar18a/sketch_mar18a.ino:48:18: error: ‘xPortGetCoreID’ was not declared in this scope
    48 | Serial.println(xPortGetCoreID());
    | ^~~~~~~~~~~~~~

    exit status 1

    Compilation error: ‘xTaskCreatePinnedToCore’ was not declared in this scope

    Reply
  33. unfortunately this example has a couple of multitasking issues.
    Never ever access Serial.print from two tasks simultanously,
    never do this with hardware ressources.
    Do not use delay(() in tasks, instead use vTaskDelay.
    Beside that, the example is helpful, but should be corrected

    Reply
    • Hi Harry.
      Thanks for pointing that out.
      I’m currently out of the office. I’ll try to review this tutorial when I get back.
      Regards,
      Sara

      Reply
  34. Great tutorial , but I have a note I think you should add it above.
    in esp32 data sheet , it sais that core 0 is used for wifi and bluetooth communtcation and other os stuff and you shouldn’t use it unless it is very important task with caution , otherwise the cpu will panic

    Reply
  35. Hello Sara and Rui,

    This is an excellent page, which has helped me to understand the importance of both hearts and how to manage them. It’s a very good introduction to the subject.
    In your example, is it normal for the priority of both tasks to be 1?

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.