mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-31 09:18:12 +00:00
Merge 254 upstream commits. Highlights relevant to the Wio fork: native NRF52 companion power-saving (sleep when !hasPendingWork), preamble 16->32 for SF<9, generic sensor-registration model, CMD_SEND_RAW_PACKET, isEink() display abstraction, KEEP_DISPLAY_ON_USB, contacts-sync/transient fixes. Conflicts resolved (11 files): - buzzer.h/.cpp: keep NRF52 include guard + our begin()->startup(); fold in upstream doc comment. - platformio (wio + eink): keep our slim env layout, add upstream kiss_modem env (+ debug_tool=stlink on eink ble). - GxEPDDisplay.h: take upstream isEink() override. - EnvironmentSensorManager.h: take upstream's wider #if guard. - DataStore.cpp: take upstream saveContacts(filter) signature, keep our ::openWrite (member openWrite would shadow the global 2-arg overload). - main.cpp: keep our watchdog init + add board.onBootComplete(); adopt upstream's `if(!hasPendingWork()) sleep` loop. - MyMesh.h: version -> v1.16-solo.0, build date 6 Jun 2026. - MyMesh.cpp: keep both CMD codes (SCREENSHOT 66 + RAW_PACKET 65), both field inits, both handlers, our screenshot fns + upstream saveContacts/save_filter, upstream hasPendingWork() + our MyMeshBot include. - UITask.cpp: keep our solo splash/clock page/snprintf/buzzer init; merge auto-off (+ opt-in KEEP_DISPLAY_ON_USB); merge low-batt shutdown (our EMA + prefs threshold + upstream's !isExternalPowered() guard and eink-aware delay). Post-merge drift fixes (upstream API/architecture changes, not conflicts): - applyTxPower(): radio_set_tx_power() removed upstream -> radio_driver.setTxPower(). - Sensor refactor dropped the per-sensor *_initialized flags our getAvailableLPPTypes() relied on; removed that override and the SENSORS page now derives available LPP types from the populated sensors_lpp buffer. README kept ours via .gitattributes merge=ours. Builds verified on all 5 Wio envs (dual, eink dual, radio_ble, dual_dev, eink_dual_dev). Not yet hardware-verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
149 lines
5.4 KiB
C++
149 lines
5.4 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
|
||
// Return GxEPD2's own reported dimensions (uses WIDTH_VISIBLE, not the full physical WIDTH
|
||
// stored in DisplayDriver). After setRotation(1): width()=HEIGHT, height()=WIDTH_VISIBLE.
|
||
int screenshotWidth() override { return (int)display.width(); }
|
||
int screenshotHeight() override { return (int)display.height(); }
|
||
uint8_t screenshotRotation() override { return (uint8_t)display.getRotation(); }
|
||
#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)
|
||
// Built-in font scale used for size 4 (huge clock digits): 6×8 cell × 7 = 42×56.
|
||
static const int BIG_TEXT_SCALE = 7;
|
||
// Size 1 scales with orientation (portrait 1×, landscape 2×); size 2 is always 12×16;
|
||
// size 3 is FreeSans18pt (~17×28); size 4 is the built-in font at 7× (~42×56),
|
||
// used only for the stacked HH/MM clock on portrait e-ink. Landscape = width >= height.
|
||
int getCharWidth() const override {
|
||
int sc = scale();
|
||
if (_text_sz == 4) return 6 * BIG_TEXT_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 == 4) return 8 * BIG_TEXT_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; }
|
||
bool isEink() override { return true; }
|
||
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;
|
||
};
|