FreeRTOS task priority

I want to run FreeRTOS on ESP32. I started to study FreeRTOS book. I am getting confused at some places.

Scheduler decide which task that should execute at this moment. It determines what process to run next based on priority

Consider three tasks T1,T2 and T3 with 1, 2, and 3 priority

  1. It is my understanding that T3 has highest priority so it will always run on ESP. So no other task will run on ESP.

Consider three tasks T1,T2 and T3 with same priority

  1. When all task are at same priority Does scheduler execute tasks in way of round robin style It means T3 will run first and then T2 run and Then T1 run

Incorrect.

If T3 is running and T1 puts in a run task event and T1 gets done then before rerunning T3, T3 will be serviced.

Or.

T1 will get more of the CPU time slice and the other 2 tasks will get less CPU time slice. Remember freeRTOS is also a state machine running a part of each task code as the switch is made from task to task.

Do not put code in the loop() when using freeRTOS. loop() is the only task that is guaranteed to not be ran per tasking iteration.

If task 1 is created first, task 1 will start to run first. If task 2 is created 2nd then task 2 will run 2nd. If task 3 is created 3rd it will run 3rd. As you may start to see each task did not start at the same time giving them slightly different task start times and end times. Which gives freeRTOS a way to break the tie.

/*
   Chappie Weather upgrade/addition
   process wind speed direction and rain fall.
*/
#include "esp32/ulp.h"
//#include "ulptool.h"
#include "driver/rtc_io.h"
#include <WiFi.h>
#include <PubSubClient.h>
#include "certs.h"
#include "sdkconfig.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include "driver/pcnt.h"
#include <driver/adc.h>
#include <SimpleKalmanFilter.h>
#include <ESP32Time.h>
////
ESP32Time rtc;
WiFiClient wifiClient;
PubSubClient MQTTclient(mqtt_server, mqtt_port, wifiClient);
////
float CalculatedVoltage = 0.0f;
float kph = 0.0f;
float rain  = 0.0f;
/*
   PCNT PCNT_UNIT_0, PCNT_CHANNEL_0 GPIO_NUM_15 = pulse input pin
   PCNT PCNT_UNIT_1, PCNT_CHANNEL_0 GPIO_NUM_4 = pulse input pin
*/
pcnt_unit_t pcnt_unit00 = PCNT_UNIT_0; //pcnt unit 0 channel 0
pcnt_unit_t pcnt_unit10 = PCNT_UNIT_1; //pcnt unit 1 channel 0
//
//
hw_timer_t * timer = NULL;
//
#define evtAnemometer  ( 1 << 0 )
#define evtRainFall    ( 1 << 1 )
#define evtParseMQTT   ( 1 << 2 )
EventGroupHandle_t eg;
#define OneMinuteGroup ( evtAnemometer | evtRainFall )
////
QueueHandle_t xQ_Message; // payload and topic queue of MQTT payload and topic
const int payloadSize = 100;
struct stu_message
{
  char payload [payloadSize] = {'\0'};
  String topic ;
} x_message;
////
SemaphoreHandle_t sema_MQTT_KeepAlive;
SemaphoreHandle_t sema_mqttOK;
SemaphoreHandle_t sema_CalculatedVoltage;
////
int mqttOK = 0; // stores a count value that is used to cause an esp reset
volatile bool TimeSet = false;
////
/*
   A single subject has been subscribed to, the mqtt broker sends out "OK" messages if the client receives an OK message the mqttOK value is set back to zero.
*/
////
void IRAM_ATTR mqttCallback(char* topic, byte * payload, unsigned int length)
{
  memset( x_message.payload, '\0', payloadSize ); // clear payload char buffer
  x_message.topic = ""; //clear topic string buffer
  x_message.topic = topic; //store new topic
  int i = 0; // extract payload
  for ( i; i < length; i++)
  {
    x_message.payload[i] = ((char)payload[i]);
  }
  x_message.payload[i] = '\0';
  xQueueOverwrite( xQ_Message, (void *) &x_message );// send data to queue
} // void mqttCallback(char* topic, byte* payload, unsigned int length)
////
// interrupt service routine for WiFi events put into IRAM
void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
{
  switch (event) {
    case SYSTEM_EVENT_STA_CONNECTED:
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      log_i("Disconnected from WiFi access point");
      break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
      log_i("WiFi client disconnected");
      break;
    default: break;
  }
} // void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
////
void IRAM_ATTR onTimer()
{
  BaseType_t xHigherPriorityTaskWoken;
  xEventGroupSetBitsFromISR(eg, OneMinuteGroup, &xHigherPriorityTaskWoken);
} // void IRAM_ATTR onTimer()
////
void setup()
{
  eg = xEventGroupCreate(); // get an event group handle
  x_message.topic.reserve(100);
  adc1_config_width(ADC_WIDTH_12Bit);
  adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);// using GPIO 34 wind direction
  adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_11);// using GPIO 39 current
  adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);// using GPIO 36 battery volts

  // hardware timer 4 set for one minute alarm
  timer = timerBegin( 3, 80, true );
  timerAttachInterrupt( timer, &onTimer, true );
  timerAlarmWrite(timer, 60000000, true);
  timerAlarmEnable(timer);
  /* Initialize PCNT's counter */
  int PCNT_H_LIM_VAL         = 3000;
  int PCNT_L_LIM_VAL         = -10;
  // 1st PCNT counter
  pcnt_config_t pcnt_config  = {};
  pcnt_config.pulse_gpio_num = GPIO_NUM_15;// Set PCNT input signal and control GPIOs
  pcnt_config.ctrl_gpio_num  = PCNT_PIN_NOT_USED;
  pcnt_config.channel        = PCNT_CHANNEL_0;
  pcnt_config.unit           = PCNT_UNIT_0;
  // What to do on the positive / negative edge of pulse input?
  pcnt_config.pos_mode       = PCNT_COUNT_INC;   // Count up on the positive edge
  pcnt_config.neg_mode       = PCNT_COUNT_DIS;   // Count down disable
  // What to do when control input is low or high?
  pcnt_config.lctrl_mode     = PCNT_MODE_KEEP; // do not count if low reverse
  pcnt_config.hctrl_mode     = PCNT_MODE_KEEP;    // Keep the primary counter mode if high
  // Set the maximum and minimum limit values to watch
  pcnt_config.counter_h_lim  = PCNT_H_LIM_VAL;
  pcnt_config.counter_l_lim  = PCNT_L_LIM_VAL;
  pcnt_unit_config(&pcnt_config); // Initialize PCNT unit
  pcnt_set_filter_value( PCNT_UNIT_0, 1); //Configure and enable the input filter
  pcnt_filter_enable( PCNT_UNIT_0 );
  pcnt_counter_pause( PCNT_UNIT_0 );
  pcnt_counter_clear( PCNT_UNIT_0 );
  pcnt_counter_resume( PCNT_UNIT_0); // start the show
  // setup 2nd PCNT
  pcnt_config = {};
  pcnt_config.pulse_gpio_num = GPIO_NUM_4;
  pcnt_config.ctrl_gpio_num  = PCNT_PIN_NOT_USED;
  pcnt_config.channel        = PCNT_CHANNEL_0;
  pcnt_config.unit           = PCNT_UNIT_1;
  pcnt_config.pos_mode       = PCNT_COUNT_INC;
  pcnt_config.neg_mode       = PCNT_COUNT_DIS;
  pcnt_config.lctrl_mode     = PCNT_MODE_KEEP;
  pcnt_config.hctrl_mode     = PCNT_MODE_KEEP;
  pcnt_config.counter_h_lim  = PCNT_H_LIM_VAL;
  pcnt_config.counter_l_lim  = PCNT_L_LIM_VAL;
  pcnt_unit_config(&pcnt_config);
  //pcnt_set_filter_value( PCNT_UNIT_1, 1 );
  //pcnt_filter_enable  ( PCNT_UNIT_1 );
  pcnt_counter_pause  ( PCNT_UNIT_1 );
  pcnt_counter_clear  ( PCNT_UNIT_1 );
  pcnt_counter_resume ( PCNT_UNIT_1 );
  //
  xQ_Message = xQueueCreate( 1, sizeof(stu_message) );
  //
  sema_CalculatedVoltage = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_CalculatedVoltage );
  sema_mqttOK = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_mqttOK );
  sema_MQTT_KeepAlive = xSemaphoreCreateBinary();
  ///
  xTaskCreatePinnedToCore( MQTTkeepalive, "MQTTkeepalive", 15000, NULL, 5, NULL, 1 );
  xTaskCreatePinnedToCore( fparseMQTT, "fparseMQTT", 10000, NULL, 5, NULL, 1 ); // assign all to core 1, WiFi in use.
  xTaskCreatePinnedToCore( fReadBattery, "fReadBattery", 4000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fReadCurrent, "fReadCurrent", 4000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fWindDirection, "fWindDirection", 10000, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( fAnemometer, "fAnemometer", 10000, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( fRainFall, "fRainFall", 10000, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( fmqttWatchDog, "fmqttWatchDog", 3000, NULL, 3, NULL, 1 ); // assign all to core 1
} //void setup()
static void init_ulp_program()
{
//  esp_err_t err = ulp_load_binary(0, ulp_main_bin_start,
//                                  (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
//  ESP_ERROR_CHECK(err);
//  //
//  rtc_gpio_init(ulp_27);
//  rtc_gpio_set_direction(ulp_27, RTC_GPIO_MODE_OUTPUT_ONLY);
//  //
//  rtc_gpio_init(ulp_13);
//  rtc_gpio_set_direction(ulp_13, RTC_GPIO_MODE_OUTPUT_ONLY);
//  /* Configure ADC channel */
//  /* Note: when changing channel here, also change 'adc_channel' constant
//     in adc.S */
//  adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11);
//  adc1_config_width(ADC_WIDTH_BIT_12);
//  adc1_ulp_enable();
//  ulp_low_threshold = 1 * (4095 / 3.3);  //1 volt
//  ulp_high_threshold = 2 * (4095 / 3.3);   // 2 volts
//
//  /* Set ULP wake up period to 100ms */
//  ulp_set_wakeup_period(0, 100 * 1000);
  /* Disable pullup on GPIO15, in case it is connected to ground to suppress
     boot messages.
  */
  //  rtc_gpio_pullup_dis(GPIO_NUM_15);
  //  rtc_gpio_hold_en(GPIO_NUM_15);
}
////
void fWindDirection( void *pvParameters )
// read the wind direction sensor, return heading in degrees
{
  float adcValue = 0.0f;
  uint64_t TimePastKalman  = esp_timer_get_time();
  SimpleKalmanFilter KF_ADC( 1.0f, 1.0f, .01f );
  float high = 0.0f;
  float low = 2000.0f;
  float ADscale = 3.3f / 4096.0f;
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 100; //delay for mS
  int count = 0;
  String windDirection;
  windDirection.reserve(20);
  String MQTTinfo = "";
  MQTTinfo.reserve( 150 );
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 250 );
  }
  for (;;)
  {
    windDirection = "";
    adcValue = float( adc1_get_raw(ADC1_CHANNEL_6) ); //take a raw ADC reading
    KF_ADC.setProcessNoise( (esp_timer_get_time() - TimePastKalman) / 1000000.0f ); //get time, in microsecods, since last readings
    adcValue = KF_ADC.updateEstimate( adcValue ); // apply simple Kalman filter
    TimePastKalman = esp_timer_get_time(); // time of update complete
    adcValue = adcValue * ADscale;
    if ( (adcValue >= 0.0f) & (adcValue <= .25f )  )
    {
      // log_i( " n" );
      windDirection.concat( "N" );
    }
    if ( (adcValue > .25f) & (adcValue <= .6f ) )
    {
      //  log_i( " e" );
      windDirection.concat( "E" );
    }
    if ( (adcValue > 2.0f) & ( adcValue < 3.3f) )
    {
      //   log_i( " s" );
      windDirection.concat( "S");
    }
    if ( (adcValue >= 1.7f) & (adcValue < 2.0f ) )
    {
      // log_i( " w" );
      windDirection.concat( "W" );
    }
    if ( count >= 30 )
    {
      MQTTinfo.concat( String(kph, 2) );
      MQTTinfo.concat( ",");
      MQTTinfo.concat( windDirection );
      MQTTinfo.concat( ",");
      MQTTinfo.concat( String(rain, 2) );
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY );
      MQTTclient.publish( topicWSWDRF, MQTTinfo.c_str() );
      xSemaphoreGive( sema_MQTT_KeepAlive );
      count = 0;
    }
    count++;
    MQTTinfo = "";
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
  }
  vTaskDelete ( NULL );
}
// read rainfall
void fRainFall( void *pvParemeters )
{
  int16_t count = 0;
  pcnt_counter_pause( PCNT_UNIT_1 );
  pcnt_counter_clear( PCNT_UNIT_1 );
  pcnt_counter_resume( PCNT_UNIT_1 );
  for  (;;)
  {
    xEventGroupWaitBits (eg, evtRainFall, pdTRUE, pdTRUE, portMAX_DELAY);
    pcnt_counter_pause( PCNT_UNIT_1 );
    pcnt_get_counter_value( PCNT_UNIT_1, &count );
    pcnt_counter_clear( PCNT_UNIT_1 );
    pcnt_counter_resume( PCNT_UNIT_1 );
    if ( count != 0 )
    {
      // 0.2794mm of rain per click clear clicks at mid night
      rain = 0.2794f * (float)count;
      //log_i( "count %d, rain rain = %f mm", count, rain );
    }
    if ( (rtc.getHour(true) == 0) && (rtc.getMinute() == 0) )
    {
      pcnt_counter_pause( PCNT_UNIT_1 );
      rain = 0.0;
      count = 0;
      pcnt_counter_clear( PCNT_UNIT_1 );
      pcnt_counter_resume( PCNT_UNIT_1 );
    }
  }
  vTaskDelete ( NULL );
}
////
void fAnemometer( void *pvParameters )
{
  int16_t count = 0;
  pcnt_counter_clear(PCNT_UNIT_0);
  pcnt_counter_resume(PCNT_UNIT_0);
  for (;;)
  {
    xEventGroupWaitBits (eg, evtAnemometer, pdTRUE, pdTRUE, portMAX_DELAY);
    pcnt_counter_pause( PCNT_UNIT_0 );
    pcnt_get_counter_value( PCNT_UNIT_0, &count); //int16_t *count
    // A wind speed of 2.4km/h causes the switch to close once per second
    kph = 2.4 * ((float)count / 60.0f);
    /*
      if ( count != 0 )
      {
        log_i( "count %d, wind KPH = %f", count, kph );
      }
    */
    count = 0;
    pcnt_counter_clear( PCNT_UNIT_0 );
    pcnt_counter_resume( PCNT_UNIT_0 );
  }
  vTaskDelete ( NULL );
}
//////
void fmqttWatchDog( void * paramater )
{
  int UpdateImeTrigger = 86400; //seconds in a day
  int UpdateTimeInterval = 86300; // 1st time update in 100 counts
  int maxNonMQTTresponse = 60;
  for (;;)
  {
    vTaskDelay( 1000 );
    if ( mqttOK >= maxNonMQTTresponse )
    {
      ESP.restart();
    }
    xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
    mqttOK++;
    xSemaphoreGive( sema_mqttOK );
    UpdateTimeInterval++; // trigger new time get
    if ( UpdateTimeInterval >= UpdateImeTrigger )
    {
      TimeSet = false; // sets doneTime to false to get an updated time after a days count of seconds
      UpdateTimeInterval = 0;
    }
  }
  vTaskDelete( NULL );
}
//////
void fparseMQTT( void *pvParameters )
{
  struct stu_message px_message;
  for (;;)
  {
    if ( xQueueReceive(xQ_Message, &px_message, portMAX_DELAY) == pdTRUE )
    {
      // parse the time from the OK message and update MCU time
      if ( String(px_message.topic) == topicOK )
      {
        if ( !TimeSet)
        {
          String temp = "";
          temp =  px_message.payload[0];
          temp += px_message.payload[1];
          temp += px_message.payload[2];
          temp += px_message.payload[3];
          int year =  temp.toInt();
          temp = "";
          temp =  px_message.payload[5];
          temp += px_message.payload[6];
          int month =  temp.toInt();
          temp =  "";
          temp =  px_message.payload[8];
          temp += px_message.payload[9];
          int day =  temp.toInt();
          temp = "";
          temp = px_message.payload[11];
          temp += px_message.payload[12];
          int hour =  temp.toInt();
          temp = "";
          temp = px_message.payload[14];
          temp += px_message.payload[15];
          int min =  temp.toInt();
          rtc.setTime( 0, min, hour, day, month, year );
          log_i( "rtc  %s ", rtc.getTime() );
          TimeSet = true;
        }
      }
      //
    } //if ( xQueueReceive(xQ_Message, &px_message, portMAX_DELAY) == pdTRUE )
    xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
    mqttOK = 0;
    xSemaphoreGive( sema_mqttOK );
  }
} // void fparseMQTT( void *pvParameters )#include <ESP32Time.h>
//////
void fReadCurrent( void * parameter )
{
  float ADbits = 4096.0f;
  float ref_voltage = 3.3f;
  float offSET = .0f;
  uint64_t TimePastKalman  = esp_timer_get_time(); // used by the Kalman filter UpdateProcessNoise, time since last kalman calculation
  SimpleKalmanFilter KF_I( 1.0f, 1.0f, .01f );
  float mA = 0.0f;
  int   printCount = 0;
  /*
     185mv/A = 5 AMP MODULE
     100mv/A = 20 amp module
     66mv/A = 30 amp module
  */
  const float mVperAmp = 185.0f;
  float adcValue = 0;
  float Voltage = 0;
  float Power = 0.0;
  String powerInfo = "";
  powerInfo.reserve( 150 );
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 250 );
  }
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 1000; //delay for mS
  for (;;)
  {
    adc1_get_raw(ADC1_CHANNEL_3); // read once discard reading
    adcValue = ( (float)adc1_get_raw(ADC1_CHANNEL_3) );
    //log_i( "adcValue I = %f", adcValue );
    Voltage = ( (adcValue * ref_voltage) / ADbits ) + offSET; // Gets you mV
    mA = Voltage / mVperAmp; // get amps
    KF_I.setProcessNoise( (esp_timer_get_time() - TimePastKalman) / 1000000.0f ); //get time, in microsecods, since last readings
    mA = KF_I.updateEstimate( mA ); // apply simple Kalman filter
    TimePastKalman = esp_timer_get_time(); // time of update complete
    printCount++;
    if ( printCount == 60 )
    {
      xSemaphoreTake( sema_CalculatedVoltage, portMAX_DELAY);
      Power = CalculatedVoltage * mA;
      log_i( "Voltage=%f mA=%f Power=%f", CalculatedVoltage, mA, Power );
      printCount = 0;
      powerInfo.concat( String(CalculatedVoltage, 2) );
      xSemaphoreGive( sema_CalculatedVoltage );
      powerInfo.concat( ",");
      powerInfo.concat( String(mA, 4) );
      powerInfo.concat( ",");
      powerInfo.concat( String(Power, 4) );
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY );
      MQTTclient.publish( topicPower, powerInfo.c_str() );
      xSemaphoreGive( sema_MQTT_KeepAlive );
      powerInfo = "";
    }
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
  }
  vTaskDelete( NULL );
} //void fReadCurrent( void * parameter )
////
void fReadBattery( void * parameter )
{
  //float ADbits = 4096.0f;
  //float ref_voltage = 3.3f;
  float ADscale = 3.3f / 4096.0f;
  float adcValue = 0.0f;
  float offSET = 0.0f;
  const float r1 = 50500.0f; // R1 in ohm, 50K
  const float r2 = 10000.0f; // R2 in ohm, 10k potentiometer
  //float Vscale = (r1+r2)/r2;
  float Vbatt = 0.0f;
  int printCount = 0;
  float vRefScale = (3.3f / 4096.0f) * ((r1 + r2) / r2);
  uint64_t TimePastKalman  = esp_timer_get_time(); // used by the Kalman filter UpdateProcessNoise, time since last kalman calculation
  SimpleKalmanFilter KF_ADC_b( 1.0f, 1.0f, .01f );
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 1000; //delay for mS
  for (;;)
  {
    adc1_get_raw(ADC1_CHANNEL_0); //read and discard
    adcValue = float( adc1_get_raw(ADC1_CHANNEL_0) ); //take a raw ADC reading
    KF_ADC_b.setProcessNoise( (esp_timer_get_time() - TimePastKalman) / 1000000.0f ); //get time, in microsecods, since last readings
    adcValue = KF_ADC_b.updateEstimate( adcValue ); // apply simple Kalman filter
    Vbatt = adcValue * vRefScale;
    xSemaphoreTake( sema_CalculatedVoltage, portMAX_DELAY );
    CalculatedVoltage = Vbatt;
    xSemaphoreGive( sema_CalculatedVoltage );
    /*
      printCount++;
      if ( printCount == 3 )
      {
      log_i( "Vbatt %f", Vbatt );
      printCount = 0;
      }
    */
    TimePastKalman = esp_timer_get_time(); // time of update complete
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
    //log_i( "fReadBattery %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
}
////
void MQTTkeepalive( void *pvParameters )
{
  sema_MQTT_KeepAlive   = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_MQTT_KeepAlive ); // found keep alive can mess with a publish, stop keep alive during publish
  // setting must be set before a mqtt connection is made
  MQTTclient.setKeepAlive( 90 ); // setting keep alive to 90 seconds makes for a very reliable connection, must be set before the 1st connection is made.
  for (;;)
  {
    //check for a is-connected and if the WiFi 'thinks' its connected, found checking on both is more realible than just a single check
    if ( (wifiClient.connected()) && (WiFi.status() == WL_CONNECTED) )
    {
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); // whiles MQTTlient.loop() is running no other mqtt operations should be in process
      MQTTclient.loop();
      xSemaphoreGive( sema_MQTT_KeepAlive );
    }
    else {
      log_i( "MQTT keep alive found MQTT status %s WiFi status %s", String(wifiClient.connected()), String(WiFi.status()) );
      if ( !(wifiClient.connected()) || !(WiFi.status() == WL_CONNECTED) )
      {
        connectToWiFi();
      }
      connectToMQTT();
    }
    vTaskDelay( 250 ); //task runs approx every 250 mS
  }
  vTaskDelete ( NULL );
}
////
void connectToWiFi()
{
  int TryCount = 0;
  while ( WiFi.status() != WL_CONNECTED )
  {
    TryCount++;
    WiFi.disconnect();
    WiFi.begin( SSID, PASSWORD );
    vTaskDelay( 4000 );
    if ( TryCount == 10 )
    {
      ESP.restart();
    }
  }
  WiFi.onEvent( WiFiEvent );
} // void connectToWiFi()
////
void connectToMQTT()
{
  MQTTclient.setKeepAlive( 90 ); // needs be made before connecting
  byte mac[5];
  WiFi.macAddress(mac);
  String clientID = String(mac[0]) + String(mac[4]) ; // use mac address to create clientID
  while ( !MQTTclient.connected() )
  {
    // boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
    MQTTclient.connect( clientID.c_str(), mqtt_username, mqtt_password, NULL , 1, true, NULL );
    vTaskDelay( 250 );
  }
  MQTTclient.setCallback( mqttCallback );
  MQTTclient.subscribe( topicOK );
} // void connectToMQTT()
////
void loop() {}

See how several of the task have the same priority.
xTaskCreatePinnedToCore( MQTTkeepalive, "MQTTkeepalive", 15000, NULL, 5, NULL, 1 );
xTaskCreatePinnedToCore( fparseMQTT, "fparseMQTT", 10000, NULL, 5, NULL, 1 ); // assign all to core 1, WiFi in use.
xTaskCreatePinnedToCore( fReadBattery, "fReadBattery", 4000, NULL, 3, NULL, 1 );
xTaskCreatePinnedToCore( fReadCurrent, "fReadCurrent", 4000, NULL, 3, NULL, 1 );
xTaskCreatePinnedToCore( fWindDirection, "fWindDirection", 10000, NULL, 4, NULL, 1 );
xTaskCreatePinnedToCore( fAnemometer, "fAnemometer", 10000, NULL, 4, NULL, 1 );
xTaskCreatePinnedToCore( fRainFall, "fRainFall", 10000, NULL, 4, NULL, 1 );
xTaskCreatePinnedToCore( fmqttWatchDog, "fmqttWatchDog", 3000, NULL, 3, NULL, 1 ); // assign all to core 1

I have written test program to check how task's execute. I have uploaded sketch but I have no idea why I don't get any message on terminal
selected baud 115200

/* Include FreeRTOS APIs and defines */
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void setup() 
{
   Serial.begin(115200);

/*For creating Task, xTaskCreate() API is called in setup function with certain parameters/arguments.*/
  xTaskCreate(
    Task1,          /*Task1 function */
    "Task1",        /*Task name - Task1*/
    1000,           /* Task stack, allocated from heap */
    NULL,           /* No param passed to task function */
    1,              /* Priority 1*/
    NULL);          /* Task handle */
  
  xTaskCreate(
    Task2,         /* Task2 function */
    "Task2",       /*Task name - Task2*/
    1000,          /*Task stack, allocated from heap */
    NULL,          /* No param passed to task function */
    2,              /* Priority 2*/                
    NULL);         /* Task handle */
}

void Task1( void * parameter )
{
  for(;;)
  {
      Serial.print("Task1 is running on core ");
  } 
}

void Task2( void * parameter )
{
  for(;;)
  {
     Serial.print("Task2 is running on core ");

  } 
}
void loop() {}

Of course the 2 tasks will not print, without a control mechanism to allow Serial.print to complete.

One will find that with the ESP32 there are times where Serial.print does not work as good as log_x where x can be 'n', 'i', 'd', or 'v' for the various Arduino IDE print levels. Serial print is slow, log_x is faster but has its limitations under the Arduino IDE. For troubleshooting I prefer to use log_x except for a few situations like plotting and finding some Guru Meditation errors.

True. But, a well-designed process model will have T3 eventually suspending itself. Perhaps pending for something to happen like a external interrupt, data entering a queue, a specific Tick Time, a Task Notification, etc. It could even be a simple vTaskDelay(). When T3 suspends, a lower-priority will run, assuming it's in the read to run state. It's YOUR job to make sure this happens.

True, assuming the mode is "Prioritized Pre-emptive Scheduling with Time Slicing".

This is a good tutorial: https://freertos.org/fr-content-src/uploads/2018/07/161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf

In generalized RTOS theory, a task only runs when TWO things are true:

  1. It has something that it can do.
  2. It is the highest priority task that has something to do.

I have no idea whether FreeRTOS on ESP32 behaves well with respect to that theory. Sometimes a "simplification" is "the task runs every time through the scheduler, and then IT decides whether it has anything to do. (you shouldn't do that with high priority tasks!)

I'm not sure why you dual serial tasks don't work. In theory:
The first task runs until the Serial Buffer is full (or until timeslice expires.) When the buffer is full, it has no more to do, so it "suspends", and is no longer "runnable."
The second task runs until ITS Serial buffer is full (Hmm: Same buffer, since it's the same serial port?)
When the buffer is emptied (by ISRs?) it would put the tasks waiting for room in the buffer into the "runnable" state, Next pass through the scheduler, it would consider running any tasks that were "waiting" for that to happen.

Now, the ESP32 is a complicated chip, and it's possible that Serial.print() requires some additional task (TCP? USB?) to run before it can complete, and that somehow those tasks are prevented from running by the user tasks...

And it's possible that Serial.print() is incorrectly (not not-at-all) FreeRTOS-aware, causing something else to go wrong.

What do you expect this code to do?

[quote="Idahowalker, post:4, topic:908884"]
Of course the 2 tasks will not print, without a control mechanism to allow Serial.print to complete.[/quote]
I couldn't figure out why task not printing on terminal. so I am checking code with two led's I don't understand why only task 1 run and why doesn't task 2 run while it has highest priority. if I change priority or keep the same priority still only task 1 is running

/* Include FreeRTOS APIs and defines */
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"

const int LED_1 = 22; //Pin of the LED_1
const int LED_2 = 25; //Pin of the LED_2

void setup()
{
   Serial.begin(115200);
   pinMode(LED_1, OUTPUT);
   pinMode(LED_2, OUTPUT);

/*For creating Task, xTaskCreate() API is called in setup function with certain parameters/arguments.*/
  xTaskCreate(
    Task1,          /*Task1 function */
    "Task1",        /*Task name - Task1*/
    1000,           /* Task stack, allocated from heap */
    NULL,           /* No param passed to task function */
    1,              /* Priority 1*/
    NULL);          /* Task handle */

  xTaskCreate(
    Task2,         /* Task2 function */
    "Task2",       /*Task name - Task2*/
    1000,          /*Task stack, allocated from heap */
    NULL,          /* No param passed to task function */
    2,              /* Priority 2*/             
    NULL);         /* Task handle */
}

void Task1( void * parameter )
{
  Serial.print("Task1 is running on core ");

  for(;;)
  {
    digitalWrite(LED_1, HIGH);
    delay(500);
    digitalWrite(LED_1, LOW);
    delay(500);
  }
}

void Task2( void * parameter )
{
  for(;;)
  {
    digitalWrite(LED_2, HIGH); // Make LED 2 Pin High
    delay(500);
    digitalWrite(LED_2, LOW); //Make LED 2 Pin Low
    delay(500);
  }
}
void loop() {}

Hi @Rehan11

You just need the place each task into a blocked state with the FreeRTOS vTaskDelay() function after sending a message string. This gives the serial port breathing space and time to transmit the messages to the console:

/* Include FreeRTOS APIs and defines */
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void setup() 
{
   Serial.begin(115200);

/*For creating Task, xTaskCreate() API is called in setup function with certain parameters/arguments.*/
  xTaskCreate(
    Task1,          /*Task1 function */
    "Task1",        /*Task name - Task1*/
    1000,           /* Task stack, allocated from heap */
    NULL,           /* No param passed to task function */
    1,              /* Priority 1*/
    NULL);          /* Task handle */
  
  xTaskCreate(
    Task2,         /* Task2 function */
    "Task2",       /*Task name - Task2*/
    1000,          /*Task stack, allocated from heap */
    NULL,          /* No param passed to task function */
    1,              /* Priority 2*/                
    NULL);         /* Task handle */
}

void Task1(void* pvParameters)
{
  for(;;)
  {
    Serial.println("Task1 is running");   // Display Task1 message
    vTaskDelay(100);                      // Block Task1 for 100ms
  } 
}

void Task2(void* pvParameters)
{
  for(;;)
  {
    Serial.println("Task2 is running");   // Display Task2 message
    vTaskDelay(100);                      // Block Task2 for 100ms
  } 
}

void loop() {}

This is no longer required.

1 Like

@Idahowalker So can I now just call vTaskDelay() with number of milliseconds within the brackets, like this?

vTaskDelay(100);       // Block Task1 for 100ms

Yes.
By not calling pdMS_TO_TICKS your program will save a little time.

Excellent, thanks.

I guess it makes sense, seeing as most people prefer to specify their delays in milliseconds, rather than scheduler ticks. I'd better change my example code above.

BTW, your code is working well. I ran your code and changed the tick count vTaskDelay(1); for less of a delay. Though the issue of how the serial print needs time to do its thing (I/O) just starts to show up.

So the Arduino core that runs under FreeRTOS isn't actually RTOS-Aware?
Or does FreeRTOS just not have "idle task" queues?

ESP32 runs an actual port of real FreeRTOS, modified to support the dual processor cores. The Arduino core adds "Arduino Stuff" on top of that.

The only way for a lower-priority task to run if a higher-priority task is already running is to have the latter enter the Blocked State. vTaskDelay() is one way to do this. There are multiple other ways.

Like using the servo library and it's I/O and giving the servo time to get to its spot. The ESP32 has been made to work in/with the Arduino IDE; hope that makes sense. Also, the Arduino's ESP 32 core is not as efficient as using the ESP32's API, directly.

I prefer to use log_x where x can be 'n', 'i', 'd', or 'v', log_i is part of the ESP32's OS. The built in OS of the ESP32 is freeRTOS.

But Serial.print() OUGHT to enter this "blocked state" when it ... blocks, right?

And ... the ESP32 FreeRTOS isn't set up to run in "preemptive" mode?

I guess you'd have to look at the source code to see how it's implemented.

By default it's in "Prioritized Pre-emptive Scheduling with Time Slicing" mode:

FreeRTOSConfig.h:

#define configUSE_PREEMPTION			1

FreeRTOS.h:

#ifndef configUSE_TIME_SLICING
	#define configUSE_TIME_SLICING 1
#endif