four-line-display: semaphores to avoid crashes

* some typos fixed
* Added semaphores around display calls; should fix https://github.com/Aircoookie/WLED/issues/3074
-- changes still need some cleanup to improve readability...
This commit is contained in:
Frank
2023-03-10 22:44:25 +01:00
parent d6c8f44dfa
commit 6ea77954bf
2 changed files with 54 additions and 9 deletions

View File

@@ -3,7 +3,7 @@
#include <Arduino.h> // WLEDMM: make sure that I2C drivers have the "right" Wire Object
#include <Wire.h>
#undef U8X8_NO_HW_I2C // WLEDMM: we do want I2C hardware drivers - if possible
//#define WIRE_INTERFACES_COUNT 2 // experimental - tell U8x8Lib that there is a econd Wire unit
//#define WIRE_INTERFACES_COUNT 2 // experimental - tell U8x8Lib that there is a second Wire unit
#include "wled.h"
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
@@ -117,6 +117,23 @@ class FourLineDisplayUsermod : public Usermod {
// HW interface & configuration
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
// semaphores - needed on ESP32 only, as we use a separate task to update the display
SemaphoreHandle_t drawMux = xSemaphoreCreateBinary(); // for drawstring and drawglyph functions (to prevent concurrent access to HW)
SemaphoreHandle_t drawMuxBig = xSemaphoreCreateBinary(); // for draw2x2GlyphIcons() and showCurrentEffectOrPalette() - more complex and not thread-safe
const TickType_t maxWait = 300 * portTICK_PERIOD_MS; // wait max. 300ms (drawstring semaphore)
const TickType_t maxWaitLong = 800 * portTICK_PERIOD_MS; // wait max. 800ms (big drawing semaphore)
#define FLD_SemaphoreTake(x,t) xSemaphoreTake((x),(t))
#define FLD_SemaphoreGive(x) xSemaphoreGive(x)
#else
// 8266 or no tasks - no semaphores
#define FLD_SemaphoreTake(x,t) pdTRUE
#define FLD_SemaphoreGive(x)
#if !defined(ARDUINO_ARCH_ESP32) && !defined(pdTRUE)
#define pdTRUE true
#endif
#endif
#ifndef FLD_SPI_DEFAULT
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
@@ -126,7 +143,7 @@ class FourLineDisplayUsermod : public Usermod {
#endif
DisplayType type = FLD_TYPE; // display type
bool typeOK = false; //WLEDMM: instead of type == NULL and type=NULL. Intially false, as display was not initialized yet
bool typeOK = false; //WLEDMM: instead of type == NULL and type=NULL. Initially false, as display was not initialized yet
bool flip = false; // flip display 180°
uint8_t contrast = 10; // screen contrast
uint8_t lineHeight = 1; // 1 row or 2 rows
@@ -195,7 +212,6 @@ class FourLineDisplayUsermod : public Usermod {
// some displays need this to properly apply contrast
void setVcomh(bool highContrast) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
//if (!canDraw()) return; // don't interfere with ongoing draw
if (u8x8 == nullptr) return;
u8x8_t *u8x8_struct = u8x8->getU8x8();
@@ -221,6 +237,8 @@ class FourLineDisplayUsermod : public Usermod {
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (u8x8 == nullptr) return;
if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex
#if defined(ARDUINO_ARCH_ESP32) && !defined(OLD_4LD_FONTS) // WLEDMM use nicer 2x2 font on ESP32
if (!ignoreLH && lineHeight==2) {
if(strlen(string) > 3) // WLEDMM little hack - less than 3 chars -> show in bold
@@ -230,18 +248,20 @@ class FourLineDisplayUsermod : public Usermod {
u8x8->drawString(col, row, string);
}
else {
u8x8->setFont(u8x8_font_chroma48medium8_r); // crashes randomly on ESP32
u8x8->drawString(col, row, string); // crashes randomly on ESP32
u8x8->setFont(u8x8_font_chroma48medium8_r);
u8x8->drawString(col, row, string);
}
#else
u8x8->setFont(u8x8_font_chroma48medium8_r);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string);
else u8x8->drawString(col, row, string);
#endif
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
}
void draw2x2String(uint8_t col, uint8_t row, const char *string) {
if (!typeOK || !enabled) return;
if (u8x8 == nullptr) return;
if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex
#if defined(ARDUINO_ARCH_ESP32) && !defined(OLD_4LD_FONTS) // WLEDMM use nicer 2x2 font on ESP32
if (lineHeight==2) { // WLEDMM use 2x3 on 128x64 displays
//u8x8->setFont(u8x8_font_profont29_2x3_r); // sans serif 2x3
@@ -261,17 +281,22 @@ class FourLineDisplayUsermod : public Usermod {
u8x8->draw2x2String(col, row, string);
}
#endif
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
}
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) {
if (!typeOK || !enabled) return;
if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex
u8x8->setFont(font);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
else u8x8->drawGlyph(col, row, glyph);
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
}
void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) {
if (!typeOK || !enabled) return;
if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex
u8x8->setFont(font);
u8x8->draw2x2Glyph(col, row, glyph);
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
}
uint8_t getCols() {
if (!typeOK || !enabled) return 0;
@@ -280,7 +305,9 @@ class FourLineDisplayUsermod : public Usermod {
void clear() {
if (!typeOK || !enabled) return;
if (nullptr == u8x8) return; // prevents some crashes
if (FLD_SemaphoreTake(drawMux, maxWaitLong ) != pdTRUE) return; // WLEDMM acquire draw mutex - clear() can take very long in software I2C mode
u8x8->clear(); // crashes randomly on ESP32
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
}
void setPowerSave(uint8_t save) {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
@@ -296,6 +323,7 @@ class FourLineDisplayUsermod : public Usermod {
void draw2x2GlyphIcons() {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (FLD_SemaphoreTake(drawMuxBig, maxWaitLong) != pdTRUE) return; // WLEDMM acquire BIG draw mutex
if (lineHeight == 2) {
drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x2, true); //brightness icon
drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x2, true); //speed icon
@@ -309,6 +337,7 @@ class FourLineDisplayUsermod : public Usermod {
drawGlyph(15, 2, 4, u8x8_4LineDisplay_WLED_icons_1x1); //palette icon
drawGlyph(15, 3, 5, u8x8_4LineDisplay_WLED_icons_1x1); //effect icon
}
FLD_SemaphoreGive(drawMuxBig); // WLEDMM release BIG draw mutex
}
/**
@@ -379,7 +408,7 @@ class FourLineDisplayUsermod : public Usermod {
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void setup() {
if (!enabled) return; // typeOK = true will be set after successfull setup
if (!enabled) return; // typeOK = true will be set after successful setup
bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
PinOwner po = PinOwner::UM_FourLineDisplay;
@@ -520,6 +549,11 @@ class FourLineDisplayUsermod : public Usermod {
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
setPowerSave(0);
drawing = false;
// init semaphores to enable drawing
FLD_SemaphoreGive(drawMux);
FLD_SemaphoreGive(drawMuxBig);
onUpdateBegin(false); // create Display task // WLEDMM bugfix: before drawing anything
delay(200);
@@ -749,7 +783,7 @@ class FourLineDisplayUsermod : public Usermod {
if (lineHeight==2) { col--; } else { row++; }
drawGlyph(col, row, (bri > 0 ? 9 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // power icon
if (lineHeight==2) { col--; } else { col = row = 0; }
drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nighlight mode
drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nightlight mode
}
/**
@@ -762,7 +796,7 @@ class FourLineDisplayUsermod : public Usermod {
markColNum = newMarkColNum;
}
//Draw the arrow for the current setting beiong changed
//Draw the arrow for the current setting being changed
void drawArrow() {
if (markColNum != 255 && markLineNum !=255) drawGlyph(markColNum, markLineNum*lineHeight, 21, u8x8_4LineDisplay_WLED_icons_1x1);
}
@@ -772,6 +806,8 @@ class FourLineDisplayUsermod : public Usermod {
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
char lineBuffer[MAX_JSON_CHARS] = { '\0' };
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (FLD_SemaphoreTake(drawMuxBig, maxWaitLong) != pdTRUE) return; // WLEDMM acquire BIG draw mutex
if (overlayUntil == 0) {
// Find the mode name in JSON
uint8_t printedChars = extractModeName(inputEffPal, qstring, lineBuffer, MAX_JSON_CHARS-1);
@@ -829,6 +865,8 @@ class FourLineDisplayUsermod : public Usermod {
drawString(1, row*lineHeight, smallBuffer3, true);
}
}
FLD_SemaphoreGive(drawMuxBig); // WLEDMM release BIG draw mutex
}
/**
@@ -1019,6 +1057,7 @@ class FourLineDisplayUsermod : public Usermod {
void sleepOrClock(bool sleepEnable) {
if (sleepEnable) {
displayTurnedOff = true;
//setContrast(contrastFix? 2+ contrast/4 : 0); // un-comment to dim display in "clock mode"
if (clockMode && ntpEnabled) {
knownMinute = knownHour = 99;
showTime();
@@ -1027,6 +1066,7 @@ class FourLineDisplayUsermod : public Usermod {
} else {
displayTurnedOff = false;
setPowerSave(0);
//setContrast(contrast); // un-comment to restore display brightness on wakeup
}
}
@@ -1398,3 +1438,8 @@ const char FourLineDisplayUsermod::_contrastFix[] PROGMEM = "contrastFix";
#ifdef ARDUINO_ARCH_ESP32
FourLineDisplayUsermod *FourLineDisplayUsermod::instance = nullptr;
#endif
// WLEDMM clean up some macros, so they don't cause problems in other usermods
#undef FLD_SemaphoreTake
#undef FLD_SemaphoreGive

View File

@@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2303100
#define VERSION 2303101
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG