Questions on ESP32 Dual Core Programming and Tasks

Cool, I like experiments

Cool, i will definitely look into this and do the experiment, Sounds Interesting and good to know.

No Doubt, I agree.

Thanks i'll try that

I've used 1 to 3. Never needed to try anything higher.

Unless you are doing something super complicated you probably only need to raise the priority of tasks that are talking to hardware, for example scanning a button or driving a display animation.

As long as you buffer data using queues then most other tasks can run at the default priority.

That seems good enough for me as well, But just to be clear
the lowest is 0 so that means Priority levels are 0 - 3 right ?
if so, it would be good to know what all the levels are but even so
knowing only this achieves my goal anyway.

No,
Originally it was a problem where you had a remote running LED Strips,
2 strips in the Array
and as a result of
delay(x) aka Blocking Code i experimented with millis
and now the idea is to put the I.R. function on 1 core and the rest of the program on the other
so the Hex Data doesn't get truncated as a result of the real time IR Input vs is a delay running at the time but i also wanted these basic aspects of ESP32 Dual Core Multi Tasking and Multi Processing explained

In a way, this is just me being me...
When i learn something knew i like to really really know about it so i apologize if i've stepped
on any toes :slight_smile:
I can assure you it's done with love LOL

Thanks mate you have been an Enormous help

For now i can easily get by with 2 or 3 task priority levels

As you have found, using an RTOS requires a different way of thinking. Each task is its own little world that runs at its own pace. The tricky part is doing inter-task communication and thankfully the RTOS gives you a ready made thread safe queue system designed for doing just that. See xQueueSend() and related functions.

That's not true in RTOS. delay() does what it can do best, i.e. stop the task until the delay has elapsed so that other tasks can run during that time.

Definitely, and.. a cooler way of thinking i might add

Yeah, that's what i derived as well which is why i asked the question about Switch / Case / Break

Ahhh, Thanks mate
Also i like tricky parts as it makes electronics interesting

so now on my list of things to do

Read the

  • Real Time Kernel Hands on etc etc 400 Pages

  • Reference Manual 399 Pages
    Pheew , Because i don't think i could have handled 400 again, 399 was my limit LOL

  • Then using this
    To Figure out Stack Size and Minimize RAM Usage

  • and also xQueueSend() i'll probably jump onto this first while referencing the manual

Thanks mate

well that was what was happening a year ago

@anon76350110, you may have already seen this, but the book "Mastering the FreeRTOS Real Time Kernel" located here is a good reference and should clear up most of the misconceptions you have about Task Handles, Task Priorities, etc.

Then you can head over to these two threads to read about the differences between vanilla FreeRTOS and the port implemented in the ESP32.
https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/freertos_idf.html
https://docs.espressif.com/projects/esp-idf/en/v4.3.1/esp32/api-guides/freertos-smp.html

Now, is the time to post an example for the amplification/clarification of some of the concepts presented so far.

Yep, that's already been recommended and i've downloaded them.

Thank you, Will do

What ??
Examples for what ?
My question was about structure and the questions have already been answered and marked as
solution.

For the benefit of future readers who have the same problem, Everything has been answered
The part about all this

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

This was answered , Nothing more needs to be said here
I haven't tested and calculated the stack size yet because it is Midnight here in sydney
and i was hoping to get some rest
But this is all explained

As for Taskhandlers

This was answered, Nothing more needs to be said here
and the point of Switch / Case / Break was also covered

Beyond that a few other cool suggestions also occurred

What sort of examples were you after ?

In the following setup (Fig-1), it is intended that Task-00 will blink LED (onboard) for 3 times at 500 ms interval and then Task-10 will blink LED for 3 times at 1000 ms interval and then control goes to Task-00 and so on. An example to demonstrate that the expected results are obtained avoiding deadlock/race conditions which might exist while both tasks compete to acquire the ownership of a common hardware resource.
dualCoreOnboard
Figure-1:

esp32's don't use blocking delay(), it just switches to the next task in the ready state and runs it. That is even it you don't use any rtos in your code. It is how the esp32 operates.

i Obtained my expected results.

I didn't pose a question where i had an actual project and would thereby provide you a schematic to this effect.

I had a few questions regarding Dual Core Structure and Limitations.
The expected result was that i get answers

I got the Answers that i expected
it is demonstrated in this forum for anyone to see the answers and verify them

I'm not sure what you really want from me
The questions are answered
it's marked as Solution
and because there was no actual mentioned project in Post 1
there is no need for schematics or Flow Control Charts

Not really sure what else to tell you

I think different tasks wanting to use the same SPI interface to talk to different peripherals would be a more realistic scenario and I would guess that the RTOS framework already has a mechanism to manage this kind of thing, So I would be lazy, look to see how it works and copy the design.

And yes, start a different discussion thread if you want to explore this further.

Yeah i've been doing a bit of experimentation and that's the result i got.
I also found that if you have this sort of thing

/*/////////////////////////////////////////////////////////////////////////////////////////
 LIBRARIES TO INLCUDE
*///---------------------------------------------------------------------------------------
#include <dummy.h>    
#include "freertos/FreeRTOS.h"   
#include "freertos/task.h"  
///////////////////////////////////////////////////////////////////////////////////////////

/*//////////////////////////////////////////////////////////////////////////////////////////
 INITIATES THE TASKCODES FOR THE CORES
*///----------------------------------------------------------------------------------------
void TaskCode1( void * pvParameters );  //Initiates TaskCode1
void TaskCode2( void * pvParameters );  //Initiates TaskCode2
void TaskCode3( void * pvParameters );  //Initiates TaskCode3
///////////////////////////////////////////////////////////////////////////////////////////

/*//////////////////////////////////////////////////////////////////////////////////////////
 DEFINES THE TASK HANDLERS TO EACH TASK
*///---------------------------------------------------------------------------------------
TaskHandle_t STARTUP_Task1;          //Assigned to Core 0
TaskHandle_t IR_Receiver_Task2;      //Assigned to Core 0
TaskHandle_t Button_PowerOff_Task3;  //Assigned to Core 0
///////////////////////////////////////////////////////////////////////////////////////////

/*/////////////////////////////////////////////////////////////////////////////////////////
 BEGIN SETUP
*//////////////////////////////////////////////////////////////////////////////////////////
void setup() 
{  // OPENS the void setup

/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                    TASK PARAMETERS
*///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*/////////////////////////////////////////////////////////////////////////////////////////
------------------------------------------ TASK 1 -----------------------------------------
///////////////////////////////////////////////////////////////////////////////////////////
 TASK NAME - STARTUP_Task1
 TASK CREATION , CORE EXECUTION AND PRIORITY
-------------------------------------------------------------------------------------------
 Create STARTUP_Task1 , Executed in TaskCode1() function , On Core 0 , Priority 0
-------------------------------------------------------------------------------------------
*///  xTaskCreatePinnedToCore(TaskCode1, "STARTUP_Task1", 10000, NULL, 0, &STARTUP_Task1,  0); 
xTaskCreatePinnedToCore(
  TaskCode1,               // Task function.
  "STARTUP_Task1",         // name of task.
  10000,                   // Stack size of task
  NULL,                    // parameter of the task
  0,                       // priority of the task 0 - 3
  &STARTUP_Task1,          // Task handle to keep track of created task
  0);                      // pin task to core X                 
delay(500); 
/*/////////////////////////////////////////////////////////////////////////////////////////
------------------------------------------ TASK 2 -----------------------------------------
///////////////////////////////////////////////////////////////////////////////////////////
 TASK NAME - IR_Receiver_Task2
 TASK CREATION , CORE EXECUTION AND PRIORITY
-------------------------------------------------------------------------------------------
 Create IR_Receiver_Task2 , Executed in TaskCode2() function , On Core 0 , Priority 0
-------------------------------------------------------------------------------------------
*///   xTaskCreatePinnedToCore(TaskCode2, "Task2", 10000, NULL, 0, &Task2,  0); 
xTaskCreatePinnedToCore( 
  TaskCode2,               // Task function
  "IR_Receiver_Task2",     // name of task
  10000,                   // Stack size of task
  NULL,                    // parameter of the task
  0,                       // priority of the task 0 - 3
  &IR_Receiver_Task2,      // Task handle to keep track of created task
  0);                      // pin task to core X                
delay(500);   
/*/////////////////////////////////////////////////////////////////////////////////////////
------------------------------------------ TASK 3 -----------------------------------------
///////////////////////////////////////////////////////////////////////////////////////////
 TASK NAME - Button_PowerOff_Task3
 TASK CREATION , CORE EXECUTION AND PRIORITY
-------------------------------------------------------------------------------------------
 Create Button_PowerOff_Task3 , Executed in TaskCode3() function , On Core 0 , Priority 0
-------------------------------------------------------------------------------------------
*///  xTaskCreatePinnedToCore(TaskCode3, "Button_PowerOff_Task3", 10000, NULL, 0, &Button_PowerOff_Task3, 0); 
xTaskCreatePinnedToCore(
  TaskCode3,               // Task function.
  "Button_PowerOff_Task3", // name of task.
  10000,                   // Stack size of task
  NULL,                    // parameter of the task
  0,                       // priority of the task 0 - 3
  &Button_PowerOff_Task3,  // Task handle to keep track of created task
  0);                      // pin task to core X                 
delay(500);
///////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////
// CLOSE SETUP
///////////////////////////////////////////////////////////////////////////////////////////
} // CLOSES the void setup

/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                    TASKCODE DEFINITIONS
*///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*/////////////////////////////////////////////////////////////////////////////////////////
---------------------------------------- TASKCODE 1 ---------------------------------------
///////////////////////////////////////////////////////////////////////////////////////////
  TaskCode1: STARTUP SEQUENCE - 
*///---------------------------------------------------------------------------------------
void TaskCode1( void * pvParameters )
{ // OPENS TaskCode1
  Serial.print(" TaskCode1 - STARTUP_Task1         - Running On CORE ");
  Serial.println(xPortGetCoreID());
  for(;;)
  {  // OPENS TaskCode1 Main FOR Loop
  Serial.print(" STARTUP_Task1 - TERMINATED");
  Serial.print("");  
vTaskDelete(STARTUP_Task1);
  }  // CLOSES TaskCode1 Main FOR Loop
} // CLOSES TaskCode1
///////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////
//---------------------------------------- TASKCODE 2 ---------------------------------------
///////////////////////////////////////////////////////////////////////////////////////////
//  TaskCode2: IR_Receiver_Task2 - 
////---------------------------------------------------------------------------------------
void TaskCode2( void * pvParameters )
{ // OPENS TaskCode2
  Serial.print(" TaskCode2 - IR_Receiver_Task2     - running on core ");
  Serial.println(xPortGetCoreID());
  for(;;)
  {  // OPENS TaskCode2 Main FOR Loop

  }  // CLOSES TaskCode2 Main FOR Loop
} // CLOSES TaskCode2
///////////////////////////////////////////////////////////////////////////////////////////
//---------------------------------------- TASKCODE 3 ---------------------------------------
///////////////////////////////////////////////////////////////////////////////////////////
//  TaskCode3: BUTTON POWER OFF - 
////---------------------------------------------------------------------------------------
void TaskCode3( void * pvParameters )
{ // OPENS TaskCode3
  Serial.print(" TaskCode3 - Button_PowerOff_Task3 - running on core ");
  Serial.println(xPortGetCoreID());
  for(;;)
  {// OPENS TaskCode1 Main FOR Loop


  }  // CLOSES TaskCode3 Main FOR Loop
} // CLOSES TaskCode3
///////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////
// BEGIN LOOP -----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////////////////
void loop() 
{
}

  • The Functions run in an infinite Loop

  • Even when set to Core 0 and With Lowest Priority (0)
    They Still run before setup and loop

  • I found that i could make these Functions behave like void setup with the use of

vTaskDelete(STARTUP_Task1);
// More Specifically
 vTaskDelete(TASK NAME OR ARGUMENT GOES HERE);

This was also cool,
First i did this


vTaskDelete(STARTUP_Task1);
  Serial.print(" STARTUP_Task1 - TERMINATED");
  Serial.print("");  
  }  // CLOSES TaskCode1 Main FOR Loop
} // CLOSES TaskCode1

My logic was that the Task would delete and then i'd get Serial to print the confirmation.
To my suprise i found no output on the serial monitor for that print line.
This was cool because it told me that the Function stops dead in it's tracks

so then i did this

  Serial.print(" STARTUP_Task1 - TERMINATED");
  Serial.print("");  
vTaskDelete(STARTUP_Task1);
  }  // CLOSES TaskCode1 Main FOR Loop
} // CLOSES TaskCode1

and that printed out the line (for obviour reasons)
i thought that was cool.
So yeah that's what i did find out, Thanks

Exactly my thinking which is why i wasn't sure what he wanted exactly.

If he wants to go into it deeper, No problem, but start a new topic.
As for me , right now i'm just digesting everything , got 800 pages of manuals to read
some experimenting to do and a few cups of coffee to down :stuck_out_tongue:

Maybe some sleep if i can squeeze it in

Through my experimentation i got the impression that
Tasks , were given priority over setup and loop
but i got the impression that all the other tasks were firing off simultaneously

I got this impression from the serial monitor output, as i would insert println statements
at strategic points and i do understand the difference between print and println
and even so the output at times was as such to suggest that

Task 1 fire off first then as task 2 was in progress task 3 fire off then back to Task 2
but that would then suggest they are starting simultaneously wouldn't it ?

Here is a Serial Monitor Output to Demonstrate that
2023-08-21 03_01_49-sketch_aug17g _ Arduino IDE 2.1.1

Rather interesting that even though you'd think they'd run sequentially

TaskCode 1 fires off with Task Name STARTUP_Task1
at this point it's suppose to read STARTUP_Task1 - TERMINATED
but instead
Task 2 and Task 3 fire off
and even though it's 1 println lower it doesn't fire off until the end
.. Very interesting
to me that suggests the Tasks are starting simultanously
Feel free to correct me

Direct to Task Notification could be used for coordination..
Something like this..

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define LED_PIN 2

#define NOTIFY_BIT 1

TaskHandle_t led10TaskHandle = NULL;
TaskHandle_t led01TaskHandle = NULL;


void setup()
{
  Serial.begin(115200);
  Serial.println("ready");
  //onboard led..
  pinMode(LED_PIN, OUTPUT);

  xTaskCreate(
    led10Task,
    "LED10 Task",
    1000,
    NULL,
    0,
    &led10TaskHandle
  );

  xTaskCreate(
    led01Task,
    "LED01 Task",
    1000,
    NULL,
    1,
    &led01TaskHandle
  );

  Serial.println("Setup completed.");
  //trigger task led01 to start..
  xTaskNotify( led01TaskHandle,
                      NOTIFY_BIT,
                      eSetBits);


}

void loop()
{
  delay(1);
}

void led10Task(void * parameter)
{
  const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 3000 );
  BaseType_t xResult;
  uint32_t ulNotifiedValue;
  unsigned long lastBlink;
  int intervalBlink = 500;
  byte blinkTimes = 0;

  while (1)
  {
    // Wait to be notified..
    xResult = xTaskNotifyWait( pdFALSE,    /* Don't clear bits on entry. */
                               ULONG_MAX,        /* Clear all bits on exit. */
                               &ulNotifiedValue, /* Stores the notified value. */
                               xMaxBlockTime );

    if ( xResult == pdPASS )
    {
      Serial.println("Task10 triggered..");
      //do our work..
      intervalBlink = 1000;
      blinkTimes = 0;
      while (blinkTimes < 7) {
        if (millis() - lastBlink >= intervalBlink) {
          lastBlink = millis();
          blinkTimes++;
          digitalWrite(LED_PIN, !digitalRead(LED_PIN));
        }
      }

      //notify led01
      xTaskNotify( led01TaskHandle,
                          NOTIFY_BIT,
                          eSetBits);



    }

    vTaskDelay(1);
  }
}

void led01Task(void * parameter)
{
  const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 6000 );
  BaseType_t xResult;
  uint32_t ulNotifiedValue;
  unsigned long lastBlink;
  int intervalBlink = 500;
  byte blinkTimes = 0;
  while (1)
  {


    // Wait to be notified..
    xResult = xTaskNotifyWait( pdFALSE,    /* Don't clear bits on entry. */
                               ULONG_MAX,        /* Clear all bits on exit. */
                               &ulNotifiedValue, /* Stores the notified value. */
                               xMaxBlockTime );

    if ( xResult == pdPASS )
    {
      //do our work..
      Serial.println("Task01 Triggered..");

      intervalBlink = 500;
      blinkTimes = 0;
      while (blinkTimes < 7) {
        if (millis() - lastBlink >= intervalBlink) {
          lastBlink = millis();
          blinkTimes++;
          digitalWrite(LED_PIN, !digitalRead(LED_PIN));
        }
      }

      //notify led10
      xTaskNotify( led10TaskHandle,
                          NOTIFY_BIT,
                          eSetBits);

    }

    vTaskDelay(1);
  }
}

simm..

fun stuff.. ~q

Thankyou will definitely try it tomorrow