ESP32 With Arduino IDE - Multi-Core Programming

48K2110

Intro: ESP32 With Arduino IDE - Multi-Core Programming

It isn’t common for a relatively small microcontroller to have two cores. This is precisely why we will highlight today this marvel of ESP32, which is Multi-Core Programming. I have already mentioned this in other videos, which I intend to talk more about in a complete playlist. I also plan to discuss the FreeRTOS (Free real-time operating systems), which is an operating system for microcontrollers that allows for easy programming, deployment, protection, connection and management for devices of small and low capacity. Returning to the topic of today’s project, let's create a program where different tasks are executed simultaneously in different cores. To do so, we will introduce you to Multi-Core Programming in ESP32 in order to know its main functions.

In our assembly, as shown in the image above, we use an i2c display, a button, a LED, and a source from 110 to 5v, which feeds our circuit.

STEP 1: Introduction

One of the many interesting features of ESP32 is that it has two Tensilica LX6 cores, which we can take advantage of to run our code with higher performance and more versatility.

• Both the SETUP and the main functions of the LOOP are executed with a priority of 1 and in core 1.

• Priorities can range from 0 to N, where 0 is the lowest priority.

• Core can be 0 or 1.

Tasks have an assigned priority so the scheduler can decide which task to execute. High-priority tasks that are ready to run will have preference over lower priority tasks. In an extreme case, the highest priority task needs the CPU all the time, and the lowest priority task would never run.

But with two cores available, the two tasks can be performed as long as they are assigned to different cores.

STEP 2: Functions

Let's look now at some of the important functions we can use.

xTaskCreate

Create a new task and add it to the list of tasks that are ready to be executed.

xTaskCreatePinnedToCore

This function does exactly the same thing as xTaskCreate. However, we have an additional parameter, which is where we will define in which core the task will be executed.

xPortGetCoreID

This function returns the number of the kernel that is executing the current task.

TaskHandle_t

This is the reference type for the created task.

The xTaskCreate call returns (as a pointer to a parameter) a TaskHandle_t that can be used as a parameter by vTaskDelete to delete a task.

vTaskDelete

Delete a created task.

STEP 3: WiFi NodeMCU-32S ESP-WROOM-32

STEP 4: 16x2 Serial LCD Display With I2c Module

STEP 5: Assembly

Our electric scheme is quite simple. Just follow it, and the project will work.

STEP 6: Library

Add the "LiquidCrystal_I2C" library for communication with the LCD display.

Access the link and download the library.

Unzip the file and paste it into the libraries folder of the Arduino IDE.

C: / Program Files (x86) / Arduino / libraries

STEP 7: Program

We will make a simple program that consists of making a LED blink and count how many times it blinks. We will also have a button that, when pressed, changes a variable of its state control. The display will update all this information.

We will program the display update task to run on the processor core UM, and the other operations will be on the ZERO core.

STEP 8: Libraries and Variables

First, let's include the library responsible for display control and define the necessary settings. It’s also important to point out the variables for control of the LED, indicate the pins that will be used, as well as the variables that indicate the core.

#include <Wire.h>
#include <LiquidCrystal_IC2.h> //biblioteca responsável pelo controle do display LiquidCrystal_I2C lcd(0x27, 16, 2); //set the LCD address to 0x27 for a 16 chars and 2 line display int count = 0; int blinked = 0; String statusButton = "DESATIVADO"; //pinos usados const uint8_t pin_led = 4; const uint8_t pin_btn = 2; //variaveis que indicam o núcleo static uint8_t taskCoreZero = 0; static uint8_t taskCoreOne = 1;

STEP 9: Setup

In this part, we initialize the LCD with the SDA and SCL pins. We turn on the display light, and create a task that will be executed in the coreTaskZero function, running in core 0 with priority 1.

void setup() {
pinMode(pin_led, OUTPUT); pinMode(pin_btn, INPUT); //inicializa o LCD com os pinos SDA e SCL lcd.begin(19, 23); // Liga a luz do display lcd.backlight(); lcd.setCursor(0, 0); lcd.print("Piscadas:"); //cria uma tarefa que será executada na função coreTaskZero, com prioridade 1 e execução no núcleo 0 //coreTaskZero: piscar LED e contar quantas vezes xTaskCreatePinnedToCore( coreTaskZero, /* função que implementa a tarefa */ "coreTaskZero", /* nome da tarefa */ 10000, /* número de palavras a serem alocadas para uso com a pilha da tarefa */ NULL, /* parâmetro de entrada para a tarefa (pode ser NULL) */ 1, /* prioridade da tarefa (0 a N) */ NULL, /* referência para a tarefa (pode ser NULL) */ taskCoreZero); /* Núcleo que executará a tarefa */ delay(500); //tempo para a tarefa iniciar

Also in the Setup, we created a task that will be executed in the coreTaskOne function with priority 2. We have also created another task that will be executed in the coreTaskTwo function, running in core 0 with priority 2.

//cria uma tarefa que será executada na função coreTaskOne, com prioridade 2 e execução no núcleo 1
//coreTaskOne: atualizar as informações do display xTaskCreatePinnedToCore( coreTaskOne, /* função que implementa a tarefa */ "coreTaskOne", /* nome da tarefa */ 10000, /* número de palavras a serem alocadas para uso com a pilha da tarefa */ NULL, /* parâmetro de entrada para a tarefa (pode ser NULL) */ 2, /* prioridade da tarefa (0 a N) */ NULL, /* referência para a tarefa (pode ser NULL) */ taskCoreOne); /* Núcleo que executará a tarefa */ delay(500); //tempo para a tarefa iniciar //cria uma tarefa que será executada na função coreTaskTwo, com prioridade 2 e execução no núcleo 0 //coreTaskTwo: vigiar o botão para detectar quando pressioná-lo xTaskCreatePinnedToCore( coreTaskTwo, /* função que implementa a tarefa */ "coreTaskTwo", /* nome da tarefa */ 10000, /* número de palavras a serem alocadas para uso com a pilha da tarefa */ NULL, /* parâmetro de entrada para a tarefa (pode ser NULL) */ 2, /* prioridade da tarefa (0 a N) */ NULL, /* referência para a tarefa (pode ser NULL) */ taskCoreZero); /* Núcleo que executará a tarefa */ } void loop() {}

STEP 10: TaskZero

This function will change the LED status every second and will blink each time (cycle on and off), incrementing our blinked variable.

//essa função ficará mudando o estado do led a cada 1 segundo
//e a cada piscada (ciclo acender e apagar) incrementará nossa variável blinked void coreTaskZero( void * pvParameters ){ String taskMessage = "Task running on core "; taskMessage = taskMessage + xPortGetCoreID(); //Serial.println(taskMessage); //log para o serial monitor while(true){ digitalWrite(pin_led, !digitalRead(pin_led)); if (++count % 2 == 0 ) blinked++; delay(1000); } }

STEP 11: TaskOne

This function will only be responsible for updating the information on the display every 100m.

//essa função será responsável apenas por atualizar as informações no
//display a cada 100ms void coreTaskOne( void * pvParameters ){ while(true){ lcd.setCursor(10, 0); lcd.print(blinked); lcd.setCursor(0,1); lcd.print(statusButton); delay(100); } }

STEP 12: TaskTwo

Finally, this function will be responsible for reading the button state and updating the control variable.

//essa função será responsável por ler o estado do botão
//e atualizar a variavel de controle. void coreTaskTwo( void * pvParameters ){ while(true){ if(digitalRead(pin_btn)){ statusButton = "Ativado "; } else statusButton = "Desativado"; delay(10); } }

6 Comments

Thanks for this tutorial !
Hi everyone! Im trying to solve something related with the 2 cores. I use LiquidCrystal_I2C.h, and start very well on the setup() but, when I start printing from core 0 it stops working. If inside the core0 task, outside of the while(true) I put lcd.init() it works in core0 but it stops working if I print from core 1. So the library is not starting for both cores, what I have to search on the library and what should I do to make it work from both cores with only one initialization?
Hi, I have an App that runs 2 tasks. One on core0 and the other on core1. Both write to an OLED display. But when the task on core0 writes to display it scrambles the text. Task on core1 don't do that. If both tasks run on core1 it works perfectly. The display lib Adafruit_SSD1306 is updated.
Any idea? Thanks
Hi Fernando, thank you for your amazing work. My steppers motors are now working because of you.

Regarding this post, can you justify the use of the fonction delay() instead of vTaskDelay().
Which one is the right one ?

Thanks for your lights on this.
Tony
This tutorial was useful but when I try to implement WiFi and IRRemote libraries(especially IR receiver) together, core 1 was panic'ed error is the output. Even i tried to run them in different cores and steady with maximum stack size, still problem prevails. Any suggestion?
Dear Fernando
Thank you for this tutorial. Its very clear and complete.
Im trying to run a dual core esp32 with one core wifi y task reading temp sensor and other core uploading data to Internet. When I try it crashes. Did you ever try somerhing similar? Can you help me with any example. Thank you in avance
Regards
Pablo