mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-29 11:01:15 +00:00
merged with master
This commit is contained in:
commit
9803141fe7
@ -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.
|
We currently support three models of radios.
|
||||||
|
|
||||||
- TTGO T-Beam
|
- 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-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)
|
- [T-Beam V1.0 w/ NEO-M8N](https://www.aliexpress.com/item/33047631119.html) (slightly better GPS)
|
||||||
- 3D printable cases
|
- 3D printable cases
|
||||||
@ -40,9 +40,10 @@ We currently support three models of radios.
|
|||||||
|
|
||||||
**Make sure to get the frequency for your country**
|
**Make sure to get the frequency for your country**
|
||||||
|
|
||||||
- US/JP/AU/NZ - 915MHz
|
- US/JP/AU/NZ/CA - 915MHz
|
||||||
- CN - 470MHz
|
- CN - 470MHz
|
||||||
- EU - 868MHz, 433MHz
|
- 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.
|
Getting a version that includes a screen is optional, but highly recommended.
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
export VERSION=0.7.9
|
export VERSION=0.7.10
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
You probably don't care about this section - skip to the next one.
|
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
|
- NRF52 BLE support
|
||||||
|
|
||||||
# Medium priority
|
# Medium priority
|
||||||
@ -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.
|
- 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
|
- read the PMU battery fault indicators and blink/led/warn user on screen
|
||||||
- discard very old nodedb records (> 1wk)
|
- discard very old nodedb records (> 1wk)
|
||||||
- add a watchdog timer
|
|
||||||
- handle millis() rollover in GPS.getTime - otherwise we will break after 50 days
|
- handle millis() rollover in GPS.getTime - otherwise we will break after 50 days
|
||||||
- report esp32 device code bugs back to the mothership via android
|
- 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)
|
- change BLE bonding to something more secure. see comment by pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND)
|
||||||
|
@ -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 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
|
- read @cyclomies long email with good ideas on optimizations and reply
|
||||||
- Remove NodeNum assignment algorithm (now that we use 4 byte node nums)
|
- DONE 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 make android app warn if firmware is too old or too new to talk to
|
||||||
- change nodenums and packetids in protobuf to be fixed32
|
- change nodenums and packetids in protobuf to be fixed32
|
||||||
- low priority: think more careful about reliable retransmit intervals
|
- low priority: think more careful about reliable retransmit intervals
|
||||||
- make ReliableRouter.pending threadsafe
|
- make ReliableRouter.pending threadsafe
|
||||||
|
@ -23,6 +23,8 @@ default_envs = tbeam ; Note: the github actions CI test build can't yet build NR
|
|||||||
|
|
||||||
[env]
|
[env]
|
||||||
|
|
||||||
|
framework = arduino
|
||||||
|
|
||||||
; customize the partition table
|
; customize the partition table
|
||||||
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
||||||
board_build.partitions = partition-table.csv
|
board_build.partitions = partition-table.csv
|
||||||
@ -72,13 +74,12 @@ lib_deps =
|
|||||||
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||||
https://github.com/meshtastic/arduino-fsm.git
|
https://github.com/meshtastic/arduino-fsm.git
|
||||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.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
|
https://github.com/meshtastic/TinyGPSPlus.git
|
||||||
|
|
||||||
; Common settings for ESP targes, mixin with extends = esp32_base
|
; Common settings for ESP targes, mixin with extends = esp32_base
|
||||||
[esp32_base]
|
[esp32_base]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
framework = arduino
|
|
||||||
src_filter =
|
src_filter =
|
||||||
${env.src_filter} -<nrf52/>
|
${env.src_filter} -<nrf52/>
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
@ -89,7 +90,7 @@ build_flags =
|
|||||||
# board_build.ldscript = linker/esp32.extram.bss.ld
|
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||||
lib_ignore = segger_rtt
|
lib_ignore = segger_rtt
|
||||||
platform_packages =
|
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
|
; The 1.0 release of the TBEAM board
|
||||||
[env:tbeam]
|
[env:tbeam]
|
||||||
@ -98,17 +99,16 @@ board = ttgo-t-beam
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
https://github.com/meshtastic/AXP202X_Library.git
|
https://github.com/meshtastic/AXP202X_Library.git
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_base.build_flags} -D TBEAM_V10
|
${esp32_base.build_flags} -D TBEAM_V10
|
||||||
|
|
||||||
; The original TBEAM board without the AXP power chip and a few other changes
|
; 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.
|
; Note: I've heard reports this didn't work. Disabled until someone with a 0.7 can test and debug.
|
||||||
;[env:tbeam0.7]
|
[env:tbeam0.7]
|
||||||
;extends = esp32_base
|
extends = esp32_base
|
||||||
;board = ttgo-t-beam
|
board = ttgo-t-beam
|
||||||
;build_flags =
|
build_flags =
|
||||||
; ${esp32_base.build_flags} -D TBEAM_V07
|
${esp32_base.build_flags} -D TBEAM_V07
|
||||||
|
|
||||||
[env:heltec]
|
[env:heltec]
|
||||||
;build_type = debug ; to make it possible to step through our jtag debugger
|
;build_type = debug ; to make it possible to step through our jtag debugger
|
||||||
@ -133,7 +133,6 @@ build_flags =
|
|||||||
; For more details see my post in the forum.
|
; For more details see my post in the forum.
|
||||||
[env:cubecellplus]
|
[env:cubecellplus]
|
||||||
platform = https://github.com/HelTecAutomation/platform-asrmicro650x.git ; we use top-of-tree because stable version has too many bugs - asrmicro650x
|
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
|
board = cubecell_board_plus
|
||||||
; FIXME, bug in cubecell arduino - they are supposed to set ARDUINO
|
; FIXME, bug in cubecell arduino - they are supposed to set ARDUINO
|
||||||
build_flags = ${env.build_flags} -DARDUINO=100 -Isrc/cubecell
|
build_flags = ${env.build_flags} -DARDUINO=100 -Isrc/cubecell
|
||||||
@ -143,7 +142,6 @@ src_filter =
|
|||||||
; Common settings for NRF52 based targets
|
; Common settings for NRF52 based targets
|
||||||
[nrf52_base]
|
[nrf52_base]
|
||||||
platform = nordicnrf52
|
platform = nordicnrf52
|
||||||
framework = arduino
|
|
||||||
debug_tool = jlink
|
debug_tool = jlink
|
||||||
build_type = debug ; I'm debugging with ICE a lot now
|
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)
|
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
|
||||||
|
126
src/GPSStatus.h
Normal file
126
src/GPSStatus.h
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "Status.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
namespace meshtastic {
|
||||||
|
|
||||||
|
/// Describes the state of the GPS system.
|
||||||
|
class GPSStatus : public Status
|
||||||
|
{
|
||||||
|
|
||||||
|
private:
|
||||||
|
CallbackObserver<GPSStatus, const GPSStatus *> statusObserver = CallbackObserver<GPSStatus, const GPSStatus *>(this, &GPSStatus::updateStatus);
|
||||||
|
|
||||||
|
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)
|
||||||
|
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, uint32_t heading, uint32_t numSatellites ) : Status()
|
||||||
|
{
|
||||||
|
this->hasLock = hasLock;
|
||||||
|
this->isConnected = isConnected;
|
||||||
|
this->latitude = latitude;
|
||||||
|
this->longitude = longitude;
|
||||||
|
this->altitude = altitude;
|
||||||
|
this->dop = dop;
|
||||||
|
this->heading = heading;
|
||||||
|
this->numSatellites = numSatellites;
|
||||||
|
}
|
||||||
|
GPSStatus(const GPSStatus &);
|
||||||
|
GPSStatus &operator=(const GPSStatus &);
|
||||||
|
|
||||||
|
void observe(Observable<const GPSStatus *> *source)
|
||||||
|
{
|
||||||
|
statusObserver.observe(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getHasLock() const
|
||||||
|
{
|
||||||
|
return hasLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getIsConnected() const
|
||||||
|
{
|
||||||
|
return isConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t getLatitude() const
|
||||||
|
{
|
||||||
|
return latitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t getLongitude() const
|
||||||
|
{
|
||||||
|
return longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t getAltitude() const
|
||||||
|
{
|
||||||
|
return altitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getDOP() const
|
||||||
|
{
|
||||||
|
return dop;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getHeading() const
|
||||||
|
{
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getNumSatellites() const
|
||||||
|
{
|
||||||
|
return numSatellites;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matches(const GPSStatus *newStatus) const
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
newStatus->hasLock != hasLock ||
|
||||||
|
newStatus->isConnected != isConnected ||
|
||||||
|
newStatus->latitude != latitude ||
|
||||||
|
newStatus->longitude != longitude ||
|
||||||
|
newStatus->altitude != altitude ||
|
||||||
|
newStatus->dop != dop ||
|
||||||
|
newStatus->heading != heading ||
|
||||||
|
newStatus->numSatellites != numSatellites
|
||||||
|
);
|
||||||
|
}
|
||||||
|
int updateStatus(const GPSStatus *newStatus) {
|
||||||
|
// Only update the status if values have actually changed
|
||||||
|
bool isDirty;
|
||||||
|
{
|
||||||
|
isDirty = matches(newStatus);
|
||||||
|
initialized = true;
|
||||||
|
hasLock = newStatus->hasLock;
|
||||||
|
isConnected = newStatus->isConnected;
|
||||||
|
latitude = newStatus->latitude;
|
||||||
|
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, heading=%f, sats=%d\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5, numSatellites);
|
||||||
|
onNewStatus.notifyObservers(this);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extern meshtastic::GPSStatus *gpsStatus;
|
83
src/NodeStatus.h
Normal file
83
src/NodeStatus.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "Status.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
namespace meshtastic {
|
||||||
|
|
||||||
|
/// Describes the state of the NodeDB system.
|
||||||
|
class NodeStatus : public Status
|
||||||
|
{
|
||||||
|
|
||||||
|
private:
|
||||||
|
CallbackObserver<NodeStatus, const NodeStatus *> statusObserver = CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
|
||||||
|
|
||||||
|
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, bool forceUpdate = false ) : Status()
|
||||||
|
{
|
||||||
|
this->forceUpdate = forceUpdate;
|
||||||
|
this->numOnline = numOnline;
|
||||||
|
this->numTotal = numTotal;
|
||||||
|
}
|
||||||
|
NodeStatus(const NodeStatus &);
|
||||||
|
NodeStatus &operator=(const NodeStatus &);
|
||||||
|
|
||||||
|
void observe(Observable<const NodeStatus *> *source)
|
||||||
|
{
|
||||||
|
statusObserver.observe(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getNumOnline() const
|
||||||
|
{
|
||||||
|
return numOnline;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getNumTotal() const
|
||||||
|
{
|
||||||
|
return numTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getLastNumTotal() const
|
||||||
|
{
|
||||||
|
return lastNumTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matches(const NodeStatus *newStatus) const
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
newStatus->getNumOnline() != numOnline ||
|
||||||
|
newStatus->getNumTotal() != numTotal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
int updateStatus(const NodeStatus *newStatus) {
|
||||||
|
// Only update the status if values have actually changed
|
||||||
|
lastNumTotal = numTotal;
|
||||||
|
bool isDirty;
|
||||||
|
{
|
||||||
|
isDirty = matches(newStatus);
|
||||||
|
initialized = true;
|
||||||
|
numOnline = newStatus->getNumOnline();
|
||||||
|
numTotal = newStatus->getNumTotal();
|
||||||
|
}
|
||||||
|
if(isDirty || newStatus->forceUpdate) {
|
||||||
|
DEBUG_MSG("Node status update: %d online, %d total\n", numOnline, numTotal);
|
||||||
|
onNewStatus.notifyObservers(this);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extern meshtastic::NodeStatus *nodeStatus;
|
179
src/Power.cpp
Normal file
179
src/Power.cpp
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
#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();
|
||||||
|
concurrency::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()
|
||||||
|
{
|
||||||
|
bool hasBattery = axp.isBatteryConnect();
|
||||||
|
int batteryVoltageMv = 0;
|
||||||
|
uint8_t batteryChargePercent = 0;
|
||||||
|
if (hasBattery) {
|
||||||
|
batteryVoltageMv = axp.getBattVoltage();
|
||||||
|
// If the AXP192 returns a valid battery percentage, use it
|
||||||
|
if (axp.getBattPercentage() >= 0) {
|
||||||
|
batteryChargePercent = axp.getBattPercentage();
|
||||||
|
} else {
|
||||||
|
// If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error
|
||||||
|
// In that case, we compute an estimate of the charge percent based on maximum and minimum voltages defined in power.h
|
||||||
|
batteryChargePercent = clamp((int)(((batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), 0, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify any status instances that are observing us
|
||||||
|
const meshtastic::PowerStatus powerStatus = meshtastic::PowerStatus(hasBattery, axp.isVBUSPlug(), axp.isChargeing(), batteryVoltageMv, batteryChargePercent);
|
||||||
|
newStatus.notifyObservers(&powerStatus);
|
||||||
|
|
||||||
|
// If we have a battery at all and it is less than 10% full, force deep sleep
|
||||||
|
if (powerStatus.getHasBattery() && !powerStatus.getHasUSB() &&
|
||||||
|
axp.getBattVoltage() < MIN_BAT_MILLIVOLTS)
|
||||||
|
powerFSM.trigger(EVENT_LOW_BATTERY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Power::doTask()
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
103
src/PowerStatus.h
Normal file
103
src/PowerStatus.h
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "Status.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
namespace meshtastic {
|
||||||
|
|
||||||
|
/// Describes the state of the GPS system.
|
||||||
|
class PowerStatus : public Status
|
||||||
|
{
|
||||||
|
|
||||||
|
private:
|
||||||
|
CallbackObserver<PowerStatus, const PowerStatus *> statusObserver = CallbackObserver<PowerStatus, const PowerStatus *>(this, &PowerStatus::updateStatus);
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
uint8_t batteryChargePercent;
|
||||||
|
/// Whether USB is connected
|
||||||
|
bool hasUSB;
|
||||||
|
/// Whether we are charging the battery
|
||||||
|
bool isCharging;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
PowerStatus() {
|
||||||
|
statusType = STATUS_TYPE_POWER;
|
||||||
|
}
|
||||||
|
PowerStatus( bool hasBattery, bool hasUSB, bool isCharging, int batteryVoltageMv, uint8_t batteryChargePercent ) : Status()
|
||||||
|
{
|
||||||
|
this->hasBattery = hasBattery;
|
||||||
|
this->hasUSB = hasUSB;
|
||||||
|
this->isCharging = isCharging;
|
||||||
|
this->batteryVoltageMv = batteryVoltageMv;
|
||||||
|
this->batteryChargePercent = batteryChargePercent;
|
||||||
|
}
|
||||||
|
PowerStatus(const PowerStatus &);
|
||||||
|
PowerStatus &operator=(const PowerStatus &);
|
||||||
|
|
||||||
|
void observe(Observable<const PowerStatus *> *source)
|
||||||
|
{
|
||||||
|
statusObserver.observe(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getHasBattery() const
|
||||||
|
{
|
||||||
|
return hasBattery;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getHasUSB() const
|
||||||
|
{
|
||||||
|
return hasUSB;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getIsCharging() const
|
||||||
|
{
|
||||||
|
return isCharging;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getBatteryVoltageMv() const
|
||||||
|
{
|
||||||
|
return batteryVoltageMv;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getBatteryChargePercent() const
|
||||||
|
{
|
||||||
|
return batteryChargePercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matches(const PowerStatus *newStatus) const
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
newStatus->getHasBattery() != hasBattery ||
|
||||||
|
newStatus->getHasUSB() != hasUSB ||
|
||||||
|
newStatus->getBatteryVoltageMv() != batteryVoltageMv
|
||||||
|
);
|
||||||
|
}
|
||||||
|
int updateStatus(const PowerStatus *newStatus) {
|
||||||
|
// Only update the status if values have actually changed
|
||||||
|
bool isDirty;
|
||||||
|
{
|
||||||
|
isDirty = matches(newStatus);
|
||||||
|
initialized = true;
|
||||||
|
hasBattery = newStatus->getHasBattery();
|
||||||
|
batteryVoltageMv = newStatus->getBatteryVoltageMv();
|
||||||
|
batteryChargePercent = newStatus->getBatteryChargePercent();
|
||||||
|
hasUSB = newStatus->getHasUSB();
|
||||||
|
isCharging = newStatus->getIsCharging();
|
||||||
|
}
|
||||||
|
if(isDirty) {
|
||||||
|
DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent);
|
||||||
|
onNewStatus.notifyObservers(this);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extern meshtastic::PowerStatus *powerStatus;
|
72
src/Status.h
Normal file
72
src/Status.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Observer.h"
|
||||||
|
|
||||||
|
// Constants for the various status types, so we can tell subclass instances apart
|
||||||
|
#define STATUS_TYPE_BASE 0
|
||||||
|
#define STATUS_TYPE_POWER 1
|
||||||
|
#define STATUS_TYPE_GPS 2
|
||||||
|
#define STATUS_TYPE_NODE 3
|
||||||
|
|
||||||
|
|
||||||
|
namespace meshtastic
|
||||||
|
{
|
||||||
|
|
||||||
|
// A base class for observable status
|
||||||
|
class Status
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
// Allows us to observe an Observable
|
||||||
|
CallbackObserver<Status, const Status *> statusObserver = CallbackObserver<Status, const Status *>(this, &Status::updateStatus);
|
||||||
|
bool initialized = false;
|
||||||
|
// Workaround for no typeid support
|
||||||
|
int statusType;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Allows us to generate observable events
|
||||||
|
Observable<const Status *> onNewStatus;
|
||||||
|
|
||||||
|
// Enable polymorphism ?
|
||||||
|
virtual ~Status() = default;
|
||||||
|
|
||||||
|
Status() {
|
||||||
|
if (!statusType)
|
||||||
|
{
|
||||||
|
statusType = STATUS_TYPE_BASE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent object copy/move
|
||||||
|
Status(const Status &) = delete;
|
||||||
|
Status &operator=(const Status &) = delete;
|
||||||
|
|
||||||
|
// Start observing a source of data
|
||||||
|
void observe(Observable<const Status *> *source)
|
||||||
|
{
|
||||||
|
statusObserver.observe(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines whether or not existing data matches the data in another Status instance
|
||||||
|
bool matches(const Status *otherStatus) const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInitialized() const
|
||||||
|
{
|
||||||
|
return initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getStatusType() const
|
||||||
|
{
|
||||||
|
return statusType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the Observable we're observing generates a new notification
|
||||||
|
int updateStatus(const Status *newStatus)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
0
src/StatusHandler.h
Normal file
0
src/StatusHandler.h
Normal file
@ -1,4 +1,5 @@
|
|||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
namespace concurrency {
|
namespace concurrency {
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "freertosinc.h"
|
#include "freertosinc.h"
|
||||||
|
#include "esp_task_wdt.h"
|
||||||
|
|
||||||
namespace concurrency {
|
namespace concurrency {
|
||||||
|
|
||||||
@ -30,8 +31,26 @@ class Thread
|
|||||||
*/
|
*/
|
||||||
virtual void doRun() = 0;
|
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:
|
private:
|
||||||
static void callRun(void *_this);
|
static void callRun(void *_this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace concurrency
|
} // namespace concurrency
|
||||||
|
@ -5,21 +5,28 @@ namespace concurrency {
|
|||||||
|
|
||||||
void WorkerThread::doRun()
|
void WorkerThread::doRun()
|
||||||
{
|
{
|
||||||
|
startWatchdog();
|
||||||
|
|
||||||
while (!wantExit) {
|
while (!wantExit) {
|
||||||
|
stopWatchdog();
|
||||||
block();
|
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
|
#ifdef DEBUG_STACK
|
||||||
static uint32_t lastPrint = 0;
|
static uint32_t lastPrint = 0;
|
||||||
if (timing::millis() - lastPrint > 10 * 1000L) {
|
if (timing::millis() - lastPrint > 10 * 1000L) {
|
||||||
lastPrint = timing::millis();
|
lastPrint = timing::millis();
|
||||||
uint32_t taskHandle = reinterpret_cast<uint32_t>(xTaskGetCurrentTaskHandle());
|
meshtastic::printThreadInfo("net");
|
||||||
DEBUG_MSG("printThreadInfo(%s) task: %" PRIx32 " core id: %u min free stack: %u\n", "thread", taskHandle, xPortGetCoreID(),
|
|
||||||
uxTaskGetStackHighWaterMark(nullptr));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
loop();
|
loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopWatchdog();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace concurrency
|
} // namespace concurrency
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
#include "MeshBluetoothService.h"
|
#include "MeshBluetoothService.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "esp_task_wdt.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "power.h"
|
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include "utils.h"
|
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
bool bluetoothOn;
|
bool bluetoothOn;
|
||||||
|
|
||||||
@ -60,111 +60,6 @@ void getMacAddr(uint8_t *dmac)
|
|||||||
assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
|
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() {
|
static void printBLEinfo() {
|
||||||
int dev_num = esp_ble_get_bond_device_num();
|
int dev_num = esp_ble_get_bond_device_num();
|
||||||
@ -190,9 +85,15 @@ void esp32Setup()
|
|||||||
|
|
||||||
// enableModemSleep();
|
// enableModemSleep();
|
||||||
|
|
||||||
#ifdef AXP192_SLAVE_ADDRESS
|
// Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any
|
||||||
axp192Init();
|
// false positives. The wait-to-sleep timeout for shutting down radios is 30 secs, so pick 45 for now.
|
||||||
#endif
|
#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
|
#if 0
|
||||||
@ -215,51 +116,12 @@ uint32_t axpDebugRead()
|
|||||||
Periodic axpDebugOutput(axpDebugRead);
|
Periodic axpDebugOutput(axpDebugRead);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/// loop code specific to ESP32 targets
|
/// loop code specific to ESP32 targets
|
||||||
void esp32Loop()
|
void esp32Loop()
|
||||||
{
|
{
|
||||||
|
esp_task_wdt_reset(); // service our app level watchdog
|
||||||
loopBLE();
|
loopBLE();
|
||||||
|
|
||||||
// for debug printing
|
// for debug printing
|
||||||
// radio.radioIf.canSleep();
|
// 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
|
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
|
#include "GPSStatus.h"
|
||||||
|
#include "../concurrency/PeriodicTask.h"
|
||||||
#include "sys/time.h"
|
#include "sys/time.h"
|
||||||
|
|
||||||
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
||||||
@ -34,11 +36,15 @@ class GPS : public Observable<void *>
|
|||||||
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
|
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
|
||||||
int32_t altitude = 0;
|
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 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
|
bool isConnected = false; // Do we have a GPS we are talking to
|
||||||
|
|
||||||
virtual ~GPS() {}
|
virtual ~GPS() {}
|
||||||
|
|
||||||
|
Observable<const meshtastic::GPSStatus *> newStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if we succeeded
|
* Returns true if we succeeded
|
||||||
*/
|
*/
|
||||||
|
@ -55,16 +55,26 @@ void NEMAGPS::loop()
|
|||||||
longitude = toDegInt(loc.lng);
|
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
|
// 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();
|
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
|
// 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
|
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
|
||||||
if (hasValidLocation)
|
if (hasValidLocation)
|
||||||
notifyObservers(NULL);
|
notifyObservers(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify any status instances that are observing us
|
||||||
|
const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
|
||||||
|
newStatus.notifyObservers(&status);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -116,7 +116,8 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
longitude = ublox.getLongitude(0);
|
longitude = ublox.getLongitude(0);
|
||||||
altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters
|
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
|
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);
|
heading = ublox.getHeading(0);
|
||||||
|
numSatellites = ublox.getSIV(0);
|
||||||
|
|
||||||
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
|
// 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!
|
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
|
||||||
@ -129,6 +130,10 @@ 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
|
} else // we didn't get a location update, go back to sleep and hope the characters show up
|
||||||
wantNewLocation = true;
|
wantNewLocation = true;
|
||||||
|
|
||||||
|
// Notify any status instances that are observing us
|
||||||
|
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
|
// 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
|
// the serial
|
||||||
setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000);
|
setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000);
|
||||||
|
41
src/main.cpp
41
src/main.cpp
@ -38,8 +38,8 @@
|
|||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include "timing.h"
|
#include "timing.h"
|
||||||
#include <Wire.h>
|
|
||||||
#include <OneButton.h>
|
#include <OneButton.h>
|
||||||
|
#include <Wire.h>
|
||||||
// #include <driver/rtc_io.h>
|
// #include <driver/rtc_io.h>
|
||||||
|
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
@ -57,8 +57,14 @@
|
|||||||
// We always create a screen object, but we only init it if we find the hardware
|
// We always create a screen object, but we only init it if we find the hardware
|
||||||
meshtastic::Screen screen(SSD1306_ADDRESS);
|
meshtastic::Screen screen(SSD1306_ADDRESS);
|
||||||
|
|
||||||
// Global power status singleton
|
// Global power status
|
||||||
meshtastic::PowerStatus powerStatus;
|
meshtastic::PowerStatus *powerStatus = new meshtastic::PowerStatus();
|
||||||
|
|
||||||
|
// Global GPS status
|
||||||
|
meshtastic::GPSStatus *gpsStatus = new meshtastic::GPSStatus();
|
||||||
|
|
||||||
|
// Global Node status
|
||||||
|
meshtastic::NodeStatus *nodeStatus = new meshtastic::NodeStatus();
|
||||||
|
|
||||||
bool ssd1306_found;
|
bool ssd1306_found;
|
||||||
bool axp192_found;
|
bool axp192_found;
|
||||||
@ -122,22 +128,24 @@ static uint32_t ledBlinker()
|
|||||||
setLed(ledOn);
|
setLed(ledOn);
|
||||||
|
|
||||||
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
|
// 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 powerStatus->getIsCharging() ? 1000 : (ledOn ? 2 : 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
concurrency::Periodic ledPeriodic(ledBlinker);
|
concurrency::Periodic ledPeriodic(ledBlinker);
|
||||||
|
|
||||||
// Prepare for button presses
|
// Prepare for button presses
|
||||||
#ifdef BUTTON_PIN
|
#ifdef BUTTON_PIN
|
||||||
OneButton userButton;
|
OneButton userButton;
|
||||||
#endif
|
#endif
|
||||||
#ifdef BUTTON_PIN_ALT
|
#ifdef BUTTON_PIN_ALT
|
||||||
OneButton userButtonAlt;
|
OneButton userButtonAlt;
|
||||||
#endif
|
#endif
|
||||||
void userButtonPressed() {
|
void userButtonPressed()
|
||||||
|
{
|
||||||
powerFSM.trigger(EVENT_PRESS);
|
powerFSM.trigger(EVENT_PRESS);
|
||||||
}
|
}
|
||||||
void userButtonPressedLong(){
|
void userButtonPressedLong()
|
||||||
|
{
|
||||||
screen.adjustBrightness();
|
screen.adjustBrightness();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,6 +234,14 @@ void setup()
|
|||||||
esp32Setup();
|
esp32Setup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef TBEAM_V10
|
||||||
|
// Currently only the tbeam has a PMU
|
||||||
|
power = new Power();
|
||||||
|
power->setup();
|
||||||
|
power->setStatusHandler(powerStatus);
|
||||||
|
powerStatus->observe(&power->newStatus);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NRF52_SERIES
|
#ifdef NRF52_SERIES
|
||||||
nrf52Setup();
|
nrf52Setup();
|
||||||
#endif
|
#endif
|
||||||
@ -254,9 +270,10 @@ void setup()
|
|||||||
gps = new NEMAGPS();
|
gps = new NEMAGPS();
|
||||||
gps->setup();
|
gps->setup();
|
||||||
#endif
|
#endif
|
||||||
|
gpsStatus->observe(&gps->newStatus);
|
||||||
|
nodeStatus->observe(&nodeDB.newStatus);
|
||||||
|
|
||||||
service.init();
|
service.init();
|
||||||
|
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
// Must be after we init the service, because the wifi settings are loaded by NodeDB (oops)
|
// Must be after we init the service, because the wifi settings are loaded by NodeDB (oops)
|
||||||
initWifi();
|
initWifi();
|
||||||
@ -342,6 +359,9 @@ void loop()
|
|||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
esp32Loop();
|
esp32Loop();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef TBEAM_V10
|
||||||
|
power->loop();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef BUTTON_PIN
|
#ifdef BUTTON_PIN
|
||||||
userButton.tick();
|
userButton.tick();
|
||||||
@ -366,9 +386,8 @@ void loop()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Update the screen last, after we've figured out what to show.
|
// Update the screen last, after we've figured out what to show.
|
||||||
screen.debug_info()->setNodeNumbersStatus(nodeDB.getNumOnlineNodes(), nodeDB.getNumNodes());
|
|
||||||
screen.debug_info()->setChannelNameStatus(channelSettings.name);
|
screen.debug_info()->setChannelNameStatus(channelSettings.name);
|
||||||
screen.debug_info()->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)
|
// 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.
|
// i.e. don't just keep spinning in loop as fast as we can.
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
#include "PowerStatus.h"
|
||||||
|
#include "GPSStatus.h"
|
||||||
|
#include "NodeStatus.h"
|
||||||
|
|
||||||
extern bool axp192_found;
|
extern bool axp192_found;
|
||||||
extern bool ssd1306_found;
|
extern bool ssd1306_found;
|
||||||
@ -9,6 +12,11 @@ extern bool isUSBPowered;
|
|||||||
|
|
||||||
// Global Screen singleton.
|
// Global Screen singleton.
|
||||||
extern meshtastic::Screen screen;
|
extern meshtastic::Screen screen;
|
||||||
|
//extern Observable<meshtastic::PowerStatus> newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class
|
||||||
|
|
||||||
|
//extern meshtastic::PowerStatus *powerStatus;
|
||||||
|
//extern meshtastic::GPSStatus *gpsStatus;
|
||||||
|
//extern meshtastic::NodeStatusHandler *nodeStatusHandler;
|
||||||
|
|
||||||
// Return a human readable string of the form "Meshtastic_ab13"
|
// Return a human readable string of the form "Meshtastic_ab13"
|
||||||
const char *getDeviceName();
|
const char *getDeviceName();
|
||||||
|
@ -304,7 +304,7 @@ int MeshService::onGPSChanged(void *unused)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Include our current battery voltage in our position announcement
|
// Include our current battery voltage in our position announcement
|
||||||
pos.battery_level = powerStatus.batteryChargePercent;
|
pos.battery_level = powerStatus->getBatteryChargePercent();
|
||||||
updateBatteryLevel(pos.battery_level);
|
updateBatteryLevel(pos.battery_level);
|
||||||
|
|
||||||
// We limit our GPS broadcasts to a max rate
|
// We limit our GPS broadcasts to a max rate
|
||||||
|
@ -339,12 +339,8 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
const SubPacket &p = mp.decoded;
|
const SubPacket &p = mp.decoded;
|
||||||
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
|
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
|
||||||
|
|
||||||
int oldNumNodes = *numNodes;
|
|
||||||
NodeInfo *info = getOrCreateNode(mp.from);
|
NodeInfo *info = getOrCreateNode(mp.from);
|
||||||
|
|
||||||
if (oldNumNodes != *numNodes)
|
|
||||||
updateGUI = true; // we just created a nodeinfo
|
|
||||||
|
|
||||||
if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen
|
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->has_position = true; // at least the time is valid
|
||||||
info->position.time = mp.rx_time;
|
info->position.time = mp.rx_time;
|
||||||
@ -360,6 +356,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
info->position.time = oldtime;
|
info->position.time = oldtime;
|
||||||
info->has_position = true;
|
info->has_position = true;
|
||||||
updateGUIforNode = info;
|
updateGUIforNode = info;
|
||||||
|
notifyObservers(true); //Force an update whether or not our node counts have changed
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,6 +371,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
devicestate.has_rx_text_message = true;
|
devicestate.has_rx_text_message = true;
|
||||||
updateTextMessage = true;
|
updateTextMessage = true;
|
||||||
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
|
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
|
||||||
|
notifyObservers(true); //Force an update whether or not our node counts have changed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -392,6 +390,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
if (changed) {
|
if (changed) {
|
||||||
updateGUIforNode = info;
|
updateGUIforNode = info;
|
||||||
powerFSM.trigger(EVENT_NODEDB_UPDATED);
|
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
|
// Not really needed - we will save anyways when we go to sleep
|
||||||
// We just changed something important about the user, store our DB
|
// We just changed something important about the user, store our DB
|
||||||
@ -399,6 +398,10 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
notifyObservers(); //If the node counts have changed, notify observers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include "Observer.h"
|
||||||
|
|
||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
|
#include "NodeStatus.h"
|
||||||
|
|
||||||
extern DeviceState devicestate;
|
extern DeviceState devicestate;
|
||||||
extern MyNodeInfo &myNodeInfo;
|
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
|
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
|
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
|
bool updateTextMessage = false; // if true, the GUI should show a new text message
|
||||||
|
Observable<const meshtastic::NodeStatus *> newStatus;
|
||||||
|
|
||||||
/// don't do mesh based algoritm for node id assignment (initially)
|
/// 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
|
/// instead just store in flash - possibly even in the initial alpha release do this hack
|
||||||
@ -91,6 +94,13 @@ class NodeDB
|
|||||||
/// Find a node in our DB, create an empty NodeInfo if missing
|
/// Find a node in our DB, create an empty NodeInfo if missing
|
||||||
NodeInfo *getOrCreateNode(NodeNum n);
|
NodeInfo *getOrCreateNode(NodeNum n);
|
||||||
|
|
||||||
|
/// Notify observers of changes to the DB
|
||||||
|
void notifyObservers(bool forceUpdate = false) {
|
||||||
|
// Notify observers of the current node state
|
||||||
|
const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineNodes(), getNumNodes(), forceUpdate);
|
||||||
|
newStatus.notifyObservers(&status);
|
||||||
|
}
|
||||||
|
|
||||||
/// read our db from flash
|
/// read our db from flash
|
||||||
void loadFromDisk();
|
void loadFromDisk();
|
||||||
|
|
||||||
|
37
src/power.h
37
src/power.h
@ -1,4 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "concurrency/PeriodicTask.h"
|
||||||
|
#include "PowerStatus.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per @spattinson
|
* Per @spattinson
|
||||||
@ -13,23 +15,26 @@
|
|||||||
#define BAT_MILLIVOLTS_FULL 4100
|
#define BAT_MILLIVOLTS_FULL 4100
|
||||||
#define BAT_MILLIVOLTS_EMPTY 3500
|
#define BAT_MILLIVOLTS_EMPTY 3500
|
||||||
|
|
||||||
namespace meshtastic
|
class Power : public concurrency::PeriodicTask
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Describes the state of the power system.
|
public:
|
||||||
struct PowerStatus {
|
|
||||||
/// Whether we have a battery connected
|
Observable<const meshtastic::PowerStatus *> newStatus;
|
||||||
bool haveBattery;
|
|
||||||
/// Battery voltage in mV, valid if haveBattery is true
|
void readPowerStatus();
|
||||||
int batteryVoltageMv;
|
void loop();
|
||||||
/// Battery charge percentage, either read directly or estimated
|
virtual bool setup();
|
||||||
int batteryChargePercent;
|
virtual void doTask();
|
||||||
/// Whether USB is connected
|
void setStatusHandler(meshtastic::PowerStatus *handler)
|
||||||
bool usb;
|
{
|
||||||
/// Whether we are charging the battery
|
statusHandler = handler;
|
||||||
bool charging;
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
meshtastic::PowerStatus *statusHandler;
|
||||||
|
virtual void axp192Init();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace meshtastic
|
extern Power *power;
|
||||||
|
|
||||||
extern meshtastic::PowerStatus powerStatus;
|
|
282
src/screen.cpp
282
src/screen.cpp
@ -35,14 +35,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
#define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space
|
#define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space
|
||||||
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
||||||
#ifdef USE_SH1106
|
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
||||||
#define SCREEN_WIDTH 132
|
|
||||||
#else
|
|
||||||
#define SCREEN_WIDTH 128
|
#define SCREEN_WIDTH 128
|
||||||
#endif
|
|
||||||
#define SCREEN_HEIGHT 64
|
#define SCREEN_HEIGHT 64
|
||||||
#define TRANSITION_FRAMERATE 30 // fps
|
#define TRANSITION_FRAMERATE 30 // fps
|
||||||
#define IDLE_FRAMERATE 10 // in fps
|
#define IDLE_FRAMERATE 1 // in fps
|
||||||
#define COMPASS_DIAM 44
|
#define COMPASS_DIAM 44
|
||||||
|
|
||||||
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
|
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
|
||||||
@ -55,7 +52,16 @@ static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
|
|||||||
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
||||||
static char btPIN[16] = "888888";
|
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 };
|
||||||
|
uint8_t imgSatellite[8] = { 0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70 };
|
||||||
|
|
||||||
|
uint32_t dopThresholds[5] = { 2000, 1000, 500, 200, 100 };
|
||||||
|
|
||||||
|
// if defined a pixel will blink to show redraws
|
||||||
|
// #define SHOW_REDRAWS
|
||||||
|
#ifdef SHOW_REDRAWS
|
||||||
|
static bool heartbeat = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
@ -143,38 +149,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
|
#if 0
|
||||||
/// @return the max y we ended up printing to
|
/// Draw a series of fields in a row, wrapping to multiple rows if needed
|
||||||
static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
/// @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);
|
// The coordinates define the left starting point of the text
|
||||||
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
|
|
||||||
const char **f = fields;
|
const char **f = fields;
|
||||||
int xo = x, yo = y;
|
int xo = x, yo = y;
|
||||||
const int COLUMNS = 2; // hardwired for two columns per row....
|
const int COLUMNS = 2; // hardwired for two columns per row....
|
||||||
int col = 0; // track which column we are on
|
int col = 0; // track which column we are on
|
||||||
while (*f) {
|
while (*f) {
|
||||||
display->drawString(xo, yo, *f);
|
display->drawString(xo, yo, *f);
|
||||||
xo += SCREEN_WIDTH / COLUMNS;
|
xo += SCREEN_WIDTH / COLUMNS;
|
||||||
// Wrap to next row, if needed.
|
// Wrap to next row, if needed.
|
||||||
if (++col >= COLUMNS) {
|
if (++col >= COLUMNS) {
|
||||||
xo = x;
|
xo = x;
|
||||||
yo += FONT_HEIGHT;
|
yo += FONT_HEIGHT;
|
||||||
col = 0;
|
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.
|
// 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, const PowerStatus *powerStatus)
|
||||||
{
|
{
|
||||||
static const uint8_t powerBar[3] = {0x81, 0xBD, 0xBD};
|
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 lightning[8] = {0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85};
|
||||||
@ -183,12 +191,12 @@ static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *img
|
|||||||
imgBuffer[i] = 0x81;
|
imgBuffer[i] = 0x81;
|
||||||
}
|
}
|
||||||
// If charging, draw a charging indicator
|
// If charging, draw a charging indicator
|
||||||
if (powerStatus->charging) {
|
if (powerStatus->getIsCharging()) {
|
||||||
memcpy(imgBuffer + 3, lightning, 8);
|
memcpy(imgBuffer + 3, lightning, 8);
|
||||||
// If not charging, Draw power bars
|
// If not charging, Draw power bars
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (powerStatus->batteryChargePercent >= 25 * i)
|
if (powerStatus->getBatteryChargePercent() >= 25 * i)
|
||||||
memcpy(imgBuffer + 1 + (i * 3), powerBar, 3);
|
memcpy(imgBuffer + 1 + (i * 3), powerBar, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,49 +204,50 @@ static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *img
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw nodes status
|
// 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, NodeStatus *nodeStatus)
|
||||||
{
|
{
|
||||||
char usersString[20];
|
char usersString[20];
|
||||||
sprintf(usersString, "%d/%d", nodesOnline, nodesTotal);
|
sprintf(usersString, "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal());
|
||||||
display->drawFastImage(x, y, 8, 8, imgUser);
|
display->drawFastImage(x, y, 8, 8, imgUser);
|
||||||
display->drawString(x + 10, y - 2, usersString);
|
display->drawString(x + 10, y - 2, usersString);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw GPS status summary
|
// Draw GPS status summary
|
||||||
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, GPS *gps)
|
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
|
||||||
{
|
{
|
||||||
if (!gps->isConnected) {
|
if (!gps->getIsConnected())
|
||||||
|
{
|
||||||
display->drawString(x, y - 2, "No GPS");
|
display->drawString(x, y - 2, "No GPS");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
display->drawFastImage(x, y, 6, 8, gps->hasLock() ? imgPositionSolid : imgPositionEmpty);
|
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
|
||||||
if (!gps->hasLock()) {
|
if (!gps->getHasLock())
|
||||||
|
{
|
||||||
display->drawString(x + 8, y - 2, "No sats");
|
display->drawString(x + 8, y - 2, "No sats");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (gps->dop <= 100) {
|
else
|
||||||
display->drawString(x + 8, y - 2, "Ideal");
|
{
|
||||||
return;
|
char satsString[3];
|
||||||
}
|
uint8_t bar[2] = { 0 };
|
||||||
if (gps->dop <= 200) {
|
|
||||||
display->drawString(x + 8, y - 2, "Exc.");
|
//Draw DOP signal bars
|
||||||
return;
|
for(int i = 0; i < 5; i++)
|
||||||
}
|
{
|
||||||
if (gps->dop <= 500) {
|
if (gps->getDOP() <= dopThresholds[i])
|
||||||
display->drawString(x + 8, y - 2, "Good");
|
bar[0] = ~((1 << (5 - i)) - 1);
|
||||||
return;
|
else
|
||||||
}
|
bar[0] = 0b10000000;
|
||||||
if (gps->dop <= 1000) {
|
//bar[1] = bar[0];
|
||||||
display->drawString(x + 8, y - 2, "Mod.");
|
display->drawFastImage(x + 9 + (i * 2), y, 2, 8, bar);
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
if (gps->dop <= 2000) {
|
//Draw satellite image
|
||||||
display->drawString(x + 8, y - 2, "Fair");
|
display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
|
||||||
return;
|
|
||||||
}
|
//Draw the number of satellites
|
||||||
if (gps->dop > 0) {
|
sprintf(satsString, "%d", gps->getNumSatellites());
|
||||||
display->drawString(x + 8, y - 2, "Poor");
|
display->drawString(x + 34, y - 2, satsString);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,28 +390,41 @@ static bool hasPosition(NodeInfo *n)
|
|||||||
static size_t nodeIndex;
|
static size_t nodeIndex;
|
||||||
static int8_t prevFrame = -1;
|
static int8_t prevFrame = -1;
|
||||||
|
|
||||||
// Draw the compass and arrow pointing to location
|
// Draw the arrow pointing to a node's location
|
||||||
static void drawCompass(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
|
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
|
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
||||||
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
||||||
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
|
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++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
points[i]->rotate(headingRadian);
|
arrowPoints[i]->rotate(headingRadian);
|
||||||
points[i]->scale(COMPASS_DIAM * 0.6);
|
arrowPoints[i]->scale(COMPASS_DIAM * 0.6);
|
||||||
points[i]->translate(compassX, compassY);
|
arrowPoints[i]->translate(compassX, compassY);
|
||||||
}
|
}
|
||||||
drawLine(display, tip, tail);
|
drawLine(display, tip, tail);
|
||||||
drawLine(display, leftArrow, tip);
|
drawLine(display, leftArrow, tip);
|
||||||
drawLine(display, rightArrow, 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
|
/// Convert an integer GPS coords to a floating point
|
||||||
@ -422,10 +444,13 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
|
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
|
||||||
n = nodeDB.getNodeByIndex(nodeIndex);
|
n = nodeDB.getNodeByIndex(nodeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We just changed to a new node screen, ask that node for updated state
|
|
||||||
displayedNodeNum = n->num;
|
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);
|
NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);
|
||||||
@ -456,29 +481,40 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
|
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
|
||||||
|
|
||||||
// coordinates for the center of the compass/circle
|
// 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;
|
||||||
|
bool hasNodeHeading = false;
|
||||||
|
|
||||||
if (ourNode && hasPosition(ourNode) && hasPosition(node)) { // display direction toward node
|
if(ourNode && hasPosition(ourNode))
|
||||||
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));
|
Position &op = ourNode->position;
|
||||||
if (d < 2000)
|
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||||
snprintf(distStr, sizeof(distStr), "%.0f m", d);
|
drawCompassHeading(display, compassX, compassY, myHeading);
|
||||||
else
|
|
||||||
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
|
|
||||||
|
|
||||||
// FIXME, also keep the guess at the operators heading and add/substract
|
if(hasPosition(node))
|
||||||
// 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));
|
// display direction toward node
|
||||||
float myHeading = estimatedHeading(DegD(p.latitude_i), DegD(p.longitude_i));
|
hasNodeHeading = true;
|
||||||
headingRadian = bearingToOther - myHeading;
|
Position &p = node->position;
|
||||||
drawCompass(display, compassX, compassY, headingRadian);
|
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||||
} else { // direction to node is unknown so display question mark
|
if (d < 2000)
|
||||||
|
snprintf(distStr, sizeof(distStr), "%.0f m", d);
|
||||||
|
else
|
||||||
|
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!hasNodeHeading)
|
||||||
|
// direction to node is unknown so display question mark
|
||||||
// Debug info for gps lock errors
|
// Debug info for gps lock errors
|
||||||
// DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
|
// 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->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?");
|
||||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||||
}
|
|
||||||
|
|
||||||
// Must be after distStr is populated
|
// Must be after distStr is populated
|
||||||
drawColumns(display, x, y, fields);
|
drawColumns(display, x, y, fields);
|
||||||
@ -576,6 +612,11 @@ void Screen::setup()
|
|||||||
// twice initially.
|
// twice initially.
|
||||||
ui.update();
|
ui.update();
|
||||||
ui.update();
|
ui.update();
|
||||||
|
|
||||||
|
// Subscribe to status updates
|
||||||
|
powerStatusObserver.observe(&powerStatus->onNewStatus);
|
||||||
|
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
|
||||||
|
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Screen::doTask()
|
void Screen::doTask()
|
||||||
@ -637,14 +678,7 @@ void Screen::doTask()
|
|||||||
// While showing the bootscreen or Bluetooth pair screen all of our
|
// While showing the bootscreen or Bluetooth pair screen all of our
|
||||||
// standard screen switching is stopped.
|
// standard screen switching is stopped.
|
||||||
if (showingNormalScreen) {
|
if (showingNormalScreen) {
|
||||||
// TODO(girts): decouple nodeDB from screen.
|
// standard screen loop handling here
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.update();
|
ui.update();
|
||||||
@ -669,8 +703,8 @@ void Screen::setFrames()
|
|||||||
DEBUG_MSG("showing standard frames\n");
|
DEBUG_MSG("showing standard frames\n");
|
||||||
showingNormalScreen = true;
|
showingNormalScreen = true;
|
||||||
|
|
||||||
size_t numnodes = nodeDB.getNumNodes();
|
|
||||||
// We don't show the node info our our node (if we have it yet - we should)
|
// We don't show the node info our our node (if we have it yet - we should)
|
||||||
|
size_t numnodes = nodeStatus->getNumTotal();
|
||||||
if (numnodes > 0)
|
if (numnodes > 0)
|
||||||
numnodes--;
|
numnodes--;
|
||||||
|
|
||||||
@ -750,20 +784,26 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
|
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
|
||||||
|
|
||||||
// Display power status
|
// Display power status
|
||||||
if (powerStatus.haveBattery)
|
if (powerStatus->getHasBattery())
|
||||||
drawBattery(display, x, y + 2, imgBattery, &powerStatus);
|
drawBattery(display, x, y + 2, imgBattery, powerStatus);
|
||||||
else
|
else
|
||||||
display->drawFastImage(x, y + 2, 16, 8, powerStatus.usb ? imgUSB : imgPower);
|
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
|
||||||
// Display nodes status
|
// Display nodes status
|
||||||
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodesOnline, nodesTotal);
|
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
|
||||||
// Display GPS status
|
// Display GPS status
|
||||||
drawGPS(display, x + (SCREEN_WIDTH * 0.66), y + 2, gps);
|
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *fields[] = {channelStr, nullptr};
|
display->drawString(x, y + FONT_HEIGHT, channelStr);
|
||||||
uint32_t yo = drawRows(display, x, y + FONT_HEIGHT, fields);
|
|
||||||
|
|
||||||
display->drawLogBuffer(x, yo);
|
display->drawLogBuffer(x, y + (FONT_HEIGHT * 2));
|
||||||
|
|
||||||
|
/* 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
|
// adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true
|
||||||
@ -781,4 +821,20 @@ void Screen::adjustBrightness()
|
|||||||
dispdev.setBrightness(brightness);
|
dispdev.setBrightness(brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Screen::handleStatusUpdate(const Status *arg)
|
||||||
|
{
|
||||||
|
//DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
|
||||||
|
switch(arg->getStatusType())
|
||||||
|
{
|
||||||
|
case STATUS_TYPE_NODE:
|
||||||
|
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())
|
||||||
|
setFrames();
|
||||||
|
prevFrame = -1;
|
||||||
|
nodeDB.updateGUI = false;
|
||||||
|
nodeDB.updateTextMessage = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setPeriod(1); // Update the screen right away
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
} // namespace meshtastic
|
} // namespace meshtastic
|
||||||
|
42
src/screen.h
42
src/screen.h
@ -29,14 +29,6 @@ class DebugInfo
|
|||||||
DebugInfo(const DebugInfo &) = delete;
|
DebugInfo(const DebugInfo &) = delete;
|
||||||
DebugInfo &operator=(const DebugInfo &) = delete;
|
DebugInfo &operator=(const DebugInfo &) = delete;
|
||||||
|
|
||||||
/// Sets user statistics.
|
|
||||||
void setNodeNumbersStatus(int online, int total)
|
|
||||||
{
|
|
||||||
concurrency::LockGuard guard(&lock);
|
|
||||||
nodesOnline = online;
|
|
||||||
nodesTotal = total;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the name of the channel.
|
/// Sets the name of the channel.
|
||||||
void setChannelNameStatus(const char *name)
|
void setChannelNameStatus(const char *name)
|
||||||
{
|
{
|
||||||
@ -44,25 +36,6 @@ class DebugInfo
|
|||||||
channelName = name;
|
channelName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets battery/charging/etc status.
|
|
||||||
//
|
|
||||||
void setPowerStatus(const PowerStatus &status)
|
|
||||||
{
|
|
||||||
concurrency::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)
|
|
||||||
{
|
|
||||||
concurrency::LockGuard guard(&lock);
|
|
||||||
gpsStatus = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend Screen;
|
friend Screen;
|
||||||
|
|
||||||
@ -71,15 +44,8 @@ class DebugInfo
|
|||||||
/// Renders the debug screen.
|
/// Renders the debug screen.
|
||||||
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
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 channelName;
|
||||||
|
|
||||||
std::string gpsStatus;
|
|
||||||
|
|
||||||
/// Protects all of internal state.
|
/// Protects all of internal state.
|
||||||
concurrency::Lock lock;
|
concurrency::Lock lock;
|
||||||
};
|
};
|
||||||
@ -93,6 +59,10 @@ class DebugInfo
|
|||||||
// simultaneously).
|
// simultaneously).
|
||||||
class Screen : public concurrency::PeriodicTask
|
class Screen : public concurrency::PeriodicTask
|
||||||
{
|
{
|
||||||
|
CallbackObserver<Screen, const Status *> powerStatusObserver = CallbackObserver<Screen, const Status *>(this, &Screen::handleStatusUpdate);
|
||||||
|
CallbackObserver<Screen, const Status *> gpsStatusObserver = CallbackObserver<Screen, const Status *>(this, &Screen::handleStatusUpdate);
|
||||||
|
CallbackObserver<Screen, const Status *> nodeStatusObserver = CallbackObserver<Screen, const Status *>(this, &Screen::handleStatusUpdate);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Screen(uint8_t address, int sda = -1, int scl = -1);
|
Screen(uint8_t address, int sda = -1, int scl = -1);
|
||||||
|
|
||||||
@ -119,7 +89,7 @@ class Screen : public concurrency::PeriodicTask
|
|||||||
|
|
||||||
// Implementation to Adjust Brightness
|
// Implementation to Adjust Brightness
|
||||||
void adjustBrightness();
|
void adjustBrightness();
|
||||||
int brightness = 150;
|
uint8_t brightness = 150;
|
||||||
|
|
||||||
/// Starts showing the Bluetooth PIN screen.
|
/// Starts showing the Bluetooth PIN screen.
|
||||||
//
|
//
|
||||||
@ -189,6 +159,8 @@ class Screen : public concurrency::PeriodicTask
|
|||||||
// Use this handle to set things like battery status, user count, GPS status, etc.
|
// Use this handle to set things like battery status, user count, GPS status, etc.
|
||||||
DebugInfo* debug_info() { return &debugInfo; }
|
DebugInfo* debug_info() { return &debugInfo; }
|
||||||
|
|
||||||
|
int handleStatusUpdate(const meshtastic::Status *arg);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Updates the UI.
|
/// Updates the UI.
|
||||||
//
|
//
|
||||||
|
Loading…
Reference in New Issue
Block a user