diff --git a/TODO.md b/TODO.md index dd4dea45..4c1f5684 100644 --- a/TODO.md +++ b/TODO.md @@ -33,8 +33,17 @@ * How do avalanche beacons work? Could this do that as well? possibly by using beacon mode feature of the RF95? * use std::map in node db +# Low power consumption tasks + +* stop using loop() instead use a job queue and let cpu sleep +* move lora rx/tx to own thread and block on IO +* measure power consumption and calculate battery life assuming no deep sleep +* do lowest sleep level possible where BT still works during normal sleeping, make sure cpu stays in that mode unless lora rx packet happens, bt rx packet happens or button press happens +* optionally do lora messaging only during special scheduled intervals (unless nodes are told to go to low latency mode), then deep sleep except during those intervals - before implementing calculate what battery life would be with this feature + # Pre-beta priority +* use variable length arduino Strings in protobufs (instead of current fixed buffers) * don't even power on bluetooth until we have some data to send to the android phone. Most of the time we should be sleeping in a lowpower "listening for lora" only mode. Once we have some packets for the phone, then power on bluetooth until the phone pulls those packets. Ever so often power on bluetooth just so we can see if the phone wants to send some packets. Possibly might need ULP processor to help with this wake process. * do hibernation mode to get power draw down to 2.5uA https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/ diff --git a/platformio.ini b/platformio.ini index 0f694ad8..509c47ba 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,7 +20,7 @@ framework = arduino board_build.partitions = partition-table.csv ; note: we add src to our include search path so that lmic_project_config can override -build_flags = -Wall -Wextra -Wno-missing-field-initializers -Isrc -O3 -Wl,-Map,.pio/build/esp32/output.map -DAXP_DEBUG_PORT=Serial +build_flags = -Wall -Wextra -Wno-missing-field-initializers -Isrc -Os -Wl,-Map,.pio/build/esp32/output.map -DAXP_DEBUG_PORT=Serial ; not needed included in ttgo-t-beam board file ; also to use PSRAM https://docs.platformio.org/en/latest/platforms/espressif32.html#external-ram-psram diff --git a/src/GPS.cpp b/src/GPS.cpp new file mode 100644 index 00000000..31e26ff7 --- /dev/null +++ b/src/GPS.cpp @@ -0,0 +1,42 @@ + +#include "GPS.h" + +HardwareSerial _serial_gps(GPS_SERIAL_NUM); + +GPS gps; + +GPS::GPS() : PeriodicTask(30 * 1000) +{ +} + +void GPS::setup() +{ +#ifdef GPS_RX_PIN + _serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); +#endif +} + +void GPS::loop() +{ + PeriodicTask::loop(); + +#ifdef GPX_RX_PIN + while (_serial_gps.available()) + { + _gps.encode(_serial_gps.read()); + } +#endif +} + +void GPS::doTask() +{ +} + +String GPS::getTime() +{ + static char t[12]; // used to sprintf for Serial output + + snprintf(t, sizeof(t), "%02d:%02d:%02d", time.hour(), time.minute(), time.second()); + return t; +} + diff --git a/src/GPS.h b/src/GPS.h new file mode 100644 index 00000000..b82cc714 --- /dev/null +++ b/src/GPS.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "PeriodicTask.h" +#include "Observer.h" + +/** + * A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading) + * + * When new data is available it will notify observers. + */ +class GPS : public PeriodicTask, public Observable, public TinyGPSPlus +{ +public: + GPS(); + + String getTime(); + + void setup(); + + virtual void loop(); + + virtual void doTask(); +}; + +extern GPS gps; diff --git a/src/Observer.cpp b/src/Observer.cpp new file mode 100644 index 00000000..468f5ade --- /dev/null +++ b/src/Observer.cpp @@ -0,0 +1,13 @@ +#include "Observer.h" + +Observer::~Observer() +{ + if (observed) + observed->removeObserver(this); + observed = NULL; +} + +void Observer::observe(Observable *o) +{ + o->addObserver(this); +} \ No newline at end of file diff --git a/src/Observer.h b/src/Observer.h new file mode 100644 index 00000000..5490be5a --- /dev/null +++ b/src/Observer.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include + +class Observable; + +class Observer +{ + Observable *observed; + +public: + Observer() : observed(NULL) {} + + virtual ~Observer(); + + void observe(Observable *o); + + virtual void onNotify(Observable *o) = 0; +}; + +class Observable +{ + std::list observers; + +public: + void notifyObservers() + { + for (std::list::const_iterator iterator = observers.begin(); iterator != observers.end(); ++iterator) + { + (*iterator)->onNotify(this); + } + } + + void addObserver(Observer *o) + { + observers.push_back(o); + } + + void removeObserver(Observer *o) + { + observers.remove(o); + } +}; + diff --git a/src/PeriodicTask.h b/src/PeriodicTask.h new file mode 100644 index 00000000..5c92f7c7 --- /dev/null +++ b/src/PeriodicTask.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include "configuration.h" + +class PeriodicTask +{ + /// we use prevMsec rather than nextMsec because it is easier to handle the uint32 rollover in that case, also changes in periodMsec take effect immediately + uint32_t prevMsec; + +public: + uint32_t periodMsec; + + virtual ~PeriodicTask() {} + + PeriodicTask(uint32_t period) : periodMsec(period) + { + prevMsec = millis(); + } + + /// call this from loop + virtual void loop() + { + uint32_t now = millis(); + if (now > (prevMsec + periodMsec)) + { + // FIXME, this lets period slightly drift based on scheduling - not sure if that is always good + prevMsec = now; + + DEBUG_MSG("Calling periodic task\n"); + doTask(); + } + } + + virtual void doTask() = 0; +}; diff --git a/src/gps.ino b/src/gps.ino deleted file mode 100644 index 02b6d55c..00000000 --- a/src/gps.ino +++ /dev/null @@ -1,70 +0,0 @@ -/* - - GPS module - - Copyright (C) 2018 by Xose PĂ©rez - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -*/ - -#include - -uint32_t LatitudeBinary; -uint32_t LongitudeBinary; -uint16_t altitudeGps; -uint8_t hdopGps; -uint8_t sats; -char t[32]; // used to sprintf for Serial output - -TinyGPSPlus _gps; -HardwareSerial _serial_gps(GPS_SERIAL_NUM); - -void gps_time(char * buffer, uint8_t size) { - snprintf(buffer, size, "%02d:%02d:%02d", _gps.time.hour(), _gps.time.minute(), _gps.time.second()); -} - -float gps_latitude() { - return _gps.location.lat(); -} - -float gps_longitude() { - return _gps.location.lng(); -} - -float gps_altitude() { - return _gps.altitude.meters(); -} - -float gps_hdop() { - return _gps.hdop.hdop(); -} - -uint8_t gps_sats() { - return _gps.satellites.value(); -} - -void gps_setup() { - #ifdef GPS_RX_PIN - _serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); - #endif -} - -static void gps_loop() { - #ifdef GPX_RX_PIN - while (_serial_gps.available()) { - _gps.encode(_serial_gps.read()); - } - #endif -} diff --git a/src/main.ino b/src/main.ino index 321c645c..8d45ee6b 100644 --- a/src/main.ino +++ b/src/main.ino @@ -29,6 +29,7 @@ #include "BluetoothUtil.h" #include "MeshBluetoothService.h" #include "MeshService.h" +#include "GPS.h" #ifdef T_BEAM_V10 #include "axp20x.h" @@ -348,7 +349,7 @@ void setup() screen_setup(); // Init GPS - gps_setup(); + gps.setup(); // Show logo on first boot after removing battery //if (bootCount == 0) { @@ -371,7 +372,7 @@ void setup() void loop() { - gps_loop(); + gps.loop(); screen_loop(); service.loop(); loopBLE(); diff --git a/src/screen.ino b/src/screen.ino index 9e0972c6..4081dc3c 100644 --- a/src/screen.ino +++ b/src/screen.ino @@ -25,6 +25,7 @@ along with this program. If not, see . #include "OLEDDisplay.h" #include "images.h" #include "fonts.h" +#include "GPS.h" #define SCREEN_HEADER_HEIGHT 14 @@ -34,21 +35,19 @@ uint8_t _screen_line = SCREEN_HEADER_HEIGHT - 1; void _screen_header() { if(!display) return; - char buffer[20]; - // Message count //snprintf(buffer, sizeof(buffer), "#%03d", ttn_get_count() % 1000); //display->setTextAlignment(TEXT_ALIGN_LEFT); //display->drawString(0, 2, buffer); // Datetime - gps_time(buffer, sizeof(buffer)); display->setTextAlignment(TEXT_ALIGN_CENTER); - display->drawString(display->getWidth()/2, 2, buffer); + display->drawString(display->getWidth()/2, 2, gps.getTime()); // Satellite count display->setTextAlignment(TEXT_ALIGN_RIGHT); - display->drawString(display->getWidth() - SATELLITE_IMAGE_WIDTH - 4, 2, itoa(gps_sats(), buffer, 10)); + char buffer[10]; + display->drawString(display->getWidth() - SATELLITE_IMAGE_WIDTH - 4, 2, itoa(gps.satellites.value(), buffer, 10)); display->drawXbm(display->getWidth() - SATELLITE_IMAGE_WIDTH, 0, SATELLITE_IMAGE_WIDTH, SATELLITE_IMAGE_HEIGHT, SATELLITE_IMAGE); }