-
-
Notifications
You must be signed in to change notification settings - Fork 106
feat: Crowpanel V2 brightness control #203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
541623f
b91421f
e71fe45
f142bdf
83681f4
6e994d7
9603b1d
42179ed
c8be3cc
816ac86
33fae73
baa08ee
a4fb22f
87a8216
76d354f
14dc991
724fdbe
48e3a59
499f3a8
dd894d9
0ef43f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,15 +6,83 @@ | |
| #include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp> | ||
| #include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp> | ||
|
|
||
| #define ELECROW_V1_ADDR 0x18 | ||
| #define ELECROW_V2_ADDR 0x30 | ||
|
|
||
| #ifndef FREQ_WRITE | ||
| #define FREQ_WRITE 14000000 | ||
| #define FREQ_WRITE 15800000 | ||
| #endif | ||
|
|
||
| class Elecrow_V2_Light : public lgfx::v1::ILight | ||
| { | ||
| public: | ||
| struct config_t { | ||
| bool isV2 = false; | ||
| bool isV3 = false; | ||
| uint8_t brightness = 153; // 60% | ||
| }; | ||
|
|
||
| const config_t &config(void) const { return _cfg; } | ||
| void config(const config_t &cfg) { _cfg = cfg; } | ||
|
|
||
| bool init(uint8_t brightness) override | ||
| { | ||
| Wire.beginTransmission(ELECROW_V2_ADDR); | ||
| if (Wire.endTransmission() == 0) { | ||
| sendI2CCommand(0x10); | ||
| _cfg.isV2 = true; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| // crowpanel V2 allows 5 brightness levels | ||
| // 0: off (0x05) | ||
| // 1 .. 51: (0x06) | ||
| // 52 .. 102: (0x07) | ||
| // 103 .. 153: (0x08) | ||
| // 154 .. 204: (0x09) | ||
| // 205 .. 255: (0x10) | ||
| // crowpanel V3 has level 0 (brightest) .. 245 (off) | ||
| void setBrightness(uint8_t brightness) override | ||
| { | ||
| if (_cfg.isV2) { | ||
| uint8_t cmd = (brightness + 50) / 51 + 5; | ||
| if (brightness >= 205) | ||
| cmd = 0x10; | ||
| sendI2CCommand(cmd); | ||
| _cfg.brightness = brightness; | ||
| } else if (_cfg.isV3) { | ||
| uint8_t brightnessV3 = 244 - (uint32_t(brightness) * 244 / 255); | ||
| sendI2CCommand(brightnessV3); | ||
| } | ||
| _cfg.brightness = brightness; | ||
| } | ||
|
|
||
| uint8_t getBrightness(void) const { return _cfg.brightness; } | ||
|
|
||
| virtual ~Elecrow_V2_Light(void) = default; | ||
|
|
||
| private: | ||
| // for V2/V3 microcontroller control | ||
| uint8_t sendI2CCommand(uint8_t cmd) | ||
| { | ||
| Wire.beginTransmission(ELECROW_V2_ADDR); | ||
| Wire.write(cmd); | ||
| uint8_t error = Wire.endTransmission(); | ||
| if (error != 0) | ||
| ILOG_ERROR("failed to send command 0x%02x: %d", cmd, error); | ||
| return error; | ||
| } | ||
|
|
||
| config_t _cfg; | ||
| }; | ||
|
|
||
| class LGFX_ELECROW70 : public lgfx::LGFX_Device | ||
| { | ||
| lgfx::Bus_RGB _bus_instance; | ||
| lgfx::Panel_RGB _panel_instance; | ||
| lgfx::Touch_GT911 _touch_instance; | ||
| Elecrow_V2_Light _light_instance; | ||
|
|
||
| public: | ||
| const uint16_t screenWidth = 800; | ||
|
|
@@ -24,28 +92,37 @@ class LGFX_ELECROW70 : public lgfx::LGFX_Device | |
|
|
||
| bool init_impl(bool use_reset, bool use_clear) override | ||
| { | ||
| ioex.attach(Wire); | ||
| ioex.setDeviceAddress(0x18); | ||
| ioex.config(1, TCA9534::Config::OUT); | ||
| ioex.config(2, TCA9534::Config::OUT); | ||
| ioex.config(3, TCA9534::Config::OUT); | ||
| ioex.config(4, TCA9534::Config::OUT); | ||
|
|
||
| ioex.output(1, TCA9534::Level::H); | ||
| ioex.output(3, TCA9534::Level::L); | ||
| ioex.output(4, TCA9534::Level::H); | ||
|
|
||
| pinMode(1, OUTPUT); | ||
| digitalWrite(1, LOW); | ||
| ioex.output(2, TCA9534::Level::L); | ||
| delay(20); | ||
| ioex.output(2, TCA9534::Level::H); | ||
| delay(100); | ||
| pinMode(1, INPUT); | ||
| Wire.beginTransmission(ELECROW_V1_ADDR); | ||
| if (Wire.endTransmission() == 0) { | ||
| ioex.attach(Wire); | ||
| ioex.setDeviceAddress(ELECROW_V1_ADDR); | ||
| ioex.config(1, TCA9534::Config::OUT); | ||
| ioex.config(2, TCA9534::Config::OUT); | ||
| ioex.config(3, TCA9534::Config::OUT); | ||
| ioex.config(4, TCA9534::Config::OUT); | ||
|
|
||
| ioex.output(1, TCA9534::Level::H); | ||
| ioex.output(3, TCA9534::Level::L); | ||
| ioex.output(4, TCA9534::Level::H); | ||
|
|
||
| pinMode(1, OUTPUT); | ||
| digitalWrite(1, LOW); | ||
| ioex.output(2, TCA9534::Level::L); | ||
| delay(20); | ||
| ioex.output(2, TCA9534::Level::H); | ||
| delay(100); | ||
| pinMode(1, INPUT); | ||
| } | ||
|
|
||
| return LGFX_Device::init_impl(use_reset, use_clear); | ||
| } | ||
|
|
||
| lgfx::ILight *light(void) const | ||
| { | ||
| // pointer is used by LGFXDriver to check for hasLight() | ||
| return _light_instance.config().isV2 || _light_instance.config().isV3 ? (lgfx::ILight *)&_light_instance : nullptr; | ||
| } | ||
|
|
||
| LGFX_ELECROW70(void) | ||
| { | ||
| { | ||
|
|
@@ -132,18 +209,30 @@ class LGFX_ELECROW70 : public lgfx::LGFX_Device | |
| _panel_instance.setTouch(&_touch_instance); | ||
| } | ||
|
|
||
| // Set the backlight control | ||
| { | ||
| auto cfg = _light_instance.config(); | ||
| _light_instance.config(cfg); | ||
| _panel_instance.setLight(&_light_instance); | ||
| } | ||
|
|
||
| setPanel(&_panel_instance); | ||
| } | ||
|
|
||
| void sleep(void) | ||
| { | ||
| ioex.output(1, TCA9534::Level::L); | ||
| if (!_light_instance.config().isV2 && !_light_instance.config().isV3) { | ||
| ioex.output(1, TCA9534::Level::L); | ||
| } | ||
| _panel->setSleep(true); | ||
| } | ||
|
|
||
| void wakeup(void) | ||
| { | ||
| ioex.output(1, TCA9534::Level::H); | ||
| _panel->setSleep(false); | ||
| if (!_light_instance.config().isV2 && !_light_instance.config().isV3) { | ||
| ioex.output(1, TCA9534::Level::H); | ||
| } | ||
| } | ||
|
Comment on lines
222
to
236
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show the relevant section with line numbers.
sed -n '180,280p' include/graphics/LGFX/LGFX_ELECROW70.h
# Find references to the light instance and sleep/wakeup behavior.
rg -n "_light_instance|setSleep\\(|isV2|isV3|brightness|0x05|backlight" include/graphics/LGFX -S
# List nearby files that may define panel/light behavior.
fd -a "LGFX_ELECROW70.h|LGFX_.*\\.h|*Light*" include/graphicsRepository: meshtastic/device-ui Length of output: 17831 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the Elecrow light implementation in context.
sed -n '1,120p' include/graphics/LGFX/LGFX_ELECROW70.h
# Inspect the LGFX configuration wrapper for light/panel interactions.
sed -n '1,260p' include/graphics/LGFX/LGFXConfig.h
# Search for setSleep implementations and backlight behavior in the LGFX subtree.
rg -n "setSleep\\(|setLight\\(|ILight|setBrightness\\(|brightness\\s*=|sleep\\(" include/graphics/LGFX -SRepository: meshtastic/device-ui Length of output: 17233 🌐 Web query:
💡 Result: In the LovyanGFX library, the setSleep(bool) function typically commands the display controller IC itself to enter a low-power mode (e.g., sending MIPI DCS commands like CMD_SLPIN) [1][2]. It does not inherently control the physical backlight GPIO pin [3][2]. Key points regarding your query: 1. Backlight Management: LovyanGFX does not automatically manage the physical backlight GPIO during sleep [3]. Because the library does not explicitly handle the backlight state during sleep transitions, you must manually control the backlight (e.g., set the GPIO pin to LOW) when preparing for system sleep [3]. 2. ILight and Brightness: The ILight interface provides the setBrightness(uint8_t brightness) method [4]. While setting brightness to 0 is intended to turn off or dim the backlight, its effectiveness depends entirely on the underlying hardware implementation of the light controller associated with your panel [5][6]. If the backlight hardware does not support dimming via the ILight interface, calling setBrightness(0) will have no effect [5]. 3. Recommendations: To ensure the backlight is off during sleep, you should explicitly set the backlight pin to an "off" state in your code before calling the system's sleep function [3]. Do not rely on Panel_Device::setSleep or ILight::setBrightness(0) to perform this hardware-specific power-down task [3][5]. For reference, the Panel_Device class acts as a base for hardware abstraction [7], but because backlight circuits vary significantly across different boards, the library leaves the physical GPIO management to the user to avoid unintended side effects or hardware damage [3]. Citations:
V2/V3 backlight still needs an explicit off/on command 🤖 Prompt for AI Agents |
||
|
|
||
| private: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: meshtastic/device-ui
Length of output: 158
🌐 Web query:
LovyanGFX ILight init setBrightness interface contract💡 Result:
In the LovyanGFX library, ILight is an abstract base class (interface) defined in the lgfx::v1 namespace, responsible for abstracting backlight control [1][2]. The ILight interface contract consists of two primary pure virtual functions [1][2]: 1. bool init(uint8_t brightness) = 0; This method is responsible for initializing the backlight hardware, such as configuring PWM pins or I2C communication for a PMIC [2][3][4]. The brightness parameter typically defines the initial light intensity level (range 0–255) [1][5][2]. 2. void setBrightness(uint8_t brightness) = 0; This method updates the backlight intensity to the specified value in the range of 0–255 [1][5][2]. Concrete implementations, such as Light_PWM or those for I2C-controlled backlights, override these methods to provide platform-specific hardware control [1][3][4]. Within the broader architecture, a Panel_Device instance composes an ILight object [6][7]. When a user calls the high-level lcd.setBrightness(b) method on their LGFX device, the library internally delegates the call to the registered ILight implementation's setBrightness method [4][7]. The init process for the light component is triggered automatically when the main LGFX_Device::init function is called during the application startup [4][7].
Citations:
🏁 Script executed:
Repository: meshtastic/device-ui
Length of output: 10896
🏁 Script executed:
Repository: meshtastic/device-ui
Length of output: 752
🏁 Script executed:
Repository: meshtastic/device-ui
Length of output: 752
Detect V3 and apply the requested brightness in
init()._cfg.isV3is never set anywhere, so the V3 path insetBrightness()/light()/sleep()/wakeup()is dead and V3 backlight control never activates.init(uint8_t brightness)also ignores the caller’s value and always sends0x10, so startup brightness andgetBrightness()are out of sync.🤖 Prompt for AI Agents