From 6a402b13fa5afea1924af9b01a241ff4638da361 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 12 Aug 2020 17:03:36 -0700 Subject: [PATCH] Add battery sensing (mostly) for TBEAM0.7 However, disabled until someone with suitable hardware can test and report back. @slavino and @tschundler would you be willing to try it with your boards? You'll need to uncomment the following line in configuration.h // #define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage --- platformio.ini | 6 +- src/Power.cpp | 154 ++++++++++++++++++++++++++++++++------------ src/configuration.h | 4 +- src/main.cpp | 2 - src/power.h | 22 +++---- 5 files changed, 131 insertions(+), 57 deletions(-) diff --git a/platformio.ini b/platformio.ini index f3a4db2a6..1b11e1cbc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,7 +9,7 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = tbeam +default_envs = tbeam [common] ; common is not currently used @@ -69,7 +69,8 @@ lib_deps = https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git https://github.com/meshtastic/RadioLib.git#d6b12f7eb0a06bd2414c79b437b25d377e3f603f https://github.com/meshtastic/TinyGPSPlus.git - + https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460 + ; Common settings for ESP targes, mixin with extends = esp32_base [esp32_base] platform = espressif32 @@ -99,7 +100,6 @@ extends = esp32_base board = ttgo-t-beam lib_deps = ${env.lib_deps} - https://github.com/meshtastic/AXP202X_Library.git build_flags = ${esp32_base.build_flags} -D TBEAM_V10 diff --git a/src/Power.cpp b/src/Power.cpp index 23332698b..7bfa42d29 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -1,27 +1,92 @@ #include "power.h" #include "PowerFSM.h" #include "main.h" -#include "utils.h" #include "sleep.h" - -#ifdef TBEAM_V10 +#include "utils.h" // FIXME. nasty hack cleanup how we load axp192 #undef AXP192_SLAVE_ADDRESS #include "axp20x.h" + +#ifdef TBEAM_V10 AXP20X_Class axp; +#endif + bool pmu_irq = false; Power *power; -bool Power::setup() +/** + * If this board has a battery level sensor, set this to a valid implementation + */ +static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level sensor + +/** + * A simple battery level sensor that assumes the battery voltage is attached via a voltage-divider to an analog input + */ +class AnalogBatteryLevel : public HasBatteryLevel { + /** + * Battery state of charge, from 0 to 100 or -1 for unknown + * + * FIXME - use a lipo lookup table, the current % full is super wrong + */ + virtual int getBattPercentage() + { + float v = getBattVoltage(); - axp192Init(); - concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device - setPeriod(1); + if (v < 2.1) + return -1; - return axp192_found; + return 100 * (getBattVoltage() - 3.27) / (4.2 - 3.27); + } + + /** + * The raw voltage of the battery or NAN if unknown + */ + virtual float getBattVoltage() + { + return +#ifdef BATTERY_PIN + analogRead(BATTERY_PIN) * 2.0 * (3.3 / 1024.0); +#else + NAN; +#endif + } + + /** + * return true if there is a battery installed in this unit + */ + virtual bool isBatteryConnect() { return true; } +} analogLevel; + +bool Power::analogInit() +{ +#ifdef BATTERY_PIN + DEBUG_MSG("Using analog input for battery level\n"); + adcAttachPin(BATTERY_PIN); + // adcStart(BATTERY_PIN); + analogReadResolution(10); // Default of 12 is not very linear. Recommended to use 10 or 11 depending on needed resolution. + batteryLevel = &analogLevel; + return true; +#else + return false; +#endif +} + +bool Power::setup() +{ + bool found = axp192Init(); + + if (!found) { + found = analogInit(); + } + if (found) { + concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device + setPeriod(1); + } + + return found; } /// Reads power status to powerStatus singleton. @@ -29,42 +94,45 @@ bool Power::setup() // TODO(girts): move this and other axp stuff to power.h/power.cpp. void Power::readPowerStatus() { - bool hasBattery = axp.isBatteryConnect(); - int batteryVoltageMv = 0; - uint8_t batteryChargePercent = 0; - if (hasBattery) { - batteryVoltageMv = axp.getBattVoltage(); - // If the AXP192 returns a valid battery percentage, use it - if (axp.getBattPercentage() >= 0) { - batteryChargePercent = axp.getBattPercentage(); - } else { - // If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error - // In that case, we compute an estimate of the charge percent based on maximum and minimum voltages defined in power.h - batteryChargePercent = clamp((int)(((batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), 0, 100); + if (batteryLevel) { + bool hasBattery = batteryLevel->isBatteryConnect(); + int batteryVoltageMv = 0; + uint8_t batteryChargePercent = 0; + if (hasBattery) { + batteryVoltageMv = batteryLevel->getBattVoltage(); + // If the AXP192 returns a valid battery percentage, use it + if (batteryLevel->getBattPercentage() >= 0) { + batteryChargePercent = batteryLevel->getBattPercentage(); + } else { + // If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error + // In that case, we compute an estimate of the charge percent based on maximum and minimum voltages defined in + // power.h + batteryChargePercent = + clamp((int)(((batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), + 0, 100); + } } + + // Notify any status instances that are observing us + const meshtastic::PowerStatus powerStatus = meshtastic::PowerStatus( + hasBattery, batteryLevel->isVBUSPlug(), batteryLevel->isChargeing(), batteryVoltageMv, batteryChargePercent); + newStatus.notifyObservers(&powerStatus); + + // If we have a battery at all and it is less than 10% full, force deep sleep + if (powerStatus.getHasBattery() && !powerStatus.getHasUSB() && batteryLevel->getBattVoltage() < MIN_BAT_MILLIVOLTS) + powerFSM.trigger(EVENT_LOW_BATTERY); } - - // Notify any status instances that are observing us - const meshtastic::PowerStatus powerStatus = meshtastic::PowerStatus(hasBattery, axp.isVBUSPlug(), axp.isChargeing(), batteryVoltageMv, batteryChargePercent); - newStatus.notifyObservers(&powerStatus); - - // If we have a battery at all and it is less than 10% full, force deep sleep - if (powerStatus.getHasBattery() && !powerStatus.getHasUSB() && - axp.getBattVoltage() < MIN_BAT_MILLIVOLTS) - powerFSM.trigger(EVENT_LOW_BATTERY); } -void Power::doTask() +void Power::doTask() { readPowerStatus(); - + // Only read once every 20 seconds once the power status for the app has been initialized - if(statusHandler && statusHandler->isInitialized()) + if (statusHandler && statusHandler->isInitialized()) setPeriod(1000 * 20); } -#endif // TBEAM_V10 -#ifdef AXP192_SLAVE_ADDRESS /** * Init the power manager chip * @@ -74,10 +142,13 @@ void Power::doTask() 30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS */ -void Power::axp192Init() +bool Power::axp192Init() { +#ifdef TBEAM_V10 if (axp192_found) { if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) { + batteryLevel = &axp; + DEBUG_MSG("AXP192 Begin PASS\n"); // axp.setChgLEDMode(LED_BLINK_4HZ); @@ -135,12 +206,16 @@ void Power::axp192Init() } else { DEBUG_MSG("AXP192 not found\n"); } -} + + return axp192_found; +#else + return false; #endif +} -void Power::loop() + +void Power::loop() { - #ifdef PMU_IRQ if (pmu_irq) { pmu_irq = false; @@ -174,6 +249,5 @@ void Power::loop() axp.clearIRQ(); } -#endif // T_BEAM_V10 - +#endif } diff --git a/src/configuration.h b/src/configuration.h index 89e8ef81d..613f20d87 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -167,7 +167,6 @@ along with this program. If not, see . // Leave undefined to disable our PMU IRQ handler #define PMU_IRQ 35 - #define AXP192_SLAVE_ADDRESS 0x34 #elif defined(TBEAM_V07) @@ -180,6 +179,7 @@ along with this program. If not, see . #define I2C_SCL 22 #define BUTTON_PIN 39 +// #define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage #ifndef USE_JTAG #define RF95_RESET 23 @@ -278,6 +278,8 @@ along with this program. If not, see . #define GPS_RX_PIN 36 #define GPS_TX_PIN 39 +// #define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage + #define I2C_SDA 21 // I2C pins for this board #define I2C_SCL 22 diff --git a/src/main.cpp b/src/main.cpp index b8ee00219..3cc4097d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -210,13 +210,11 @@ void setup() esp32Setup(); #endif -#ifdef TBEAM_V10 // Currently only the tbeam has a PMU power = new Power(); power->setup(); power->setStatusHandler(powerStatus); powerStatus->observe(&power->newStatus); -#endif #ifdef NRF52_SERIES nrf52Setup(); diff --git a/src/power.h b/src/power.h index dd6b8ce87..a779089ad 100644 --- a/src/power.h +++ b/src/power.h @@ -1,6 +1,6 @@ #pragma once -#include "concurrency/PeriodicTask.h" #include "PowerStatus.h" +#include "concurrency/PeriodicTask.h" /** * Per @spattinson @@ -18,23 +18,23 @@ class Power : public concurrency::PeriodicTask { - public: - + public: Observable newStatus; void readPowerStatus(); void loop(); virtual bool setup(); virtual void doTask(); - void setStatusHandler(meshtastic::PowerStatus *handler) - { - statusHandler = handler; - } - - protected: - meshtastic::PowerStatus *statusHandler; - virtual void axp192Init(); + void setStatusHandler(meshtastic::PowerStatus *handler) { statusHandler = handler; } + protected: + meshtastic::PowerStatus *statusHandler; + + /// Setup a axp192, return true if found + bool axp192Init(); + + /// Setup a simple ADC input based battery sensor + bool analogInit(); }; extern Power *power; \ No newline at end of file