diff --git a/platformio.ini b/platformio.ini index f3a4db2a..56efa748 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 @@ -32,7 +32,6 @@ board_build.partitions = partition-table.csv ; note: we add src to our include search path so that lmic_project_config can override ; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Os -Wl,-Map,.pio/build/output.map - -DAXP_DEBUG_PORT=Serial -DHW_VERSION_${sysenv.COUNTRY} -DAPP_VERSION=${sysenv.APP_VERSION} -DHW_VERSION=${sysenv.HW_VERSION} @@ -69,7 +68,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 @@ -80,6 +80,7 @@ debug_init_break = tbreak setup build_flags = ${env.build_flags} -Wall -Wextra -Isrc/esp32 -mfix-esp32-psram-cache-issue -lnimble -std=c++11 -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG + -DAXP_DEBUG_PORT=Serial # Hmm - this doesn't work yet # board_build.ldscript = linker/esp32.extram.bss.ld lib_ignore = segger_rtt @@ -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 23332698..7bfa42d2 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 89e8ef81..613f20d8 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/gps/GPS.h b/src/gps/GPS.h index 46b4a5c0..409c6da9 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -25,7 +25,7 @@ void readFromRTC(); * * When new data is available it will notify observers. */ -class GPS : public Observable +class GPS { protected: bool hasValidLocation = false; // default to false, until we complete our first read @@ -48,6 +48,7 @@ class GPS : public Observable virtual ~GPS() {} + /** We will notify this observable anytime GPS state has changed meaningfully */ Observable newStatus; /** diff --git a/src/gps/NEMAGPS.cpp b/src/gps/NEMAGPS.cpp index 1645a165..a1d848ad 100644 --- a/src/gps/NEMAGPS.cpp +++ b/src/gps/NEMAGPS.cpp @@ -66,10 +66,6 @@ void NEMAGPS::loop() // expect gps pos lat=37.520825, lon=-122.309162, alt=158 DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5); - - hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0 - if (hasValidLocation) - notifyObservers(NULL); } // Notify any status instances that are observing us diff --git a/src/gps/UBloxGPS.cpp b/src/gps/UBloxGPS.cpp index 0ed506f6..c7a8501c 100644 --- a/src/gps/UBloxGPS.cpp +++ b/src/gps/UBloxGPS.cpp @@ -160,7 +160,6 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s { if (hasValidLocation) { wantNewLocation = false; - notifyObservers(NULL); // ublox.powerOff(); } } else // we didn't get a location update, go back to sleep and hope the characters show up diff --git a/src/main.cpp b/src/main.cpp index b8ee0021..3cc4097d 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/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 38babd90..f5d0ac17 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -68,7 +68,8 @@ void MeshService::init() sendOwnerPeriod.setup(); nodeDB.init(); - gpsObserver.observe(gps); + assert(gps); + gpsObserver.observe(&gps->newStatus); packetReceivedObserver.observe(&router.notifyPacketReceived); } @@ -283,9 +284,8 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies) sendToMesh(p); } -int MeshService::onGPSChanged(void *unused) +int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) { - // DEBUG_MSG("got gps notify\n"); // Update our local node info with our position (even if we don't decide to update anyone else) MeshPacket *p = router.allocForSending(); @@ -305,6 +305,8 @@ int MeshService::onGPSChanged(void *unused) pos.battery_level = powerStatus->getBatteryChargePercent(); updateBatteryLevel(pos.battery_level); + // DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level); + // We limit our GPS broadcasts to a max rate static uint32_t lastGpsSend; uint32_t now = timing::millis(); diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index f6e688e1..7e881077 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -4,6 +4,7 @@ #include #include +#include "GPSStatus.h" #include "MemoryPool.h" #include "MeshRadio.h" #include "MeshTypes.h" @@ -17,7 +18,8 @@ */ class MeshService { - CallbackObserver gpsObserver = CallbackObserver(this, &MeshService::onGPSChanged); + CallbackObserver gpsObserver = + CallbackObserver(this, &MeshService::onGPSChanged); CallbackObserver packetReceivedObserver = CallbackObserver(this, &MeshService::handleFromRadio); @@ -85,7 +87,7 @@ class MeshService /// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh /// returns 0 to allow futher processing - int onGPSChanged(void *arg); + int onGPSChanged(const meshtastic::GPSStatus *arg); /// Handle a packet that just arrived from the radio. This method does _not_ free the provided packet. If it needs /// to keep the packet around it makes a copy diff --git a/src/power.h b/src/power.h index dd6b8ce8..a779089a 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