mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-04 09:02:21 +00:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51267379ab | ||
|
|
e2cf2ba4f2 | ||
|
|
4550cce639 | ||
|
|
7c0d13f00a | ||
|
|
f78f3232e2 | ||
|
|
7802d00031 | ||
|
|
40a15248e8 | ||
|
|
3a62453b8b | ||
|
|
c3f7829255 | ||
|
|
37d9fb2dad | ||
|
|
4388e72dec | ||
|
|
9803141fe7 | ||
|
|
1f0e9cc1c3 | ||
|
|
92b30ebec6 | ||
|
|
ccadb6a43d | ||
|
|
6f7f540c79 | ||
|
|
d5b8038457 | ||
|
|
0a6059ba13 | ||
|
|
aba5b01fa0 | ||
|
|
09f4943869 | ||
|
|
29c8543f87 | ||
|
|
7bd4940ed8 | ||
|
|
d5116935b5 | ||
|
|
0d320fe29b | ||
|
|
4159461a62 | ||
|
|
f4bd39e3fa | ||
|
|
fbc36a2cfd | ||
|
|
e93ba73adb | ||
|
|
03301f093d | ||
|
|
55a5fa6fb5 | ||
|
|
4d04d10135 | ||
|
|
cda423acab | ||
|
|
0f92678c3b | ||
|
|
8d122f36e3 | ||
|
|
439cdfbb32 | ||
|
|
0a6ab31e10 | ||
|
|
0b6486256d | ||
|
|
da12b93f82 | ||
|
|
57d968cdcd | ||
|
|
aaca854620 | ||
|
|
ac2d3e2ae0 | ||
|
|
33946af39f |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -7,4 +7,13 @@ main/credentials.h
|
|||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
.autotools
|
||||||
|
.built
|
||||||
|
.context
|
||||||
|
.cproject
|
||||||
|
.idea/*
|
||||||
|
.vagrant
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -61,5 +61,6 @@
|
|||||||
"ocrypto",
|
"ocrypto",
|
||||||
"protobufs",
|
"protobufs",
|
||||||
"wifi"
|
"wifi"
|
||||||
]
|
],
|
||||||
|
"C_Cpp.dimInactiveRegions": true
|
||||||
}
|
}
|
||||||
17
.vscode/tasks.json
vendored
Normal file
17
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "PlatformIO",
|
||||||
|
"task": "Build",
|
||||||
|
"problemMatcher": [
|
||||||
|
"$platformio"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"label": "PlatformIO: Build"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -22,10 +22,10 @@ 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 (usually the recommended choice)
|
||||||
|
- [T-Beam V1.1 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)
|
||||||
|
- [T-Beam V0.7 w/ NEO-6M](https://www.aliexpress.com/item/4000574335430.html) (will work but **you must use the tbeam0.7 firmware ** - but the T-Beam V1.0 or later are better!)
|
||||||
- 3D printable cases
|
- 3D printable cases
|
||||||
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
|
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
|
||||||
- [T-Beam V1](https://www.thingiverse.com/thing:3830711)
|
- [T-Beam V1](https://www.thingiverse.com/thing:3830711)
|
||||||
@@ -43,6 +43,7 @@ We currently support three models of radios.
|
|||||||
- US/JP/AU/NZ/CA - 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.
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ COUNTRIES="US EU433 EU865 CN JP"
|
|||||||
#COUNTRIES=US
|
#COUNTRIES=US
|
||||||
#COUNTRIES=CN
|
#COUNTRIES=CN
|
||||||
|
|
||||||
BOARDS="ttgo-lora32-v2 ttgo-lora32-v1 tbeam heltec"
|
BOARDS="ttgo-lora32-v2 ttgo-lora32-v1 tbeam heltec tbeam0.7"
|
||||||
#BOARDS=tbeam
|
#BOARDS=tbeam
|
||||||
|
|
||||||
OUTDIR=release/latest
|
OUTDIR=release/latest
|
||||||
|
|||||||
3
bin/nrf52840-gdbserver.sh
Executable file
3
bin/nrf52840-gdbserver.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52832_XXAA
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
export VERSION=0.7.10
|
export VERSION=0.7.11
|
||||||
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
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 (plan for geofence mode and battery save mode)
|
|
||||||
- NRF52 BLE support
|
|
||||||
|
|
||||||
# Medium priority
|
# Medium priority
|
||||||
|
|
||||||
Items to complete before 1.0.
|
Items to complete before 1.0.
|
||||||
|
|||||||
@@ -1,5 +1,36 @@
|
|||||||
# NRF52 TODO
|
# NRF52 TODO
|
||||||
|
|
||||||
|
|
||||||
|
## RAK815
|
||||||
|
|
||||||
|
### Bootloader
|
||||||
|
Installing the adafruit bootloader is optional - I think the stock bootloader will work okay for most.
|
||||||
|
|
||||||
|
```
|
||||||
|
kevinh@kevin-server:~/development/meshtastic/Adafruit_nRF52_Bootloader$ make BOARD=rak815 flash
|
||||||
|
LD rak815_bootloader-0.3.2-111-g9478eb7-dirty.out
|
||||||
|
text data bss dec hex filename
|
||||||
|
20888 1124 15006 37018 909a _build/build-rak815/rak815_bootloader-0.3.2-111-g9478eb7-dirty.out
|
||||||
|
Create rak815_bootloader-0.3.2-111-g9478eb7-dirty.hex
|
||||||
|
Create rak815_bootloader-0.3.2-111-g9478eb7-dirty-nosd.hex
|
||||||
|
Flashing: rak815_bootloader-0.3.2-111-g9478eb7-dirty-nosd.hex
|
||||||
|
nrfjprog --program _build/build-rak815/rak815_bootloader-0.3.2-111-g9478eb7-dirty-nosd.hex --sectoranduicrerase -f nrf52 --reset
|
||||||
|
Parsing hex file.
|
||||||
|
Erasing page at address 0x0.
|
||||||
|
Erasing page at address 0x74000.
|
||||||
|
Erasing page at address 0x75000.
|
||||||
|
Erasing page at address 0x76000.
|
||||||
|
Erasing page at address 0x77000.
|
||||||
|
Erasing page at address 0x78000.
|
||||||
|
Erasing page at address 0x79000.
|
||||||
|
Erasing UICR flash area.
|
||||||
|
Applying system reset.
|
||||||
|
Checking that the area to write is not protected.
|
||||||
|
Programming device.
|
||||||
|
Applying system reset.
|
||||||
|
Run.
|
||||||
|
```
|
||||||
|
|
||||||
## Misc work items
|
## Misc work items
|
||||||
|
|
||||||
RAM investigation.
|
RAM investigation.
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ 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
|
||||||
@@ -90,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]
|
||||||
@@ -99,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
|
||||||
|
|||||||
@@ -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 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;
|
||||||
|
uint32_t numSatellites = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
GPSStatus() {
|
GPSStatus() {
|
||||||
statusType = STATUS_TYPE_GPS;
|
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->hasLock = hasLock;
|
||||||
this->isConnected = isConnected;
|
this->isConnected = isConnected;
|
||||||
@@ -31,6 +33,8 @@ namespace meshtastic {
|
|||||||
this->longitude = longitude;
|
this->longitude = longitude;
|
||||||
this->altitude = altitude;
|
this->altitude = altitude;
|
||||||
this->dop = dop;
|
this->dop = dop;
|
||||||
|
this->heading = heading;
|
||||||
|
this->numSatellites = numSatellites;
|
||||||
}
|
}
|
||||||
GPSStatus(const GPSStatus &);
|
GPSStatus(const GPSStatus &);
|
||||||
GPSStatus &operator=(const GPSStatus &);
|
GPSStatus &operator=(const GPSStatus &);
|
||||||
@@ -70,6 +74,16 @@ namespace meshtastic {
|
|||||||
return dop;
|
return dop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t getHeading() const
|
||||||
|
{
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getNumSatellites() const
|
||||||
|
{
|
||||||
|
return numSatellites;
|
||||||
|
}
|
||||||
|
|
||||||
bool matches(const GPSStatus *newStatus) const
|
bool matches(const GPSStatus *newStatus) const
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
@@ -78,7 +92,9 @@ namespace meshtastic {
|
|||||||
newStatus->latitude != latitude ||
|
newStatus->latitude != latitude ||
|
||||||
newStatus->longitude != longitude ||
|
newStatus->longitude != longitude ||
|
||||||
newStatus->altitude != altitude ||
|
newStatus->altitude != altitude ||
|
||||||
newStatus->dop != dop
|
newStatus->dop != dop ||
|
||||||
|
newStatus->heading != heading ||
|
||||||
|
newStatus->numSatellites != numSatellites
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
int updateStatus(const GPSStatus *newStatus) {
|
int updateStatus(const GPSStatus *newStatus) {
|
||||||
@@ -93,9 +109,11 @@ namespace meshtastic {
|
|||||||
longitude = newStatus->longitude;
|
longitude = newStatus->longitude;
|
||||||
altitude = newStatus->altitude;
|
altitude = newStatus->altitude;
|
||||||
dop = newStatus->dop;
|
dop = newStatus->dop;
|
||||||
|
heading = newStatus->heading;
|
||||||
|
numSatellites = newStatus->numSatellites;
|
||||||
}
|
}
|
||||||
if(isDirty) {
|
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);
|
onNewStatus.notifyObservers(this);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -15,13 +15,17 @@ namespace meshtastic {
|
|||||||
uint8_t numOnline = 0;
|
uint8_t numOnline = 0;
|
||||||
uint8_t numTotal = 0;
|
uint8_t numTotal = 0;
|
||||||
|
|
||||||
|
uint8_t lastNumTotal = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
bool forceUpdate = false;
|
||||||
|
|
||||||
NodeStatus() {
|
NodeStatus() {
|
||||||
statusType = STATUS_TYPE_NODE;
|
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->numOnline = numOnline;
|
||||||
this->numTotal = numTotal;
|
this->numTotal = numTotal;
|
||||||
}
|
}
|
||||||
@@ -43,6 +47,11 @@ namespace meshtastic {
|
|||||||
return numTotal;
|
return numTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t getLastNumTotal() const
|
||||||
|
{
|
||||||
|
return lastNumTotal;
|
||||||
|
}
|
||||||
|
|
||||||
bool matches(const NodeStatus *newStatus) const
|
bool matches(const NodeStatus *newStatus) const
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
@@ -52,6 +61,7 @@ namespace meshtastic {
|
|||||||
}
|
}
|
||||||
int updateStatus(const NodeStatus *newStatus) {
|
int updateStatus(const NodeStatus *newStatus) {
|
||||||
// Only update the status if values have actually changed
|
// Only update the status if values have actually changed
|
||||||
|
lastNumTotal = numTotal;
|
||||||
bool isDirty;
|
bool isDirty;
|
||||||
{
|
{
|
||||||
isDirty = matches(newStatus);
|
isDirty = matches(newStatus);
|
||||||
@@ -59,7 +69,7 @@ namespace meshtastic {
|
|||||||
numOnline = newStatus->getNumOnline();
|
numOnline = newStatus->getNumOnline();
|
||||||
numTotal = newStatus->getNumTotal();
|
numTotal = newStatus->getNumTotal();
|
||||||
}
|
}
|
||||||
if(isDirty) {
|
if(isDirty || newStatus->forceUpdate) {
|
||||||
DEBUG_MSG("Node status update: %d online, %d total\n", numOnline, numTotal);
|
DEBUG_MSG("Node status update: %d online, %d total\n", numOnline, numTotal);
|
||||||
onNewStatus.notifyObservers(this);
|
onNewStatus.notifyObservers(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
#include "PeriodicTask.h"
|
|
||||||
#include "Periodic.h"
|
|
||||||
PeriodicScheduler periodicScheduler;
|
|
||||||
|
|
||||||
PeriodicTask::PeriodicTask(uint32_t initialPeriod) : period(initialPeriod) {}
|
|
||||||
|
|
||||||
void PeriodicTask::setup()
|
|
||||||
{
|
|
||||||
periodicScheduler.schedule(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// call this from loop
|
|
||||||
void PeriodicScheduler::loop()
|
|
||||||
{
|
|
||||||
meshtastic::LockGuard lg(&lock);
|
|
||||||
|
|
||||||
uint32_t now = millis();
|
|
||||||
for (auto t : tasks) {
|
|
||||||
if (t->period && (now - t->lastMsec) >= t->period) {
|
|
||||||
|
|
||||||
t->doTask();
|
|
||||||
t->lastMsec = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PeriodicScheduler::schedule(PeriodicTask *t)
|
|
||||||
{
|
|
||||||
meshtastic::LockGuard lg(&lock);
|
|
||||||
tasks.insert(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PeriodicScheduler::unschedule(PeriodicTask *t)
|
|
||||||
{
|
|
||||||
meshtastic::LockGuard lg(&lock);
|
|
||||||
tasks.erase(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Periodic::doTask()
|
|
||||||
{
|
|
||||||
uint32_t p = callback();
|
|
||||||
setPeriod(p);
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "lock.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
class PeriodicTask;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs all PeriodicTasks in the system.
|
|
||||||
*
|
|
||||||
* Currently called from main loop() but eventually should be its own thread blocked on a freertos timer.
|
|
||||||
*/
|
|
||||||
class PeriodicScheduler
|
|
||||||
{
|
|
||||||
friend class PeriodicTask;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This really should be some form of heap, and when the period gets changed on a task it should get
|
|
||||||
* rescheduled in that heap. Currently it is just a dumb array and everytime we run loop() we check
|
|
||||||
* _every_ tasks. If it was a heap we'd only have to check the first task.
|
|
||||||
*/
|
|
||||||
std::unordered_set<PeriodicTask *> tasks;
|
|
||||||
|
|
||||||
// Protects the above variables.
|
|
||||||
meshtastic::Lock lock;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Run any next tasks which are due for execution
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void schedule(PeriodicTask *t);
|
|
||||||
void unschedule(PeriodicTask *t);
|
|
||||||
};
|
|
||||||
|
|
||||||
extern PeriodicScheduler periodicScheduler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A base class for tasks that want their doTask() method invoked periodically
|
|
||||||
*
|
|
||||||
* FIXME: currently just syntatic sugar for polling in loop (you must call .loop), but eventually
|
|
||||||
* generalize with the freertos scheduler so we can save lots of power by having everything either in
|
|
||||||
* something like this or triggered off of an irq.
|
|
||||||
*/
|
|
||||||
class PeriodicTask
|
|
||||||
{
|
|
||||||
friend class PeriodicScheduler;
|
|
||||||
|
|
||||||
uint32_t lastMsec = 0;
|
|
||||||
uint32_t period = 1; // call soon after creation
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~PeriodicTask() { periodicScheduler.unschedule(this); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor (will schedule with the global PeriodicScheduler)
|
|
||||||
*/
|
|
||||||
PeriodicTask(uint32_t initialPeriod = 1);
|
|
||||||
|
|
||||||
/** MUST be be called once at startup (but after threading is running - i.e. not from a constructor)
|
|
||||||
*/
|
|
||||||
void setup();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a new period in msecs (can be called from doTask or elsewhere and the scheduler will cope)
|
|
||||||
* While zero this task is disabled and will not run
|
|
||||||
*/
|
|
||||||
void setPeriod(uint32_t p)
|
|
||||||
{
|
|
||||||
lastMsec = millis(); // reset starting from now
|
|
||||||
period = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t getPeriod() const { return period; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Syntatic sugar for suspending tasks
|
|
||||||
*/
|
|
||||||
void disable() { setPeriod(0); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void doTask() = 0;
|
|
||||||
};
|
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef TBEAM_V10
|
#ifdef TBEAM_V10
|
||||||
|
|
||||||
// FIXME. nasty hack cleanup how we load axp192
|
// FIXME. nasty hack cleanup how we load axp192
|
||||||
@@ -19,7 +18,7 @@ bool Power::setup()
|
|||||||
{
|
{
|
||||||
|
|
||||||
axp192Init();
|
axp192Init();
|
||||||
PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
||||||
setPeriod(1);
|
setPeriod(1);
|
||||||
|
|
||||||
return axp192_found;
|
return axp192_found;
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
static void sdsEnter()
|
static void sdsEnter()
|
||||||
{
|
{
|
||||||
@@ -15,7 +16,7 @@ static void sdsEnter()
|
|||||||
|
|
||||||
// Don't deepsleep if we have USB power or if the user as pressed a button recently
|
// Don't deepsleep if we have USB power or if the user as pressed a button recently
|
||||||
// !isUSBPowered <- doesn't work yet because the axp192 isn't letting the battery fully charge when we are awake - FIXME
|
// !isUSBPowered <- doesn't work yet because the axp192 isn't letting the battery fully charge when we are awake - FIXME
|
||||||
if (millis() - lastPressMs > radioConfig.preferences.mesh_sds_timeout_secs)
|
if (timing::millis() - lastPressMs > radioConfig.preferences.mesh_sds_timeout_secs)
|
||||||
{
|
{
|
||||||
doDeepSleep(radioConfig.preferences.sds_secs);
|
doDeepSleep(radioConfig.preferences.sds_secs);
|
||||||
}
|
}
|
||||||
@@ -130,7 +131,7 @@ static void onEnter()
|
|||||||
|
|
||||||
static uint32_t lastPingMs;
|
static uint32_t lastPingMs;
|
||||||
|
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
|
|
||||||
if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask other nodes to update their state
|
if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask other nodes to update their state
|
||||||
if (displayedNodeNum)
|
if (displayedNodeNum)
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
#include "WorkerThread.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#ifdef configUSE_PREEMPTION
|
|
||||||
|
|
||||||
void Thread::start(const char *name, size_t stackSize, uint32_t priority)
|
|
||||||
{
|
|
||||||
auto r = xTaskCreate(callRun, name, stackSize, this, priority, &taskHandle);
|
|
||||||
assert(r == pdPASS);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Thread::callRun(void *_this)
|
|
||||||
{
|
|
||||||
((Thread *)_this)->doRun();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
if (millis() - lastPrint > 10 * 1000L) {
|
|
||||||
lastPrint = millis();
|
|
||||||
meshtastic::printThreadInfo("net");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
stopWatchdog();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify this thread so it can run
|
|
||||||
*/
|
|
||||||
void NotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
|
|
||||||
{
|
|
||||||
xTaskNotify(taskHandle, v, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifiedWorkerThread::block()
|
|
||||||
{
|
|
||||||
xTaskNotifyWait(0, // don't clear notification on entry
|
|
||||||
clearOnRead, ¬ification, portMAX_DELAY); // Wait forever
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
#include "esp_task_wdt.h"
|
|
||||||
#include "freertosinc.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#ifdef HAS_FREE_RTOS
|
|
||||||
|
|
||||||
class Thread
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
TaskHandle_t taskHandle = NULL;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set this to true to ask thread to cleanly exit asap
|
|
||||||
*/
|
|
||||||
volatile bool wantExit = false;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY);
|
|
||||||
|
|
||||||
virtual ~Thread() { vTaskDelete(taskHandle); }
|
|
||||||
|
|
||||||
uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* The method that will be called when start is called.
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting onlyschool arduino loop() code.
|
|
||||||
*
|
|
||||||
* Use as a mixin base class for the classes you want to convert.
|
|
||||||
*
|
|
||||||
* https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html
|
|
||||||
*/
|
|
||||||
class WorkerThread : public Thread
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
|
||||||
*/
|
|
||||||
virtual void block() = 0;
|
|
||||||
|
|
||||||
virtual void loop() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The method that will be called when start is called.
|
|
||||||
*/
|
|
||||||
virtual void doRun();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A worker thread that waits on a freertos notification
|
|
||||||
*/
|
|
||||||
class NotifiedWorkerThread : public WorkerThread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Notify this thread so it can run
|
|
||||||
*/
|
|
||||||
void notify(uint32_t v = 0, eNotifyAction action = eNoAction);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify from an ISR
|
|
||||||
*
|
|
||||||
* This must be inline or IRAM_ATTR on ESP32
|
|
||||||
*/
|
|
||||||
inline void notifyFromISR(BaseType_t *highPriWoken, uint32_t v = 0, eNotifyAction action = eNoAction)
|
|
||||||
{
|
|
||||||
xTaskNotifyFromISR(taskHandle, v, action, highPriWoken);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* The notification that was most recently used to wake the thread. Read from loop()
|
|
||||||
*/
|
|
||||||
uint32_t notification = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* What notification bits should be cleared just after we read and return them in notification?
|
|
||||||
*
|
|
||||||
* Defaults to clear all of them.
|
|
||||||
*/
|
|
||||||
uint32_t clearOnRead = UINT32_MAX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
|
||||||
*/
|
|
||||||
virtual void block();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
15
src/commands.h
Normal file
15
src/commands.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* @brief This class enables on the fly software and hardware setup.
|
||||||
|
* It will contain all command messages to change internal settings.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum class Cmd {
|
||||||
|
INVALID,
|
||||||
|
SET_ON,
|
||||||
|
SET_OFF,
|
||||||
|
ON_PRESS,
|
||||||
|
START_BLUETOOTH_PIN_SCREEN,
|
||||||
|
STOP_BLUETOOTH_PIN_SCREEN,
|
||||||
|
STOP_BOOT_SCREEN,
|
||||||
|
PRINT,
|
||||||
|
};
|
||||||
23
src/concurrency/Lock.cpp
Normal file
23
src/concurrency/Lock.cpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#include "Lock.h"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
Lock::Lock()
|
||||||
|
{
|
||||||
|
handle = xSemaphoreCreateBinary();
|
||||||
|
assert(handle);
|
||||||
|
assert(xSemaphoreGive(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lock::lock()
|
||||||
|
{
|
||||||
|
assert(xSemaphoreTake(handle, portMAX_DELAY));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lock::unlock()
|
||||||
|
{
|
||||||
|
assert(xSemaphoreGive(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
33
src/concurrency/Lock.h
Normal file
33
src/concurrency/Lock.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../freertosinc.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Simple wrapper around FreeRTOS API for implementing a mutex lock
|
||||||
|
*/
|
||||||
|
class Lock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Lock();
|
||||||
|
|
||||||
|
Lock(const Lock &) = delete;
|
||||||
|
Lock &operator=(const Lock &) = delete;
|
||||||
|
|
||||||
|
/// Locks the lock.
|
||||||
|
//
|
||||||
|
// Must not be called from an ISR.
|
||||||
|
void lock();
|
||||||
|
|
||||||
|
// Unlocks the lock.
|
||||||
|
//
|
||||||
|
// Must not be called from an ISR.
|
||||||
|
void unlock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SemaphoreHandle_t handle;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
15
src/concurrency/LockGuard.cpp
Normal file
15
src/concurrency/LockGuard.cpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#include "LockGuard.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
LockGuard::LockGuard(Lock *lock) : lock(lock)
|
||||||
|
{
|
||||||
|
lock->lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
LockGuard::~LockGuard()
|
||||||
|
{
|
||||||
|
lock->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
23
src/concurrency/LockGuard.h
Normal file
23
src/concurrency/LockGuard.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Lock.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief RAII lock guard
|
||||||
|
*/
|
||||||
|
class LockGuard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LockGuard(Lock *lock);
|
||||||
|
~LockGuard();
|
||||||
|
|
||||||
|
LockGuard(const LockGuard &) = delete;
|
||||||
|
LockGuard &operator=(const LockGuard &) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Lock *lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
19
src/concurrency/NotifiedWorkerThread.cpp
Normal file
19
src/concurrency/NotifiedWorkerThread.cpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include "NotifiedWorkerThread.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify this thread so it can run
|
||||||
|
*/
|
||||||
|
void NotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
|
||||||
|
{
|
||||||
|
xTaskNotify(taskHandle, v, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifiedWorkerThread::block()
|
||||||
|
{
|
||||||
|
xTaskNotifyWait(0, // don't clear notification on entry
|
||||||
|
clearOnRead, ¬ification, portMAX_DELAY); // Wait forever
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
47
src/concurrency/NotifiedWorkerThread.h
Normal file
47
src/concurrency/NotifiedWorkerThread.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WorkerThread.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A worker thread that waits on a freertos notification
|
||||||
|
*/
|
||||||
|
class NotifiedWorkerThread : public WorkerThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Notify this thread so it can run
|
||||||
|
*/
|
||||||
|
void notify(uint32_t v = 0, eNotifyAction action = eNoAction);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify from an ISR
|
||||||
|
*
|
||||||
|
* This must be inline or IRAM_ATTR on ESP32
|
||||||
|
*/
|
||||||
|
inline void notifyFromISR(BaseType_t *highPriWoken, uint32_t v = 0, eNotifyAction action = eNoAction)
|
||||||
|
{
|
||||||
|
xTaskNotifyFromISR(taskHandle, v, action, highPriWoken);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* The notification that was most recently used to wake the thread. Read from loop()
|
||||||
|
*/
|
||||||
|
uint32_t notification = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What notification bits should be cleared just after we read and return them in notification?
|
||||||
|
*
|
||||||
|
* Defaults to clear all of them.
|
||||||
|
*/
|
||||||
|
uint32_t clearOnRead = UINT32_MAX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
||||||
|
*/
|
||||||
|
virtual void block();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PeriodicTask.h"
|
#include "PeriodicTask.h"
|
||||||
#include <Arduino.h>
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Periodically invoke a callback.
|
* @brief Periodically invoke a callback. This just provides C-style callback conventions
|
||||||
*
|
* rather than a virtual function - FIXME, remove?
|
||||||
* This just provides C style callback conventions rather than a virtual function - FIXME, remove?
|
|
||||||
*/
|
*/
|
||||||
class Periodic : public PeriodicTask
|
class Periodic : public PeriodicTask
|
||||||
{
|
{
|
||||||
@@ -17,5 +17,10 @@ class Periodic : public PeriodicTask
|
|||||||
Periodic(uint32_t (*_callback)()) : callback(_callback) {}
|
Periodic(uint32_t (*_callback)()) : callback(_callback) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void doTask();
|
void doTask() {
|
||||||
|
uint32_t p = callback();
|
||||||
|
setPeriod(p);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
35
src/concurrency/PeriodicScheduler.cpp
Normal file
35
src/concurrency/PeriodicScheduler.cpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#include "PeriodicScheduler.h"
|
||||||
|
#include "PeriodicTask.h"
|
||||||
|
#include "LockGuard.h"
|
||||||
|
#include "../timing.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/// call this from loop
|
||||||
|
void PeriodicScheduler::loop()
|
||||||
|
{
|
||||||
|
LockGuard lg(&lock);
|
||||||
|
|
||||||
|
uint32_t now = timing::millis();
|
||||||
|
for (auto t : tasks) {
|
||||||
|
if (t->period && (now - t->lastMsec) >= t->period) {
|
||||||
|
|
||||||
|
t->doTask();
|
||||||
|
t->lastMsec = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeriodicScheduler::schedule(PeriodicTask *t)
|
||||||
|
{
|
||||||
|
LockGuard lg(&lock);
|
||||||
|
tasks.insert(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeriodicScheduler::unschedule(PeriodicTask *t)
|
||||||
|
{
|
||||||
|
LockGuard lg(&lock);
|
||||||
|
tasks.erase(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
40
src/concurrency/PeriodicScheduler.h
Normal file
40
src/concurrency/PeriodicScheduler.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Lock.h"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
class PeriodicTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Runs all PeriodicTasks in the system. Currently called from main loop()
|
||||||
|
* but eventually should be its own thread blocked on a freertos timer.
|
||||||
|
*/
|
||||||
|
class PeriodicScheduler
|
||||||
|
{
|
||||||
|
friend class PeriodicTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This really should be some form of heap, and when the period gets changed on a task it should get
|
||||||
|
* rescheduled in that heap. Currently it is just a dumb array and everytime we run loop() we check
|
||||||
|
* _every_ tasks. If it was a heap we'd only have to check the first task.
|
||||||
|
*/
|
||||||
|
std::unordered_set<PeriodicTask *> tasks;
|
||||||
|
|
||||||
|
// Protects the above variables.
|
||||||
|
Lock lock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Run any next tasks which are due for execution
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void schedule(PeriodicTask *t);
|
||||||
|
void unschedule(PeriodicTask *t);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern PeriodicScheduler periodicScheduler;
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
16
src/concurrency/PeriodicTask.cpp
Normal file
16
src/concurrency/PeriodicTask.cpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#include "PeriodicTask.h"
|
||||||
|
#include "Periodic.h"
|
||||||
|
#include "LockGuard.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
PeriodicScheduler periodicScheduler;
|
||||||
|
|
||||||
|
PeriodicTask::PeriodicTask(uint32_t initialPeriod) : period(initialPeriod) {}
|
||||||
|
|
||||||
|
void PeriodicTask::setup()
|
||||||
|
{
|
||||||
|
periodicScheduler.schedule(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
56
src/concurrency/PeriodicTask.h
Normal file
56
src/concurrency/PeriodicTask.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "PeriodicScheduler.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A base class for tasks that want their doTask() method invoked periodically
|
||||||
|
*
|
||||||
|
* @todo currently just syntatic sugar for polling in loop (you must call .loop), but eventually
|
||||||
|
* generalize with the freertos scheduler so we can save lots of power by having everything either in
|
||||||
|
* something like this or triggered off of an irq.
|
||||||
|
*/
|
||||||
|
class PeriodicTask
|
||||||
|
{
|
||||||
|
friend class PeriodicScheduler;
|
||||||
|
|
||||||
|
uint32_t lastMsec = 0;
|
||||||
|
uint32_t period = 1; // call soon after creation
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~PeriodicTask() { periodicScheduler.unschedule(this); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor (will schedule with the global PeriodicScheduler)
|
||||||
|
*/
|
||||||
|
PeriodicTask(uint32_t initialPeriod = 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MUST be be called once at startup (but after threading is running - i.e. not from a constructor)
|
||||||
|
*/
|
||||||
|
void setup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a new period in msecs (can be called from doTask or elsewhere and the scheduler will cope)
|
||||||
|
* While zero this task is disabled and will not run
|
||||||
|
*/
|
||||||
|
void setPeriod(uint32_t p)
|
||||||
|
{
|
||||||
|
lastMsec = timing::millis(); // reset starting from now
|
||||||
|
period = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getPeriod() const { return period; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntatic sugar for suspending tasks
|
||||||
|
*/
|
||||||
|
void disable() { setPeriod(0); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void doTask() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
17
src/concurrency/Thread.cpp
Normal file
17
src/concurrency/Thread.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include "Thread.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
void Thread::start(const char *name, size_t stackSize, uint32_t priority)
|
||||||
|
{
|
||||||
|
auto r = xTaskCreate(callRun, name, stackSize, this, priority, &taskHandle);
|
||||||
|
assert(r == pdPASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::callRun(void *_this)
|
||||||
|
{
|
||||||
|
((Thread *)_this)->doRun();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
56
src/concurrency/Thread.h
Normal file
56
src/concurrency/Thread.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "freertosinc.h"
|
||||||
|
#include "esp_task_wdt.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Base threading
|
||||||
|
*/
|
||||||
|
class Thread
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
TaskHandle_t taskHandle = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set this to true to ask thread to cleanly exit asap
|
||||||
|
*/
|
||||||
|
volatile bool wantExit = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY);
|
||||||
|
|
||||||
|
virtual ~Thread() { vTaskDelete(taskHandle); }
|
||||||
|
|
||||||
|
uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* The method that will be called when start is called.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
32
src/concurrency/WorkerThread.cpp
Normal file
32
src/concurrency/WorkerThread.cpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#include "WorkerThread.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
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;
|
||||||
|
if (timing::millis() - lastPrint > 10 * 1000L) {
|
||||||
|
lastPrint = timing::millis();
|
||||||
|
meshtastic::printThreadInfo("net");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
stopWatchdog();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
29
src/concurrency/WorkerThread.h
Normal file
29
src/concurrency/WorkerThread.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting
|
||||||
|
* old-school arduino loop() code. Use as a mixin base class for the classes you want to convert.
|
||||||
|
*
|
||||||
|
* @link https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html
|
||||||
|
*/
|
||||||
|
class WorkerThread : public Thread
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
||||||
|
*/
|
||||||
|
virtual void block() = 0;
|
||||||
|
|
||||||
|
virtual void loop() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method that will be called when start is called.
|
||||||
|
*/
|
||||||
|
virtual void doRun();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#include "debug.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "freertosinc.h"
|
|
||||||
#include "configuration.h"
|
|
||||||
|
|
||||||
namespace meshtastic
|
|
||||||
{
|
|
||||||
|
|
||||||
void printThreadInfo(const char *extra)
|
|
||||||
{
|
|
||||||
#ifndef NO_ESP32
|
|
||||||
uint32_t taskHandle = reinterpret_cast<uint32_t>(xTaskGetCurrentTaskHandle());
|
|
||||||
DEBUG_MSG("printThreadInfo(%s) task: %" PRIx32 " core id: %u min free stack: %u\n", extra, taskHandle, xPortGetCoreID(),
|
|
||||||
uxTaskGetStackHighWaterMark(nullptr));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace meshtastic
|
|
||||||
10
src/debug.h
10
src/debug.h
@@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace meshtastic
|
|
||||||
{
|
|
||||||
|
|
||||||
/// Dumps out which core we are running on, and min level of remaining stack
|
|
||||||
/// seen.
|
|
||||||
void printThreadInfo(const char *extra);
|
|
||||||
|
|
||||||
} // namespace meshtastic
|
|
||||||
@@ -3,21 +3,22 @@
|
|||||||
#include "CallbackCharacteristic.h"
|
#include "CallbackCharacteristic.h"
|
||||||
#include "RadioLibInterface.h"
|
#include "RadioLibInterface.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "lock.h"
|
#include "../concurrency/LockGuard.h"
|
||||||
|
#include "../timing.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <BLE2902.h>
|
#include <BLE2902.h>
|
||||||
#include <CRC32.h>
|
#include <CRC32.h>
|
||||||
#include <Update.h>
|
#include <Update.h>
|
||||||
#include <esp_gatt_defs.h>
|
#include <esp_gatt_defs.h>
|
||||||
|
|
||||||
using namespace meshtastic;
|
//using namespace meshtastic;
|
||||||
|
|
||||||
CRC32 crc;
|
CRC32 crc;
|
||||||
uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
|
uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
|
||||||
|
|
||||||
uint32_t updateExpectedSize, updateActualSize;
|
uint32_t updateExpectedSize, updateActualSize;
|
||||||
|
|
||||||
Lock *updateLock;
|
concurrency::Lock *updateLock;
|
||||||
|
|
||||||
class TotalSizeCharacteristic : public CallbackCharacteristic
|
class TotalSizeCharacteristic : public CallbackCharacteristic
|
||||||
{
|
{
|
||||||
@@ -30,7 +31,7 @@ class TotalSizeCharacteristic : public CallbackCharacteristic
|
|||||||
|
|
||||||
void onWrite(BLECharacteristic *c)
|
void onWrite(BLECharacteristic *c)
|
||||||
{
|
{
|
||||||
LockGuard g(updateLock);
|
concurrency::LockGuard g(updateLock);
|
||||||
// Check if there is enough to OTA Update
|
// Check if there is enough to OTA Update
|
||||||
uint32_t len = getValue32(c, 0);
|
uint32_t len = getValue32(c, 0);
|
||||||
updateExpectedSize = len;
|
updateExpectedSize = len;
|
||||||
@@ -65,7 +66,7 @@ class DataCharacteristic : public CallbackCharacteristic
|
|||||||
|
|
||||||
void onWrite(BLECharacteristic *c)
|
void onWrite(BLECharacteristic *c)
|
||||||
{
|
{
|
||||||
LockGuard g(updateLock);
|
concurrency::LockGuard g(updateLock);
|
||||||
std::string value = c->getValue();
|
std::string value = c->getValue();
|
||||||
uint32_t len = value.length();
|
uint32_t len = value.length();
|
||||||
assert(len <= MAX_BLOCKSIZE);
|
assert(len <= MAX_BLOCKSIZE);
|
||||||
@@ -89,7 +90,7 @@ class CRC32Characteristic : public CallbackCharacteristic
|
|||||||
|
|
||||||
void onWrite(BLECharacteristic *c)
|
void onWrite(BLECharacteristic *c)
|
||||||
{
|
{
|
||||||
LockGuard g(updateLock);
|
concurrency::LockGuard g(updateLock);
|
||||||
uint32_t expectedCRC = getValue32(c, 0);
|
uint32_t expectedCRC = getValue32(c, 0);
|
||||||
uint32_t actualCRC = crc.finalize();
|
uint32_t actualCRC = crc.finalize();
|
||||||
DEBUG_MSG("expected CRC %u\n", expectedCRC);
|
DEBUG_MSG("expected CRC %u\n", expectedCRC);
|
||||||
@@ -106,7 +107,7 @@ class CRC32Characteristic : public CallbackCharacteristic
|
|||||||
} else {
|
} else {
|
||||||
if (Update.end()) {
|
if (Update.end()) {
|
||||||
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
|
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
|
||||||
rebootAtMsec = millis() + 5000;
|
rebootAtMsec = timing::millis() + 5000;
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
|
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
|
||||||
}
|
}
|
||||||
@@ -124,7 +125,7 @@ class CRC32Characteristic : public CallbackCharacteristic
|
|||||||
|
|
||||||
void bluetoothRebootCheck()
|
void bluetoothRebootCheck()
|
||||||
{
|
{
|
||||||
if (rebootAtMsec && millis() > rebootAtMsec) {
|
if (rebootAtMsec && timing::millis() > rebootAtMsec) {
|
||||||
DEBUG_MSG("Rebooting for update\n");
|
DEBUG_MSG("Rebooting for update\n");
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
@@ -137,7 +138,7 @@ See bluetooth-api.md
|
|||||||
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion)
|
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion)
|
||||||
{
|
{
|
||||||
if (!updateLock)
|
if (!updateLock)
|
||||||
updateLock = new Lock();
|
updateLock = new concurrency::Lock();
|
||||||
|
|
||||||
// Create the BLE Service
|
// Create the BLE Service
|
||||||
BLEService *service = server->createService(BLEUUID("cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"), 25, 0);
|
BLEService *service = server->createService(BLEUUID("cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"), 25, 0);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "time.h"
|
#include "timing.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <sys/time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#ifdef GPS_RX_PIN
|
#ifdef GPS_RX_PIN
|
||||||
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
|
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
|
||||||
@@ -27,7 +27,7 @@ void readFromRTC()
|
|||||||
struct timeval tv; /* btw settimeofday() is helpfull here too*/
|
struct timeval tv; /* btw settimeofday() is helpfull here too*/
|
||||||
|
|
||||||
if (!gettimeofday(&tv, NULL)) {
|
if (!gettimeofday(&tv, NULL)) {
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
|
|
||||||
DEBUG_MSG("Read RTC time as %ld (cur millis %u) valid=%d\n", tv.tv_sec, now, timeSetFromGPS);
|
DEBUG_MSG("Read RTC time as %ld (cur millis %u) valid=%d\n", tv.tv_sec, now, timeSetFromGPS);
|
||||||
timeStartMsec = now;
|
timeStartMsec = now;
|
||||||
@@ -68,11 +68,9 @@ void perhapsSetRTC(struct tm &t)
|
|||||||
perhapsSetRTC(&tv);
|
perhapsSetRTC(&tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
uint32_t getTime()
|
uint32_t getTime()
|
||||||
{
|
{
|
||||||
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
|
return ((timing::millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getValidTime()
|
uint32_t getValidTime()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "GPSStatus.h"
|
#include "GPSStatus.h"
|
||||||
#include "PeriodicTask.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
|
||||||
@@ -36,6 +36,8 @@ 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
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "NEMAGPS.h"
|
#include "NEMAGPS.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
static int32_t toDegInt(RawDegrees d)
|
static int32_t toDegInt(RawDegrees d)
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,7 @@ void NEMAGPS::loop()
|
|||||||
reader.encode(c);
|
reader.encode(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
if ((now - lastUpdateMsec) > 20 * 1000) { // Ugly hack for now - limit update checks to once every 20 secs (but still consume
|
if ((now - lastUpdateMsec) > 20 * 1000) { // Ugly hack for now - limit update checks to once every 20 secs (but still consume
|
||||||
// serial chars at whatever rate)
|
// serial chars at whatever rate)
|
||||||
lastUpdateMsec = now;
|
lastUpdateMsec = now;
|
||||||
@@ -54,12 +55,18 @@ 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)
|
||||||
@@ -67,7 +74,7 @@ void NEMAGPS::loop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Notify any status instances that are observing us
|
// 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);
|
newStatus.notifyObservers(&status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "PeriodicTask.h"
|
#include "../concurrency/PeriodicTask.h"
|
||||||
#include "TinyGPS++.h"
|
#include "TinyGPS++.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
UBloxGPS::UBloxGPS() : PeriodicTask()
|
UBloxGPS::UBloxGPS() : concurrency::PeriodicTask()
|
||||||
{
|
{
|
||||||
notifySleepObserver.observe(¬ifySleep);
|
notifySleepObserver.observe(¬ifySleep);
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ bool UBloxGPS::setup()
|
|||||||
ok = ublox.saveConfiguration(3000);
|
ok = ublox.saveConfiguration(3000);
|
||||||
assert(ok);
|
assert(ok);
|
||||||
|
|
||||||
PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -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);
|
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
|
||||||
|
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,7 +131,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
wantNewLocation = true;
|
wantNewLocation = true;
|
||||||
|
|
||||||
// Notify any status instances that are observing us
|
// 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);
|
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
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "PeriodicTask.h"
|
#include "../concurrency/PeriodicTask.h"
|
||||||
#include "SparkFun_Ublox_Arduino_Library.h"
|
#include "SparkFun_Ublox_Arduino_Library.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
* When new data is available it will notify observers.
|
* When new data is available it will notify observers.
|
||||||
*/
|
*/
|
||||||
class UBloxGPS : public GPS, public PeriodicTask
|
class UBloxGPS : public GPS, public concurrency::PeriodicTask
|
||||||
{
|
{
|
||||||
SFE_UBLOX_GPS ublox;
|
SFE_UBLOX_GPS ublox;
|
||||||
|
|
||||||
|
|||||||
@@ -26,28 +26,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "fonts.h"
|
#include "graphics/images.h"
|
||||||
#include "images.h"
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "screen.h"
|
#include "Screen.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "configs.h"
|
||||||
|
|
||||||
#define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space
|
using namespace meshtastic; /** @todo remove */
|
||||||
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
|
||||||
#ifdef USE_SH1106
|
|
||||||
#define SCREEN_WIDTH 132
|
|
||||||
#else
|
|
||||||
#define SCREEN_WIDTH 128
|
|
||||||
#endif
|
|
||||||
#define SCREEN_HEIGHT 64
|
|
||||||
#define TRANSITION_FRAMERATE 30 // fps
|
|
||||||
#define IDLE_FRAMERATE 1 // in fps
|
|
||||||
#define COMPASS_DIAM 44
|
|
||||||
|
|
||||||
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
|
namespace graphics
|
||||||
|
|
||||||
namespace meshtastic
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// A text message frame + debug frame + all the node infos
|
// A text message frame + debug frame + all the node infos
|
||||||
@@ -55,10 +43,11 @@ 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
|
#ifdef SHOW_REDRAWS
|
||||||
static bool heartbeat = false;
|
static bool heartbeat = false;
|
||||||
#endif
|
#endif
|
||||||
@@ -215,38 +204,39 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *no
|
|||||||
// Draw GPS status summary
|
// Draw GPS status summary
|
||||||
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
|
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");
|
display->drawString(x, y - 2, "No GPS");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
|
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
|
||||||
if (!gps->getHasLock()) {
|
if (!gps->getHasLock())
|
||||||
|
{
|
||||||
display->drawString(x + 8, y - 2, "No sats");
|
display->drawString(x + 8, y - 2, "No sats");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (gps->getDOP() <= 100) {
|
else
|
||||||
display->drawString(x + 8, y - 2, "Ideal");
|
{
|
||||||
return;
|
char satsString[3];
|
||||||
}
|
uint8_t bar[2] = { 0 };
|
||||||
if (gps->getDOP() <= 200) {
|
|
||||||
display->drawString(x + 8, y - 2, "Exc.");
|
//Draw DOP signal bars
|
||||||
return;
|
for(int i = 0; i < 5; i++)
|
||||||
}
|
{
|
||||||
if (gps->getDOP() <= 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->getDOP() <= 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->getDOP() <= 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->getDOP() > 0) {
|
sprintf(satsString, "%d", gps->getNumSatellites());
|
||||||
display->drawString(x + 8, y - 2, "Poor");
|
display->drawString(x + 34, y - 2, satsString);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,28 +379,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
|
||||||
@@ -430,10 +433,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);
|
||||||
@@ -464,29 +470,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);
|
||||||
@@ -537,7 +554,7 @@ void Screen::handleSetOn(bool on)
|
|||||||
|
|
||||||
void Screen::setup()
|
void Screen::setup()
|
||||||
{
|
{
|
||||||
PeriodicTask::setup();
|
concurrency::PeriodicTask::setup();
|
||||||
|
|
||||||
// We don't set useDisplay until setup() is called, because some boards have a declaration of this object but the device
|
// We don't set useDisplay until setup() is called, because some boards have a declaration of this object but the device
|
||||||
// is never found when probing i2c and therefore we don't call setup and never want to do (invalid) accesses to this device.
|
// is never found when probing i2c and therefore we don't call setup and never want to do (invalid) accesses to this device.
|
||||||
@@ -601,7 +618,7 @@ void Screen::doTask()
|
|||||||
|
|
||||||
// Process incoming commands.
|
// Process incoming commands.
|
||||||
for (;;) {
|
for (;;) {
|
||||||
CmdItem cmd;
|
ScreenCmd cmd;
|
||||||
if (!cmdQueue.dequeue(&cmd, 0)) {
|
if (!cmdQueue.dequeue(&cmd, 0)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -752,7 +769,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
|
|
||||||
char channelStr[20];
|
char channelStr[20];
|
||||||
{
|
{
|
||||||
LockGuard guard(&lock);
|
concurrency::LockGuard guard(&lock);
|
||||||
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
|
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
|
||||||
|
|
||||||
// Display power status
|
// Display power status
|
||||||
@@ -763,7 +780,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
// Display nodes status
|
// Display nodes status
|
||||||
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
|
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, gpsStatus);
|
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
display->drawString(x, y + FONT_HEIGHT, channelStr);
|
display->drawString(x, y + FONT_HEIGHT, channelStr);
|
||||||
@@ -793,15 +810,20 @@ void Screen::adjustBrightness()
|
|||||||
dispdev.setBrightness(brightness);
|
dispdev.setBrightness(brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Screen::handleStatusUpdate(const Status *arg)
|
int Screen::handleStatusUpdate(const meshtastic::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()) {
|
switch(arg->getStatusType())
|
||||||
case STATUS_TYPE_NODE:
|
{
|
||||||
setFrames();
|
case STATUS_TYPE_NODE:
|
||||||
break;
|
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())
|
||||||
|
setFrames();
|
||||||
|
prevFrame = -1;
|
||||||
|
nodeDB.updateGUI = false;
|
||||||
|
nodeDB.updateTextMessage = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
setPeriod(1); // Update the screen right away
|
setPeriod(1); // Update the screen right away
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace meshtastic
|
} // namespace graphics
|
||||||
@@ -10,16 +10,14 @@
|
|||||||
#include <SSD1306Wire.h>
|
#include <SSD1306Wire.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "PeriodicTask.h"
|
#include "concurrency/PeriodicTask.h"
|
||||||
#include "TypedQueue.h"
|
#include "TypedQueue.h"
|
||||||
#include "lock.h"
|
#include "concurrency/LockGuard.h"
|
||||||
#include "PowerStatus.h"
|
#include "power.h"
|
||||||
#include "GPSStatus.h"
|
#include "commands.h"
|
||||||
#include "NodeStatus.h"
|
|
||||||
#include "Observer.h"
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace meshtastic
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
@@ -35,7 +33,7 @@ class DebugInfo
|
|||||||
/// Sets the name of the channel.
|
/// Sets the name of the channel.
|
||||||
void setChannelNameStatus(const char *name)
|
void setChannelNameStatus(const char *name)
|
||||||
{
|
{
|
||||||
LockGuard guard(&lock);
|
concurrency::LockGuard guard(&lock);
|
||||||
channelName = name;
|
channelName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,21 +48,21 @@ class DebugInfo
|
|||||||
std::string channelName;
|
std::string channelName;
|
||||||
|
|
||||||
/// Protects all of internal state.
|
/// Protects all of internal state.
|
||||||
Lock lock;
|
concurrency::Lock lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Deals with showing things on the screen of the device.
|
/**
|
||||||
//
|
* @brief This class deals with showing things on the screen of the device.
|
||||||
// Other than setup(), this class is thread-safe. All state-changing calls are
|
*
|
||||||
// queued and executed when the main loop calls us.
|
* @details Other than setup(), this class is thread-safe as long as drawFrame is not called
|
||||||
//
|
* multiple times simultaneously. All state-changing calls are queued and executed
|
||||||
// This class is thread-safe (as long as drawFrame is not called multiple times
|
* when the main loop calls us.
|
||||||
// simultaneously).
|
*/
|
||||||
class Screen : public PeriodicTask
|
class Screen : public concurrency::PeriodicTask
|
||||||
{
|
{
|
||||||
CallbackObserver<Screen, const Status *> powerStatusObserver = CallbackObserver<Screen, const Status *>(this, &Screen::handleStatusUpdate);
|
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||||
CallbackObserver<Screen, const Status *> gpsStatusObserver = CallbackObserver<Screen, const Status *>(this, &Screen::handleStatusUpdate);
|
CallbackObserver<Screen, const meshtastic::Status *> gpsStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||||
CallbackObserver<Screen, const Status *> nodeStatusObserver = CallbackObserver<Screen, const Status *>(this, &Screen::handleStatusUpdate);
|
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver = CallbackObserver<Screen, const meshtastic::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);
|
||||||
@@ -84,15 +82,15 @@ class Screen : public PeriodicTask
|
|||||||
handleSetOn(
|
handleSetOn(
|
||||||
false); // We handle off commands immediately, because they might be called because the CPU is shutting down
|
false); // We handle off commands immediately, because they might be called because the CPU is shutting down
|
||||||
else
|
else
|
||||||
enqueueCmd(CmdItem{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF});
|
enqueueCmd(ScreenCmd{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles a button press.
|
/// Handles a button press.
|
||||||
void onPress() { enqueueCmd(CmdItem{.cmd = Cmd::ON_PRESS}); }
|
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
|
||||||
|
|
||||||
// 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.
|
||||||
//
|
//
|
||||||
@@ -100,22 +98,22 @@ class Screen : public PeriodicTask
|
|||||||
// with the PIN.
|
// with the PIN.
|
||||||
void startBluetoothPinScreen(uint32_t pin)
|
void startBluetoothPinScreen(uint32_t pin)
|
||||||
{
|
{
|
||||||
CmdItem cmd;
|
ScreenCmd cmd;
|
||||||
cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN;
|
cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN;
|
||||||
cmd.bluetooth_pin = pin;
|
cmd.bluetooth_pin = pin;
|
||||||
enqueueCmd(cmd);
|
enqueueCmd(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops showing the bluetooth PIN screen.
|
/// Stops showing the bluetooth PIN screen.
|
||||||
void stopBluetoothPinScreen() { enqueueCmd(CmdItem{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
|
void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
|
||||||
|
|
||||||
/// Stops showing the boot screen.
|
/// Stops showing the boot screen.
|
||||||
void stopBootScreen() { enqueueCmd(CmdItem{.cmd = Cmd::STOP_BOOT_SCREEN}); }
|
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
|
||||||
|
|
||||||
/// Writes a string to the screen.
|
/// Writes a string to the screen.
|
||||||
void print(const char *text)
|
void print(const char *text)
|
||||||
{
|
{
|
||||||
CmdItem cmd;
|
ScreenCmd cmd;
|
||||||
cmd.cmd = Cmd::PRINT;
|
cmd.cmd = Cmd::PRINT;
|
||||||
// TODO(girts): strdup() here is scary, but we can't use std::string as
|
// TODO(girts): strdup() here is scary, but we can't use std::string as
|
||||||
// FreeRTOS queue is just dumbly copying memory contents. It would be
|
// FreeRTOS queue is just dumbly copying memory contents. It would be
|
||||||
@@ -160,7 +158,7 @@ class Screen : public PeriodicTask
|
|||||||
/// Returns a handle to the DebugInfo screen.
|
/// Returns a handle to the DebugInfo screen.
|
||||||
//
|
//
|
||||||
// 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() { return &debugInfo; }
|
DebugInfo* debug_info() { return &debugInfo; }
|
||||||
|
|
||||||
int handleStatusUpdate(const meshtastic::Status *arg);
|
int handleStatusUpdate(const meshtastic::Status *arg);
|
||||||
|
|
||||||
@@ -171,17 +169,7 @@ class Screen : public PeriodicTask
|
|||||||
void doTask() final;
|
void doTask() final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class Cmd {
|
struct ScreenCmd {
|
||||||
INVALID,
|
|
||||||
SET_ON,
|
|
||||||
SET_OFF,
|
|
||||||
ON_PRESS,
|
|
||||||
START_BLUETOOTH_PIN_SCREEN,
|
|
||||||
STOP_BLUETOOTH_PIN_SCREEN,
|
|
||||||
STOP_BOOT_SCREEN,
|
|
||||||
PRINT,
|
|
||||||
};
|
|
||||||
struct CmdItem {
|
|
||||||
Cmd cmd;
|
Cmd cmd;
|
||||||
union {
|
union {
|
||||||
uint32_t bluetooth_pin;
|
uint32_t bluetooth_pin;
|
||||||
@@ -190,7 +178,7 @@ class Screen : public PeriodicTask
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Enques given command item to be processed by main loop().
|
/// Enques given command item to be processed by main loop().
|
||||||
bool enqueueCmd(const CmdItem &cmd)
|
bool enqueueCmd(const ScreenCmd &cmd)
|
||||||
{
|
{
|
||||||
if (!useDisplay)
|
if (!useDisplay)
|
||||||
return true; // claim success if our display is not in use
|
return true; // claim success if our display is not in use
|
||||||
@@ -214,7 +202,7 @@ class Screen : public PeriodicTask
|
|||||||
static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
|
||||||
/// Queue of commands to execute in doTask.
|
/// Queue of commands to execute in doTask.
|
||||||
TypedQueue<CmdItem> cmdQueue;
|
TypedQueue<ScreenCmd> cmdQueue;
|
||||||
/// Whether we are using a display
|
/// Whether we are using a display
|
||||||
bool useDisplay = false;
|
bool useDisplay = false;
|
||||||
/// Whether the display is currently powered
|
/// Whether the display is currently powered
|
||||||
@@ -225,7 +213,9 @@ class Screen : public PeriodicTask
|
|||||||
|
|
||||||
/// Holds state for debug information
|
/// Holds state for debug information
|
||||||
DebugInfo debugInfo;
|
DebugInfo debugInfo;
|
||||||
|
|
||||||
/// Display device
|
/// Display device
|
||||||
|
/** @todo display abstraction */
|
||||||
#ifdef USE_SH1106
|
#ifdef USE_SH1106
|
||||||
SH1106Wire dispdev;
|
SH1106Wire dispdev;
|
||||||
#else
|
#else
|
||||||
@@ -235,4 +225,4 @@ class Screen : public PeriodicTask
|
|||||||
OLEDDisplayUi ui;
|
OLEDDisplayUi ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace meshtastic
|
} // namespace graphics
|
||||||
17
src/graphics/configs.h
Normal file
17
src/graphics/configs.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "fonts.h"
|
||||||
|
|
||||||
|
#define FONT_HEIGHT 14 // actually 13 for "Arial 10" but want a little extra space
|
||||||
|
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
||||||
|
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
||||||
|
#define SCREEN_WIDTH 128
|
||||||
|
#define SCREEN_HEIGHT 64
|
||||||
|
#define TRANSITION_FRAMERATE 30 // fps
|
||||||
|
#define IDLE_FRAMERATE 1 // in fps
|
||||||
|
#define COMPASS_DIAM 44
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
|
||||||
|
// if defined a pixel will blink to show redraws
|
||||||
|
// #define SHOW_REDRAWS
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
const uint8_t Custom_ArialMT_Plain_10[] PROGMEM = {
|
const uint8_t Custom_ArialMT_Plain_10[] PROGMEM = {
|
||||||
0x0A, // Width: 10
|
0x0A, // Width: 10
|
||||||
0x0A, // Height: 10
|
0x0A, // Height: 10
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#define SATELLITE_IMAGE_WIDTH 16
|
#define SATELLITE_IMAGE_WIDTH 16
|
||||||
#define SATELLITE_IMAGE_HEIGHT 15
|
#define SATELLITE_IMAGE_HEIGHT 15
|
||||||
const uint8_t SATELLITE_IMAGE[] PROGMEM = {0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0x20, 0x07, 0x70, 0x02,
|
const uint8_t SATELLITE_IMAGE[] PROGMEM = {0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0x20, 0x07, 0x70, 0x02,
|
||||||
@@ -10,12 +12,12 @@ const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99
|
|||||||
const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF };
|
const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF };
|
||||||
const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF };
|
const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF };
|
||||||
|
|
||||||
#include "icon.xbm"
|
#include "img/icon.xbm"
|
||||||
|
|
||||||
// We now programmatically draw our compass
|
// We now programmatically draw our compass
|
||||||
#if 0
|
#if 0
|
||||||
const
|
const
|
||||||
#include "compass.xbm"
|
#include "img/compass.xbm"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
50
src/lock.cpp
50
src/lock.cpp
@@ -1,50 +0,0 @@
|
|||||||
#include "lock.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace meshtastic
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef configUSE_PREEMPTION
|
|
||||||
Lock::Lock()
|
|
||||||
{
|
|
||||||
handle = xSemaphoreCreateBinary();
|
|
||||||
assert(handle);
|
|
||||||
assert(xSemaphoreGive(handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock::lock()
|
|
||||||
{
|
|
||||||
assert(xSemaphoreTake(handle, portMAX_DELAY));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock::unlock()
|
|
||||||
{
|
|
||||||
assert(xSemaphoreGive(handle));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
Lock::Lock()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock::lock()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock::unlock()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
LockGuard::LockGuard(Lock *lock) : lock(lock)
|
|
||||||
{
|
|
||||||
lock->lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
LockGuard::~LockGuard()
|
|
||||||
{
|
|
||||||
lock->unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace meshtastic
|
|
||||||
47
src/lock.h
47
src/lock.h
@@ -1,47 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "freertosinc.h"
|
|
||||||
|
|
||||||
namespace meshtastic
|
|
||||||
{
|
|
||||||
|
|
||||||
// Simple wrapper around FreeRTOS API for implementing a mutex lock.
|
|
||||||
class Lock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Lock();
|
|
||||||
|
|
||||||
Lock(const Lock &) = delete;
|
|
||||||
Lock &operator=(const Lock &) = delete;
|
|
||||||
|
|
||||||
/// Locks the lock.
|
|
||||||
//
|
|
||||||
// Must not be called from an ISR.
|
|
||||||
void lock();
|
|
||||||
|
|
||||||
// Unlocks the lock.
|
|
||||||
//
|
|
||||||
// Must not be called from an ISR.
|
|
||||||
void unlock();
|
|
||||||
|
|
||||||
private:
|
|
||||||
#ifdef configUSE_PREEMPTION
|
|
||||||
SemaphoreHandle_t handle;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// RAII lock guard.
|
|
||||||
class LockGuard
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LockGuard(Lock *lock);
|
|
||||||
~LockGuard();
|
|
||||||
|
|
||||||
LockGuard(const LockGuard &) = delete;
|
|
||||||
LockGuard &operator=(const LockGuard &) = delete;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Lock *lock;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace meshtastic
|
|
||||||
21
src/main.cpp
21
src/main.cpp
@@ -25,7 +25,7 @@
|
|||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NEMAGPS.h"
|
#include "NEMAGPS.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "Periodic.h"
|
#include "concurrency/Periodic.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "UBloxGPS.h"
|
#include "UBloxGPS.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
@@ -35,8 +35,9 @@
|
|||||||
#include "DSRRouter.h"
|
#include "DSRRouter.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
|
#include "timing.h"
|
||||||
#include <OneButton.h>
|
#include <OneButton.h>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
// #include <driver/rtc_io.h>
|
// #include <driver/rtc_io.h>
|
||||||
@@ -54,7 +55,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 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);
|
graphics::Screen screen(SSD1306_ADDRESS);
|
||||||
|
|
||||||
// Global power status
|
// Global power status
|
||||||
meshtastic::PowerStatus *powerStatus = new meshtastic::PowerStatus();
|
meshtastic::PowerStatus *powerStatus = new meshtastic::PowerStatus();
|
||||||
@@ -130,7 +131,7 @@ static uint32_t ledBlinker()
|
|||||||
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 2 : 1000);
|
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 2 : 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
Periodic ledPeriodic(ledBlinker);
|
concurrency::Periodic ledPeriodic(ledBlinker);
|
||||||
|
|
||||||
// Prepare for button presses
|
// Prepare for button presses
|
||||||
#ifdef BUTTON_PIN
|
#ifdef BUTTON_PIN
|
||||||
@@ -333,7 +334,7 @@ uint32_t axpDebugRead()
|
|||||||
return 30 * 1000;
|
return 30 * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
Periodic axpDebugOutput(axpDebugRead);
|
concurrency::Periodic axpDebugOutput(axpDebugRead);
|
||||||
axpDebugOutput.setup();
|
axpDebugOutput.setup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -346,7 +347,7 @@ void loop()
|
|||||||
powerFSM.run_machine();
|
powerFSM.run_machine();
|
||||||
service.loop();
|
service.loop();
|
||||||
|
|
||||||
periodicScheduler.loop();
|
concurrency::periodicScheduler.loop();
|
||||||
// axpDebugOutput.loop();
|
// axpDebugOutput.loop();
|
||||||
|
|
||||||
#ifdef DEBUG_PORT
|
#ifdef DEBUG_PORT
|
||||||
@@ -371,21 +372,21 @@ void loop()
|
|||||||
|
|
||||||
// Show boot screen for first 3 seconds, then switch to normal operation.
|
// Show boot screen for first 3 seconds, then switch to normal operation.
|
||||||
static bool showingBootScreen = true;
|
static bool showingBootScreen = true;
|
||||||
if (showingBootScreen && (millis() > 3000)) {
|
if (showingBootScreen && (timing::millis() > 3000)) {
|
||||||
screen.stopBootScreen();
|
screen.stopBootScreen();
|
||||||
showingBootScreen = false;
|
showingBootScreen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_STACK
|
#ifdef DEBUG_STACK
|
||||||
static uint32_t lastPrint = 0;
|
static uint32_t lastPrint = 0;
|
||||||
if (millis() - lastPrint > 10 * 1000L) {
|
if (timing::millis() - lastPrint > 10 * 1000L) {
|
||||||
lastPrint = millis();
|
lastPrint = timing::millis();
|
||||||
meshtastic::printThreadInfo("main");
|
meshtastic::printThreadInfo("main");
|
||||||
}
|
}
|
||||||
#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()->setChannelNameStatus(channelSettings.name);
|
screen.debug_info()->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)
|
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "PowerStatus.h"
|
#include "PowerStatus.h"
|
||||||
#include "GPSStatus.h"
|
#include "GPSStatus.h"
|
||||||
#include "NodeStatus.h"
|
#include "NodeStatus.h"
|
||||||
@@ -11,7 +11,7 @@ extern bool isCharging;
|
|||||||
extern bool isUSBPowered;
|
extern bool isUSBPowered;
|
||||||
|
|
||||||
// Global Screen singleton.
|
// Global Screen singleton.
|
||||||
extern meshtastic::Screen screen;
|
extern graphics::Screen screen;
|
||||||
//extern Observable<meshtastic::PowerStatus> newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class
|
//extern Observable<meshtastic::PowerStatus> newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class
|
||||||
|
|
||||||
//extern meshtastic::PowerStatus *powerStatus;
|
//extern meshtastic::PowerStatus *powerStatus;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PacketHistory.h"
|
#include "PacketHistory.h"
|
||||||
#include "PeriodicTask.h"
|
#include "../concurrency/PeriodicTask.h"
|
||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,12 +7,13 @@
|
|||||||
//#include "MeshBluetoothService.h"
|
//#include "MeshBluetoothService.h"
|
||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "Periodic.h"
|
#include "../concurrency/Periodic.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
#include "BluetoothUtil.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
|
#include "BluetoothUtil.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone.
|
receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone.
|
||||||
@@ -55,7 +56,7 @@ static uint32_t sendOwnerCb()
|
|||||||
return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
|
return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Periodic sendOwnerPeriod(sendOwnerCb);
|
static concurrency::Periodic sendOwnerPeriod(sendOwnerCb);
|
||||||
|
|
||||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||||
{
|
{
|
||||||
@@ -308,7 +309,7 @@ int MeshService::onGPSChanged(void *unused)
|
|||||||
|
|
||||||
// We limit our GPS broadcasts to a max rate
|
// We limit our GPS broadcasts to a max rate
|
||||||
static uint32_t lastGpsSend;
|
static uint32_t lastGpsSend;
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
if (lastGpsSend == 0 || now - lastGpsSend > radioConfig.preferences.position_broadcast_secs * 1000) {
|
if (lastGpsSend == 0 || now - lastGpsSend > radioConfig.preferences.position_broadcast_secs * 1000) {
|
||||||
lastGpsSend = now;
|
lastGpsSend = now;
|
||||||
DEBUG_MSG("Sending position to mesh\n");
|
DEBUG_MSG("Sending position to mesh\n");
|
||||||
|
|||||||
@@ -339,11 +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);
|
||||||
|
|
||||||
notifyObservers();
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -359,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,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;
|
||||||
@@ -391,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
|
||||||
@@ -398,6 +398,10 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
notifyObservers(); //If the node counts have changed, notify observers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,9 +95,9 @@ class NodeDB
|
|||||||
NodeInfo *getOrCreateNode(NodeNum n);
|
NodeInfo *getOrCreateNode(NodeNum n);
|
||||||
|
|
||||||
/// Notify observers of changes to the DB
|
/// Notify observers of changes to the DB
|
||||||
void notifyObservers() {
|
void notifyObservers(bool forceUpdate = false) {
|
||||||
// Notify observers of the current node state
|
// 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);
|
newStatus.notifyObservers(&status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "PacketHistory.h"
|
#include "PacketHistory.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
|
#include "../timing.h"
|
||||||
|
|
||||||
PacketHistory::PacketHistory()
|
PacketHistory::PacketHistory()
|
||||||
{
|
{
|
||||||
@@ -18,7 +19,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
|
|||||||
return false; // Not a floodable message ID, so we don't care
|
return false; // Not a floodable message ID, so we don't care
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
for (size_t i = 0; i < recentPackets.size();) {
|
for (size_t i = 0; i < recentPackets.size();) {
|
||||||
PacketRecord &r = recentPackets[i];
|
PacketRecord &r = recentPackets[i];
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "RadioInterface.h"
|
#include "RadioInterface.h"
|
||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
|
#include "timing.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
PhoneAPI::PhoneAPI()
|
PhoneAPI::PhoneAPI()
|
||||||
@@ -20,7 +21,7 @@ void PhoneAPI::init()
|
|||||||
void PhoneAPI::checkConnectionTimeout()
|
void PhoneAPI::checkConnectionTimeout()
|
||||||
{
|
{
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
bool newConnected = (millis() - lastContactMsec < radioConfig.preferences.phone_timeout_secs * 1000L);
|
bool newConnected = (timing::millis() - lastContactMsec < radioConfig.preferences.phone_timeout_secs * 1000L);
|
||||||
if (!newConnected) {
|
if (!newConnected) {
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
onConnectionChanged(isConnected);
|
onConnectionChanged(isConnected);
|
||||||
@@ -34,7 +35,7 @@ void PhoneAPI::checkConnectionTimeout()
|
|||||||
void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
||||||
{
|
{
|
||||||
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep
|
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep
|
||||||
lastContactMsec = millis();
|
lastContactMsec = timing::millis();
|
||||||
if (!isConnected) {
|
if (!isConnected) {
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
onConnectionChanged(isConnected);
|
onConnectionChanged(isConnected);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
|
#include "timing.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
@@ -155,7 +156,7 @@ size_t RadioInterface::beginSending(MeshPacket *p)
|
|||||||
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
|
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
|
||||||
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
|
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
|
||||||
|
|
||||||
lastTxStart = millis();
|
lastTxStart = timing::millis();
|
||||||
|
|
||||||
PacketHeader *h = (PacketHeader *)radiobuf;
|
PacketHeader *h = (PacketHeader *)radiobuf;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "PointerQueue.h"
|
#include "PointerQueue.h"
|
||||||
#include "WorkerThread.h"
|
#include "../concurrency/NotifiedWorkerThread.h"
|
||||||
#include "mesh.pb.h"
|
#include "mesh.pb.h"
|
||||||
|
|
||||||
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
|
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
|
||||||
@@ -43,7 +43,7 @@ typedef enum {
|
|||||||
*
|
*
|
||||||
* This defines the SOLE API for talking to radios (because soon we will have alternate radio implementations)
|
* This defines the SOLE API for talking to radios (because soon we will have alternate radio implementations)
|
||||||
*/
|
*/
|
||||||
class RadioInterface : protected NotifiedWorkerThread
|
class RadioInterface : protected concurrency::NotifiedWorkerThread
|
||||||
{
|
{
|
||||||
friend class MeshRadio; // for debugging we let that class touch pool
|
friend class MeshRadio; // for debugging we let that class touch pool
|
||||||
PointerQueue<MeshPacket> *rxDest = NULL;
|
PointerQueue<MeshPacket> *rxDest = NULL;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ static SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
|
|||||||
|
|
||||||
RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
|
RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
|
||||||
SPIClass &spi, PhysicalLayer *_iface)
|
SPIClass &spi, PhysicalLayer *_iface)
|
||||||
: PeriodicTask(0), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
|
: concurrency::PeriodicTask(0), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
|
||||||
{
|
{
|
||||||
assert(!instance); // We assume only one for now
|
assert(!instance); // We assume only one for now
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PeriodicTask.h"
|
#include "../concurrency/PeriodicTask.h"
|
||||||
#include "RadioInterface.h"
|
#include "RadioInterface.h"
|
||||||
|
|
||||||
#ifdef CubeCell_BoardPlus
|
#ifdef CubeCell_BoardPlus
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
#define INTERRUPT_ATTR
|
#define INTERRUPT_ATTR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class RadioLibInterface : public RadioInterface, private PeriodicTask
|
class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTask
|
||||||
{
|
{
|
||||||
/// Used as our notification from the ISR
|
/// Used as our notification from the ISR
|
||||||
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };
|
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
// ReliableRouter::ReliableRouter() {}
|
// ReliableRouter::ReliableRouter() {}
|
||||||
|
|
||||||
@@ -162,7 +163,7 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
|
|||||||
*/
|
*/
|
||||||
void ReliableRouter::doRetransmissions()
|
void ReliableRouter::doRetransmissions()
|
||||||
{
|
{
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
|
|
||||||
// FIXME, we should use a better datastructure rather than walking through this map.
|
// FIXME, we should use a better datastructure rather than walking through this map.
|
||||||
// for(auto el: pending) {
|
// for(auto el: pending) {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "FloodingRouter.h"
|
#include "FloodingRouter.h"
|
||||||
#include "PeriodicTask.h"
|
#include "../concurrency/PeriodicTask.h"
|
||||||
|
#include "../timing.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +49,7 @@ struct PendingPacket {
|
|||||||
PendingPacket() {}
|
PendingPacket() {}
|
||||||
PendingPacket(MeshPacket *p);
|
PendingPacket(MeshPacket *p);
|
||||||
|
|
||||||
void setNextTx() { nextTxMsec = millis() + random(20 * 1000L, 22 * 1000L); }
|
void setNextTx() { nextTxMsec = timing::millis() + random(20 * 1000L, 22 * 1000L); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class GlobalPacketIdHashFunction
|
class GlobalPacketIdHashFunction
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "PeriodicTask.h"
|
#include "concurrency/PeriodicTask.h"
|
||||||
#include "PowerStatus.h"
|
#include "PowerStatus.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
#define BAT_MILLIVOLTS_FULL 4100
|
#define BAT_MILLIVOLTS_FULL 4100
|
||||||
#define BAT_MILLIVOLTS_EMPTY 3500
|
#define BAT_MILLIVOLTS_EMPTY 3500
|
||||||
|
|
||||||
class Power : public PeriodicTask
|
class Power : public concurrency::PeriodicTask
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "timing.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
|
|
||||||
@@ -123,11 +123,11 @@ bool doPreflightSleep()
|
|||||||
/// Tell devices we are going to sleep and wait for them to handle things
|
/// Tell devices we are going to sleep and wait for them to handle things
|
||||||
static void waitEnterSleep()
|
static void waitEnterSleep()
|
||||||
{
|
{
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
while (!doPreflightSleep()) {
|
while (!doPreflightSleep()) {
|
||||||
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
|
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
|
||||||
|
|
||||||
if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
|
if (timing::millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
|
||||||
recordCriticalError(ErrSleepEnterWait);
|
recordCriticalError(ErrSleepEnterWait);
|
||||||
assert(0); // FIXME - for now we just restart, need to fix bug #167
|
assert(0); // FIXME - for now we just restart, need to fix bug #167
|
||||||
break;
|
break;
|
||||||
|
|||||||
10
src/timing.cpp
Normal file
10
src/timing.cpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include "timing.h"
|
||||||
|
#include "freertosinc.h"
|
||||||
|
|
||||||
|
namespace timing {
|
||||||
|
|
||||||
|
uint32_t millis() {
|
||||||
|
return xTaskGetTickCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace timing
|
||||||
9
src/timing.h
Normal file
9
src/timing.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace timing {
|
||||||
|
|
||||||
|
uint32_t millis();
|
||||||
|
|
||||||
|
} // namespace timing
|
||||||
Reference in New Issue
Block a user