diff --git a/src/graphics/EInkDisplay.cpp b/src/graphics/EInkDisplay.cpp index a731b3ef..4b296a2d 100644 --- a/src/graphics/EInkDisplay.cpp +++ b/src/graphics/EInkDisplay.cpp @@ -54,8 +54,10 @@ EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl) // FIXME quick hack to limit drawing to a very slow rate uint32_t lastDrawMsec; -// Write the buffer to the display memory -void EInkDisplay::display(void) +/** + * Force a display update if we haven't drawn within the specified msecLimit + */ +bool EInkDisplay::forceDisplay(uint32_t msecLimit) { // No need to grab this lock because we are on our own SPI bus // concurrency::LockGuard g(spiLock); @@ -63,7 +65,7 @@ void EInkDisplay::display(void) uint32_t now = millis(); uint32_t sinceLast = now - lastDrawMsec; - if (framePtr && (sinceLast > 60 * 1000 || lastDrawMsec == 0)) { + if (framePtr && (sinceLast > msecLimit || lastDrawMsec == 0)) { lastDrawMsec = now; // FIXME - only draw bits have changed (use backbuf similar to the other displays) @@ -84,11 +86,26 @@ void EInkDisplay::display(void) updateDisplay(); // Send image to display and refresh DEBUG_MSG("done\n"); - // Put screen to sleep to save power + // Put screen to sleep to save power ePaper.Sleep(); + return true; + } else { + // DEBUG_MSG("Skipping eink display\n"); + return false; } } +// Write the buffer to the display memory +void EInkDisplay::display(void) +{ + // We don't allow regular 'dumb' display() calls to draw on eink until we've shown + // at least one forceDisplay() keyframe. This prevents flashing when we should the critical + // bootscreen (that we want to look nice) + if (lastDrawMsec) + if (forceDisplay(slowUpdateMsec)) // Show the first screen a few seconds after boot, then slower + slowUpdateMsec = 5 * 60 * 1000; +} + // Send a command to the display (low level function) void EInkDisplay::sendCommand(uint8_t com) { diff --git a/src/graphics/EInkDisplay.h b/src/graphics/EInkDisplay.h index f12ad688..a36c1a77 100644 --- a/src/graphics/EInkDisplay.h +++ b/src/graphics/EInkDisplay.h @@ -14,15 +14,26 @@ */ class EInkDisplay : public OLEDDisplay { + /// How often should we update the display, at first we do an update 5 secs after boot, + /// thereafter we do once per 5 minutes + uint32_t slowUpdateMsec = 5 * 1000; + public: /* constructor FIXME - the parameters are not used, just a temporary hack to keep working like the old displays */ EInkDisplay(uint8_t address, int sda, int scl); - // Write the buffer to the display memory + // Write the buffer to the display memory (for eink we only do this occasionally) virtual void display(void); + /** + * Force a display update if we haven't drawn within the specified msecLimit + * + * @return true if we did draw the screen + */ + bool forceDisplay(uint32_t msecLimit = 2000); + protected: // the header size of the buffer used, e.g. for the SPI command header virtual int getBufferOffset(void) { return 0; } @@ -33,3 +44,5 @@ class EInkDisplay : public OLEDDisplay // Connect to the display virtual bool connect(); }; + + diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index cba5648c..a5c15f49 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -72,7 +72,10 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1 // draw an xbm image. // Please note that everything that should be transitioned // needs to be drawn relative to x and y - display->drawXbm(x + 32, y, icon_width, icon_height, (const uint8_t *)icon_bits); + + // draw centered left to right and centered above the one line of app text + display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_16 - icon_height) / 2, icon_width, + icon_height, (const uint8_t *)icon_bits); display->setFont(ArialMT_Plain_16); display->setTextAlignment(TEXT_ALIGN_CENTER); @@ -85,6 +88,7 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1 snprintf(buf, sizeof(buf), "%s", xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long display->drawString(SCREEN_WIDTH - 20, 0, buf); + screen->forceDisplay(); } static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) @@ -570,7 +574,8 @@ void _screen_header() } #endif -Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl), ui(&dispdev) { +Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl), ui(&dispdev) +{ cmdQueue.setReader(this); } @@ -608,7 +613,7 @@ void Screen::setup() displayWidth = dispdev.width(); displayHeight = dispdev.height(); - + ui.setTimePerTransition(300); // msecs ui.setIndicatorPosition(BOTTOM); // Defines where the first frame is located in the bar. @@ -658,6 +663,14 @@ void Screen::setup() nodeStatusObserver.observe(&nodeStatus->onNewStatus); } +void Screen::forceDisplay() +{ + // Nasty hack to force epaper updates for 'key' frames. FIXME, cleanup. +#ifdef HAS_EINK + dispdev.forceDisplay(); +#endif +} + int32_t Screen::runOnce() { // If we don't have a screen, don't ever spend any CPU for us. @@ -722,6 +735,7 @@ int32_t Screen::runOnce() DEBUG_MSG("Setting idle framerate\n"); targetFramerate = IDLE_FRAMERATE; ui.setTargetFPS(targetFramerate); + forceDisplay(); } // While showing the bootscreen or Bluetooth pair screen all of our diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index e4007aea..7c8d3435 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -180,6 +180,9 @@ class Screen : public concurrency::OSThread int handleStatusUpdate(const meshtastic::Status *arg); + /// Used to force (super slow) eink displays to draw critical frames + void forceDisplay(); + protected: /// Updates the UI. //