user39683: ESP32 with RTOS, test app with 2 independent tasks. Each prints a message to OLED display.
Why text gets frequently scrambled? Mutex have been used to share display resource.


#define DEBUG_ESP              //comment out to deactivate debug console verbose
#ifdef DEBUG_ESP
  #define pDBGln(x) Serial.println(x)
  #define pDBG(x)   Serial.print(x)
#else 
  #define pDBG(...)
  #define pDBGln(...)
#endif 

//create handle for the mutex. It will be used to reference mutex
SemaphoreHandle_t  xMutex;

//*********************************SSD1306 OLED Display
#define I2C_SDA              14
#define I2C_SCL              15
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//*****************************************************

void setup() {

  // create mutex and assign it a already create handler 
  xMutex = xSemaphoreCreateMutex();  

  Wire.begin(I2C_SDA,I2C_SCL);
  Serial.begin(115200);
        
   // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    pDBGln("SSD1306 allocation failed - Halt");
    for(;;); // Don't proceed, loop forever
  }else{
    // Show initial display buffer contents on the screen --
    // the library initializes this with an Adafruit splash screen.
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);  
    display.setCursor(0, 0);
    display.clearDisplay(); 
  }

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

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

void loop() {}

void task01( void * parameter ){
  while(1){
    printToDisplay("Task01..............");
    vTaskDelay(1500);
  }
}

void task02( void * parameter ){
  while(1){
    printToDisplay("Task02..............");
    vTaskDelay(2500);
  }
}

void printToDisplay(String text){
  bool myMutex;
  while(1){
    // take mutex
    myMutex = xSemaphoreTake(xMutex, portMAX_DELAY);
    if(myMutex){ 
      static byte   lineCounter = 0;
      static String displayText[8];
      if(lineCounter>7){
        for(int i=0;i<7;i++){
          displayText[i] = displayText[i+1];
        }
        displayText[7] = text;
        display.clearDisplay();  
        display.setCursor(0, 0);  
        for(int i=0;i<8;i++){
          display.println(displayText[i]);   
        }    
      }else{
        displayText[lineCounter] = text; 
        display.println(displayText[lineCounter]);
        if(lineCounter<8){lineCounter++;}
      }  
      display.display(); 

      // release mutex
      xSemaphoreGive(xMutex); 
      break;
    }
  }
}

The code below uses no RTOS and works perfectly: (my project requires RTOS)
#define DEBUG_ESP              //comment out to deactivate debug console verbose
#ifdef DEBUG_ESP
  #define pDBGln(x) Serial.println(x)
  #define pDBG(x)   Serial.print(x)
#else 
  #define pDBG(...)
  #define pDBGln(...)
#endif 

byte lineCounter = 0;

//*********************************SSD1306 OLED Display
#define I2C_SDA              14
#define I2C_SCL              15
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//*****************************************************

void setup() {

  Wire.begin(I2C_SDA,I2C_SCL);
  Serial.begin(115200);
        
   // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    pDBGln("SSD1306 allocation failed - Halt");
    for(;;); // Don't proceed, loop forever
  }else{
    // Show initial display buffer contents on the screen --
    // the library initializes this with an Adafruit splash screen.
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);  
    display.setCursor(0, 0);
    display.clearDisplay(); 
  }
}

void loop() {
  pDBGln("Start Display Test...");
  task01();
  task02();
}

void task01(){
  printToDisplay("Task01..............");
  delay(500);
}

void task02(){
  printToDisplay("Task02..............");
  delay(500);
}

void printToDisplay(String text){
  static byte   lineCounter = 0;
  static String displayText[8];
  if(lineCounter>7){
    for(int i=0;i<7;i++){
      displayText[i] = displayText[i+1];
    }
    displayText[7] = text;
    display.clearDisplay();  
    display.setCursor(0, 0);  
    for(int i=0;i<8;i++){
      display.println(displayText[i]);   
    }    
  }else{
    displayText[lineCounter] = text; 
    display.println(displayText[lineCounter]);
    if(lineCounter<8){lineCounter++;}
  }  
  display.display(); 
}


user19409: This is a formal answer to the question based on the comments between Paulo Borges and Majenko.

The ESP32 is dual core, meaning that it comes with 2 Xtensa LX6 32-bit microprocessors: core 0 and core 1.
The issue is not a bug in your code but rather that you are running your threads on Core 0 instead of Core 1.
In the ESP, the Core 0 is used for the RF communications. Even with the mutex protection, it is highly likely that your tasks are having timing issues.
Move Task01 and Task02 into Core 1 and your program will run without any display issues.

39683: <p>ESP32 with RTOS, test app with 2 independent tasks. Each prints a message to OLED display.
Why text gets frequently scrambled? Mutex have been used to share display resource.</p>
<p><a href="https://i.stack.imgur.com/JD6cJ.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/JD6cJ.jpg" alt="enter image description here" /></a>
<a href="https://i.stack.imgur.com/Ab7tB.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Ab7tB.jpg" alt="enter image description here" /></a></p>
<pre><code>#define DEBUG_ESP              //comment out to deactivate debug console verbose
#ifdef DEBUG_ESP
  #define pDBGln(x) Serial.println(x)
  #define pDBG(x)   Serial.print(x)
#else 
  #define pDBG(...)
  #define pDBGln(...)
#endif 

//create handle for the mutex. It will be used to reference mutex
SemaphoreHandle_t  xMutex;

//*********************************SSD1306 OLED Display
#define I2C_SDA              14
#define I2C_SCL              15
#include &lt;Wire.h&gt;
#include &lt;Adafruit_SSD1306.h&gt;
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;Wire, -1);
//*****************************************************

void setup() {

  // create mutex and assign it a already create handler 
  xMutex = xSemaphoreCreateMutex();  

  Wire.begin(I2C_SDA,I2C_SCL);
  Serial.begin(115200);
        
   // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    pDBGln(&quot;SSD1306 allocation failed - Halt&quot;);
    for(;;); // Don't proceed, loop forever
  }else{
    // Show initial display buffer contents on the screen --
    // the library initializes this with an Adafruit splash screen.
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);  
    display.setCursor(0, 0);
    display.clearDisplay(); 
  }

    xTaskCreatePinnedToCore(
        task01,             /* Function to implement the task */
        &quot;task01&quot;,           /* Name of the task */
        10000,              /* Stack size in words */
        NULL,               /* Task input parameter */
        1,                  /* Priority of the task */
        NULL,               /* Task handle. */
        0);                 /* Core where the task should run */      

    xTaskCreatePinnedToCore(
        task02,             /* Function to implement the task */
        &quot;task02&quot;,           /* Name of the task */
        10000,              /* Stack size in words */
        NULL,               /* Task input parameter */
        1,                  /* Priority of the task */
        NULL,               /* Task handle. */
        1);                 /* Core where the task should run */ 
}

void loop() {}

void task01( void * parameter ){
  while(1){
    printToDisplay(&quot;Task01..............&quot;);
    vTaskDelay(1500);
  }
}

void task02( void * parameter ){
  while(1){
    printToDisplay(&quot;Task02..............&quot;);
    vTaskDelay(2500);
  }
}

void printToDisplay(String text){
  bool myMutex;
  while(1){
    // take mutex
    myMutex = xSemaphoreTake(xMutex, portMAX_DELAY);
    if(myMutex){ 
      static byte   lineCounter = 0;
      static String displayText[8];
      if(lineCounter&gt;7){
        for(int i=0;i&lt;7;i++){
          displayText[i] = displayText[i+1];
        }
        displayText[7] = text;
        display.clearDisplay();  
        display.setCursor(0, 0);  
        for(int i=0;i&lt;8;i++){
          display.println(displayText[i]);   
        }    
      }else{
        displayText[lineCounter] = text; 
        display.println(displayText[lineCounter]);
        if(lineCounter&lt;8){lineCounter++;}
      }  
      display.display(); 

      // release mutex
      xSemaphoreGive(xMutex); 
      break;
    }
  }
}
</code></pre>
<p>The code below uses no RTOS and works perfectly: (my project requires RTOS)</p>
<pre><code>#define DEBUG_ESP              //comment out to deactivate debug console verbose
#ifdef DEBUG_ESP
  #define pDBGln(x) Serial.println(x)
  #define pDBG(x)   Serial.print(x)
#else 
  #define pDBG(...)
  #define pDBGln(...)
#endif 

byte lineCounter = 0;

//*********************************SSD1306 OLED Display
#define I2C_SDA              14
#define I2C_SCL              15
#include &lt;Wire.h&gt;
#include &lt;Adafruit_SSD1306.h&gt;
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;Wire, -1);
//*****************************************************

void setup() {

  Wire.begin(I2C_SDA,I2C_SCL);
  Serial.begin(115200);
        
   // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    pDBGln(&quot;SSD1306 allocation failed - Halt&quot;);
    for(;;); // Don't proceed, loop forever
  }else{
    // Show initial display buffer contents on the screen --
    // the library initializes this with an Adafruit splash screen.
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);  
    display.setCursor(0, 0);
    display.clearDisplay(); 
  }
}

void loop() {
  pDBGln(&quot;Start Display Test...&quot;);
  task01();
  task02();
}

void task01(){
  printToDisplay(&quot;Task01..............&quot;);
  delay(500);
}

void task02(){
  printToDisplay(&quot;Task02..............&quot;);
  delay(500);
}

void printToDisplay(String text){
  static byte   lineCounter = 0;
  static String displayText[8];
  if(lineCounter&gt;7){
    for(int i=0;i&lt;7;i++){
      displayText[i] = displayText[i+1];
    }
    displayText[7] = text;
    display.clearDisplay();  
    display.setCursor(0, 0);  
    for(int i=0;i&lt;8;i++){
      display.println(displayText[i]);   
    }    
  }else{
    displayText[lineCounter] = text; 
    display.println(displayText[lineCounter]);
    if(lineCounter&lt;8){lineCounter++;}
  }  
  display.display(); 
}
</code></pre>
 19409: <p>This is a formal answer to the question based on the comments between Paulo Borges and Majenko.</p>
<hr />
<p>The ESP32 is dual core, meaning that it comes with 2 Xtensa LX6 32-bit microprocessors: core 0 and core 1.</p>
<p>The issue is not a bug in your code but rather that you are running your threads on Core 0 instead of Core 1.</p>
<p>In the ESP, the Core 0 is used for the RF communications. Even with the mutex protection, it is highly likely that your tasks are having timing issues.</p>
<p>Move Task01 and Task02 into Core 1 and your program will run without any display issues.</p>
