Skip to content

Commit

Permalink
Finish powermon/powerstress (#4230)
Browse files Browse the repository at this point in the history
* 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 #4135

* powermon WIP (for #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 #4136

* powermon gps support
#4136

* Add CPU deep and light sleep powermon states
#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
#4230 (review)

* Support driving multiple output gpios from one input.

While investigating #4230 (review)
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 <[email protected]>
  • Loading branch information
geeksville and thebentern authored Aug 6, 2024
1 parent 06eaf2b commit c1870f9
Show file tree
Hide file tree
Showing 18 changed files with 422 additions and 69 deletions.
84 changes: 84 additions & 0 deletions src/GpioLogic.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 144 additions & 0 deletions src/GpioLogic.h
Original file line number Diff line number Diff line change
@@ -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 changes: 66 additions & 0 deletions src/Led.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 7 additions & 0 deletions src/Led.h
Original file line number Diff line number Diff line change
@@ -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;
3 changes: 0 additions & 3 deletions src/Power.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,6 @@ RAK9154Sensor rak9154Sensor;
#endif

#ifdef HAS_PMU
#include "XPowersAXP192.tpp"
#include "XPowersAXP2101.tpp"
#include "XPowersLibInterface.hpp"
XPowersLibInterface *PMU = NULL;
#else

Expand Down
Loading

0 comments on commit c1870f9

Please sign in to comment.