mirror of
https://github.com/pelgraine/Meck.git
synced 2026-08-08 01:32:42 +02:00
So the power investigation wraps up like this:
GPS — fixed (the "off" toggle and boot path now actually cut the XL9555 rail). Kept in main.cpp + UITask.cpp. BLE controller — the ~13 mA between the BLE build (BLE off) and standalone is the controller staying initialised; the boot-gate in SerialBLEInterface.cpp/.h defers BLEDevice::init() until first enable, reclaiming it. That's the meaningful idle win. CPU, gyro rail, ES8311, frontlight — all measured and ruled out. The residual ~12 mA Max-vs-Pro is distributed always-on hardware with no software switch — hardware overhead, not a bug. Defer ESP32 BLE controller bring-up to first user enable MyMesh::startInterface() called serial.enable() unconditionally at boot. On the ESP32 BLE build this ran the deferred-init SerialBLEInterface's _realBegin()/BLEDevice::init() and powered the BT controller before main.cpp's boot-time disable(), which only stops advertising and cannot power the controller back down -- so the controller stayed up while "off", drawing ~13 mA at idle. Guard the enable() so ESP32 BLE builds skip it at boot; the controller now comes up lazily on the first enable() when the user turns Bluetooth on from the Bluetooth page. WiFi builds are unaffected and still enable at boot.
This commit is contained in:
@@ -1584,7 +1584,16 @@ uint32_t MyMesh::getBLEPin() {
|
||||
|
||||
void MyMesh::startInterface(BaseSerialInterface &serial) {
|
||||
_serial = &serial;
|
||||
#if defined(ESP32) && defined(BLE_PIN_CODE) && !defined(WIFI_SSID) && !defined(MECK_WIFI_COMPANION)
|
||||
// ESP32 BLE companion: do NOT power the BT controller at boot. With the
|
||||
// deferred-init SerialBLEInterface the controller stays down until the first
|
||||
// enable(), which now happens only when the user turns Bluetooth on from the
|
||||
// Bluetooth page. Reclaims the controller idle current in the standalone-first
|
||||
// default. nRF52 BLE (separate class, no deferred init) and USB-serial / WiFi
|
||||
// are unaffected and still enable at boot below.
|
||||
#else
|
||||
serial.enable();
|
||||
#endif
|
||||
}
|
||||
|
||||
void MyMesh::handleCmdFrame(size_t len) {
|
||||
|
||||
@@ -2596,6 +2596,9 @@ void setup() {
|
||||
#ifdef PIN_GPS_EN
|
||||
digitalWrite(PIN_GPS_EN, !GPS_EN_ACTIVE);
|
||||
#endif
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
board.gpsPowerOff(); // MAX: GPS power is XL9555-routed, not PIN_GPS_EN
|
||||
#endif
|
||||
sensors.setSettingValue("gps", "0");
|
||||
}
|
||||
Serial.printf("GPS: power %s\n", gps_wanted ? "ON" : "OFF");
|
||||
|
||||
@@ -1863,6 +1863,9 @@ void UITask::shutdown(bool restart){
|
||||
#ifdef PIN_GPS_EN
|
||||
digitalWrite(PIN_GPS_EN, !GPS_EN_ACTIVE);
|
||||
#endif
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
board.gpsPowerOff(); // MAX: GPS power is XL9555-routed, not PIN_GPS_EN
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -2804,6 +2807,9 @@ void UITask::toggleGPS() {
|
||||
#ifdef PIN_GPS_EN
|
||||
digitalWrite(PIN_GPS_EN, !GPS_EN_ACTIVE);
|
||||
#endif
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
board.gpsPowerOff(); // MAX: GPS power is XL9555-routed, not PIN_GPS_EN
|
||||
#endif
|
||||
notify(UIEventType::ack);
|
||||
} else {
|
||||
// Enable GPS — power on hardware
|
||||
@@ -2812,6 +2818,9 @@ void UITask::toggleGPS() {
|
||||
#ifdef PIN_GPS_EN
|
||||
digitalWrite(PIN_GPS_EN, GPS_EN_ACTIVE);
|
||||
#endif
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
board.gpsPowerOn(); // MAX: GPS power is XL9555-routed, not PIN_GPS_EN
|
||||
#endif
|
||||
notify(UIEventType::ack);
|
||||
}
|
||||
the_mesh.savePrefs();
|
||||
|
||||
@@ -21,11 +21,21 @@ void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code
|
||||
sprintf(name, "%02X%02X%02X%02X%02X%02X", // modify (IN-OUT param)
|
||||
addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
|
||||
}
|
||||
char dev_name[32+16];
|
||||
sprintf(dev_name, "%s%s", prefix, name);
|
||||
// Store the device name and defer the actual controller bring-up
|
||||
// (BLEDevice::init + GATT setup) until the first enable(). This keeps the BT
|
||||
// controller powered down while BLE is disabled -- the standalone-first
|
||||
// default -- which reclaims its idle current. The controller comes up on the
|
||||
// first enable and, as with the web-reader teardown path, stays up until the
|
||||
// next reboot thereafter.
|
||||
snprintf(_dev_name, sizeof(_dev_name), "%s%s", prefix, name);
|
||||
_begun = false;
|
||||
}
|
||||
|
||||
// Deferred BLE controller + GATT bring-up. Called from the first enable() so
|
||||
// the controller is not powered while BLE is disabled.
|
||||
void SerialBLEInterface::_realBegin() {
|
||||
// Create the BLE Device
|
||||
BLEDevice::init(dev_name);
|
||||
BLEDevice::init(_dev_name);
|
||||
BLEDevice::setSecurityCallbacks(this);
|
||||
BLEDevice::setMTU(MAX_FRAME_SIZE);
|
||||
|
||||
@@ -35,7 +45,7 @@ void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code
|
||||
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN, ESP_PWR_LVL_P9);
|
||||
|
||||
BLESecurity sec;
|
||||
sec.setStaticPIN(pin_code);
|
||||
sec.setStaticPIN(_pin_code);
|
||||
sec.setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND);
|
||||
|
||||
//BLEDevice::setPower(ESP_PWR_LVL_N8);
|
||||
@@ -161,6 +171,11 @@ void SerialBLEInterface::onWrite(BLECharacteristic* pCharacteristic, esp_ble_gat
|
||||
void SerialBLEInterface::enable() {
|
||||
if (_isEnabled) return;
|
||||
|
||||
if (!_begun) { // deferred controller bring-up on first enable
|
||||
_realBegin();
|
||||
_begun = true;
|
||||
}
|
||||
|
||||
_isEnabled = true;
|
||||
clearBuffers();
|
||||
|
||||
|
||||
@@ -13,9 +13,11 @@ class SerialBLEInterface : public BaseSerialInterface, BLESecurityCallbacks, BLE
|
||||
bool deviceConnected;
|
||||
bool oldDeviceConnected;
|
||||
bool _isEnabled;
|
||||
bool _begun; // has _realBegin() run? (deferred BLE bring-up)
|
||||
uint16_t last_conn_id;
|
||||
uint8_t _remote_bda[6]; // peer BDA, stored in onConnect for conn param updates
|
||||
uint32_t _pin_code;
|
||||
char _dev_name[48]; // stored in begin(), consumed by deferred _realBegin()
|
||||
unsigned long _last_write;
|
||||
unsigned long adv_restart_time;
|
||||
|
||||
@@ -32,6 +34,8 @@ class SerialBLEInterface : public BaseSerialInterface, BLESecurityCallbacks, BLE
|
||||
|
||||
void clearBuffers() { recv_queue_len = 0; send_queue_len = 0; }
|
||||
|
||||
void _realBegin(); // deferred BLE controller + GATT bring-up
|
||||
|
||||
protected:
|
||||
// BLESecurityCallbacks methods
|
||||
uint32_t onPassKeyRequest() override;
|
||||
@@ -57,6 +61,7 @@ public:
|
||||
oldDeviceConnected = false;
|
||||
adv_restart_time = 0;
|
||||
_isEnabled = false;
|
||||
_begun = false;
|
||||
_last_write = 0;
|
||||
last_conn_id = 0;
|
||||
memset(_remote_bda, 0, 6);
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
// CPU Frequency Scaling for ESP32-S3
|
||||
//
|
||||
// Typical current draw (CPU only, rough):
|
||||
// 240 MHz ~70-80 mA
|
||||
// 160 MHz ~50-60 mA
|
||||
// 80 MHz ~30-40 mA
|
||||
// 40 MHz ~15-20 mA (low-power / lock screen mode)
|
||||
//
|
||||
// SPI peripherals and UART use their own clock dividers from the APB clock,
|
||||
// so LoRa, e-ink, and GPS serial all work fine at 80MHz and 40MHz.
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
#ifndef CPU_FREQ_IDLE
|
||||
#define CPU_FREQ_IDLE 80 // MHz — normal mesh listening
|
||||
#endif
|
||||
|
||||
#ifndef CPU_FREQ_BOOST
|
||||
#define CPU_FREQ_BOOST 240 // MHz — heavy processing
|
||||
#endif
|
||||
|
||||
#ifndef CPU_FREQ_LOW_POWER
|
||||
#define CPU_FREQ_LOW_POWER 80 // MHz — lock screen / idle standby (40 MHz breaks I2C)
|
||||
#endif
|
||||
|
||||
#ifndef CPU_BOOST_TIMEOUT_MS
|
||||
#define CPU_BOOST_TIMEOUT_MS 10000 // 10 seconds
|
||||
#endif
|
||||
|
||||
class CPUPowerManager {
|
||||
public:
|
||||
CPUPowerManager() : _boosted(false), _lowPower(false), _boost_started(0) {}
|
||||
|
||||
void begin() {
|
||||
setCpuFrequencyMhz(CPU_FREQ_IDLE);
|
||||
_boosted = false;
|
||||
_lowPower = false;
|
||||
MESH_DEBUG_PRINTLN("CPU power: idle at %d MHz", CPU_FREQ_IDLE);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (_boosted && (millis() - _boost_started >= CPU_BOOST_TIMEOUT_MS)) {
|
||||
// Return to low-power if locked, otherwise normal idle
|
||||
if (_lowPower) {
|
||||
setCpuFrequencyMhz(CPU_FREQ_LOW_POWER);
|
||||
MESH_DEBUG_PRINTLN("CPU power: boost expired, returning to low-power %d MHz", CPU_FREQ_LOW_POWER);
|
||||
} else {
|
||||
setCpuFrequencyMhz(CPU_FREQ_IDLE);
|
||||
MESH_DEBUG_PRINTLN("CPU power: idle at %d MHz", CPU_FREQ_IDLE);
|
||||
}
|
||||
_boosted = false;
|
||||
}
|
||||
}
|
||||
|
||||
void setBoost() {
|
||||
if (!_boosted) {
|
||||
setCpuFrequencyMhz(CPU_FREQ_BOOST);
|
||||
_boosted = true;
|
||||
MESH_DEBUG_PRINTLN("CPU power: boosted to %d MHz", CPU_FREQ_BOOST);
|
||||
}
|
||||
_boost_started = millis();
|
||||
}
|
||||
|
||||
void setIdle() {
|
||||
if (_boosted) {
|
||||
setCpuFrequencyMhz(CPU_FREQ_IDLE);
|
||||
_boosted = false;
|
||||
MESH_DEBUG_PRINTLN("CPU power: idle at %d MHz", CPU_FREQ_IDLE);
|
||||
}
|
||||
if (_lowPower) {
|
||||
_lowPower = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Low-power mode — drops CPU to 40 MHz for lock screen standby.
|
||||
// If currently boosted, the boost timeout will return to 40 MHz
|
||||
// instead of 80 MHz.
|
||||
void setLowPower() {
|
||||
_lowPower = true;
|
||||
if (!_boosted) {
|
||||
setCpuFrequencyMhz(CPU_FREQ_LOW_POWER);
|
||||
MESH_DEBUG_PRINTLN("CPU power: low-power at %d MHz", CPU_FREQ_LOW_POWER);
|
||||
}
|
||||
// If boosted, the loop() timeout will drop to low-power instead of idle
|
||||
}
|
||||
|
||||
// Exit low-power mode — returns to normal idle (80 MHz).
|
||||
// If currently boosted, the boost timeout will return to idle
|
||||
// instead of low-power.
|
||||
void clearLowPower() {
|
||||
_lowPower = false;
|
||||
if (!_boosted) {
|
||||
setCpuFrequencyMhz(CPU_FREQ_IDLE);
|
||||
MESH_DEBUG_PRINTLN("CPU power: idle at %d MHz (low-power cleared)", CPU_FREQ_IDLE);
|
||||
}
|
||||
// If boosted, the loop() timeout will drop to idle as normal
|
||||
}
|
||||
|
||||
bool isBoosted() const { return _boosted; }
|
||||
bool isLowPower() const { return _lowPower; }
|
||||
uint32_t getFrequencyMHz() const { return getCpuFrequencyMhz(); }
|
||||
|
||||
private:
|
||||
bool _boosted;
|
||||
bool _lowPower;
|
||||
unsigned long _boost_started;
|
||||
};
|
||||
|
||||
#endif // ESP32
|
||||
Reference in New Issue
Block a user