Compare commits

...
Author SHA1 Message Date
Jonathan Bennett 0ff7bc322f Merge branch 'develop' into power-cleanup 2026-04-24 10:50:14 -05:00
Jonathan Bennett 7adfc3f992 Remove incorrect LED_STATE_ON definition for t-beam-s3 (#10280)
Fixes #9912  and #10170
2026-04-24 15:17:33 +10:00
DmitryandCopilot Autofix powered by AI ba9cadc14d Fix INA226 detection for non-TI compatible chip (Silergy) (#10247)
* Fix INA226 detection for non-TI compatible chip (Silergy)

* Removed extra I2C transaction + 20ms delay on every scan of address 0x40 (including real SHT2x sensors).

Changes suggested by Copilot

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Apply formatting (trunk fmt)

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-24 13:09:37 +10:00
Jonathan Bennett 2815c9abc0 Re-add isVbusIn() override (Thanks CoPilot!) 2026-04-23 21:13:23 -05:00
Jonathan Bennett 761ac4e08a Drop the LongPressIrq detection 2026-04-23 21:12:59 -05:00
Jonathan Bennett f8368f5bd2 Enable PMU IRQ for t-beam-s3, and power button as cancel. 2026-04-23 20:30:25 -05:00
Jonathan Bennett f60d329574 Merge branch 'develop' into power-cleanup 2026-04-23 19:58:13 -05:00
Jonathan BennettandCopilot 897c591ffe Update src/power.h marking pmu_irq volatile
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-23 19:56:22 -05:00
EmanueleandJonathan Bennett 924411de59 T-Watch S3 Power button managment (#9855)
* PMU interrupt pin defined in t-watch s3

* Implement button control on T-Watch S3

Added interrupt handling for the Power/Corona button on T-Watch S3, I use it to control screen state.

* Reducing labels

* Reducing labels

* Updated the comment

* ISR is now IRAM-safe

Updated interrupt management not to cause random crashes.

* Trunk

* Simplify and use INPUT_BROKER_CANCEL

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2026-04-23 19:53:59 -05:00
Jonathan Bennett e5caf2d52f Leftover fix from local merge 2026-04-23 14:59:25 -05:00
Jonathan Bennett 53413ee502 Merge branch 'develop' into power-cleanup 2026-04-23 14:57:32 -05:00
Jonathan Bennett 04b5f14969 Begin power.cpp/h cleanup 2026-04-23 14:45:20 -05:00
5 changed files with 133 additions and 130 deletions
+26 -114
View File
@@ -68,11 +68,7 @@
#define ETH ETH2
#endif // HAS_ETHERNET
#endif
#ifndef DELAY_FOREVER
#define DELAY_FOREVER portMAX_DELAY
#endif
#endif // MQTT
#if defined(BATTERY_PIN) && defined(ARCH_ESP32)
@@ -94,34 +90,6 @@ static const adc_atten_t atten = ADC_ATTENUATION;
#endif
#endif // BATTERY_PIN && ARCH_ESP32
#ifdef EXT_PWR_DETECT
#ifndef EXT_PWR_DETECT_MODE
#define EXT_PWR_DETECT_MODE INPUT
// If using internal pull resistors, we can infer EXT_PWR_DETECT_VALUE
#elif EXT_PWR_DETECT_MODE == INPUT_PULLUP
#define EXT_PWR_DETECT_VALUE LOW
#elif EXT_PWR_DETECT_MODE == INPUT_PULLDOWN
#define EXT_PWR_DETECT_VALUE HIGH
#endif
#ifndef EXT_PWR_DETECT_VALUE
#define EXT_PWR_DETECT_VALUE HIGH
#endif
#endif
#ifdef EXT_CHRG_DETECT
#ifndef EXT_CHRG_DETECT_MODE
#define EXT_CHRG_DETECT_MODE INPUT
// If using internal pull resistors, we can infer EXT_CHRG_DETECT_VALUE
#elif EXT_CHRG_DETECT_MODE == INPUT_PULLUP
#define EXT_CHRG_DETECT_VALUE LOW
#elif EXT_CHRG_DETECT_MODE == INPUT_PULLDOWN
#define EXT_CHRG_DETECT_VALUE HIGH
#endif
#ifndef EXT_CHRG_DETECT_VALUE
#define EXT_CHRG_DETECT_VALUE HIGH
#endif
#endif
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#if __has_include(<Adafruit_INA219.h>)
INA219Sensor ina219Sensor;
@@ -160,7 +128,7 @@ MAX17048Sensor max17048Sensor;
NullSensor max17048Sensor;
#endif
#endif
#endif
#endif // !MESHTASTIC_EXCLUDE_I2C
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && HAS_RAKPROT
RAK9154Sensor rak9154Sensor;
@@ -178,46 +146,12 @@ XPowersPPM *PPM = NULL;
#ifdef HAS_PMU
XPowersLibInterface *PMU = NULL;
#else
// Copy of the base class defined in axp20x.h.
// I'd rather not include axp20x.h as it brings Wire dependency.
class HasBatteryLevel
{
public:
/**
* Battery state of charge, from 0 to 100 or -1 for unknown
*/
virtual int getBatteryPercent() { return -1; }
/**
* The raw voltage of the battery or NAN if unknown
*/
virtual uint16_t getBattVoltage() { return 0; }
/**
* return true if there is a battery installed in this unit
*/
virtual bool isBatteryConnect() { return false; }
virtual bool isVbusIn() { return false; }
virtual bool isCharging() { return false; }
};
#endif
bool pmu_irq = false;
Power *power;
using namespace meshtastic;
// NRF52 has AREF_VOLTAGE defined in architecture.h but
// make sure it's included. If something is wrong with NRF52
// definition - compilation will fail on missing definition
#if !defined(AREF_VOLTAGE) && !defined(ARCH_NRF52)
#define AREF_VOLTAGE 3.3
#endif
/**
* If this board has a battery level sensor, set this to a valid implementation
*/
@@ -260,7 +194,7 @@ static void battery_adcDisable()
#endif
}
#endif
#endif // BATTERY_PIN
/**
* A simple battery level sensor that assumes the battery voltage is attached
@@ -319,7 +253,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
/**
* The raw voltage of the batteryin millivolts or NAN if unknown
* The raw voltage of the battery in millivolts or NAN if unknown
*/
virtual uint16_t getBattVoltage() override
{
@@ -336,16 +270,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
#endif
#ifndef ADC_MULTIPLIER
#define ADC_MULTIPLIER 2.0
#endif
#ifndef BATTERY_SENSE_SAMPLES
#define BATTERY_SENSE_SAMPLES \
15 // Set the number of samples, it has an effect of increasing sensitivity in
// complex electromagnetic environment.
#endif
#ifdef BATTERY_PIN
// Override variant or default ADC_MULTIPLIER if we have the override pref
float operativeAdcMultiplier =
@@ -1000,6 +924,14 @@ int32_t Power::runOnce()
powerFSM.trigger(EVENT_POWER_CONNECTED);
}
#ifdef PMU_POWER_BUTTON_IS_CANCEL
// cancel action also turns the screen on and off.
if (PMU->isPekeyShortPressIrq()) {
LOG_INFO("Input: Corona Button Click");
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_CANCEL, .kbchar = 0, .touchX = 0, .touchY = 0};
inputBroker->injectInputEvent(&event);
}
#endif
/*
Other things we could check if we cared...
@@ -1016,13 +948,6 @@ int32_t Power::runOnce()
LOG_DEBUG("Battery removed");
}
*/
#ifndef T_WATCH_S3 // FIXME - why is this triggering on the T-Watch S3?
if (PMU->isPekeyLongPressIrq()) {
LOG_DEBUG("PEK long button press");
if (screen)
screen->setOn(false);
}
#endif
PMU->clearIrqStatus();
}
@@ -1091,8 +1016,8 @@ void Power::attachPowerInterrupts()
if (PMU) {
attachInterrupt(
PMU_IRQ,
[] {
pmu_irq = true;
[]() {
power->pmu_irq = true;
power->setIntervalFromNow(0);
runASAP = true;
},
@@ -1394,19 +1319,16 @@ bool Power::axpChipInit()
uint64_t pmuIrqMask = 0;
if (PMU->getChipModel() == XPOWERS_AXP192) {
pmuIrqMask = XPOWERS_AXP192_VBUS_INSERT_IRQ | XPOWERS_AXP192_BAT_INSERT_IRQ | XPOWERS_AXP192_PKEY_SHORT_IRQ;
pmuIrqMask = XPOWERS_AXP192_VBUS_INSERT_IRQ | XPOWERS_AXP192_VBUS_REMOVE_IRQ | XPOWERS_AXP192_PKEY_SHORT_IRQ;
} else if (PMU->getChipModel() == XPOWERS_AXP2101) {
pmuIrqMask = XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_PKEY_SHORT_IRQ;
pmuIrqMask = XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | XPOWERS_AXP2101_PKEY_SHORT_IRQ;
}
pinMode(PMU_IRQ, INPUT);
// we do not look for AXPXXX_CHARGING_FINISHED_IRQ & AXPXXX_CHARGING_IRQ
// because it occurs repeatedly while there is no battery also it could cause
// inadvertent waking from light sleep just because the battery filled we
// don't look for AXPXXX_BATT_REMOVED_IRQ because it occurs repeatedly while
// no battery installed we don't look at AXPXXX_VBUS_REMOVED_IRQ because we
// don't have anything hooked to vbus
// We wake on IRQ, so only enable the IRQs that we care about.
// we want USB plug and unplug to update the screen and LED status,
// and short press on the power button to trigger the "cancel" action in the UI (which also turns the screen on and off).
PMU->enableIRQ(pmuIrqMask);
PMU->clearIrqStatus();
@@ -1714,7 +1636,7 @@ bool Power::lipoChargerInit()
return true;
}
#else
#else // HAS_PPM
/**
* The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel
*/
@@ -1722,7 +1644,7 @@ bool Power::lipoChargerInit()
{
return false;
}
#endif
#endif // HAS_PPM
#ifdef HELTEC_MESH_SOLAR
#include "meshSolarApp.h"
@@ -1784,7 +1706,7 @@ bool Power::meshSolarInit()
return true;
}
#else
#else // HELTEC_MESH_SOLAR
/**
* The meshSolar battery level sensor is unavailable - default to
* AnalogBatteryLevel
@@ -1793,7 +1715,7 @@ bool Power::meshSolarInit()
{
return false;
}
#endif
#endif // HELTEC_MESH_SOLAR
#ifdef HAS_SERIAL_BATTERY_LEVEL
#include <SoftwareSerial.h>
@@ -1801,7 +1723,7 @@ bool Power::meshSolarInit()
/**
* SerialBatteryLevel class for pulling battery information from a secondary MCU over serial.
*/
class SerialBatteryLevel : public HasBatteryLevel
class SerialBatteryLevel : public AnalogBatteryLevel
{
public:
@@ -1878,16 +1800,6 @@ class SerialBatteryLevel : public HasBatteryLevel
return false;
}
virtual bool isCharging() override
{
#ifdef EXT_CHRG_DETECT
return digitalRead(EXT_CHRG_DETECT) == EXT_CHRG_DETECT_VALUE;
#endif
// by default, we check the battery voltage only
return isVbusIn();
}
private:
SoftwareSerial BatterySerial = SoftwareSerial(SERIAL_BATTERY_RX, SERIAL_BATTERY_TX);
uint8_t Data[6] = {0};
@@ -1917,7 +1829,7 @@ bool Power::serialBatteryInit()
return true;
}
#else
#else // HAS_SERIAL_BATTERY_LEVEL
/**
* If this device has no serial battery level sensor, don't try to use it.
*/
@@ -1925,4 +1837,4 @@ bool Power::serialBatteryInit()
{
return false;
}
#endif
#endif // HAS_SERIAL_BATTERY_LEVEL
+25 -10
View File
@@ -415,30 +415,45 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
#if !defined(M5STACK_UNITC6L)
case INA_ADDR: // Same as SHT2X
case INA_ADDR_ALTERNATE:
case INA_ADDR_WAVESHARE_UPS:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2);
LOG_DEBUG("Register MFG_UID: 0x%x", registerValue);
if (registerValue == 0x5449) {
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFF), 2);
LOG_DEBUG("Register DIE_UID: 0x%x", registerValue);
case INA_ADDR_WAVESHARE_UPS: {
uint16_t mfg = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2);
if (registerValue == 0x2260) {
LOG_DEBUG("Register MFG_UID: 0x%x", mfg);
// Only read DIE_UID for vendors we recognize as INA-compatible to avoid
// an extra I2C transaction + delay on other devices sharing this address.
if (mfg == 0x5449 || mfg == 0x190F) {
uint16_t die = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFF), 2);
LOG_DEBUG("Register DIE_UID: 0x%x", die);
// TI INA226 or fully compatible clones (e.g. TPA626)
if (mfg == 0x5449 && die == 0x2260) {
logFoundDevice("INA226", (uint8_t)addr.address);
type = INA226;
} else {
}
// Silergy SQ52201 (INA226-compatible with different IDs)
else if (mfg == 0x190F && die == 0x0000) {
logFoundDevice("INA226 (SQ52201)", (uint8_t)addr.address);
type = INA226;
}
// TI INA260
else if (mfg == 0x5449) {
logFoundDevice("INA260", (uint8_t)addr.address);
type = INA260;
}
}
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
} else if (detectSHT21SerialNumber(i2cBus, (uint8_t)addr.address)) {
if (type == NONE && detectSHT21SerialNumber(i2cBus, (uint8_t)addr.address)) {
logFoundDevice("SHTXX (SHT2X)", (uint8_t)addr.address);
type = SHTXX;
}
#endif
} else { // Assume INA219 if none of the above ones are found
else { // Assume INA219 if none of the above ones are found
logFoundDevice("INA219", (uint8_t)addr.address);
type = INA219;
}
break;
}
case INA3221_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2);
LOG_DEBUG("Register MFG_UID FE: 0x%x", registerValue);
+75
View File
@@ -28,6 +28,54 @@
#define NUM_CELLS 1
#endif
// Set the number of samples, it has an effect of increasing sensitivity in complex electromagnetic environment.
#ifndef BATTERY_SENSE_SAMPLES
#define BATTERY_SENSE_SAMPLES 15
#endif
#ifndef ADC_MULTIPLIER
#define ADC_MULTIPLIER 2.0
#endif
#ifdef EXT_PWR_DETECT
#ifndef EXT_PWR_DETECT_MODE
#define EXT_PWR_DETECT_MODE INPUT
// If using internal pull resistors, we can infer EXT_PWR_DETECT_VALUE
#elif EXT_PWR_DETECT_MODE == INPUT_PULLUP
#define EXT_PWR_DETECT_VALUE LOW
#elif EXT_PWR_DETECT_MODE == INPUT_PULLDOWN
#define EXT_PWR_DETECT_VALUE HIGH
#endif
#ifndef EXT_PWR_DETECT_VALUE
#define EXT_PWR_DETECT_VALUE HIGH
#endif
#endif
#ifdef EXT_CHRG_DETECT
#ifndef EXT_CHRG_DETECT_MODE
#define EXT_CHRG_DETECT_MODE INPUT
// If using internal pull resistors, we can infer EXT_CHRG_DETECT_VALUE
#elif EXT_CHRG_DETECT_MODE == INPUT_PULLUP
#define EXT_CHRG_DETECT_VALUE LOW
#elif EXT_CHRG_DETECT_MODE == INPUT_PULLDOWN
#define EXT_CHRG_DETECT_VALUE HIGH
#endif
#ifndef EXT_CHRG_DETECT_VALUE
#define EXT_CHRG_DETECT_VALUE HIGH
#endif
#endif
#ifndef DELAY_FOREVER
#define DELAY_FOREVER portMAX_DELAY
#endif
// NRF52 has AREF_VOLTAGE defined in architecture.h but
// make sure it's included. If something is wrong with NRF52
// definition - compilation will fail on missing definition
#if !defined(AREF_VOLTAGE) && !defined(ARCH_NRF52)
#define AREF_VOLTAGE 3.3
#endif
#ifdef BAT_MEASURE_ADC_UNIT
extern RTC_NOINIT_ATTR uint64_t RTC_reg_b;
#include "soc/sens_reg.h" // needed for adc pin reset
@@ -86,6 +134,31 @@ extern RAK9154Sensor rak9154Sensor;
extern XPowersLibInterface *PMU;
#endif
#ifndef HAS_PMU
// Copy of the base class defined in axp20x.h, to prevent an automatic wire.h dependency
class HasBatteryLevel
{
public:
/**
* Battery state of charge, from 0 to 100 or -1 for unknown
*/
virtual int getBatteryPercent() { return -1; }
/**
* The raw voltage of the battery or NAN if unknown
*/
virtual uint16_t getBattVoltage() { return 0; }
/**
* return true if there is a battery installed in this unit
*/
virtual bool isBatteryConnect() { return false; }
virtual bool isVbusIn() { return false; }
virtual bool isCharging() { return false; }
};
#endif
class Power : public concurrency::OSThread
{
@@ -110,6 +183,8 @@ class Power : public concurrency::OSThread
void attachPowerInterrupts();
void detachPowerInterrupts();
volatile bool pmu_irq = false;
protected:
meshtastic::PowerStatus *statusHandler;
+3
View File
@@ -42,6 +42,7 @@
#define DAC_I2S_MCLK -1
#define HAS_AXP2101
#define PMU_POWER_BUTTON_IS_CANCEL // maps a short click of the power button to a cancel action (turning off the screen)
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
@@ -60,6 +61,8 @@
#define BUTTON_PIN 0 // only for Plus version
#define PMU_IRQ 21 // Interrupt pin for the PMU
#define USE_SX1262
#define USE_SX1268
+4 -6
View File
@@ -9,8 +9,6 @@
#define BUTTON_PIN 0 // The middle button GPIO on the T-Beam S3
// #define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module.
#define LED_STATE_ON 0 // State when LED is lit
// TTGO uses a common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if
// not found then probe for SX1262
#define USE_SX1262
@@ -48,9 +46,9 @@
#define LR11X0_DIO_AS_RF_SWITCH
#endif
// Leave undefined to disable our PMU IRQ handler. DO NOT ENABLE THIS because the pmuirq can cause sperious interrupts
// and waking from light sleep
// #define PMU_IRQ 40
// Voiding warrenties, we're gonna try the IRQ
#define PMU_IRQ 40
#define PMU_POWER_BUTTON_IS_CANCEL // maps a short click of the power button to a cancel action (turning off the screen)
#define HAS_AXP2101
// PCF8563 RTC Module
@@ -76,4 +74,4 @@
// has 32768 Hz crystal
#define HAS_32768HZ 1
#define USE_SH1106
#define USE_SH1106