mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-23 03:00:56 +00:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ef2cc8623 | ||
|
|
b41a32c6b6 | ||
|
|
1ebd7b0c3e | ||
|
|
5457541244 | ||
|
|
ef325289eb | ||
|
|
40c63c0615 | ||
|
|
a9de8b9bb3 | ||
|
|
66a7f896c8 | ||
|
|
45a36f5571 | ||
|
|
62493efc40 | ||
|
|
2848b76cc9 | ||
|
|
ef8bea478d | ||
|
|
a8e4bbbe65 | ||
|
|
9a414d9c77 | ||
|
|
3d21794039 | ||
|
|
beac614e65 | ||
|
|
87f2673fc4 | ||
|
|
999b292717 | ||
|
|
8330c3270e | ||
|
|
0c8e0efed2 | ||
|
|
c44d8a0433 | ||
|
|
49b4ed2a89 | ||
|
|
4b9ea4f808 | ||
|
|
c3beca3e23 | ||
|
|
95cb6b06e4 | ||
|
|
c46a884558 | ||
|
|
2044427e97 | ||
|
|
514ebdf013 | ||
|
|
10f64590a9 | ||
|
|
4a70ba1f7a | ||
|
|
dd6a402ea0 | ||
|
|
bed7d8a619 | ||
|
|
1b6e8e36d3 | ||
|
|
7a4b8cde11 | ||
|
|
113859e791 | ||
|
|
a6b82ccfd9 | ||
|
|
e8b8ec69f1 | ||
|
|
023f1c24fb | ||
|
|
f00d07baa3 | ||
|
|
62c228b986 | ||
|
|
1a3cc40c7e | ||
|
|
bdcd5c3981 | ||
|
|
fc82e872d6 | ||
|
|
b47c54b5b6 | ||
|
|
c0c83ad389 |
@@ -1,3 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
export VERSION=1.1.1
|
export VERSION=1.1.5
|
||||||
@@ -42,7 +42,6 @@ Expected sequence for initial download:
|
|||||||
- Read a RadioConfig from "radio" - used to get the channel and radio settings
|
- Read a RadioConfig from "radio" - used to get the channel and radio settings
|
||||||
- Read a User from "user" - to get the username for this node
|
- Read a User from "user" - to get the username for this node
|
||||||
- Read a MyNodeInfo from "mynode" to get information about this local device
|
- Read a MyNodeInfo from "mynode" to get information about this local device
|
||||||
- Write an empty record to "nodeinfo" to restart the nodeinfo reading state machine
|
|
||||||
- Read a series of NodeInfo packets to build the phone's copy of the current NodeDB for the mesh
|
- Read a series of NodeInfo packets to build the phone's copy of the current NodeDB for the mesh
|
||||||
- Read a endConfig packet that indicates that the entire state you need has been sent.
|
- Read a endConfig packet that indicates that the entire state you need has been sent.
|
||||||
- Read a series of MeshPackets until it returns empty to get any messages that arrived for this node while the phone was away
|
- Read a series of MeshPackets until it returns empty to get any messages that arrived for this node while the phone was away
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
gps todo - bug 376
|
You probably don't care about this ugly file of personal notes ;-)
|
||||||
|
|
||||||
for taiwan region:
|
for taiwan region:
|
||||||
bin/run.sh --set region 8
|
bin/run.sh --set region 8
|
||||||
@@ -6,40 +6,12 @@ bin/run.sh --set region 8
|
|||||||
time only mode
|
time only mode
|
||||||
./bin/run.sh --set gps_operation 3
|
./bin/run.sh --set gps_operation 3
|
||||||
|
|
||||||
increase acquisition time until ublox power management can be improved see 9.3.1
|
|
||||||
|
|
||||||
ublox parsing failure
|
ublox parsing failure
|
||||||
|
|
||||||
record power measurements and update spreadsheet
|
record power measurements and update spreadsheet
|
||||||
|
|
||||||
fix has_gps based on new logic
|
|
||||||
|
|
||||||
make sure we are turning off lora radio in deep sleep
|
|
||||||
|
|
||||||
don't send locations if the user has forbidden that (lie to phone so phone won't either)
|
|
||||||
|
|
||||||
have loop methods return allowable sleep time (from their perspective)
|
have loop methods return allowable sleep time (from their perspective)
|
||||||
increase main cpu sleep time
|
increase main cpu sleep time
|
||||||
|
|
||||||
add set router mode in python tool - it will also set GPS to stationary
|
|
||||||
make sure location still gets set once per boot and stays marked as valid on the gui
|
|
||||||
send position updates super rarely
|
|
||||||
turn off checking for usb power and forcing always on
|
|
||||||
(which will shrink DARK and NB period to zero and
|
|
||||||
make light_sleep very long)
|
|
||||||
|
|
||||||
warn people about crummy gps antennas - add to faq
|
warn people about crummy gps antennas - add to faq
|
||||||
|
|
||||||
|
|
||||||
gps states
|
|
||||||
|
|
||||||
Active - for gps_attempt_time seconds
|
|
||||||
Sleeping - for (gps_update_rate or sleep forever) seconds
|
|
||||||
ForcedSleep - PowerFSM says we don't want to use GPS right now
|
|
||||||
(no need for sleep forever state)
|
|
||||||
|
|
||||||
gps triggers
|
|
||||||
GPS_TRIG_FORCE_SLEEP - from powerfsm
|
|
||||||
GPS_TRIG_FORCE_WAKE - from powerfsm
|
|
||||||
GPS_SETTINGS - if GPS settings changed, reset params and possibly become active
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ From lower to higher power consumption.
|
|||||||
|
|
||||||
- full on (ON) - Everything is on, can eventually timeout and lower to a lower power state
|
- full on (ON) - Everything is on, can eventually timeout and lower to a lower power state
|
||||||
onEntry: setBluetoothOn(true), screen.setOn(true)
|
onEntry: setBluetoothOn(true), screen.setOn(true)
|
||||||
onExit: screen.setOn(false)
|
onExit: screen->setOn(false)
|
||||||
|
|
||||||
- has power (POWER) - Screen is on, device doesn't sleep, bluetooth on, will stay in this state as long as we have power
|
- has power (POWER) - Screen is on, device doesn't sleep, bluetooth on, will stay in this state as long as we have power
|
||||||
onEntry: setBluetooth off, screen on
|
onEntry: setBluetooth off, screen on
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/n
|
|||||||
-DHW_VERSION_${sysenv.COUNTRY}
|
-DHW_VERSION_${sysenv.COUNTRY}
|
||||||
-DAPP_VERSION=${sysenv.APP_VERSION}
|
-DAPP_VERSION=${sysenv.APP_VERSION}
|
||||||
-DHW_VERSION=${sysenv.HW_VERSION}
|
-DHW_VERSION=${sysenv.HW_VERSION}
|
||||||
|
-DUSE_THREAD_NAMES
|
||||||
|
|
||||||
; leave this commented out to avoid breaking Windows
|
; leave this commented out to avoid breaking Windows
|
||||||
;upload_port = /dev/ttyUSB0
|
;upload_port = /dev/ttyUSB0
|
||||||
@@ -59,15 +60,16 @@ debug_tool = jlink
|
|||||||
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
|
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
|
||||||
1260 ; OneButton library for non-blocking button debounce
|
https://github.com/geeksville/OneButton.git ; OneButton library for non-blocking button debounce
|
||||||
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
||||||
https://github.com/meshtastic/arduino-fsm.git
|
https://github.com/meshtastic/arduino-fsm.git#2f106146071fc7bc620e1e8d4b88dc4e0266ce39
|
||||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#cb8353dfddd1b0e205098f5e70d5f2a5f74b4838
|
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad
|
||||||
https://github.com/meshtastic/RadioLib.git#1083c2e76f9906c5f80dfec726facebf8413eef0
|
https://github.com/meshtastic/RadioLib.git#8657380241bce681c33aab46598bbf13b11f876c
|
||||||
https://github.com/meshtastic/TinyGPSPlus.git
|
https://github.com/meshtastic/TinyGPSPlus.git
|
||||||
https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460
|
https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460
|
||||||
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||||
SPI
|
SPI
|
||||||
|
https://github.com/geeksville/ArduinoThread.git#333ffd09b596977c217ba25da4258f588b462ac6
|
||||||
|
|
||||||
; Common settings for conventional (non Portduino) Ardino targets
|
; Common settings for conventional (non Portduino) Ardino targets
|
||||||
[arduino_base]
|
[arduino_base]
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
virtual bool isBatteryConnect() { return getBattVoltage() != -1; }
|
virtual bool isBatteryConnect() { return getBattVoltage() != -1; }
|
||||||
} analogLevel;
|
} analogLevel;
|
||||||
|
|
||||||
|
Power::Power() : OSThread("Power") {}
|
||||||
|
|
||||||
bool Power::analogInit()
|
bool Power::analogInit()
|
||||||
{
|
{
|
||||||
#ifdef BATTERY_PIN
|
#ifdef BATTERY_PIN
|
||||||
@@ -86,10 +88,7 @@ bool Power::setup()
|
|||||||
if (!found) {
|
if (!found) {
|
||||||
found = analogInit();
|
found = analogInit();
|
||||||
}
|
}
|
||||||
if (found) {
|
enabled = found;
|
||||||
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
|
||||||
setPeriod(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
@@ -135,13 +134,47 @@ void Power::readPowerStatus()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Power::doTask()
|
int32_t Power::runOnce()
|
||||||
{
|
{
|
||||||
readPowerStatus();
|
readPowerStatus();
|
||||||
|
|
||||||
|
#ifdef TBEAM_V10
|
||||||
|
// WE no longer use the IRQ line to wake the CPU (due to false wakes from sleep), but we do poll
|
||||||
|
// the IRQ status by reading the registers over I2C
|
||||||
|
axp.readIRQ();
|
||||||
|
|
||||||
|
if (axp.isVbusRemoveIRQ()) {
|
||||||
|
DEBUG_MSG("USB unplugged\n");
|
||||||
|
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
||||||
|
}
|
||||||
|
if (axp.isVbusPlugInIRQ()) {
|
||||||
|
DEBUG_MSG("USB plugged In\n");
|
||||||
|
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Other things we could check if we cared...
|
||||||
|
|
||||||
|
if (axp.isChargingIRQ()) {
|
||||||
|
DEBUG_MSG("Battery start charging\n");
|
||||||
|
}
|
||||||
|
if (axp.isChargingDoneIRQ()) {
|
||||||
|
DEBUG_MSG("Battery fully charged\n");
|
||||||
|
}
|
||||||
|
if (axp.isBattPlugInIRQ()) {
|
||||||
|
DEBUG_MSG("Battery inserted\n");
|
||||||
|
}
|
||||||
|
if (axp.isBattRemoveIRQ()) {
|
||||||
|
DEBUG_MSG("Battery removed\n");
|
||||||
|
}
|
||||||
|
if (axp.isPEKShortPressIRQ()) {
|
||||||
|
DEBUG_MSG("PEK short button press\n");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
axp.clearIRQ();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Only read once every 20 seconds once the power status for the app has been initialized
|
// Only read once every 20 seconds once the power status for the app has been initialized
|
||||||
if (statusHandler && statusHandler->isInitialized())
|
return (statusHandler && statusHandler->isInitialized()) ? (1000 * 20) : RUN_SAME;
|
||||||
setPeriod(1000 * 20);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -208,8 +241,7 @@ bool Power::axp192Init()
|
|||||||
// no battery also it could cause inadvertent waking from light sleep just because the battery filled
|
// no battery also it could cause inadvertent waking from light sleep just because the battery filled
|
||||||
// we don't look for AXP202_BATT_REMOVED_IRQ because it occurs repeatedly while no battery installed
|
// we don't look for AXP202_BATT_REMOVED_IRQ because it occurs repeatedly while no battery installed
|
||||||
// we don't look at AXP202_VBUS_REMOVED_IRQ because we don't have anything hooked to vbus
|
// we don't look at AXP202_VBUS_REMOVED_IRQ because we don't have anything hooked to vbus
|
||||||
axp.enableIRQ(AXP202_BATT_CONNECT_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ,
|
axp.enableIRQ(AXP202_BATT_CONNECT_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ, 1);
|
||||||
1);
|
|
||||||
|
|
||||||
axp.clearIRQ();
|
axp.clearIRQ();
|
||||||
#endif
|
#endif
|
||||||
@@ -226,43 +258,3 @@ bool Power::axp192Init()
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Power::loop()
|
|
||||||
{
|
|
||||||
#ifdef PMU_IRQ
|
|
||||||
if (pmu_irq) {
|
|
||||||
pmu_irq = false;
|
|
||||||
axp.readIRQ();
|
|
||||||
|
|
||||||
DEBUG_MSG("pmu irq!\n");
|
|
||||||
|
|
||||||
if (axp.isChargingIRQ()) {
|
|
||||||
DEBUG_MSG("Battery start charging\n");
|
|
||||||
}
|
|
||||||
if (axp.isChargingDoneIRQ()) {
|
|
||||||
DEBUG_MSG("Battery fully charged\n");
|
|
||||||
}
|
|
||||||
if (axp.isVbusRemoveIRQ()) {
|
|
||||||
DEBUG_MSG("USB unplugged\n");
|
|
||||||
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
|
||||||
}
|
|
||||||
if (axp.isVbusPlugInIRQ()) {
|
|
||||||
DEBUG_MSG("USB plugged In\n");
|
|
||||||
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
|
||||||
}
|
|
||||||
if (axp.isBattPlugInIRQ()) {
|
|
||||||
DEBUG_MSG("Battery inserted\n");
|
|
||||||
}
|
|
||||||
if (axp.isBattRemoveIRQ()) {
|
|
||||||
DEBUG_MSG("Battery removed\n");
|
|
||||||
}
|
|
||||||
if (axp.isPEKShortPressIRQ()) {
|
|
||||||
DEBUG_MSG("PEK short button press\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
readPowerStatus();
|
|
||||||
axp.clearIRQ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ static uint32_t secsSlept;
|
|||||||
static void lsEnter()
|
static void lsEnter()
|
||||||
{
|
{
|
||||||
DEBUG_MSG("lsEnter begin, ls_secs=%u\n", getPref_ls_secs());
|
DEBUG_MSG("lsEnter begin, ls_secs=%u\n", getPref_ls_secs());
|
||||||
screen.setOn(false);
|
screen->setOn(false);
|
||||||
secsSlept = 0; // How long have we been sleeping this time
|
secsSlept = 0; // How long have we been sleeping this time
|
||||||
|
|
||||||
DEBUG_MSG("lsEnter end\n");
|
DEBUG_MSG("lsEnter end\n");
|
||||||
@@ -102,7 +102,7 @@ static void lsExit()
|
|||||||
|
|
||||||
static void nbEnter()
|
static void nbEnter()
|
||||||
{
|
{
|
||||||
screen.setOn(false);
|
screen->setOn(false);
|
||||||
setBluetoothEnable(false);
|
setBluetoothEnable(false);
|
||||||
|
|
||||||
// FIXME - check if we already have packets for phone and immediately trigger EVENT_PACKETS_FOR_PHONE
|
// FIXME - check if we already have packets for phone and immediately trigger EVENT_PACKETS_FOR_PHONE
|
||||||
@@ -111,24 +111,33 @@ static void nbEnter()
|
|||||||
static void darkEnter()
|
static void darkEnter()
|
||||||
{
|
{
|
||||||
setBluetoothEnable(true);
|
setBluetoothEnable(true);
|
||||||
screen.setOn(false);
|
screen->setOn(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void serialEnter()
|
static void serialEnter()
|
||||||
{
|
{
|
||||||
setBluetoothEnable(false);
|
setBluetoothEnable(false);
|
||||||
screen.setOn(true);
|
screen->setOn(true);
|
||||||
|
screen->print("Using API...\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void powerEnter()
|
static void powerEnter()
|
||||||
{
|
{
|
||||||
screen.setOn(true);
|
screen->setOn(true);
|
||||||
setBluetoothEnable(true);
|
setBluetoothEnable(true);
|
||||||
|
screen->print("Powered...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void powerExit()
|
||||||
|
{
|
||||||
|
screen->setOn(true);
|
||||||
|
setBluetoothEnable(true);
|
||||||
|
screen->print("Unpowered...\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onEnter()
|
static void onEnter()
|
||||||
{
|
{
|
||||||
screen.setOn(true);
|
screen->setOn(true);
|
||||||
setBluetoothEnable(true);
|
setBluetoothEnable(true);
|
||||||
|
|
||||||
static uint32_t lastPingMs;
|
static uint32_t lastPingMs;
|
||||||
@@ -144,7 +153,7 @@ static void onEnter()
|
|||||||
|
|
||||||
static void screenPress()
|
static void screenPress()
|
||||||
{
|
{
|
||||||
screen.onPress();
|
screen->onPress();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bootEnter() {}
|
static void bootEnter() {}
|
||||||
@@ -156,7 +165,7 @@ State stateDARK(darkEnter, NULL, NULL, "DARK");
|
|||||||
State stateSERIAL(serialEnter, NULL, NULL, "SERIAL");
|
State stateSERIAL(serialEnter, NULL, NULL, "SERIAL");
|
||||||
State stateBOOT(bootEnter, NULL, NULL, "BOOT");
|
State stateBOOT(bootEnter, NULL, NULL, "BOOT");
|
||||||
State stateON(onEnter, NULL, NULL, "ON");
|
State stateON(onEnter, NULL, NULL, "ON");
|
||||||
State statePOWER(powerEnter, NULL, NULL, "POWER");
|
State statePOWER(powerEnter, NULL, powerExit, "POWER");
|
||||||
Fsm powerFSM(&stateBOOT);
|
Fsm powerFSM(&stateBOOT);
|
||||||
|
|
||||||
void PowerFSM_setup()
|
void PowerFSM_setup()
|
||||||
|
|||||||
@@ -20,5 +20,6 @@
|
|||||||
#define EVENT_POWER_DISCONNECTED 14
|
#define EVENT_POWER_DISCONNECTED 14
|
||||||
|
|
||||||
extern Fsm powerFSM;
|
extern Fsm powerFSM;
|
||||||
|
extern State statePOWER, stateSERIAL;
|
||||||
|
|
||||||
void PowerFSM_setup();
|
void PowerFSM_setup();
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "WorkerThread.h"
|
|
||||||
|
|
||||||
namespace concurrency {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A worker thread that waits on a freertos notification
|
|
||||||
*/
|
|
||||||
class BaseNotifiedWorkerThread : public WorkerThread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Notify this thread so it can run
|
|
||||||
*/
|
|
||||||
virtual void notify(uint32_t v = 0, eNotifyAction action = eNoAction) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify from an ISR
|
|
||||||
*
|
|
||||||
* This must be inline or IRAM_ATTR on ESP32
|
|
||||||
*/
|
|
||||||
virtual void notifyFromISR(BaseType_t *highPriWoken, uint32_t v = 0, eNotifyAction action = eNoAction) { notify(v, action); }
|
|
||||||
|
|
||||||
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() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#include "Thread.h"
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
namespace concurrency
|
|
||||||
{
|
|
||||||
|
|
||||||
void BaseThread::callRun(void *_this)
|
|
||||||
{
|
|
||||||
((BaseThread *)_this)->doRun();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "freertosinc.h"
|
|
||||||
|
|
||||||
namespace concurrency
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Base threading
|
|
||||||
*/
|
|
||||||
class BaseThread
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* set this to true to ask thread to cleanly exit asap
|
|
||||||
*/
|
|
||||||
volatile bool wantExit = false;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY) = 0;
|
|
||||||
|
|
||||||
virtual ~BaseThread() {}
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
*/
|
|
||||||
virtual void serviceWatchdog() {}
|
|
||||||
virtual void startWatchdog() {}
|
|
||||||
virtual void stopWatchdog() {}
|
|
||||||
|
|
||||||
static void callRun(void *_this);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
39
src/concurrency/BinarySemaphoreFreeRTOS.cpp
Normal file
39
src/concurrency/BinarySemaphoreFreeRTOS.cpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#include "concurrency/BinarySemaphoreFreeRTOS.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#ifdef HAS_FREE_RTOS
|
||||||
|
|
||||||
|
namespace concurrency
|
||||||
|
{
|
||||||
|
|
||||||
|
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS()
|
||||||
|
{
|
||||||
|
semaphore = xSemaphoreCreateBinary();
|
||||||
|
}
|
||||||
|
|
||||||
|
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS()
|
||||||
|
{
|
||||||
|
vSemaphoreDelete(semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns false if we were interrupted
|
||||||
|
*/
|
||||||
|
bool BinarySemaphoreFreeRTOS::take(uint32_t msec)
|
||||||
|
{
|
||||||
|
return xSemaphoreTake(semaphore, pdMS_TO_TICKS(msec));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinarySemaphoreFreeRTOS::give()
|
||||||
|
{
|
||||||
|
xSemaphoreGive(semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
IRAM_ATTR void BinarySemaphoreFreeRTOS::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken)
|
||||||
|
{
|
||||||
|
xSemaphoreGiveFromISR(semaphore, pxHigherPriorityTaskWoken);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
|
|
||||||
|
#endif
|
||||||
31
src/concurrency/BinarySemaphoreFreeRTOS.h
Normal file
31
src/concurrency/BinarySemaphoreFreeRTOS.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "../freertosinc.h"
|
||||||
|
|
||||||
|
namespace concurrency
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef HAS_FREE_RTOS
|
||||||
|
|
||||||
|
class BinarySemaphoreFreeRTOS
|
||||||
|
{
|
||||||
|
SemaphoreHandle_t semaphore;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BinarySemaphoreFreeRTOS();
|
||||||
|
~BinarySemaphoreFreeRTOS();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns false if we timed out
|
||||||
|
*/
|
||||||
|
bool take(uint32_t msec);
|
||||||
|
|
||||||
|
void give();
|
||||||
|
|
||||||
|
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
36
src/concurrency/BinarySemaphorePosix.cpp
Normal file
36
src/concurrency/BinarySemaphorePosix.cpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#include "concurrency/BinarySemaphorePosix.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#ifndef HAS_FREE_RTOS
|
||||||
|
|
||||||
|
namespace concurrency
|
||||||
|
{
|
||||||
|
|
||||||
|
BinarySemaphorePosix::BinarySemaphorePosix()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BinarySemaphorePosix::~BinarySemaphorePosix()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns false if we timed out
|
||||||
|
*/
|
||||||
|
bool BinarySemaphorePosix::take(uint32_t msec)
|
||||||
|
{
|
||||||
|
delay(msec); // FIXME
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinarySemaphorePosix::give()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IRAM_ATTR void BinarySemaphorePosix::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
|
|
||||||
|
#endif
|
||||||
31
src/concurrency/BinarySemaphorePosix.h
Normal file
31
src/concurrency/BinarySemaphorePosix.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "../freertosinc.h"
|
||||||
|
|
||||||
|
namespace concurrency
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifndef HAS_FREE_RTOS
|
||||||
|
|
||||||
|
class BinarySemaphorePosix
|
||||||
|
{
|
||||||
|
// SemaphoreHandle_t semaphore;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BinarySemaphorePosix();
|
||||||
|
~BinarySemaphorePosix();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns false if we timed out
|
||||||
|
*/
|
||||||
|
bool take(uint32_t msec);
|
||||||
|
|
||||||
|
void give();
|
||||||
|
|
||||||
|
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#include "NotifiedWorkerThread.h"
|
|
||||||
|
|
||||||
#ifdef HAS_FREE_RTOS
|
|
||||||
|
|
||||||
namespace concurrency {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify this thread so it can run
|
|
||||||
*/
|
|
||||||
void FreeRtosNotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
|
|
||||||
{
|
|
||||||
xTaskNotify(taskHandle, v, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeRtosNotifiedWorkerThread::block()
|
|
||||||
{
|
|
||||||
xTaskNotifyWait(0, // don't clear notification on entry
|
|
||||||
clearOnRead, ¬ification, portMAX_DELAY); // Wait forever
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BaseNotifiedWorkerThread.h"
|
|
||||||
|
|
||||||
#ifdef HAS_FREE_RTOS
|
|
||||||
|
|
||||||
namespace concurrency {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A worker thread that waits on a freertos notification
|
|
||||||
*/
|
|
||||||
class FreeRtosNotifiedWorkerThread : public BaseNotifiedWorkerThread
|
|
||||||
{
|
|
||||||
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:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
|
||||||
*/
|
|
||||||
virtual void block();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
#include "FreeRtosThread.h"
|
|
||||||
|
|
||||||
#ifdef HAS_FREE_RTOS
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
|
||||||
#include "esp_task_wdt.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace concurrency
|
|
||||||
{
|
|
||||||
|
|
||||||
void FreeRtosThread::start(const char *name, size_t stackSize, uint32_t priority)
|
|
||||||
{
|
|
||||||
auto r = xTaskCreate(callRun, name, stackSize, this, priority, &taskHandle);
|
|
||||||
assert(r == pdPASS);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeRtosThread::serviceWatchdog()
|
|
||||||
{
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
|
||||||
esp_task_wdt_reset();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeRtosThread::startWatchdog()
|
|
||||||
{
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
|
||||||
auto r = esp_task_wdt_add(taskHandle);
|
|
||||||
assert(r == ESP_OK);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeRtosThread::stopWatchdog()
|
|
||||||
{
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
|
||||||
auto r = esp_task_wdt_delete(taskHandle);
|
|
||||||
assert(r == ESP_OK);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BaseThread.h"
|
|
||||||
#include "freertosinc.h"
|
|
||||||
|
|
||||||
#ifdef HAS_FREE_RTOS
|
|
||||||
|
|
||||||
namespace concurrency
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Base threading
|
|
||||||
*/
|
|
||||||
class FreeRtosThread : public BaseThread
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
TaskHandle_t taskHandle = NULL;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY);
|
|
||||||
|
|
||||||
virtual ~FreeRtosThread() { 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();
|
|
||||||
void startWatchdog();
|
|
||||||
void stopWatchdog();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
|
|
||||||
#endif
|
|
||||||
35
src/concurrency/InterruptableDelay.cpp
Normal file
35
src/concurrency/InterruptableDelay.cpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#include "concurrency/InterruptableDelay.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
namespace concurrency
|
||||||
|
{
|
||||||
|
|
||||||
|
InterruptableDelay::InterruptableDelay() {}
|
||||||
|
|
||||||
|
InterruptableDelay::~InterruptableDelay() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns false if we were interrupted
|
||||||
|
*/
|
||||||
|
bool InterruptableDelay::delay(uint32_t msec)
|
||||||
|
{
|
||||||
|
// DEBUG_MSG("delay %u ", msec);
|
||||||
|
|
||||||
|
// sem take will return false if we timed out (i.e. were not interrupted)
|
||||||
|
bool r = semaphore.take(msec);
|
||||||
|
|
||||||
|
// DEBUG_MSG("interrupt=%d\n", r);
|
||||||
|
return !r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterruptableDelay::interrupt()
|
||||||
|
{
|
||||||
|
semaphore.give();
|
||||||
|
}
|
||||||
|
|
||||||
|
IRAM_ATTR void InterruptableDelay::interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken)
|
||||||
|
{
|
||||||
|
semaphore.giveFromISR(pxHigherPriorityTaskWoken);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
42
src/concurrency/InterruptableDelay.h
Normal file
42
src/concurrency/InterruptableDelay.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../freertosinc.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAS_FREE_RTOS
|
||||||
|
#include "concurrency/BinarySemaphoreFreeRTOS.h"
|
||||||
|
#define BinarySemaphore BinarySemaphoreFreeRTOS
|
||||||
|
#else
|
||||||
|
#include "concurrency/BinarySemaphorePosix.h"
|
||||||
|
#define BinarySemaphore BinarySemaphorePosix
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace concurrency
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that provides delay(msec) like functionality, but can be interrupted by calling interrupt().
|
||||||
|
*
|
||||||
|
* Useful for they top level loop() delay call to keep the CPU powered down until our next scheduled event or some external event.
|
||||||
|
*
|
||||||
|
* This is implmented for FreeRTOS but should be easy to port to other operating systems.
|
||||||
|
*/
|
||||||
|
class InterruptableDelay
|
||||||
|
{
|
||||||
|
BinarySemaphore semaphore;
|
||||||
|
|
||||||
|
public:
|
||||||
|
InterruptableDelay();
|
||||||
|
~InterruptableDelay();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns false if we were interrupted
|
||||||
|
*/
|
||||||
|
bool delay(uint32_t msec);
|
||||||
|
|
||||||
|
void interrupt();
|
||||||
|
|
||||||
|
void interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
85
src/concurrency/NotifiedWorkerThread.cpp
Normal file
85
src/concurrency/NotifiedWorkerThread.cpp
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#include "NotifiedWorkerThread.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace concurrency
|
||||||
|
{
|
||||||
|
|
||||||
|
static bool debugNotification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify this thread so it can run
|
||||||
|
*/
|
||||||
|
bool NotifiedWorkerThread::notify(uint32_t v, bool overwrite)
|
||||||
|
{
|
||||||
|
bool r = notifyCommon(v, overwrite);
|
||||||
|
|
||||||
|
if (r)
|
||||||
|
mainDelay.interrupt();
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify this thread so it can run
|
||||||
|
*/
|
||||||
|
IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite)
|
||||||
|
{
|
||||||
|
if (overwrite || notification == 0) {
|
||||||
|
enabled = true;
|
||||||
|
setInterval(0); // Run ASAP
|
||||||
|
|
||||||
|
notification = v;
|
||||||
|
if (debugNotification)
|
||||||
|
DEBUG_MSG("setting notification %d\n", v);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (debugNotification)
|
||||||
|
DEBUG_MSG("dropping notification %d\n", v);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify from an ISR
|
||||||
|
*
|
||||||
|
* This must be inline or IRAM_ATTR on ESP32
|
||||||
|
*/
|
||||||
|
IRAM_ATTR bool NotifiedWorkerThread::notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite)
|
||||||
|
{
|
||||||
|
bool r = notifyCommon(v, overwrite);
|
||||||
|
if (r)
|
||||||
|
mainDelay.interruptFromISR(highPriWoken);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a notification to fire in delay msecs
|
||||||
|
*/
|
||||||
|
bool NotifiedWorkerThread::notifyLater(uint32_t delay, uint32_t v, bool overwrite)
|
||||||
|
{
|
||||||
|
bool didIt = notify(v, overwrite);
|
||||||
|
|
||||||
|
if (didIt) { // If we didn't already have something queued, override the delay to be larger
|
||||||
|
setIntervalFromNow(delay); // a new version of setInterval relative to the current time
|
||||||
|
if (debugNotification)
|
||||||
|
DEBUG_MSG("delaying notification %u\n", delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
return didIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t NotifiedWorkerThread::runOnce()
|
||||||
|
{
|
||||||
|
auto n = notification;
|
||||||
|
enabled = false; // Only run once per notification
|
||||||
|
notification = 0; // clear notification
|
||||||
|
if (n) {
|
||||||
|
onNotify(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RUN_SAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -1,17 +1,50 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "FreeRtosNotifiedWorkerThread.h"
|
#include "OSThread.h"
|
||||||
#include "PosixNotifiedWorkerThread.h"
|
|
||||||
|
|
||||||
namespace concurrency
|
namespace concurrency
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef HAS_FREE_RTOS
|
/**
|
||||||
typedef FreeRtosNotifiedWorkerThread NotifiedWorkerThread;
|
* @brief A worker thread that waits on a freertos notification
|
||||||
#endif
|
*/
|
||||||
|
class NotifiedWorkerThread : public OSThread
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The notification that was most recently used to wake the thread. Read from runOnce()
|
||||||
|
*/
|
||||||
|
uint32_t notification = 0;
|
||||||
|
|
||||||
#ifdef __unix__
|
public:
|
||||||
typedef PosixNotifiedWorkerThread NotifiedWorkerThread;
|
NotifiedWorkerThread(const char *name) : OSThread(name) {}
|
||||||
#endif
|
|
||||||
|
/**
|
||||||
|
* Notify this thread so it can run
|
||||||
|
*/
|
||||||
|
bool notify(uint32_t v, bool overwrite);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify from an ISR
|
||||||
|
*
|
||||||
|
* This must be inline or IRAM_ATTR on ESP32
|
||||||
|
*/
|
||||||
|
bool notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a notification to fire in delay msecs
|
||||||
|
*/
|
||||||
|
bool notifyLater(uint32_t delay, uint32_t v, bool overwrite);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void onNotify(uint32_t notification) = 0;
|
||||||
|
|
||||||
|
virtual int32_t runOnce();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Notify this thread so it can run
|
||||||
|
*/
|
||||||
|
bool notifyCommon(uint32_t v, bool overwrite);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace concurrency
|
} // namespace concurrency
|
||||||
|
|||||||
79
src/concurrency/OSThread.cpp
Normal file
79
src/concurrency/OSThread.cpp
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#include "OSThread.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace concurrency
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Show debugging info for disabled threads
|
||||||
|
bool OSThread::showDisabled;
|
||||||
|
|
||||||
|
/// Show debugging info for threads when we run them
|
||||||
|
bool OSThread::showRun = false;
|
||||||
|
|
||||||
|
/// Show debugging info for threads we decide not to run;
|
||||||
|
bool OSThread::showWaiting = false;
|
||||||
|
|
||||||
|
ThreadController mainController, timerController;
|
||||||
|
InterruptableDelay mainDelay;
|
||||||
|
|
||||||
|
void OSThread::setup()
|
||||||
|
{
|
||||||
|
mainController.ThreadName = "mainController";
|
||||||
|
timerController.ThreadName = "timerController";
|
||||||
|
}
|
||||||
|
|
||||||
|
OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller)
|
||||||
|
: Thread(NULL, period), controller(_controller)
|
||||||
|
{
|
||||||
|
ThreadName = _name;
|
||||||
|
|
||||||
|
if (controller)
|
||||||
|
controller->add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
OSThread::~OSThread()
|
||||||
|
{
|
||||||
|
if (controller)
|
||||||
|
controller->remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
|
||||||
|
*/
|
||||||
|
void OSThread::setIntervalFromNow(unsigned long _interval)
|
||||||
|
{
|
||||||
|
// Save interval
|
||||||
|
interval = _interval;
|
||||||
|
|
||||||
|
// Cache the next run based on the last_run
|
||||||
|
_cached_next_run = millis() + interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OSThread::shouldRun(unsigned long time)
|
||||||
|
{
|
||||||
|
bool r = Thread::shouldRun(time);
|
||||||
|
|
||||||
|
if (showRun && r)
|
||||||
|
DEBUG_MSG("Thread %s: run\n", ThreadName.c_str());
|
||||||
|
|
||||||
|
if (showWaiting && enabled && !r)
|
||||||
|
DEBUG_MSG("Thread %s: wait %lu\n", ThreadName.c_str(), interval);
|
||||||
|
|
||||||
|
if (showDisabled && !enabled)
|
||||||
|
DEBUG_MSG("Thread %s: disabled\n", ThreadName.c_str());
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OSThread::run()
|
||||||
|
{
|
||||||
|
auto newDelay = runOnce();
|
||||||
|
|
||||||
|
runned();
|
||||||
|
|
||||||
|
if (newDelay >= 0)
|
||||||
|
setInterval(newDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
70
src/concurrency/OSThread.h
Normal file
70
src/concurrency/OSThread.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "Thread.h"
|
||||||
|
#include "ThreadController.h"
|
||||||
|
#include "concurrency/InterruptableDelay.h"
|
||||||
|
|
||||||
|
namespace concurrency
|
||||||
|
{
|
||||||
|
|
||||||
|
extern ThreadController mainController, timerController;
|
||||||
|
extern InterruptableDelay mainDelay;
|
||||||
|
|
||||||
|
#define RUN_SAME -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Base threading
|
||||||
|
*
|
||||||
|
* This is a pseudo threading layer that is super easy to port, well suited to our slow network and very ram & power efficient.
|
||||||
|
*
|
||||||
|
* TODO FIXME @geeksville
|
||||||
|
*
|
||||||
|
* move more things into OSThreads
|
||||||
|
* remove lock/lockguard
|
||||||
|
*
|
||||||
|
* move typedQueue into concurrency
|
||||||
|
* remove freertos from typedqueue
|
||||||
|
*/
|
||||||
|
class OSThread : public Thread
|
||||||
|
{
|
||||||
|
ThreadController *controller;
|
||||||
|
|
||||||
|
/// Show debugging info for disabled threads
|
||||||
|
static bool showDisabled;
|
||||||
|
|
||||||
|
/// Show debugging info for threads when we run them
|
||||||
|
static bool showRun;
|
||||||
|
|
||||||
|
/// Show debugging info for threads we decide not to run;
|
||||||
|
static bool showWaiting;
|
||||||
|
|
||||||
|
public:
|
||||||
|
OSThread(const char *name, uint32_t period = 0, ThreadController *controller = &mainController);
|
||||||
|
|
||||||
|
virtual ~OSThread();
|
||||||
|
|
||||||
|
virtual bool shouldRun(unsigned long time);
|
||||||
|
|
||||||
|
static void setup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
|
||||||
|
*/
|
||||||
|
void setIntervalFromNow(unsigned long _interval);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* The method that will be called each time our thread gets a chance to run
|
||||||
|
*
|
||||||
|
* Returns desired period for next invocation (or RUN_SAME for no change)
|
||||||
|
*/
|
||||||
|
virtual int32_t runOnce() = 0;
|
||||||
|
|
||||||
|
// Do not override this
|
||||||
|
virtual void run();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -1,26 +1,24 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PeriodicTask.h"
|
#include "concurrency/OSThread.h"
|
||||||
|
|
||||||
namespace concurrency {
|
namespace concurrency
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Periodically invoke a callback. This just provides C-style callback conventions
|
* @brief Periodically invoke a callback. This just provides C-style callback conventions
|
||||||
* rather than a virtual function - FIXME, remove?
|
* rather than a virtual function - FIXME, remove?
|
||||||
*/
|
*/
|
||||||
class Periodic : public PeriodicTask
|
class Periodic : public OSThread
|
||||||
{
|
{
|
||||||
uint32_t (*callback)();
|
int32_t (*callback)();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
|
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
|
||||||
Periodic(uint32_t (*_callback)()) : callback(_callback) {}
|
Periodic(const char *name, int32_t (*_callback)()) : OSThread(name), callback(_callback) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void doTask() {
|
int32_t runOnce() { return callback(); }
|
||||||
uint32_t p = callback();
|
|
||||||
setPeriod(p);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace concurrency
|
} // namespace concurrency
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
#include "PeriodicScheduler.h"
|
|
||||||
#include "PeriodicTask.h"
|
|
||||||
#include "LockGuard.h"
|
|
||||||
|
|
||||||
namespace concurrency {
|
|
||||||
|
|
||||||
/// call this from loop
|
|
||||||
void PeriodicScheduler::loop()
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
LockGuard lg(&lock);
|
|
||||||
tasks.insert(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PeriodicScheduler::unschedule(PeriodicTask *t)
|
|
||||||
{
|
|
||||||
LockGuard lg(&lock);
|
|
||||||
tasks.erase(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#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
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#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
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "PeriodicScheduler.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 = 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
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#include "PosixNotifiedWorkerThread.h"
|
|
||||||
|
|
||||||
#ifdef __unix__
|
|
||||||
|
|
||||||
#include <Utility.h>
|
|
||||||
|
|
||||||
using namespace concurrency;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify this thread so it can run
|
|
||||||
*/
|
|
||||||
void PosixNotifiedWorkerThread::notify(uint32_t v, eNotifyAction action) NOT_IMPLEMENTED("notify");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
|
||||||
*/
|
|
||||||
void PosixNotifiedWorkerThread::block() NOT_IMPLEMENTED("block");
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BaseNotifiedWorkerThread.h"
|
|
||||||
|
|
||||||
namespace concurrency {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A worker thread that waits on a freertos notification
|
|
||||||
*/
|
|
||||||
class PosixNotifiedWorkerThread : public BaseNotifiedWorkerThread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Notify this thread so it can run
|
|
||||||
*/
|
|
||||||
void notify(uint32_t v = 0, eNotifyAction action = eNoAction);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
|
||||||
*/
|
|
||||||
virtual void block();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BaseThread.h"
|
|
||||||
|
|
||||||
#ifdef __unix__
|
|
||||||
|
|
||||||
namespace concurrency
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Base threading
|
|
||||||
*/
|
|
||||||
class PosixThread : public BaseThread
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
public:
|
|
||||||
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY) {}
|
|
||||||
|
|
||||||
virtual ~PosixThread() {}
|
|
||||||
|
|
||||||
// uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* The method that will be called when start is called.
|
|
||||||
*/
|
|
||||||
virtual void doRun() = 0;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "FreeRtosThread.h"
|
|
||||||
#include "PosixThread.h"
|
|
||||||
|
|
||||||
namespace concurrency
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef HAS_FREE_RTOS
|
|
||||||
typedef FreeRtosThread Thread;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __unix__
|
|
||||||
typedef PosixThread Thread;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
#include "WorkerThread.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 (millis() - lastPrint > 10 * 1000L) {
|
|
||||||
lastPrint = millis();
|
|
||||||
meshtastic::printThreadInfo("net");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
stopWatchdog();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace concurrency
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#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
|
|
||||||
@@ -40,7 +40,7 @@ void WiFiServerAPI::loop()
|
|||||||
|
|
||||||
#define MESHTASTIC_PORTNUM 4403
|
#define MESHTASTIC_PORTNUM 4403
|
||||||
|
|
||||||
WiFiServerPort::WiFiServerPort() : WiFiServer(MESHTASTIC_PORTNUM) {}
|
WiFiServerPort::WiFiServerPort() : WiFiServer(MESHTASTIC_PORTNUM), concurrency::OSThread("ApiServer") {}
|
||||||
|
|
||||||
void WiFiServerPort::init()
|
void WiFiServerPort::init()
|
||||||
{
|
{
|
||||||
@@ -48,7 +48,7 @@ void WiFiServerPort::init()
|
|||||||
begin();
|
begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFiServerPort::loop()
|
int32_t WiFiServerPort::runOnce()
|
||||||
{
|
{
|
||||||
auto client = available();
|
auto client = available();
|
||||||
if (client) {
|
if (client) {
|
||||||
@@ -59,7 +59,10 @@ void WiFiServerPort::loop()
|
|||||||
openAPI = new WiFiServerAPI(client);
|
openAPI = new WiFiServerAPI(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openAPI)
|
if (openAPI) {
|
||||||
// Allow idle processing so the API can read from its incoming stream
|
// Allow idle processing so the API can read from its incoming stream
|
||||||
openAPI->loop();
|
openAPI->loop();
|
||||||
|
return 0; // run fast while our API server is running
|
||||||
|
} else
|
||||||
|
return 100; // only check occasionally for incoming connections
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "StreamAPI.h"
|
#include "StreamAPI.h"
|
||||||
|
#include "concurrency/OSThread.h"
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,7 +28,7 @@ class WiFiServerAPI : public StreamAPI
|
|||||||
/**
|
/**
|
||||||
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
|
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
|
||||||
*/
|
*/
|
||||||
class WiFiServerPort : public WiFiServer
|
class WiFiServerPort : public WiFiServer, private concurrency::OSThread
|
||||||
{
|
{
|
||||||
/** The currently open port
|
/** The currently open port
|
||||||
*
|
*
|
||||||
@@ -41,5 +42,5 @@ class WiFiServerPort : public WiFiServer
|
|||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void loop();
|
int32_t runOnce();
|
||||||
};
|
};
|
||||||
|
|||||||
141
src/gps/GPS.cpp
141
src/gps/GPS.cpp
@@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
|
#include "RTC.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
// If we have a serial GPS port it will not be null
|
// If we have a serial GPS port it will not be null
|
||||||
#ifdef GPS_RX_PIN
|
#ifdef GPS_RX_PIN
|
||||||
@@ -23,76 +23,8 @@ uint8_t GPS::i2cAddress = GPS_I2C_ADDRESS;
|
|||||||
uint8_t GPS::i2cAddress = 0;
|
uint8_t GPS::i2cAddress = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool timeSetFromGPS; // We try to set our time from GPS each time we wake from sleep
|
|
||||||
|
|
||||||
GPS *gps;
|
GPS *gps;
|
||||||
|
|
||||||
// stuff that really should be in in the instance instead...
|
|
||||||
static uint32_t
|
|
||||||
timeStartMsec; // Once we have a GPS lock, this is where we hold the initial msec clock that corresponds to that time
|
|
||||||
static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only updated once on initial lock
|
|
||||||
|
|
||||||
void readFromRTC()
|
|
||||||
{
|
|
||||||
struct timeval tv; /* btw settimeofday() is helpfull here too*/
|
|
||||||
|
|
||||||
if (!gettimeofday(&tv, NULL)) {
|
|
||||||
uint32_t now = millis();
|
|
||||||
|
|
||||||
DEBUG_MSG("Read RTC time as %ld (cur millis %u) valid=%d\n", tv.tv_sec, now, timeSetFromGPS);
|
|
||||||
timeStartMsec = now;
|
|
||||||
zeroOffsetSecs = tv.tv_sec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
|
||||||
bool perhapsSetRTC(const struct timeval *tv)
|
|
||||||
{
|
|
||||||
if (!timeSetFromGPS) {
|
|
||||||
timeSetFromGPS = true;
|
|
||||||
DEBUG_MSG("Setting RTC %ld secs\n", tv->tv_sec);
|
|
||||||
#ifndef NO_ESP32
|
|
||||||
settimeofday(tv, NULL);
|
|
||||||
#else
|
|
||||||
DEBUG_MSG("ERROR TIME SETTING NOT IMPLEMENTED!\n");
|
|
||||||
#endif
|
|
||||||
readFromRTC();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool perhapsSetRTC(struct tm &t)
|
|
||||||
{
|
|
||||||
/* Convert to unix time
|
|
||||||
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
|
|
||||||
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
|
||||||
*/
|
|
||||||
time_t res = mktime(&t);
|
|
||||||
struct timeval tv;
|
|
||||||
tv.tv_sec = res;
|
|
||||||
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
|
|
||||||
|
|
||||||
// DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
|
|
||||||
if (t.tm_year < 0 || t.tm_year >= 300) {
|
|
||||||
// DEBUG_MSG("Ignoring invalid GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return perhapsSetRTC(&tv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t getTime()
|
|
||||||
{
|
|
||||||
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t getValidTime()
|
|
||||||
{
|
|
||||||
return timeSetFromGPS ? getTime() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GPS::setup()
|
bool GPS::setup()
|
||||||
{
|
{
|
||||||
setAwake(true); // Wake GPS power before doing any init
|
setAwake(true); // Wake GPS power before doing any init
|
||||||
@@ -104,6 +36,23 @@ bool GPS::setup()
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Record that we have a GPS
|
||||||
|
void GPS::setConnected()
|
||||||
|
{
|
||||||
|
if (!hasGPS) {
|
||||||
|
hasGPS = true;
|
||||||
|
shouldPublish = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPS::setNumSatellites(uint8_t n)
|
||||||
|
{
|
||||||
|
if (n != numSatellites) {
|
||||||
|
numSatellites = n;
|
||||||
|
shouldPublish = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
|
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
|
||||||
*
|
*
|
||||||
@@ -151,7 +100,7 @@ uint32_t GPS::getWakeTime() const
|
|||||||
return t; // already maxint
|
return t; // already maxint
|
||||||
|
|
||||||
if (t == 0)
|
if (t == 0)
|
||||||
t = 5 * 60; // Allow up to 5 mins for each attempt (probably will be much less if we can find sats)
|
t = 15 * 60; // Allow up to 5 mins for each attempt (probably will be much less if we can find sats)
|
||||||
|
|
||||||
t *= 1000; // msecs
|
t *= 1000; // msecs
|
||||||
|
|
||||||
@@ -165,7 +114,8 @@ uint32_t GPS::getSleepTime() const
|
|||||||
uint32_t t = radioConfig.preferences.gps_update_interval;
|
uint32_t t = radioConfig.preferences.gps_update_interval;
|
||||||
|
|
||||||
auto op = getGpsOp();
|
auto op = getGpsOp();
|
||||||
if ((timeSetFromGPS && op == GpsOperation_GpsOpTimeOnly) || (op == GpsOperation_GpsOpDisabled))
|
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||||
|
if ((gotTime && op == GpsOperation_GpsOpTimeOnly) || (op == GpsOperation_GpsOpDisabled))
|
||||||
t = UINT32_MAX; // Sleep forever now
|
t = UINT32_MAX; // Sleep forever now
|
||||||
|
|
||||||
if (t == UINT32_MAX)
|
if (t == UINT32_MAX)
|
||||||
@@ -181,19 +131,23 @@ uint32_t GPS::getSleepTime() const
|
|||||||
|
|
||||||
void GPS::publishUpdate()
|
void GPS::publishUpdate()
|
||||||
{
|
{
|
||||||
DEBUG_MSG("publishing GPS lock=%d\n", hasLock());
|
if (shouldPublish) {
|
||||||
|
shouldPublish = false;
|
||||||
|
|
||||||
// Notify any status instances that are observing us
|
DEBUG_MSG("publishing GPS lock=%d\n", hasLock());
|
||||||
const meshtastic::GPSStatus status =
|
|
||||||
meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
|
// Notify any status instances that are observing us
|
||||||
newStatus.notifyObservers(&status);
|
const meshtastic::GPSStatus status =
|
||||||
|
meshtastic::GPSStatus(hasLock(), isConnected(), latitude, longitude, altitude, dop, heading, numSatellites);
|
||||||
|
newStatus.notifyObservers(&status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPS::loop()
|
int32_t GPS::runOnce()
|
||||||
{
|
{
|
||||||
if (whileIdle()) {
|
if (whileIdle()) {
|
||||||
// if we have received valid NMEA claim we are connected
|
// if we have received valid NMEA claim we are connected
|
||||||
isConnected = true;
|
setConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are overdue for an update, turn on the GPS and at least publish the current status
|
// If we are overdue for an update, turn on the GPS and at least publish the current status
|
||||||
@@ -208,14 +162,23 @@ void GPS::loop()
|
|||||||
// While we are awake
|
// While we are awake
|
||||||
if (isAwake) {
|
if (isAwake) {
|
||||||
// DEBUG_MSG("looking for location\n");
|
// DEBUG_MSG("looking for location\n");
|
||||||
if ((now - lastWhileActiveMsec) > 1000) {
|
if ((now - lastWhileActiveMsec) > 5000) {
|
||||||
lastWhileActiveMsec = now;
|
lastWhileActiveMsec = now;
|
||||||
whileActive();
|
whileActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we've already set time from the GPS, no need to ask the GPS
|
// If we've already set time from the GPS, no need to ask the GPS
|
||||||
bool gotTime = timeSetFromGPS || lookForTime();
|
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||||
|
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
||||||
|
gotTime = true;
|
||||||
|
shouldPublish = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool gotLoc = lookForLocation();
|
bool gotLoc = lookForLocation();
|
||||||
|
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
|
||||||
|
hasValidLocation = true;
|
||||||
|
shouldPublish = true;
|
||||||
|
}
|
||||||
|
|
||||||
// We've been awake too long - force sleep
|
// We've been awake too long - force sleep
|
||||||
auto wakeTime = getWakeTime();
|
auto wakeTime = getWakeTime();
|
||||||
@@ -224,9 +187,7 @@ void GPS::loop()
|
|||||||
// Once we get a location we no longer desperately want an update
|
// Once we get a location we no longer desperately want an update
|
||||||
// or if we got a time and we are in GpsOpTimeOnly mode
|
// or if we got a time and we are in GpsOpTimeOnly mode
|
||||||
// DEBUG_MSG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime);
|
// DEBUG_MSG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime);
|
||||||
if (gotLoc || tooLong || (gotTime && getGpsOp() == GpsOperation_GpsOpTimeOnly)) {
|
if ((gotLoc && gotTime) || tooLong || (gotTime && getGpsOp() == GpsOperation_GpsOpTimeOnly)) {
|
||||||
if (gotLoc)
|
|
||||||
hasValidLocation = true;
|
|
||||||
|
|
||||||
if (tooLong) {
|
if (tooLong) {
|
||||||
// we didn't get a location during this ack window, therefore declare loss of lock
|
// we didn't get a location during this ack window, therefore declare loss of lock
|
||||||
@@ -234,9 +195,16 @@ void GPS::loop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
setAwake(false);
|
setAwake(false);
|
||||||
publishUpdate(); // publish our update for this just finished acquisition window
|
shouldPublish = true; // publish our update for this just finished acquisition window
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If state has changed do a publish
|
||||||
|
publishUpdate();
|
||||||
|
|
||||||
|
// 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms
|
||||||
|
// if not awake we can run super infrquently (once every 5 secs?) to see if we need to wake.
|
||||||
|
return isAwake ? 100 : 5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPS::forceWake(bool on)
|
void GPS::forceWake(bool on)
|
||||||
@@ -247,7 +215,10 @@ void GPS::forceWake(bool on)
|
|||||||
wakeAllowed = true;
|
wakeAllowed = true;
|
||||||
} else {
|
} else {
|
||||||
wakeAllowed = false;
|
wakeAllowed = false;
|
||||||
setAwake(false);
|
|
||||||
|
// Note: if the gps was already awake, we DO NOT shut it down, because we want to allow it to complete its lock
|
||||||
|
// attempt even if we are in light sleep. Once the attempt succeeds (or times out) we'll then shut it down.
|
||||||
|
// setAwake(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../concurrency/PeriodicTask.h"
|
|
||||||
#include "GPSStatus.h"
|
#include "GPSStatus.h"
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "sys/time.h"
|
#include "concurrency/OSThread.h"
|
||||||
|
|
||||||
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
|
||||||
bool perhapsSetRTC(const struct timeval *tv);
|
|
||||||
bool perhapsSetRTC(struct tm &t);
|
|
||||||
|
|
||||||
// Generate a string representation of DOP
|
// Generate a string representation of DOP
|
||||||
const char *getDOPString(uint32_t dop);
|
const char *getDOPString(uint32_t dop);
|
||||||
|
|
||||||
/// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero
|
|
||||||
uint32_t getTime();
|
|
||||||
|
|
||||||
/// Return time since 1970 in secs. If we don't have a GPS lock return zero
|
|
||||||
uint32_t getValidTime();
|
|
||||||
|
|
||||||
void readFromRTC();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading)
|
* A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading)
|
||||||
*
|
*
|
||||||
* When new data is available it will notify observers.
|
* When new data is available it will notify observers.
|
||||||
*/
|
*/
|
||||||
class GPS
|
class GPS : private concurrency::OSThread
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastWhileActiveMsec = 0;
|
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastWhileActiveMsec = 0;
|
||||||
@@ -36,9 +23,14 @@ class GPS
|
|||||||
|
|
||||||
bool wakeAllowed = true; // false if gps must be forced to sleep regardless of what time it is
|
bool wakeAllowed = true; // false if gps must be forced to sleep regardless of what time it is
|
||||||
|
|
||||||
|
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
|
||||||
|
|
||||||
|
bool hasGPS = false; // Do we have a GPS we are talking to
|
||||||
|
|
||||||
|
uint8_t numSatellites = 0;
|
||||||
|
|
||||||
CallbackObserver<GPS, void *> notifySleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareSleep);
|
CallbackObserver<GPS, void *> notifySleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareSleep);
|
||||||
|
|
||||||
protected:
|
|
||||||
public:
|
public:
|
||||||
/** If !NULL we will use this serial port to construct our GPS */
|
/** If !NULL we will use this serial port to construct our GPS */
|
||||||
static HardwareSerial *_serial_gps;
|
static HardwareSerial *_serial_gps;
|
||||||
@@ -51,9 +43,8 @@ class GPS
|
|||||||
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs
|
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs
|
||||||
// scaling before use)
|
// scaling before use)
|
||||||
uint32_t heading = 0; // Heading of motion, in degrees * 10^-5
|
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
|
GPS() : concurrency::OSThread("GPS") {}
|
||||||
|
|
||||||
virtual ~GPS() {} // FIXME, we really should unregister our sleep observer
|
virtual ~GPS() {} // FIXME, we really should unregister our sleep observer
|
||||||
|
|
||||||
@@ -65,11 +56,12 @@ class GPS
|
|||||||
*/
|
*/
|
||||||
virtual bool setup();
|
virtual bool setup();
|
||||||
|
|
||||||
virtual void loop();
|
|
||||||
|
|
||||||
/// Returns ture if we have acquired GPS lock.
|
/// Returns ture if we have acquired GPS lock.
|
||||||
bool hasLock() const { return hasValidLocation; }
|
bool hasLock() const { return hasValidLocation; }
|
||||||
|
|
||||||
|
/// Return true if we are connected to a GPS
|
||||||
|
bool isConnected() const { return hasGPS; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
|
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
|
||||||
* called after the CPU wakes from light-sleep state
|
* called after the CPU wakes from light-sleep state
|
||||||
@@ -113,6 +105,11 @@ class GPS
|
|||||||
*/
|
*/
|
||||||
virtual bool lookForLocation() = 0;
|
virtual bool lookForLocation() = 0;
|
||||||
|
|
||||||
|
/// Record that we have a GPS
|
||||||
|
void setConnected();
|
||||||
|
|
||||||
|
void setNumSatellites(uint8_t n);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
|
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
|
||||||
/// always returns 0 to indicate okay to sleep
|
/// always returns 0 to indicate okay to sleep
|
||||||
@@ -139,6 +136,8 @@ class GPS
|
|||||||
* Tell users we have new GPS readings
|
* Tell users we have new GPS readings
|
||||||
*/
|
*/
|
||||||
void publishUpdate();
|
void publishUpdate();
|
||||||
|
|
||||||
|
virtual int32_t runOnce();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern GPS *gps;
|
extern GPS *gps;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "NMEAGPS.h"
|
#include "NMEAGPS.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "RTC.h"
|
||||||
|
|
||||||
static int32_t toDegInt(RawDegrees d)
|
static int32_t toDegInt(RawDegrees d)
|
||||||
{
|
{
|
||||||
@@ -44,7 +45,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
t.tm_mon = d.month() - 1;
|
t.tm_mon = d.month() - 1;
|
||||||
t.tm_year = d.year() - 1900;
|
t.tm_year = d.year() - 1900;
|
||||||
t.tm_isdst = false;
|
t.tm_isdst = false;
|
||||||
perhapsSetRTC(t);
|
perhapsSetRTC(RTCQualityGPS, t);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else
|
} else
|
||||||
@@ -83,7 +84,7 @@ bool NMEAGPS::lookForLocation()
|
|||||||
heading = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
|
heading = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
|
||||||
}
|
}
|
||||||
if (reader.satellites.isValid()) {
|
if (reader.satellites.isValid()) {
|
||||||
numSatellites = reader.satellites.value();
|
setNumSatellites(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
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../concurrency/PeriodicTask.h"
|
|
||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "TinyGPS++.h"
|
#include "TinyGPS++.h"
|
||||||
|
|||||||
77
src/gps/RTC.cpp
Normal file
77
src/gps/RTC.cpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include "RTC.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
static RTCQuality currentQuality = RTCQualityNone;
|
||||||
|
|
||||||
|
RTCQuality getRTCQuality()
|
||||||
|
{
|
||||||
|
return currentQuality;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stuff that really should be in in the instance instead...
|
||||||
|
static uint32_t
|
||||||
|
timeStartMsec; // Once we have a GPS lock, this is where we hold the initial msec clock that corresponds to that time
|
||||||
|
static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only updated once on initial lock
|
||||||
|
|
||||||
|
void readFromRTC()
|
||||||
|
{
|
||||||
|
struct timeval tv; /* btw settimeofday() is helpfull here too*/
|
||||||
|
|
||||||
|
if (!gettimeofday(&tv, NULL)) {
|
||||||
|
uint32_t now = millis();
|
||||||
|
|
||||||
|
DEBUG_MSG("Read RTC time as %ld (cur millis %u) quality=%d\n", tv.tv_sec, now, currentQuality);
|
||||||
|
timeStartMsec = now;
|
||||||
|
zeroOffsetSecs = tv.tv_sec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
||||||
|
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
|
||||||
|
{
|
||||||
|
if (q > currentQuality) {
|
||||||
|
currentQuality = q;
|
||||||
|
DEBUG_MSG("Setting RTC %ld secs\n", tv->tv_sec);
|
||||||
|
#ifndef NO_ESP32
|
||||||
|
settimeofday(tv, NULL);
|
||||||
|
#else
|
||||||
|
DEBUG_MSG("ERROR TIME SETTING NOT IMPLEMENTED!\n");
|
||||||
|
#endif
|
||||||
|
readFromRTC();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool perhapsSetRTC(RTCQuality q, struct tm &t)
|
||||||
|
{
|
||||||
|
/* Convert to unix time
|
||||||
|
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
|
||||||
|
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
||||||
|
*/
|
||||||
|
time_t res = mktime(&t);
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = res;
|
||||||
|
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
|
||||||
|
|
||||||
|
// DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
|
||||||
|
if (t.tm_year < 0 || t.tm_year >= 300) {
|
||||||
|
// DEBUG_MSG("Ignoring invalid GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return perhapsSetRTC(q, &tv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getTime()
|
||||||
|
{
|
||||||
|
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getValidTime(RTCQuality minQuality)
|
||||||
|
{
|
||||||
|
return (currentQuality >= minQuality) ? getTime() : 0;
|
||||||
|
}
|
||||||
30
src/gps/RTC.h
Normal file
30
src/gps/RTC.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "sys/time.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
enum RTCQuality {
|
||||||
|
/// We haven't had our RTC set yet
|
||||||
|
RTCQualityNone = 0,
|
||||||
|
|
||||||
|
/// Some other node gave us a time we can use
|
||||||
|
RTCQualityFromNet = 1,
|
||||||
|
|
||||||
|
/// Our time is based on our own GPS
|
||||||
|
RTCQualityGPS = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
RTCQuality getRTCQuality();
|
||||||
|
|
||||||
|
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
||||||
|
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv);
|
||||||
|
bool perhapsSetRTC(RTCQuality q, struct tm &t);
|
||||||
|
|
||||||
|
/// Return time since 1970 in secs. While quality is RTCQualityNone we will be returning time based at zero
|
||||||
|
uint32_t getTime();
|
||||||
|
|
||||||
|
/// Return time since 1970 in secs. If quality is RTCQualityNone return zero
|
||||||
|
uint32_t getValidTime(RTCQuality minQuality);
|
||||||
|
|
||||||
|
void readFromRTC();
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "UBloxGPS.h"
|
#include "UBloxGPS.h"
|
||||||
|
#include "RTC.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -7,20 +8,23 @@ UBloxGPS::UBloxGPS() {}
|
|||||||
|
|
||||||
bool UBloxGPS::tryConnect()
|
bool UBloxGPS::tryConnect()
|
||||||
{
|
{
|
||||||
isConnected = false;
|
bool c = false;
|
||||||
|
|
||||||
if (_serial_gps)
|
if (_serial_gps)
|
||||||
isConnected = ublox.begin(*_serial_gps);
|
c = ublox.begin(*_serial_gps);
|
||||||
|
|
||||||
if (!isConnected && i2cAddress) {
|
if (!c && i2cAddress) {
|
||||||
extern bool neo6M; // Super skanky - if we are talking to the device i2c we assume it is a neo7 on a RAK815, which
|
extern bool neo6M; // Super skanky - if we are talking to the device i2c we assume it is a neo7 on a RAK815, which
|
||||||
// supports the newer API
|
// supports the newer API
|
||||||
neo6M = true;
|
neo6M = true;
|
||||||
|
|
||||||
isConnected = ublox.begin(Wire, i2cAddress);
|
c = ublox.begin(Wire, i2cAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
return isConnected;
|
if (c)
|
||||||
|
setConnected();
|
||||||
|
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UBloxGPS::setupGPS()
|
bool UBloxGPS::setupGPS()
|
||||||
@@ -32,7 +36,7 @@ bool UBloxGPS::setupGPS()
|
|||||||
_serial_gps->begin(GPS_BAUDRATE);
|
_serial_gps->begin(GPS_BAUDRATE);
|
||||||
#endif
|
#endif
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
_serial_gps->setRxBufferSize(1024); // the default is 256
|
_serial_gps->setRxBufferSize(2048); // the default is 256
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +48,7 @@ bool UBloxGPS::setupGPS()
|
|||||||
for (int i = 0; (i < 3) && !tryConnect(); i++)
|
for (int i = 0; (i < 3) && !tryConnect(); i++)
|
||||||
delay(500);
|
delay(500);
|
||||||
|
|
||||||
if (isConnected) {
|
if (isConnected()) {
|
||||||
DEBUG_MSG("Connected to UBLOX GPS successfully\n");
|
DEBUG_MSG("Connected to UBLOX GPS successfully\n");
|
||||||
|
|
||||||
if (!setUBXMode())
|
if (!setUBXMode())
|
||||||
@@ -105,8 +109,8 @@ bool UBloxGPS::factoryReset()
|
|||||||
for (int i = 0; (i < 3) && !tryConnect(); i++)
|
for (int i = 0; (i < 3) && !tryConnect(); i++)
|
||||||
delay(500);
|
delay(500);
|
||||||
|
|
||||||
DEBUG_MSG("GPS Factory reset success=%d\n", isConnected);
|
DEBUG_MSG("GPS Factory reset success=%d\n", isConnected());
|
||||||
if (isConnected)
|
if (isConnected())
|
||||||
ok = setUBXMode();
|
ok = setUBXMode();
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
@@ -126,7 +130,7 @@ void UBloxGPS::whileActive()
|
|||||||
// Update fixtype
|
// Update fixtype
|
||||||
if (ublox.moduleQueried.fixType) {
|
if (ublox.moduleQueried.fixType) {
|
||||||
fixType = ublox.getFixType(0);
|
fixType = ublox.getFixType(0);
|
||||||
DEBUG_MSG("GPS fix type %d\n", fixType);
|
// DEBUG_MSG("GPS fix type %d, numSats %d\n", fixType, numSatellites);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,23 +142,21 @@ void UBloxGPS::whileActive()
|
|||||||
*/
|
*/
|
||||||
bool UBloxGPS::lookForTime()
|
bool UBloxGPS::lookForTime()
|
||||||
{
|
{
|
||||||
if (fixType >= 2) {
|
if (ublox.moduleQueried.gpsSecond) {
|
||||||
if (ublox.moduleQueried.gpsSecond) {
|
/* Convert to unix time
|
||||||
/* Convert to unix time
|
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January
|
||||||
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January
|
1, 1970 (midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
||||||
1, 1970 (midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
*/
|
||||||
*/
|
struct tm t;
|
||||||
struct tm t;
|
t.tm_sec = ublox.getSecond(0);
|
||||||
t.tm_sec = ublox.getSecond(0);
|
t.tm_min = ublox.getMinute(0);
|
||||||
t.tm_min = ublox.getMinute(0);
|
t.tm_hour = ublox.getHour(0);
|
||||||
t.tm_hour = ublox.getHour(0);
|
t.tm_mday = ublox.getDay(0);
|
||||||
t.tm_mday = ublox.getDay(0);
|
t.tm_mon = ublox.getMonth(0) - 1;
|
||||||
t.tm_mon = ublox.getMonth(0) - 1;
|
t.tm_year = ublox.getYear(0) - 1900;
|
||||||
t.tm_year = ublox.getYear(0) - 1900;
|
t.tm_isdst = false;
|
||||||
t.tm_isdst = false;
|
perhapsSetRTC(RTCQualityGPS, t);
|
||||||
perhapsSetRTC(t);
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -170,6 +172,12 @@ bool UBloxGPS::lookForLocation()
|
|||||||
{
|
{
|
||||||
bool foundLocation = false;
|
bool foundLocation = false;
|
||||||
|
|
||||||
|
if (ublox.moduleQueried.SIV)
|
||||||
|
setNumSatellites(ublox.getSIV(0));
|
||||||
|
|
||||||
|
if (ublox.moduleQueried.pDOP)
|
||||||
|
dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
|
||||||
|
|
||||||
// we only notify if position has changed due to a new fix
|
// we only notify if position has changed due to a new fix
|
||||||
if ((fixType >= 3 && fixType <= 4)) {
|
if ((fixType >= 3 && fixType <= 4)) {
|
||||||
if (ublox.moduleQueried.latitude) // rd fixes only
|
if (ublox.moduleQueried.latitude) // rd fixes only
|
||||||
@@ -177,17 +185,14 @@ bool UBloxGPS::lookForLocation()
|
|||||||
latitude = ublox.getLatitude(0);
|
latitude = ublox.getLatitude(0);
|
||||||
longitude = ublox.getLongitude(0);
|
longitude = ublox.getLongitude(0);
|
||||||
altitude = ublox.getAltitudeMSL(0) / 1000; // in mm convert to meters
|
altitude = ublox.getAltitudeMSL(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
|
|
||||||
|
|
||||||
// Note: heading is only currently implmented in the ublox for the 8m chipset - therefore
|
// Note: heading is only currently implmented in the ublox for the 8m chipset - therefore
|
||||||
// don't read it here - it will generate an ignored getPVT command on the 6ms
|
// don't read it here - it will generate an ignored getPVT command on the 6ms
|
||||||
// heading = ublox.getHeading(0);
|
// 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!
|
||||||
foundLocation =
|
foundLocation = (latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000);
|
||||||
(latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000) && (numSatellites > 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -561,7 +561,9 @@ void _screen_header()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Screen::Screen(uint8_t address, int sda, int scl) : cmdQueue(32), dispdev(address, sda, scl), ui(&dispdev) {}
|
Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl), ui(&dispdev) {
|
||||||
|
cmdQueue.setReader(this);
|
||||||
|
}
|
||||||
|
|
||||||
void Screen::handleSetOn(bool on)
|
void Screen::handleSetOn(bool on)
|
||||||
{
|
{
|
||||||
@@ -573,9 +575,12 @@ void Screen::handleSetOn(bool on)
|
|||||||
DEBUG_MSG("Turning on screen\n");
|
DEBUG_MSG("Turning on screen\n");
|
||||||
dispdev.displayOn();
|
dispdev.displayOn();
|
||||||
dispdev.displayOn();
|
dispdev.displayOn();
|
||||||
|
enabled = true;
|
||||||
|
setInterval(0); // Draw ASAP
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("Turning off screen\n");
|
DEBUG_MSG("Turning off screen\n");
|
||||||
dispdev.displayOff();
|
dispdev.displayOff();
|
||||||
|
enabled = false;
|
||||||
}
|
}
|
||||||
screenOn = on;
|
screenOn = on;
|
||||||
}
|
}
|
||||||
@@ -583,8 +588,6 @@ void Screen::handleSetOn(bool on)
|
|||||||
|
|
||||||
void Screen::setup()
|
void Screen::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.
|
||||||
useDisplay = true;
|
useDisplay = true;
|
||||||
@@ -642,14 +645,24 @@ void Screen::setup()
|
|||||||
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Screen::doTask()
|
int32_t Screen::runOnce()
|
||||||
{
|
{
|
||||||
// If we don't have a screen, don't ever spend any CPU for us.
|
// If we don't have a screen, don't ever spend any CPU for us.
|
||||||
if (!useDisplay) {
|
if (!useDisplay) {
|
||||||
setPeriod(0);
|
enabled = false;
|
||||||
return;
|
return RUN_SAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show boot screen for first 3 seconds, then switch to normal operation.
|
||||||
|
static bool showingBootScreen = true;
|
||||||
|
if (showingBootScreen && (millis() > 3000)) {
|
||||||
|
stopBootScreen();
|
||||||
|
showingBootScreen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the screen last, after we've figured out what to show.
|
||||||
|
debug_info()->setChannelNameStatus(getChannelName());
|
||||||
|
|
||||||
// Process incoming commands.
|
// Process incoming commands.
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ScreenCmd cmd;
|
ScreenCmd cmd;
|
||||||
@@ -684,8 +697,8 @@ void Screen::doTask()
|
|||||||
|
|
||||||
if (!screenOn) { // If we didn't just wake and the screen is still off, then
|
if (!screenOn) { // If we didn't just wake and the screen is still off, then
|
||||||
// stop updating until it is on again
|
// stop updating until it is on again
|
||||||
setPeriod(0);
|
enabled = false;
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch to a low framerate (to save CPU) when we are not in transition
|
// Switch to a low framerate (to save CPU) when we are not in transition
|
||||||
@@ -711,7 +724,7 @@ void Screen::doTask()
|
|||||||
// soon, otherwise just 1 fps (to save CPU) We also ask to be called twice
|
// soon, otherwise just 1 fps (to save CPU) We also ask to be called twice
|
||||||
// as fast as we really need so that any rounding errors still result with
|
// as fast as we really need so that any rounding errors still result with
|
||||||
// the correct framerate
|
// the correct framerate
|
||||||
setPeriod(1000 / targetFramerate);
|
return (1000 / targetFramerate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Screen::drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
void Screen::drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
@@ -801,7 +814,7 @@ void Screen::handleOnPress()
|
|||||||
// If screen was off, just wake it, otherwise advance to next frame
|
// If screen was off, just wake it, otherwise advance to next frame
|
||||||
// If we are in a transition, the press must have bounced, drop it.
|
// If we are in a transition, the press must have bounced, drop it.
|
||||||
if (ui.getUiState()->frameState == FIXED) {
|
if (ui.getUiState()->frameState == FIXED) {
|
||||||
setPeriod(1); // redraw ASAP
|
setInterval(0); // redraw ASAP
|
||||||
ui.nextFrame();
|
ui.nextFrame();
|
||||||
|
|
||||||
DEBUG_MSG("Setting fast framerate\n");
|
DEBUG_MSG("Setting fast framerate\n");
|
||||||
@@ -1062,7 +1075,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
|||||||
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
|
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
|
||||||
setFrames(); // Regen the list of screens
|
setFrames(); // Regen the list of screens
|
||||||
prevFrame = -1; // Force a GUI update
|
prevFrame = -1; // Force a GUI update
|
||||||
setPeriod(1); // Update the screen right away
|
setInterval(0); // Update the screen right away
|
||||||
}
|
}
|
||||||
nodeDB.updateGUI = false;
|
nodeDB.updateGUI = false;
|
||||||
nodeDB.updateTextMessage = false;
|
nodeDB.updateTextMessage = false;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
#include "TypedQueue.h"
|
#include "TypedQueue.h"
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
#include "concurrency/LockGuard.h"
|
#include "concurrency/LockGuard.h"
|
||||||
#include "concurrency/PeriodicTask.h"
|
#include "concurrency/OSThread.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ class DebugInfo
|
|||||||
* multiple times simultaneously. All state-changing calls are queued and executed
|
* multiple times simultaneously. All state-changing calls are queued and executed
|
||||||
* when the main loop calls us.
|
* when the main loop calls us.
|
||||||
*/
|
*/
|
||||||
class Screen : public concurrency::PeriodicTask
|
class Screen : public concurrency::OSThread
|
||||||
{
|
{
|
||||||
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver =
|
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver =
|
||||||
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||||
@@ -184,7 +184,7 @@ class Screen : public concurrency::PeriodicTask
|
|||||||
/// Updates the UI.
|
/// Updates the UI.
|
||||||
//
|
//
|
||||||
// Called periodically from the main loop.
|
// Called periodically from the main loop.
|
||||||
void doTask() final;
|
int32_t runOnce() final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ScreenCmd {
|
struct ScreenCmd {
|
||||||
@@ -202,7 +202,7 @@ class Screen : public concurrency::PeriodicTask
|
|||||||
return true; // claim success if our display is not in use
|
return true; // claim success if our display is not in use
|
||||||
else {
|
else {
|
||||||
bool success = cmdQueue.enqueue(cmd, 0);
|
bool success = cmdQueue.enqueue(cmd, 0);
|
||||||
setPeriod(1); // handle ASAP
|
enabled = true; // handle ASAP (we are the registered reader for cmdQueue, but might have been disabled)
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
224
src/main.cpp
224
src/main.cpp
@@ -1,25 +1,3 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Main module
|
|
||||||
|
|
||||||
# Modified by Kyle T. Gabriel to fix issue with incorrect GPS data for TTNMapper
|
|
||||||
|
|
||||||
Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com>
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Air530GPS.h"
|
#include "Air530GPS.h"
|
||||||
#include "MeshRadio.h"
|
#include "MeshRadio.h"
|
||||||
@@ -27,14 +5,16 @@
|
|||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "UBloxGPS.h"
|
#include "UBloxGPS.h"
|
||||||
#include "concurrency/Periodic.h"
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
// #include "rom/rtc.h"
|
// #include "rom/rtc.h"
|
||||||
#include "DSRRouter.h"
|
#include "DSRRouter.h"
|
||||||
// #include "debug.h"
|
// #include "debug.h"
|
||||||
|
#include "RTC.h"
|
||||||
#include "SPILock.h"
|
#include "SPILock.h"
|
||||||
|
#include "concurrency/OSThread.h"
|
||||||
|
#include "concurrency/Periodic.h"
|
||||||
#include "graphics/Screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "meshwifi/meshhttp.h"
|
#include "meshwifi/meshhttp.h"
|
||||||
@@ -56,8 +36,10 @@
|
|||||||
#include "variant.h"
|
#include "variant.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using namespace concurrency;
|
||||||
|
|
||||||
// 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
|
||||||
graphics::Screen screen(SSD1306_ADDRESS);
|
graphics::Screen *screen;
|
||||||
|
|
||||||
// Global power status
|
// Global power status
|
||||||
meshtastic::PowerStatus *powerStatus = new meshtastic::PowerStatus();
|
meshtastic::PowerStatus *powerStatus = new meshtastic::PowerStatus();
|
||||||
@@ -71,8 +53,7 @@ meshtastic::NodeStatus *nodeStatus = new meshtastic::NodeStatus();
|
|||||||
bool ssd1306_found;
|
bool ssd1306_found;
|
||||||
bool axp192_found;
|
bool axp192_found;
|
||||||
|
|
||||||
DSRRouter realRouter;
|
Router *router = NULL; // Users of router don't care what sort of subclass implements that API
|
||||||
Router &router = realRouter; // Users of router don't care what sort of subclass implements that API
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Application
|
// Application
|
||||||
@@ -122,7 +103,7 @@ const char *getDeviceName()
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t ledBlinker()
|
static int32_t ledBlinker()
|
||||||
{
|
{
|
||||||
static bool ledOn;
|
static bool ledOn;
|
||||||
ledOn ^= 1;
|
ledOn ^= 1;
|
||||||
@@ -130,26 +111,105 @@ static uint32_t ledBlinker()
|
|||||||
setLed(ledOn);
|
setLed(ledOn);
|
||||||
|
|
||||||
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
|
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
|
||||||
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 2 : 1000);
|
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 1 : 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
concurrency::Periodic ledPeriodic(ledBlinker);
|
/// Wrapper to convert our powerFSM stuff into a 'thread'
|
||||||
|
class PowerFSMThread : public OSThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
|
||||||
|
PowerFSMThread() : OSThread("PowerFSM") {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int32_t runOnce()
|
||||||
|
{
|
||||||
|
powerFSM.run_machine();
|
||||||
|
|
||||||
|
/// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake
|
||||||
|
/// cpu for serial rx - FIXME)
|
||||||
|
auto state = powerFSM.getState();
|
||||||
|
canSleep = (state != &statePOWER) && (state != &stateSERIAL);
|
||||||
|
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watch a GPIO and if we get an IRQ, wake the main thread.
|
||||||
|
* Use to add wake on button press
|
||||||
|
*/
|
||||||
|
void wakeOnIrq(int irq, int mode)
|
||||||
|
{
|
||||||
|
attachInterrupt(
|
||||||
|
irq,
|
||||||
|
[] {
|
||||||
|
BaseType_t higherWake = 0;
|
||||||
|
mainDelay.interruptFromISR(&higherWake);
|
||||||
|
},
|
||||||
|
FALLING);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ButtonThread : public OSThread
|
||||||
|
{
|
||||||
// Prepare for button presses
|
// Prepare for button presses
|
||||||
#ifdef BUTTON_PIN
|
#ifdef BUTTON_PIN
|
||||||
OneButton userButton;
|
OneButton userButton;
|
||||||
#endif
|
#endif
|
||||||
#ifdef BUTTON_PIN_ALT
|
#ifdef BUTTON_PIN_ALT
|
||||||
OneButton userButtonAlt;
|
OneButton userButtonAlt;
|
||||||
#endif
|
#endif
|
||||||
void userButtonPressed()
|
|
||||||
{
|
public:
|
||||||
powerFSM.trigger(EVENT_PRESS);
|
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
|
||||||
}
|
ButtonThread() : OSThread("Button")
|
||||||
void userButtonPressedLong()
|
{
|
||||||
{
|
#ifdef BUTTON_PIN
|
||||||
screen.adjustBrightness();
|
userButton = OneButton(BUTTON_PIN, true, true);
|
||||||
}
|
userButton.attachClick(userButtonPressed);
|
||||||
|
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||||
|
wakeOnIrq(BUTTON_PIN, FALLING);
|
||||||
|
#endif
|
||||||
|
#ifdef BUTTON_PIN_ALT
|
||||||
|
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
|
||||||
|
userButtonAlt.attachClick(userButtonPressed);
|
||||||
|
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||||
|
wakeOnIrq(BUTTON_PIN_ALT, FALLING);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// If the button is pressed we suppress CPU sleep until release
|
||||||
|
int32_t runOnce()
|
||||||
|
{
|
||||||
|
canSleep = true; // Assume we should not keep the board awake
|
||||||
|
|
||||||
|
#ifdef BUTTON_PIN
|
||||||
|
userButton.tick();
|
||||||
|
canSleep &= userButton.isIdle();
|
||||||
|
#endif
|
||||||
|
#ifdef BUTTON_PIN_ALT
|
||||||
|
userButtonAlt.tick();
|
||||||
|
canSleep &= userButton.isIdle();
|
||||||
|
#endif
|
||||||
|
// if(!canSleep) DEBUG_MSG("Supressing sleep!\n");
|
||||||
|
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void userButtonPressed()
|
||||||
|
{
|
||||||
|
// DEBUG_MSG("press!\n");
|
||||||
|
powerFSM.trigger(EVENT_PRESS);
|
||||||
|
}
|
||||||
|
static void userButtonPressedLong() { screen->adjustBrightness(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
static Periodic *ledPeriodic;
|
||||||
|
static OSThread *powerFSMthread, *buttonThread;
|
||||||
|
|
||||||
|
RadioInterface *rIf = NULL;
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
@@ -174,6 +234,12 @@ void setup()
|
|||||||
digitalWrite(RESET_OLED, 1);
|
digitalWrite(RESET_OLED, 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
OSThread::setup();
|
||||||
|
|
||||||
|
ledPeriodic = new Periodic("Blink", ledBlinker);
|
||||||
|
|
||||||
|
router = new DSRRouter();
|
||||||
|
|
||||||
#ifdef I2C_SDA
|
#ifdef I2C_SDA
|
||||||
Wire.begin(I2C_SDA, I2C_SCL);
|
Wire.begin(I2C_SDA, I2C_SCL);
|
||||||
#else
|
#else
|
||||||
@@ -185,23 +251,12 @@ void setup()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Buttons & LED
|
// Buttons & LED
|
||||||
#ifdef BUTTON_PIN
|
buttonThread = new ButtonThread();
|
||||||
userButton = OneButton(BUTTON_PIN, true, true);
|
|
||||||
userButton.attachClick(userButtonPressed);
|
|
||||||
userButton.attachDuringLongPress(userButtonPressedLong);
|
|
||||||
#endif
|
|
||||||
#ifdef BUTTON_PIN_ALT
|
|
||||||
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
|
|
||||||
userButtonAlt.attachClick(userButtonPressed);
|
|
||||||
userButton.attachDuringLongPress(userButtonPressedLong);
|
|
||||||
#endif
|
|
||||||
#ifdef LED_PIN
|
#ifdef LED_PIN
|
||||||
pinMode(LED_PIN, OUTPUT);
|
pinMode(LED_PIN, OUTPUT);
|
||||||
digitalWrite(LED_PIN, 1 ^ LED_INVERTED); // turn on for now
|
digitalWrite(LED_PIN, 1 ^ LED_INVERTED); // turn on for now
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ledPeriodic.setup();
|
|
||||||
|
|
||||||
// Hello
|
// Hello
|
||||||
DEBUG_MSG("Meshtastic swver=%s, hwver=%s\n", optstr(APP_VERSION), optstr(HW_VERSION));
|
DEBUG_MSG("Meshtastic swver=%s, hwver=%s\n", optstr(APP_VERSION), optstr(HW_VERSION));
|
||||||
|
|
||||||
@@ -234,14 +289,15 @@ void setup()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Initialize the screen first so we can show the logo while we start up everything else.
|
// Initialize the screen first so we can show the logo while we start up everything else.
|
||||||
|
screen = new graphics::Screen(SSD1306_ADDRESS);
|
||||||
#if defined(ST7735_CS) || defined(HAS_EINK)
|
#if defined(ST7735_CS) || defined(HAS_EINK)
|
||||||
screen.setup();
|
screen->setup();
|
||||||
#else
|
#else
|
||||||
if (ssd1306_found)
|
if (ssd1306_found)
|
||||||
screen.setup();
|
screen->setup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
screen.print("Started...\n");
|
screen->print("Started...\n");
|
||||||
|
|
||||||
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
|
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
|
||||||
|
|
||||||
@@ -298,8 +354,7 @@ void setup()
|
|||||||
digitalWrite(SX1262_ANT_SW, 1);
|
digitalWrite(SX1262_ANT_SW, 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// MUST BE AFTER service.init, so we have our radio config settings (from nodedb init)
|
// radio init MUST BE AFTER service.init, so we have our radio config settings (from nodedb init)
|
||||||
RadioInterface *rIf = NULL;
|
|
||||||
|
|
||||||
#if defined(RF95_IRQ)
|
#if defined(RF95_IRQ)
|
||||||
if (!rIf) {
|
if (!rIf) {
|
||||||
@@ -340,10 +395,11 @@ void setup()
|
|||||||
if (!rIf)
|
if (!rIf)
|
||||||
recordCriticalError(ErrNoRadio);
|
recordCriticalError(ErrNoRadio);
|
||||||
else
|
else
|
||||||
router.addInterface(rIf);
|
router->addInterface(rIf);
|
||||||
|
|
||||||
// This must be _after_ service.init because we need our preferences loaded from flash to have proper timeout values
|
// This must be _after_ service.init because we need our preferences loaded from flash to have proper timeout values
|
||||||
PowerFSM_setup(); // we will transition to ON in a couple of seconds, FIXME, only do this for cold boots, not waking from SDS
|
PowerFSM_setup(); // we will transition to ON in a couple of seconds, FIXME, only do this for cold boots, not waking from SDS
|
||||||
|
powerFSMthread = new PowerFSMThread();
|
||||||
|
|
||||||
// setBluetoothEnable(false); we now don't start bluetooth until we enter the proper state
|
// setBluetoothEnable(false); we now don't start bluetooth until we enter the proper state
|
||||||
setCPUFast(false); // 80MHz is fine for our slow peripherals
|
setCPUFast(false); // 80MHz is fine for our slow peripherals
|
||||||
@@ -366,21 +422,12 @@ uint32_t axpDebugRead()
|
|||||||
return 30 * 1000;
|
return 30 * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
concurrency::Periodic axpDebugOutput(axpDebugRead);
|
Periodic axpDebugOutput(axpDebugRead);
|
||||||
axpDebugOutput.setup();
|
axpDebugOutput.setup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
uint32_t msecstosleep = 1000 * 30; // How long can we sleep before we again need to service the main loop?
|
|
||||||
|
|
||||||
if (gps)
|
|
||||||
gps->loop(); // FIXME, remove from main, instead block on read
|
|
||||||
router.loop();
|
|
||||||
powerFSM.run_machine();
|
|
||||||
service.loop();
|
|
||||||
|
|
||||||
concurrency::periodicScheduler.loop();
|
|
||||||
// axpDebugOutput.loop();
|
// axpDebugOutput.loop();
|
||||||
|
|
||||||
#ifdef DEBUG_PORT
|
#ifdef DEBUG_PORT
|
||||||
@@ -392,25 +439,9 @@ void loop()
|
|||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
esp32Loop();
|
esp32Loop();
|
||||||
#endif
|
#endif
|
||||||
#ifdef TBEAM_V10
|
|
||||||
power->loop();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BUTTON_PIN
|
// For debugging
|
||||||
userButton.tick();
|
// if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();
|
||||||
#endif
|
|
||||||
#ifdef BUTTON_PIN_ALT
|
|
||||||
userButtonAlt.tick();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
loopWifi();
|
|
||||||
|
|
||||||
// Show boot screen for first 3 seconds, then switch to normal operation.
|
|
||||||
static bool showingBootScreen = true;
|
|
||||||
if (showingBootScreen && (millis() > 3000)) {
|
|
||||||
screen.stopBootScreen();
|
|
||||||
showingBootScreen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_STACK
|
#ifdef DEBUG_STACK
|
||||||
static uint32_t lastPrint = 0;
|
static uint32_t lastPrint = 0;
|
||||||
@@ -420,19 +451,18 @@ void loop()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Update the screen last, after we've figured out what to show.
|
|
||||||
screen.debug_info()->setChannelNameStatus(getChannelName());
|
|
||||||
|
|
||||||
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
|
|
||||||
// i.e. don't just keep spinning in loop as fast as we can.
|
|
||||||
// DEBUG_MSG("msecs %d\n", msecstosleep);
|
|
||||||
|
|
||||||
// FIXME - until button press handling is done by interrupt (see polling above) we can't sleep very long at all or buttons
|
|
||||||
// feel slow
|
|
||||||
msecstosleep = 10; // FIXME, stop early if something happens and sleep much longer
|
|
||||||
|
|
||||||
// TODO: This should go into a thread handled by FreeRTOS.
|
// TODO: This should go into a thread handled by FreeRTOS.
|
||||||
handleWebResponse();
|
handleWebResponse();
|
||||||
|
|
||||||
delay(msecstosleep);
|
service.loop();
|
||||||
|
|
||||||
|
long delayMsec = mainController.runOrDelay();
|
||||||
|
|
||||||
|
/* if (mainController.nextThread && delayMsec)
|
||||||
|
DEBUG_MSG("Next %s in %ld\n", mainController.nextThread->ThreadName.c_str(),
|
||||||
|
mainController.nextThread->tillRun(millis())); */
|
||||||
|
|
||||||
|
// We want to sleep as long as possible here - because it saves power
|
||||||
|
mainDelay.delay(delayMsec);
|
||||||
|
// if (didWake) DEBUG_MSG("wake!\n");
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/main.h
18
src/main.h
@@ -1,28 +1,24 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "graphics/Screen.h"
|
|
||||||
#include "PowerStatus.h"
|
|
||||||
#include "GPSStatus.h"
|
#include "GPSStatus.h"
|
||||||
#include "NodeStatus.h"
|
#include "NodeStatus.h"
|
||||||
|
#include "PowerStatus.h"
|
||||||
|
#include "graphics/Screen.h"
|
||||||
|
|
||||||
extern bool axp192_found;
|
extern bool axp192_found;
|
||||||
extern bool ssd1306_found;
|
extern bool ssd1306_found;
|
||||||
extern bool isCharging;
|
extern bool isCharging;
|
||||||
extern bool isUSBPowered;
|
extern bool isUSBPowered;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Global Screen singleton.
|
// Global Screen singleton.
|
||||||
extern graphics::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;
|
||||||
//extern meshtastic::GPSStatus *gpsStatus;
|
// extern meshtastic::GPSStatus *gpsStatus;
|
||||||
//extern meshtastic::NodeStatusHandler *nodeStatusHandler;
|
// extern meshtastic::NodeStatusHandler *nodeStatusHandler;
|
||||||
|
|
||||||
// Return a human readable string of the form "Meshtastic_ab13"
|
// Return a human readable string of the form "Meshtastic_ab13"
|
||||||
const char *getDeviceName();
|
const char *getDeviceName();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop();
|
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PacketHistory.h"
|
#include "PacketHistory.h"
|
||||||
#include "../concurrency/PeriodicTask.h"
|
|
||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
|
#include "RTC.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
@@ -48,14 +49,14 @@ MeshService service;
|
|||||||
|
|
||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
|
|
||||||
static uint32_t sendOwnerCb()
|
static int32_t sendOwnerCb()
|
||||||
{
|
{
|
||||||
service.sendOurOwner();
|
service.sendOurOwner();
|
||||||
|
|
||||||
return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000;
|
return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static concurrency::Periodic sendOwnerPeriod(sendOwnerCb);
|
static concurrency::Periodic *sendOwnerPeriod;
|
||||||
|
|
||||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||||
{
|
{
|
||||||
@@ -64,17 +65,18 @@ MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
|||||||
|
|
||||||
void MeshService::init()
|
void MeshService::init()
|
||||||
{
|
{
|
||||||
sendOwnerPeriod.setup();
|
sendOwnerPeriod = new concurrency::Periodic("SendOwner", sendOwnerCb);
|
||||||
|
|
||||||
nodeDB.init();
|
nodeDB.init();
|
||||||
|
|
||||||
if (gps)
|
if (gps)
|
||||||
gpsObserver.observe(&gps->newStatus);
|
gpsObserver.observe(&gps->newStatus);
|
||||||
packetReceivedObserver.observe(&router.notifyPacketReceived);
|
packetReceivedObserver.observe(&router->notifyPacketReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshService::sendOurOwner(NodeNum dest, bool wantReplies)
|
void MeshService::sendOurOwner(NodeNum dest, bool wantReplies)
|
||||||
{
|
{
|
||||||
MeshPacket *p = router.allocForSending();
|
MeshPacket *p = router->allocForSending();
|
||||||
p->to = dest;
|
p->to = dest;
|
||||||
p->decoded.want_response = wantReplies;
|
p->decoded.want_response = wantReplies;
|
||||||
p->decoded.which_payload = SubPacket_user_tag;
|
p->decoded.which_payload = SubPacket_user_tag;
|
||||||
@@ -121,7 +123,7 @@ const MeshPacket *MeshService::handleFromRadioUser(const MeshPacket *mp)
|
|||||||
sendOurOwner(mp->from);
|
sendOurOwner(mp->from);
|
||||||
|
|
||||||
String lcd = String("Joined: ") + mp->decoded.user.long_name + "\n";
|
String lcd = String("Joined: ") + mp->decoded.user.long_name + "\n";
|
||||||
screen.print(lcd.c_str());
|
screen->print(lcd.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return mp;
|
return mp;
|
||||||
@@ -139,7 +141,7 @@ void MeshService::handleIncomingPosition(const MeshPacket *mp)
|
|||||||
tv.tv_sec = secs;
|
tv.tv_sec = secs;
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
perhapsSetRTC(&tv);
|
perhapsSetRTC(RTCQualityFromNet, &tv);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("Ignoring incoming packet - not a position\n");
|
DEBUG_MSG("Ignoring incoming packet - not a position\n");
|
||||||
@@ -150,12 +152,8 @@ int MeshService::handleFromRadio(const MeshPacket *mp)
|
|||||||
{
|
{
|
||||||
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
|
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
|
||||||
|
|
||||||
// If it is a position packet, perhaps set our clock (if we don't have a GPS of our own, otherwise wait for that to work)
|
// If it is a position packet, perhaps set our clock - this must be before nodeDB.updateFrom
|
||||||
if (!gps->isConnected)
|
handleIncomingPosition(mp);
|
||||||
handleIncomingPosition(mp);
|
|
||||||
else {
|
|
||||||
DEBUG_MSG("Ignoring incoming time, because we have a GPS\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mp->which_payload == MeshPacket_decoded_tag && mp->decoded.which_payload == SubPacket_user_tag) {
|
if (mp->which_payload == MeshPacket_decoded_tag && mp->decoded.which_payload == SubPacket_user_tag) {
|
||||||
mp = handleFromRadioUser(mp);
|
mp = handleFromRadioUser(mp);
|
||||||
@@ -229,8 +227,8 @@ void MeshService::handleToRadio(MeshPacket &p)
|
|||||||
if (p.id == 0)
|
if (p.id == 0)
|
||||||
p.id = generatePacketId(); // If the phone didn't supply one, then pick one
|
p.id = generatePacketId(); // If the phone didn't supply one, then pick one
|
||||||
|
|
||||||
p.rx_time = getValidTime(); // Record the time the packet arrived from the phone
|
p.rx_time = getValidTime(RTCQualityFromNet); // Record the time the packet arrived from the phone
|
||||||
// (so we update our nodedb for the local node)
|
// (so we update our nodedb for the local node)
|
||||||
|
|
||||||
// Send the packet into the mesh
|
// Send the packet into the mesh
|
||||||
|
|
||||||
@@ -250,10 +248,10 @@ void MeshService::sendToMesh(MeshPacket *p)
|
|||||||
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
|
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
|
||||||
|
|
||||||
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
|
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
|
||||||
// nodes shouldn't trust it anyways) Note: for now, we allow a device with a local GPS to include the time, so that gpsless
|
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
|
||||||
// devices can get time.
|
// devices can get time.
|
||||||
if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag) {
|
if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag) {
|
||||||
if (!gps->isConnected) {
|
if (getRTCQuality() < RTCQualityGPS) {
|
||||||
DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time);
|
DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time);
|
||||||
p->decoded.position.time = 0;
|
p->decoded.position.time = 0;
|
||||||
} else
|
} else
|
||||||
@@ -261,7 +259,7 @@ void MeshService::sendToMesh(MeshPacket *p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
|
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
|
||||||
router.sendLocal(p);
|
router->sendLocal(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
|
void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
|
||||||
@@ -283,12 +281,13 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
|
|||||||
assert(node->has_position);
|
assert(node->has_position);
|
||||||
|
|
||||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||||
MeshPacket *p = router.allocForSending();
|
MeshPacket *p = router->allocForSending();
|
||||||
p->to = dest;
|
p->to = dest;
|
||||||
p->decoded.which_payload = SubPacket_position_tag;
|
p->decoded.which_payload = SubPacket_position_tag;
|
||||||
p->decoded.position = node->position;
|
p->decoded.position = node->position;
|
||||||
p->decoded.want_response = wantReplies;
|
p->decoded.want_response = wantReplies;
|
||||||
p->decoded.position.time = getValidTime(); // This nodedb timestamp might be stale, so update it if our clock is valid.
|
p->decoded.position.time =
|
||||||
|
getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid.
|
||||||
sendToMesh(p);
|
sendToMesh(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +295,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
|
|||||||
{
|
{
|
||||||
|
|
||||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||||
MeshPacket *p = router.allocForSending();
|
MeshPacket *p = router->allocForSending();
|
||||||
p->decoded.which_payload = SubPacket_position_tag;
|
p->decoded.which_payload = SubPacket_position_tag;
|
||||||
|
|
||||||
Position &pos = p->decoded.position;
|
Position &pos = p->decoded.position;
|
||||||
@@ -306,9 +305,10 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
|
|||||||
pos.altitude = gps->altitude;
|
pos.altitude = gps->altitude;
|
||||||
pos.latitude_i = gps->latitude;
|
pos.latitude_i = gps->latitude;
|
||||||
pos.longitude_i = gps->longitude;
|
pos.longitude_i = gps->longitude;
|
||||||
pos.time = getValidTime();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos.time = getValidTime(RTCQualityGPS);
|
||||||
|
|
||||||
// Include our current battery voltage in our position announcement
|
// Include our current battery voltage in our position announcement
|
||||||
pos.battery_level = powerStatus->getBatteryChargePercent();
|
pos.battery_level = powerStatus->getBatteryChargePercent();
|
||||||
updateBatteryLevel(pos.battery_level);
|
updateBatteryLevel(pos.battery_level);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "PacketHistory.h"
|
#include "PacketHistory.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
|
#include "RTC.h"
|
||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
@@ -414,10 +415,10 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
|
|
||||||
switch (p.which_payload) {
|
switch (p.which_payload) {
|
||||||
case SubPacket_position_tag: {
|
case SubPacket_position_tag: {
|
||||||
// we carefully preserve the old time, because we always trust our local timestamps more
|
// we always trust our local timestamps more
|
||||||
uint32_t oldtime = info->position.time;
|
|
||||||
info->position = p.position;
|
info->position = p.position;
|
||||||
info->position.time = oldtime;
|
if (mp.rx_time)
|
||||||
|
info->position.time = mp.rx_time;
|
||||||
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
|
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||||
|
|||||||
@@ -144,11 +144,18 @@ const char *getChannelName();
|
|||||||
|
|
||||||
PREF_GET(send_owner_interval, 4)
|
PREF_GET(send_owner_interval, 4)
|
||||||
PREF_GET(position_broadcast_secs, 15 * 60)
|
PREF_GET(position_broadcast_secs, 15 * 60)
|
||||||
PREF_GET(wait_bluetooth_secs, 120)
|
|
||||||
|
// Each time we wake into the DARK state allow 1 minute to send and receive BLE packets to the phone
|
||||||
|
PREF_GET(wait_bluetooth_secs, 60)
|
||||||
|
|
||||||
PREF_GET(screen_on_secs, 60)
|
PREF_GET(screen_on_secs, 60)
|
||||||
PREF_GET(mesh_sds_timeout_secs, 2 * 60 * 60)
|
PREF_GET(mesh_sds_timeout_secs, 2 * 60 * 60)
|
||||||
PREF_GET(phone_sds_timeout_sec, 2 * 60 * 60)
|
PREF_GET(phone_sds_timeout_sec, 2 * 60 * 60)
|
||||||
PREF_GET(sds_secs, 365 * 24 * 60 * 60)
|
PREF_GET(sds_secs, 365 * 24 * 60 * 60)
|
||||||
PREF_GET(ls_secs, 60 * 60)
|
|
||||||
|
// We default to sleeping (with bluetooth off for 5 minutes at a time). This seems to be a good tradeoff between
|
||||||
|
// latency for the user sending messages and power savings because of not having to run (expensive) ESP32 bluetooth
|
||||||
|
PREF_GET(ls_secs, 5 * 60)
|
||||||
|
|
||||||
PREF_GET(phone_timeout_secs, 15 * 60)
|
PREF_GET(phone_timeout_secs, 15 * 60)
|
||||||
PREF_GET(min_wake_secs, 10)
|
PREF_GET(min_wake_secs, 10)
|
||||||
|
|||||||
@@ -109,7 +109,11 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_SEND_MY_INFO:
|
case STATE_SEND_MY_INFO:
|
||||||
myNodeInfo.has_gps = gps && gps->isConnected; // Update with latest GPS connect info
|
// If the user has specified they don't want our node to share its location, make sure to tell the phone
|
||||||
|
// app not to send locations on our behalf.
|
||||||
|
myNodeInfo.has_gps = (radioConfig.preferences.location_share == LocationSharing_LocDisabled)
|
||||||
|
? true
|
||||||
|
: (gps && gps->isConnected()); // Update with latest GPS connect info
|
||||||
fromRadioScratch.which_variant = FromRadio_my_info_tag;
|
fromRadioScratch.which_variant = FromRadio_my_info_tag;
|
||||||
fromRadioScratch.variant.my_info = myNodeInfo;
|
fromRadioScratch.variant.my_info = myNodeInfo;
|
||||||
state = STATE_SEND_RADIO;
|
state = STATE_SEND_RADIO;
|
||||||
@@ -117,7 +121,14 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
|
|
||||||
case STATE_SEND_RADIO:
|
case STATE_SEND_RADIO:
|
||||||
fromRadioScratch.which_variant = FromRadio_radio_tag;
|
fromRadioScratch.which_variant = FromRadio_radio_tag;
|
||||||
|
|
||||||
fromRadioScratch.variant.radio = radioConfig;
|
fromRadioScratch.variant.radio = radioConfig;
|
||||||
|
|
||||||
|
// NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior.
|
||||||
|
// So even if we internally use 0 to represent 'use default' we still need to send the value we are
|
||||||
|
// using to the app (so that even old phone apps work with new device loads).
|
||||||
|
fromRadioScratch.variant.radio.preferences.ls_secs = getPref_ls_secs();
|
||||||
|
|
||||||
state = STATE_SEND_NODEINFO;
|
state = STATE_SEND_NODEINFO;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#define MAX_POWER 20
|
#define MAX_POWER 20
|
||||||
// if we use 20 we are limited to 1% duty cycle or hw might overheat. For continuous operation set a limit of 17
|
// if we use 20 we are limited to 1% duty cycle or hw might overheat. For continuous operation set a limit of 17
|
||||||
|
// In theory up to 27 dBm is possible, but the modules installed in most radios can cope with a max of 20. So BIG WARNING
|
||||||
|
// if you set power to something higher than 17 or 20 you might fry your board.
|
||||||
|
|
||||||
#define POWER_DEFAULT 17 // How much power to use if the user hasn't set a power level
|
#define POWER_DEFAULT 17 // How much power to use if the user hasn't set a power level
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
|||||||
DEBUG_MSG(")\n");
|
DEBUG_MSG(")\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioInterface::RadioInterface()
|
RadioInterface::RadioInterface()
|
||||||
{
|
{
|
||||||
assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
|
assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
|
||||||
|
|
||||||
@@ -120,10 +120,7 @@ bool RadioInterface::init()
|
|||||||
// we now expect interfaces to operate in promiscous mode
|
// we now expect interfaces to operate in promiscous mode
|
||||||
// radioIf.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor
|
// radioIf.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor
|
||||||
// time.
|
// time.
|
||||||
|
|
||||||
// we want this thread to run at very high priority, because it is effectively running as a user space ISR
|
|
||||||
start("radio", RADIO_STACK_SIZE, configMAX_PRIORITIES - 1); // Start our worker thread
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ typedef struct {
|
|||||||
*
|
*
|
||||||
* 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 concurrency::NotifiedWorkerThread
|
class RadioInterface
|
||||||
{
|
{
|
||||||
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;
|
||||||
@@ -72,6 +72,8 @@ class RadioInterface : protected concurrency::NotifiedWorkerThread
|
|||||||
*/
|
*/
|
||||||
RadioInterface();
|
RadioInterface();
|
||||||
|
|
||||||
|
virtual ~RadioInterface() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set where to deliver received packets. This method should only be used by the Router class
|
* Set where to deliver received packets. This method should only be used by the Router class
|
||||||
*/
|
*/
|
||||||
@@ -117,8 +119,6 @@ class RadioInterface : protected concurrency::NotifiedWorkerThread
|
|||||||
*/
|
*/
|
||||||
size_t beginSending(MeshPacket *p);
|
size_t beginSending(MeshPacket *p);
|
||||||
|
|
||||||
virtual void loop() {} // Idle processing
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some regulatory regions limit xmit power.
|
* Some regulatory regions limit xmit power.
|
||||||
* This function should be called by subclasses after setting their desired power. It might lower it
|
* This function should be called by subclasses after setting their desired power. It might lower it
|
||||||
|
|||||||
@@ -19,17 +19,11 @@ void LockingModule::SPItransfer(uint8_t cmd, uint8_t reg, uint8_t *dataOut, uint
|
|||||||
|
|
||||||
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)
|
||||||
: concurrency::PeriodicTask(0), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
|
: NotifiedWorkerThread("RadioIf"), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
|
||||||
{
|
{
|
||||||
instance = this;
|
instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RadioLibInterface::init()
|
|
||||||
{
|
|
||||||
setup(); // init our timer
|
|
||||||
return RadioInterface::init();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
// ESP32 doesn't use that flag
|
// ESP32 doesn't use that flag
|
||||||
#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR()
|
#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR()
|
||||||
@@ -41,9 +35,8 @@ void INTERRUPT_ATTR RadioLibInterface::isrLevel0Common(PendingISR cause)
|
|||||||
{
|
{
|
||||||
instance->disableInterrupt();
|
instance->disableInterrupt();
|
||||||
|
|
||||||
instance->pending = cause;
|
|
||||||
BaseType_t xHigherPriorityTaskWoken;
|
BaseType_t xHigherPriorityTaskWoken;
|
||||||
instance->notifyFromISR(&xHigherPriorityTaskWoken, cause, eSetValueWithOverwrite);
|
instance->notifyFromISR(&xHigherPriorityTaskWoken, cause, true);
|
||||||
|
|
||||||
/* Force a context switch if xHigherPriorityTaskWoken is now set to pdTRUE.
|
/* Force a context switch if xHigherPriorityTaskWoken is now set to pdTRUE.
|
||||||
The macro used to do this is dependent on the port and may be called
|
The macro used to do this is dependent on the port and may be called
|
||||||
@@ -191,10 +184,8 @@ transmitters that we are potentially stomping on. Requires further thought.
|
|||||||
|
|
||||||
FIXME, the MIN_TX_WAIT_MSEC and MAX_TX_WAIT_MSEC values should be tuned via logic analyzer later.
|
FIXME, the MIN_TX_WAIT_MSEC and MAX_TX_WAIT_MSEC values should be tuned via logic analyzer later.
|
||||||
*/
|
*/
|
||||||
void RadioLibInterface::loop()
|
void RadioLibInterface::onNotify(uint32_t notification)
|
||||||
{
|
{
|
||||||
pending = ISR_NONE;
|
|
||||||
|
|
||||||
switch (notification) {
|
switch (notification) {
|
||||||
case ISR_TX:
|
case ISR_TX:
|
||||||
handleTransmitInterrupt();
|
handleTransmitInterrupt();
|
||||||
@@ -209,6 +200,8 @@ void RadioLibInterface::loop()
|
|||||||
startTransmitTimer();
|
startTransmitTimer();
|
||||||
break;
|
break;
|
||||||
case TRANSMIT_DELAY_COMPLETED:
|
case TRANSMIT_DELAY_COMPLETED:
|
||||||
|
// DEBUG_MSG("delay done\n");
|
||||||
|
|
||||||
// If we are not currently in receive mode, then restart the timer and try again later (this can happen if the main thread
|
// If we are not currently in receive mode, then restart the timer and try again later (this can happen if the main thread
|
||||||
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
|
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
|
||||||
if (!txQueue.isEmpty()) {
|
if (!txQueue.isEmpty()) {
|
||||||
@@ -229,25 +222,14 @@ void RadioLibInterface::loop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioLibInterface::doTask()
|
|
||||||
{
|
|
||||||
disable(); // Don't call this callback again
|
|
||||||
|
|
||||||
// We use without overwrite, so that if there is already an interrupt pending to be handled, that gets handle properly (the
|
|
||||||
// ISR handler will restart our timer)
|
|
||||||
|
|
||||||
notify(TRANSMIT_DELAY_COMPLETED, eSetValueWithoutOverwrite);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RadioLibInterface::startTransmitTimer(bool withDelay)
|
void RadioLibInterface::startTransmitTimer(bool withDelay)
|
||||||
{
|
{
|
||||||
// If we have work to do and the timer wasn't already scheduled, schedule it now
|
// If we have work to do and the timer wasn't already scheduled, schedule it now
|
||||||
if (getPeriod() == 0 && !txQueue.isEmpty()) {
|
if (!txQueue.isEmpty()) {
|
||||||
uint32_t delay =
|
uint32_t delay =
|
||||||
!withDelay ? 1 : random(MIN_TX_WAIT_MSEC, MAX_TX_WAIT_MSEC); // See documentation for loop() wrt these values
|
!withDelay ? 1 : random(MIN_TX_WAIT_MSEC, MAX_TX_WAIT_MSEC); // See documentation for loop() wrt these values
|
||||||
// DEBUG_MSG("xmit timer %d\n", delay);
|
// DEBUG_MSG("xmit timer %d\n", delay);
|
||||||
// DEBUG_MSG("delaying %u\n", delay);
|
notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable
|
||||||
setPeriod(delay);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../concurrency/PeriodicTask.h"
|
#include "../concurrency/OSThread.h"
|
||||||
#include "RadioInterface.h"
|
#include "RadioInterface.h"
|
||||||
|
|
||||||
#ifdef CubeCell_BoardPlus
|
#ifdef CubeCell_BoardPlus
|
||||||
@@ -59,13 +59,11 @@ class LockingModule : public Module
|
|||||||
virtual void SPItransfer(uint8_t cmd, uint8_t reg, uint8_t *dataOut, uint8_t *dataIn, uint8_t numBytes);
|
virtual void SPItransfer(uint8_t cmd, uint8_t reg, uint8_t *dataOut, uint8_t *dataIn, uint8_t numBytes);
|
||||||
};
|
};
|
||||||
|
|
||||||
class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTask
|
class RadioLibInterface : public RadioInterface, protected concurrency::NotifiedWorkerThread
|
||||||
{
|
{
|
||||||
/// 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 };
|
||||||
|
|
||||||
volatile PendingISR pending = ISR_NONE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw ISR handler that just calls our polymorphic method
|
* Raw ISR handler that just calls our polymorphic method
|
||||||
*/
|
*/
|
||||||
@@ -137,6 +135,11 @@ class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTa
|
|||||||
*/
|
*/
|
||||||
virtual void startReceive() = 0;
|
virtual void startReceive() = 0;
|
||||||
|
|
||||||
|
/** are we actively receiving a packet (only called during receiving state)
|
||||||
|
* This method is only public to facilitate debugging. Do not call.
|
||||||
|
*/
|
||||||
|
virtual bool isActivelyReceiving() = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** if we have something waiting to send, start a short random timer so we can come check for collision before actually doing
|
/** if we have something waiting to send, start a short random timer so we can come check for collision before actually doing
|
||||||
* the transmit
|
* the transmit
|
||||||
@@ -150,7 +153,7 @@ class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTa
|
|||||||
|
|
||||||
static void timerCallback(void *p1, uint32_t p2);
|
static void timerCallback(void *p1, uint32_t p2);
|
||||||
|
|
||||||
virtual void doTask();
|
virtual void onNotify(uint32_t notification);
|
||||||
|
|
||||||
/** start an immediate transmit
|
/** start an immediate transmit
|
||||||
* This method is virtual so subclasses can hook as needed, subclasses should not call directly
|
* This method is virtual so subclasses can hook as needed, subclasses should not call directly
|
||||||
@@ -158,10 +161,6 @@ class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTa
|
|||||||
virtual void startSend(MeshPacket *txp);
|
virtual void startSend(MeshPacket *txp);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Initialise the Driver transport hardware and software.
|
|
||||||
/// Make sure the Driver is properly configured before calling init().
|
|
||||||
/// \return true if initialisation succeeded.
|
|
||||||
virtual bool init();
|
|
||||||
|
|
||||||
/** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */
|
/** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */
|
||||||
virtual void configHardwareForSend() {}
|
virtual void configHardwareForSend() {}
|
||||||
@@ -176,9 +175,6 @@ class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTa
|
|||||||
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
||||||
virtual bool canSendImmediately();
|
virtual bool canSendImmediately();
|
||||||
|
|
||||||
/** are we actively receiving a packet (only called during receiving state) */
|
|
||||||
virtual bool isActivelyReceiving() = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw ISR handler that just calls our polymorphic method
|
* Raw ISR handler that just calls our polymorphic method
|
||||||
*/
|
*/
|
||||||
@@ -193,7 +189,5 @@ class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTa
|
|||||||
*/
|
*/
|
||||||
virtual void addReceiveMetadata(MeshPacket *mp) = 0;
|
virtual void addReceiveMetadata(MeshPacket *mp) = 0;
|
||||||
|
|
||||||
virtual void loop(); // Idle processing
|
|
||||||
|
|
||||||
virtual void setStandby() = 0;
|
virtual void setStandby() = 0;
|
||||||
};
|
};
|
||||||
@@ -160,9 +160,10 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
|
|||||||
/**
|
/**
|
||||||
* Do any retransmissions that are scheduled (FIXME - for the time being called from loop)
|
* Do any retransmissions that are scheduled (FIXME - for the time being called from loop)
|
||||||
*/
|
*/
|
||||||
void ReliableRouter::doRetransmissions()
|
int32_t ReliableRouter::doRetransmissions()
|
||||||
{
|
{
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
|
int32_t d = INT32_MAX;
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -192,5 +193,13 @@ void ReliableRouter::doRetransmissions()
|
|||||||
p.setNextTx();
|
p.setNextTx();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// Not yet time
|
||||||
|
int32_t t = p.nextTxMsec - now;
|
||||||
|
|
||||||
|
d = min(t, d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return d;
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "FloodingRouter.h"
|
#include "FloodingRouter.h"
|
||||||
#include "../concurrency/PeriodicTask.h"
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,10 +79,13 @@ class ReliableRouter : public FloodingRouter
|
|||||||
virtual ErrorCode send(MeshPacket *p);
|
virtual ErrorCode send(MeshPacket *p);
|
||||||
|
|
||||||
/** Do our retransmission handling */
|
/** Do our retransmission handling */
|
||||||
virtual void loop()
|
virtual int32_t runOnce()
|
||||||
{
|
{
|
||||||
doRetransmissions();
|
auto d = FloodingRouter::runOnce();
|
||||||
FloodingRouter::loop();
|
|
||||||
|
int32_t r = doRetransmissions();
|
||||||
|
|
||||||
|
return min(d, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -124,6 +126,8 @@ class ReliableRouter : public FloodingRouter
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Do any retransmissions that are scheduled (FIXME - for the time being called from loop)
|
* Do any retransmissions that are scheduled (FIXME - for the time being called from loop)
|
||||||
|
*
|
||||||
|
* @return the number of msecs until our next retransmission or MAXINT if none scheduled
|
||||||
*/
|
*/
|
||||||
void doRetransmissions();
|
int32_t doRetransmissions();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
#include "CryptoEngine.h"
|
#include "CryptoEngine.h"
|
||||||
#include "GPS.h"
|
#include "RTC.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include <NodeDB.h>
|
#include <NodeDB.h>
|
||||||
@@ -34,25 +34,29 @@ Allocator<MeshPacket> &packetPool = staticPool;
|
|||||||
*
|
*
|
||||||
* Currently we only allow one interface, that may change in the future
|
* Currently we only allow one interface, that may change in the future
|
||||||
*/
|
*/
|
||||||
Router::Router() : fromRadioQueue(MAX_RX_FROMRADIO)
|
Router::Router() : concurrency::OSThread("Router"), fromRadioQueue(MAX_RX_FROMRADIO)
|
||||||
{
|
{
|
||||||
// This is called pre main(), don't touch anything here, the following code is not safe
|
// This is called pre main(), don't touch anything here, the following code is not safe
|
||||||
|
|
||||||
/* DEBUG_MSG("Size of NodeInfo %d\n", sizeof(NodeInfo));
|
/* DEBUG_MSG("Size of NodeInfo %d\n", sizeof(NodeInfo));
|
||||||
DEBUG_MSG("Size of SubPacket %d\n", sizeof(SubPacket));
|
DEBUG_MSG("Size of SubPacket %d\n", sizeof(SubPacket));
|
||||||
DEBUG_MSG("Size of MeshPacket %d\n", sizeof(MeshPacket)); */
|
DEBUG_MSG("Size of MeshPacket %d\n", sizeof(MeshPacket)); */
|
||||||
|
|
||||||
|
fromRadioQueue.setReader(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* do idle processing
|
* do idle processing
|
||||||
* Mostly looking in our incoming rxPacket queue and calling handleReceived.
|
* Mostly looking in our incoming rxPacket queue and calling handleReceived.
|
||||||
*/
|
*/
|
||||||
void Router::loop()
|
int32_t Router::runOnce()
|
||||||
{
|
{
|
||||||
MeshPacket *mp;
|
MeshPacket *mp;
|
||||||
while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) {
|
while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) {
|
||||||
perhapsHandleReceived(mp);
|
perhapsHandleReceived(mp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return INT32_MAX; // Wait a long time - until we get woken for the message queue
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a unique packet id
|
/// Generate a unique packet id
|
||||||
@@ -89,7 +93,7 @@ MeshPacket *Router::allocForSending()
|
|||||||
p->to = NODENUM_BROADCAST;
|
p->to = NODENUM_BROADCAST;
|
||||||
p->hop_limit = HOP_RELIABLE;
|
p->hop_limit = HOP_RELIABLE;
|
||||||
p->id = generatePacketId();
|
p->id = generatePacketId();
|
||||||
p->rx_time = getValidTime(); // Just in case we process the packet locally - make sure it has a valid timestamp
|
p->rx_time = getValidTime(RTCQualityFromNet); // Just in case we process the packet locally - make sure it has a valid timestamp
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@@ -198,9 +202,8 @@ NodeNum Router::getNodeNum()
|
|||||||
*/
|
*/
|
||||||
void Router::handleReceived(MeshPacket *p)
|
void Router::handleReceived(MeshPacket *p)
|
||||||
{
|
{
|
||||||
// FIXME, this class shouldn't EVER need to know about the GPS, move getValidTime() into a non gps dependent function
|
|
||||||
// Also, we should set the time from the ISR and it should have msec level resolution
|
// Also, we should set the time from the ISR and it should have msec level resolution
|
||||||
p->rx_time = getValidTime(); // store the arrival timestamp for the phone
|
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
||||||
|
|
||||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||||
if (perhapsDecode(p)) {
|
if (perhapsDecode(p)) {
|
||||||
|
|||||||
@@ -5,12 +5,13 @@
|
|||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "PointerQueue.h"
|
#include "PointerQueue.h"
|
||||||
#include "RadioInterface.h"
|
#include "RadioInterface.h"
|
||||||
|
#include "concurrency/OSThread.h"
|
||||||
#include "mesh.pb.h"
|
#include "mesh.pb.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mesh aware router that supports multiple interfaces.
|
* A mesh aware router that supports multiple interfaces.
|
||||||
*/
|
*/
|
||||||
class Router
|
class Router : protected concurrency::OSThread
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
RadioInterface *iface;
|
RadioInterface *iface;
|
||||||
@@ -44,7 +45,7 @@ class Router
|
|||||||
* do idle processing
|
* do idle processing
|
||||||
* Mostly looking in our incoming rxPacket queue and calling handleReceived.
|
* Mostly looking in our incoming rxPacket queue and calling handleReceived.
|
||||||
*/
|
*/
|
||||||
virtual void loop();
|
virtual int32_t runOnce();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Works like send, but if we are sending to the local node, we directly put the message in the receive queue
|
* Works like send, but if we are sending to the local node, we directly put the message in the receive queue
|
||||||
@@ -113,7 +114,7 @@ class Router
|
|||||||
void handleReceived(MeshPacket *p);
|
void handleReceived(MeshPacket *p);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Router &router;
|
extern Router *router;
|
||||||
|
|
||||||
/// Generate a unique packet id
|
/// Generate a unique packet id
|
||||||
// FIXME, move this someplace better
|
// FIXME, move this someplace better
|
||||||
|
|||||||
@@ -179,16 +179,18 @@ void SX1262Interface::startReceive()
|
|||||||
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||||
bool SX1262Interface::isActivelyReceiving()
|
bool SX1262Interface::isActivelyReceiving()
|
||||||
{
|
{
|
||||||
// The IRQ status will be cleared when we start our read operation. Check if we've started a preamble, but haven't yet
|
// The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
|
||||||
// received and handled the interrupt for reading the packet/handling errors.
|
// received and handled the interrupt for reading the packet/handling errors.
|
||||||
|
// FIXME: it would be better to check for preamble, but we currently have our ISR not set to fire for packets that
|
||||||
|
// never even get a valid header, so we don't want preamble to get set and stay set due to noise on the network.
|
||||||
|
|
||||||
uint16_t irq = lora.getIrqStatus();
|
uint16_t irq = lora.getIrqStatus();
|
||||||
bool hasPreamble = (irq & SX126X_IRQ_PREAMBLE_DETECTED);
|
bool hasPreamble = (irq & SX126X_IRQ_HEADER_VALID);
|
||||||
|
|
||||||
// this is not correct - often always true - need to add an extra conditional
|
// this is not correct - often always true - need to add an extra conditional
|
||||||
// size_t bytesPending = lora.getPacketLength();
|
// size_t bytesPending = lora.getPacketLength();
|
||||||
|
|
||||||
// if (hasPreamble || bytesPending) DEBUG_MSG("rx hasPre %d, bytes %d\n", hasPreamble, bytesPending);
|
// if (hasPreamble) DEBUG_MSG("rx hasPreamble\n");
|
||||||
return hasPreamble;
|
return hasPreamble;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "concurrency/OSThread.h"
|
||||||
#include "freertosinc.h"
|
#include "freertosinc.h"
|
||||||
|
|
||||||
#ifdef HAS_FREE_RTOS
|
#ifdef HAS_FREE_RTOS
|
||||||
@@ -15,6 +16,7 @@ template <class T> class TypedQueue
|
|||||||
{
|
{
|
||||||
static_assert(std::is_pod<T>::value, "T must be pod");
|
static_assert(std::is_pod<T>::value, "T must be pod");
|
||||||
QueueHandle_t h;
|
QueueHandle_t h;
|
||||||
|
concurrency::OSThread *reader = NULL;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TypedQueue(int maxElements)
|
TypedQueue(int maxElements)
|
||||||
@@ -29,13 +31,35 @@ template <class T> class TypedQueue
|
|||||||
|
|
||||||
bool isEmpty() { return uxQueueMessagesWaiting(h) == 0; }
|
bool isEmpty() { return uxQueueMessagesWaiting(h) == 0; }
|
||||||
|
|
||||||
bool enqueue(T x, TickType_t maxWait = portMAX_DELAY) { return xQueueSendToBack(h, &x, maxWait) == pdTRUE; }
|
bool enqueue(T x, TickType_t maxWait = portMAX_DELAY)
|
||||||
|
{
|
||||||
|
if (reader) {
|
||||||
|
reader->setInterval(0);
|
||||||
|
concurrency::mainDelay.interrupt();
|
||||||
|
}
|
||||||
|
return xQueueSendToBack(h, &x, maxWait) == pdTRUE;
|
||||||
|
}
|
||||||
|
|
||||||
bool enqueueFromISR(T x, BaseType_t *higherPriWoken) { return xQueueSendToBackFromISR(h, &x, higherPriWoken) == pdTRUE; }
|
bool enqueueFromISR(T x, BaseType_t *higherPriWoken)
|
||||||
|
{
|
||||||
|
if (reader) {
|
||||||
|
reader->setInterval(0);
|
||||||
|
concurrency::mainDelay.interruptFromISR(higherPriWoken);
|
||||||
|
}
|
||||||
|
return xQueueSendToBackFromISR(h, &x, higherPriWoken) == pdTRUE;
|
||||||
|
}
|
||||||
|
|
||||||
bool dequeue(T *p, TickType_t maxWait = portMAX_DELAY) { return xQueueReceive(h, p, maxWait) == pdTRUE; }
|
bool dequeue(T *p, TickType_t maxWait = portMAX_DELAY) { return xQueueReceive(h, p, maxWait) == pdTRUE; }
|
||||||
|
|
||||||
bool dequeueFromISR(T *p, BaseType_t *higherPriWoken) { return xQueueReceiveFromISR(h, p, higherPriWoken); }
|
bool dequeueFromISR(T *p, BaseType_t *higherPriWoken) { return xQueueReceiveFromISR(h, p, higherPriWoken); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a thread that is reading from this queue
|
||||||
|
* If a message is pushed to this queue that thread will be scheduled to run ASAP.
|
||||||
|
*
|
||||||
|
* Note: thread will not be automatically enabled, just have its interval set to 0
|
||||||
|
*/
|
||||||
|
void setReader(concurrency::OSThread *t) { reader = t; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@@ -49,6 +73,7 @@ template <class T> class TypedQueue
|
|||||||
template <class T> class TypedQueue
|
template <class T> class TypedQueue
|
||||||
{
|
{
|
||||||
std::queue<T> q;
|
std::queue<T> q;
|
||||||
|
concurrency::OSThread *reader = NULL;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TypedQueue(int maxElements) {}
|
TypedQueue(int maxElements) {}
|
||||||
@@ -59,6 +84,11 @@ template <class T> class TypedQueue
|
|||||||
|
|
||||||
bool enqueue(T x, TickType_t maxWait = portMAX_DELAY)
|
bool enqueue(T x, TickType_t maxWait = portMAX_DELAY)
|
||||||
{
|
{
|
||||||
|
if (reader) {
|
||||||
|
reader->setInterval(0);
|
||||||
|
concurrency::mainDelay.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
q.push(x);
|
q.push(x);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -77,5 +107,7 @@ template <class T> class TypedQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
// bool dequeueFromISR(T *p, BaseType_t *higherPriWoken) { return xQueueReceiveFromISR(h, p, higherPriWoken); }
|
// bool dequeueFromISR(T *p, BaseType_t *higherPriWoken) { return xQueueReceiveFromISR(h, p, higherPriWoken); }
|
||||||
|
|
||||||
|
void setReader(concurrency::OSThread *t) { reader = t; }
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -121,13 +121,7 @@ void initWifi()
|
|||||||
DEBUG_MSG("Not using WIFI\n");
|
DEBUG_MSG("Not using WIFI\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform idle loop processing required by the wifi layer
|
|
||||||
void loopWifi()
|
|
||||||
{
|
|
||||||
// FIXME, once we have coroutines - just use a coroutine instead of this nasty loopWifi()
|
|
||||||
if (apiPort)
|
|
||||||
apiPort->loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void initApiServer()
|
static void initApiServer()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,9 +12,6 @@
|
|||||||
void initWifi();
|
void initWifi();
|
||||||
void deinitWifi();
|
void deinitWifi();
|
||||||
|
|
||||||
/// Perform idle loop processing required by the wifi layer
|
|
||||||
void loopWifi();
|
|
||||||
|
|
||||||
bool isWifiAvailable();
|
bool isWifiAvailable();
|
||||||
|
|
||||||
void handleDNSResponse();
|
void handleDNSResponse();
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
#include "NimbleBluetoothAPI.h"
|
#include "NimbleBluetoothAPI.h"
|
||||||
#include "PhoneAPI.h"
|
#include "PhoneAPI.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include <WiFi.h>
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "esp_bt.h"
|
#include "esp_bt.h"
|
||||||
#include "host/util/util.h"
|
#include "host/util/util.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "meshwifi/meshwifi.h"
|
||||||
#include "nimble/NimbleDefs.h"
|
#include "nimble/NimbleDefs.h"
|
||||||
#include "services/gap/ble_svc_gap.h"
|
#include "services/gap/ble_svc_gap.h"
|
||||||
#include "services/gatt/ble_svc_gatt.h"
|
#include "services/gatt/ble_svc_gatt.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "meshwifi/meshwifi.h"
|
#include <WiFi.h>
|
||||||
|
|
||||||
static bool pinShowing;
|
static bool pinShowing;
|
||||||
|
|
||||||
@@ -20,14 +20,14 @@ static void startCb(uint32_t pin)
|
|||||||
{
|
{
|
||||||
pinShowing = true;
|
pinShowing = true;
|
||||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||||
screen.startBluetoothPinScreen(pin);
|
screen->startBluetoothPinScreen(pin);
|
||||||
};
|
};
|
||||||
|
|
||||||
static void stopCb()
|
static void stopCb()
|
||||||
{
|
{
|
||||||
if (pinShowing) {
|
if (pinShowing) {
|
||||||
pinShowing = false;
|
pinShowing = false;
|
||||||
screen.stopBluetoothPinScreen();
|
screen->stopBluetoothPinScreen();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -533,7 +533,7 @@ void setBluetoothEnable(bool on)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// shutdown wifi
|
// shutdown wifi
|
||||||
deinitWifi();
|
deinitWifi();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "PowerStatus.h"
|
#include "PowerStatus.h"
|
||||||
#include "concurrency/PeriodicTask.h"
|
#include "concurrency/OSThread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per @spattinson
|
* Per @spattinson
|
||||||
@@ -15,16 +15,17 @@
|
|||||||
#define BAT_MILLIVOLTS_FULL 4100
|
#define BAT_MILLIVOLTS_FULL 4100
|
||||||
#define BAT_MILLIVOLTS_EMPTY 3500
|
#define BAT_MILLIVOLTS_EMPTY 3500
|
||||||
|
|
||||||
class Power : public concurrency::PeriodicTask
|
class Power : private concurrency::OSThread
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Observable<const meshtastic::PowerStatus *> newStatus;
|
Observable<const meshtastic::PowerStatus *> newStatus;
|
||||||
|
|
||||||
|
Power();
|
||||||
|
|
||||||
void readPowerStatus();
|
void readPowerStatus();
|
||||||
void loop();
|
|
||||||
virtual bool setup();
|
virtual bool setup();
|
||||||
virtual void doTask();
|
virtual int32_t runOnce();
|
||||||
void setStatusHandler(meshtastic::PowerStatus *handler) { statusHandler = handler; }
|
void setStatusHandler(meshtastic::PowerStatus *handler) { statusHandler = handler; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ void doDeepSleep(uint64_t msecToWake)
|
|||||||
notifySleep.notifyObservers(NULL); // also tell the regular sleep handlers
|
notifySleep.notifyObservers(NULL); // also tell the regular sleep handlers
|
||||||
notifyDeepSleep.notifyObservers(NULL);
|
notifyDeepSleep.notifyObservers(NULL);
|
||||||
|
|
||||||
screen.setOn(false); // datasheet says this will draw only 10ua
|
screen->setOn(false); // datasheet says this will draw only 10ua
|
||||||
|
|
||||||
nodeDB.saveToDisk();
|
nodeDB.saveToDisk();
|
||||||
|
|
||||||
@@ -170,17 +170,18 @@ void doDeepSleep(uint64_t msecToWake)
|
|||||||
|
|
||||||
#ifdef TBEAM_V10
|
#ifdef TBEAM_V10
|
||||||
if (axp192_found) {
|
if (axp192_found) {
|
||||||
|
// Obsolete comment: from back when we we used to receive lora packets while CPU was in deep sleep.
|
||||||
|
// We no longer do that, because our light-sleep current draws are low enough and it provides fast start/low cost
|
||||||
|
// wake. We currently use deep sleep only for 'we want our device to actually be off - because our battery is
|
||||||
|
// critically low'. So in deep sleep we DO shut down power to LORA (and when we boot later we completely reinit it)
|
||||||
|
//
|
||||||
// No need to turn this off if the power draw in sleep mode really is just 0.2uA and turning it off would
|
// No need to turn this off if the power draw in sleep mode really is just 0.2uA and turning it off would
|
||||||
// leave floating input for the IRQ line
|
// leave floating input for the IRQ line
|
||||||
|
|
||||||
// If we want to leave the radio receving in would be 11.5mA current draw, but most of the time it is just waiting
|
// If we want to leave the radio receving in would be 11.5mA current draw, but most of the time it is just waiting
|
||||||
// in its sequencer (true?) so the average power draw should be much lower even if we were listinging for packets
|
// in its sequencer (true?) so the average power draw should be much lower even if we were listinging for packets
|
||||||
// all the time.
|
// all the time.
|
||||||
|
|
||||||
// axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF); // LORA radio
|
axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF); // LORA radio
|
||||||
|
|
||||||
// now done by UBloxGPS.cpp
|
|
||||||
// setGPSPower(false);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user