mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-10-09 03:06:39 +00:00
feat(ui-lvgl): power saving, review fixes, shared helpers
Power: the UI loop sleeps until something is due (never with packets queued); screen off puts the CPU at 80 MHz, the GT911 to sleep (unless a tap wakes the screen) and the LP5814 in standby. GPS off now cuts the L76K's rail and UART on the L2 (L2GpsProvider). The battery divider is powered only for a reading (measured: settles at once); readings within 2 s are reused. The SD mount is retried at most every 5 s. Fixes: WiFi left on after leaving a scan, live tiles' WiFi kept with the screen off on the map, an ADC read every second for the status bar, touch polled on every pass while dark, a scan cutting an update's WiFi, relay count not refreshing in a channel thread, raw @[nick] in room previews. Labels skip unchanged text (no redraw). Helpers: scrollList, dimOverlay, settingRow, flexBox, nvs::get/put, trailFilePath (a second save in the same minute no longer overwrites), unreadTotal. Dead code removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,11 @@ static const uint8_t ADS_REG_CONFIG = 0x01;
|
||||
static const uint16_t ADS_CFG_BATT = 0x8000 | 0x4000 | 0x0200 | 0x0100 | 0x0080 | 0x0003;
|
||||
// +/-4.096V FSR -> 125uV/LSB; battery is behind a x2 divider -> 0.25 mV/LSB
|
||||
static const float ADS_MV_PER_LSB = 0.125f * 2.0f;
|
||||
static const uint32_t BATT_SETTLE_MS = 1; // divider on -> input settled (measured: at once)
|
||||
static const uint32_t BATT_REUSE_MS = 2000; // the UI, diagnostics and the app all ask
|
||||
|
||||
// GT911: its command register; 0x05 = sleep
|
||||
static const uint8_t GT911_ADDR = 0x5D;
|
||||
|
||||
bool WioTrackerL2Board::expWriteReg(uint8_t reg, uint8_t val) {
|
||||
Wire.beginTransmission(TCA9535_ADDR);
|
||||
@@ -64,7 +69,7 @@ bool WioTrackerL2Board::initExpander() {
|
||||
delay(10);
|
||||
expSetOutput(EXP_PIN_TF_EN, HIGH);
|
||||
delay(10);
|
||||
expSetOutput(EXP_PIN_BAT_ADC_EN, HIGH);
|
||||
expSetOutput(EXP_PIN_BAT_ADC_EN, LOW); // the divider only draws while a reading is taken
|
||||
delay(10);
|
||||
expSetOutput(EXP_PIN_GNSS_EN, HIGH);
|
||||
delay(10);
|
||||
@@ -216,15 +221,40 @@ int16_t WioTrackerL2Board::adsReadRaw() {
|
||||
// A failed or zero ADS1115 read (the I2C bus busy or the ADC not answering)
|
||||
// keeps the last good value instead of reporting 0 mV -- that read as 0 % in
|
||||
// the status bar and could trip the low-battery shutdown. Retried once first.
|
||||
// The divider is powered only around the reading; one taken in the last 2 s
|
||||
// is reused.
|
||||
uint16_t WioTrackerL2Board::getBattMilliVolts() {
|
||||
if (!expander_ok) return 0; // BAT_ADC_EN rail never came up
|
||||
if (batt_mv_last && millis() - batt_read_ms < BATT_REUSE_MS) return batt_mv_last;
|
||||
|
||||
expWritePin(EXP_PIN_BAT_ADC_EN, HIGH);
|
||||
delay(BATT_SETTLE_MS);
|
||||
for (int attempt = 0; attempt < 2; attempt++) {
|
||||
int16_t raw = adsReadRaw();
|
||||
if (raw > 0) {
|
||||
batt_mv_last = (uint16_t)(raw * ADS_MV_PER_LSB);
|
||||
return batt_mv_last;
|
||||
break;
|
||||
}
|
||||
}
|
||||
expWritePin(EXP_PIN_BAT_ADC_EN, LOW);
|
||||
batt_read_ms = millis();
|
||||
return batt_mv_last;
|
||||
}
|
||||
|
||||
void WioTrackerL2Board::touchSleep() {
|
||||
if (!expander_ok) return;
|
||||
expWritePin(EXP_PIN_TP_INT, LOW); // held low while it sleeps (it is, from the reset sequence on)
|
||||
Wire.beginTransmission(GT911_ADDR);
|
||||
Wire.write((uint8_t)0x80);
|
||||
Wire.write((uint8_t)0x40);
|
||||
Wire.write((uint8_t)0x05);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
void WioTrackerL2Board::touchWake() {
|
||||
if (!expander_ok) return;
|
||||
expWritePin(EXP_PIN_TP_INT, HIGH); // 2-5 ms high wakes it
|
||||
delay(5);
|
||||
expWritePin(EXP_PIN_TP_INT, LOW);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#define ADS1115_ADDR 0x48
|
||||
|
||||
// TCA9535 pin numbering: 0..7 = port 0 (P00..P07), 8..15 = port 1 (P10..P17)
|
||||
#define EXP_PIN_WAKE_BTN 0 // input - side WAKE button
|
||||
#define EXP_PIN_WAKE_BTN 0 // input - WAKE button (top edge)
|
||||
#define EXP_PIN_I2C_IRQ 1 // input - shared I2C IRQ
|
||||
#define EXP_PIN_SD_DETECT 2 // input - microSD card detect
|
||||
#define EXP_PIN_TP_INT 3 // output - touch panel interrupt (driven for reset seq)
|
||||
@@ -57,8 +57,6 @@ public:
|
||||
// antenna away from class-D switching noise and to save battery
|
||||
void setSpeakerAmp(bool on) { expWritePin(EXP_PIN_PA_EN, on); }
|
||||
|
||||
// hardware reset pulse to the L76K GNSS (active HIGH on this board); the
|
||||
// module keeps almanac/ephemeris across it, so this is a warm restart
|
||||
// GNSS rail: off saves the whole receiver when GPS is disabled; on re-runs
|
||||
// the power-up reset so the module comes back cleanly
|
||||
void setGnssPower(bool on) {
|
||||
@@ -73,6 +71,10 @@ public:
|
||||
// Grove expansion port rail (nothing on-board depends on it)
|
||||
void setGrovePower(bool on) { expWritePin(EXP_PIN_GROVE_EN, on); }
|
||||
|
||||
bool gnssPowered() const { return (out_shadow[EXP_PIN_GNSS_EN >> 3] >> (EXP_PIN_GNSS_EN & 7)) & 1; }
|
||||
|
||||
// hardware reset pulse to the L76K GNSS (active HIGH on this board); the
|
||||
// module keeps almanac/ephemeris across it, so this is a warm restart
|
||||
void gnssReset() {
|
||||
expWritePin(EXP_PIN_GNSS_RST, HIGH);
|
||||
delay(10);
|
||||
@@ -82,6 +84,11 @@ public:
|
||||
// WAKE button on expander P00: pressed = level differs from boot baseline
|
||||
bool readWakeButton();
|
||||
|
||||
// GT911 touch controller: sleep (a few mA -> tens of uA) while the screen is
|
||||
// off and a touch shouldn't wake it; wake with a pulse on its INT line.
|
||||
void touchSleep();
|
||||
void touchWake();
|
||||
|
||||
// VBUS presence via the AW35615 USB-C controller (I2C 0x22)
|
||||
bool isExternalPowered() override;
|
||||
int awRead(uint8_t reg); // AW35615 register, -1 on error
|
||||
@@ -95,6 +102,7 @@ private:
|
||||
bool aw_ok = false; // AW35615 USB-C controller responded at probe
|
||||
uint8_t wake_btn_baseline = 0; // idle level of P00, captured at init
|
||||
uint16_t batt_mv_last = 0; // last good battery reading (a failed read keeps it)
|
||||
uint32_t batt_read_ms = 0; // when it was taken (reads closer together reuse it)
|
||||
|
||||
int expReadInputs(); // 16-bit input register pair, -1 on error
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ class WioTrackerL2Backlight : public lgfx::v1::ILight {
|
||||
static constexpr uint8_t REG_LED0_PWM = 0x18;
|
||||
|
||||
uint8_t _brightness = 153; // 60%
|
||||
bool _standby = false; // chip disabled (the screen is off)
|
||||
|
||||
void writeReg(uint8_t reg, uint8_t value) {
|
||||
Wire.beginTransmission(LP5814_I2C_ADDR);
|
||||
@@ -41,13 +42,7 @@ class WioTrackerL2Backlight : public lgfx::v1::ILight {
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
public:
|
||||
bool init(uint8_t brightness) override {
|
||||
Wire.beginTransmission(LP5814_I2C_ADDR);
|
||||
if (Wire.endTransmission() != 0) {
|
||||
return false; // LP5814 not found
|
||||
}
|
||||
|
||||
void configure() {
|
||||
writeReg(REG_DEVICE_CONFIG0, 0x01); // chip enable
|
||||
writeReg(REG_MAX_CURRENT, 0x01); // 51 mA max current
|
||||
writeReg(REG_ENABLE_CONTROL, 0x00); // outputs off while configuring
|
||||
@@ -59,12 +54,28 @@ public:
|
||||
writeReg(REG_ENABLE_CONTROL, 0x0F); // enable all 4 channels
|
||||
writeReg(REG_UPDATE, 0x55); // latch (LP5814 requires 0x55)
|
||||
delay(5);
|
||||
}
|
||||
|
||||
public:
|
||||
bool init(uint8_t brightness) override {
|
||||
Wire.beginTransmission(LP5814_I2C_ADDR);
|
||||
if (Wire.endTransmission() != 0) {
|
||||
return false; // LP5814 not found
|
||||
}
|
||||
configure();
|
||||
setBrightness(brightness);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0 (LovyanGFX's sleep()) puts the chip in standby; the next level brings it
|
||||
// back, set up again.
|
||||
void setBrightness(uint8_t brightness) override {
|
||||
if (brightness == 0) {
|
||||
writeReg(REG_DEVICE_CONFIG0, 0x00); // chip disable: standby
|
||||
_standby = true;
|
||||
return;
|
||||
}
|
||||
if (_standby) { configure(); _standby = false; }
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
writeReg(REG_LED0_PWM + i, brightness);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ WRAPPER_CLASS radio_driver(radio, board);
|
||||
|
||||
ESP32RTCClock fallback_clock;
|
||||
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||
MicroNMEALocationProvider gps(Serial1, &rtc_clock);
|
||||
L2GpsProvider gps(Serial1, &rtc_clock);
|
||||
EnvironmentSensorManager sensors(gps);
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
@@ -22,6 +22,28 @@ EnvironmentSensorManager sensors(gps);
|
||||
void buzzerAmpPower(bool on) { board.setSpeakerAmp(on); }
|
||||
#endif
|
||||
|
||||
void L2GpsProvider::begin() {
|
||||
board.setGnssPower(true); // with the power-up reset
|
||||
if (_uart_off) { // back from stop(): the UART again
|
||||
Serial1.setRxBufferSize(1024);
|
||||
Serial1.begin(GPS_BAUD_RATE, SERIAL_8N1, PIN_GPS_TX, PIN_GPS_RX);
|
||||
_uart_off = false;
|
||||
}
|
||||
MicroNMEALocationProvider::begin();
|
||||
}
|
||||
|
||||
void L2GpsProvider::stop() {
|
||||
MicroNMEALocationProvider::stop();
|
||||
// The UART's TX idling high would feed the unpowered module through its pin.
|
||||
Serial1.end();
|
||||
pinMode(PIN_GPS_RX, INPUT); // PIN_GPS_RX: the module's RX, our TX
|
||||
_uart_off = true;
|
||||
board.setGnssPower(false);
|
||||
}
|
||||
|
||||
void L2GpsProvider::reset() { board.gnssReset(); }
|
||||
bool L2GpsProvider::isEnabled() { return board.gnssPowered(); }
|
||||
|
||||
bool radio_init() {
|
||||
MESH_DEBUG_PRINTLN("radio_init: rtc + sx1262 init");
|
||||
fallback_clock.begin();
|
||||
|
||||
@@ -14,11 +14,24 @@
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#endif
|
||||
|
||||
// The L76K's power and reset lines are on the IO expander, out of the NMEA
|
||||
// provider's reach: begin() / stop() / reset() drive them here, so GPS off
|
||||
// (Settings, duty cycling) really powers the receiver down.
|
||||
class L2GpsProvider : public MicroNMEALocationProvider {
|
||||
bool _uart_off = false;
|
||||
public:
|
||||
using MicroNMEALocationProvider::MicroNMEALocationProvider;
|
||||
void begin() override;
|
||||
void stop() override;
|
||||
void reset() override;
|
||||
bool isEnabled() override;
|
||||
};
|
||||
|
||||
extern WioTrackerL2Board board;
|
||||
extern WRAPPER_CLASS radio_driver;
|
||||
extern AutoDiscoverRTCClock rtc_clock;
|
||||
extern EnvironmentSensorManager sensors;
|
||||
extern MicroNMEALocationProvider gps;
|
||||
extern L2GpsProvider gps;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
extern DISPLAY_CLASS display;
|
||||
|
||||
Reference in New Issue
Block a user