init commit

This commit is contained in:
feeling001@gmail.com
2026-06-18 15:04:18 +02:00
commit 8bab06b72a
22 changed files with 3258 additions and 0 deletions
+110
View File
@@ -0,0 +1,110 @@
#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 — Waveshare 4.2" mono GDEY042T81 (400×300) ─────────────────
// Adjust the model constant if your display differs.
using DisplayType = GxEPD2_BW<GxEPD2_420_GDEY042T81,
GxEPD2_420_GDEY042T81::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_GDEY042T81(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;