mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-24 11:40:27 +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/tasks.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",
|
||||
"protobufs",
|
||||
"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.
|
||||
|
||||
- TTGO T-Beam
|
||||
|
||||
- [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)
|
||||
- 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-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
|
||||
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
|
||||
- [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
|
||||
- CN - 470MHz
|
||||
- EU - 868MHz, 433MHz
|
||||
- full list of LoRa frequencies per region is available [here](https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country.html)
|
||||
|
||||
Getting a version that includes a screen is optional, but highly recommended.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ COUNTRIES="US EU433 EU865 CN JP"
|
||||
#COUNTRIES=US
|
||||
#COUNTRIES=CN
|
||||
|
||||
BOARDS="ttgo-lora32-v2 ttgo-lora32-v1 tbeam heltec"
|
||||
BOARDS="ttgo-lora32-v2 ttgo-lora32-v1 tbeam heltec tbeam0.7"
|
||||
#BOARDS=tbeam
|
||||
|
||||
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.
|
||||
|
||||
- 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
|
||||
|
||||
Items to complete before 1.0.
|
||||
|
||||
@@ -1,5 +1,36 @@
|
||||
# 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
|
||||
|
||||
RAM investigation.
|
||||
|
||||
@@ -74,7 +74,7 @@ lib_deps =
|
||||
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||
https://github.com/meshtastic/arduino-fsm.git
|
||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git
|
||||
https://github.com/meshtastic/RadioLib.git
|
||||
https://github.com/meshtastic/RadioLib.git#6aa38a85856012c99c4e9b4e7cee35e37671a4bc
|
||||
https://github.com/meshtastic/TinyGPSPlus.git
|
||||
|
||||
; Common settings for ESP targes, mixin with extends = esp32_base
|
||||
@@ -90,7 +90,7 @@ build_flags =
|
||||
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||
lib_ignore = segger_rtt
|
||||
platform_packages =
|
||||
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#f26c4f96fefd13ed0ed042e27954f8aba6328f6b
|
||||
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#71ed4002c953d8c87f44ed27e34fe0735f99013e
|
||||
|
||||
; The 1.0 release of the TBEAM board
|
||||
[env:tbeam]
|
||||
@@ -99,17 +99,16 @@ board = ttgo-t-beam
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
https://github.com/meshtastic/AXP202X_Library.git
|
||||
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TBEAM_V10
|
||||
|
||||
; The original TBEAM board without the AXP power chip and a few other changes
|
||||
; Note: I've heard reports this didn't work. Disabled until someone with a 0.7 can test and debug.
|
||||
;[env:tbeam0.7]
|
||||
;extends = esp32_base
|
||||
;board = ttgo-t-beam
|
||||
;build_flags =
|
||||
; ${esp32_base.build_flags} -D TBEAM_V07
|
||||
[env:tbeam0.7]
|
||||
extends = esp32_base
|
||||
board = ttgo-t-beam
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TBEAM_V07
|
||||
|
||||
[env:heltec]
|
||||
;build_type = debug ; to make it possible to step through our jtag debugger
|
||||
|
||||
@@ -17,13 +17,15 @@ namespace meshtastic {
|
||||
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
|
||||
int32_t altitude = 0;
|
||||
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use)
|
||||
uint32_t heading = 0;
|
||||
uint32_t numSatellites = 0;
|
||||
|
||||
public:
|
||||
|
||||
GPSStatus() {
|
||||
statusType = STATUS_TYPE_GPS;
|
||||
}
|
||||
GPSStatus( bool hasLock, bool isConnected, int32_t latitude, int32_t longitude, int32_t altitude, uint32_t dop ) : Status()
|
||||
GPSStatus( bool hasLock, bool isConnected, int32_t latitude, int32_t longitude, int32_t altitude, uint32_t dop, uint32_t heading, uint32_t numSatellites ) : Status()
|
||||
{
|
||||
this->hasLock = hasLock;
|
||||
this->isConnected = isConnected;
|
||||
@@ -31,6 +33,8 @@ namespace meshtastic {
|
||||
this->longitude = longitude;
|
||||
this->altitude = altitude;
|
||||
this->dop = dop;
|
||||
this->heading = heading;
|
||||
this->numSatellites = numSatellites;
|
||||
}
|
||||
GPSStatus(const GPSStatus &);
|
||||
GPSStatus &operator=(const GPSStatus &);
|
||||
@@ -70,6 +74,16 @@ namespace meshtastic {
|
||||
return dop;
|
||||
}
|
||||
|
||||
uint32_t getHeading() const
|
||||
{
|
||||
return heading;
|
||||
}
|
||||
|
||||
uint32_t getNumSatellites() const
|
||||
{
|
||||
return numSatellites;
|
||||
}
|
||||
|
||||
bool matches(const GPSStatus *newStatus) const
|
||||
{
|
||||
return (
|
||||
@@ -78,7 +92,9 @@ namespace meshtastic {
|
||||
newStatus->latitude != latitude ||
|
||||
newStatus->longitude != longitude ||
|
||||
newStatus->altitude != altitude ||
|
||||
newStatus->dop != dop
|
||||
newStatus->dop != dop ||
|
||||
newStatus->heading != heading ||
|
||||
newStatus->numSatellites != numSatellites
|
||||
);
|
||||
}
|
||||
int updateStatus(const GPSStatus *newStatus) {
|
||||
@@ -93,9 +109,11 @@ namespace meshtastic {
|
||||
longitude = newStatus->longitude;
|
||||
altitude = newStatus->altitude;
|
||||
dop = newStatus->dop;
|
||||
heading = newStatus->heading;
|
||||
numSatellites = newStatus->numSatellites;
|
||||
}
|
||||
if(isDirty) {
|
||||
DEBUG_MSG("New GPS pos lat=%f, lon=%f, alt=%d, pdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2);
|
||||
DEBUG_MSG("New GPS pos lat=%f, lon=%f, alt=%d, pdop=%f, heading=%f, sats=%d\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5, numSatellites);
|
||||
onNewStatus.notifyObservers(this);
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -15,13 +15,17 @@ namespace meshtastic {
|
||||
uint8_t numOnline = 0;
|
||||
uint8_t numTotal = 0;
|
||||
|
||||
uint8_t lastNumTotal = 0;
|
||||
|
||||
public:
|
||||
bool forceUpdate = false;
|
||||
|
||||
NodeStatus() {
|
||||
statusType = STATUS_TYPE_NODE;
|
||||
}
|
||||
NodeStatus( uint8_t numOnline, uint8_t numTotal ) : Status()
|
||||
NodeStatus( uint8_t numOnline, uint8_t numTotal, bool forceUpdate = false ) : Status()
|
||||
{
|
||||
this->forceUpdate = forceUpdate;
|
||||
this->numOnline = numOnline;
|
||||
this->numTotal = numTotal;
|
||||
}
|
||||
@@ -43,6 +47,11 @@ namespace meshtastic {
|
||||
return numTotal;
|
||||
}
|
||||
|
||||
uint8_t getLastNumTotal() const
|
||||
{
|
||||
return lastNumTotal;
|
||||
}
|
||||
|
||||
bool matches(const NodeStatus *newStatus) const
|
||||
{
|
||||
return (
|
||||
@@ -52,6 +61,7 @@ namespace meshtastic {
|
||||
}
|
||||
int updateStatus(const NodeStatus *newStatus) {
|
||||
// Only update the status if values have actually changed
|
||||
lastNumTotal = numTotal;
|
||||
bool isDirty;
|
||||
{
|
||||
isDirty = matches(newStatus);
|
||||
@@ -59,7 +69,7 @@ namespace meshtastic {
|
||||
numOnline = newStatus->getNumOnline();
|
||||
numTotal = newStatus->getNumTotal();
|
||||
}
|
||||
if(isDirty) {
|
||||
if(isDirty || newStatus->forceUpdate) {
|
||||
DEBUG_MSG("Node status update: %d online, %d total\n", numOnline, numTotal);
|
||||
onNewStatus.notifyObservers(this);
|
||||
}
|
||||
|
||||
@@ -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 "sleep.h"
|
||||
|
||||
|
||||
#ifdef TBEAM_V10
|
||||
|
||||
// FIXME. nasty hack cleanup how we load axp192
|
||||
@@ -19,7 +18,7 @@ bool Power::setup()
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
return axp192_found;
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
#include "NodeDB.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "screen.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
#include "timing.h"
|
||||
|
||||
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
|
||||
// !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);
|
||||
}
|
||||
@@ -130,7 +131,7 @@ static void onEnter()
|
||||
|
||||
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 (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
|
||||
|
||||
#include "PeriodicTask.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
/**
|
||||
* Periodically invoke a callback.
|
||||
*
|
||||
* This just provides C style callback conventions rather than a virtual function - FIXME, remove?
|
||||
* @brief Periodically invoke a callback. This just provides C-style callback conventions
|
||||
* rather than a virtual function - FIXME, remove?
|
||||
*/
|
||||
class Periodic : public PeriodicTask
|
||||
{
|
||||
@@ -17,5 +17,10 @@ class Periodic : public PeriodicTask
|
||||
Periodic(uint32_t (*_callback)()) : callback(_callback) {}
|
||||
|
||||
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 "RadioLibInterface.h"
|
||||
#include "configuration.h"
|
||||
#include "lock.h"
|
||||
#include "../concurrency/LockGuard.h"
|
||||
#include "../timing.h"
|
||||
#include <Arduino.h>
|
||||
#include <BLE2902.h>
|
||||
#include <CRC32.h>
|
||||
#include <Update.h>
|
||||
#include <esp_gatt_defs.h>
|
||||
|
||||
using namespace meshtastic;
|
||||
//using namespace meshtastic;
|
||||
|
||||
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 updateExpectedSize, updateActualSize;
|
||||
|
||||
Lock *updateLock;
|
||||
concurrency::Lock *updateLock;
|
||||
|
||||
class TotalSizeCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
@@ -30,7 +31,7 @@ class TotalSizeCharacteristic : public CallbackCharacteristic
|
||||
|
||||
void onWrite(BLECharacteristic *c)
|
||||
{
|
||||
LockGuard g(updateLock);
|
||||
concurrency::LockGuard g(updateLock);
|
||||
// Check if there is enough to OTA Update
|
||||
uint32_t len = getValue32(c, 0);
|
||||
updateExpectedSize = len;
|
||||
@@ -65,7 +66,7 @@ class DataCharacteristic : public CallbackCharacteristic
|
||||
|
||||
void onWrite(BLECharacteristic *c)
|
||||
{
|
||||
LockGuard g(updateLock);
|
||||
concurrency::LockGuard g(updateLock);
|
||||
std::string value = c->getValue();
|
||||
uint32_t len = value.length();
|
||||
assert(len <= MAX_BLOCKSIZE);
|
||||
@@ -89,7 +90,7 @@ class CRC32Characteristic : public CallbackCharacteristic
|
||||
|
||||
void onWrite(BLECharacteristic *c)
|
||||
{
|
||||
LockGuard g(updateLock);
|
||||
concurrency::LockGuard g(updateLock);
|
||||
uint32_t expectedCRC = getValue32(c, 0);
|
||||
uint32_t actualCRC = crc.finalize();
|
||||
DEBUG_MSG("expected CRC %u\n", expectedCRC);
|
||||
@@ -106,7 +107,7 @@ class CRC32Characteristic : public CallbackCharacteristic
|
||||
} else {
|
||||
if (Update.end()) {
|
||||
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
|
||||
rebootAtMsec = millis() + 5000;
|
||||
rebootAtMsec = timing::millis() + 5000;
|
||||
} else {
|
||||
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
|
||||
}
|
||||
@@ -124,7 +125,7 @@ class CRC32Characteristic : public CallbackCharacteristic
|
||||
|
||||
void bluetoothRebootCheck()
|
||||
{
|
||||
if (rebootAtMsec && millis() > rebootAtMsec) {
|
||||
if (rebootAtMsec && timing::millis() > rebootAtMsec) {
|
||||
DEBUG_MSG("Rebooting for update\n");
|
||||
ESP.restart();
|
||||
}
|
||||
@@ -137,7 +138,7 @@ See bluetooth-api.md
|
||||
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion)
|
||||
{
|
||||
if (!updateLock)
|
||||
updateLock = new Lock();
|
||||
updateLock = new concurrency::Lock();
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *service = server->createService(BLEUUID("cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"), 25, 0);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
#include "GPS.h"
|
||||
#include "configuration.h"
|
||||
#include "time.h"
|
||||
#include "timing.h"
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef GPS_RX_PIN
|
||||
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
|
||||
@@ -27,7 +27,7 @@ void readFromRTC()
|
||||
struct timeval tv; /* btw settimeofday() is helpfull here too*/
|
||||
|
||||
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);
|
||||
timeStartMsec = now;
|
||||
@@ -68,11 +68,9 @@ void perhapsSetRTC(struct tm &t)
|
||||
perhapsSetRTC(&tv);
|
||||
}
|
||||
|
||||
#include <time.h>
|
||||
|
||||
uint32_t getTime()
|
||||
{
|
||||
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
|
||||
return ((timing::millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
|
||||
}
|
||||
|
||||
uint32_t getValidTime()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "Observer.h"
|
||||
#include "GPSStatus.h"
|
||||
#include "PeriodicTask.h"
|
||||
#include "../concurrency/PeriodicTask.h"
|
||||
#include "sys/time.h"
|
||||
|
||||
/// 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 altitude = 0;
|
||||
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use)
|
||||
uint32_t heading = 0; // Heading of motion, in degrees * 10^-5
|
||||
uint32_t numSatellites = 0;
|
||||
|
||||
bool isConnected = false; // Do we have a GPS we are talking to
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "NEMAGPS.h"
|
||||
#include "configuration.h"
|
||||
#include "timing.h"
|
||||
|
||||
static int32_t toDegInt(RawDegrees d)
|
||||
{
|
||||
@@ -19,7 +20,7 @@ void NEMAGPS::loop()
|
||||
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
|
||||
// serial chars at whatever rate)
|
||||
lastUpdateMsec = now;
|
||||
@@ -54,12 +55,18 @@ void NEMAGPS::loop()
|
||||
longitude = toDegInt(loc.lng);
|
||||
}
|
||||
// Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
|
||||
if(reader.hdop.isValid()) {
|
||||
if (reader.hdop.isValid()) {
|
||||
dop = reader.hdop.value();
|
||||
}
|
||||
if (reader.course.isValid()) {
|
||||
heading = reader.course.value() * 1e3; //Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
|
||||
}
|
||||
if (reader.satellites.isValid()) {
|
||||
numSatellites = reader.satellites.value();
|
||||
}
|
||||
|
||||
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
||||
DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2);
|
||||
DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5);
|
||||
|
||||
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
|
||||
if (hasValidLocation)
|
||||
@@ -67,7 +74,7 @@ void NEMAGPS::loop()
|
||||
}
|
||||
|
||||
// Notify any status instances that are observing us
|
||||
const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop);
|
||||
const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
|
||||
newStatus.notifyObservers(&status);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "GPS.h"
|
||||
#include "Observer.h"
|
||||
#include "PeriodicTask.h"
|
||||
#include "../concurrency/PeriodicTask.h"
|
||||
#include "TinyGPS++.h"
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "sleep.h"
|
||||
#include <assert.h>
|
||||
|
||||
UBloxGPS::UBloxGPS() : PeriodicTask()
|
||||
UBloxGPS::UBloxGPS() : concurrency::PeriodicTask()
|
||||
{
|
||||
notifySleepObserver.observe(¬ifySleep);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ bool UBloxGPS::setup()
|
||||
ok = ublox.saveConfiguration(3000);
|
||||
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;
|
||||
} 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);
|
||||
altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters
|
||||
dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
|
||||
heading = ublox.getHeading(0);
|
||||
numSatellites = ublox.getSIV(0);
|
||||
|
||||
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
|
||||
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
|
||||
@@ -129,7 +131,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
||||
wantNewLocation = true;
|
||||
|
||||
// Notify any status instances that are observing us
|
||||
const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop);
|
||||
const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
|
||||
newStatus.notifyObservers(&status);
|
||||
|
||||
// Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "GPS.h"
|
||||
#include "Observer.h"
|
||||
#include "PeriodicTask.h"
|
||||
#include "../concurrency/PeriodicTask.h"
|
||||
#include "SparkFun_Ublox_Arduino_Library.h"
|
||||
|
||||
/**
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
* 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;
|
||||
|
||||
|
||||
@@ -26,28 +26,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "configuration.h"
|
||||
#include "fonts.h"
|
||||
#include "images.h"
|
||||
#include "graphics/images.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "screen.h"
|
||||
#include "Screen.h"
|
||||
#include "utils.h"
|
||||
#include "configs.h"
|
||||
|
||||
#define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space
|
||||
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
||||
#ifdef USE_SH1106
|
||||
#define SCREEN_WIDTH 132
|
||||
#else
|
||||
#define SCREEN_WIDTH 128
|
||||
#endif
|
||||
#define SCREEN_HEIGHT 64
|
||||
#define TRANSITION_FRAMERATE 30 // fps
|
||||
#define IDLE_FRAMERATE 1 // in fps
|
||||
#define COMPASS_DIAM 44
|
||||
using namespace meshtastic; /** @todo remove */
|
||||
|
||||
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
|
||||
|
||||
namespace meshtastic
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
// 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 char btPIN[16] = "888888";
|
||||
|
||||
uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C};
|
||||
uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C };
|
||||
uint8_t imgSatellite[8] = { 0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70 };
|
||||
|
||||
uint32_t dopThresholds[5] = { 2000, 1000, 500, 200, 100 };
|
||||
|
||||
// if defined a pixel will blink to show redraws
|
||||
// #define SHOW_REDRAWS
|
||||
#ifdef SHOW_REDRAWS
|
||||
static bool heartbeat = false;
|
||||
#endif
|
||||
@@ -215,38 +204,39 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *no
|
||||
// Draw GPS status summary
|
||||
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
|
||||
{
|
||||
if (!gps->getIsConnected()) {
|
||||
if (!gps->getIsConnected())
|
||||
{
|
||||
display->drawString(x, y - 2, "No GPS");
|
||||
return;
|
||||
}
|
||||
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
|
||||
if (!gps->getHasLock()) {
|
||||
if (!gps->getHasLock())
|
||||
{
|
||||
display->drawString(x + 8, y - 2, "No sats");
|
||||
return;
|
||||
}
|
||||
if (gps->getDOP() <= 100) {
|
||||
display->drawString(x + 8, y - 2, "Ideal");
|
||||
return;
|
||||
}
|
||||
if (gps->getDOP() <= 200) {
|
||||
display->drawString(x + 8, y - 2, "Exc.");
|
||||
return;
|
||||
}
|
||||
if (gps->getDOP() <= 500) {
|
||||
display->drawString(x + 8, y - 2, "Good");
|
||||
return;
|
||||
}
|
||||
if (gps->getDOP() <= 1000) {
|
||||
display->drawString(x + 8, y - 2, "Mod.");
|
||||
return;
|
||||
}
|
||||
if (gps->getDOP() <= 2000) {
|
||||
display->drawString(x + 8, y - 2, "Fair");
|
||||
return;
|
||||
}
|
||||
if (gps->getDOP() > 0) {
|
||||
display->drawString(x + 8, y - 2, "Poor");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
char satsString[3];
|
||||
uint8_t bar[2] = { 0 };
|
||||
|
||||
//Draw DOP signal bars
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
if (gps->getDOP() <= dopThresholds[i])
|
||||
bar[0] = ~((1 << (5 - i)) - 1);
|
||||
else
|
||||
bar[0] = 0b10000000;
|
||||
//bar[1] = bar[0];
|
||||
display->drawFastImage(x + 9 + (i * 2), y, 2, 8, bar);
|
||||
}
|
||||
|
||||
//Draw satellite image
|
||||
display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
|
||||
|
||||
//Draw the number of satellites
|
||||
sprintf(satsString, "%d", gps->getNumSatellites());
|
||||
display->drawString(x + 34, y - 2, satsString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,28 +379,41 @@ static bool hasPosition(NodeInfo *n)
|
||||
static size_t nodeIndex;
|
||||
static int8_t prevFrame = -1;
|
||||
|
||||
// Draw the compass and arrow pointing to location
|
||||
static void drawCompass(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
|
||||
// Draw the arrow pointing to a node's location
|
||||
static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
|
||||
{
|
||||
// display->drawXbm(compassX, compassY, compass_width, compass_height,
|
||||
// (const uint8_t *)compass_bits);
|
||||
|
||||
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
||||
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
||||
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
|
||||
|
||||
Point *points[] = {&tip, &tail, &leftArrow, &rightArrow};
|
||||
|
||||
Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
points[i]->rotate(headingRadian);
|
||||
points[i]->scale(COMPASS_DIAM * 0.6);
|
||||
points[i]->translate(compassX, compassY);
|
||||
arrowPoints[i]->rotate(headingRadian);
|
||||
arrowPoints[i]->scale(COMPASS_DIAM * 0.6);
|
||||
arrowPoints[i]->translate(compassX, compassY);
|
||||
}
|
||||
drawLine(display, tip, tail);
|
||||
drawLine(display, leftArrow, tip);
|
||||
drawLine(display, rightArrow, tip);
|
||||
}
|
||||
|
||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||
// Draw the compass heading
|
||||
static void drawCompassHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading)
|
||||
{
|
||||
Point N1(-0.04f, -0.65f), N2( 0.04f, -0.65f);
|
||||
Point N3(-0.04f, -0.55f), N4( 0.04f, -0.55f);
|
||||
Point *rosePoints[] = {&N1, &N2, &N3, &N4};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
rosePoints[i]->rotate(myHeading);
|
||||
rosePoints[i]->scale(COMPASS_DIAM);
|
||||
rosePoints[i]->translate(compassX, compassY);
|
||||
}
|
||||
drawLine(display, N1, N3);
|
||||
drawLine(display, N2, N4);
|
||||
drawLine(display, N1, N4);
|
||||
}
|
||||
|
||||
/// Convert an integer GPS coords to a floating point
|
||||
@@ -430,10 +433,13 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
|
||||
n = nodeDB.getNodeByIndex(nodeIndex);
|
||||
}
|
||||
|
||||
// We just changed to a new node screen, ask that node for updated state
|
||||
displayedNodeNum = n->num;
|
||||
service.sendNetworkPing(displayedNodeNum, true);
|
||||
|
||||
// We just changed to a new node screen, ask that node for updated state if it's older than 2 minutes
|
||||
if(sinceLastSeen(n) > 120)
|
||||
{
|
||||
service.sendNetworkPing(displayedNodeNum, true);
|
||||
}
|
||||
}
|
||||
|
||||
NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);
|
||||
@@ -464,29 +470,40 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
|
||||
|
||||
// coordinates for the center of the compass/circle
|
||||
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2;
|
||||
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 5, compassY = y + SCREEN_HEIGHT / 2;
|
||||
bool hasNodeHeading = false;
|
||||
|
||||
if (ourNode && hasPosition(ourNode) && hasPosition(node)) { // display direction toward node
|
||||
Position &op = ourNode->position, &p = node->position;
|
||||
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
if (d < 2000)
|
||||
snprintf(distStr, sizeof(distStr), "%.0f m", d);
|
||||
else
|
||||
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
|
||||
if(ourNode && hasPosition(ourNode))
|
||||
{
|
||||
Position &op = ourNode->position;
|
||||
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
drawCompassHeading(display, compassX, compassY, myHeading);
|
||||
|
||||
// FIXME, also keep the guess at the operators heading and add/substract
|
||||
// it. currently we don't do this and instead draw north up only.
|
||||
float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
float myHeading = estimatedHeading(DegD(p.latitude_i), DegD(p.longitude_i));
|
||||
headingRadian = bearingToOther - myHeading;
|
||||
drawCompass(display, compassX, compassY, headingRadian);
|
||||
} else { // direction to node is unknown so display question mark
|
||||
if(hasPosition(node))
|
||||
{
|
||||
// display direction toward node
|
||||
hasNodeHeading = true;
|
||||
Position &p = node->position;
|
||||
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
if (d < 2000)
|
||||
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_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
|
||||
|
||||
display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?");
|
||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||
}
|
||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||
|
||||
|
||||
// Must be after distStr is populated
|
||||
drawColumns(display, x, y, fields);
|
||||
@@ -537,7 +554,7 @@ void Screen::handleSetOn(bool on)
|
||||
|
||||
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
|
||||
// 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.
|
||||
for (;;) {
|
||||
CmdItem cmd;
|
||||
ScreenCmd cmd;
|
||||
if (!cmdQueue.dequeue(&cmd, 0)) {
|
||||
break;
|
||||
}
|
||||
@@ -752,7 +769,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
|
||||
char channelStr[20];
|
||||
{
|
||||
LockGuard guard(&lock);
|
||||
concurrency::LockGuard guard(&lock);
|
||||
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
|
||||
|
||||
// Display power status
|
||||
@@ -763,7 +780,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
// Display nodes status
|
||||
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
|
||||
// Display GPS status
|
||||
drawGPS(display, x + (SCREEN_WIDTH * 0.66), y + 2, gpsStatus);
|
||||
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
|
||||
}
|
||||
|
||||
display->drawString(x, y + FONT_HEIGHT, channelStr);
|
||||
@@ -793,15 +810,20 @@ void Screen::adjustBrightness()
|
||||
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());
|
||||
switch (arg->getStatusType()) {
|
||||
case STATUS_TYPE_NODE:
|
||||
setFrames();
|
||||
break;
|
||||
//DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
|
||||
switch(arg->getStatusType())
|
||||
{
|
||||
case STATUS_TYPE_NODE:
|
||||
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())
|
||||
setFrames();
|
||||
prevFrame = -1;
|
||||
nodeDB.updateGUI = false;
|
||||
nodeDB.updateTextMessage = false;
|
||||
break;
|
||||
}
|
||||
setPeriod(1); // Update the screen right away
|
||||
return 0;
|
||||
}
|
||||
} // namespace meshtastic
|
||||
} // namespace graphics
|
||||
@@ -10,16 +10,14 @@
|
||||
#include <SSD1306Wire.h>
|
||||
#endif
|
||||
|
||||
#include "PeriodicTask.h"
|
||||
#include "concurrency/PeriodicTask.h"
|
||||
#include "TypedQueue.h"
|
||||
#include "lock.h"
|
||||
#include "PowerStatus.h"
|
||||
#include "GPSStatus.h"
|
||||
#include "NodeStatus.h"
|
||||
#include "Observer.h"
|
||||
#include "concurrency/LockGuard.h"
|
||||
#include "power.h"
|
||||
#include "commands.h"
|
||||
#include <string>
|
||||
|
||||
namespace meshtastic
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
// Forward declarations
|
||||
@@ -35,7 +33,7 @@ class DebugInfo
|
||||
/// Sets the name of the channel.
|
||||
void setChannelNameStatus(const char *name)
|
||||
{
|
||||
LockGuard guard(&lock);
|
||||
concurrency::LockGuard guard(&lock);
|
||||
channelName = name;
|
||||
}
|
||||
|
||||
@@ -50,21 +48,21 @@ class DebugInfo
|
||||
std::string channelName;
|
||||
|
||||
/// Protects all of internal state.
|
||||
Lock lock;
|
||||
concurrency::Lock lock;
|
||||
};
|
||||
|
||||
/// 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.
|
||||
//
|
||||
// This class is thread-safe (as long as drawFrame is not called multiple times
|
||||
// simultaneously).
|
||||
class Screen : public PeriodicTask
|
||||
/**
|
||||
* @brief This class deals with showing things on the screen of the device.
|
||||
*
|
||||
* @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
|
||||
* when the main loop calls us.
|
||||
*/
|
||||
class Screen : public concurrency::PeriodicTask
|
||||
{
|
||||
CallbackObserver<Screen, const Status *> powerStatusObserver = CallbackObserver<Screen, const Status *>(this, &Screen::handleStatusUpdate);
|
||||
CallbackObserver<Screen, const Status *> gpsStatusObserver = CallbackObserver<Screen, const Status *>(this, &Screen::handleStatusUpdate);
|
||||
CallbackObserver<Screen, const Status *> nodeStatusObserver = CallbackObserver<Screen, const Status *>(this, &Screen::handleStatusUpdate);
|
||||
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||
CallbackObserver<Screen, const meshtastic::Status *> gpsStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||
|
||||
public:
|
||||
Screen(uint8_t address, int sda = -1, int scl = -1);
|
||||
@@ -84,15 +82,15 @@ class Screen : public PeriodicTask
|
||||
handleSetOn(
|
||||
false); // We handle off commands immediately, because they might be called because the CPU is shutting down
|
||||
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.
|
||||
void onPress() { enqueueCmd(CmdItem{.cmd = Cmd::ON_PRESS}); }
|
||||
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
|
||||
|
||||
// Implementation to Adjust Brightness
|
||||
void adjustBrightness();
|
||||
int brightness = 150;
|
||||
uint8_t brightness = 150;
|
||||
|
||||
/// Starts showing the Bluetooth PIN screen.
|
||||
//
|
||||
@@ -100,22 +98,22 @@ class Screen : public PeriodicTask
|
||||
// with the PIN.
|
||||
void startBluetoothPinScreen(uint32_t pin)
|
||||
{
|
||||
CmdItem cmd;
|
||||
ScreenCmd cmd;
|
||||
cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN;
|
||||
cmd.bluetooth_pin = pin;
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
void stopBootScreen() { enqueueCmd(CmdItem{.cmd = Cmd::STOP_BOOT_SCREEN}); }
|
||||
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
|
||||
|
||||
/// Writes a string to the screen.
|
||||
void print(const char *text)
|
||||
{
|
||||
CmdItem cmd;
|
||||
ScreenCmd cmd;
|
||||
cmd.cmd = Cmd::PRINT;
|
||||
// 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
|
||||
@@ -160,7 +158,7 @@ class Screen : public PeriodicTask
|
||||
/// Returns a handle to the DebugInfo screen.
|
||||
//
|
||||
// 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);
|
||||
|
||||
@@ -171,17 +169,7 @@ class Screen : public PeriodicTask
|
||||
void doTask() final;
|
||||
|
||||
private:
|
||||
enum class Cmd {
|
||||
INVALID,
|
||||
SET_ON,
|
||||
SET_OFF,
|
||||
ON_PRESS,
|
||||
START_BLUETOOTH_PIN_SCREEN,
|
||||
STOP_BLUETOOTH_PIN_SCREEN,
|
||||
STOP_BOOT_SCREEN,
|
||||
PRINT,
|
||||
};
|
||||
struct CmdItem {
|
||||
struct ScreenCmd {
|
||||
Cmd cmd;
|
||||
union {
|
||||
uint32_t bluetooth_pin;
|
||||
@@ -190,7 +178,7 @@ class Screen : public PeriodicTask
|
||||
};
|
||||
|
||||
/// Enques given command item to be processed by main loop().
|
||||
bool enqueueCmd(const CmdItem &cmd)
|
||||
bool enqueueCmd(const ScreenCmd &cmd)
|
||||
{
|
||||
if (!useDisplay)
|
||||
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);
|
||||
|
||||
/// Queue of commands to execute in doTask.
|
||||
TypedQueue<CmdItem> cmdQueue;
|
||||
TypedQueue<ScreenCmd> cmdQueue;
|
||||
/// Whether we are using a display
|
||||
bool useDisplay = false;
|
||||
/// Whether the display is currently powered
|
||||
@@ -225,7 +213,9 @@ class Screen : public PeriodicTask
|
||||
|
||||
/// Holds state for debug information
|
||||
DebugInfo debugInfo;
|
||||
|
||||
/// Display device
|
||||
/** @todo display abstraction */
|
||||
#ifdef USE_SH1106
|
||||
SH1106Wire dispdev;
|
||||
#else
|
||||
@@ -235,4 +225,4 @@ class Screen : public PeriodicTask
|
||||
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 = {
|
||||
0x0A, // Width: 10
|
||||
0x0A, // Height: 10
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define SATELLITE_IMAGE_WIDTH 16
|
||||
#define SATELLITE_IMAGE_HEIGHT 15
|
||||
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 imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF };
|
||||
|
||||
#include "icon.xbm"
|
||||
#include "img/icon.xbm"
|
||||
|
||||
// We now programmatically draw our compass
|
||||
#if 0
|
||||
const
|
||||
#include "compass.xbm"
|
||||
#include "img/compass.xbm"
|
||||
#endif
|
||||
|
||||
#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 "NEMAGPS.h"
|
||||
#include "NodeDB.h"
|
||||
#include "Periodic.h"
|
||||
#include "concurrency/Periodic.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "UBloxGPS.h"
|
||||
#include "configuration.h"
|
||||
@@ -35,8 +35,9 @@
|
||||
#include "DSRRouter.h"
|
||||
#include "debug.h"
|
||||
#include "main.h"
|
||||
#include "screen.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "sleep.h"
|
||||
#include "timing.h"
|
||||
#include <OneButton.h>
|
||||
#include <Wire.h>
|
||||
// #include <driver/rtc_io.h>
|
||||
@@ -54,7 +55,7 @@
|
||||
#endif
|
||||
|
||||
// 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
|
||||
meshtastic::PowerStatus *powerStatus = new meshtastic::PowerStatus();
|
||||
@@ -130,7 +131,7 @@ static uint32_t ledBlinker()
|
||||
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 2 : 1000);
|
||||
}
|
||||
|
||||
Periodic ledPeriodic(ledBlinker);
|
||||
concurrency::Periodic ledPeriodic(ledBlinker);
|
||||
|
||||
// Prepare for button presses
|
||||
#ifdef BUTTON_PIN
|
||||
@@ -333,7 +334,7 @@ uint32_t axpDebugRead()
|
||||
return 30 * 1000;
|
||||
}
|
||||
|
||||
Periodic axpDebugOutput(axpDebugRead);
|
||||
concurrency::Periodic axpDebugOutput(axpDebugRead);
|
||||
axpDebugOutput.setup();
|
||||
#endif
|
||||
|
||||
@@ -346,7 +347,7 @@ void loop()
|
||||
powerFSM.run_machine();
|
||||
service.loop();
|
||||
|
||||
periodicScheduler.loop();
|
||||
concurrency::periodicScheduler.loop();
|
||||
// axpDebugOutput.loop();
|
||||
|
||||
#ifdef DEBUG_PORT
|
||||
@@ -371,21 +372,21 @@ void loop()
|
||||
|
||||
// Show boot screen for first 3 seconds, then switch to normal operation.
|
||||
static bool showingBootScreen = true;
|
||||
if (showingBootScreen && (millis() > 3000)) {
|
||||
if (showingBootScreen && (timing::millis() > 3000)) {
|
||||
screen.stopBootScreen();
|
||||
showingBootScreen = false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_STACK
|
||||
static uint32_t lastPrint = 0;
|
||||
if (millis() - lastPrint > 10 * 1000L) {
|
||||
lastPrint = millis();
|
||||
if (timing::millis() - lastPrint > 10 * 1000L) {
|
||||
lastPrint = timing::millis();
|
||||
meshtastic::printThreadInfo("main");
|
||||
}
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
|
||||
#include "screen.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "PowerStatus.h"
|
||||
#include "GPSStatus.h"
|
||||
#include "NodeStatus.h"
|
||||
@@ -11,7 +11,7 @@ extern bool isCharging;
|
||||
extern bool isUSBPowered;
|
||||
|
||||
// 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 meshtastic::PowerStatus *powerStatus;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "PacketHistory.h"
|
||||
#include "PeriodicTask.h"
|
||||
#include "../concurrency/PeriodicTask.h"
|
||||
#include "Router.h"
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,12 +7,13 @@
|
||||
//#include "MeshBluetoothService.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "Periodic.h"
|
||||
#include "../concurrency/Periodic.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.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 "timing.h"
|
||||
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
static Periodic sendOwnerPeriod(sendOwnerCb);
|
||||
static concurrency::Periodic sendOwnerPeriod(sendOwnerCb);
|
||||
|
||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||
{
|
||||
@@ -308,7 +309,7 @@ int MeshService::onGPSChanged(void *unused)
|
||||
|
||||
// We limit our GPS broadcasts to a max rate
|
||||
static uint32_t lastGpsSend;
|
||||
uint32_t now = millis();
|
||||
uint32_t now = timing::millis();
|
||||
if (lastGpsSend == 0 || now - lastGpsSend > radioConfig.preferences.position_broadcast_secs * 1000) {
|
||||
lastGpsSend = now;
|
||||
DEBUG_MSG("Sending position to mesh\n");
|
||||
|
||||
@@ -339,11 +339,8 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
const SubPacket &p = mp.decoded;
|
||||
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
|
||||
|
||||
int oldNumNodes = *numNodes;
|
||||
NodeInfo *info = getOrCreateNode(mp.from);
|
||||
|
||||
notifyObservers();
|
||||
|
||||
if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen
|
||||
info->has_position = true; // at least the time is valid
|
||||
info->position.time = mp.rx_time;
|
||||
@@ -359,6 +356,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
info->position.time = oldtime;
|
||||
info->has_position = true;
|
||||
updateGUIforNode = info;
|
||||
notifyObservers(true); //Force an update whether or not our node counts have changed
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -373,6 +371,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
devicestate.has_rx_text_message = true;
|
||||
updateTextMessage = true;
|
||||
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
|
||||
notifyObservers(true); //Force an update whether or not our node counts have changed
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -391,6 +390,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
if (changed) {
|
||||
updateGUIforNode = info;
|
||||
powerFSM.trigger(EVENT_NODEDB_UPDATED);
|
||||
notifyObservers(true); //Force an update whether or not our node counts have changed
|
||||
|
||||
// Not really needed - we will save anyways when we go to sleep
|
||||
// We just changed something important about the user, store our DB
|
||||
@@ -398,6 +398,10 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
notifyObservers(); //If the node counts have changed, notify observers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,9 +95,9 @@ class NodeDB
|
||||
NodeInfo *getOrCreateNode(NodeNum n);
|
||||
|
||||
/// Notify observers of changes to the DB
|
||||
void notifyObservers() {
|
||||
void notifyObservers(bool forceUpdate = false) {
|
||||
// Notify observers of the current node state
|
||||
const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineNodes(), getNumNodes());
|
||||
const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineNodes(), getNumNodes(), forceUpdate);
|
||||
newStatus.notifyObservers(&status);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "PacketHistory.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "../timing.h"
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
uint32_t now = millis();
|
||||
uint32_t now = timing::millis();
|
||||
for (size_t i = 0; i < recentPackets.size();) {
|
||||
PacketRecord &r = recentPackets[i];
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "PowerFSM.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "GPS.h"
|
||||
#include "timing.h"
|
||||
#include <assert.h>
|
||||
|
||||
PhoneAPI::PhoneAPI()
|
||||
@@ -20,7 +21,7 @@ void PhoneAPI::init()
|
||||
void PhoneAPI::checkConnectionTimeout()
|
||||
{
|
||||
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) {
|
||||
isConnected = false;
|
||||
onConnectionChanged(isConnected);
|
||||
@@ -34,7 +35,7 @@ void PhoneAPI::checkConnectionTimeout()
|
||||
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
|
||||
lastContactMsec = millis();
|
||||
lastContactMsec = timing::millis();
|
||||
if (!isConnected) {
|
||||
isConnected = true;
|
||||
onConnectionChanged(isConnected);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "assert.h"
|
||||
#include "configuration.h"
|
||||
#include "sleep.h"
|
||||
#include "timing.h"
|
||||
#include <assert.h>
|
||||
#include <pb_decode.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());
|
||||
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
|
||||
|
||||
lastTxStart = millis();
|
||||
lastTxStart = timing::millis();
|
||||
|
||||
PacketHeader *h = (PacketHeader *)radiobuf;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "MeshTypes.h"
|
||||
#include "Observer.h"
|
||||
#include "PointerQueue.h"
|
||||
#include "WorkerThread.h"
|
||||
#include "../concurrency/NotifiedWorkerThread.h"
|
||||
#include "mesh.pb.h"
|
||||
|
||||
#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)
|
||||
*/
|
||||
class RadioInterface : protected NotifiedWorkerThread
|
||||
class RadioInterface : protected concurrency::NotifiedWorkerThread
|
||||
{
|
||||
friend class MeshRadio; // for debugging we let that class touch pool
|
||||
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,
|
||||
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
|
||||
instance = this;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "PeriodicTask.h"
|
||||
#include "../concurrency/PeriodicTask.h"
|
||||
#include "RadioInterface.h"
|
||||
|
||||
#ifdef CubeCell_BoardPlus
|
||||
@@ -16,7 +16,7 @@
|
||||
#define INTERRUPT_ATTR
|
||||
#endif
|
||||
|
||||
class RadioLibInterface : public RadioInterface, private PeriodicTask
|
||||
class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTask
|
||||
{
|
||||
/// Used as our notification from the ISR
|
||||
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "MeshTypes.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "timing.h"
|
||||
|
||||
// ReliableRouter::ReliableRouter() {}
|
||||
|
||||
@@ -162,7 +163,7 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
|
||||
*/
|
||||
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.
|
||||
// for(auto el: pending) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "FloodingRouter.h"
|
||||
#include "PeriodicTask.h"
|
||||
#include "../concurrency/PeriodicTask.h"
|
||||
#include "../timing.h"
|
||||
#include <unordered_map>
|
||||
|
||||
/**
|
||||
@@ -48,7 +49,7 @@ struct PendingPacket {
|
||||
PendingPacket() {}
|
||||
PendingPacket(MeshPacket *p);
|
||||
|
||||
void setNextTx() { nextTxMsec = millis() + random(20 * 1000L, 22 * 1000L); }
|
||||
void setNextTx() { nextTxMsec = timing::millis() + random(20 * 1000L, 22 * 1000L); }
|
||||
};
|
||||
|
||||
class GlobalPacketIdHashFunction
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "PeriodicTask.h"
|
||||
#include "concurrency/PeriodicTask.h"
|
||||
#include "PowerStatus.h"
|
||||
|
||||
/**
|
||||
@@ -15,7 +15,7 @@
|
||||
#define BAT_MILLIVOLTS_FULL 4100
|
||||
#define BAT_MILLIVOLTS_EMPTY 3500
|
||||
|
||||
class Power : public PeriodicTask
|
||||
class Power : public concurrency::PeriodicTask
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "NodeDB.h"
|
||||
#include "configuration.h"
|
||||
#include "error.h"
|
||||
|
||||
#include "timing.h"
|
||||
#include "main.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
|
||||
static void waitEnterSleep()
|
||||
{
|
||||
uint32_t now = millis();
|
||||
uint32_t now = timing::millis();
|
||||
while (!doPreflightSleep()) {
|
||||
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);
|
||||
assert(0); // FIXME - for now we just restart, need to fix bug #167
|
||||
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