Compare commits
1 commit
main
...
feature/ep
Author | SHA1 | Date | |
---|---|---|---|
8906bbee10 |
2 changed files with 96 additions and 98 deletions
176
src/lib/epd.cpp
176
src/lib/epd.cpp
|
@ -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();
|
||||||
|
@ -535,3 +520,14 @@ void EPDManager::prepareDisplayUpdateTask(void* pvParameters) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue