esp32 watchdog and cores

Hi,
I made 2 case:

  • first case:
    I have 3 tasks with priority level 10 that are running on core 0.
void Task1code(void * pvParameters) {
	for (;;)
	{
		Serial.print("CODE 1 on core: ");
		Serial.println(xPortGetCoreID());
		vTaskDelay(1);
	}
}

void Task2code(void * pvParameters) {
	for (;;)
	{
		Serial.print("CODE 2 on core: ");
		Serial.println(xPortGetCoreID());
	}
}

void Task3code(void * pvParameters) {
	for (;;)
	{
		Serial.print("CODE 3 on core: ");
		Serial.println(xPortGetCoreID());
	}
}

void setup() {

	Serial.begin(115200);

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

	xTaskCreatePinnedToCore(
		Task2code,				/* Function to implement the task */
		"Task2",				/* Name of the task */
		2048,					/* Stack size in words */
		NULL,					/* Task input parameter */
		10,						/* Priority of the task */
		NULL,					/* Task handle. */
		0);						/* Core where the task should run */

	xTaskCreatePinnedToCore(
		Task3code,				/* Function to implement the task */
		"Task3",				/* Name of the task */
		2048,					/* Stack size in words */
		NULL,					/* Task input parameter */
		10,						/* Priority of the task */
		NULL,					/* Task handle. */
		0);						/* Core where the task should run */
}

// the loop function runs over and over again until power down or reset
void loop() {
	
	//Serial.print("LOOP on core: ");
	//Serial.println(xPortGetCoreID());
	delay(1000);
}

After uploading, the code is running and all 3 task get some time to run and after cca. 5 second the program get restarted because the task IDLE0 did not reset the watchdog in time. Here is the image of the output on the serial monitor.

  • second case:
    I am running the same 3 task on the code 1.
void Task1code(void * pvParameters) {
	for (;;)
	{
		Serial.print("CODE 1 on core: ");
		Serial.println(xPortGetCoreID());
		vTaskDelay(1);
	}
}

void Task2code(void * pvParameters) {
	for (;;)
	{
		Serial.print("CODE 2 on core: ");
		Serial.println(xPortGetCoreID());
	}
}

void Task3code(void * pvParameters) {
	for (;;)
	{
		Serial.print("CODE 3 on core: ");
		Serial.println(xPortGetCoreID());
	}
}

void setup() {

	Serial.begin(115200);

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

	xTaskCreatePinnedToCore(
		Task2code,				/* Function to implement the task */
		"Task2",				/* Name of the task */
		2048,					/* Stack size in words */
		NULL,					/* Task input parameter */
		10,						/* Priority of the task */
		NULL,					/* Task handle. */
		1);						/* Core where the task should run */

	xTaskCreatePinnedToCore(
		Task3code,				/* Function to implement the task */
		"Task3",				/* Name of the task */
		2048,					/* Stack size in words */
		NULL,					/* Task input parameter */
		10,						/* Priority of the task */
		NULL,					/* Task handle. */
		1);						/* Core where the task should run */
}

// the loop function runs over and over again until power down or reset
void loop() {
	
	//Serial.print("LOOP on core: ");
	//Serial.println(xPortGetCoreID());
	delay(1000);
}

The output is now different. Only the first and second task are executed and the program don t restart. It look like the task IDLE1 don t need to reset the watchdog. Here is the output on the serial monitor.

So why the core 0 and core 1 didn t get the same output?

void Task1code(void * pvParameters) {
	for (;;)
	{
		Serial.print("CODE 1 on core: ");
		Serial.println(xPortGetCoreID());
		vTaskDelay(1);
	}
}

void Task2code(void * pvParameters) {
	for (;;)
	{
		Serial.print("CODE 2 on core: ");
		Serial.println(xPortGetCoreID());
	}
}

void Task3code(void * pvParameters) {
	for (;;)
	{
		Serial.print("CODE 3 on core: ");
		Serial.println(xPortGetCoreID());
	}
}

void setup() {

	Serial.begin(115200);

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

	xTaskCreatePinnedToCore(
		Task2code,				/* Function to implement the task */
		"Task2",				/* Name of the task */
		2048,					/* Stack size in words */
		NULL,					/* Task input parameter */
		10,						/* Priority of the task */
		NULL,					/* Task handle. */
		0);						/* Core where the task should run */

	xTaskCreatePinnedToCore(
		Task3code,				/* Function to implement the task */
		"Task3",				/* Name of the task */
		2048,					/* Stack size in words */
		NULL,					/* Task input parameter */
		10,						/* Priority of the task */
		NULL,					/* Task handle. */
		0);						/* Core where the task should run */
}

// the loop function runs over and over again until power down or reset
void loop() {
	
	//Serial.print("LOOP on core: ");
	//Serial.println(xPortGetCoreID());
	delay(1000);
}

You do realize that this task void Task1code(void * pvParameters) and this task void Task2code(void * pvParameters), and this task void Task3code(void * pvParameters) do not have any flow control?

A few moments after this is ran xTaskCreatePinnedToCore(
Task1code, /* Function to implement the task /
"Task1", /
Name of the task /
2048, /
Stack size in words /
NULL, /
Task input parameter /
10, /
Priority of the task /
NULL, /
Task handle. /
0); /
Core where the task should run */
that task has core0 all to its self without any flow control. Thus, that task is running as fast as a 240mHz core can run. Your next thing will be to try to slip another task onto the same core, and then, even a third task.

AND you have code in loop()!!! A major freeRTOS poo!

#include "sdkconfig.h"
#include "esp32/ulp.h"
#include "driver/rtc_io.h"
#include "esp_system.h" //This inclusion configures the peripherals in the ESP system.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include <Adafruit_NeoPixel.h>
#include "AudioAnalyzer.h"
////
/* define event group and event bits */
EventGroupHandle_t eg;
#define evtDo_AudioReadFreq       ( 1 << 0 ) // 1
////
TickType_t xTicksToWait0 = 0;
////
QueueHandle_t xQ_LED_Info;
////
const int NeoPixelPin = 26;
const int LED_COUNT = 24; //total number of leds in the strip
const int NOISE = 10; // noise that you want to chop off
const int SEG = 6; // how many parts you want to separate the led strip into
const int Priority4 = 4;
const int TaskStack40K = 40000;
const int TaskCore1  = 1;
const int TaskCore0 = 0;
const int AudioSampleSize = 6;
const int Brightness = 180;
const int A_D_ConversionBits = 4096; // arduino use 1024, ESP32 use 4096
////
Analyzer Audio = Analyzer( 5, 15, 36 );//Strobe pin ->15  RST pin ->2 Analog Pin ->36
// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
Adafruit_NeoPixel leds = Adafruit_NeoPixel( LED_COUNT, NeoPixelPin, NEO_GRB + NEO_KHZ800 );
////
int FreqVal[7];//create an array to store the value of different freq
////
void ULP_BLINK_RUN(uint32_t us);
////
void setup()
{
  ULP_BLINK_RUN(100000);
  eg = xEventGroupCreate();
  Audio.Init(); // start the audio analyzer
  leds.begin(); // Call this to start up the LED strip.
  clearLEDs();  // This function, defined below, de-energizes all LEDs...
  leds.show();  // ...but the LEDs don't actually update until you call this.
  ////
  xQ_LED_Info = xQueueCreate ( 1, sizeof(FreqVal) );
  //////////////////////////////////////////////////////////////////////////////////////////////
  xTaskCreatePinnedToCore( fDo_AudioReadFreq, "fDo_ AudioReadFreq", TaskStack40K, NULL, Priority4, NULL, TaskCore1 ); //assigned to core
  xTaskCreatePinnedToCore( fDo_LEDs, "fDo_ LEDs", TaskStack40K, NULL, Priority4, NULL, TaskCore0 ); //assigned to core
  xEventGroupSetBits( eg, evtDo_AudioReadFreq );
} // setup()
////
void loop() {} // void loop
////
void fDo_LEDs( void *pvParameters )
{
  int iFreqVal[7];
  int j;
  leds.setBrightness( Brightness ); //  1 = min brightness (off), 255 = max brightness.
  for (;;)
  {
    if (xQueueReceive( xQ_LED_Info, &iFreqVal,  portMAX_DELAY) == pdTRUE)
    {
      j = 0;
      //assign different values for different parts of the led strip
      for (j = 0; j < LED_COUNT; j++)
      {
        if ( (0 <= j) && (j < (LED_COUNT / SEG)) )
        {
          set(j, iFreqVal[0]); // set the color of led
        }
        else if ( ((LED_COUNT / SEG) <= j) && (j < (LED_COUNT / SEG * 2)) )
        {
          set(j, iFreqVal[1]); //orginal code
        }
        else if ( ((LED_COUNT / SEG * 2) <= j) && (j < (LED_COUNT / SEG * 3)) )
        {
          set(j, iFreqVal[2]);
        }
        else if ( ((LED_COUNT / SEG * 3) <= j) && (j < (LED_COUNT / SEG * 4)) )
        {
          set(j, iFreqVal[3]);
        }
        else if ( ((LED_COUNT / SEG * 4) <= j) && (j < (LED_COUNT / SEG * 5)) )
        {
          set(j, iFreqVal[4]);
        }
        else
        {
          set(j, iFreqVal[5]);
        }
      }
      leds.show();
    }
    xEventGroupSetBits( eg, evtDo_AudioReadFreq );
  }
  vTaskDelete( NULL );
} // void fDo_ LEDs( void *pvParameters )
////
void fDo_AudioReadFreq( void *pvParameters )
{
  int64_t EndTime = esp_timer_get_time();
  int64_t StartTime = esp_timer_get_time(); //gets time in uSeconds like Arduino Micros
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDo_AudioReadFreq, pdTRUE, pdTRUE, portMAX_DELAY);
    EndTime = esp_timer_get_time() - StartTime;
    // log_i( "TimeSpentOnTasks: %d", EndTime );
    Audio.ReadFreq(FreqVal);
    for (int i = 0; i < 7; i++)
    {
      FreqVal[i] = constrain( FreqVal[i], NOISE, A_D_ConversionBits );
      FreqVal[i] = map( FreqVal[i], NOISE, A_D_ConversionBits, 0, 255 );
      // log_i( "Freq %d Value: %d", i, FreqVal[i]);//used for debugging and Freq choosing
    }
    xQueueSend( xQ_LED_Info, ( void * ) &FreqVal, xTicksToWait0 );
    StartTime = esp_timer_get_time();
  }
  vTaskDelete( NULL );
} // fDo_ AudioReadFreq( void *pvParameters )
////
//the following function set the led color based on its position and freq value
//
void set(byte position, int value)
{
  // segment 0, red
  if ( (0 <= position) && (position < LED_COUNT / SEG) ) // segment 0 (bottom to top), red
  {
    if ( value == 0 )
    {
      leds.setPixelColor( position, 0, 0, 0 );
    }
    else
    {
      // increase light output of a low number
      // value += 10;
      // value = constrain( value, 0, 255 ); // keep raised value within limits
      leds.setPixelColor( position, leds.Color( value , 0, 0) );
    }
  }
  else if ( (LED_COUNT / SEG <= position) && (position < LED_COUNT / SEG * 2) ) // segment 1 yellow
  {
    if ( value == 0 )
    {
      leds.setPixelColor(position, leds.Color(0, 0, 0));
    }
    else
    {
      leds.setPixelColor(position, leds.Color( value, value, 0)); // works better to make yellow
    }
  }
  else if ( (LED_COUNT / SEG * 2 <= position) && (position < LED_COUNT / SEG * 3) ) // segment 2 pink
  {
    if ( value == 0 )
    {
      leds.setPixelColor(position, leds.Color(0, 0, 0));
    }
    else
    {
      leds.setPixelColor(position, leds.Color( value, 0, value * .91) ); // pink
    }
  }
  else if ( (LED_COUNT / SEG * 3 <= position) && (position < LED_COUNT / SEG * 4) ) // seg 3, green
  {
    if ( value == 0 )
    {
      leds.setPixelColor(position, leds.Color( 0, 0, 0));
    }
    else //
    {
      leds.setPixelColor( position, leds.Color( 0, value, 0) ); //
    }
  }
  else if ( (LED_COUNT / SEG * 4 <= position) && (position < LED_COUNT / SEG * 5) ) // segment 4, leds.color( R, G, B ), blue
  {
    if ( value == 0 )
    {
      leds.setPixelColor(position, leds.Color( 0, 0, 0));
    }
    else //
    {
      leds.setPixelColor(position, leds.Color( 0, 0, value) ); // blue
    }
  }
  else // segment 5
  {
    if ( value == 0 )
    {
      leds.setPixelColor(position, leds.Color( 0, 0, 0)); // only helps a little bit in turning the leds off
    }
    else
    {
      leds.setPixelColor( position, leds.Color( value, value * .3, 0) ); // orange
    }
  }
} // void set(byte position, int value)
////
void clearLEDs()
{
  for (int i = 0; i < LED_COUNT; i++)
  {
    leds.setPixelColor(i, 0);
  }
} // void clearLEDs()
//////////////////////////////////////////////
/*
  Each I_XXX preprocessor define translates into a single 32-bit instruction. So you can count instructions to learn which memory address are used and where the free mem space starts.

  To generate branch instructions, special M_ preprocessor defines are used. M_LABEL define can be used to define a branch target.
  Implementation note: these M_ preprocessor defines will be translated into two ulp_insn_t values: one is a token value which contains label number, and the other is the actual instruction.

*/
void ULP_BLINK_RUN(uint32_t us)
{
  size_t load_addr = 0;
  RTC_SLOW_MEM[12] = 0;
  ulp_set_wakeup_period(0, us);
  const ulp_insn_t  ulp_blink[] =
  {
    I_MOVI(R3, 12),                         // #12 -> R3
    I_LD(R0, R3, 0),                        // R0 = RTC_SLOW_MEM[R3(#12)]
    M_BL(1, 1),                             // GOTO M_LABEL(1) IF R0 < 1
    I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 1),  // RTC_GPIO2 = 1
    I_SUBI(R0, R0, 1),                      // R0 = R0 - 1, R0 = 1, R0 = 0
    I_ST(R0, R3, 0),                        // RTC_SLOW_MEM[R3(#12)] = R0
    M_BX(2),                                // GOTO M_LABEL(2)
    M_LABEL(1),                             // M_LABEL(1)
    I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 0),// RTC_GPIO2 = 0
    I_ADDI(R0, R0, 1),                    // R0 = R0 + 1, R0 = 0, R0 = 1
    I_ST(R0, R3, 0),                      // RTC_SLOW_MEM[R3(#12)] = R0
    M_LABEL(2),                             // M_LABEL(2)
    I_HALT()                                // HALT COPROCESSOR
  };
  const gpio_num_t led_gpios[] =
  {
    GPIO_NUM_2,
    // GPIO_NUM_0,
    // GPIO_NUM_4
  };
  for (size_t i = 0; i < sizeof(led_gpios) / sizeof(led_gpios[0]); ++i) {
    rtc_gpio_init(led_gpios[i]);
    rtc_gpio_set_direction(led_gpios[i], RTC_GPIO_MODE_OUTPUT_ONLY);
    rtc_gpio_set_level(led_gpios[i], 0);
  }
  size_t size = sizeof(ulp_blink) / sizeof(ulp_insn_t);
  ulp_process_macros_and_load( load_addr, ulp_blink, &size);
  ulp_run( load_addr );
} // void ULP_BLINK_RUN(uint32_t us)
//////////////////////////////////////////////

Each task is running on its own core.

The tasks have flow control.

Total time of both tasks completing their functions is 779uS.

loop() runs with a priority of 1, under freeRTOS on an ESP32 in the Arduion IDE.

Using freeRTOS on an ESP32 under the Arduino IDE (core), loop() is assigned to do cleanup and 'any' other interruption in loop() can cause faluts; like code being ran in loop().

I've never used more then priority 5.

setup() and loop() both run on core1.

Oh, that code, I posted above, uses three ESP32 cores.

Thanks for your reply and for the example code!

I made that tasks without flow control on purpose because I am in a learning process on how exactly to use freeRTOS on esp32 through Arduino IDE and how it is working. I read ESP-IDF Programming Guide but sill have doubts on some topics and on internet I find only basics stuff.

Oh yes I forgot that setup() is running on core 1 so if i run all tasks on core 1 it will never start all tasks because it have lover priority. But still don t understand why the watchdog didn t triggered.

You sad it is not good to have code in setup()? Where should it be? You too have code in setup().

You said that you never use more than priority 5. What is the difference if i use priority 5, 4, 3 or 50, 40, 30? The handler will switch to the task that is available and have the highest priority so it make no difference.

falseAlarm:
You sad it is not good to have code in setup()? Where should it be? You too have code in setup().

You said that you never use more than priority 5. What is the difference if i use priority 5, 4, 3 or 50, 40, 30? The handler will switch to the task that is available and have the highest priority so it make no difference.

Oops, I meant loop(), fixed my error

Use what ever you want for priorities, it will be a good lesson.