Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
8906bbee10
EPD single instance test 2025-01-05 22:43:02 +01:00
2 changed files with 96 additions and 98 deletions

View file

@ -69,26 +69,7 @@ EPDManager::EPDManager()
, fontSatsymbol{nullptr} , fontSatsymbol{nullptr}
, bgColor{GxEPD_BLACK} , bgColor{GxEPD_BLACK}
, fgColor{GxEPD_WHITE} , fgColor{GxEPD_WHITE}
, displays{ , display{EPD_CLASS(&EPD_CS[0], &EPD_DC, &EPD_RESET[0], &EPD_BUSY[0])}
#ifdef IS_BTCLOCK_V8
EPD_CLASS(&EPD_CS[0], &EPD_DC, &EPD_RESET[0], &EPD_BUSY[0]),
EPD_CLASS(&EPD_CS[1], &EPD_DC, &EPD_RESET[1], &EPD_BUSY[1]),
EPD_CLASS(&EPD_CS[2], &EPD_DC, &EPD_RESET[2], &EPD_BUSY[2]),
EPD_CLASS(&EPD_CS[3], &EPD_DC, &EPD_RESET[3], &EPD_BUSY[3]),
EPD_CLASS(&EPD_CS[4], &EPD_DC, &EPD_RESET[4], &EPD_BUSY[4]),
EPD_CLASS(&EPD_CS[5], &EPD_DC, &EPD_RESET[5], &EPD_BUSY[5]),
EPD_CLASS(&EPD_CS[6], &EPD_DC, &EPD_RESET[6], &EPD_BUSY[6]),
EPD_CLASS(&EPD_CS[7], &EPD_DC, &EPD_RESET[7], &EPD_BUSY[7])
#else
EPD_CLASS(&EPD_CS[0], &EPD_DC, &EPD_RESET[0], &EPD_BUSY[0]),
EPD_CLASS(&EPD_CS[1], &EPD_DC, &EPD_RESET[1], &EPD_BUSY[1]),
EPD_CLASS(&EPD_CS[2], &EPD_DC, &EPD_RESET[2], &EPD_BUSY[2]),
EPD_CLASS(&EPD_CS[3], &EPD_DC, &EPD_RESET[3], &EPD_BUSY[3]),
EPD_CLASS(&EPD_CS[4], &EPD_DC, &EPD_RESET[4], &EPD_BUSY[4]),
EPD_CLASS(&EPD_CS[5], &EPD_DC, &EPD_RESET[5], &EPD_BUSY[5]),
EPD_CLASS(&EPD_CS[6], &EPD_DC, &EPD_RESET[6], &EPD_BUSY[6])
#endif
}
{ {
} }
@ -119,11 +100,10 @@ void EPDManager::initialize() {
String fontName = preferences.getString("fontName", DEFAULT_FONT_NAME); String fontName = preferences.getString("fontName", DEFAULT_FONT_NAME);
loadFonts(fontName); loadFonts(fontName);
// Initialize displays // Initialize first display
switchToDisplay(0);
std::lock_guard<std::mutex> lockMcp(mcpMutex); std::lock_guard<std::mutex> lockMcp(mcpMutex);
for (auto& display : displays) { display.init(0, true, 30);
display.init(0, true, 30);
}
// Create update queue and task // Create update queue and task
updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem)); updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem));
@ -228,46 +208,48 @@ void EPDManager::waitUntilNoneBusy() {
} }
void EPDManager::setupDisplay(uint dispNum, const GFXfont* font) { void EPDManager::setupDisplay(uint dispNum, const GFXfont* font) {
displays[dispNum].setRotation(2); switchToDisplay(dispNum);
displays[dispNum].setFont(font); display.setRotation(2);
displays[dispNum].setTextColor(fgColor); display.setFont(font);
displays[dispNum].fillScreen(bgColor); display.setTextColor(fgColor);
display.fillScreen(bgColor);
} }
void EPDManager::splitText(uint dispNum, const String& top, const String& bottom, bool partial) { void EPDManager::splitText(uint dispNum, const String& top, const String& bottom, bool partial) {
switchToDisplay(dispNum);
if (preferences.getBool("verticalDesc", DEFAULT_VERTICAL_DESC) && dispNum == 0) { if (preferences.getBool("verticalDesc", DEFAULT_VERTICAL_DESC) && dispNum == 0) {
displays[dispNum].setRotation(1); display.setRotation(1);
} else { } else {
displays[dispNum].setRotation(2); display.setRotation(2);
} }
displays[dispNum].setFont(fontSmall); display.setFont(fontSmall);
displays[dispNum].setTextColor(fgColor); display.setTextColor(fgColor);
// Top text // Top text
int16_t ttbx, ttby; int16_t ttbx, ttby;
uint16_t ttbw, ttbh; uint16_t ttbw, ttbh;
displays[dispNum].getTextBounds(top, 0, 0, &ttbx, &ttby, &ttbw, &ttbh); display.getTextBounds(top, 0, 0, &ttbx, &ttby, &ttbw, &ttbh);
uint16_t tx = ((displays[dispNum].width() - ttbw) / 2) - ttbx; uint16_t tx = ((display.width() - ttbw) / 2) - ttbx;
uint16_t ty = ((displays[dispNum].height() - ttbh) / 2) - ttby - ttbh / 2 - 12; uint16_t ty = ((display.height() - ttbh) / 2) - ttby - ttbh / 2 - 12;
// Bottom text // Bottom text
int16_t tbbx, tbby; int16_t tbbx, tbby;
uint16_t tbbw, tbbh; uint16_t tbbw, tbbh;
displays[dispNum].getTextBounds(bottom, 0, 0, &tbbx, &tbby, &tbbw, &tbbh); display.getTextBounds(bottom, 0, 0, &tbbx, &tbby, &tbbw, &tbbh);
uint16_t bx = ((displays[dispNum].width() - tbbw) / 2) - tbbx; uint16_t bx = ((display.width() - tbbw) / 2) - tbbx;
uint16_t by = ((displays[dispNum].height() - tbbh) / 2) - tbby + tbbh / 2 + 12; uint16_t by = ((display.height() - tbbh) / 2) - tbby + tbbh / 2 + 12;
// Make separator as wide as the shortest text // Make separator as wide as the shortest text
uint16_t lineWidth = (tbbw < ttbh) ? tbbw : ttbw; uint16_t lineWidth = (tbbw < ttbh) ? tbbw : ttbw;
uint16_t lineX = round((displays[dispNum].width() - lineWidth) / 2); uint16_t lineX = round((display.width() - lineWidth) / 2);
displays[dispNum].fillScreen(bgColor); display.fillScreen(bgColor);
displays[dispNum].setCursor(tx, ty); display.setCursor(tx, ty);
displays[dispNum].print(top); display.print(top);
displays[dispNum].fillRoundRect(lineX, displays[dispNum].height() / 2 - 3, display.fillRoundRect(lineX, display.height() / 2 - 3,
lineWidth, 6, 3, fgColor); lineWidth, 6, 3, fgColor);
displays[dispNum].setCursor(bx, by); display.setCursor(bx, by);
displays[dispNum].print(bottom); display.print(bottom);
} }
void EPDManager::showDigit(uint dispNum, char chr, bool partial, const GFXfont* font) { void EPDManager::showDigit(uint dispNum, char chr, bool partial, const GFXfont* font) {
@ -280,17 +262,17 @@ void EPDManager::showDigit(uint dispNum, char chr, bool partial, const GFXfont*
int16_t tbx, tby; int16_t tbx, tby;
uint16_t tbw, tbh; uint16_t tbw, tbh;
displays[dispNum].getTextBounds(str, 0, 0, &tbx, &tby, &tbw, &tbh); display.getTextBounds(str, 0, 0, &tbx, &tby, &tbw, &tbh);
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx; uint16_t x = ((display.width() - tbw) / 2) - tbx;
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby; uint16_t y = ((display.height() - tbh) / 2) - tby;
displays[dispNum].setCursor(x, y); display.setCursor(x, y);
displays[dispNum].print(str); display.print(str);
if (chr == '.') { if (chr == '.') {
displays[dispNum].fillRect(0, 0, displays[dispNum].width(), display.fillRect(0, 0, display.width(),
round(displays[dispNum].height() * 0.67), bgColor); round(display.height() * 0.67), bgColor);
} }
} }
@ -299,11 +281,11 @@ void EPDManager::showChars(uint dispNum, const String& chars, bool partial, cons
int16_t tbx, tby; int16_t tbx, tby;
uint16_t tbw, tbh; uint16_t tbw, tbh;
displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh); display.getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh);
// Center the bounding box by transposition of the origin // Center the bounding box by transposition of the origin
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx; uint16_t x = ((display.width() - tbw) / 2) - tbx;
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby; uint16_t y = ((display.height() - tbh) / 2) - tby;
for (size_t i = 0; i < chars.length(); i++) { for (size_t i = 0; i < chars.length(); i++) {
char c = chars[i]; char c = chars[i];
@ -313,12 +295,12 @@ void EPDManager::showChars(uint dispNum, const String& chars, bool partial, cons
int16_t dotDescent = dotGlyph->yOffset; int16_t dotDescent = dotGlyph->yOffset;
// Draw the dot with adjusted y-position // Draw the dot with adjusted y-position
displays[dispNum].setCursor(x, y + dotDescent + dotGlyph->height + 8); display.setCursor(x, y + dotDescent + dotGlyph->height + 8);
displays[dispNum].print(c); display.print(c);
} else { } else {
// For other characters, use the original y-position // For other characters, use the original y-position
displays[dispNum].setCursor(x, y); display.setCursor(x, y);
displays[dispNum].print(c); display.print(c);
} }
// Move x-position for the next character // Move x-position for the next character
@ -327,11 +309,12 @@ void EPDManager::showChars(uint dispNum, const String& chars, bool partial, cons
} }
bool EPDManager::renderIcon(uint dispNum, const String& text, bool partial) { bool EPDManager::renderIcon(uint dispNum, const String& text, bool partial) {
displays[dispNum].setRotation(2); switchToDisplay(dispNum);
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), display.setRotation(2);
displays[dispNum].height()); display.setPartialWindow(0, 0, display.width(),
displays[dispNum].fillScreen(bgColor); display.height());
displays[dispNum].setTextColor(fgColor); display.fillScreen(bgColor);
display.setTextColor(fgColor);
uint iconIndex = 0; uint iconIndex = 0;
uint width = 122; uint width = 122;
@ -352,27 +335,28 @@ bool EPDManager::renderIcon(uint dispNum, const String& text, bool partial) {
return false; return false;
} }
int x_offset = (displays[dispNum].width() - logo.width) / 2; int x_offset = (display.width() - logo.width) / 2;
int y_offset = (displays[dispNum].height() - logo.height) / 2; int y_offset = (display.height() - logo.height) / 2;
displays[dispNum].drawInvertedBitmap(x_offset, y_offset, logo.data, display.drawInvertedBitmap(x_offset, y_offset, logo.data,
logo.width, logo.height, fgColor); logo.width, logo.height, fgColor);
return true; return true;
} }
int x_offset = (displays[dispNum].width() - width) / 2; int x_offset = (display.width() - width) / 2;
int y_offset = (displays[dispNum].height() - height) / 2; int y_offset = (display.height() - height) / 2;
displays[dispNum].drawInvertedBitmap(x_offset, y_offset, epd_icons_allArray[iconIndex], display.drawInvertedBitmap(x_offset, y_offset, epd_icons_allArray[iconIndex],
width, height, fgColor); width, height, fgColor);
return true; return true;
} }
void EPDManager::renderText(uint dispNum, const String& text, bool partial) { void EPDManager::renderText(uint dispNum, const String& text, bool partial) {
displays[dispNum].setRotation(2); switchToDisplay(dispNum);
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), display.setRotation(2);
displays[dispNum].height()); display.setPartialWindow(0, 0, display.width(),
displays[dispNum].fillScreen(GxEPD_WHITE); display.height());
displays[dispNum].setTextColor(GxEPD_BLACK); display.fillScreen(GxEPD_WHITE);
displays[dispNum].setCursor(0, 50); display.setTextColor(GxEPD_BLACK);
display.setCursor(0, 50);
std::stringstream ss; std::stringstream ss;
ss.str(text.c_str()); ss.str(text.c_str());
@ -381,16 +365,17 @@ void EPDManager::renderText(uint dispNum, const String& text, bool partial) {
while (std::getline(ss, line, '\n')) { while (std::getline(ss, line, '\n')) {
if (line.rfind("*", 0) == 0) { if (line.rfind("*", 0) == 0) {
line.erase(std::remove(line.begin(), line.end(), '*'), line.end()); line.erase(std::remove(line.begin(), line.end(), '*'), line.end());
displays[dispNum].setFont(&FreeSansBold9pt7b); display.setFont(&FreeSansBold9pt7b);
} else { } else {
displays[dispNum].setFont(&FreeSans9pt7b); display.setFont(&FreeSans9pt7b);
} }
displays[dispNum].println(line.c_str()); display.println(line.c_str());
} }
} }
void EPDManager::renderQr(uint dispNum, const String& text, bool partial) { void EPDManager::renderQr(uint dispNum, const String& text, bool partial) {
#ifdef USE_QR #ifdef USE_QR
switchToDisplay(dispNum);
// Dynamically allocate QR buffer // Dynamically allocate QR buffer
uint8_t* qrcode = (uint8_t*)malloc(qrcodegen_BUFFER_LEN_MAX); uint8_t* qrcode = (uint8_t*)malloc(qrcodegen_BUFFER_LEN_MAX);
if (!qrcode) { if (!qrcode) {
@ -405,17 +390,17 @@ void EPDManager::renderQr(uint dispNum, const String& text, bool partial) {
if (ok) { if (ok) {
const int size = qrcodegen_getSize(qrcode); const int size = qrcodegen_getSize(qrcode);
const int padding = floor(float(displays[dispNum].width() - (size * 4)) / 2); const int padding = floor(float(display.width() - (size * 4)) / 2);
const int paddingY = floor(float(displays[dispNum].height() - (size * 4)) / 2); const int paddingY = floor(float(display.height() - (size * 4)) / 2);
displays[dispNum].setRotation(2); display.setRotation(2);
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), display.setPartialWindow(0, 0, display.width(),
displays[dispNum].height()); display.height());
displays[dispNum].fillScreen(GxEPD_WHITE); display.fillScreen(GxEPD_WHITE);
for (int y = 0; y < size * 4; y++) { for (int y = 0; y < size * 4; y++) {
for (int x = 0; x < size * 4; x++) { for (int x = 0; x < size * 4; x++) {
displays[dispNum].drawPixel( display.drawPixel(
padding + x, paddingY + y, padding + x, paddingY + y,
qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4)) qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4))
? GxEPD_BLACK ? GxEPD_BLACK
@ -449,10 +434,10 @@ void EPDManager::updateDisplayTask(void* pvParameters) noexcept {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
std::lock_guard<std::mutex> lock(instance.displayMutexes[epdIndex]); std::lock_guard<std::mutex> lock(instance.displayMutexes[epdIndex]);
{
std::lock_guard<std::mutex> lockMcp(mcpMutex); std::lock_guard<std::mutex> lockMcp(mcpMutex);
instance.displays[epdIndex].init(0, false, 40); instance.initializeDisplay(epdIndex);
}
uint32_t count = 0; uint32_t count = 0;
while (instance.EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10) { while (instance.EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10) {
@ -469,8 +454,8 @@ void EPDManager::updateDisplayTask(void* pvParameters) noexcept {
char tries = 0; char tries = 0;
while (tries < 3) { while (tries < 3) {
if (instance.displays[epdIndex].displayWithReturn(updatePartial)) { if (instance.display.displayWithReturn(updatePartial)) {
instance.displays[epdIndex].powerOff(); instance.display.powerOff();
instance.currentContent[epdIndex] = instance.content[epdIndex]; instance.currentContent[epdIndex] = instance.content[epdIndex];
if (!updatePartial) { if (!updatePartial) {
instance.lastFullRefresh[epdIndex] = millis(); instance.lastFullRefresh[epdIndex] = millis();
@ -534,4 +519,15 @@ void EPDManager::prepareDisplayUpdateTask(void* pvParameters) {
xTaskNotifyGive(instance.tasks[epdIndex]); xTaskNotifyGive(instance.tasks[epdIndex]);
} }
} }
} }
void EPDManager::switchToDisplay(uint dispNum) {
display.setCSPin(&EPD_CS[dispNum]);
display.setRSTPin(&EPD_RESET[dispNum]);
display.setBusyPin(&EPD_BUSY[dispNum]);
}
void EPDManager::initializeDisplay(uint dispNum) {
switchToDisplay(dispNum);
display.init(0, false, 40);
}

View file

@ -80,6 +80,8 @@ private:
static void updateDisplayTask(void* pvParameters) noexcept; static void updateDisplayTask(void* pvParameters) noexcept;
static void prepareDisplayUpdateTask(void* pvParameters); static void prepareDisplayUpdateTask(void* pvParameters);
void switchToDisplay(uint dispNum);
void initializeDisplay(uint dispNum);
// Member variables // Member variables
std::array<String, NUM_SCREENS> currentContent; std::array<String, NUM_SCREENS> currentContent;
@ -119,8 +121,8 @@ private:
static std::array<MCP23X17_Pin, NUM_SCREENS> EPD_RESET; static std::array<MCP23X17_Pin, NUM_SCREENS> EPD_RESET;
#endif #endif
// Display array // Single display instance
std::array<GxEPD2_BW<EPD_CLASS, EPD_CLASS::HEIGHT>, NUM_SCREENS> displays; GxEPD2_BW<EPD_CLASS, EPD_CLASS::HEIGHT> display;
static constexpr size_t UPDATE_QUEUE_SIZE = 14; static constexpr size_t UPDATE_QUEUE_SIZE = 14;
static constexpr uint32_t BUSY_TIMEOUT_COUNT = 200; static constexpr uint32_t BUSY_TIMEOUT_COUNT = 200;