Files

112 lines
4.0 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;