FreeRTOS example-codes for ESp32

Hi,
until today I was not aware of that - if I understand right - there is a FreeRTOS-derivate for ESP32

So does anybody know of a few example-codes that show how to use FreeRTOS in combination with Arduino-IDE and C++

best regards Stefan

Is your google broken?

a7

alto777:
Is your google broken?

I too would like to see a non-snarky answer to this question.

With specific instructions, how does one get started with FreeRTOS on boards in the Arduino ecosystem?

I'm interested mainly in Teensy (Freescale processors) and ESP (Espressif processor) boards. Do I need different versions of the OS for these different boards? How to integrate it into Arduino IDE. And, more importantly does it also integrate with Eclipse / Sloeber?

The problem is not finding results with Google. The problem is too many results. Which one to pick? Where are the definitive answers? I don't want to waste time following crappy instructions from an outdated link.

Add to that, the Espressif documentation just plane sucks. The information may be there, but it's disjointed, unorganized, and contradictory. Nothing of the quality you'd get from a US chip vendor.

I'm an intermediate to advanced C++ programmer. So, the problem isn't understanding the concepts. It's finding correct and up-to-date instructions for doing the setup and configuration.

1 Like

ESP32's use freeRTPS as their OS. ESP8266 have a model without OS and with OS.

Basic freeRTOS includes and they do NOT burn ram when used.

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

These two includes are to make it easier to talk ESP32 API and to use log_x for printing, faster printing than Swerial.print.

#include "sdkconfig.h"
#include "esp_system.h"
1 Like

Here is the creation of tasks in setup()

 xTaskCreatePinnedToCore( fparseMQTT, "fparseMQTT", 7000, NULL, 5, NULL, 1 ); // assign all to core 1, WiFi in use.
  xTaskCreatePinnedToCore( fCollectHistory, "fCollectHistory", 10000, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( MQTTkeepalive, "MQTTkeepalive", 10000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fUpdateDisplay, "fUpdateDisplay", 50000, NULL, 5, NULL, 1 );
  xTaskCreatePinnedToCore( DoTheBME280Thing, "DoTheBME280Thing", 20000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fDoTheHumidityThing, "fDoTheHumidityThing", 2000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fDoTheSunLampThing, "fDoTheSunLampThing", 2000, NULL, 3, NULL, 1 );

I use xTaskCreatePinnedToCore because it's what I got used to using. I have some ESP32's not using WiFi which means I get 2 cores and another processor to use. xTaskCreatePinnedToCore input parameters are which function will become a task, a text of what message you want to freeRTOS engine to display incase your tasks freeRTOS faults, the size of the stack space that will be reserved for the task, tasks input parameters, task priority, a handle that will be return upon task creation, which can be used for task starting stopping and pausing, and the core to assign the task to.

I've not found the passing of task parameters to work under the Arduino IDE, instead for inter tasks sharing of data I use queues.

If WiFi is being used all tasks should go to core 1 until you get familiar with when you can use a task assigned to core0 and WiFi is in use.

1 Like

@Idahowalker,
Unfortunately, your posts don't address any of the issues in my post.

Important your void loop() should look like this

void loop() { }

Why should your void loop() be empty.

under freeRTOS void() is assigned task priority of 1, actually it it a bit less than one. freeRTOS uses the time when loop() runs to clean up, like memory fragmentation. loop() is not guaranteed to run in a timely fashion. And whiles loop() is running a freeRTOS task may preempt a loop() task.

and here is a freeRTOS task:

void DoTheBME280Thing( void *pvParameters )
{
  //log_i( "start bme680" );
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDoTheBME280Thing, pdTRUE, pdTRUE, portMAX_DELAY ); //
    // log_i( "Signal strength %d", WiFi.RSSI() );
    vTaskDelay( 2 );
    if ( !isnan(bme.readTemperature()) )
    {
      ePtr[0] = bme.readTemperature();
      ePtr[1] = bme.readPressure() / 133.3223684f; // mmHg
      ePtr[2] = bme.readHumidity();
      if ( MQTTclient.connected() )
      {
        MQTTclient.publish( topicInsideTemp, String(ePtr[0]).c_str() );
        vTaskDelay( 2 ); // gives the Raspberry Pi 4 time to receive the message and process
        MQTTclient.publish( topicInsideHumidity, String(ePtr[1]).c_str() );
        vTaskDelay( 2 ); // no delay and RPi is still processing previous message
        MQTTclient.publish( topicInsidePressure, String(ePtr[2]).c_str() );
      }
    }
    //log_i( "DoTheBME280Thing high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete ( NULL );
}

In this section of code

{
  //log_i( "start bme680" );<<<<<<<this section
  for (;;)
  {

you can use it like setup() creating task variables, starting devices and the like.

This xEventGroupWaitBits (eg, evtDoTheBME280Thing, pdTRUE, pdTRUE, portMAX_DELAY ); is a task trigger. This particular task trigger allows multiple tasks to be triggered at the same time. Task priority comes into play when triggering multiple tasks at the same time.

This vTaskDelay( 2 ); is a NON-blocking delay. For 2 milliseconds the task will be put on hold and other tasks will be incremented forward in their running of code.

The basic components of a task are

void DoTheBME280Thing( void *pvParameters )
{
  for (;;) //keeps the task runing forever
  {
    xEventGroupWaitBits (eg, evtDoTheBME280Thing, pdTRUE, pdTRUE, portMAX_DELAY ); // this is a way to trigger a task to run. There are several ways to trigger task such sa events or event goups or timers.
    }
    //log_i( "DoTheBME280Thing high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) ); // yhis is a tool to use when creating tasks. I start with 10K for task size then obsurve how much memory the task actually uses and pair it down. Look for a task to take less then 2000 then assigned. If the task takes more your'll need to allocate more.
  }
  vTaskDelete ( NULL ); // another important part of the task is to kill the task if it ever gets to this point. which I've not had issue with.
}

gfvalvo:
@Idahowalker,
Unfortunately, your posts don't address any of the issues in my post.

Not a useful answer in the bunch.

Any questions on the tasks, I'll try to answer as best as I can.

ESP32 has excellent documentation

ESP32 API API Reference - ESP32 - ‚ÄĒ ESP-IDF Programming Guide latest documentation

So far all the ESP32 developer boards I bought come with freeRTOS and the API examples, mostly, work under the Arduino API. The API examples provide me with a good starting point for using the API.

gfvalvo:
Add to that, the Espressif documentation just plane sucks. The information may be there, but it's disjointed, unorganized, and contradictory. Nothing of the quality you'd get from a US chip vendor.

I'm an intermediate to advanced C++ programmer. So, the problem isn't understanding the concepts. It's finding correct and up-to-date instructions for doing the setup and configuration.

We must be reading different sources then, I haven't had any problems finding clear documentation for the ESP32. The documentation, development environment and examples were much easier to get started with than the STM32 ecosystem, for example, with its 99 different IDEs/programmers/code generators and 5 different APIs and hardware abstraction layers.

Start here: https://docs.espressif.com/projects/esp-idf/en/latest/esp32

The Get Started document has step-by-step instructions to set up the environment and install everything. It's been a while since I've followed it, but it worked just fine for me. Alternatively, you could use the ESP-IDF using PlatformIO, which is nice if you're already familiar with it.

The Technical Reference Manual has everything you need to know about the chip: hardware capabilities, block diagrams, peripheral registers documentation, etc. It might not be as beginner-friendly as your standard AVR datasheet, but it's a very useful resource nonetheless.

You probably won't need the low-level information in the technical reference, though: the ESP-IDF has high-level abstractions for most peripherals. See the API reference. On most pages, there's a link to the examples folder on GitHub, which is the best way to get started in my opinion, most examples are well documented and come with a tutorial document. The API reference itself is pretty dry, of course, but that's why the examples exist.
For specific (mostly advanced) topics, there's also a more detailed API Guide.
If things aren't clear from the documentation or examples, you can often find an answer to your questions in the ESP-IDF issues section on GitHub, or on the ESP32 forums.

The ESP-IDF is built on top of a FreeRTOS port, see the ESP-IDF FreeRTOS API reference, as well as the official FreeRTOS API Reference, which has a bit more details (do check the versions, though, the ESP-IDF version used in Arduino doesn't use the latest FreeRTOS version). The FreeRTOS scheduling guide might be of interest as well.

The ESP32 Arduino Core is built on top of the ESP-IDF (and hence FreeRTOS). It even includes a "FreeRTOS.ino" example that explains how to create FreeRTOS tasks in Arduino, as well as other examples that make use of FreeRTOS features directly.
You can use the ESP-IDF examples on Arduino without too much issues, just keep in mind that the Arduino platform already initializes some things for you.

Apart from the official Espressif docs and examples, I also found Neil Kolban's Book on ESP32 quite useful.

I'm not familiar with any Teensy FreeRTOS ports.

Pieter

PieterP:
The Get Started document has step-by-step instructions to set up the environment and install everything. It's been a while since I've followed it, but it worked just fine for me. Alternatively, you could use the ESP-IDF using PlatformIO, which is nice if you're already familiar with it.

Pieter,

Thanks. Understand you have no info on Teensy FreeRTOS ports. So, sticking with ESP32. Your post is an example of what I meant by too much information. Right now, I'm just looking for basic configuration info.

What is the ESP-IDF environment, why do I need it, and why do I need to "set it up"? Let's keep it simple. Say all I want to do is buy an Adafruit Huzzah32 Feather and start learning FreeRTOS in the Arduino IDE (and Sloeber). Is the "ESP32 Arduino Core" included (like any other board in the Arduino ecosystem) when I install ESP32 under "Preferences"? How does the ESP-IDF and API relate to that, and do I care?

Thanks again.

A fully functional (part 1) freeRTOS program wrote under the Arduino IDE for you to look over.
I figured out how to use freeRTOS in the Arduino IDE after using a DUE and uMT, a RTOS.

/*
   10/17/202 Ambrose Campbell
   Project to display MQTT data on a OLED screen
*/
#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 <Adafruit_GFX.h>
#include <Adafruit_SSD1351.h>
#include "time.h"
#include <driver/adc.h>
//
//
hw_timer_t * timer = NULL;
EventGroupHandle_t eg;
#define evtDoDisplay             ( 1 << 1 )
#define evtCollectHistory        ( 1 << 2 )
#define evtParseMQTT             ( 1 << 3 )
#define evtDoTheAirParticleThing ( 1 << 4 )
// the events in this event groups tasks have been given a delay to avoid a possible race condition.
#define evtGroupBits ( evtCollectHistory | evtDoDisplay | evtDoTheAirParticleThing )
//                                                 CS GPIO_NUM_27, DC GPIO_NUM_12, DIN GPIO_NUM_13, CLK GPIO_NUM_14, RST GPIO_NUM_26
Adafruit_SSD1351 tft = Adafruit_SSD1351( 128, 128, GPIO_NUM_27,    GPIO_NUM_12,    GPIO_NUM_13,     GPIO_NUM_14,     GPIO_NUM_26 );
////
WiFiClient   wifiClient;
PubSubClient MQTTclient( mqtt_server, mqtt_port, wifiClient );
//////
// black, blue, red, green, cyan, magenta, yellow, white, lightyellow, brown
const int DataCells = 60;
int    ColorPalette[10] = { 0x0000, 0x001F, 0xF800, 0x07E0, 0x07FF, 0xF81F, 0xFFE0, 0xFFFF, 0xFFFFD8, 0xFF8040 };
int    arrayCellPtr = 0;
float  oPressureHistory[DataCells] = {0.0f};
String str_eTopic;
char   strPayload [300] = {NULL};
float  oTemperature = 0.0f;
float  oHumidity = 0.0f;
float  oIAQ = 0.0f; // Indexed Air Quality
float  oPressure = 0.0f;
////
////
SemaphoreHandle_t sema_HistoryCompleted;
SemaphoreHandle_t sema_MQTT_Parser;;
SemaphoreHandle_t sema_MQTT_KeepAlive;
////
volatile int iDoTheThing = 0;
////
void IRAM_ATTR onTimer()
{
  BaseType_t xHigherPriorityTaskWoken;
  iDoTheThing++;
  if ( iDoTheThing == 60000 )
  {
    xEventGroupSetBitsFromISR( eg, evtGroupBits, &xHigherPriorityTaskWoken );
    iDoTheThing = 0;
  }
}
////
void setup()
{
  /* Use 4th timer of 4.
    1 tick 1/(80MHZ/80) = 1us set divider 80 and count up.
    Attach onTimer function to timer
    Set alarm to call timer ISR, every 1000uS and repeat / reset ISR (true) after each alarm
    Start an timer alarm
  */
  timer = timerBegin( 3, 80, true );
  timerAttachInterrupt( timer, &onTimer, true );
  timerAlarmWrite(timer, 1000, true);
  timerAlarmEnable(timer);
  //
  str_eTopic.reserve(300);
  //
  eg = xEventGroupCreate();
  //
  tft.begin();
  tft.fillScreen(0x0000);
  //
  sema_MQTT_Parser      = xSemaphoreCreateBinary();
  sema_HistoryCompleted = xSemaphoreCreateBinary();
  sema_MQTT_KeepAlive   = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_HistoryCompleted );
  xSemaphoreGive( sema_MQTT_KeepAlive ); // found keep alive can mess with a publish, stop keep alive during publish
  ////
  xTaskCreatePinnedToCore( fparseMQTT,      "fparseMQTT",      7000,  NULL, 5, NULL, 1 ); // assign all to core 1, WiFi in use.
  xTaskCreatePinnedToCore( fCollectHistory, "fCollectHistory", 10000, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( MQTTkeepalive,   "MQTTkeepalive",   7000,  NULL, 2, NULL, 1 ); //this task makes a WiFi and MQTT connection.
  xTaskCreatePinnedToCore( fUpdateDisplay,  "fUpdateDisplay",  50000, NULL, 5, NULL, 1 );
  ////
} //setup() END
////
////
void GetTheTime()
{
  char* ntpServer = "2.us.pool.ntp.org";
  int gmtOffset_sec = -(3600 * 7 );
  int daylightOffset_sec = 3600;
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
}
////
// http://www.cplusplus.com/reference/ctime/strftime/
////
int getHour()
{
  struct tm timeinfo;
  getLocalTime(&timeinfo);
  char _time[ 5 ];
  strftime( _time, 80, "%T", &timeinfo );
  return String(_time).toInt();
}
////
void printLocalTime()
{
  struct tm timeinfo;
  getLocalTime(&timeinfo);
  char _time[ 80 ];
  strftime( _time, 80, "%T", &timeinfo );
  log_i( "%s", _time);
}
////
/*
   Collect history information
   task triggered by hardware timer once a minute
   stores history
*/
void fCollectHistory( void * parameter )
{
  int StorageTriggerCount = 119;
  bool Tick = true;
  int maxStorageTriggerCount = 120;
  for (;;)
  {
    xEventGroupWaitBits (eg, evtCollectHistory, pdTRUE, pdTRUE, portMAX_DELAY );
    xSemaphoreTake( sema_HistoryCompleted, portMAX_DELAY );
    oPressureHistory[arrayCellPtr] = oPressure;
    StorageTriggerCount++; //triggered by the timer isr once a minute
    log_i( "storage trigger count %d", StorageTriggerCount );
    if ( StorageTriggerCount == maxStorageTriggerCount )
    {
      arrayCellPtr++;
      log_i( "arrayCellPtr %d", arrayCellPtr );
      if ( arrayCellPtr == DataCells )
      {
        arrayCellPtr = 0;
      }
      StorageTriggerCount = 0;
    }
    xSemaphoreGive( sema_HistoryCompleted );
    // log_i( " high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
} //void fCollectHistory( void * parameter )
////
// Using a semaphore to protect the PSRAM from multiple tasks access the same PSRM locations
////

part 2

void fUpdateDisplay( void * parameter )
{
  // Color definitions
  // http://www.barth-dev.de/online/rgb565-color-picker/
  /*
    ColorPalette[0] = 0x0000; //BLACK
    ColorPalette[1] = 0x001F; //BLUE
    ColorPalette[2] = 0xF800; //RED
    ColorPalette[3] = 0x07E0; //GREEN
    ColorPalette[4] = 0x07FF; //CYAN
    ColorPalette[5] = 0xF81F; //MAGENTA
    ColorPalette[6] = 0xFFE0; //YELLOW
    ColorPalette[7] = 0xFFFF; //WHITE
    ColorPalette[8] = 0xFFFFD8; //LIGHTYELLOW
    ColorPalette[9] = 0xFF8040; //BROWN
  */
  int rowRef = 110;
  int hRef = int( oPressureHistory[0] );
  const int nextPoint = 2;
  int nextCol = 0;
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDoDisplay, pdTRUE, pdTRUE, portMAX_DELAY ); //
    vTaskDelay( 1 );
    tft.fillScreen( ColorPalette[0] );
    //tft.setTextColor( ColorPalette[3] );
    //tft.print( "Temp: " + String(oTemperature) + "C " + String((oTemperature * 9 / 5) + 32) + "F"  );
    //    tft.setCursor( 0, 10 );
    //    tft.print( "Humidity " + String(ePtr[2]) + "%" );
    //
    tft.setTextColor( ColorPalette[6] );
    tft.setCursor( 0, 25 );
    tft.print( "Temperature: " + String(oTemperature) + "F" );
    tft.setCursor( 0, 35 );
    tft.print( "Humidity " + String(oHumidity) + "%" );
    //
    //set the color of the value to be displayed
    if ( oIAQ < 51.0f )
    {
      tft.setTextColor( ColorPalette[3] );
    }
    if ( (oIAQ >= 50.0f) && (oIAQ <= 100.0f) )
    {
      tft.setTextColor( ColorPalette[6] );
    }
    if ( (oIAQ >= 100.0f) && (oIAQ <= 150.0f) )
    {
      tft.setTextColor( ColorPalette[9] );
    }
    if ( (oIAQ >= 150.0f) && (oIAQ <= 200.0f) )
    {
      tft.setTextColor( ColorPalette[2] );
    }
    if ( (oIAQ >= 200.00f) && (oIAQ <= 300.0f) )
    {
      tft.setTextColor( ColorPalette[5] );
    }
    if ( (oIAQ > 300.0f) )
    {
      tft.setTextColor( ColorPalette[7] );
    }
    // display AQI
    tft.setCursor( 0, 45 );
    tft.print( "AQ Index " + String(oIAQ) );
    //
    tft.setTextColor( ColorPalette[1] ); //set graph line color
    tft.setCursor( 0, 85 );
    tft.print( "Pres: " + String(oPressure) + "mmHg" );
    hRef = int( oPressureHistory[0] );
    nextCol = 0;
    xSemaphoreTake( sema_HistoryCompleted, portMAX_DELAY );
    for (int i = 0; i <= DataCells; i++)
    {
      int hDisplacement = hRef - int( oPressureHistory[i] ); // cell height displacement from base line
      tft.setCursor( nextCol , (rowRef + hDisplacement) );
      tft.print( "_" );
      nextCol += nextPoint;
    }
    // advance cursor to place a begin of graph marker. each letter is 5 spaces wide but graph position marker only advances 2 spaces
    // to keep cursor at begining place cursor +2 the nextCol position.
    tft.setCursor( (arrayCellPtr * nextPoint), (rowRef + 3) );
    tft.print( "I" );
    xSemaphoreGive( sema_HistoryCompleted );
    //     log_i( "fUpdateDisplay MEMORY WATERMARK %d", uxTaskGetStackHighWaterMark(NULL) );
  }
  vTaskDelete( NULL );
} // void fUpdateDisplay( void * parameter )
/*
    Important to not set vTaskDelay to less then 10. Errors begin to develop with the MQTT and network connection.
    makes the initial wifi/mqtt connection and works to keeps those connections open.
*/
void MQTTkeepalive( void *pvParameters )
{
  // 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 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 ( !(WiFi.status() == WL_CONNECTED) )
      {
        connectToWiFi();
      }
      connectToMQTT();
    }
    vTaskDelay( 250 ); //task runs approx every 250 mS
  }
  vTaskDelete ( NULL );
}
////
void connectToMQTT()
{
  log_i( "connect to mqtt" );
  while ( !MQTTclient.connected() )
  {
    MQTTclient.connect( clientID, mqtt_username, mqtt_password );
    log_i( "connecting to MQTT" );
    vTaskDelay( 250 );
  }
  log_i("MQTT Connected");
  MQTTclient.setCallback( mqttCallback );
  MQTTclient.subscribe( mqtt_topic );
}
//
void connectToWiFi()
{
  byte mac[6];
  log_i( "connect to wifi" );
  while ( WiFi.status() != WL_CONNECTED )
  {
    WiFi.disconnect();
    WiFi.begin( SSID, PASSWORD );
    log_i(" waiting on wifi connection" );
    vTaskDelay( 4000 );
  }
  log_i( "Connected to WiFi" );
  WiFi.macAddress(mac);
  log_i( "mac address %d.%d.%d.%d.%d", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]  );
  WiFi.onEvent( WiFiEvent );
  GetTheTime();
  printLocalTime();
}
////
void fparseMQTT( void *pvParameters )
{
  xSemaphoreGive ( sema_MQTT_Parser );
  for (;;)
  {
    xEventGroupWaitBits (eg, evtParseMQTT, pdTRUE, pdTRUE, portMAX_DELAY ); //
    xSemaphoreTake( sema_MQTT_Parser, portMAX_DELAY );
    if ( str_eTopic == topicOutsideTemperature )
    {
      oTemperature = String(strPayload).toFloat();
    }
    if ( (String)str_eTopic == topicOutsideHumidity )
    {
      oHumidity = String(strPayload).toFloat();
    }
    if ( (String)str_eTopic == topicAQIndex )
    {
      oIAQ = String(strPayload).toFloat();
    }
    if ( String(str_eTopic) == topicOutsidePressure )
    {
      oPressure = String(strPayload).toFloat();
    }
    // clear pointer locations
    memset( strPayload, NULL, 300 );
    str_eTopic = ""; //clear string buffer
    xSemaphoreGive( sema_MQTT_Parser );
  }
} // void fparseMQTT( void *pvParameters )
////
static void mqttCallback(char* topic, byte * payload, unsigned int length)
{
  xSemaphoreTake( sema_MQTT_Parser, portMAX_DELAY);
  str_eTopic = topic;
  int i = 0;
  for ( i; i < length; i++) 
  {
    strPayload[i] = ((char)payload[i]);
  }
  strPayload[i] = NULL;
  //log_i( "topic %s payload %s" ,str_eTopicPtr, strPayloadPtr );
  xSemaphoreGive ( sema_MQTT_Parser );
  xEventGroupSetBits( eg, evtParseMQTT ); // trigger tasks
} // void mqttCallback(char* topic, byte* payload, unsigned int length)
////
void WiFiEvent(WiFiEvent_t event)
{
  switch (event) {
    case SYSTEM_EVENT_STA_CONNECTED:
      log_i("Connected to access point");
      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 loop() { }

@Idahowalker, thanks. I'll come back to study your examples in detail at the proper time. But, for now, the essence of my questions is:

gfvalvo:
Pieter,

Thanks. Understand you have no info on Teensy FreeRTOS ports. So, sticking with ESP32. Your post is an example of what I meant by too much information. Right now, I'm just looking for basic configuration info.

What is the ESP-IDF environment, why do I need it, and why do I need to "set it up"? Let's keep it simple. Say all I want to do is buy an Adafruit Huzzah32 Feather and start learning FreeRTOS in the Arduino IDE (and Sloeber). Is the "ESP32 Arduino Core" included (like any other board in the Arduino ecosystem) when I install ESP32 under "Preferences"? How does the ESP-IDF and API relate to that, and do I care?

Thanks again.

gfvalvo:
@Idahowalker, thanks. I'll come back to study your examples in detail at the proper time. But, for now, the essence of my questions is:

You do not care.
I have on occasion looked into changing the configuration of ESP32/IDE interactions to find that the configuration under the Arduino IDE is already optimized.
I do not use the ESPRESSIF IDE.
I use only ESP32's made by HiLetGo. I know HiLetGo adheres strictly to the ESPRESSIF specs.

StefanL38:
Hi,
until today I was not aware of that - if I understand right - there is a FreeRTOS-derivate for ESP32

So does anybody know of a few example-codes that show how to use FreeRTOS in combination with Arduino-IDE and C++

best regards Stefan

I've, frankly, had to figure it out. Going from using uMT on a DUE to freeRTOS was an easy transition. The uMT documentation helped a lot.

gfvalvo:
Pieter,

Thanks. Understand you have no info on Teensy FreeRTOS ports. So, sticking with ESP32. Your post is an example of what I meant by too much information. Right now, I'm just looking for basic configuration info.

What is the ESP-IDF environment, why do I need it, and why do I need to "set it up"? Let's keep it simple. Say all I want to do is buy an Adafruit Huzzah32 Feather and start learning FreeRTOS in the Arduino IDE (and Sloeber). Is the "ESP32 Arduino Core" included (like any other board in the Arduino ecosystem) when I install ESP32 under "Preferences"? How does the ESP-IDF and API relate to that, and do I care?

Thanks again.

The ESP-IDF is the Espressif IoT Development Framework. It contains the FreeRTOS kernel (which allows you to run multiple tasks/‚Äúprocesses‚ÄĚ in parallel, and provides the basic features you would expect from an operating system), libraries for WiFi, Bluetooth, networking, flash memory filesystems, etc. Most of it is written in C, but you can easily use it in a C++ project, it implements things like std::thread and C++ atomics.
The ESP-IDF also contains the necessary tools for uploading code to the ESP32, a build system, etc., which is why you need to set it up if you want to program an ESP32 development board. "Setting up" is just downloading the ESP-IDF and installing the toolchain (compiler, build system etc.), it's really not a big deal (on Linux, at least, I've never tried it on Windows).

The ESP32 Arduino Core is simply a wrapper for the ESP-IDF, which is meant to be more beginner-friendly and compatible with the Arduino API and framework. It contains higher-level C++ libraries that just wrap the ESP-IDF C libraries. Most beginners or intermediate Arduino users will simply use these C++ libraries, so they only care about the Arduino part, but you can use all ESP-IDF and FreeRTOS APIs and features in your Arduino code as well, which makes it really powerful (I focused on the ESP-IDF and FreeRTOS, because that's what the original question was about).
The Arduino Core is easier to install than the ESP-IDF, you can just use the Boards Manager in the IDE, see the installation instructions. It includes the necessary parts of the ESP-IDF and the toolchain.

If you just want to get started, install the Arduino core (see link above) and try the FreeRTOS.ino example I referred to earlier. For the FreeRTOS specific functions, you'll need the ESP-IDF and FreeRTOS API reference, as mentioned in my previous reply.

I'm not familiar with Sloeber, I'm a Visual Studio Code user myself.

(I use ‚ÄúArduino Core‚ÄĚ in the sense of https://www.arduino.cc/en/guide/cores.)