Skip to content
Open
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
131 changes: 110 additions & 21 deletions include/graphics/LGFX/LGFX_ELECROW70.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Comment on lines +28 to +36

Copy link
Copy Markdown
Contributor

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:

#!/bin/bash
rg -nP 'isV3' --type=cpp --type-add 'header:*.h' --type=header
rg -nP '\.init\s*\(' -C2 include/graphics/LGFX/LGFX_ELECROW70.h

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:

#!/bin/bash
set -euo pipefail

printf '\n== file outline ==\n'
ast-grep outline include/graphics/LGFX/LGFX_ELECROW70.h --view expanded || true

printf '\n== relevant file slice ==\n'
sed -n '1,220p' include/graphics/LGFX/LGFX_ELECROW70.h | cat -n

printf '\n== search for ELECROW variants and V3 ==\n'
rg -n "ELECROW|isV3|setBrightness\\(|init\\(uint8_t brightness\\)" include/graphics/LGFX -S

Repository: meshtastic/device-ui

Length of output: 10896


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '220,250p' include/graphics/LGFX/LGFX_ELECROW70.h | cat -n

Repository: meshtastic/device-ui

Length of output: 752


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '220,240p' include/graphics/LGFX/LGFX_ELECROW70.h | cat -n

Repository: meshtastic/device-ui

Length of output: 752


Detect V3 and apply the requested brightness in init(). _cfg.isV3 is never set anywhere, so the V3 path in setBrightness()/light()/sleep()/wakeup() is dead and V3 backlight control never activates. init(uint8_t brightness) also ignores the caller’s value and always sends 0x10, so startup brightness and getBrightness() are out of sync.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@include/graphics/LGFX/LGFX_ELECROW70.h` around lines 28 - 36, Update
ELECROW70::init(uint8_t brightness) so it detects the V3 device and sets
_cfg.isV3 when the V3 address/handshake is found, keeping the existing V2
detection intact. Also stop hardcoding the startup command: use the brightness
argument when sending the initial backlight command so init() matches
getBrightness() and the later setBrightness()/light()/sleep()/wakeup() paths
stay consistent.


// 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;
Expand All @@ -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)
{
{
Expand Down Expand Up @@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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/graphics

Repository: 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 -S

Repository: meshtastic/device-ui

Length of output: 17233


🌐 Web query:

LovyanGFX Panel_Device setSleep attached light backlight sleep behavior ILight setBrightness 0

💡 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
_panel->setSleep(true) only puts the panel IC to sleep; it does not power down the backlight. For V2/V3, send brightness 0 in sleep() and restore the saved brightness in wakeup(), otherwise the backlight can stay lit while the panel is asleep.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@include/graphics/LGFX/LGFX_ELECROW70.h` around lines 222 - 236, The
sleep/wakeup handling in LGFX_ELECROW70 currently only toggles panel sleep and
the IO expander for non-V2/V3 boards, so V2/V3 backlight remains on. Update the
sleep() and wakeup() methods to explicitly turn the backlight off by sending
brightness 0 during sleep and restore the previously saved brightness during
wakeup, while keeping the existing _panel->setSleep(true/false) and TCA9534
output logic intact. Use the existing config checks on _light_instance and the
current backlight control path in LGFX_ELECROW70 to place the fix.


private:
Expand Down
Loading