mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-26 14:58:12 +00:00
- Add lib/GxEPD2-patch/src/GxEPD2_BW.h: local patched copy of GxEPD2_BW.h
that exposes getBuffer()/getBufferSize() under ENABLE_SCREENSHOT guard.
Include guard (_GxEPD2_BW_H_) prevents double-inclusion of the installed
library version.
- GxEPDDisplay.h: use patched header via relative include when
ENABLE_SCREENSHOT so the patch takes precedence over the installed lib
(PlatformIO adds library paths before -I build_flags).
- DisplayDriver.h: add virtual getBuffer()/getBufferSize()/getDisplayType()
defaults (nullptr/0/0) under ENABLE_SCREENSHOT.
- SH1106Display.h / SSD1306Display.h / GxEPDDisplay.h: add concrete overrides;
getDisplayType() returns 0 (OLED) or 1 (e-ink).
- MyMesh.cpp/h: replace fragile C-cast with virtual dispatch in
handleScreenshotRequest(); extend 5-byte header to 6 bytes by appending
display_type so the host tool can decode the correct pixel layout.
- tools/screenshot.py: parse 6-byte header; add eink_buffer_to_image()
that decodes the row-major MSB-first GxEPD2 buffer with DISPLAY_ROTATION=1
(phys_x = 127-ly, phys_y = lx); dispatch on display_type.
- variants/wio-tracker-l1-eink/platformio.ini: add
[env:WioTrackerL1Eink_companion_dual_dev] with ENABLE_SCREENSHOT.
- variants/wio-tracker-l1/platformio.ini: unchanged (OLED env already exists).
Builds verified: WioTrackerL1Eink_companion_dual_dev SUCCESS,
WioTrackerL1Eink_companion_radio_ble SUCCESS (unaffected).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
138 lines
4.6 KiB
C++
138 lines
4.6 KiB
C++
#pragma once
|
||
|
||
#include <SPI.h>
|
||
#include <Wire.h>
|
||
|
||
#define ENABLE_GxEPD2_GFX 0
|
||
|
||
// When ENABLE_SCREENSHOT is active, use the patched copy of GxEPD2_BW.h from
|
||
// lib/GxEPD2-patch/src/ that exposes getBuffer()/getBufferSize(). The include
|
||
// guard (_GxEPD2_BW_H_) then prevents the installed library version from
|
||
// being compiled again. Non-screenshot builds use the installed library as-is.
|
||
#ifdef ENABLE_SCREENSHOT
|
||
#include "../../../lib/GxEPD2-patch/src/GxEPD2_BW.h"
|
||
#else
|
||
#include <GxEPD2_BW.h>
|
||
#endif
|
||
#include <GxEPD2_3C.h>
|
||
#include <GxEPD2_4C.h>
|
||
#include <GxEPD2_7C.h>
|
||
#include <Fonts/FreeSans9pt7b.h>
|
||
#include <Fonts/FreeSansBold12pt7b.h>
|
||
#include <Fonts/FreeSans18pt7b.h>
|
||
#include <CRC32.h>
|
||
|
||
#include "DisplayDriver.h"
|
||
#include "LemonFont.h"
|
||
|
||
#ifndef DISPLAY_ROTATION
|
||
#define DISPLAY_ROTATION 0
|
||
#endif
|
||
|
||
// Panel native dimensions before rotation. Derived from the model class when
|
||
// EINK_DISPLAY_MODEL is set; override with EINK_PANEL_W/H for other panels.
|
||
#if defined(EINK_DISPLAY_MODEL)
|
||
#ifndef EINK_PANEL_W
|
||
#define EINK_PANEL_W EINK_DISPLAY_MODEL::WIDTH
|
||
#endif
|
||
#ifndef EINK_PANEL_H
|
||
#define EINK_PANEL_H EINK_DISPLAY_MODEL::HEIGHT
|
||
#endif
|
||
#else
|
||
#ifndef EINK_PANEL_W
|
||
#define EINK_PANEL_W 200
|
||
#define EINK_PANEL_H 200
|
||
#endif
|
||
#endif
|
||
|
||
// Odd rotations (1, 3) swap width and height.
|
||
#define EINK_DISP_W ((DISPLAY_ROTATION & 1) ? EINK_PANEL_H : EINK_PANEL_W)
|
||
#define EINK_DISP_H ((DISPLAY_ROTATION & 1) ? EINK_PANEL_W : EINK_PANEL_H)
|
||
|
||
class GxEPDDisplay : public DisplayDriver {
|
||
#if defined(EINK_DISPLAY_MODEL)
|
||
GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT> display;
|
||
#else
|
||
GxEPD2_BW<GxEPD2_150_BN, 200> display;
|
||
#endif
|
||
bool _init = false;
|
||
bool _isOn = false;
|
||
bool _use_lemon = false;
|
||
uint16_t _curr_color;
|
||
CRC32 display_crc;
|
||
int last_display_crc_value = 0;
|
||
int _text_sz = 1;
|
||
uint8_t _full_refresh_interval = 0;
|
||
uint8_t _partial_count = 0;
|
||
|
||
int16_t drawLemonChar(int16_t x, int16_t y, uint32_t cp, int sc);
|
||
uint8_t lemonXAdvance(uint32_t cp, int sc);
|
||
int scale() const { return (width() >= height()) ? 2 : 1; }
|
||
|
||
public:
|
||
#if defined(EINK_DISPLAY_MODEL)
|
||
GxEPDDisplay() : DisplayDriver(EINK_DISP_W, EINK_DISP_H),
|
||
display(EINK_DISPLAY_MODEL(PIN_DISPLAY_CS, PIN_DISPLAY_DC, PIN_DISPLAY_RST, PIN_DISPLAY_BUSY)) {}
|
||
#else
|
||
GxEPDDisplay() : DisplayDriver(EINK_DISP_W, EINK_DISP_H),
|
||
display(GxEPD2_150_BN(DISP_CS, DISP_DC, DISP_RST, DISP_BUSY)) {}
|
||
#endif
|
||
|
||
#ifdef ENABLE_SCREENSHOT
|
||
const uint8_t* getBuffer() override { return display.getBuffer(); }
|
||
uint16_t getBufferSize() override { return display.getBufferSize(); }
|
||
uint8_t getDisplayType() override { return 1; } // 1 = e-ink
|
||
#endif
|
||
|
||
// Line height and approx. char width for each font size:
|
||
// 1 = FreeSans9pt (lineH=16, charW≈9)
|
||
// 2 = FreeSansBold12pt (lineH=20, charW≈12)
|
||
// 3 = FreeSans18pt (lineH=28, charW≈17)
|
||
// Size 1 scales with orientation (portrait 1×, landscape 2×); size 2 is always 12×16;
|
||
// size 3 is FreeSans18pt (~17×28). Landscape = width >= height.
|
||
int getCharWidth() const override {
|
||
int sc = scale();
|
||
if (_text_sz == 3) return 17;
|
||
if (_text_sz == 2) return 12 * sc;
|
||
return (_use_lemon ? 5 : 6) * sc;
|
||
}
|
||
int getLineHeight() const override {
|
||
int sc = scale();
|
||
if (_text_sz == 3) return 28;
|
||
if (_text_sz == 2) return 16 * sc;
|
||
return (_use_lemon ? 10 : 8) * sc;
|
||
}
|
||
void setLemonFont(bool enabled) override { _use_lemon = enabled; _vw_dirty = true; }
|
||
bool isLemonFont() const override { return _use_lemon; }
|
||
void translateUTF8ToBlocks(char* dest, const char* src, size_t dest_size) override {
|
||
if (_use_lemon) {
|
||
strncpy(dest, src, dest_size - 1);
|
||
dest[dest_size - 1] = '\0';
|
||
} else {
|
||
translateUTF8Static(dest, src, dest_size);
|
||
}
|
||
}
|
||
bool begin();
|
||
|
||
bool isOn() override { return _isOn; }
|
||
void turnOn() override;
|
||
void turnOff() override;
|
||
void clear() override;
|
||
void startFrame(Color bkg = DARK) override;
|
||
void setTextSize(int sz) override;
|
||
void setColor(Color c) override;
|
||
void setCursor(int x, int y) override;
|
||
void print(const char* str) override;
|
||
void fillRect(int x, int y, int w, int h) override;
|
||
void drawRect(int x, int y, int w, int h) override;
|
||
void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override;
|
||
uint16_t getTextWidth(const char* str) override;
|
||
uint16_t getCodepointWidth(uint32_t cp) override {
|
||
if (_use_lemon && _text_sz == 1) return lemonXAdvance(cp, scale());
|
||
return DisplayDriver::getCodepointWidth(cp);
|
||
}
|
||
void setDisplayRotation(uint8_t rot) override;
|
||
void setFullRefreshInterval(uint8_t n) override { _full_refresh_interval = n; _partial_count = 0; }
|
||
void endFrame() override;
|
||
};
|