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