mirror of
https://github.com/meshtastic/firmware.git
synced 2025-02-08 05:31:25 +00:00
Refactor EInkDisplay (#3299)
* Refactor EInkDisplay A lot of variant specific code is merged, with the macros pushed to the respective variant.h files. "Dynamic Partial" code has been purged, pending a rewrite. * fix: declare class only if USE_EINK, init all members * refactor: move macros to platformio.ini Responds to https://github.com/meshtastic/firmware/pull/3299#issuecomment-1966425926 * fix: EInkDisplay::connect() references old macros Usage was in a block of variant-specific code, which had been intentionally left untouched. * fix: remove duplicate macros from variant.h --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
parent
7aee014f5e
commit
6acc63729b
@ -2,127 +2,49 @@
|
||||
|
||||
#ifdef USE_EINK
|
||||
#include "EInkDisplay2.h"
|
||||
#include "GxEPD2_BW.h"
|
||||
#include "SPILock.h"
|
||||
#include "main.h"
|
||||
#include <SPI.h>
|
||||
|
||||
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0)
|
||||
SPIClass *hspi = NULL;
|
||||
#endif
|
||||
/*
|
||||
The macros EINK_DISPLAY_MODEL, EINK_WIDTH, and EINK_HEIGHT are defined as build_flags in a variant's platformio.ini
|
||||
Previously, these macros were defined at the top of this file.
|
||||
|
||||
#define COLORED GxEPD_BLACK
|
||||
#define UNCOLORED GxEPD_WHITE
|
||||
For archival reasons, note that the following configurations had also been tested during this period:
|
||||
* ifdef RAK4631
|
||||
- 4.2 inch
|
||||
EINK_DISPLAY_MODEL: GxEPD2_420_M01
|
||||
EINK_WIDTH: 300
|
||||
EINK_WIDTH: 400
|
||||
|
||||
#if defined(TTGO_T_ECHO)
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_154_D67
|
||||
#elif defined(RAK4630)
|
||||
- 2.9 inch
|
||||
EINK_DISPLAY_MODEL: GxEPD2_290_T5D
|
||||
EINK_WIDTH: 296
|
||||
EINK_HEIGHT: 128
|
||||
|
||||
// GxEPD2_213_BN - RAK14000 2.13 inch b/w 250x122 - changed from GxEPD2_213_B74 - which was not going to give fast refresh
|
||||
// support
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_213_BN
|
||||
|
||||
// 4.2 inch 300x400 - GxEPD2_420_M01
|
||||
// #define TECHO_DISPLAY_MODEL GxEPD2_420_M01
|
||||
|
||||
// 2.9 inch 296x128 - GxEPD2_290_T5D
|
||||
// #define TECHO_DISPLAY_MODEL GxEPD2_290_T5D
|
||||
|
||||
// 1.54 inch 200x200 - GxEPD2_154_M09
|
||||
// #define TECHO_DISPLAY_MODEL GxEPD2_154_M09
|
||||
|
||||
#elif defined(MAKERPYTHON)
|
||||
// 2.9 inch 296x128 - GxEPD2_290_T5D
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_290_T5D
|
||||
|
||||
#elif defined(PCA10059)
|
||||
|
||||
// 4.2 inch 300x400 - GxEPD2_420_M01
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_420_M01
|
||||
|
||||
#elif defined(M5_COREINK)
|
||||
// M5Stack CoreInk
|
||||
// 1.54 inch 200x200 - GxEPD2_154_M09
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_154_M09
|
||||
|
||||
#elif defined(HELTEC_WIRELESS_PAPER)
|
||||
// #define TECHO_DISPLAY_MODEL GxEPD2_213_T5D
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_213_FC1
|
||||
|
||||
#elif defined(HELTEC_WIRELESS_PAPER_V1_0)
|
||||
// 2.13" 122x250 - DEPG0213BNS800
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_213_BN
|
||||
|
||||
#endif
|
||||
|
||||
GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT> *adafruitDisplay;
|
||||
- 1.54 inch
|
||||
EINK_DISPLAY_MODEL: GxEPD2_154_M09
|
||||
EINK_WIDTH: 200
|
||||
EINK_HEIGHT: 200
|
||||
*/
|
||||
|
||||
// Constructor
|
||||
EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus)
|
||||
{
|
||||
#if defined(TTGO_T_ECHO)
|
||||
setGeometry(GEOMETRY_RAWMODE, 200, 200);
|
||||
#elif defined(RAK4630)
|
||||
|
||||
// GxEPD2_213_BN - RAK14000 2.13 inch b/w 250x122
|
||||
setGeometry(GEOMETRY_RAWMODE, 250, 122);
|
||||
this->displayBufferSize = 250 * (128 / 8);
|
||||
// GxEPD2_420_M01
|
||||
// setGeometry(GEOMETRY_RAWMODE, 300, 400);
|
||||
|
||||
// GxEPD2_290_T5D
|
||||
// setGeometry(GEOMETRY_RAWMODE, 296, 128);
|
||||
|
||||
// GxEPD2_154_M09
|
||||
// setGeometry(GEOMETRY_RAWMODE, 200, 200);
|
||||
|
||||
#elif defined(HELTEC_WIRELESS_PAPER_V1_0)
|
||||
|
||||
// The display's memory is actually 128px x 250px
|
||||
// Setting the buffersize manually prevents 122/8 truncating to a 15 byte width
|
||||
// (Or something like that..)
|
||||
|
||||
// Set dimensions in OLEDDisplay base class
|
||||
this->geometry = GEOMETRY_RAWMODE;
|
||||
this->displayWidth = 250;
|
||||
this->displayHeight = 122;
|
||||
this->displayBufferSize = 250 * (128 / 8);
|
||||
this->displayWidth = EINK_WIDTH;
|
||||
this->displayHeight = EINK_HEIGHT;
|
||||
|
||||
#elif defined(HELTEC_WIRELESS_PAPER)
|
||||
// GxEPD2_213_BN - 2.13 inch b/w 250x122
|
||||
setGeometry(GEOMETRY_RAWMODE, 250, 122);
|
||||
#elif defined(MAKERPYTHON)
|
||||
// GxEPD2_290_T5D
|
||||
setGeometry(GEOMETRY_RAWMODE, 296, 128);
|
||||
// Round shortest side up to nearest byte, to prevent truncation causing an undersized buffer
|
||||
uint16_t shortSide = min(EINK_WIDTH, EINK_HEIGHT);
|
||||
uint16_t longSide = max(EINK_WIDTH, EINK_HEIGHT);
|
||||
if (shortSide % 8 != 0)
|
||||
shortSide = (shortSide | 7) + 1;
|
||||
|
||||
#elif defined(PCA10059)
|
||||
|
||||
// GxEPD2_420_M01
|
||||
setGeometry(GEOMETRY_RAWMODE, 300, 400);
|
||||
|
||||
#elif defined(M5_COREINK)
|
||||
|
||||
// M5Stack_CoreInk 200x200
|
||||
// 1.54 inch 200x200 - GxEPD2_154_M09
|
||||
setGeometry(GEOMETRY_RAWMODE, EPD_HEIGHT, EPD_WIDTH);
|
||||
#elif defined(my)
|
||||
|
||||
// GxEPD2_290_T5D
|
||||
setGeometry(GEOMETRY_RAWMODE, 296, 128);
|
||||
LOG_DEBUG("GEOMETRY_RAWMODE, 296, 128\n");
|
||||
|
||||
#elif defined(ESP32_S3_PICO)
|
||||
|
||||
// GxEPD2_290_T94_V2
|
||||
setGeometry(GEOMETRY_RAWMODE, EPD_WIDTH, EPD_HEIGHT);
|
||||
LOG_DEBUG("GEOMETRY_RAWMODE, 296, 128\n");
|
||||
|
||||
#endif
|
||||
// setGeometry(GEOMETRY_RAWMODE, 128, 64); // old resolution
|
||||
// setGeometry(GEOMETRY_128_64); // We originally used this because I wasn't sure if rawmode worked - it does
|
||||
this->displayBufferSize = longSide * (shortSide / 8);
|
||||
}
|
||||
|
||||
// FIXME quick hack to limit drawing to a very slow rate
|
||||
uint32_t lastDrawMsec;
|
||||
|
||||
/**
|
||||
* Force a display update if we haven't drawn within the specified msecLimit
|
||||
*/
|
||||
@ -131,13 +53,6 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
|
||||
// No need to grab this lock because we are on our own SPI bus
|
||||
// concurrency::LockGuard g(spiLock);
|
||||
|
||||
#if defined(USE_EINK_DYNAMIC_REFRESH)
|
||||
// Decide between full refresh, fast refresh, or skipping the update
|
||||
bool continueUpdate = determineRefreshMode();
|
||||
if (!continueUpdate)
|
||||
return false;
|
||||
#else
|
||||
|
||||
uint32_t now = millis();
|
||||
uint32_t sinceLast = now - lastDrawMsec;
|
||||
|
||||
@ -146,52 +61,34 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
|
||||
else
|
||||
return false;
|
||||
|
||||
#endif
|
||||
|
||||
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
|
||||
// tft.drawBitmap(0, 0, buffer, 128, 64, TFT_YELLOW, TFT_BLACK);
|
||||
for (uint32_t y = 0; y < displayHeight; y++) {
|
||||
for (uint32_t x = 0; x < displayWidth; x++) {
|
||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficient
|
||||
auto b = buffer[x + (y / 8) * displayWidth];
|
||||
auto isset = b & (1 << (y & 7));
|
||||
adafruitDisplay->drawPixel(x, y, isset ? COLORED : UNCOLORED);
|
||||
adafruitDisplay->drawPixel(x, y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG("Updating E-Paper... ");
|
||||
|
||||
#if defined(TTGO_T_ECHO)
|
||||
adafruitDisplay->nextPage();
|
||||
#elif defined(RAK4630) || defined(MAKERPYTHON)
|
||||
|
||||
// RAK14000 2.13 inch b/w 250x122 actually now does support fast refresh
|
||||
#if false
|
||||
// Currently unused; rescued from commented-out line during a refactor
|
||||
// Use a meaningful macro here if variant doesn't want fast refresh
|
||||
|
||||
// Full update mode (slow)
|
||||
// adafruitDisplay->display(false); // FIXME, use fast refresh mode
|
||||
|
||||
// Only enable for e-Paper with support for fast updates and comment out above adafruitDisplay->display(false);
|
||||
// 1.54 inch 200x200 - GxEPD2_154_M09
|
||||
// 2.13 inch 250x122 - GxEPD2_213_BN
|
||||
// 2.9 inch 296x128 - GxEPD2_290_T5D
|
||||
// 4.2 inch 300x400 - GxEPD2_420_M01
|
||||
adafruitDisplay->nextPage();
|
||||
|
||||
#elif defined(PCA10059) || defined(M5_COREINK)
|
||||
adafruitDisplay->nextPage();
|
||||
#elif defined(HELTEC_WIRELESS_PAPER_V1_0)
|
||||
adafruitDisplay->nextPage();
|
||||
#elif defined(HELTEC_WIRELESS_PAPER)
|
||||
adafruitDisplay->nextPage();
|
||||
#elif defined(ESP32_S3_PICO)
|
||||
adafruitDisplay->nextPage();
|
||||
#elif defined(PRIVATE_HW) || defined(my)
|
||||
adafruitDisplay->display(false)
|
||||
#else
|
||||
// Fast update mode
|
||||
adafruitDisplay->nextPage();
|
||||
#endif
|
||||
|
||||
#ifndef EINK_NO_HIBERNATE // Only hibernate if controller IC will preserve image memory
|
||||
// Put screen to sleep to save power (possibly not necessary because we already did poweroff inside of display)
|
||||
adafruitDisplay->hibernate();
|
||||
LOG_DEBUG("done\n");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -203,15 +100,9 @@ void EInkDisplay::display(void)
|
||||
// at least one forceDisplay() keyframe. This prevents flashing when we should the critical
|
||||
// bootscreen (that we want to look nice)
|
||||
|
||||
#ifdef USE_EINK_DYNAMIC_REFRESH
|
||||
lowPriority();
|
||||
forceDisplay();
|
||||
highPriority();
|
||||
#else
|
||||
if (lastDrawMsec) {
|
||||
forceDisplay(slowUpdateMsec); // Show the first screen a few seconds after boot, then slower
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Send a command to the display (low level function)
|
||||
@ -226,7 +117,7 @@ void EInkDisplay::setDetected(uint8_t detected)
|
||||
(void)detected;
|
||||
}
|
||||
|
||||
// Connect to the display
|
||||
// Connect to the display - variant specific
|
||||
bool EInkDisplay::connect()
|
||||
{
|
||||
LOG_INFO("Doing EInk init\n");
|
||||
@ -244,9 +135,9 @@ bool EInkDisplay::connect()
|
||||
|
||||
#if defined(TTGO_T_ECHO)
|
||||
{
|
||||
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
|
||||
|
||||
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
adafruitDisplay->init();
|
||||
adafruitDisplay->setRotation(3);
|
||||
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
|
||||
@ -254,8 +145,8 @@ bool EInkDisplay::connect()
|
||||
#elif defined(RAK4630) || defined(MAKERPYTHON)
|
||||
{
|
||||
if (eink_found) {
|
||||
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
||||
// RAK14000 2.13 inch b/w 250x122 does actually now support fast refresh
|
||||
adafruitDisplay->setRotation(3);
|
||||
@ -296,8 +187,8 @@ bool EInkDisplay::connect()
|
||||
delay(100);
|
||||
|
||||
// Create GxEPD2 objects
|
||||
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
|
||||
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
|
||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
|
||||
// Init GxEPD2
|
||||
adafruitDisplay->init();
|
||||
@ -311,228 +202,36 @@ bool EInkDisplay::connect()
|
||||
pinMode(Vext, OUTPUT);
|
||||
digitalWrite(Vext, LOW);
|
||||
delay(100);
|
||||
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
|
||||
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
|
||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
adafruitDisplay->init();
|
||||
adafruitDisplay->setRotation(3);
|
||||
}
|
||||
#elif defined(PCA10059)
|
||||
{
|
||||
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
||||
adafruitDisplay->setRotation(3);
|
||||
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
|
||||
}
|
||||
#elif defined(M5_COREINK)
|
||||
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
||||
adafruitDisplay->setRotation(0);
|
||||
adafruitDisplay->setPartialWindow(0, 0, EPD_WIDTH, EPD_HEIGHT);
|
||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||
#elif defined(my) || defined(ESP32_S3_PICO)
|
||||
{
|
||||
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
||||
adafruitDisplay->setRotation(1);
|
||||
adafruitDisplay->setPartialWindow(0, 0, EPD_WIDTH, EPD_HEIGHT);
|
||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||
}
|
||||
#endif
|
||||
|
||||
// adafruitDisplay->setFullWindow();
|
||||
// adafruitDisplay->fillScreen(UNCOLORED);
|
||||
// adafruitDisplay->drawCircle(100, 100, 20, COLORED);
|
||||
// adafruitDisplay->display(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use a mix of full refresh, fast refresh, and update skipping, to balance urgency and display health
|
||||
#if defined(USE_EINK_DYNAMIC_REFRESH)
|
||||
|
||||
// Suggest that subsequent updates should use fast-refresh
|
||||
void EInkDisplay::highPriority()
|
||||
{
|
||||
isHighPriority = true;
|
||||
}
|
||||
|
||||
// Suggest that subsequent updates should use full-refresh
|
||||
void EInkDisplay::lowPriority()
|
||||
{
|
||||
isHighPriority = false;
|
||||
}
|
||||
|
||||
// Full-refresh is explicitly requested for next one update - no skipping please
|
||||
void EInkDisplay::demandFullRefresh()
|
||||
{
|
||||
demandingFull = true;
|
||||
}
|
||||
|
||||
// configure display for fast-refresh
|
||||
void EInkDisplay::configForFastRefresh()
|
||||
{
|
||||
// Display-specific code can go here
|
||||
#if defined(PRIVATE_HW)
|
||||
#else
|
||||
// Otherwise:
|
||||
adafruitDisplay->setPartialWindow(0, 0, adafruitDisplay->width(), adafruitDisplay->height());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Configure display for full-refresh
|
||||
void EInkDisplay::configForFullRefresh()
|
||||
{
|
||||
// Display-specific code can go here
|
||||
#if defined(PRIVATE_HW)
|
||||
#else
|
||||
// Otherwise:
|
||||
adafruitDisplay->setFullWindow();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef EINK_FASTREFRESH_ERASURE_LIMIT
|
||||
// Count black pixels in an image. Used for "erasure tracking"
|
||||
int32_t EInkDisplay::countBlackPixels()
|
||||
{
|
||||
int32_t blackCount = 0; // Signed, to avoid underflow when comparing
|
||||
for (uint16_t b = 0; b < (displayWidth / 8) * displayHeight; b++) {
|
||||
for (uint8_t i = 0; i < 7; i++) {
|
||||
// Check if each bit is black or white
|
||||
blackCount += (buffer[b] >> i) & 1;
|
||||
}
|
||||
}
|
||||
return blackCount;
|
||||
}
|
||||
|
||||
// Evaluate the (rough) amount of black->white pixel change since last full refresh
|
||||
bool EInkDisplay::tooManyErasures()
|
||||
{
|
||||
// Ideally, we would compare the new and old buffers, to count *actual* white-to-black pixel changes
|
||||
// but that would require substantially more "code tampering"
|
||||
|
||||
// Get the black pixel stats for this image
|
||||
int32_t blackCount = countBlackPixels();
|
||||
int32_t blackDifference = blackCount - prevBlackCount;
|
||||
|
||||
// Update the running total of "erasures" - black pixels which have become white, since last full-refresh
|
||||
if (blackDifference < 0)
|
||||
erasedSinceFull -= blackDifference;
|
||||
|
||||
// Store black pixel count for next time
|
||||
prevBlackCount = blackCount;
|
||||
|
||||
// Log the running total - help devs setup new boards
|
||||
LOG_DEBUG("Dynamic Refresh: erasedSinceFull=%hu, EINK_FASTREFRESH_ERASURE_LIMIT=%hu\n", erasedSinceFull,
|
||||
EINK_FASTREFRESH_ERASURE_LIMIT);
|
||||
|
||||
// Check if too many pixels have been erased
|
||||
if (erasedSinceFull > EINK_FASTREFRESH_ERASURE_LIMIT)
|
||||
return true; // Too many
|
||||
else
|
||||
return false; // Still okay
|
||||
}
|
||||
#endif // ifdef EINK_FASTREFRESH_ERASURE_LIMIT
|
||||
|
||||
bool EInkDisplay::newImageMatchesOld()
|
||||
{
|
||||
uint32_t newImageHash = 0;
|
||||
|
||||
// Generate hash: sum all bytes in the image buffer
|
||||
for (uint16_t b = 0; b < (displayWidth / 8) * displayHeight; b++) {
|
||||
newImageHash += buffer[b];
|
||||
}
|
||||
|
||||
// Compare hashes
|
||||
bool hashMatches = (newImageHash == prevImageHash);
|
||||
|
||||
// Update the cached hash
|
||||
prevImageHash = newImageHash;
|
||||
|
||||
// Return the comparison result
|
||||
return hashMatches;
|
||||
}
|
||||
|
||||
// Choose between, full-refresh, fast refresh, and update skipping, to balance urgency and display health.
|
||||
bool EInkDisplay::determineRefreshMode()
|
||||
{
|
||||
uint32_t now = millis();
|
||||
uint32_t sinceLast = now - lastUpdateMsec;
|
||||
|
||||
// If rate-limiting dropped a high-priority update:
|
||||
// promote this update, so it runs ASAP
|
||||
if (missedHighPriorityUpdate) {
|
||||
isHighPriority = true;
|
||||
missedHighPriorityUpdate = false;
|
||||
}
|
||||
|
||||
// Abort: if too soon for a new frame (unless demanding full)
|
||||
if (!demandingFull && isHighPriority && fastRefreshCount > 0 && sinceLast < highPriorityLimitMsec) {
|
||||
LOG_DEBUG("Dynamic Refresh: update skipped. Exceeded EINK_HIGHPRIORITY_LIMIT_SECONDS\n");
|
||||
missedHighPriorityUpdate = true;
|
||||
return false;
|
||||
}
|
||||
if (!demandingFull && !isHighPriority && !demandingFull && sinceLast < lowPriorityLimitMsec) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If demanded full refresh: give it to them
|
||||
if (demandingFull)
|
||||
needsFull = true;
|
||||
|
||||
// Check if old image (fast-refresh) should be redrawn (as full), for image quality
|
||||
if (fastRefreshCount > 0 && !isHighPriority)
|
||||
needsFull = true;
|
||||
|
||||
// If too many fast updates, require a full-refresh (display health)
|
||||
if (fastRefreshCount >= fastRefreshLimit)
|
||||
needsFull = true;
|
||||
|
||||
#ifdef EINK_FASTREFRESH_ERASURE_LIMIT
|
||||
// Some displays struggle with erasing black pixels to white, during fast-refresh
|
||||
if (tooManyErasures())
|
||||
needsFull = true;
|
||||
#endif
|
||||
|
||||
// If image matches
|
||||
// (Block must run, even if full already selected, to store hash for next time)
|
||||
if (newImageMatchesOld()) {
|
||||
// If low priority: limit rate
|
||||
// otherwise, every loop() will run the hash method
|
||||
if (!isHighPriority)
|
||||
lastUpdateMsec = now;
|
||||
|
||||
// If update is *not* for display health or image quality, skip it
|
||||
if (!needsFull)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Conditions assessed - not skipping - load the appropriate config
|
||||
|
||||
// If options require a full refresh
|
||||
if (!isHighPriority || needsFull) {
|
||||
if (fastRefreshCount > 0)
|
||||
configForFullRefresh();
|
||||
|
||||
LOG_DEBUG("Dynamic Refresh: conditions met for full-refresh\n");
|
||||
fastRefreshCount = 0;
|
||||
needsFull = false;
|
||||
demandingFull = false;
|
||||
erasedSinceFull = 0; // Reset the count for EINK_FASTREFRESH_ERASURE_LIMIT - tracks ghosting buildup
|
||||
}
|
||||
|
||||
// If options allow a fast-refresh
|
||||
else {
|
||||
if (fastRefreshCount == 0)
|
||||
configForFastRefresh();
|
||||
|
||||
LOG_DEBUG("Dynamic Refresh: conditions met for fast-refresh\n");
|
||||
fastRefreshCount++;
|
||||
}
|
||||
|
||||
lastUpdateMsec = now; // Mark time for rate limiting
|
||||
return true; // Instruct calling method to continue with update
|
||||
}
|
||||
|
||||
#endif // End USE_EINK_DYNAMIC_REFRESH
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_EINK
|
||||
|
||||
#include "GxEPD2_BW.h"
|
||||
#include <OLEDDisplay.h>
|
||||
|
||||
#if defined(HELTEC_WIRELESS_PAPER_V1_0)
|
||||
@ -16,6 +19,7 @@
|
||||
* Use the fast NRF52 SPI API rather than the slow standard arduino version
|
||||
*
|
||||
* turn radio back on - currently with both on spi bus is fucked? or are we leaving chip select asserted?
|
||||
* Suggestion: perhaps similar to HELTEC_WIRELESS_PAPER issue, which resolved with rtc_gpio_hold_dis()
|
||||
*/
|
||||
class EInkDisplay : public OLEDDisplay
|
||||
{
|
||||
@ -55,80 +59,17 @@ class EInkDisplay : public OLEDDisplay
|
||||
// Connect to the display
|
||||
virtual bool connect() override;
|
||||
|
||||
#if defined(USE_EINK_DYNAMIC_REFRESH)
|
||||
// Full, fast, or skip: balance urgency with display health
|
||||
// AdafruitGFX display object - instantiated in connect(), variant specific
|
||||
GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT> *adafruitDisplay = NULL;
|
||||
|
||||
// Use fast refresh if EITHER:
|
||||
// * highPriority() was set
|
||||
// * a highPriority() update was previously skipped, for rate-limiting - (EINK_HIGHPRIORITY_LIMIT_SECONDS)
|
||||
|
||||
// Use full refresh if EITHER:
|
||||
// * lowPriority() was set
|
||||
// * demandFullRefresh() was called - (single shot)
|
||||
// * too many fast updates in a row: protect display - (EINK_FASTREFRESH_REPEAT_LIMIT)
|
||||
// * no recent updates, and last update was fast: redraw for image quality (EINK_LOWPRIORITY_LIMIT_SECONDS)
|
||||
// * (optional) too many "erasures" since full-refresh (black pixels cleared to white)
|
||||
|
||||
// Rate limit if:
|
||||
// * lowPriority() - (EINK_LOWPRIORITY_LIMIT_SECONDS)
|
||||
// * highPriority(), if multiple fast updates have run back-to-back - (EINK_HIGHPRIORITY_LIMIT_SECONDS)
|
||||
|
||||
// Skip update entirely if ALL criteria met:
|
||||
// * new image matches old image
|
||||
// * lowPriority()
|
||||
// * no call to demandFullRefresh()
|
||||
// * not redrawing for image quality
|
||||
// * not refreshing for display health
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
// To implement for your E-Ink display:
|
||||
// * edit configForFastRefresh()
|
||||
// * edit configForFullRefresh()
|
||||
// * add macros to variant.h, and adjust to taste:
|
||||
|
||||
/*
|
||||
#define USE_EINK_DYNAMIC_REFRESH
|
||||
#define EINK_LOWPRIORITY_LIMIT_SECONDS 30
|
||||
#define EINK_HIGHPRIORITY_LIMIT_SECONDS 1
|
||||
#define EINK_FASTREFRESH_REPEAT_LIMIT 5
|
||||
#define EINK_FASTREFRESH_ERASURE_LIMIT 300 // optional
|
||||
*/
|
||||
|
||||
public:
|
||||
void highPriority(); // Suggest fast refresh
|
||||
void lowPriority(); // Suggest full refresh
|
||||
void demandFullRefresh(); // For next update: explicitly request full refresh
|
||||
|
||||
protected:
|
||||
void configForFastRefresh(); // Display specific code to select fast refresh mode
|
||||
void configForFullRefresh(); // Display specific code to return to full refresh mode
|
||||
bool newImageMatchesOld(); // Is the new update actually different to the last image?
|
||||
bool determineRefreshMode(); // Called immediately before data written to display - choose refresh mode, or abort update
|
||||
#ifdef EINK_FASTREFRESH_ERASURE_LIMIT
|
||||
int32_t countBlackPixels(); // Calculate the number of black pixels in the new image
|
||||
bool tooManyErasures(); // Has too much "ghosting" (black pixels erased to white) accumulated since last full-refresh?
|
||||
// If display uses HSPI
|
||||
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0)
|
||||
SPIClass *hspi = NULL;
|
||||
#endif
|
||||
|
||||
bool isHighPriority = true; // Does the method calling update believe that this is urgent?
|
||||
bool needsFull = false; // Is a full refresh forced? (display health)
|
||||
bool demandingFull = false; // Was full refresh specifically requested? (splash screens, etc)
|
||||
bool missedHighPriorityUpdate = false; // Was a high priority update skipped for rate-limiting?
|
||||
uint16_t fastRefreshCount = 0; // How many fast updates have occurred since last full refresh?
|
||||
uint32_t lastUpdateMsec = 0; // When did the last update occur?
|
||||
uint32_t prevImageHash = 0; // Used to check if update will change screen image (skippable or not)
|
||||
int32_t prevBlackCount = 0; // How many black pixels were in the previous image
|
||||
uint32_t erasedSinceFull = 0; // How many black pixels have been set back to white since last full-refresh? (roughly)
|
||||
|
||||
// Set in variant.h
|
||||
const uint32_t lowPriorityLimitMsec = (uint32_t)1000 * EINK_LOWPRIORITY_LIMIT_SECONDS; // Max rate for fast refreshes
|
||||
const uint32_t highPriorityLimitMsec = (uint32_t)1000 * EINK_HIGHPRIORITY_LIMIT_SECONDS; // Max rate for full refreshes
|
||||
const uint32_t fastRefreshLimit = EINK_FASTREFRESH_REPEAT_LIMIT; // Max consecutive fast updates, before full is triggered
|
||||
|
||||
#else // !USE_EINK_DYNAMIC_REFRESH
|
||||
// Tolerate calls to these methods anywhere, just to be safe
|
||||
void highPriority() {}
|
||||
void lowPriority() {}
|
||||
void demandFullRefresh() {}
|
||||
#endif
|
||||
private:
|
||||
// FIXME quick hack to limit drawing to a very slow rate
|
||||
uint32_t lastDrawMsec = 0;
|
||||
};
|
||||
|
||||
#endif
|
@ -4,6 +4,9 @@ board = nordic_pca10059
|
||||
board_level = extra
|
||||
build_flags = ${nrf52840_base.build_flags} -Ivariants/Dongle_nRF52840-pca10059-v1 -D NORDIC_PCA10059
|
||||
-L "${platformio.libdeps_dir}/${this.__env__}/BSEC2 Software Library/src/cortex-m4/fpv4-sp-d16-hard"
|
||||
-DEINK_DISPLAY_MODEL=GxEPD2_420_M01
|
||||
-DEINK_WIDTH=300
|
||||
-DEINK_HEIGHT=400
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/Dongle_nRF52840-pca10059-v1>
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
|
@ -136,7 +136,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
|
||||
#define USE_SX1262
|
||||
#define SX126X_CS (0 + 31) // LORA_CS P0.31
|
||||
#define SX126X_DIO1 (0 + 29) // DIO1 P0.29
|
||||
#define SX126X_BUSY (0 + 2) // LORA_BUSY P0.02
|
||||
#define SX126X_BUSY (0 + 2) // LORA_BUSY P0.02
|
||||
#define SX126X_RESET (32 + 15) // LORA_RESET P1.15
|
||||
#define SX126X_TXEN (32 + 13) // TXEN P1.13 NiceRF 868 dont use
|
||||
#define SX126X_RXEN (32 + 10) // RXEN P1.10 NiceRF 868 dont use
|
||||
|
@ -10,5 +10,8 @@ lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
|
||||
zinggjm/GxEPD2@^1.4.9
|
||||
-DEINK_DISPLAY_MODEL=GxEPD2_290_T5D
|
||||
-DEINK_WIDTH=296
|
||||
-DEINK_HEIGHT=128
|
||||
debug_tool = jlink
|
||||
;upload_port = /dev/ttyACM4
|
||||
|
@ -16,9 +16,9 @@ build_flags = ${esp32_base.build_flags}
|
||||
;-DPRIVATE_HW
|
||||
-Ivariants/esp32-s3-pico
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DTECHO_DISPLAY_MODEL=GxEPD2_290_T94_V2
|
||||
-DEPD_HEIGHT=128
|
||||
-DEPD_WIDTH=296
|
||||
-DEINK_DISPLAY_MODEL=GxEPD2_290_T94_V2
|
||||
-DEINK_WIDTH=296
|
||||
-DEINK_HEIGHT=128
|
||||
|
||||
lib_deps = ${esp32s3_base.lib_deps}
|
||||
zinggjm/GxEPD2@^1.5.3
|
||||
|
@ -74,4 +74,4 @@
|
||||
#define PIN_EINK_DC 33
|
||||
#define PIN_EINK_RES 42 // 37 //(-1) // cant be MISO Waveshare ??)
|
||||
#define PIN_EINK_SCLK 35
|
||||
#define PIN_EINK_MOSI 36
|
||||
#define PIN_EINK_MOSI 36
|
@ -2,7 +2,12 @@
|
||||
extends = esp32s3_base
|
||||
board = heltec_wifi_lora_32_V3
|
||||
build_flags =
|
||||
${esp32s3_base.build_flags} -D HELTEC_WIRELESS_PAPER -I variants/heltec_wireless_paper
|
||||
${esp32s3_base.build_flags}
|
||||
-I variants/heltec_wireless_paper
|
||||
-D HELTEC_WIRELESS_PAPER
|
||||
-D EINK_DISPLAY_MODEL=GxEPD2_213_FC1
|
||||
-D EINK_WIDTH=250
|
||||
-D EINK_HEIGHT=122
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
https://github.com/ixt/GxEPD2#39f325b677713eb04dfcc83b8e402e77523fb8bf
|
||||
|
@ -5,6 +5,9 @@ build_flags =
|
||||
${esp32s3_base.build_flags}
|
||||
-I variants/heltec_wireless_paper_v1
|
||||
-D HELTEC_WIRELESS_PAPER_V1_0
|
||||
-D EINK_DISPLAY_MODEL=GxEPD2_213_BN
|
||||
-D EINK_WIDTH=250
|
||||
-D EINK_HEIGHT=122
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
https://github.com/meshtastic/GxEPD2/
|
||||
|
@ -6,13 +6,6 @@
|
||||
|
||||
#define USE_EINK
|
||||
|
||||
// Settings for Dynamic Refresh mode
|
||||
// Change between full-refresh, fast-refresh, or update-skipping, to balance urgency and display health.
|
||||
#define USE_EINK_DYNAMIC_REFRESH
|
||||
#define EINK_LOWPRIORITY_LIMIT_SECONDS 30
|
||||
#define EINK_HIGHPRIORITY_LIMIT_SECONDS 1
|
||||
#define EINK_FASTREFRESH_REPEAT_LIMIT 5
|
||||
|
||||
/*
|
||||
* eink display pins
|
||||
*/
|
||||
|
@ -9,8 +9,9 @@ build_flags =
|
||||
;-D RADIOLIB_VERBOSE
|
||||
-Ofast
|
||||
-D__MCUXPRESSO
|
||||
-DEPD_HEIGHT=200
|
||||
-DEPD_WIDTH=200
|
||||
-DEINK_DISPLAY_MODEL=GxEPD2_154_M09
|
||||
-DEINK_WIDTH=200
|
||||
-DEINK_HEIGHT=200
|
||||
-DUSER_SETUP_LOADED
|
||||
-DM5_COREINK
|
||||
-DM5STACK
|
||||
|
@ -19,9 +19,9 @@ build_flags =
|
||||
;${esp32_base.build_flags} -D MY_ESP32S3_DIY -I variants/my_esp32s3_diy_eink
|
||||
${esp32_base.build_flags} -D PRIVATE_HW -I variants/my_esp32s3_diy_eink
|
||||
-Dmy
|
||||
-DTECHO_DISPLAY_MODEL=GxEPD2_290_T5D
|
||||
-DEPD_HEIGHT=128
|
||||
-DEPD_WIDTH=296
|
||||
-DEINK_DISPLAY_MODEL=GxEPD2_290_T5D
|
||||
-DEINK_WIDTH=296
|
||||
-DEINK_HEIGHT=128
|
||||
-DBOARD_HAS_PSRAM
|
||||
-mfix-esp32-psram-cache-issue
|
||||
-DARDUINO_USB_MODE=0
|
||||
|
@ -53,4 +53,4 @@
|
||||
#define PIN_EINK_DC 1
|
||||
#define PIN_EINK_RES (-1)
|
||||
#define PIN_EINK_SCLK 5
|
||||
#define PIN_EINK_MOSI 6
|
||||
#define PIN_EINK_MOSI 6
|
@ -5,6 +5,9 @@ board = wiscore_rak4631
|
||||
build_flags = ${nrf52840_base.build_flags} -Ivariants/rak10701 -D RAK_4631
|
||||
-L "${platformio.libdeps_dir}/${this.__env__}/BSEC2 Software Library/src/cortex-m4/fpv4-sp-d16-hard"
|
||||
-DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely.
|
||||
-DEINK_DISPLAY_MODEL=GxEPD2_213_BN
|
||||
-DEINK_WIDTH=250
|
||||
-DEINK_HEIGHT=122
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak10701> +<mesh/eth/> +<mesh/api/> +<mqtt/>
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
|
@ -202,13 +202,13 @@ static const uint8_t WB_SPI_MOSI = 30; // IO_SLOT
|
||||
|
||||
/* Setup of the SX1262 LoRa module ( https://docs.rakwireless.com/Product-Categories/WisBlock/RAK4631/Datasheet/ )
|
||||
|
||||
P1.10 NSS SPI NSS (Arduino GPIO number 42)
|
||||
P1.11 SCK SPI CLK (Arduino GPIO number 43)
|
||||
P1.12 MOSI SPI MOSI (Arduino GPIO number 44)
|
||||
P1.13 MISO SPI MISO (Arduino GPIO number 45)
|
||||
P1.14 BUSY BUSY signal (Arduino GPIO number 46)
|
||||
P1.15 DIO1 DIO1 event interrupt (Arduino GPIO number 47)
|
||||
P1.06 NRESET NRESET manual reset of the SX1262 (Arduino GPIO number 38)
|
||||
P1.10 NSS SPI NSS (Arduino GPIO number 42)
|
||||
P1.11 SCK SPI CLK (Arduino GPIO number 43)
|
||||
P1.12 MOSI SPI MOSI (Arduino GPIO number 44)
|
||||
P1.13 MISO SPI MISO (Arduino GPIO number 45)
|
||||
P1.14 BUSY BUSY signal (Arduino GPIO number 46)
|
||||
P1.15 DIO1 DIO1 event interrupt (Arduino GPIO number 47)
|
||||
P1.06 NRESET NRESET manual reset of the SX1262 (Arduino GPIO number 38)
|
||||
|
||||
Important for successful SX1262 initialization:
|
||||
|
||||
|
@ -5,6 +5,9 @@ board = wiscore_rak4631
|
||||
build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631 -D RAK_4631
|
||||
-L "${platformio.libdeps_dir}/${this.__env__}/BSEC2 Software Library/src/cortex-m4/fpv4-sp-d16-hard"
|
||||
-DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely.
|
||||
-DEINK_DISPLAY_MODEL=GxEPD2_213_BN
|
||||
-DEINK_WIDTH=250
|
||||
-DEINK_HEIGHT=122
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> +<mesh/eth/> +<mesh/api/> +<mqtt/>
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
|
@ -181,13 +181,13 @@ static const uint8_t SCK = PIN_SPI_SCK;
|
||||
|
||||
/* Setup of the SX1262 LoRa module ( https://docs.rakwireless.com/Product-Categories/WisBlock/RAK4631/Datasheet/ )
|
||||
|
||||
P1.10 NSS SPI NSS (Arduino GPIO number 42)
|
||||
P1.11 SCK SPI CLK (Arduino GPIO number 43)
|
||||
P1.12 MOSI SPI MOSI (Arduino GPIO number 44)
|
||||
P1.13 MISO SPI MISO (Arduino GPIO number 45)
|
||||
P1.14 BUSY BUSY signal (Arduino GPIO number 46)
|
||||
P1.15 DIO1 DIO1 event interrupt (Arduino GPIO number 47)
|
||||
P1.06 NRESET NRESET manual reset of the SX1262 (Arduino GPIO number 38)
|
||||
P1.10 NSS SPI NSS (Arduino GPIO number 42)
|
||||
P1.11 SCK SPI CLK (Arduino GPIO number 43)
|
||||
P1.12 MOSI SPI MOSI (Arduino GPIO number 44)
|
||||
P1.13 MISO SPI MISO (Arduino GPIO number 45)
|
||||
P1.14 BUSY BUSY signal (Arduino GPIO number 46)
|
||||
P1.15 DIO1 DIO1 event interrupt (Arduino GPIO number 47)
|
||||
P1.06 NRESET NRESET manual reset of the SX1262 (Arduino GPIO number 38)
|
||||
|
||||
Important for successful SX1262 initialization:
|
||||
|
||||
|
@ -4,6 +4,9 @@ extends = nrf52840_base
|
||||
board = wiscore_rak4631
|
||||
build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631_epaper -D RAK_4631
|
||||
-L "${platformio.libdeps_dir}/${this.__env__}/BSEC2 Software Library/src/cortex-m4/fpv4-sp-d16-hard"
|
||||
-DEINK_DISPLAY_MODEL=GxEPD2_213_BN
|
||||
-DEINK_WIDTH=250
|
||||
-DEINK_HEIGHT=122
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631_epaper>
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
|
@ -6,6 +6,9 @@ board = wiscore_rak4631
|
||||
build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631_epaper -D RAK_4631
|
||||
-L "${platformio.libdeps_dir}/${this.__env__}/BSEC2 Software Library/src/cortex-m4/fpv4-sp-d16-hard"
|
||||
-D PIN_EINK_EN=34
|
||||
-D EINK_DISPLAY_MODEL=GxEPD2_213_BN
|
||||
-D EINK_WIDTH=250
|
||||
-D EINK_HEIGHT=122
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631_epaper_onrxtx>
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
|
@ -8,6 +8,9 @@ debug_tool = jlink
|
||||
build_flags = ${nrf52840_base.build_flags} -Ivariants/t-echo
|
||||
-DGPS_POWER_TOGGLE
|
||||
-L "${platformio.libdeps_dir}/${this.__env__}/BSEC2 Software Library/src/cortex-m4/fpv4-sp-d16-hard"
|
||||
-DEINK_DISPLAY_MODEL=GxEPD2_154_D67
|
||||
-DEINK_WIDTH=200
|
||||
-DEINK_HEIGHT=200
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/t-echo>
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
|
Loading…
Reference in New Issue
Block a user