ESP32, Dual Core Programming

I'm working with an M5Stack that uses an ESP32 which has dual cores. I've been researching this question on the web and here on the Arduino forums and can't find an answer.

This is on a project that I originally wrote for an Arduino MEGA, and I'm porting it over to the M5Stack with a remote Arduino NANO, where I need comms between the two.

By default, the Arduino code runs on Core 1 for the M5Stack.

I would like to utilize Core 0 to run the communications for my project (currently using 2 of the hardware Serial ports) and setup data exchange between the cores using Global Variables (possibly one or two structures) in a loop (while(1) or for(,,). Is this possible using (Generic code xTaskCreatePinnedToCore):

xTaskCreatePinnedToCore(CommsCode, "AllComms", 10000, NULL, 1, &Task1, 0);  

and NOT having to assign all of the other code to Core 1 (which should be run by default on Core 1).

Or, is it not that simple?

Sir Michael

Why do you think that's necessary?

That will spool up a task on Core 0 alright. But where you go from there is a rather steep learning curve if you have no experience writing code for a multi-tasking environment.

ESP32 uses FreeRTOS. Handling tasks and inter-task communications requires a different way of thinking and different programming paradigm than working in a single-task only environment.

You might want to start here.

I also highly recommend the book Mastering-the-FreeRTOS-Real-Time-Kernel.

That gets you going with vanilla (single Core) FreeRTOS. Then, you'll want to check out Espressif's additions to support running on dual Cores.

1 Like

For inter-task communication you'll probably need the task handle of the default task on core 1 created by the Arduino framework. Not sure where that is defined. It'll probably be easier/quicker just to create your own core 1 task and task handle.

It depends. Using Queues, Event Groups, Mutexes, and Semaphores doesn't require knowledge of task handles. Using Direct Task Notifications does.

A task can get it's own handle with xTaskGetCurrentTaskHandle().

Thanks for the replies Everyone!

Why do you think that's necessary?

For the current level of code, No, I don't think that it's necessary at this point.

The current project is fairly substantial (not sure how to count the lines of code in the Arduino IDE). I started developing it back in 2017 and the early hardware got too hard to maintain so I'm in the throws of redesigning it and adding a graphical display.

However, for a future function that I want to add, which is to provide communications over the internet and one of the requirements would be to service that comms about every 250ms (or faster?). with my current level of code, I don't think that would be possible.

I was hoping that I could use Core 0 to process that comms as necessary, then I also figured that the other 2 existing comm ports could be service using Core 0.

I used to be a Software Test Engineer (in a past life) and I do still enjoy building projects using Arduino's and trying to expand to use other small platforms.

I'll have to read up on the RTOS (as suggested) and see what I can figure out. Sounds like it's not going to be as easy as what I had envisioned (after viewing some of the examples).

ANOTHER QUESTION: Is it possible to interrupt the Arduino code on a regular basis (assuming I don't have long delay()s) that could service that interrupt on a regular basis. I've used interrupts before, but on a hardware basis. Is there some kind of a timing interrupt that can be setup in the Arduino?

Sir Michael

That code runs on core 0. If your code on core 0 takes too long bad things will happen. That is why "user code" defaults to core 1.

There's an excellent series of introductory videos by Shawn Hymel that covers such topics.

If it's simply that your Mega is too slow and you need something faster but don't want to make substantial changes to the way you program then just choose a faster Arduino that doesn't use an RTOS, for example the Due.

Surely this is possible on any single-task microcontroller.

The only pre-condition is to write your code from the very first to the very last line beeing non-blocking.

A connection to internet by using UDP-messages can send data once every 50 milliseconds without a problem.

Ideally you would handle the UART communication as bytes come in , in a non blocking way and once the payload is full then you process it.

It’s much likely better this way rather than go see the incoming buffer every 250ms

(At 115200 bauds you could have received ~2800 bytes which is waaauyyy more than can fit une de Serial buffer).

I think you should keep all on Core1 (what else is taking so much time on that core Thant you think it would be better on Core0?)

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.