Finish powermon/powerstress (#4230)

* Turn off vscode cmake prompt - we don't use cmake on meshtastic

* Add rak4631_dap variant for debugging with NanoDAP debug probe device.

* The rak device can also run freertos (which is underneath nrf52 arduino)

* Add semihosting support for nrf52840 devices
Initial platformio.ini file only supports rak4630
Default to non TCP for the semihosting log output for now...
Fixes https://github.com/meshtastic/firmware/issues/4135

* powermon WIP (for https://github.com/meshtastic/firmware/issues/4136 )

* oops - mean't to mark the _dbg variant as an 'extra' board.

* powermon wip

* Make serial port on wio-sdk-wm1110 board work
By disabling the (inaccessible) adafruit USB

* Instrument (radiolib only for now) lora for powermon
per https://github.com/meshtastic/firmware/issues/4136

* powermon gps support
https://github.com/meshtastic/firmware/issues/4136

* Add CPU deep and light sleep powermon states
https://github.com/meshtastic/firmware/issues/4136

* Change the board/swversion bootstring so it is a new "structured" log msg.

* powermon wip

* add example script for getting esp S3 debugging working
Not yet used but I didn't want these nasty tricks to get lost yet.

* Add PowerMon reporting for screen and bluetooth pwr.

* make power.powermon_enables config setting work.

* update to latest protobufs

* fix bogus shellcheck warning

* make powermon optional (but default enabled because tiny and no runtime impact)

* tell vscode, if formatting, use whatever our trunk formatter wants
without this flag if the user has set some other formatter (clang)
in their user level settings, it will be looking in the wrong directory
for the clang options (we want the options in .trunk/clang)

Note: formatOnSave is true in master, which means a bunch of our older
files are non compliant and if you edit them it will generate lots of
formatting related diffs.  I guess I'll start letting that happen with
my future commits ;-).

* add PowerStress module

* nrf52 arduino is built upon freertos, so let platformio debug it

* don't accidentally try to Segger ICE if we are using another ICE

* clean up RedirectablePrint::log so it doesn't have three very different implementations inline.

* remove NoopPrint - it is no longer needed

* when talking to API clients via serial, don't turn off log msgs instead encapsuate them

* fix the build - would loop forever if there were no files to send

* don't use Segger code if not talking to a Segger debugger

* when encapsulating logs, make sure the strings always has nul terminators

* nrf52 soft device will watchdog if you use ICE while BT on...
so have debugger disable bluetooth.

* Important to not print debug messages while writing to the toPhone scratch buffer

* don't include newlines if encapsulating log records as protobufs

* update to latest protobufs (needed for powermon goo)

* PowerStress WIP

* for #4154 and #4136 add concept of dependent gpios...
Which is currently only tested with the LED but eventually
will be used for shared GPIO/screen power rail enable
and LED forcing (which is a sanity check in the power stress
testing)

* fix linter warning

* Transformer is a better name for the LED input > operation > output classes

* PMW led changes to work on esp32-s3

* power stress improvements

* allow ble logrecords to be fetched either by NOTIFY or INDICATE ble types

This allows 'lossless' log reading.  If client has requested INDICATE
(rather than NOTIFY) each log record emitted via log() will have to fetched
by the client device before the meshtastic node can continue.

* Fix serious problem with nrf52 BLE logging.
When doing notifies of LogRecords it is important to use the
binary write routines - writing using the 'string' write won't work.
Because protobufs can contain \0 nuls inside of them which if being
parsed as a string will cause only a portion of the protobuf to be sent.
I noticed this because some log messages were not getting through.

* fix gpio transformer stuff to work correctly with LED_INVERTED

Thanks @todd-herbert for noticing this and the great stack trace.
The root cause was that I had accidentially shadowed outPin in a subclass
with an unneeded override.  It would break on any board that had inverted
LED power.

fixes
https://github.com/meshtastic/firmware/pull/4230#pullrequestreview-2217389099

* Support driving multiple output gpios from one input.

While investigating https://github.com/meshtastic/firmware/pull/4230#pullrequestreview-2217389099
I noticed in variant.h that there are now apparently newer TBEAMs than mine
that have _both_ a GPIO based power LED and the PMU based LED.  Add a splitter
so that we can drive two output GPIOs from one logical signal.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
geeksville 2024-08-06 10:35:54 -07:00 committed by GitHub
parent 06eaf2ba5d
commit c1870f91fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 422 additions and 69 deletions

84
src/GpioLogic.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "GpioLogic.h"
#include <assert.h>
void GpioVirtPin::set(bool value)
{
if (value != this->value) {
this->value = value ? PinState::On : PinState::Off;
if (dependentPin)
dependentPin->update();
}
}
GpioTransformer::GpioTransformer(GpioPin *outPin) : outPin(outPin) {}
void GpioTransformer::set(bool value)
{
outPin->set(value);
}
GpioNotTransformer::GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioTransformer(outPin), inPin(inPin)
{
assert(!inPin->dependentPin); // We only allow one dependent pin
inPin->dependentPin = this;
// Don't update at construction time, because various GpioPins might be global constructor based not yet initied because
// order of operations for global constructors is not defined.
// update();
}
/**
* Update the output pin based on the current state of the input pin.
*/
void GpioNotTransformer::update()
{
auto p = inPin->get();
if (p == GpioVirtPin::PinState::Unset)
return; // Not yet fully initialized
set(!p);
}
GpioBinaryTransformer::GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation)
: GpioTransformer(outPin), inPin1(inPin1), inPin2(inPin2), operation(operation)
{
assert(!inPin1->dependentPin); // We only allow one dependent pin
inPin1->dependentPin = this;
assert(!inPin2->dependentPin); // We only allow one dependent pin
inPin2->dependentPin = this;
// Don't update at construction time, because various GpioPins might be global constructor based not yet initied because
// order of operations for global constructors is not defined.
// update();
}
void GpioBinaryTransformer::update()
{
auto p1 = inPin1->get(), p2 = inPin2->get();
GpioVirtPin::PinState newValue = GpioVirtPin::PinState::Unset;
if (p1 == GpioVirtPin::PinState::Unset)
newValue = p2; // Not yet fully initialized
else if (p2 == GpioVirtPin::PinState::Unset)
newValue = p1; // Not yet fully initialized
// If we've already found our value just use it, otherwise need to do the operation
if (newValue == GpioVirtPin::PinState::Unset) {
switch (operation) {
case And:
newValue = (GpioVirtPin::PinState)(p1 && p2);
break;
case Or:
newValue = (GpioVirtPin::PinState)(p1 || p2);
break;
case Xor:
newValue = (GpioVirtPin::PinState)(p1 != p2);
break;
default:
assert(false);
}
}
set(newValue);
}
GpioSplitter::GpioSplitter(GpioPin *outPin1, GpioPin *outPin2) : outPin1(outPin1), outPin2(outPin2) {}

144
src/GpioLogic.h Normal file
View File

@ -0,0 +1,144 @@
#pragma once
#include "configuration.h"
/**This is a set of classes to mediate access to GPIOs in a structured way. Most usage of GPIOs do not
require these classes! But if your hardware has a GPIO that is 'shared' between multiple devices (i.e. a shared power enable)
then using these classes might be able to let you cleanly turn on that enable when either dependent device is needed.
Note: these classes are intended to be 99% inline for the common case so should have minimal impact on flash or RAM
requirements.
*/
/**
* A logical GPIO pin (not necessary raw hardware).
*/
class GpioPin
{
public:
virtual void set(bool value) = 0;
};
/**
* A physical GPIO hw pin.
*/
class GpioHwPin : public GpioPin
{
uint32_t num;
public:
explicit GpioHwPin(uint32_t num) : num(num) {}
void set(bool value) { digitalWrite(num, value); }
};
class GpioTransformer;
class GpioNotTransformer;
class GpioBinaryTransformer;
/**
* A virtual GPIO pin.
*/
class GpioVirtPin : public GpioPin
{
friend class GpioBinaryTransformer;
friend class GpioNotTransformer;
public:
enum PinState { On = true, Off = false, Unset = 2 };
void set(bool value);
PinState get() const { return value; }
private:
PinState value = PinState::Unset;
GpioTransformer *dependentPin = NULL;
};
#include <assert.h>
/**
* A 'smart' trigger that can depend in a fake GPIO and if that GPIO changes, drive some other downstream GPIO to change.
* notably: the set method is not public (because it always is calculated by a subclass)
*/
class GpioTransformer
{
public:
/**
* Update the output pin based on the current state of the input pin.
*/
virtual void update() = 0;
protected:
GpioTransformer(GpioPin *outPin);
void set(bool value);
private:
GpioPin *outPin;
};
/**
* A transformer that performs a unary NOT operation from an input.
*/
class GpioNotTransformer : public GpioTransformer
{
public:
GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin);
protected:
friend class GpioVirtPin;
/**
* Update the output pin based on the current state of the input pin.
*/
void update();
private:
GpioVirtPin *inPin;
};
/**
* A transformer that combines multiple virtual pins to drive an output pin
*/
class GpioBinaryTransformer : public GpioTransformer
{
public:
enum Operation { And, Or, Xor };
GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation);
protected:
friend class GpioVirtPin;
/**
* Update the output pin based on the current state of the input pins.
*/
void update();
private:
GpioVirtPin *inPin1;
GpioVirtPin *inPin2;
Operation operation;
};
/**
* Sometimes a single output GPIO single needs to drive multiple physical GPIOs. This class provides that.
*/
class GpioSplitter : public GpioPin
{
public:
GpioSplitter(GpioPin *outPin1, GpioPin *outPin2);
void set(bool value)
{
outPin1->set(value);
outPin2->set(value);
}
private:
GpioPin *outPin1;
GpioPin *outPin2;
};

66
src/Led.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "Led.h"
#include "PowerMon.h"
#include "main.h"
#include "power.h"
GpioVirtPin ledForceOn, ledBlink;
#if defined(LED_PIN)
// Most boards have a GPIO for LED control
static GpioHwPin ledRawHwPin(LED_PIN);
#else
static GpioVirtPin ledRawHwPin; // Dummy pin for no hardware
#endif
#if LED_STATE_ON == 0
static GpioVirtPin ledHwPin;
static GpioNotTransformer ledInverter(&ledHwPin, &ledRawHwPin);
#else
static GpioPin &ledHwPin = ledRawHwPin;
#endif
#if defined(HAS_PMU)
/**
* A GPIO controlled by the PMU
*/
class GpioPmuPin : public GpioPin
{
public:
void set(bool value)
{
if (pmu_found && PMU) {
// blink the axp led
PMU->setChargingLedMode(value ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
}
}
} ledPmuHwPin;
// In some cases we need to drive a PMU LED and a normal LED
static GpioSplitter ledFinalPin(&ledHwPin, &ledPmuHwPin);
#else
static GpioPin &ledFinalPin = ledHwPin;
#endif
#ifdef USE_POWERMON
/**
* We monitor changes to the LED drive output because we use that as a sanity test in our power monitor stuff.
*/
class MonitoredLedPin : public GpioPin
{
public:
void set(bool value)
{
if (powerMon) {
if (value)
powerMon->setState(meshtastic_PowerMon_State_LED_On);
else
powerMon->clearState(meshtastic_PowerMon_State_LED_On);
}
ledFinalPin.set(value);
}
} monitoredLedPin;
#else
static GpioPin &monitoredLedPin = ledFinalPin;
#endif
static GpioBinaryTransformer ledForcer(&ledForceOn, &ledBlink, &monitoredLedPin, GpioBinaryTransformer::Or);

7
src/Led.h Normal file
View File

@ -0,0 +1,7 @@
#include "GpioLogic.h"
#include "configuration.h"
/**
* ledForceOn and ledForceOff both override the normal ledBlinker behavior (which is controlled by main)
*/
extern GpioVirtPin ledForceOn, ledBlink;

View File

@ -80,9 +80,6 @@ RAK9154Sensor rak9154Sensor;
#endif
#ifdef HAS_PMU
#include "XPowersAXP192.tpp"
#include "XPowersAXP2101.tpp"
#include "XPowersLibInterface.hpp"
XPowersLibInterface *PMU = NULL;
#else

View File

@ -9,6 +9,7 @@
*/
#include "PowerFSM.h"
#include "Default.h"
#include "Led.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerMon.h"
@ -50,7 +51,6 @@ static bool isPowered()
static void sdsEnter()
{
LOG_DEBUG("Enter state: SDS\n");
powerMon->setState(meshtastic_PowerMon_State_CPU_DeepSleep);
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false);
}
@ -70,7 +70,6 @@ static uint32_t secsSlept;
static void lsEnter()
{
LOG_INFO("lsEnter begin, ls_secs=%u\n", config.power.ls_secs);
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(false);
secsSlept = 0; // How long have we been sleeping this time
@ -91,7 +90,7 @@ static void lsIdle()
uint32_t sleepTime = SLEEP_TIME;
powerMon->setState(meshtastic_PowerMon_State_CPU_LightSleep);
setLed(false); // Never leave led on while in light sleep
ledBlink.set(false); // Never leave led on while in light sleep
esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL);
powerMon->clearState(meshtastic_PowerMon_State_CPU_LightSleep);
@ -99,7 +98,7 @@ static void lsIdle()
case ESP_SLEEP_WAKEUP_TIMER:
// Normal case: timer expired, we should just go back to sleep ASAP
setLed(true); // briefly turn on led
ledBlink.set(true); // briefly turn on led
wakeCause2 = doLightSleep(100); // leave led on for 1ms
secsSlept += sleepTime;
@ -134,7 +133,7 @@ static void lsIdle()
}
} else {
// Time to stop sleeping!
setLed(false);
ledBlink.set(false);
LOG_INFO("Reached ls_secs, servicing loop()\n");
powerFSM.trigger(EVENT_WAKE_TIMER);
}
@ -149,7 +148,6 @@ static void lsExit()
static void nbEnter()
{
LOG_DEBUG("Enter state: NB\n");
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
screen->setOn(false);
#ifdef ARCH_ESP32
// Only ESP32 should turn off bluetooth
@ -161,8 +159,6 @@ static void nbEnter()
static void darkEnter()
{
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
setBluetoothEnable(true);
screen->setOn(false);
}
@ -170,8 +166,6 @@ static void darkEnter()
static void serialEnter()
{
LOG_DEBUG("Enter state: SERIAL\n");
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
setBluetoothEnable(false);
screen->setOn(true);
screen->print("Serial connected\n");
@ -180,7 +174,6 @@ static void serialEnter()
static void serialExit()
{
// Turn bluetooth back on when we leave serial stream API
powerMon->setState(meshtastic_PowerMon_State_BT_On);
setBluetoothEnable(true);
screen->print("Serial disconnected\n");
}
@ -193,8 +186,6 @@ static void powerEnter()
LOG_INFO("Loss of power in Powered\n");
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
} else {
powerMon->setState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true);
setBluetoothEnable(true);
// within enter() the function getState() returns the state we came from
@ -218,8 +209,6 @@ static void powerIdle()
static void powerExit()
{
powerMon->setState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true);
setBluetoothEnable(true);
@ -231,8 +220,6 @@ static void powerExit()
static void onEnter()
{
LOG_DEBUG("Enter state: ON\n");
powerMon->setState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true);
setBluetoothEnable(true);
}

View File

@ -2,9 +2,11 @@
#include "NodeDB.h"
// Use the 'live' config flag to figure out if we should be showing this message
static bool is_power_enabled(uint64_t m)
bool PowerMon::is_power_enabled(uint64_t m)
{
return (m & config.power.powermon_enables) ? true : false;
// FIXME: VERY STRANGE BUG: if I or in "force_enabled || " the flashed image on a rak4631 is not accepted by the bootloader as
// valid!!! Possibly a linker/gcc/bootloader bug somewhere?
return ((m & config.power.powermon_enables) ? true : false);
}
void PowerMon::setState(_meshtastic_PowerMon_State state, const char *reason)

View File

@ -17,6 +17,13 @@ class PowerMon
{
uint64_t states = 0UL;
friend class PowerStressModule;
/**
* If stress testing we always want all events logged
*/
bool force_enabled = false;
public:
PowerMon() {}
@ -27,6 +34,9 @@ class PowerMon
private:
// Emit the coded log message
void emitLog(const char *reason);
// Use the 'live' config flag to figure out if we should be showing this message
bool is_power_enabled(uint64_t m);
};
extern PowerMon *powerMon;

View File

@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Screen.h"
#include "../userPrefs.h"
#include "PowerMon.h"
#include "configuration.h"
#if HAS_SCREEN
#include <OLEDDisplay.h>
@ -1574,6 +1575,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
if (on != screenOn) {
if (on) {
LOG_INFO("Turning on screen\n");
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
#ifdef T_WATCH_S3
PMU->enablePowerOutput(XPOWERS_ALDO2);
#endif
@ -1599,6 +1601,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
setInterval(0); // Draw ASAP
runASAP = true;
} else {
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
#ifdef USE_EINK
// eInkScreensaver parameter is usually NULL (default argument), default frame used instead
setScreensaverFrames(einkScreensaver);

View File

@ -15,6 +15,7 @@
#include "power.h"
// #include "debug.h"
#include "FSCommon.h"
#include "Led.h"
#include "RTC.h"
#include "SPILock.h"
#include "concurrency/OSThread.h"
@ -197,7 +198,7 @@ static int32_t ledBlinker()
static bool ledOn;
ledOn ^= 1;
setLed(ledOn);
ledBlink.set(ledOn);
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 1 : 1000);

View File

@ -9,9 +9,9 @@
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
#include "Led.h"
#include "power.h"
#include "serialization/JSON.h"
#include "sleep.h"
#include <FSCommon.h>
#include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp>
@ -798,9 +798,9 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
if (blink_target == "LED") {
uint8_t count = 10;
while (count > 0) {
setLed(true);
ledBlink.set(true);
delay(50);
setLed(false);
ledBlink.set(false);
delay(50);
count = count - 1;
}

View File

@ -1,10 +1,14 @@
#include "PowerStressModule.h"
#include "Led.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerMon.h"
#include "RTC.h"
#include "Router.h"
#include "configuration.h"
#include "main.h"
#include "sleep.h"
#include "target_specific.h"
extern void printInfo();
@ -29,6 +33,10 @@ bool PowerStressModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req,
case meshtastic_PowerStressMessage_Opcode_PRINT_INFO:
printInfo();
// Now that we know we are actually doing power stress testing, go ahead and turn on all enables (so the log is fully
// detailed)
powerMon->force_enabled = true;
break;
default:
@ -44,7 +52,6 @@ bool PowerStressModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req,
int32_t PowerStressModule::runOnce()
{
if (!config.power.powermon_enables) {
// Powermon not enabled - stop using CPU/stop this thread
return disable();
@ -58,19 +65,68 @@ int32_t PowerStressModule::runOnce()
// Done with the previous command - our sleep must have finished
p.cmd = meshtastic_PowerStressMessage_Opcode_UNSET;
p.num_seconds = 0;
isRunningCommand = false;
LOG_INFO("S:PS:%u\n", p.cmd);
} else {
sleep_msec = (int32_t)(p.num_seconds * 1000);
isRunningCommand = !!sleep_msec; // if the command wants us to sleep, make sure to mark that we have something running
if (p.cmd != meshtastic_PowerStressMessage_Opcode_UNSET) {
sleep_msec = (int32_t)(p.num_seconds * 1000);
isRunningCommand = !!sleep_msec; // if the command wants us to sleep, make sure to mark that we have something running
LOG_INFO(
"S:PS:%u\n",
p.cmd); // Emit a structured log saying we are starting a powerstress state (to make it easier to parse later)
switch (p.cmd) {
case meshtastic_PowerStressMessage_Opcode_UNSET: // No need to start a new command
break;
case meshtastic_PowerStressMessage_Opcode_LED_ON:
break;
default:
LOG_ERROR("PowerStress operation %d not yet implemented!\n", p.cmd);
sleep_msec = 0; // Don't do whatever sleep was requested...
break;
switch (p.cmd) {
case meshtastic_PowerStressMessage_Opcode_LED_ON:
ledForceOn.set(true);
break;
case meshtastic_PowerStressMessage_Opcode_LED_OFF:
ledForceOn.set(false);
break;
case meshtastic_PowerStressMessage_Opcode_GPS_ON:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_GPS_OFF:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_LORA_OFF:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_LORA_RX:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_LORA_TX:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_SCREEN_OFF:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_SCREEN_ON:
// FIXME - implement
break;
case meshtastic_PowerStressMessage_Opcode_BT_OFF:
setBluetoothEnable(false);
break;
case meshtastic_PowerStressMessage_Opcode_BT_ON:
setBluetoothEnable(true);
break;
case meshtastic_PowerStressMessage_Opcode_CPU_DEEPSLEEP:
doDeepSleep(sleep_msec, true);
break;
case meshtastic_PowerStressMessage_Opcode_CPU_FULLON: {
uint32_t start_msec = millis();
while ((millis() - start_msec) < (uint32_t)sleep_msec)
; // Don't let CPU idle at all
sleep_msec = 0; // we already slept
break;
}
case meshtastic_PowerStressMessage_Opcode_CPU_IDLE:
// FIXME - implement
break;
default:
LOG_ERROR("PowerStress operation %d not yet implemented!\n", p.cmd);
sleep_msec = 0; // Don't do whatever sleep was requested...
break;
}
}
}
return sleep_msec;

View File

@ -1,4 +1,5 @@
#include "PowerFSM.h"
#include "PowerMon.h"
#include "configuration.h"
#include "esp_task_wdt.h"
#include "main.h"
@ -34,6 +35,7 @@ void setBluetoothEnable(bool enable)
nimbleBluetooth = new NimbleBluetooth();
}
if (enable && !nimbleBluetooth->isActive()) {
powerMon->setState(meshtastic_PowerMon_State_BT_On);
nimbleBluetooth->setup();
}
// For ESP32, no way to recover from bluetooth shutdown without reboot

View File

@ -9,6 +9,7 @@
#include <stdio.h>
// #include <Adafruit_USBD_Device.h>
#include "NodeDB.h"
#include "PowerMon.h"
#include "error.h"
#include "main.h"
@ -91,6 +92,8 @@ void setBluetoothEnable(bool enable)
}
if (enable) {
powerMon->setState(meshtastic_PowerMon_State_BT_On);
// If not yet set-up
if (!nrf52Bluetooth) {
LOG_DEBUG("Initializing NRF52 Bluetooth\n");
@ -105,8 +108,10 @@ void setBluetoothEnable(bool enable)
nrf52Bluetooth->resumeAdvertising();
}
// Disable (if previously set-up)
else if (nrf52Bluetooth)
else if (nrf52Bluetooth) {
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
nrf52Bluetooth->shutdown();
}
}
#else
#warning NRF52 "Bluetooth disable" workaround does not apply to builds with MESHTASTIC_EXCLUDE_BLUETOOTH

View File

@ -53,6 +53,13 @@ extern INA3221Sensor ina3221Sensor;
extern RAK9154Sensor rak9154Sensor;
#endif
#ifdef HAS_PMU
#include "XPowersAXP192.tpp"
#include "XPowersAXP2101.tpp"
#include "XPowersLibInterface.hpp"
extern XPowersLibInterface *PMU;
#endif
class Power : private concurrency::OSThread
{

View File

@ -5,6 +5,7 @@
#endif
#include "ButtonThread.h"
#include "Led.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
@ -81,26 +82,6 @@ void setCPUFast(bool on)
#endif
}
void setLed(bool ledOn)
{
if (ledOn)
powerMon->setState(meshtastic_PowerMon_State_LED_On);
else
powerMon->clearState(meshtastic_PowerMon_State_LED_On);
#ifdef LED_PIN
// toggle the led so we can get some rough sense of how often loop is pausing
digitalWrite(LED_PIN, ledOn ^ LED_STATE_ON ^ HIGH);
#endif
#ifdef HAS_PMU
if (pmu_found && PMU) {
// blink the axp led
PMU->setChargingLedMode(ledOn ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
}
#endif
}
// Perform power on init that we do on each wake from deep sleep
void initDeepSleep()
{
@ -230,6 +211,8 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false)
notifyDeepSleep.notifyObservers(NULL);
#endif
powerMon->setState(meshtastic_PowerMon_State_CPU_DeepSleep);
screen->doDeepSleep(); // datasheet says this will draw only 10ua
nodeDB->saveToDisk();
@ -257,7 +240,7 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false)
digitalWrite(PIN_3V3_EN, LOW);
#endif
#endif
setLed(false);
ledBlink.set(false);
#ifdef RESET_OLED
digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power

View File

@ -22,7 +22,6 @@ extern XPowersLibInterface *PMU;
void initDeepSleep();
void setCPUFast(bool on);
void setLed(bool ledOn);
/** return true if sleep is allowed right now */
bool doPreflightSleep();

View File

@ -6,13 +6,13 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
#define EXTERNAL_NUM_INTERRUPTS 46
#define NUM_DIGITAL_PINS 48
#define NUM_ANALOG_INPUTS 20
#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1)
#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1)
#define digitalPinHasPWM(p) (p < 46)
// Now declared in .platformio/packages/framework-arduinoespressif32/cores/esp32/Arduino.h
// #define NUM_ANALOG_INPUTS 20
// #define EXTERNAL_NUM_INTERRUPTS 46
// #define NUM_DIGITAL_PINS 48
// #define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1)
// #define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1)
// #define digitalPinHasPWM(p) (p < 46)
static const uint8_t TX = 43;
static const uint8_t RX = 44;
@ -39,4 +39,4 @@ static const uint8_t SCK = 12;
// #define PMU_IRQ (40)
#define RTC_INT (14)
#endif /* Pins_Arduino_h */
#endif /* Pins_Arduino_h */