mirror of
https://github.com/meshtastic/firmware.git
synced 2025-02-01 02:09:57 +00:00
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:
parent
06eaf2ba5d
commit
c1870f91fc
84
src/GpioLogic.cpp
Normal file
84
src/GpioLogic.cpp
Normal 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
144
src/GpioLogic.h
Normal 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
66
src/Led.cpp
Normal 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
7
src/Led.h
Normal 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;
|
@ -80,9 +80,6 @@ RAK9154Sensor rak9154Sensor;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_PMU
|
||||
#include "XPowersAXP192.tpp"
|
||||
#include "XPowersAXP2101.tpp"
|
||||
#include "XPowersLibInterface.hpp"
|
||||
XPowersLibInterface *PMU = NULL;
|
||||
#else
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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 */
|
Loading…
Reference in New Issue
Block a user