E-Ink (pocketmage_eink)
E-Ink API
The 320x240 GDEQ031T10 e-ink panel is the full-page working display. Text rendering goes through FontEngine via the u8g2f bridge; PocketmageEink owns the panel driver, the refresh policy (fast partial vs slow full), the status bar, and a background refresh task.
Access
#include <pocketmage.h>
PocketmageEink& eink = EINK(); // singleton
DisplayT& einkDisplay = display; // GxEPD2 BW driver
U8G2_FOR_ADAFRUIT_GFX &tf = u8g2f; // text bridge for the panel
EINK() returns the single instance. setupEink() runs in PocketMage_INIT(): it initializes the panel at 16 MHz SPI, sets rotation 3, starts einkHandlerTask on core 0 (10 KB stack, priority 1), and registers the u8g2f bridge with black on white defaults.
Refresh policy
All panel drawing goes through refresh(), which owns the update rhythm:
FAST_REFRESHoff (production default). Everyrefresh()is a full update withdisplay(); a fast waveform is used for normal redraws and a slow ~3s waveform for the periodic clean, tracked byFULL_REFRESH_AFTER. The panel is hibernated afterwards.FAST_REFRESHon (beta default). Differential partial updates (~0.65s) with a full update at boot, after wake, and everyFAST_REFRESH_AFTERpartials; a slow clean removes accumulated ghosting.forceSlowFullUpdate(true)forces the next refresh to take the slow full path regardless of counters.
markPanelNeedsFullRefresh() forces a real full update on the next refresh; the OS calls it at wake so the panel resyncs with GxEPD2’s previous-image RAM.
Class reference
PocketmageEink
class PocketmageEink {
public:
void setLineSpacing(uint8_t lineSpacing);
void setFullRefreshAfter(uint8_t fullRefreshAfter);
void refresh();
void setFastFullRefresh(bool setting);
void statusBar(const String& input, bool fullWindow = false);
void drawStatusBar(const String& input);
void resetDisplay(bool clearScreen = true, uint16_t color = GxEPD_WHITE);
int countLines(const String& input, size_t maxLineLength = 29);
uint8_t getFontHeight();
int maxLines();
uint16_t getEinkTextWidth(const String& s);
uint8_t getLineSpacing();
DisplayT& getDisplay();
void forceSlowFullUpdate(bool force);
void markPanelNeedsFullRefresh();
void lockPanel();
void unlockPanel();
};
refresh
Renders the current buffer per the refresh policy above. It takes the panel mutex for the whole redraw; another task already holding the panel blocks until it finishes. Clears the framebuffer and powers the panel off (or hibernates in the legacy path) afterwards, so the next screen build starts from white.
statusBar / drawStatusBar
Draws the 26px status band: a bounded 20px box with the text in the white region above it, a battery box on the right (30px), and the input text fitted with kLabelCascade + ellipsis. drawStatusBar does the band; statusBar(true) forces a partial-window draw first so only the band area refreshes when the caller relies on display() afterward.
resetDisplay
Sets rotation 3 and the full window, optionally filling color. Call before rebuilding a screen when the prior frame is unknown.
Layout helpers
countLines(input, maxLineLength)counts rendered lines for an unwrapped string (wraps at\nand atmaxLineLength).maxLines()returns how many body lines fit the content region.getFontHeight()andgetEinkTextWidth()measure withFontStyle::Body(see font).
lockPanel / unlockPanel
The recursive mutex. refresh() takes it itself; hold it when a task draws into the framebuffer directly, so a background refresh cannot swap buffers mid-draw. See threading.
The background task
void einkHandler(void *parameter);
Created by setupEink() on core 0: after a 250 ms settle it calls the app hook einkHandler_APP() (app builds) or applicationEinkHandler() (host builds, see pocketmage_globals.h) every 50 ms, then yields. The hook is where an app does its periodic redraws. On deep sleep the task is deleted under the panel lock so an in-flight refresh completes first.
Example
#include <pocketmage.h>
extern "C" int main(int, char**) {
FontEngine::setTextColor(DisplayTarget::EINK, GxEPD_BLACK);
FontEngine::drawText(DisplayTarget::EINK, 8, 120, "hello eink",
FontStyle::Heading1);
EINK().statusBar("pocketmage");
EINK().refresh();
return 0;
}
The buffer is the GLCD framebuffer of display; fill it with