From 33946af39f240a072c1dbf59ee91504334074057 Mon Sep 17 00:00:00 2001 From: Marcel van der Boom Date: Thu, 25 Jun 2020 21:15:12 +0200 Subject: [PATCH 01/21] SCREEN_WIDTH is visible area already, not addressable area - sh1106 starts showing from column 2 (the library handles the offsets) so we don't actually need the different screen width here. --- src/screen.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/screen.cpp b/src/screen.cpp index f64f3784a..b72712378 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -35,11 +35,8 @@ along with this program. If not, see . #define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space #define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1) -#ifdef USE_SH1106 -#define SCREEN_WIDTH 132 -#else +// This means the *visible* area (sh1106 can address 132, but shows 128 for example) #define SCREEN_WIDTH 128 -#endif #define SCREEN_HEIGHT 64 #define TRANSITION_FRAMERATE 30 // fps #define IDLE_FRAMERATE 10 // in fps From ac2d3e2ae0b65dff561618a15acda9451bd9f318 Mon Sep 17 00:00:00 2001 From: Marcel van der Boom Date: Thu, 25 Jun 2020 21:16:35 +0200 Subject: [PATCH 02/21] Correct type of setBrightness parameter --- src/screen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screen.h b/src/screen.h index 6d4d8b373..5f2eb9264 100644 --- a/src/screen.h +++ b/src/screen.h @@ -119,7 +119,7 @@ class Screen : public PeriodicTask // Implementation to Adjust Brightness void adjustBrightness(); - int brightness = 150; + uint8_t brightness = 150; /// Starts showing the Bluetooth PIN screen. // From 542b8b26ce35962a9b511ca1e93c5c0df2a4ac60 Mon Sep 17 00:00:00 2001 From: Professr Date: Sat, 27 Jun 2020 21:19:49 -0700 Subject: [PATCH 03/21] Abstracted statuses, made display event-driven --- src/GPSStatus.h | 79 +++++++++++++++++ src/NodeStatus.h | 62 ++++++++++++++ src/Power.cpp | 178 +++++++++++++++++++++++++++++++++++++++ src/PowerStatus.h | 77 +++++++++++++++++ src/esp32/main-esp32.cpp | 148 -------------------------------- src/gps/GPS.h | 3 + src/gps/NEMAGPS.cpp | 8 ++ src/gps/UBloxGPS.cpp | 10 ++- src/main.cpp | 26 ++++-- src/main.h | 8 ++ src/mesh/MeshService.cpp | 2 +- src/mesh/NodeDB.cpp | 3 +- src/mesh/NodeDB.h | 12 +++ src/power.h | 37 ++++---- src/screen.cpp | 131 ++++++++++++++++------------ src/screen.h | 47 +++-------- 16 files changed, 565 insertions(+), 266 deletions(-) create mode 100644 src/GPSStatus.h create mode 100644 src/NodeStatus.h create mode 100644 src/Power.cpp create mode 100644 src/PowerStatus.h diff --git a/src/GPSStatus.h b/src/GPSStatus.h new file mode 100644 index 000000000..b6e7cf3a7 --- /dev/null +++ b/src/GPSStatus.h @@ -0,0 +1,79 @@ +#pragma once +#include +#include "lock.h" +#include "configuration.h" + +namespace meshtastic { + +/// Describes the state of the GPS system. +struct GPSStatus +{ + + bool isDirty = false; + bool hasLock = false; // default to false, until we complete our first read + bool isConnected = false; // Do we have a GPS we are talking to + + int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double + int32_t altitude = 0; + uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use) + +}; + +class GPSStatusHandler +{ + + private: + GPSStatus status; + CallbackObserver gpsObserver = CallbackObserver(this, &GPSStatusHandler::updateStatus); + bool initialized = false; + /// Protects all of internal state. + Lock lock; + + public: + Observable onNewStatus; + + void observe(Observable *source) + { + gpsObserver.observe(source); + } + + bool isInitialized() { LockGuard guard(&lock); return initialized; } + bool hasLock() { LockGuard guard(&lock); return status.hasLock; } + bool isConnected() { LockGuard guard(&lock); return status.isConnected; } + int32_t getLatitude() { LockGuard guard(&lock); return status.latitude; } + int32_t getLongitude() { LockGuard guard(&lock); return status.longitude; } + int32_t getAltitude() { LockGuard guard(&lock); return status.altitude; } + uint32_t getDOP() { LockGuard guard(&lock); return status.dop; } + + int updateStatus(const GPSStatus newStatus) { + // Only update the status if values have actually changed + status.isDirty = ( + newStatus.hasLock != status.hasLock || + newStatus.isConnected != status.isConnected || + newStatus.latitude != status.latitude || + newStatus.longitude != status.longitude || + newStatus.altitude != status.altitude || + newStatus.dop != status.latitude + ); + { + LockGuard guard(&lock); + initialized = true; + status.hasLock = newStatus.hasLock; + status.isConnected = newStatus.isConnected; + status.latitude = newStatus.latitude; + status.longitude = newStatus.longitude; + status.altitude = newStatus.altitude; + status.dop = newStatus.dop; + } + if(status.isDirty) { + DEBUG_MSG("New GPS pos lat=%f, lon=%f, alt=%d, pdop=%f\n", status.latitude * 1e-7, status.longitude * 1e-7, status.altitude, status.dop * 1e-2); + onNewStatus.notifyObservers(NULL); + } + return 0; + } + +}; + +} + +extern meshtastic::GPSStatus gpsStatus; \ No newline at end of file diff --git a/src/NodeStatus.h b/src/NodeStatus.h new file mode 100644 index 000000000..b8429b94d --- /dev/null +++ b/src/NodeStatus.h @@ -0,0 +1,62 @@ +#pragma once +#include +#include "lock.h" +#include "configuration.h" + +namespace meshtastic { + +/// Describes the state of the GPS system. +struct NodeStatus +{ + + bool isDirty = false; + size_t numOnline; // Number of nodes online + size_t numTotal; // Number of nodes total + +}; + +class NodeStatusHandler +{ + + private: + NodeStatus status; + CallbackObserver nodeObserver = CallbackObserver(this, &NodeStatusHandler::updateStatus); + bool initialized = false; + /// Protects all of internal state. + Lock lock; + + public: + Observable onNewStatus; + + void observe(Observable *source) + { + nodeObserver.observe(source); + } + + bool isInitialized() const { return initialized; } + size_t getNumOnline() const { return status.numOnline; } + size_t getNumTotal() const { return status.numTotal; } + + int updateStatus(const NodeStatus newStatus) + { + // Only update the status if values have actually changed + status.isDirty = ( + newStatus.numOnline != status.numOnline || + newStatus.numTotal != status.numTotal + ); + LockGuard guard(&lock); + initialized = true; + status.numOnline = newStatus.numOnline; + status.numTotal = newStatus.numTotal; + if(status.isDirty) { + DEBUG_MSG("Node status update: %d online, %d total\n", status.numOnline, status.numTotal); + onNewStatus.notifyObservers(NULL); + } + return 0; + } + +}; + +} + +extern meshtastic::NodeStatus nodeStatus; \ No newline at end of file diff --git a/src/Power.cpp b/src/Power.cpp new file mode 100644 index 000000000..63a4566d5 --- /dev/null +++ b/src/Power.cpp @@ -0,0 +1,178 @@ +#include "Power.h" +#include "PowerFSM.h" +#include "main.h" +#include "utils.h" +#include "sleep.h" + + +#ifdef TBEAM_V10 + +// FIXME. nasty hack cleanup how we load axp192 +#undef AXP192_SLAVE_ADDRESS +#include "axp20x.h" +AXP20X_Class axp; +bool pmu_irq = false; + +Power *power; + +bool Power::setup() +{ + + axp192Init(); + PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device + setPeriod(1); + + return axp192_found; +} + +/// Reads power status to powerStatus singleton. +// +// TODO(girts): move this and other axp stuff to power.h/power.cpp. +void Power::readPowerStatus() +{ + meshtastic::PowerStatus status; + status.hasBattery = axp.isBatteryConnect(); + if (status.hasBattery) { + status.batteryVoltageMv = axp.getBattVoltage(); + // If the AXP192 returns a valid battery percentage, use it + if (axp.getBattPercentage() >= 0) { + status.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 + status.batteryChargePercent = clamp((int)(((status.batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), 0, 100); + } + } + status.hasUSB = axp.isVBUSPlug(); + status.isCharging = axp.isChargeing(); + newStatus.notifyObservers(status); + + // If we have a battery at all and it is less than 10% full, force deep sleep + if (status.hasBattery && !status.hasUSB && + axp.getBattVoltage() < MIN_BAT_MILLIVOLTS) + powerFSM.trigger(EVENT_LOW_BATTERY); +} + +void Power::doTask() +{ + readPowerStatus(); + + // Only read once every 20 seconds once the power status for the app has been initialized + if(statusHandler && statusHandler->isInitialized()) + setPeriod(1000 * 20); +} +#endif // TBEAM_V10 + +#ifdef AXP192_SLAVE_ADDRESS +/** + * Init the power manager chip + * + * axp192 power + DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the axp192 + share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1 + 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() +{ + if (axp192_found) { + if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) { + DEBUG_MSG("AXP192 Begin PASS\n"); + + // axp.setChgLEDMode(LED_BLINK_4HZ); + DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE"); + DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE"); + DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE"); + DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE"); + DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE"); + DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE"); + DEBUG_MSG("----------------------------------------\n"); + + axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio + axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS main power + axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON); + axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON); + axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); + axp.setDCDC1Voltage(3300); // for the OLED power + + DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE"); + DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE"); + DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE"); + DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE"); + DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE"); + DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE"); + + axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA); // actual limit (in HW) on the tbeam is 450mA +#if 0 + + // Not connected + //val = 0xfc; + //axp._writeByte(AXP202_VHTF_CHGSET, 1, &val); // Set temperature protection + + //not used + //val = 0x46; + //axp._writeByte(AXP202_OFF_CTL, 1, &val); // enable bat detection +#endif + axp.debugCharging(); + +#ifdef PMU_IRQ + pinMode(PMU_IRQ, INPUT); + attachInterrupt( + PMU_IRQ, [] { pmu_irq = true; }, FALLING); + + axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1); + axp.enableIRQ(AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ | AXP202_CHARGING_FINISHED_IRQ | AXP202_CHARGING_IRQ | + AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ, + 1); + + axp.clearIRQ(); +#endif + readPowerStatus(); + } else { + DEBUG_MSG("AXP192 Begin FAIL\n"); + } + } else { + DEBUG_MSG("AXP192 not found\n"); + } +} +#endif + +void Power::loop() +{ + +#ifdef PMU_IRQ + if (pmu_irq) { + pmu_irq = false; + axp.readIRQ(); + + DEBUG_MSG("pmu irq!\n"); + + if (axp.isChargingIRQ()) { + DEBUG_MSG("Battery start charging\n"); + } + if (axp.isChargingDoneIRQ()) { + DEBUG_MSG("Battery fully charged\n"); + } + if (axp.isVbusRemoveIRQ()) { + DEBUG_MSG("USB unplugged\n"); + } + if (axp.isVbusPlugInIRQ()) { + DEBUG_MSG("USB plugged In\n"); + } + if (axp.isBattPlugInIRQ()) { + DEBUG_MSG("Battery inserted\n"); + } + if (axp.isBattRemoveIRQ()) { + DEBUG_MSG("Battery removed\n"); + } + if (axp.isPEKShortPressIRQ()) { + DEBUG_MSG("PEK short button press\n"); + } + + readPowerStatus(); + axp.clearIRQ(); + } + +#endif // T_BEAM_V10 + +} diff --git a/src/PowerStatus.h b/src/PowerStatus.h new file mode 100644 index 000000000..e5deb727d --- /dev/null +++ b/src/PowerStatus.h @@ -0,0 +1,77 @@ +#pragma once +#include "lock.h" +#include "configuration.h" + +namespace meshtastic +{ + +/// Describes the state of the power system. +struct PowerStatus +{ + + /// Whether or not values have changed since last read + bool isDirty = false; + /// Whether we have a battery connected + bool hasBattery; + /// Battery voltage in mV, valid if haveBattery is true + int batteryVoltageMv; + /// Battery charge percentage, either read directly or estimated + int batteryChargePercent; + /// Whether USB is connected + bool hasUSB; + /// Whether we are charging the battery + bool isCharging; + +}; + +class PowerStatusHandler +{ + + private: + PowerStatus status; + CallbackObserver powerObserver = CallbackObserver(this, &PowerStatusHandler::updateStatus); + bool initialized = false; + /// Protects all of internal state. + Lock lock; + + public: + + Observable onNewStatus; + + void observe(Observable *source) + { + powerObserver.observe(source); + } + + bool isInitialized() { LockGuard guard(&lock); return initialized; } + bool isCharging() { LockGuard guard(&lock); return status.isCharging; } + bool hasUSB() { LockGuard guard(&lock); return status.hasUSB; } + bool hasBattery() { LockGuard guard(&lock); return status.hasBattery; } + int getBatteryVoltageMv() { LockGuard guard(&lock); return status.batteryVoltageMv; } + int getBatteryChargePercent() { LockGuard guard(&lock); return status.batteryChargePercent; } + + int updateStatus(const PowerStatus newStatus) { + // Only update the status if values have actually changed + status.isDirty = ( + newStatus.hasBattery != status.hasBattery || + newStatus.hasUSB != status.hasUSB || + newStatus.batteryVoltageMv != status.batteryVoltageMv + ); + LockGuard guard(&lock); + initialized = true; + status.hasBattery = newStatus.hasBattery; + status.batteryVoltageMv = newStatus.batteryVoltageMv; + status.batteryChargePercent = newStatus.batteryChargePercent; + status.hasUSB = newStatus.hasUSB; + status.isCharging = newStatus.isCharging; + if(status.isDirty) { + DEBUG_MSG("Battery %dmV %d%%\n", status.batteryVoltageMv, status.batteryChargePercent); + onNewStatus.notifyObservers(this); + } + return 0; + } +}; + +} // namespace meshtastic + +extern meshtastic::PowerStatus powerStatus; diff --git a/src/esp32/main-esp32.cpp b/src/esp32/main-esp32.cpp index e0d53eab1..3b0b56d74 100644 --- a/src/esp32/main-esp32.cpp +++ b/src/esp32/main-esp32.cpp @@ -3,7 +3,6 @@ #include "PowerFSM.h" #include "configuration.h" #include "main.h" -#include "power.h" #include "sleep.h" #include "utils.h" #include "target_specific.h" @@ -60,111 +59,6 @@ void getMacAddr(uint8_t *dmac) assert(esp_efuse_mac_get_default(dmac) == ESP_OK); } -#ifdef TBEAM_V10 - -// FIXME. nasty hack cleanup how we load axp192 -#undef AXP192_SLAVE_ADDRESS -#include "axp20x.h" -AXP20X_Class axp; -bool pmu_irq = false; - -/// Reads power status to powerStatus singleton. -// -// TODO(girts): move this and other axp stuff to power.h/power.cpp. -void readPowerStatus() -{ - powerStatus.haveBattery = axp.isBatteryConnect(); - if (powerStatus.haveBattery) { - powerStatus.batteryVoltageMv = axp.getBattVoltage(); - // If the AXP192 returns a valid battery percentage, use it - if (axp.getBattPercentage() >= 0) { - powerStatus.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 - powerStatus.batteryChargePercent = clamp((int)(((powerStatus.batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), 0, 100); - } - DEBUG_MSG("Battery %dmV %d%%\n", powerStatus.batteryVoltageMv, powerStatus.batteryChargePercent); - } - powerStatus.usb = axp.isVBUSPlug(); - powerStatus.charging = axp.isChargeing(); -} -#endif // TBEAM_V10 - -#ifdef AXP192_SLAVE_ADDRESS -/** - * Init the power manager chip - * - * axp192 power - DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the axp192 - share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1 - 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 axp192Init() -{ - if (axp192_found) { - if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) { - DEBUG_MSG("AXP192 Begin PASS\n"); - - // axp.setChgLEDMode(LED_BLINK_4HZ); - DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("----------------------------------------\n"); - - axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio - axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS main power - axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON); - axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON); - axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); - axp.setDCDC1Voltage(3300); // for the OLED power - - DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE"); - DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE"); - - axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA); // actual limit (in HW) on the tbeam is 450mA -#if 0 - - // Not connected - //val = 0xfc; - //axp._writeByte(AXP202_VHTF_CHGSET, 1, &val); // Set temperature protection - - //not used - //val = 0x46; - //axp._writeByte(AXP202_OFF_CTL, 1, &val); // enable bat detection -#endif - axp.debugCharging(); - -#ifdef PMU_IRQ - pinMode(PMU_IRQ, INPUT); - attachInterrupt( - PMU_IRQ, [] { pmu_irq = true; }, FALLING); - - axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1); - axp.enableIRQ(AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ | AXP202_CHARGING_FINISHED_IRQ | AXP202_CHARGING_IRQ | - AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ, - 1); - - axp.clearIRQ(); -#endif - readPowerStatus(); - } else { - DEBUG_MSG("AXP192 Begin FAIL\n"); - } - } else { - DEBUG_MSG("AXP192 not found\n"); - } -} -#endif - /* static void printBLEinfo() { int dev_num = esp_ble_get_bond_device_num(); @@ -190,9 +84,6 @@ void esp32Setup() // enableModemSleep(); -#ifdef AXP192_SLAVE_ADDRESS - axp192Init(); -#endif } #if 0 @@ -223,43 +114,4 @@ void esp32Loop() // for debug printing // radio.radioIf.canSleep(); - -#ifdef PMU_IRQ - if (pmu_irq) { - pmu_irq = false; - axp.readIRQ(); - - DEBUG_MSG("pmu irq!\n"); - - if (axp.isChargingIRQ()) { - DEBUG_MSG("Battery start charging\n"); - } - if (axp.isChargingDoneIRQ()) { - DEBUG_MSG("Battery fully charged\n"); - } - if (axp.isVbusRemoveIRQ()) { - DEBUG_MSG("USB unplugged\n"); - } - if (axp.isVbusPlugInIRQ()) { - DEBUG_MSG("USB plugged In\n"); - } - if (axp.isBattPlugInIRQ()) { - DEBUG_MSG("Battery inserted\n"); - } - if (axp.isBattRemoveIRQ()) { - DEBUG_MSG("Battery removed\n"); - } - if (axp.isPEKShortPressIRQ()) { - DEBUG_MSG("PEK short button press\n"); - } - - readPowerStatus(); - axp.clearIRQ(); - } - - if (powerStatus.haveBattery && !powerStatus.usb && - axp.getBattVoltage() < MIN_BAT_MILLIVOLTS) // If we have a battery at all and it is less than 10% full, force deep sleep - powerFSM.trigger(EVENT_LOW_BATTERY); - -#endif // T_BEAM_V10 } \ No newline at end of file diff --git a/src/gps/GPS.h b/src/gps/GPS.h index b2d1916d7..d03bd350f 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -1,6 +1,7 @@ #pragma once #include "Observer.h" +#include "GPSStatus.h" #include "PeriodicTask.h" #include "sys/time.h" @@ -40,6 +41,8 @@ class GPS : public Observable virtual ~GPS() {} + Observable newStatus; + /** * Returns true if we succeeded */ diff --git a/src/gps/NEMAGPS.cpp b/src/gps/NEMAGPS.cpp index baba0424f..b13fa9b95 100644 --- a/src/gps/NEMAGPS.cpp +++ b/src/gps/NEMAGPS.cpp @@ -65,5 +65,13 @@ void NEMAGPS::loop() if (hasValidLocation) notifyObservers(NULL); } + meshtastic::GPSStatus status; + status.hasLock = hasLock(); + status.isConnected = isConnected; + status.latitude = latitude; + status.longitude = longitude; + status.altitude = altitude; + status.dop = dop; + newStatus.notifyObservers(status); } } \ No newline at end of file diff --git a/src/gps/UBloxGPS.cpp b/src/gps/UBloxGPS.cpp index 012211299..199657e84 100644 --- a/src/gps/UBloxGPS.cpp +++ b/src/gps/UBloxGPS.cpp @@ -116,7 +116,6 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s longitude = ublox.getLongitude(0); altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it - DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d, pdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2); // bogus lat lon is reported as 0 or 0 (can be bogus just for one) // Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg! @@ -129,6 +128,15 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s } else // we didn't get a location update, go back to sleep and hope the characters show up wantNewLocation = true; + meshtastic::GPSStatus status; + status.hasLock = hasLock(); + status.isConnected = isConnected; + status.latitude = latitude; + status.longitude = longitude; + status.altitude = altitude; + status.dop = dop; + newStatus.notifyObservers(status); + // Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over // the serial setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000); diff --git a/src/main.cpp b/src/main.cpp index 667c10697..0dac6af8d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,7 +30,7 @@ #include "UBloxGPS.h" #include "configuration.h" #include "error.h" -#include "power.h" +#include "Power.h" // #include "rom/rtc.h" #include "DSRRouter.h" #include "debug.h" @@ -56,8 +56,14 @@ // We always create a screen object, but we only init it if we find the hardware meshtastic::Screen screen(SSD1306_ADDRESS); -// Global power status singleton -meshtastic::PowerStatus powerStatus; +// Global power status +meshtastic::PowerStatusHandler *powerStatusHandler = new meshtastic::PowerStatusHandler(); + +// Global GPS status +meshtastic::GPSStatusHandler *gpsStatusHandler = new meshtastic::GPSStatusHandler(); + +// Global Node status +meshtastic::NodeStatusHandler *nodeStatusHandler = new meshtastic::NodeStatusHandler(); bool ssd1306_found; bool axp192_found; @@ -121,7 +127,7 @@ static uint32_t ledBlinker() setLed(ledOn); // have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that - return powerStatus.charging ? 1000 : (ledOn ? 2 : 1000); + return powerStatusHandler->isCharging() ? 1000 : (ledOn ? 2 : 1000); } Periodic ledPeriodic(ledBlinker); @@ -223,6 +229,10 @@ void setup() ssd1306_found = false; // forget we even have the hardware esp32Setup(); + power = new Power(); + power->setup(); + power->setStatusHandler(powerStatusHandler); + powerStatusHandler->observe(&power->newStatus); #endif #ifdef NRF52_SERIES @@ -253,9 +263,10 @@ void setup() gps = new NEMAGPS(); gps->setup(); #endif + gpsStatusHandler->observe(&gps->newStatus); + nodeStatusHandler->observe(&nodeDB.newStatus); service.init(); - #ifndef NO_ESP32 // Must be after we init the service, because the wifi settings are loaded by NodeDB (oops) initWifi(); @@ -295,6 +306,7 @@ void setup() // This must be _after_ service.init because we need our preferences loaded from flash to have proper timeout values PowerFSM_setup(); // we will transition to ON in a couple of seconds, FIXME, only do this for cold boots, not waking from SDS + // setBluetoothEnable(false); we now don't start bluetooth until we enter the proper state setCPUFast(false); // 80MHz is fine for our slow peripherals } @@ -340,6 +352,7 @@ void loop() #ifndef NO_ESP32 esp32Loop(); + power->loop(); #endif #ifdef BUTTON_PIN @@ -365,9 +378,8 @@ void loop() #endif // Update the screen last, after we've figured out what to show. - screen.debug()->setNodeNumbersStatus(nodeDB.getNumOnlineNodes(), nodeDB.getNumNodes()); screen.debug()->setChannelNameStatus(channelSettings.name); - screen.debug()->setPowerStatus(powerStatus); + //screen.debug()->setPowerStatus(powerStatus); // No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in) // i.e. don't just keep spinning in loop as fast as we can. diff --git a/src/main.h b/src/main.h index 9d0cde8db..26e63cf7e 100644 --- a/src/main.h +++ b/src/main.h @@ -1,6 +1,9 @@ #pragma once #include "screen.h" +#include "PowerStatus.h" +#include "GPSStatus.h" +#include "NodeStatus.h" extern bool axp192_found; extern bool ssd1306_found; @@ -9,6 +12,11 @@ extern bool isUSBPowered; // Global Screen singleton. extern meshtastic::Screen screen; +extern Observable newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class + +extern meshtastic::PowerStatusHandler *powerStatusHandler; +extern meshtastic::GPSStatusHandler *gpsStatusHandler; +extern meshtastic::NodeStatusHandler *nodeStatusHandler; // Return a human readable string of the form "Meshtastic_ab13" const char *getDeviceName(); diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 70e20f803..83739b320 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -303,7 +303,7 @@ int MeshService::onGPSChanged(void *unused) } // Include our current battery voltage in our position announcement - pos.battery_level = powerStatus.batteryChargePercent; + pos.battery_level = powerStatusHandler->getBatteryChargePercent(); updateBatteryLevel(pos.battery_level); // We limit our GPS broadcasts to a max rate diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index ca28c17a5..00d3f5699 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -342,8 +342,7 @@ void NodeDB::updateFrom(const MeshPacket &mp) int oldNumNodes = *numNodes; NodeInfo *info = getOrCreateNode(mp.from); - if (oldNumNodes != *numNodes) - updateGUI = true; // we just created a nodeinfo + notifyObservers(); if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen info->has_position = true; // at least the time is valid diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 611024e22..12a023d6c 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -2,9 +2,11 @@ #include #include +#include "Observer.h" #include "MeshTypes.h" #include "mesh-pb-constants.h" +#include "NodeStatus.h" extern DeviceState devicestate; extern MyNodeInfo &myNodeInfo; @@ -32,6 +34,7 @@ class NodeDB bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled NodeInfo *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI bool updateTextMessage = false; // if true, the GUI should show a new text message + Observable newStatus; /// don't do mesh based algoritm for node id assignment (initially) /// instead just store in flash - possibly even in the initial alpha release do this hack @@ -91,6 +94,15 @@ class NodeDB /// Find a node in our DB, create an empty NodeInfo if missing NodeInfo *getOrCreateNode(NodeNum n); + /// Notify observers of changes to the DB + void notifyObservers() { + // Notify observers of the current node state + meshtastic::NodeStatus status; + status.numOnline = getNumOnlineNodes(); + status.numTotal = getNumNodes(); + newStatus.notifyObservers(status); + } + /// read our db from flash void loadFromDisk(); diff --git a/src/power.h b/src/power.h index 21c913d50..63929cc2e 100644 --- a/src/power.h +++ b/src/power.h @@ -1,4 +1,6 @@ #pragma once +#include "PeriodicTask.h" +#include "PowerStatus.h" /** * Per @spattinson @@ -13,23 +15,26 @@ #define BAT_MILLIVOLTS_FULL 4100 #define BAT_MILLIVOLTS_EMPTY 3500 -namespace meshtastic +class Power : public PeriodicTask { -/// Describes the state of the power system. -struct PowerStatus { - /// Whether we have a battery connected - bool haveBattery; - /// Battery voltage in mV, valid if haveBattery is true - int batteryVoltageMv; - /// Battery charge percentage, either read directly or estimated - int batteryChargePercent; - /// Whether USB is connected - bool usb; - /// Whether we are charging the battery - bool charging; + public: + + Observable newStatus; + + void readPowerStatus(); + void loop(); + virtual bool setup(); + virtual void doTask(); + void setStatusHandler(meshtastic::PowerStatusHandler *handler) + { + statusHandler = handler; + } + + protected: + meshtastic::PowerStatusHandler *statusHandler; + virtual void axp192Init(); + }; -} // namespace meshtastic - -extern meshtastic::PowerStatus powerStatus; +extern Power *power; \ No newline at end of file diff --git a/src/screen.cpp b/src/screen.cpp index f64f3784a..d655a924c 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -42,7 +42,7 @@ along with this program. If not, see . #endif #define SCREEN_HEIGHT 64 #define TRANSITION_FRAMERATE 30 // fps -#define IDLE_FRAMERATE 10 // in fps +#define IDLE_FRAMERATE 1 // in fps #define COMPASS_DIAM 44 #define NUM_EXTRA_FRAMES 2 // text message and debug frame @@ -56,6 +56,7 @@ static uint32_t targetFramerate = IDLE_FRAMERATE; static char btPIN[16] = "888888"; uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C }; +static bool heartbeat = false; static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { @@ -142,38 +143,40 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char * } } -/// Draw a series of fields in a row, wrapping to multiple rows if needed -/// @return the max y we ended up printing to -static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields) -{ - // The coordinates define the left starting point of the text - display->setTextAlignment(TEXT_ALIGN_LEFT); +#if 0 + /// Draw a series of fields in a row, wrapping to multiple rows if needed + /// @return the max y we ended up printing to + static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields) + { + // The coordinates define the left starting point of the text + display->setTextAlignment(TEXT_ALIGN_LEFT); - const char **f = fields; - int xo = x, yo = y; - const int COLUMNS = 2; // hardwired for two columns per row.... - int col = 0; // track which column we are on - while (*f) { - display->drawString(xo, yo, *f); - xo += SCREEN_WIDTH / COLUMNS; - // Wrap to next row, if needed. - if (++col >= COLUMNS) { - xo = x; - yo += FONT_HEIGHT; - col = 0; + const char **f = fields; + int xo = x, yo = y; + const int COLUMNS = 2; // hardwired for two columns per row.... + int col = 0; // track which column we are on + while (*f) { + display->drawString(xo, yo, *f); + xo += SCREEN_WIDTH / COLUMNS; + // Wrap to next row, if needed. + if (++col >= COLUMNS) { + xo = x; + yo += FONT_HEIGHT; + col = 0; + } + f++; + } + if (col != 0) { + // Include last incomplete line in our total. + yo += FONT_HEIGHT; } - f++; - } - if (col != 0) { - // Include last incomplete line in our total. - yo += FONT_HEIGHT; - } - return yo; -} + return yo; + } +#endif // Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage. -static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, PowerStatus *powerStatus) { +static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, PowerStatusHandler *powerStatusHandler) { static const uint8_t powerBar[3] = { 0x81, 0xBD, 0xBD }; static const uint8_t lightning[8] = { 0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85 }; // Clear the bar area on the battery image @@ -181,36 +184,36 @@ static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *img imgBuffer[i] = 0x81; } // If charging, draw a charging indicator - if (powerStatus->charging) { + if (powerStatusHandler->isCharging()) { memcpy(imgBuffer + 3, lightning, 8); // If not charging, Draw power bars } else { for (int i = 0; i < 4; i++) { - if(powerStatus->batteryChargePercent >= 25 * i) memcpy(imgBuffer + 1 + (i * 3), powerBar, 3); + if(powerStatusHandler->getBatteryChargePercent() >= 25 * i) memcpy(imgBuffer + 1 + (i * 3), powerBar, 3); } } display->drawFastImage(x, y, 16, 8, imgBuffer); } // Draw nodes status -static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, int nodesOnline, int nodesTotal) { +static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatusHandler *nodeStatusHandler) { char usersString[20]; - sprintf(usersString, "%d/%d", nodesOnline, nodesTotal); + sprintf(usersString, "%d/%d", nodeStatusHandler->getNumOnline(), nodeStatusHandler->getNumTotal()); display->drawFastImage(x, y, 8, 8, imgUser); display->drawString(x + 10, y - 2, usersString); } // Draw GPS status summary -static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, GPS *gps) { - if(!gps->isConnected) { display->drawString(x, y - 2, "No GPS"); return; } +static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, GPSStatusHandler *gpsStatusHandler) { + if(!gpsStatusHandler->isConnected()) { display->drawString(x, y - 2, "No GPS"); return; } display->drawFastImage(x, y, 6, 8, gps->hasLock() ? imgPositionSolid : imgPositionEmpty ); - if(!gps->hasLock()) { display->drawString(x + 8, y - 2, "No sats"); return; } - if(gps->dop <= 100) { display->drawString(x + 8, y - 2, "Ideal"); return; } - if(gps->dop <= 200) { display->drawString(x + 8, y - 2, "Exc."); return; } - if(gps->dop <= 500) { display->drawString(x + 8, y - 2, "Good"); return; } - if(gps->dop <= 1000) { display->drawString(x + 8, y - 2, "Mod."); return; } - if(gps->dop <= 2000) { display->drawString(x + 8, y - 2, "Fair"); return; } - if(gps->dop > 0) { display->drawString(x + 8, y - 2, "Poor"); return; } + if(!gpsStatusHandler->hasLock()) { display->drawString(x + 8, y - 2, "No sats"); return; } + if(gpsStatusHandler->getDOP() <= 100) { display->drawString(x + 8, y - 2, "Ideal"); return; } + if(gpsStatusHandler->getDOP() <= 200) { display->drawString(x + 8, y - 2, "Exc."); return; } + if(gpsStatusHandler->getDOP() <= 500) { display->drawString(x + 8, y - 2, "Good"); return; } + if(gpsStatusHandler->getDOP() <= 1000) { display->drawString(x + 8, y - 2, "Mod."); return; } + if(gpsStatusHandler->getDOP() <= 2000) { display->drawString(x + 8, y - 2, "Fair"); return; } + if(gpsStatusHandler->getDOP() > 0) { display->drawString(x + 8, y - 2, "Poor"); return; } } /// Ported from my old java code, returns distance in meters along the globe @@ -545,6 +548,11 @@ void Screen::setup() // twice initially. ui.update(); ui.update(); + + // Subscribe to status updates + powerStatusObserver.observe(&powerStatusHandler->onNewStatus); + gpsStatusObserver.observe(&gpsStatusHandler->onNewStatus); + nodeStatusObserver.observe(&nodeStatusHandler->onNewStatus); } void Screen::doTask() @@ -606,14 +614,7 @@ void Screen::doTask() // While showing the bootscreen or Bluetooth pair screen all of our // standard screen switching is stopped. if (showingNormalScreen) { - // TODO(girts): decouple nodeDB from screen. - // standard screen loop handling ehre - // If the # nodes changes, we need to regen our list of screens - if (nodeDB.updateGUI || nodeDB.updateTextMessage) { - setFrames(); - nodeDB.updateGUI = false; - nodeDB.updateTextMessage = false; - } + // standard screen loop handling here } ui.update(); @@ -638,8 +639,8 @@ void Screen::setFrames() DEBUG_MSG("showing standard frames\n"); showingNormalScreen = true; - size_t numnodes = nodeDB.getNumNodes(); // We don't show the node info our our node (if we have it yet - we should) + size_t numnodes = nodeStatusHandler->getNumTotal(); if (numnodes > 0) numnodes--; @@ -719,17 +720,19 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str()); // Display power status - if (powerStatus.haveBattery) drawBattery(display, x, y + 2, imgBattery, &powerStatus); else display->drawFastImage(x, y + 2, 16, 8, powerStatus.usb ? imgUSB : imgPower); + if (powerStatusHandler->hasBattery()) drawBattery(display, x, y + 2, imgBattery, powerStatusHandler); else display->drawFastImage(x, y + 2, 16, 8, powerStatusHandler->hasUSB() ? imgUSB : imgPower); // Display nodes status - drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodesOnline, nodesTotal); + drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatusHandler); // Display GPS status - drawGPS(display, x + (SCREEN_WIDTH * 0.66), y + 2, gps); + drawGPS(display, x + (SCREEN_WIDTH * 0.66), y + 2, gpsStatusHandler); } - const char *fields[] = {channelStr, nullptr}; - uint32_t yo = drawRows(display, x, y + FONT_HEIGHT, fields); + display->drawString(x, y + FONT_HEIGHT, channelStr); - display->drawLogBuffer(x, yo); + display->drawLogBuffer(x, y + (FONT_HEIGHT * 2)); + + if(heartbeat) display->setPixel(0, 0); + heartbeat = !heartbeat; } //adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true @@ -747,4 +750,20 @@ void Screen::adjustBrightness(){ dispdev.setBrightness(brightness); } +int Screen::handlePowerStatusUpdate(void *arg) { + //DEBUG_MSG("Screen got power status update\n"); + setPeriod(1); // Update the screen right away + return 0; +} +int Screen::handleGPSStatusUpdate(void *arg) { + //DEBUG_MSG("Screen got gps status update\n"); + setPeriod(1); // Update the screen right away + return 0; +} +int Screen::handleNodeStatusUpdate(void *arg) { + //DEBUG_MSG("Screen got node status update\n"); + setFrames(); // Update our frames if the node database has changed + setPeriod(1); // Update the screen right away + return 0; +} } // namespace meshtastic diff --git a/src/screen.h b/src/screen.h index 6d4d8b373..8c76a5769 100644 --- a/src/screen.h +++ b/src/screen.h @@ -13,7 +13,10 @@ #include "PeriodicTask.h" #include "TypedQueue.h" #include "lock.h" -#include "power.h" +#include "PowerStatus.h" +#include "GPSStatus.h" +#include "NodeStatus.h" +#include "Observer.h" #include namespace meshtastic @@ -29,14 +32,6 @@ class DebugInfo DebugInfo(const DebugInfo &) = delete; DebugInfo &operator=(const DebugInfo &) = delete; - /// Sets user statistics. - void setNodeNumbersStatus(int online, int total) - { - LockGuard guard(&lock); - nodesOnline = online; - nodesTotal = total; - } - /// Sets the name of the channel. void setChannelNameStatus(const char *name) { @@ -44,25 +39,6 @@ class DebugInfo channelName = name; } - /// Sets battery/charging/etc status. - // - void setPowerStatus(const PowerStatus &status) - { - LockGuard guard(&lock); - powerStatus = status; - } - - /// Sets GPS status. - // - // If this function never gets called, we assume GPS does not exist on this - // device. - // TODO(girts): figure out what the format should be. - void setGPSStatus(const char *status) - { - LockGuard guard(&lock); - gpsStatus = status; - } - private: friend Screen; @@ -71,15 +47,8 @@ class DebugInfo /// Renders the debug screen. void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); - int nodesOnline = 0; - int nodesTotal = 0; - - PowerStatus powerStatus; - std::string channelName; - std::string gpsStatus; - /// Protects all of internal state. Lock lock; }; @@ -93,6 +62,10 @@ class DebugInfo // simultaneously). class Screen : public PeriodicTask { + CallbackObserver powerStatusObserver = CallbackObserver(this, &Screen::handlePowerStatusUpdate); + CallbackObserver gpsStatusObserver = CallbackObserver(this, &Screen::handleGPSStatusUpdate); + CallbackObserver nodeStatusObserver = CallbackObserver(this, &Screen::handleNodeStatusUpdate); + public: Screen(uint8_t address, int sda = -1, int scl = -1); @@ -189,6 +162,10 @@ class Screen : public PeriodicTask // Use this handle to set things like battery status, user count, GPS status, etc. DebugInfo *debug() { return &debugInfo; } + int handlePowerStatusUpdate(void *arg); + int handleGPSStatusUpdate(void *arg); + int handleNodeStatusUpdate(void *arg); + protected: /// Updates the UI. // From bd477f0fb2c34c27fc162a25025a9efd6ea00b91 Mon Sep 17 00:00:00 2001 From: geeksville Date: Sun, 28 Jun 2020 11:12:12 -0700 Subject: [PATCH 04/21] turn on thread watchdog --- docs/software/TODO.md | 1 - src/WorkerThread.cpp | 9 +++++++++ src/WorkerThread.h | 20 +++++++++++++++++++- src/esp32/main-esp32.cpp | 19 ++++++++++++++++--- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 60d0e5b0a..b9ad6eae6 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -48,7 +48,6 @@ Items after the first final candidate release. - split out the software update utility so other projects can use it. Have the appload specify the URL for downloads. - read the PMU battery fault indicators and blink/led/warn user on screen - discard very old nodedb records (> 1wk) -- add a watchdog timer - handle millis() rollover in GPS.getTime - otherwise we will break after 50 days - report esp32 device code bugs back to the mothership via android - change BLE bonding to something more secure. see comment by pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND) diff --git a/src/WorkerThread.cpp b/src/WorkerThread.cpp index 3c7ea4c9a..8831fab58 100644 --- a/src/WorkerThread.cpp +++ b/src/WorkerThread.cpp @@ -17,8 +17,15 @@ void Thread::callRun(void *_this) void WorkerThread::doRun() { + startWatchdog(); + while (!wantExit) { + stopWatchdog(); block(); + startWatchdog(); + + // no need - startWatchdog is guaranteed to give us one full watchdog interval + // serviceWatchdog(); // Let our loop worker have one full watchdog interval (at least) to run #ifdef DEBUG_STACK static uint32_t lastPrint = 0; @@ -30,6 +37,8 @@ void WorkerThread::doRun() loop(); } + + stopWatchdog(); } /** diff --git a/src/WorkerThread.h b/src/WorkerThread.h index ab1864869..3a7432207 100644 --- a/src/WorkerThread.h +++ b/src/WorkerThread.h @@ -1,5 +1,6 @@ -#include +#include "esp_task_wdt.h" #include "freertosinc.h" +#include #ifdef HAS_FREE_RTOS @@ -26,6 +27,23 @@ class Thread */ virtual void doRun() = 0; + /** + * All thread run methods must periodically call serviceWatchdog, or the system will declare them hung and panic. + * + * this only applies after startWatchdog() has been called. If you need to sleep for a long time call stopWatchdog() + */ + void serviceWatchdog() { esp_task_wdt_reset(); } + void startWatchdog() + { + auto r = esp_task_wdt_add(taskHandle); + assert(r == ESP_OK); + } + void stopWatchdog() + { + auto r = esp_task_wdt_delete(taskHandle); + assert(r == ESP_OK); + } + private: static void callRun(void *_this); }; diff --git a/src/esp32/main-esp32.cpp b/src/esp32/main-esp32.cpp index e0d53eab1..b7bd2d7c7 100644 --- a/src/esp32/main-esp32.cpp +++ b/src/esp32/main-esp32.cpp @@ -2,11 +2,12 @@ #include "MeshBluetoothService.h" #include "PowerFSM.h" #include "configuration.h" +#include "esp_task_wdt.h" #include "main.h" #include "power.h" #include "sleep.h" -#include "utils.h" #include "target_specific.h" +#include "utils.h" bool bluetoothOn; @@ -82,7 +83,9 @@ void readPowerStatus() } 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 - powerStatus.batteryChargePercent = clamp((int)(((powerStatus.batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), 0, 100); + powerStatus.batteryChargePercent = clamp((int)(((powerStatus.batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / + (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), + 0, 100); } DEBUG_MSG("Battery %dmV %d%%\n", powerStatus.batteryVoltageMv, powerStatus.batteryChargePercent); } @@ -193,6 +196,16 @@ void esp32Setup() #ifdef AXP192_SLAVE_ADDRESS axp192Init(); #endif + +// Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any +// false positives. The wait-to-sleep timeout for shutting down radios is 30 secs, so pick 45 for now. +#define APP_WATCHDOG_SECS 45 + + auto res = esp_task_wdt_init(APP_WATCHDOG_SECS, true); + assert(res == ESP_OK); + + res = esp_task_wdt_add(NULL); + assert(res == ESP_OK); } #if 0 @@ -215,10 +228,10 @@ uint32_t axpDebugRead() Periodic axpDebugOutput(axpDebugRead); #endif - /// loop code specific to ESP32 targets void esp32Loop() { + esp_task_wdt_reset(); // service our app level watchdog loopBLE(); // for debug printing From 8fa44c3590bdfe5591cfbf31df2603443d8c198b Mon Sep 17 00:00:00 2001 From: Professr Date: Sun, 28 Jun 2020 18:55:51 -0700 Subject: [PATCH 05/21] Disabled the display heartbeat pixel for pull request --- src/screen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/screen.cpp b/src/screen.cpp index 7ba153843..74f0065df 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -765,8 +765,10 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 display->drawLogBuffer(x, y + (FONT_HEIGHT * 2)); + /* Display a heartbeat pixel that blinks every time the frame is redrawn if(heartbeat) display->setPixel(0, 0); heartbeat = !heartbeat; + */ } // adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true From f2e6c6de5865b38d77ab4f2906efe788786114bd Mon Sep 17 00:00:00 2001 From: Professr Date: Sun, 28 Jun 2020 19:03:39 -0700 Subject: [PATCH 06/21] Fixed filename case sensitivity --- src/Power.cpp | 2 +- src/main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index a5f258ced..4b6226304 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -1,4 +1,4 @@ -#include "Power.h" +#include "power.h" #include "PowerFSM.h" #include "main.h" #include "utils.h" diff --git a/src/main.cpp b/src/main.cpp index 63770e135..56856f6eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,7 +30,7 @@ #include "UBloxGPS.h" #include "configuration.h" #include "error.h" -#include "Power.h" +#include "power.h" // #include "rom/rtc.h" #include "DSRRouter.h" #include "debug.h" From 8381512ce4437161fa312bfcae48ed2ff785d402 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 1 Jul 2020 09:32:01 -0700 Subject: [PATCH 07/21] todo updates --- docs/software/TODO.md | 2 +- docs/software/mesh-alg.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index b9ad6eae6..e8355bcf1 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -2,7 +2,7 @@ You probably don't care about this section - skip to the next one. -- implement first cut of router mode: preferentially handle flooding, and change sleep and GPS behaviors +- implement first cut of router mode: preferentially handle flooding, and change sleep and GPS behaviors (plan for geofence mode and battery save mode) - NRF52 BLE support # Medium priority diff --git a/docs/software/mesh-alg.md b/docs/software/mesh-alg.md index 2a2e4dd26..f9427cd21 100644 --- a/docs/software/mesh-alg.md +++ b/docs/software/mesh-alg.md @@ -32,8 +32,8 @@ optimizations / low priority: - read this [this](http://pages.cs.wisc.edu/~suman/pubs/nadv-mobihoc05.pdf) paper and others and make our naive flood routing less naive - read @cyclomies long email with good ideas on optimizations and reply -- Remove NodeNum assignment algorithm (now that we use 4 byte node nums) -- make android app warn if firmware is too old or too new to talk to +- DONE Remove NodeNum assignment algorithm (now that we use 4 byte node nums) +- DONE make android app warn if firmware is too old or too new to talk to - change nodenums and packetids in protobuf to be fixed32 - low priority: think more careful about reliable retransmit intervals - make ReliableRouter.pending threadsafe From a7456a11263cd0e35d6105609a3d070ebda16734 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 1 Jul 2020 10:08:09 -0700 Subject: [PATCH 08/21] all targets are arduino - for now --- platformio.ini | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index bad647bfa..f54ab582f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -23,6 +23,8 @@ default_envs = tbeam ; Note: the github actions CI test build can't yet build NR [env] +framework = arduino + ; customize the partition table ; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables board_build.partitions = partition-table.csv @@ -78,7 +80,6 @@ lib_deps = ; Common settings for ESP targes, mixin with extends = esp32_base [esp32_base] platform = espressif32 -framework = arduino src_filter = ${env.src_filter} - upload_speed = 921600 @@ -133,7 +134,6 @@ build_flags = ; For more details see my post in the forum. [env:cubecellplus] platform = https://github.com/HelTecAutomation/platform-asrmicro650x.git ; we use top-of-tree because stable version has too many bugs - asrmicro650x -framework = arduino board = cubecell_board_plus ; FIXME, bug in cubecell arduino - they are supposed to set ARDUINO build_flags = ${env.build_flags} -DARDUINO=100 -Isrc/cubecell @@ -143,7 +143,6 @@ src_filter = ; Common settings for NRF52 based targets [nrf52_base] platform = nordicnrf52 -framework = arduino debug_tool = jlink build_type = debug ; I'm debugging with ICE a lot now ; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME) From a4f53270e82fe71ab06fd7b13c859af6a0e229e0 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 1 Jul 2020 10:08:38 -0700 Subject: [PATCH 09/21] fix heltec build (and fix formatting) --- src/main.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 56856f6eb..89704e6e3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,8 +37,8 @@ #include "main.h" #include "screen.h" #include "sleep.h" -#include #include +#include // #include #ifndef NO_ESP32 @@ -134,15 +134,17 @@ Periodic ledPeriodic(ledBlinker); // Prepare for button presses #ifdef BUTTON_PIN - OneButton userButton; +OneButton userButton; #endif #ifdef BUTTON_PIN_ALT - OneButton userButtonAlt; +OneButton userButtonAlt; #endif -void userButtonPressed() { +void userButtonPressed() +{ powerFSM.trigger(EVENT_PRESS); } -void userButtonPressedLong(){ +void userButtonPressedLong() +{ screen.adjustBrightness(); } @@ -229,6 +231,10 @@ void setup() ssd1306_found = false; // forget we even have the hardware esp32Setup(); +#endif + +#ifdef TBEAM_V10 + // Currently only the tbeam has a PMU power = new Power(); power->setup(); power->setStatusHandler(powerStatus); @@ -306,7 +312,6 @@ void setup() // This must be _after_ service.init because we need our preferences loaded from flash to have proper timeout values PowerFSM_setup(); // we will transition to ON in a couple of seconds, FIXME, only do this for cold boots, not waking from SDS - // setBluetoothEnable(false); we now don't start bluetooth until we enter the proper state setCPUFast(false); // 80MHz is fine for our slow peripherals } @@ -352,6 +357,8 @@ void loop() #ifndef NO_ESP32 esp32Loop(); +#endif +#ifdef TBEAM_V10 power->loop(); #endif @@ -379,7 +386,7 @@ void loop() // Update the screen last, after we've figured out what to show. screen.debug()->setChannelNameStatus(channelSettings.name); - //screen.debug()->setPowerStatus(powerStatus); + // screen.debug()->setPowerStatus(powerStatus); // No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in) // i.e. don't just keep spinning in loop as fast as we can. From c327fee986911772ce283ebe25b3c2b125f74419 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 1 Jul 2020 10:09:06 -0700 Subject: [PATCH 10/21] Fix formatting --- src/screen.cpp | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/screen.cpp b/src/screen.cpp index 74f0065df..19f1fd4fb 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -55,8 +55,11 @@ static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES]; static uint32_t targetFramerate = IDLE_FRAMERATE; static char btPIN[16] = "888888"; -uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C }; +uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C}; + +#ifdef SHOW_REDRAWS static bool heartbeat = false; +#endif static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { @@ -177,10 +180,10 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char * #endif // Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage. -static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus) +static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus) { - static const uint8_t powerBar[3] = { 0x81, 0xBD, 0xBD }; - static const uint8_t lightning[8] = { 0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85 }; + static const uint8_t powerBar[3] = {0x81, 0xBD, 0xBD}; + static const uint8_t lightning[8] = {0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85}; // Clear the bar area on the battery image for (int i = 1; i < 14; i++) { imgBuffer[i] = 0x81; @@ -191,7 +194,7 @@ static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *img // If not charging, Draw power bars } else { for (int i = 0; i < 4; i++) { - if(powerStatus->getBatteryChargePercent() >= 25 * i) + if (powerStatus->getBatteryChargePercent() >= 25 * i) memcpy(imgBuffer + 1 + (i * 3), powerBar, 3); } } @@ -199,7 +202,7 @@ static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *img } // Draw nodes status -static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *nodeStatus) +static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *nodeStatus) { char usersString[20]; sprintf(usersString, "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal()); @@ -765,10 +768,12 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 display->drawLogBuffer(x, y + (FONT_HEIGHT * 2)); - /* Display a heartbeat pixel that blinks every time the frame is redrawn - if(heartbeat) display->setPixel(0, 0); + /* Display a heartbeat pixel that blinks every time the frame is redrawn */ +#ifdef SHOW_REDRAWS + if (heartbeat) + display->setPixel(0, 0); heartbeat = !heartbeat; - */ +#endif } // adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true @@ -786,13 +791,13 @@ void Screen::adjustBrightness() dispdev.setBrightness(brightness); } -int Screen::handleStatusUpdate(const Status *arg) { +int Screen::handleStatusUpdate(const Status *arg) +{ DEBUG_MSG("Screen got status update %d\n", arg->getStatusType()); - switch(arg->getStatusType()) - { - case STATUS_TYPE_NODE: - setFrames(); - break; + switch (arg->getStatusType()) { + case STATUS_TYPE_NODE: + setFrames(); + break; } setPeriod(1); // Update the screen right away return 0; From 3151cfb06402605749ce6b60aca15f3dd63b50e4 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 1 Jul 2020 10:09:32 -0700 Subject: [PATCH 11/21] 0.7.10 --- bin/version.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/version.sh b/bin/version.sh index 58db3c516..11037db32 100644 --- a/bin/version.sh +++ b/bin/version.sh @@ -1,3 +1,3 @@ -export VERSION=0.7.9 \ No newline at end of file +export VERSION=0.7.10 \ No newline at end of file From 9b9447858a5b98ec6c87c7a6a8cb1f6a1a320a2b Mon Sep 17 00:00:00 2001 From: Toby Murray Date: Wed, 1 Jul 2020 13:12:00 -0400 Subject: [PATCH 12/21] Add Canada to list of countries that use 915 MHz Meshtastic prompted me to get a couple boards to try, and I had to figure out what frequency. Canada uses the same US902-928 as the US, add it to the list for simplicity. Not sure where to find an "official" reference, but there's a reference here: https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country.html --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cbe76494d..c50a1d67d 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ We currently support three models of radios. **Make sure to get the frequency for your country** -- US/JP/AU/NZ - 915MHz +- US/JP/AU/NZ/CA - 915MHz - CN - 470MHz - EU - 868MHz, 433MHz From 26c43e70910b9980ae59b85941b4c3122e8869af Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 1 Jul 2020 10:22:17 -0700 Subject: [PATCH 13/21] minor docs --- src/screen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/screen.cpp b/src/screen.cpp index 19f1fd4fb..00c361b2f 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -57,6 +57,8 @@ static char btPIN[16] = "888888"; uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C}; +// if defined a pixel will blink to show redraws +// #define SHOW_REDRAWS #ifdef SHOW_REDRAWS static bool heartbeat = false; #endif From da12b93f82528a36cedd473a732e79c8ea06453e Mon Sep 17 00:00:00 2001 From: Slavomir Hustaty Date: Thu, 2 Jul 2020 16:54:24 +0200 Subject: [PATCH 14/21] Update configuration.h https://user-images.githubusercontent.com/1584034/86362734-08525e00-bc76-11ea-8a34-8579d1fa2965.jpg related to issue https://github.com/meshtastic/Meshtastic-device/issues/243 --- src/configuration.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/configuration.h b/src/configuration.h index d0ec7aad3..f362eec96 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -180,8 +180,8 @@ along with this program. If not, see . // This board has different GPS pins than all other boards #undef GPS_RX_PIN #undef GPS_TX_PIN -#define GPS_RX_PIN 12 -#define GPS_TX_PIN 15 +#define GPS_RX_PIN 15 +#define GPS_TX_PIN 12 #elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) // This string must exactly match the case used in release file names or the android updater won't work From 439cdfbb3254c1b4810a3adffa1fba72aabfb86d Mon Sep 17 00:00:00 2001 From: Slavomir Hustaty Date: Fri, 3 Jul 2020 07:41:22 +0200 Subject: [PATCH 15/21] Update configuration.h https://github.com/meshtastic/Meshtastic-device/issues/243#issuecomment-653361142 --- src/configuration.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/configuration.h b/src/configuration.h index f362eec96..d0ec7aad3 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -180,8 +180,8 @@ along with this program. If not, see . // This board has different GPS pins than all other boards #undef GPS_RX_PIN #undef GPS_TX_PIN -#define GPS_RX_PIN 15 -#define GPS_TX_PIN 12 +#define GPS_RX_PIN 12 +#define GPS_TX_PIN 15 #elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) // This string must exactly match the case used in release file names or the android updater won't work From 8d122f36e3ac037cabb96d0993a0bf5c2f3bfdd6 Mon Sep 17 00:00:00 2001 From: Slavomir Hustaty Date: Fri, 3 Jul 2020 07:44:14 +0200 Subject: [PATCH 16/21] Update platformio.ini https://github.com/meshtastic/Meshtastic-device/issues/243#issuecomment-653361142 --- platformio.ini | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/platformio.ini b/platformio.ini index f54ab582f..4e61e9acc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -99,17 +99,16 @@ 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 ; The original TBEAM board without the AXP power chip and a few other changes ; Note: I've heard reports this didn't work. Disabled until someone with a 0.7 can test and debug. -;[env:tbeam0.7] -;extends = esp32_base -;board = ttgo-t-beam -;build_flags = -; ${esp32_base.build_flags} -D TBEAM_V07 +[env:tbeam0.7] +extends = esp32_base +board = ttgo-t-beam +build_flags = + ${esp32_base.build_flags} -D TBEAM_V07 [env:heltec] ;build_type = debug ; to make it possible to step through our jtag debugger From 0f92678c3bf5421fa40f686cdfd4bb337f78b44b Mon Sep 17 00:00:00 2001 From: Slavomir Hustaty Date: Fri, 3 Jul 2020 10:35:42 +0200 Subject: [PATCH 17/21] Update README.md TBeam 0.7 + W.W. LoRa freqs list link --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c50a1d67d..c5d7b1bbf 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ This software is 100% open source and developed by a group of hobbyist experimen We currently support three models of radios. - TTGO T-Beam - + - [T-Beam V0.7 w/ NEO-6M](https://www.aliexpress.com/item/4000574335430.html) - [T-Beam V1.0 w/ NEO-6M - special Meshtastic version](https://www.aliexpress.com/item/4001178678568.html) (Includes built-in OLED display and they have **preinstalled** the meshtastic software) - [T-Beam V1.0 w/ NEO-M8N](https://www.aliexpress.com/item/33047631119.html) (slightly better GPS) - 3D printable cases @@ -43,6 +43,7 @@ We currently support three models of radios. - US/JP/AU/NZ/CA - 915MHz - CN - 470MHz - EU - 868MHz, 433MHz +- full list of LoRa frequencies per region is available [here](https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country.html) Getting a version that includes a screen is optional, but highly recommended. From cda423acab5aa08b969722d9e4124bab1047db1c Mon Sep 17 00:00:00 2001 From: Professr Date: Fri, 3 Jul 2020 02:53:56 -0700 Subject: [PATCH 18/21] Changed GPS DOP display to bars, added satellites display and compass rose --- src/GPSStatus.h | 24 +++++++- src/gps/GPS.h | 2 + src/gps/NEMAGPS.cpp | 12 +++- src/gps/UBloxGPS.cpp | 4 +- src/screen.cpp | 140 +++++++++++++++++++++++++------------------ 5 files changed, 116 insertions(+), 66 deletions(-) diff --git a/src/GPSStatus.h b/src/GPSStatus.h index 8daf97cdc..ed9e0fbc6 100644 --- a/src/GPSStatus.h +++ b/src/GPSStatus.h @@ -17,13 +17,15 @@ namespace meshtastic { int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double int32_t altitude = 0; uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use) + uint32_t heading = 0; + uint32_t numSatellites = 0; public: GPSStatus() { statusType = STATUS_TYPE_GPS; } - GPSStatus( bool hasLock, bool isConnected, int32_t latitude, int32_t longitude, int32_t altitude, uint32_t dop ) : Status() + GPSStatus( bool hasLock, bool isConnected, int32_t latitude, int32_t longitude, int32_t altitude, uint32_t dop, uint32_t heading, uint32_t numSatellites ) : Status() { this->hasLock = hasLock; this->isConnected = isConnected; @@ -31,6 +33,8 @@ namespace meshtastic { this->longitude = longitude; this->altitude = altitude; this->dop = dop; + this->heading = heading; + this->numSatellites = numSatellites; } GPSStatus(const GPSStatus &); GPSStatus &operator=(const GPSStatus &); @@ -70,6 +74,16 @@ namespace meshtastic { return dop; } + uint32_t getHeading() const + { + return heading; + } + + uint32_t getNumSatellites() const + { + return numSatellites; + } + bool matches(const GPSStatus *newStatus) const { return ( @@ -78,7 +92,9 @@ namespace meshtastic { newStatus->latitude != latitude || newStatus->longitude != longitude || newStatus->altitude != altitude || - newStatus->dop != dop + newStatus->dop != dop || + newStatus->heading != heading || + newStatus->numSatellites != numSatellites ); } int updateStatus(const GPSStatus *newStatus) { @@ -93,9 +109,11 @@ namespace meshtastic { longitude = newStatus->longitude; altitude = newStatus->altitude; dop = newStatus->dop; + heading = newStatus->heading; + numSatellites = newStatus->numSatellites; } if(isDirty) { - DEBUG_MSG("New GPS pos lat=%f, lon=%f, alt=%d, pdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2); + DEBUG_MSG("New GPS pos lat=%f, lon=%f, alt=%d, pdop=%f, heading=%f, sats=%d\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5, numSatellites); onNewStatus.notifyObservers(this); } return 0; diff --git a/src/gps/GPS.h b/src/gps/GPS.h index c8b55eb2d..5c52e7cab 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -36,6 +36,8 @@ class GPS : public Observable int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double int32_t altitude = 0; uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use) + uint32_t heading = 0; // Heading of motion, in degrees * 10^-5 + uint32_t numSatellites = 0; bool isConnected = false; // Do we have a GPS we are talking to diff --git a/src/gps/NEMAGPS.cpp b/src/gps/NEMAGPS.cpp index 4858806ca..606d21eed 100644 --- a/src/gps/NEMAGPS.cpp +++ b/src/gps/NEMAGPS.cpp @@ -54,12 +54,18 @@ void NEMAGPS::loop() longitude = toDegInt(loc.lng); } // Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it - if(reader.hdop.isValid()) { + if (reader.hdop.isValid()) { dop = reader.hdop.value(); } + if (reader.course.isValid()) { + heading = reader.course.value() * 1e3; //Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5 + } + if (reader.satellites.isValid()) { + numSatellites = reader.satellites.value(); + } // 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\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2); + 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) @@ -67,7 +73,7 @@ void NEMAGPS::loop() } // Notify any status instances that are observing us - const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop); + const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites); newStatus.notifyObservers(&status); } } \ No newline at end of file diff --git a/src/gps/UBloxGPS.cpp b/src/gps/UBloxGPS.cpp index 7c940983d..6c2660671 100644 --- a/src/gps/UBloxGPS.cpp +++ b/src/gps/UBloxGPS.cpp @@ -116,6 +116,8 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s longitude = ublox.getLongitude(0); altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it + heading = ublox.getHeading(0); + numSatellites = ublox.getSIV(0); // bogus lat lon is reported as 0 or 0 (can be bogus just for one) // Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg! @@ -129,7 +131,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s wantNewLocation = true; // Notify any status instances that are observing us - const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop); + const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites); newStatus.notifyObservers(&status); // Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over diff --git a/src/screen.cpp b/src/screen.cpp index 74f0065df..11cee0bad 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -55,8 +55,10 @@ static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES]; static uint32_t targetFramerate = IDLE_FRAMERATE; static char btPIN[16] = "888888"; -uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C }; -static bool heartbeat = false; +uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C }; +uint8_t imgSatellite[8] = { 0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70 }; + +uint32_t dopThresholds[5] = { 2000, 1000, 500, 200, 100 }; static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { @@ -210,38 +212,39 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *no // Draw GPS status summary static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps) { - if (!gps->getIsConnected()) { + if (!gps->getIsConnected()) + { display->drawString(x, y - 2, "No GPS"); return; } display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty); - if (!gps->getHasLock()) { + if (!gps->getHasLock()) + { display->drawString(x + 8, y - 2, "No sats"); return; - } - if (gps->getDOP() <= 100) { - display->drawString(x + 8, y - 2, "Ideal"); - return; - } - if (gps->getDOP() <= 200) { - display->drawString(x + 8, y - 2, "Exc."); - return; - } - if (gps->getDOP() <= 500) { - display->drawString(x + 8, y - 2, "Good"); - return; - } - if (gps->getDOP() <= 1000) { - display->drawString(x + 8, y - 2, "Mod."); - return; - } - if (gps->getDOP() <= 2000) { - display->drawString(x + 8, y - 2, "Fair"); - return; - } - if (gps->getDOP() > 0) { - display->drawString(x + 8, y - 2, "Poor"); - return; + } + else + { + char satsString[3]; + uint8_t bar[2] = { 0 }; + + //Draw DOP signal bars + for(int i = 0; i < 5; i++) + { + if (gps->getDOP() <= dopThresholds[i]) + bar[0] = ~((1 << (5 - i)) - 1); + else + bar[0] = 0b10000000; + //bar[1] = bar[0]; + display->drawFastImage(x + 9 + (i * 2), y, 2, 8, bar); + } + + //Draw satellite image + display->drawFastImage(x + 24, y, 8, 8, imgSatellite); + + //Draw the number of satellites + sprintf(satsString, "%d", gps->getNumSatellites()); + display->drawString(x + 34, y - 2, satsString); } } @@ -384,28 +387,41 @@ static bool hasPosition(NodeInfo *n) static size_t nodeIndex; static int8_t prevFrame = -1; -// Draw the compass and arrow pointing to location -static void drawCompass(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian) +// Draw the arrow pointing to a node's location +static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian) { - // display->drawXbm(compassX, compassY, compass_width, compass_height, - // (const uint8_t *)compass_bits); - Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f; Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY); - Point *points[] = {&tip, &tail, &leftArrow, &rightArrow}; + + Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow}; for (int i = 0; i < 4; i++) { - points[i]->rotate(headingRadian); - points[i]->scale(COMPASS_DIAM * 0.6); - points[i]->translate(compassX, compassY); + arrowPoints[i]->rotate(headingRadian); + arrowPoints[i]->scale(COMPASS_DIAM * 0.6); + arrowPoints[i]->translate(compassX, compassY); } drawLine(display, tip, tail); drawLine(display, leftArrow, tip); drawLine(display, rightArrow, tip); +} - display->drawCircle(compassX, compassY, COMPASS_DIAM / 2); +// Draw the compass heading +static void drawCompassHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading) +{ + Point N1(-0.04f, -0.65f), N2( 0.04f, -0.65f); + Point N3(-0.04f, -0.55f), N4( 0.04f, -0.55f); + Point *rosePoints[] = {&N1, &N2, &N3, &N4}; + + for (int i = 0; i < 4; i++) { + rosePoints[i]->rotate(myHeading); + rosePoints[i]->scale(COMPASS_DIAM); + rosePoints[i]->translate(compassX, compassY); + } + drawLine(display, N1, N3); + drawLine(display, N2, N4); + drawLine(display, N1, N4); } /// Convert an integer GPS coords to a floating point @@ -459,29 +475,35 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ const char *fields[] = {username, distStr, signalStr, lastStr, NULL}; // coordinates for the center of the compass/circle - int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2; + int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 5, compassY = y + SCREEN_HEIGHT / 2; - if (ourNode && hasPosition(ourNode) && hasPosition(node)) { // display direction toward node - Position &op = ourNode->position, &p = node->position; - float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); - if (d < 2000) - snprintf(distStr, sizeof(distStr), "%.0f m", d); - else - snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000); + if(ourNode && hasPosition(ourNode)) + { + Position &op = ourNode->position; + float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); + drawCompassHeading(display, compassX, compassY, myHeading); - // FIXME, also keep the guess at the operators heading and add/substract - // it. currently we don't do this and instead draw north up only. - float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); - float myHeading = estimatedHeading(DegD(p.latitude_i), DegD(p.longitude_i)); - headingRadian = bearingToOther - myHeading; - drawCompass(display, compassX, compassY, headingRadian); - } else { // direction to node is unknown so display question mark - // Debug info for gps lock errors - // DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node)); + if(hasPosition(node)) { // display direction toward node + Position &p = node->position; + float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); + if (d < 2000) + snprintf(distStr, sizeof(distStr), "%.0f m", d); + else + snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000); - display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?"); - display->drawCircle(compassX, compassY, COMPASS_DIAM / 2); + // FIXME, also keep the guess at the operators heading and add/substract + // it. currently we don't do this and instead draw north up only. + float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); + headingRadian = bearingToOther - myHeading; + drawNodeHeading(display, compassX, compassY, headingRadian); + } else { // direction to node is unknown so display question mark + // Debug info for gps lock errors + // DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node)); + display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?"); + } } + display->drawCircle(compassX, compassY, COMPASS_DIAM / 2); + // Must be after distStr is populated drawColumns(display, x, y, fields); @@ -758,7 +780,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 // Display nodes status drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus); // Display GPS status - drawGPS(display, x + (SCREEN_WIDTH * 0.66), y + 2, gpsStatus); + drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus); } display->drawString(x, y + FONT_HEIGHT, channelStr); @@ -787,7 +809,7 @@ void Screen::adjustBrightness() } int Screen::handleStatusUpdate(const Status *arg) { - DEBUG_MSG("Screen got status update %d\n", arg->getStatusType()); + //DEBUG_MSG("Screen got status update %d\n", arg->getStatusType()); switch(arg->getStatusType()) { case STATUS_TYPE_NODE: From 7bd4940ed8e81c8bcf8065dcb267d5f1adc7f6ae Mon Sep 17 00:00:00 2001 From: geeksville Date: Sun, 5 Jul 2020 12:04:15 -0700 Subject: [PATCH 19/21] fix #254 --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index f54ab582f..03b07b0e8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -74,7 +74,7 @@ lib_deps = Wire ; explicitly needed here because the AXP202 library forgets to add it https://github.com/meshtastic/arduino-fsm.git https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git - https://github.com/meshtastic/RadioLib.git + https://github.com/meshtastic/RadioLib.git#6aa38a85856012c99c4e9b4e7cee35e37671a4bc https://github.com/meshtastic/TinyGPSPlus.git ; Common settings for ESP targes, mixin with extends = esp32_base @@ -90,7 +90,7 @@ build_flags = # board_build.ldscript = linker/esp32.extram.bss.ld lib_ignore = segger_rtt platform_packages = - framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#f26c4f96fefd13ed0ed042e27954f8aba6328f6b + framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#71ed4002c953d8c87f44ed27e34fe0735f99013e ; The 1.0 release of the TBEAM board [env:tbeam] From 6f7f540c7923b5d9338a0c4841e81623c2002e22 Mon Sep 17 00:00:00 2001 From: Ellie Hussey Date: Sun, 5 Jul 2020 17:03:12 -0700 Subject: [PATCH 20/21] Added the option for forced NodeStatus updates on user change or text message, tweaked compass (#256) --- src/NodeStatus.h | 14 ++++++++++++-- src/mesh/NodeDB.cpp | 10 +++++++--- src/mesh/NodeDB.h | 4 ++-- src/screen.cpp | 23 ++++++++++++++++------- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/NodeStatus.h b/src/NodeStatus.h index 275199467..dc567fd2f 100644 --- a/src/NodeStatus.h +++ b/src/NodeStatus.h @@ -15,13 +15,17 @@ namespace meshtastic { uint8_t numOnline = 0; uint8_t numTotal = 0; + uint8_t lastNumTotal = 0; + public: + bool forceUpdate = false; NodeStatus() { statusType = STATUS_TYPE_NODE; } - NodeStatus( uint8_t numOnline, uint8_t numTotal ) : Status() + NodeStatus( uint8_t numOnline, uint8_t numTotal, bool forceUpdate = false ) : Status() { + this->forceUpdate = forceUpdate; this->numOnline = numOnline; this->numTotal = numTotal; } @@ -43,6 +47,11 @@ namespace meshtastic { return numTotal; } + uint8_t getLastNumTotal() const + { + return lastNumTotal; + } + bool matches(const NodeStatus *newStatus) const { return ( @@ -52,6 +61,7 @@ namespace meshtastic { } int updateStatus(const NodeStatus *newStatus) { // Only update the status if values have actually changed + lastNumTotal = numTotal; bool isDirty; { isDirty = matches(newStatus); @@ -59,7 +69,7 @@ namespace meshtastic { numOnline = newStatus->getNumOnline(); numTotal = newStatus->getNumTotal(); } - if(isDirty) { + if(isDirty || newStatus->forceUpdate) { DEBUG_MSG("Node status update: %d online, %d total\n", numOnline, numTotal); onNewStatus.notifyObservers(this); } diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 00d3f5699..fe816a202 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -339,11 +339,8 @@ void NodeDB::updateFrom(const MeshPacket &mp) const SubPacket &p = mp.decoded; DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time); - int oldNumNodes = *numNodes; NodeInfo *info = getOrCreateNode(mp.from); - notifyObservers(); - if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen info->has_position = true; // at least the time is valid info->position.time = mp.rx_time; @@ -359,6 +356,7 @@ void NodeDB::updateFrom(const MeshPacket &mp) info->position.time = oldtime; info->has_position = true; updateGUIforNode = info; + notifyObservers(true); //Force an update whether or not our node counts have changed break; } @@ -373,6 +371,7 @@ void NodeDB::updateFrom(const MeshPacket &mp) devicestate.has_rx_text_message = true; updateTextMessage = true; powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); + notifyObservers(true); //Force an update whether or not our node counts have changed } } break; @@ -391,6 +390,7 @@ void NodeDB::updateFrom(const MeshPacket &mp) if (changed) { updateGUIforNode = info; powerFSM.trigger(EVENT_NODEDB_UPDATED); + notifyObservers(true); //Force an update whether or not our node counts have changed // Not really needed - we will save anyways when we go to sleep // We just changed something important about the user, store our DB @@ -398,6 +398,10 @@ void NodeDB::updateFrom(const MeshPacket &mp) } break; } + + default: { + notifyObservers(); //If the node counts have changed, notify observers + } } } } diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index ec967de31..2465c2021 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -95,9 +95,9 @@ class NodeDB NodeInfo *getOrCreateNode(NodeNum n); /// Notify observers of changes to the DB - void notifyObservers() { + void notifyObservers(bool forceUpdate = false) { // Notify observers of the current node state - const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineNodes(), getNumNodes()); + const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineNodes(), getNumNodes(), forceUpdate); newStatus.notifyObservers(&status); } diff --git a/src/screen.cpp b/src/screen.cpp index a0b78c027..71005288f 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -479,6 +479,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ // coordinates for the center of the compass/circle int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 5, compassY = y + SCREEN_HEIGHT / 2; + bool hasNodeHeading = false; if(ourNode && hasPosition(ourNode)) { @@ -486,7 +487,10 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); drawCompassHeading(display, compassX, compassY, myHeading); - if(hasPosition(node)) { // display direction toward node + if(hasPosition(node)) + { + // display direction toward node + hasNodeHeading = true; Position &p = node->position; float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); if (d < 2000) @@ -499,12 +503,13 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); headingRadian = bearingToOther - myHeading; drawNodeHeading(display, compassX, compassY, headingRadian); - } else { // direction to node is unknown so display question mark - // Debug info for gps lock errors - // DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node)); - display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?"); - } + } } + if(!hasNodeHeading) + // direction to node is unknown so display question mark + // Debug info for gps lock errors + // DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node)); + display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?"); display->drawCircle(compassX, compassY, COMPASS_DIAM / 2); @@ -819,7 +824,11 @@ int Screen::handleStatusUpdate(const Status *arg) switch(arg->getStatusType()) { case STATUS_TYPE_NODE: - setFrames(); + if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) + setFrames(); + prevFrame = -1; + nodeDB.updateGUI = false; + nodeDB.updateTextMessage = false; break; } setPeriod(1); // Update the screen right away From ccadb6a43de9db480507b97bc61d97cd08c32473 Mon Sep 17 00:00:00 2001 From: Professr Date: Sun, 5 Jul 2020 19:56:57 -0700 Subject: [PATCH 21/21] Added sinceLastSeen check to pings generated by node UI --- src/screen.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/screen.cpp b/src/screen.cpp index 71005288f..0db66b2c0 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -444,10 +444,13 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes(); n = nodeDB.getNodeByIndex(nodeIndex); } - - // We just changed to a new node screen, ask that node for updated state displayedNodeNum = n->num; - service.sendNetworkPing(displayedNodeNum, true); + + // We just changed to a new node screen, ask that node for updated state if it's older than 2 minutes + if(sinceLastSeen(n) > 120) + { + service.sendNetworkPing(displayedNodeNum, true); + } } NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);