2

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.

enter image description here enter image description here

#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(); 
}
9
  • At first glance that all looks fine. Maybe there's something behind the scenes that's messing it up. Maybe you should have all the display code in one dedicated thread and pass data to that thread from the others. – Majenko Apr 11 at 14:29
  • You mean create a third task printToDisplay(String text)? – Paulo Borges Apr 11 at 14:50
  • A third task that takes instruction through some other route (maybe a pipe or something similar) from the other tasks and displays the information. – Majenko Apr 11 at 15:07
  • Just found that if I put Task01 to run on the same core as Task02 (core 1) it works perfectly. And if I put both to run on core 0 the mess becomes a lot worse. The problem has clearly something to do when the task running on core 0 tries to write to the display even protected by Mutex. – Paulo Borges Apr 11 at 15:40
  • 1
    Core 0 is used for the Wifi and IP stack. There's probably some timing issue then. – Majenko Apr 11 at 15:48
1

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.