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 */
    &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("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(); }

1 Answers1

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.

sa_leinad
  • 3,218
  • 2
  • 23
  • 51