112 lines
4.0 KiB
C++
112 lines
4.0 KiB
C++
#pragma once
|
||
|
||
/**
|
||
* @file DisplayManager.h
|
||
* @brief E-Ink display driver abstraction using GxEPD2_BW.
|
||
*
|
||
* Responsibilities:
|
||
* - Manage the 400×300 Waveshare 4.2" GxEPD2 display via SPI.
|
||
* - Render a 2×2 tile grid; each tile shows one data field.
|
||
* - Perform partial refresh for numeric value updates (fast, no flicker).
|
||
* - Perform full refresh on page change or periodically to prevent ghosting.
|
||
* - Show a splash/boot screen, BLE status overlay and AP-mode notice.
|
||
*
|
||
* Partial refresh strategy:
|
||
* Each tile's "value area" (excluding the fixed label and border) is
|
||
* re-drawn with partial update. The borders and labels are only redrawn
|
||
* during a full refresh, saving ~250 ms per cycle.
|
||
*/
|
||
|
||
#include "Config.h"
|
||
#include <GxEPD2_BW.h>
|
||
#include <Fonts/FreeMonoBold24pt7b.h>
|
||
#include <Fonts/FreeMonoBold12pt7b.h>
|
||
#include <Fonts/FreeMono9pt7b.h>
|
||
|
||
// ── Display model — match the validated reference sketch for the 4.2" panel.
|
||
// The generic 420 driver is used here to avoid a controller mismatch that can
|
||
// leave the panel completely blank during init.
|
||
using DisplayType = GxEPD2_BW<GxEPD2_420,
|
||
GxEPD2_420::HEIGHT>;
|
||
|
||
class DisplayManager {
|
||
public:
|
||
/**
|
||
* @brief Initialise the SPI bus and display. Shows the splash screen.
|
||
* Must be called from setup() before any render methods.
|
||
*/
|
||
void begin();
|
||
|
||
/**
|
||
* @brief Main render call — invoke from loop().
|
||
* Decides between partial and full refresh based on:
|
||
* - Whether data has changed since the last render.
|
||
* - Whether FULL_REFRESH_INTERVAL_MS has elapsed.
|
||
* - An explicit forceFullRefresh() request.
|
||
* @param state Current boat data snapshot.
|
||
* @param cfg Current page configuration.
|
||
*/
|
||
void render(const BoatState& state, const PageConfig& cfg);
|
||
|
||
/** @brief Request a full (slow) refresh on the next render() call. */
|
||
void forceFullRefresh() { _needFullRefresh = true; }
|
||
|
||
/** @brief Show a BLE scanning / connecting notice. */
|
||
void showBleStatus(AppState::BleStatus status);
|
||
|
||
/** @brief Show the AP mode access screen with SSID and IP. */
|
||
void showApScreen(const char* ssid, const char* ip);
|
||
|
||
/** @brief Show the OTA progress screen. */
|
||
void showOtaScreen(uint8_t percent);
|
||
|
||
/** @brief Show the passkey entry prompt. */
|
||
void showPasskeyPrompt(uint32_t pin);
|
||
|
||
/** @brief Hibernate the display to save power (full blank). */
|
||
void hibernate();
|
||
|
||
// Internal access for GxEPD2
|
||
DisplayType& epd() { return _display; }
|
||
|
||
private:
|
||
DisplayType _display {
|
||
GxEPD2_420(Pins::EPD_CS,
|
||
Pins::EPD_DC,
|
||
Pins::EPD_RST,
|
||
Pins::EPD_BUSY)
|
||
};
|
||
|
||
bool _initialised = false;
|
||
bool _needFullRefresh = true;
|
||
uint32_t _lastFullRefresh = 0;
|
||
uint32_t _lastRender = 0;
|
||
|
||
// Cached value strings per tile to detect changes
|
||
char _prevValue[Display::TILE_COUNT][16];
|
||
|
||
// ── Drawing helpers ───────────────────────────────────────────────────────
|
||
|
||
void _drawSplash();
|
||
void _drawGrid();
|
||
void _drawTile(uint8_t idx, const TileConfig& tile, const BoatState& state, bool partial);
|
||
void _drawTileLabel(uint8_t idx, const TileConfig& tile);
|
||
void _drawTileValue(uint8_t idx, const char* value, bool partial);
|
||
|
||
// Compute the pixel rect for a tile
|
||
struct Rect { uint16_t x, y, w, h; };
|
||
Rect _tileRect(uint8_t idx) const;
|
||
Rect _valuRect(uint8_t idx) const; ///< Sub-rect for the value only
|
||
|
||
// Extract a display string from the boat state for a given field
|
||
void _fieldToString(DataField field, const BoatState& state,
|
||
char* out, size_t outLen) const;
|
||
|
||
// Centre text in a rect
|
||
void _drawCentred(const char* text, uint16_t x, uint16_t y,
|
||
uint16_t w, uint16_t h);
|
||
};
|
||
|
||
// Singleton accessor
|
||
extern DisplayManager displayManager;
|