2024-03-03 02:07:29 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "configuration.h"
|
|
|
|
|
|
|
|
#if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY)
|
|
|
|
|
|
|
|
#include "EInkDisplay2.h"
|
|
|
|
#include "GxEPD2_BW.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
Derives from the EInkDisplay adapter class.
|
|
|
|
Accepts suggestions from Screen class about frame type.
|
|
|
|
Determines which refresh type is most suitable.
|
|
|
|
(Full, Fast, Skip)
|
|
|
|
*/
|
|
|
|
|
|
|
|
class EInkDynamicDisplay : public EInkDisplay
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
// Constructor
|
|
|
|
// ( Parameters unused, passed to EInkDisplay. Maintains compatibility OLEDDisplay class )
|
|
|
|
EInkDynamicDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus);
|
|
|
|
~EInkDynamicDisplay();
|
|
|
|
|
|
|
|
// What kind of frame is this
|
|
|
|
enum frameFlagTypes : uint8_t {
|
|
|
|
BACKGROUND = (1 << 0), // For frames via display()
|
|
|
|
RESPONSIVE = (1 << 1), // For frames via forceDisplay()
|
|
|
|
COSMETIC = (1 << 2), // For splashes
|
|
|
|
DEMAND_FAST = (1 << 3), // Special case only
|
|
|
|
};
|
2024-03-09 13:30:16 +00:00
|
|
|
void addFrameFlag(frameFlagTypes flag);
|
2024-03-03 02:07:29 +00:00
|
|
|
|
|
|
|
// Set the correct frame flag, then call universal "update()" method
|
|
|
|
void display() override;
|
|
|
|
bool forceDisplay(uint32_t msecLimit) override; // Shadows base class. Parameter and return val unused.
|
|
|
|
|
|
|
|
protected:
|
|
|
|
enum refreshTypes : uint8_t { // Which refresh operation will be used
|
|
|
|
UNSPECIFIED,
|
|
|
|
FULL,
|
|
|
|
FAST,
|
|
|
|
SKIPPED,
|
|
|
|
};
|
|
|
|
enum reasonTypes : uint8_t { // How was the decision reached
|
|
|
|
NO_OBJECTIONS,
|
2024-03-06 14:26:31 +00:00
|
|
|
ASYNC_REFRESH_BLOCKED_DEMANDFAST,
|
|
|
|
ASYNC_REFRESH_BLOCKED_COSMETIC,
|
|
|
|
ASYNC_REFRESH_BLOCKED_RESPONSIVE,
|
|
|
|
ASYNC_REFRESH_BLOCKED_BACKGROUND,
|
|
|
|
DISPLAY_NOT_READY_FOR_FULL,
|
2024-03-03 02:07:29 +00:00
|
|
|
EXCEEDED_RATELIMIT_FAST,
|
|
|
|
EXCEEDED_RATELIMIT_FULL,
|
|
|
|
FLAGGED_COSMETIC,
|
|
|
|
FLAGGED_DEMAND_FAST,
|
|
|
|
EXCEEDED_LIMIT_FASTREFRESH,
|
|
|
|
EXCEEDED_GHOSTINGLIMIT,
|
|
|
|
FRAME_MATCHED_PREVIOUS,
|
|
|
|
BACKGROUND_USES_FAST,
|
|
|
|
FLAGGED_BACKGROUND,
|
|
|
|
REDRAW_WITH_FULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
void configForFastRefresh(); // GxEPD2 code to set fast-refresh
|
|
|
|
void configForFullRefresh(); // GxEPD2 code to set full-refresh
|
|
|
|
bool determineMode(); // Assess situation, pick a refresh type
|
|
|
|
void applyRefreshMode(); // Run any relevant GxEPD2 code, so next update will use correct refresh type
|
|
|
|
void adjustRefreshCounters(); // Update fastRefreshCount
|
|
|
|
bool update(); // Trigger the display update - determine mode, then call base class
|
|
|
|
|
|
|
|
// Checks as part of determineMode()
|
2024-03-06 14:26:31 +00:00
|
|
|
void checkForPromotion(); // Was a frame skipped (rate, display busy) that should have been a FAST refresh?
|
2024-03-03 02:07:29 +00:00
|
|
|
void checkRateLimiting(); // Is this frame too soon?
|
|
|
|
void checkCosmetic(); // Was the COSMETIC flag set?
|
|
|
|
void checkDemandingFast(); // Was the DEMAND_FAST flag set?
|
|
|
|
void checkFrameMatchesPrevious(); // Does the new frame match the existing display image?
|
2024-03-09 14:38:39 +00:00
|
|
|
void checkConsecutiveFastRefreshes(); // Too many fast-refreshes consecutively?
|
2024-03-03 02:07:29 +00:00
|
|
|
void checkFastRequested(); // Was the flag set for RESPONSIVE, or only BACKGROUND?
|
|
|
|
|
|
|
|
void resetRateLimiting(); // Set previousRunMs - this now counts as an update, for rate-limiting
|
|
|
|
void hashImage(); // Generate a hashed version of this frame, to compare against previous update
|
|
|
|
void storeAndReset(); // Keep results of determineMode() for later, tidy-up for next call
|
|
|
|
|
|
|
|
// What we are determining for this frame
|
2024-03-09 14:07:13 +00:00
|
|
|
frameFlagTypes frameFlags = BACKGROUND; // Frame characteristics - determineMode() input
|
2024-03-03 02:07:29 +00:00
|
|
|
refreshTypes refresh = UNSPECIFIED; // Refresh type - determineMode() output
|
|
|
|
reasonTypes reason = NO_OBJECTIONS; // Reason - why was refresh type used
|
|
|
|
|
|
|
|
// What happened last time determineMode() ran
|
2024-03-09 14:07:13 +00:00
|
|
|
frameFlagTypes previousFrameFlags = BACKGROUND; // (Previous) Frame flags
|
|
|
|
refreshTypes previousRefresh = UNSPECIFIED; // (Previous) Outcome
|
|
|
|
reasonTypes previousReason = NO_OBJECTIONS; // (Previous) Reason
|
2024-03-03 02:07:29 +00:00
|
|
|
|
|
|
|
uint32_t previousRunMs = -1; // When did determineMode() last run (rather than rejecting for rate-limiting)
|
|
|
|
uint32_t imageHash = 0; // Hash of the current frame. Don't bother updating if nothing has changed!
|
|
|
|
uint32_t previousImageHash = 0; // Hash of the previous update's frame
|
|
|
|
uint32_t fastRefreshCount = 0; // How many fast-refreshes consecutively since last full refresh?
|
|
|
|
refreshTypes currentConfig = FULL; // Which refresh type is GxEPD2 currently configured for
|
|
|
|
|
|
|
|
// Optional - track ghosting, pixel by pixel
|
|
|
|
#ifdef EINK_LIMIT_GHOSTING_PX
|
|
|
|
void countGhostPixels(); // Count any pixels which have moved from black to white since last full-refresh
|
|
|
|
void checkExcessiveGhosting(); // Check if ghosting exceeds defined limit
|
|
|
|
void resetGhostPixelTracking(); // Clear the dirty pixels array. Call when full-refresh cleans the display.
|
|
|
|
uint8_t *dirtyPixels; // Any pixels that have been black since last full-refresh (dynamically allocated mem)
|
|
|
|
uint32_t ghostPixelCount = 0; // Number of pixels with problematic ghosting. Retained here for LOG_DEBUG use
|
|
|
|
#endif
|
2024-03-06 14:26:31 +00:00
|
|
|
|
|
|
|
// Conditional - async full refresh - only with modified meshtastic/GxEPD2
|
|
|
|
#if defined(HAS_EINK_ASYNCFULL)
|
|
|
|
void checkAsyncFullRefresh(); // Check the status of "async full-refresh"; run the post-update code if the hardware is ready
|
|
|
|
void endOrDetach(); // Run the post-update code, or delegate it off to checkAsyncFullRefresh()
|
|
|
|
void endUpdate() override {} // Disable base-class behavior of running post-update immediately after forceDisplay()
|
|
|
|
bool asyncRefreshRunning = false; // Flag, checked by checkAsyncFullRefresh()
|
|
|
|
#endif
|
2024-03-03 02:07:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|