Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion examples/companion_radio/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ void halt() {
bool wifi_enabled = false; // set at boot from prefs; false also when the effective SSID is blank
#endif

#if defined(DISPLAY_CLASS) && defined(EINK_REFRESH_LOOP)
static void loopDuringDisplayRefresh();
#endif

void setup() {
Serial.begin(115200);
board.begin();
Expand Down Expand Up @@ -284,15 +288,36 @@ void setup() {
#ifdef DISPLAY_CLASS
ui_task.begin(disp, &sensors, the_mesh.getNodePrefs()); // still want to pass this in as dependency, as prefs might be moved
the_mesh.setListener(&ui_task);
#ifdef EINK_REFRESH_LOOP
if (disp != NULL) disp->setBusyHook(loopDuringDisplayRefresh);
#endif
#endif

board.onBootComplete();
}

void loop() {
// Work that must keep running even while the UI is blocked (e.g. by an e-ink refresh).
// Called from loop() and from the display busy hook, so both always run the same set.
static void loopCore() {
the_mesh.loop();
interface_manager.loop();
sensors.loop();
}

#if defined(DISPLAY_CLASS) && defined(EINK_REFRESH_LOOP)
// Runs from inside the e-ink busy wait, so radio/BLE/sensors keep going during a panel refresh.
// Must not touch the display: UI listener callbacks only update state and schedule the next frame.
static void loopDuringDisplayRefresh() {
static bool active = false;
if (active) return;
active = true;
loopCore();
active = false;
}
#endif

void loop() {
loopCore();
#ifdef DISPLAY_CLASS
ui_task.loop();
#endif
Expand Down
1 change: 1 addition & 0 deletions src/helpers/ui/DisplayDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class DisplayDriver {
virtual bool isOn() = 0;
virtual bool isEink() { return false; } // default to non-eink, override in eink drivers
virtual void forceFullRefresh() {} // next refresh will be full for eink
virtual void setBusyHook(void (*hook)()) {} // eink: hook is called repeatedly while waiting for panel refresh
virtual void turnOn() = 0;
virtual void turnOn(int max_partial) { turnOn(); }
virtual void turnOff() = 0;
Expand Down
9 changes: 9 additions & 0 deletions src/helpers/ui/GxEPDDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ void GxEPDDisplay::drawSleepIcon() {
display.fillRect((x - 5) * scale_x - 1, y * scale_y, (W+5) * scale_x + 2, (H-2) * scale_y + 1, GxEPD_WHITE);
_curr_color = GxEPD_BLACK;
drawXbm(x, y, sleep_icon_24x24, W, H);
if (_busy_hook) display.epd2.setBusyCallback(busyCallback, this);
display.display(true);
display.epd2.setBusyCallback(NULL);
}

void GxEPDDisplay::turnOff(bool blank) {
Expand All @@ -104,8 +106,10 @@ void GxEPDDisplay::turnOff(bool blank) {
void GxEPDDisplay::blankScreen() {
// do full refresh before powering off to clear screen
// no full refresh needed at wakeup
if (_busy_hook) display.epd2.setBusyCallback(busyCallback, this);
display.clearScreen(0xFF); // Clears microcontroller side RAM
display.writeScreenBuffer(0xFF); // Forces 0xFF (White) into the display controller's history registers
display.epd2.setBusyCallback(NULL);
_partial_refresh_count = 0;
last_display_crc_value = 0;
// we can hibernate as screen has been cleaned
Expand All @@ -126,8 +130,10 @@ void GxEPDDisplay::startFrame(ColorVal bkg) {
|| (_max_partial_refresh > 0
&& _partial_refresh_count >= _max_partial_refresh)) {
// forces a full wipe of the screen ...
if (_busy_hook) display.epd2.setBusyCallback(busyCallback, this);
display.clearScreen(0xFF); // Clears microcontroller side RAM
display.writeScreenBuffer(0xFF); // Forces 0xFF (White) into the display controller's history registers
display.epd2.setBusyCallback(NULL);
// we'll need a partial refresh after that (whatever crc value is)
last_display_crc_value = 0;
_partial_refresh_count = 0;
Expand Down Expand Up @@ -239,7 +245,10 @@ void GxEPDDisplay::endFrame() {
if (_isOn == false) return;
uint32_t crc = display_crc.finalize();
if (crc != last_display_crc_value) {
// keep the rest of the firmware running while the panel refreshes (~0.5s partial)
if (_busy_hook) display.epd2.setBusyCallback(busyCallback, this);
display.display(true);
display.epd2.setBusyCallback(NULL);
_partial_refresh_count++;
}
last_display_crc_value = crc;
Expand Down
7 changes: 7 additions & 0 deletions src/helpers/ui/GxEPDDisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class GxEPDDisplay : public DisplayDriver {
bool _force_full_refresh = false;
int _partial_refresh_count = 0;
int _max_partial_refresh = 0;
void (*_busy_hook)() = NULL;

static void busyCallback(const void* self) {
((const GxEPDDisplay*)self)->_busy_hook();
delay(1); // same as GxEPD2 does without a callback
}

public:
#if defined(EINK_DISPLAY_MODEL)
Expand All @@ -53,6 +59,7 @@ class GxEPDDisplay : public DisplayDriver {
bool isEink() override { return true; }
void forceFullRefresh() override { _force_full_refresh = true; }
void turnOn(int max_partial) override;
void setBusyHook(void (*hook)()) override { _busy_hook = hook; }
void turnOn() override;
void turnOff(bool blank) override;
void turnOff() override { turnOff(false); }
Expand Down
2 changes: 2 additions & 0 deletions variants/lilygo_techo/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ build_flags =
${LilyGo_T-Echo.build_flags}
-I src/helpers/ui
-I examples/companion_radio/ui-new
-D EINK_REFRESH_LOOP
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D QSPIFLASH=1
Expand Down Expand Up @@ -120,6 +121,7 @@ build_flags =
${LilyGo_T-Echo.build_flags}
-I src/helpers/ui
-I examples/companion_radio/ui-new
-D EINK_REFRESH_LOOP
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D OFFLINE_QUEUE_SIZE=256
Expand Down
Loading