Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 64 additions & 7 deletions src/graphics/Backlight.cpp
Original file line number Diff line number Diff line change
@@ -1,30 +1,75 @@
#include "graphics/Backlight.h"

#if HAS_PWM_BACKLIGHT
#if HAS_BACKLIGHT

#include "mesh/NodeDB.h"

#if HAS_GPIO_BACKLIGHT && defined(PCA_PIN_EINK_EN)
#include "main.h" // the GPIO expander the rail hangs off
#endif

namespace graphics
{
namespace
{
bool pinConfigured = false;
bool initialized = false;

// What the hardware is driven at right now, which is not the stored level while the screen is off.
uint8_t litLevel = 0;

// Level restored when the backlight is switched back on after being toggled off.
#if HAS_PWM_BACKLIGHT
uint8_t lastOnLevel = PWM_BACKLIGHT_DEFAULT;
#else
uint8_t lastOnLevel = GPIO_BACKLIGHT_ON_LEVEL;
#endif

void drive(uint8_t level)
{
if (!pinConfigured) {
pinMode(PIN_PWM_BACKLIGHT, OUTPUT);
pinConfigured = true;
}
litLevel = level;
#if HAS_PWM_BACKLIGHT
analogWrite(PIN_PWM_BACKLIGHT, level);
#elif defined(PIN_EINK_EN)
digitalWrite(PIN_EINK_EN, level > 0 ? HIGH : LOW);
#elif defined(PCA_PIN_EINK_EN)
io.digitalWrite(PCA_PIN_EINK_EN, level > 0 ? HIGH : LOW);
#endif
}
} // namespace

void backlightInit()
{
if (initialized)
return;
initialized = true;

#if HAS_PWM_BACKLIGHT
pinMode(PIN_PWM_BACKLIGHT, OUTPUT);
#elif defined(PIN_EINK_EN)
pinMode(PIN_EINK_EN, OUTPUT);
#endif
// PCA_PIN_EINK_EN is already configured by the variant's earlyInitVariant()

#if HAS_GPIO_BACKLIGHT
// Any level that is not ours, such as the legacy 153 default, was not set here, so fall back to
// the variant default rather than reading it as "lit".
if (uiconfig.screen_brightness != 0 && uiconfig.screen_brightness != GPIO_BACKLIGHT_ON_LEVEL)
uiconfig.screen_brightness = GPIO_BACKLIGHT_DEFAULT_LEVEL;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
#endif

// So a toggle or a momentary press restores the level the user last chose, not the compiled default
if (uiconfig.screen_brightness > 0)
lastOnLevel = uiconfig.screen_brightness;

drive(uiconfig.screen_brightness);
}

void backlightSet(uint8_t level)
{
#if HAS_GPIO_BACKLIGHT
// The rail has no intermediate states, so keep the stored level to the two this backend drives
level = level > 0 ? GPIO_BACKLIGHT_ON_LEVEL : 0;
#endif
if (level > 0)
lastOnLevel = level;
uiconfig.screen_brightness = level;
Expand All @@ -36,11 +81,21 @@ uint8_t backlightGet()
return uiconfig.screen_brightness;
}

bool backlightIsLit()
{
return litLevel > 0;
}

void backlightOn()
{
drive(uiconfig.screen_brightness);
}

void backlightMomentaryOn()
{
drive(lastOnLevel);
}

void backlightOff()
{
drive(0);
Expand All @@ -51,6 +106,7 @@ void backlightToggle()
backlightSet(uiconfig.screen_brightness > 0 ? 0 : lastOnLevel);
}

#if HAS_PWM_BACKLIGHT
void backlightStepUp()
{
uint16_t raised = (uint16_t)uiconfig.screen_brightness + PWM_BACKLIGHT_STEP;
Expand All @@ -66,6 +122,7 @@ void backlightStepDown()
? PWM_BACKLIGHT_MIN
: uiconfig.screen_brightness - PWM_BACKLIGHT_STEP);
}
#endif
} // namespace graphics

#endif // HAS_PWM_BACKLIGHT
#endif // HAS_BACKLIGHT
43 changes: 39 additions & 4 deletions src/graphics/Backlight.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,25 @@

#include "configuration.h"

// PWM backlight control. A variant opts in by defining PIN_PWM_BACKLIGHT, optionally with
// PWM_BACKLIGHT_DEFAULT, _MIN, _MAX and _STEP. Levels are 0..255 in uiconfig.screen_brightness.
// Backlight control for a PWM rail (PIN_PWM_BACKLIGHT) or an on/off GPIO rail (PIN_EINK_EN,
// PCA_PIN_EINK_EN). uiconfig.screen_brightness is the configured level, not the live pin state.

#if defined(PIN_PWM_BACKLIGHT)
#define HAS_PWM_BACKLIGHT 1
#else
#define HAS_PWM_BACKLIGHT 0
#endif

// MINI_EPAPER_S3 names its panel power rail PIN_EINK_EN. That is not a backlight and has to stay
// powered for the panel to work, so EInkDisplay::connect() drives it instead.
#if !HAS_PWM_BACKLIGHT && (defined(PIN_EINK_EN) || defined(PCA_PIN_EINK_EN)) && !defined(MINI_EPAPER_S3)
#define HAS_GPIO_BACKLIGHT 1
#else
#define HAS_GPIO_BACKLIGHT 0
#endif

#define HAS_BACKLIGHT (HAS_PWM_BACKLIGHT || HAS_GPIO_BACKLIGHT)

#if HAS_PWM_BACKLIGHT

#ifndef PWM_BACKLIGHT_DEFAULT
Expand All @@ -26,21 +36,46 @@
#define PWM_BACKLIGHT_STEP 20
#endif

#endif // HAS_PWM_BACKLIGHT

#if HAS_GPIO_BACKLIGHT

// On or off only, so these are the sole levels this backend stores. A variant defines
// GPIO_BACKLIGHT_DEFAULT_ON to power up lit.
#define GPIO_BACKLIGHT_ON_LEVEL 255
#if defined(GPIO_BACKLIGHT_DEFAULT_ON)
#define GPIO_BACKLIGHT_DEFAULT_LEVEL GPIO_BACKLIGHT_ON_LEVEL
#else
#define GPIO_BACKLIGHT_DEFAULT_LEVEL 0
#endif

#endif // HAS_GPIO_BACKLIGHT

#if HAS_BACKLIGHT

namespace graphics
{
void backlightInit(); // configure the pin, settle the stored level, then drive it. Idempotent

void backlightSet(uint8_t level);

uint8_t backlightGet();
uint8_t backlightGet(); // configured level, unchanged by backlightOff()

bool backlightIsLit(); // what the hardware is being driven at right now

void backlightOn(); // drive the stored level

void backlightMomentaryOn(); // light it regardless of the stored level, for press-and-hold

void backlightOff(); // drive 0, leaving the stored level alone

void backlightToggle();

#if HAS_PWM_BACKLIGHT
void backlightStepUp();

void backlightStepDown();
#endif
} // namespace graphics

#endif // HAS_PWM_BACKLIGHT
#endif // HAS_BACKLIGHT
15 changes: 5 additions & 10 deletions src/graphics/EInkDisplay2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,19 +143,14 @@ bool EInkDisplay::connect()
{
LOG_INFO("Do EInk init");

#ifdef PIN_EINK_EN
// backlight power, HIGH is backlight on, LOW is off
pinMode(PIN_EINK_EN, OUTPUT);
#ifdef ELECROW_ThinkNode_M1
// ThinkNode M1 has a hardware dimmable backlight. Start enabled
digitalWrite(PIN_EINK_EN, HIGH);
#elif defined(MINI_EPAPER_S3)
#if HAS_GPIO_BACKLIGHT
// Frontlight rail, level comes from uiconfig and is defaulted per variant
graphics::backlightInit();
#elif defined(PIN_EINK_EN)
// T-Mini Epaper S3 requires panel power rail enabled before SPI transfer.
pinMode(PIN_EINK_EN, OUTPUT);
digitalWrite(PIN_EINK_EN, HIGH);
delay(10);
#else
digitalWrite(PIN_EINK_EN, LOW);
#endif
#endif

#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE) || defined(TTGO_T_ECHO_PLUS) || \
Expand Down
19 changes: 7 additions & 12 deletions src/graphics/Screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -706,14 +706,8 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
dispdev->displayOn();
#endif

#if HAS_PWM_BACKLIGHT
#if HAS_BACKLIGHT
graphics::backlightOn();
#elif defined(PIN_EINK_EN)
if (uiconfig.screen_brightness == 1)
digitalWrite(PIN_EINK_EN, HIGH);
#elif defined(PCA_PIN_EINK_EN)
if (uiconfig.screen_brightness > 0)
io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
#endif

#if defined(ST7789_CS) && \
Expand Down Expand Up @@ -772,12 +766,8 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
drawLockdownLockScreen(dispdev);
#endif

#if HAS_PWM_BACKLIGHT
#if HAS_BACKLIGHT
graphics::backlightOff();
#elif defined(PIN_EINK_EN)
digitalWrite(PIN_EINK_EN, LOW);
#elif defined(PCA_PIN_EINK_EN)
io.digitalWrite(PCA_PIN_EINK_EN, LOW);
#endif

dispdev->displayOff();
Expand Down Expand Up @@ -835,6 +825,11 @@ void Screen::setup()
// Enable display rendering
useDisplay = true;

#if HAS_BACKLIGHT
// Settles uiconfig.screen_brightness for GPIO backlights, so read it only after this
graphics::backlightInit();
#endif

// Load saved brightness from UI config
// For OLED displays (SSD1306), default brightness is 255 if not set
if (uiconfig.screen_brightness == 0) {
Expand Down
22 changes: 2 additions & 20 deletions src/graphics/draw/MenuHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1177,7 +1177,7 @@ void menuHandler::homeBaseMenu()
}
optionsEnumArray[options++] = Mute;
}
#if HAS_PWM_BACKLIGHT || defined(PIN_EINK_EN) || defined(PCA_PIN_EINK_EN)
#if HAS_BACKLIGHT
optionsArray[options] = "Toggle Backlight";
optionsEnumArray[options++] = Backlight;
#else
Expand Down Expand Up @@ -1207,27 +1207,9 @@ void menuHandler::homeBaseMenu()
}
} else if (selected == Backlight) {
screen->setOn(false);
#if HAS_PWM_BACKLIGHT
#if HAS_BACKLIGHT
graphics::backlightToggle();
saveUIConfig();
#elif defined(PIN_EINK_EN)
if (uiconfig.screen_brightness == 1) {
uiconfig.screen_brightness = 0;
digitalWrite(PIN_EINK_EN, LOW);
} else {
uiconfig.screen_brightness = 1;
digitalWrite(PIN_EINK_EN, HIGH);
}
saveUIConfig();
#elif defined(PCA_PIN_EINK_EN)
if (uiconfig.screen_brightness > 0) {
uiconfig.screen_brightness = 0;
io.digitalWrite(PCA_PIN_EINK_EN, LOW);
} else {
uiconfig.screen_brightness = 1;
io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
}
saveUIConfig();
#endif
} else if (selected == Sleep) {
screen->setOn(false);
Expand Down
46 changes: 22 additions & 24 deletions src/input/InputBroker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

#if defined(BUTTON_PIN_TOUCH)
ButtonThread *TouchButtonThread = nullptr;
#if HAS_PWM_BACKLIGHT || defined(PIN_EINK_EN)
#if HAS_BACKLIGHT
static bool touchBacklightWasOn = false;
static bool touchBacklightActive = false;
#endif
Expand Down Expand Up @@ -247,41 +247,39 @@ void InputBroker::Init()
};
touchConfig.singlePress = INPUT_BROKER_NONE;
touchConfig.longPress = INPUT_BROKER_BACK;
#if HAS_PWM_BACKLIGHT || defined(PIN_EINK_EN)
#if HAS_BACKLIGHT
// Touch pad drives the backlight on devices that have one
touchConfig.longPress = INPUT_BROKER_NONE;
#endif
#if HAS_BACKLIGHT || defined(HAPTIC_FEEDBACK_PIN)
touchConfig.suppressLeadUpSound = true;
touchConfig.onPress = []() {
touchBacklightWasOn = uiconfig.screen_brightness > 0;
if (!touchBacklightWasOn) {
#if HAS_PWM_BACKLIGHT
graphics::backlightOn();
#else
digitalWrite(PIN_EINK_EN, HIGH);
#if defined(HAPTIC_FEEDBACK_PIN)
initHapticFeedback();
#endif
}
// One handler per event, a second assignment would silently drop the first
touchConfig.onPress = []() {
#if HAS_BACKLIGHT
touchBacklightWasOn = graphics::backlightIsLit();
if (!touchBacklightWasOn)
graphics::backlightMomentaryOn();
touchBacklightActive = true;
#endif
#if defined(HAPTIC_FEEDBACK_PIN)
// Blip on touch, second blip when long-press fires (500 ms = touchConfig.longPressTime default).
hapticFeedback->pulse(80);
hapticFeedback->armDelayedPulse(500, 80);
#endif
};
touchConfig.onRelease = []() {
if (touchBacklightActive && !touchBacklightWasOn) {
#if HAS_PWM_BACKLIGHT
#if HAS_BACKLIGHT
if (touchBacklightActive && !touchBacklightWasOn)
graphics::backlightOff();
#else
digitalWrite(PIN_EINK_EN, LOW);
#endif
}
touchBacklightActive = false;
};
#endif
#if defined(HAPTIC_FEEDBACK_PIN)
// Blip on touch, second blip when long-press fires (500 ms = touchConfig.longPressTime default).
touchConfig.suppressLeadUpSound = true;
initHapticFeedback();
touchConfig.onPress = []() {
hapticFeedback->pulse(80);
hapticFeedback->armDelayedPulse(500, 80);
hapticFeedback->cancelDelayedPulse();
#endif
};
touchConfig.onRelease = []() { hapticFeedback->cancelDelayedPulse(); };
#endif
TouchButtonThread->initButton(touchConfig);
#endif
Expand Down
3 changes: 2 additions & 1 deletion variants/esp32s3/ELECROW-ThinkNode-M5/variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@

#define USE_EINK
// Note: this is really just backlight power
#define PCA_PIN_EINK_EN 5 // This is the pin number on the GPIO expander
#define PCA_PIN_EINK_EN 5 // This is the pin number on the GPIO expander
#define GPIO_BACKLIGHT_DEFAULT_ON // matches the previous screen_brightness > 0 wake behaviour
#define PIN_EINK_CS 39
#define PIN_EINK_BUSY 42
#define PIN_EINK_DC 40
Expand Down
Loading
Loading