Merge pull request #259 from grcasanova/concurrency

Threading refactored
1.2-legacy
Kevin Hester 2020-07-06 14:15:18 -07:00 zatwierdzone przez GitHub
commit 4388e72dec
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
52 zmienionych plików z 570 dodań i 501 usunięć

11
.gitignore vendored
Wyświetl plik

@ -7,4 +7,13 @@ main/credentials.h
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/extensions.json
*.code-workspace
*.code-workspace
.DS_Store
Thumbs.db
.autotools
.built
.context
.cproject
.idea/*
.vagrant

Wyświetl plik

@ -61,5 +61,6 @@
"ocrypto",
"protobufs",
"wifi"
]
],
"C_Cpp.dimInactiveRegions": true
}

17
.vscode/tasks.json vendored 100644
Wyświetl plik

@ -0,0 +1,17 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "PlatformIO",
"task": "Build",
"problemMatcher": [
"$platformio"
],
"group": {
"kind": "build",
"isDefault": true
},
"label": "PlatformIO: Build"
}
]
}

Wyświetl plik

@ -1,43 +0,0 @@
#include "PeriodicTask.h"
#include "Periodic.h"
PeriodicScheduler periodicScheduler;
PeriodicTask::PeriodicTask(uint32_t initialPeriod) : period(initialPeriod) {}
void PeriodicTask::setup()
{
periodicScheduler.schedule(this);
}
/// call this from loop
void PeriodicScheduler::loop()
{
meshtastic::LockGuard lg(&lock);
uint32_t now = millis();
for (auto t : tasks) {
if (t->period && (now - t->lastMsec) >= t->period) {
t->doTask();
t->lastMsec = now;
}
}
}
void PeriodicScheduler::schedule(PeriodicTask *t)
{
meshtastic::LockGuard lg(&lock);
tasks.insert(t);
}
void PeriodicScheduler::unschedule(PeriodicTask *t)
{
meshtastic::LockGuard lg(&lock);
tasks.erase(t);
}
void Periodic::doTask()
{
uint32_t p = callback();
setPeriod(p);
}

Wyświetl plik

@ -1,85 +0,0 @@
#pragma once
#include "lock.h"
#include <Arduino.h>
#include <cstdint>
#include <unordered_set>
class PeriodicTask;
/**
* Runs all PeriodicTasks in the system.
*
* Currently called from main loop() but eventually should be its own thread blocked on a freertos timer.
*/
class PeriodicScheduler
{
friend class PeriodicTask;
/**
* This really should be some form of heap, and when the period gets changed on a task it should get
* rescheduled in that heap. Currently it is just a dumb array and everytime we run loop() we check
* _every_ tasks. If it was a heap we'd only have to check the first task.
*/
std::unordered_set<PeriodicTask *> tasks;
// Protects the above variables.
meshtastic::Lock lock;
public:
/// Run any next tasks which are due for execution
void loop();
private:
void schedule(PeriodicTask *t);
void unschedule(PeriodicTask *t);
};
extern PeriodicScheduler periodicScheduler;
/**
* A base class for tasks that want their doTask() method invoked periodically
*
* FIXME: currently just syntatic sugar for polling in loop (you must call .loop), but eventually
* generalize with the freertos scheduler so we can save lots of power by having everything either in
* something like this or triggered off of an irq.
*/
class PeriodicTask
{
friend class PeriodicScheduler;
uint32_t lastMsec = 0;
uint32_t period = 1; // call soon after creation
public:
virtual ~PeriodicTask() { periodicScheduler.unschedule(this); }
/**
* Constructor (will schedule with the global PeriodicScheduler)
*/
PeriodicTask(uint32_t initialPeriod = 1);
/** MUST be be called once at startup (but after threading is running - i.e. not from a constructor)
*/
void setup();
/**
* Set a new period in msecs (can be called from doTask or elsewhere and the scheduler will cope)
* While zero this task is disabled and will not run
*/
void setPeriod(uint32_t p)
{
lastMsec = millis(); // reset starting from now
period = p;
}
uint32_t getPeriod() const { return period; }
/**
* Syntatic sugar for suspending tasks
*/
void disable() { setPeriod(0); }
protected:
virtual void doTask() = 0;
};

Wyświetl plik

@ -4,7 +4,6 @@
#include "utils.h"
#include "sleep.h"
#ifdef TBEAM_V10
// FIXME. nasty hack cleanup how we load axp192
@ -19,7 +18,7 @@ bool Power::setup()
{
axp192Init();
PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
setPeriod(1);
return axp192_found;

Wyświetl plik

@ -8,6 +8,7 @@
#include "screen.h"
#include "sleep.h"
#include "target_specific.h"
#include "timing.h"
static void sdsEnter()
{
@ -15,7 +16,7 @@ static void sdsEnter()
// Don't deepsleep if we have USB power or if the user as pressed a button recently
// !isUSBPowered <- doesn't work yet because the axp192 isn't letting the battery fully charge when we are awake - FIXME
if (millis() - lastPressMs > radioConfig.preferences.mesh_sds_timeout_secs)
if (timing::millis() - lastPressMs > radioConfig.preferences.mesh_sds_timeout_secs)
{
doDeepSleep(radioConfig.preferences.sds_secs);
}
@ -130,7 +131,7 @@ static void onEnter()
static uint32_t lastPingMs;
uint32_t now = millis();
uint32_t now = timing::millis();
if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask other nodes to update their state
if (displayedNodeNum)

Wyświetl plik

@ -1,58 +0,0 @@
#include "WorkerThread.h"
#include "debug.h"
#include <assert.h>
#ifdef configUSE_PREEMPTION
void Thread::start(const char *name, size_t stackSize, uint32_t priority)
{
auto r = xTaskCreate(callRun, name, stackSize, this, priority, &taskHandle);
assert(r == pdPASS);
}
void Thread::callRun(void *_this)
{
((Thread *)_this)->doRun();
}
void WorkerThread::doRun()
{
startWatchdog();
while (!wantExit) {
stopWatchdog();
block();
startWatchdog();
// no need - startWatchdog is guaranteed to give us one full watchdog interval
// serviceWatchdog(); // Let our loop worker have one full watchdog interval (at least) to run
#ifdef DEBUG_STACK
static uint32_t lastPrint = 0;
if (millis() - lastPrint > 10 * 1000L) {
lastPrint = millis();
meshtastic::printThreadInfo("net");
}
#endif
loop();
}
stopWatchdog();
}
/**
* Notify this thread so it can run
*/
void NotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
{
xTaskNotify(taskHandle, v, action);
}
void NotifiedWorkerThread::block()
{
xTaskNotifyWait(0, // don't clear notification on entry
clearOnRead, &notification, portMAX_DELAY); // Wait forever
}
#endif

Wyświetl plik

@ -1,114 +0,0 @@
#include "esp_task_wdt.h"
#include "freertosinc.h"
#include <Arduino.h>
#ifdef HAS_FREE_RTOS
class Thread
{
protected:
TaskHandle_t taskHandle = NULL;
/**
* set this to true to ask thread to cleanly exit asap
*/
volatile bool wantExit = false;
public:
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY);
virtual ~Thread() { vTaskDelete(taskHandle); }
uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
protected:
/**
* The method that will be called when start is called.
*/
virtual void doRun() = 0;
/**
* All thread run methods must periodically call serviceWatchdog, or the system will declare them hung and panic.
*
* this only applies after startWatchdog() has been called. If you need to sleep for a long time call stopWatchdog()
*/
void serviceWatchdog() { esp_task_wdt_reset(); }
void startWatchdog()
{
auto r = esp_task_wdt_add(taskHandle);
assert(r == ESP_OK);
}
void stopWatchdog()
{
auto r = esp_task_wdt_delete(taskHandle);
assert(r == ESP_OK);
}
private:
static void callRun(void *_this);
};
/**
* This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting onlyschool arduino loop() code.
*
* Use as a mixin base class for the classes you want to convert.
*
* https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html
*/
class WorkerThread : public Thread
{
protected:
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block() = 0;
virtual void loop() = 0;
/**
* The method that will be called when start is called.
*/
virtual void doRun();
};
/**
* A worker thread that waits on a freertos notification
*/
class NotifiedWorkerThread : public WorkerThread
{
public:
/**
* Notify this thread so it can run
*/
void notify(uint32_t v = 0, eNotifyAction action = eNoAction);
/**
* Notify from an ISR
*
* This must be inline or IRAM_ATTR on ESP32
*/
inline void notifyFromISR(BaseType_t *highPriWoken, uint32_t v = 0, eNotifyAction action = eNoAction)
{
xTaskNotifyFromISR(taskHandle, v, action, highPriWoken);
}
protected:
/**
* The notification that was most recently used to wake the thread. Read from loop()
*/
uint32_t notification = 0;
/**
* What notification bits should be cleared just after we read and return them in notification?
*
* Defaults to clear all of them.
*/
uint32_t clearOnRead = UINT32_MAX;
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block();
};
#endif

Wyświetl plik

@ -0,0 +1,23 @@
#include "Lock.h"
#include <cassert>
namespace concurrency {
Lock::Lock()
{
handle = xSemaphoreCreateBinary();
assert(handle);
assert(xSemaphoreGive(handle));
}
void Lock::lock()
{
assert(xSemaphoreTake(handle, portMAX_DELAY));
}
void Lock::unlock()
{
assert(xSemaphoreGive(handle));
}
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,33 @@
#pragma once
#include "../freertosinc.h"
namespace concurrency {
/**
* @brief Simple wrapper around FreeRTOS API for implementing a mutex lock
*/
class Lock
{
public:
Lock();
Lock(const Lock &) = delete;
Lock &operator=(const Lock &) = delete;
/// Locks the lock.
//
// Must not be called from an ISR.
void lock();
// Unlocks the lock.
//
// Must not be called from an ISR.
void unlock();
private:
SemaphoreHandle_t handle;
};
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,15 @@
#include "LockGuard.h"
namespace concurrency {
LockGuard::LockGuard(Lock *lock) : lock(lock)
{
lock->lock();
}
LockGuard::~LockGuard()
{
lock->unlock();
}
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,23 @@
#pragma once
#include "Lock.h"
namespace concurrency {
/**
* @brief RAII lock guard
*/
class LockGuard
{
public:
LockGuard(Lock *lock);
~LockGuard();
LockGuard(const LockGuard &) = delete;
LockGuard &operator=(const LockGuard &) = delete;
private:
Lock *lock;
};
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,19 @@
#include "NotifiedWorkerThread.h"
namespace concurrency {
/**
* Notify this thread so it can run
*/
void NotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
{
xTaskNotify(taskHandle, v, action);
}
void NotifiedWorkerThread::block()
{
xTaskNotifyWait(0, // don't clear notification on entry
clearOnRead, &notification, portMAX_DELAY); // Wait forever
}
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,47 @@
#pragma once
#include "WorkerThread.h"
namespace concurrency {
/**
* @brief A worker thread that waits on a freertos notification
*/
class NotifiedWorkerThread : public WorkerThread
{
public:
/**
* Notify this thread so it can run
*/
void notify(uint32_t v = 0, eNotifyAction action = eNoAction);
/**
* Notify from an ISR
*
* This must be inline or IRAM_ATTR on ESP32
*/
inline void notifyFromISR(BaseType_t *highPriWoken, uint32_t v = 0, eNotifyAction action = eNoAction)
{
xTaskNotifyFromISR(taskHandle, v, action, highPriWoken);
}
protected:
/**
* The notification that was most recently used to wake the thread. Read from loop()
*/
uint32_t notification = 0;
/**
* What notification bits should be cleared just after we read and return them in notification?
*
* Defaults to clear all of them.
*/
uint32_t clearOnRead = UINT32_MAX;
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block();
};
} // namespace concurrency

Wyświetl plik

@ -1,12 +1,12 @@
#pragma once
#include "PeriodicTask.h"
#include <Arduino.h>
namespace concurrency {
/**
* Periodically invoke a callback.
*
* This just provides C style callback conventions rather than a virtual function - FIXME, remove?
* @brief Periodically invoke a callback. This just provides C-style callback conventions
* rather than a virtual function - FIXME, remove?
*/
class Periodic : public PeriodicTask
{
@ -17,5 +17,10 @@ class Periodic : public PeriodicTask
Periodic(uint32_t (*_callback)()) : callback(_callback) {}
protected:
void doTask();
void doTask() {
uint32_t p = callback();
setPeriod(p);
}
};
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,35 @@
#include "PeriodicScheduler.h"
#include "PeriodicTask.h"
#include "LockGuard.h"
#include "../timing.h"
namespace concurrency {
/// call this from loop
void PeriodicScheduler::loop()
{
LockGuard lg(&lock);
uint32_t now = timing::millis();
for (auto t : tasks) {
if (t->period && (now - t->lastMsec) >= t->period) {
t->doTask();
t->lastMsec = now;
}
}
}
void PeriodicScheduler::schedule(PeriodicTask *t)
{
LockGuard lg(&lock);
tasks.insert(t);
}
void PeriodicScheduler::unschedule(PeriodicTask *t)
{
LockGuard lg(&lock);
tasks.erase(t);
}
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,40 @@
#pragma once
#include "Lock.h"
#include <cstdint>
#include <unordered_set>
namespace concurrency {
class PeriodicTask;
/**
* @brief Runs all PeriodicTasks in the system. Currently called from main loop()
* but eventually should be its own thread blocked on a freertos timer.
*/
class PeriodicScheduler
{
friend class PeriodicTask;
/**
* This really should be some form of heap, and when the period gets changed on a task it should get
* rescheduled in that heap. Currently it is just a dumb array and everytime we run loop() we check
* _every_ tasks. If it was a heap we'd only have to check the first task.
*/
std::unordered_set<PeriodicTask *> tasks;
// Protects the above variables.
Lock lock;
public:
/// Run any next tasks which are due for execution
void loop();
private:
void schedule(PeriodicTask *t);
void unschedule(PeriodicTask *t);
};
extern PeriodicScheduler periodicScheduler;
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,16 @@
#include "PeriodicTask.h"
#include "Periodic.h"
#include "LockGuard.h"
namespace concurrency {
PeriodicScheduler periodicScheduler;
PeriodicTask::PeriodicTask(uint32_t initialPeriod) : period(initialPeriod) {}
void PeriodicTask::setup()
{
periodicScheduler.schedule(this);
}
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,56 @@
#pragma once
#include "PeriodicScheduler.h"
#include "timing.h"
namespace concurrency {
/**
* @brief A base class for tasks that want their doTask() method invoked periodically
*
* @todo currently just syntatic sugar for polling in loop (you must call .loop), but eventually
* generalize with the freertos scheduler so we can save lots of power by having everything either in
* something like this or triggered off of an irq.
*/
class PeriodicTask
{
friend class PeriodicScheduler;
uint32_t lastMsec = 0;
uint32_t period = 1; // call soon after creation
public:
virtual ~PeriodicTask() { periodicScheduler.unschedule(this); }
/**
* Constructor (will schedule with the global PeriodicScheduler)
*/
PeriodicTask(uint32_t initialPeriod = 1);
/**
* MUST be be called once at startup (but after threading is running - i.e. not from a constructor)
*/
void setup();
/**
* Set a new period in msecs (can be called from doTask or elsewhere and the scheduler will cope)
* While zero this task is disabled and will not run
*/
void setPeriod(uint32_t p)
{
lastMsec = timing::millis(); // reset starting from now
period = p;
}
uint32_t getPeriod() const { return period; }
/**
* Syntatic sugar for suspending tasks
*/
void disable() { setPeriod(0); }
protected:
virtual void doTask() = 0;
};
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,17 @@
#include "Thread.h"
#include "timing.h"
namespace concurrency {
void Thread::start(const char *name, size_t stackSize, uint32_t priority)
{
auto r = xTaskCreate(callRun, name, stackSize, this, priority, &taskHandle);
assert(r == pdPASS);
}
void Thread::callRun(void *_this)
{
((Thread *)_this)->doRun();
}
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,56 @@
#pragma once
#include "freertosinc.h"
#include "esp_task_wdt.h"
namespace concurrency {
/**
* @brief Base threading
*/
class Thread
{
protected:
TaskHandle_t taskHandle = NULL;
/**
* set this to true to ask thread to cleanly exit asap
*/
volatile bool wantExit = false;
public:
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY);
virtual ~Thread() { vTaskDelete(taskHandle); }
uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
protected:
/**
* The method that will be called when start is called.
*/
virtual void doRun() = 0;
/**
* All thread run methods must periodically call serviceWatchdog, or the system will declare them hung and panic.
*
* this only applies after startWatchdog() has been called. If you need to sleep for a long time call stopWatchdog()
*/
void serviceWatchdog() { esp_task_wdt_reset(); }
void startWatchdog()
{
auto r = esp_task_wdt_add(taskHandle);
assert(r == ESP_OK);
}
void stopWatchdog()
{
auto r = esp_task_wdt_delete(taskHandle);
assert(r == ESP_OK);
}
private:
static void callRun(void *_this);
};
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,32 @@
#include "WorkerThread.h"
#include "timing.h"
namespace concurrency {
void WorkerThread::doRun()
{
startWatchdog();
while (!wantExit) {
stopWatchdog();
block();
startWatchdog();
// no need - startWatchdog is guaranteed to give us one full watchdog interval
// serviceWatchdog(); // Let our loop worker have one full watchdog interval (at least) to run
#ifdef DEBUG_STACK
static uint32_t lastPrint = 0;
if (timing::millis() - lastPrint > 10 * 1000L) {
lastPrint = timing::millis();
meshtastic::printThreadInfo("net");
}
#endif
loop();
}
stopWatchdog();
}
} // namespace concurrency

Wyświetl plik

@ -0,0 +1,29 @@
#pragma once
#include "Thread.h"
namespace concurrency {
/**
* @brief This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting
* old-school arduino loop() code. Use as a mixin base class for the classes you want to convert.
*
* @link https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html
*/
class WorkerThread : public Thread
{
protected:
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block() = 0;
virtual void loop() = 0;
/**
* The method that will be called when start is called.
*/
virtual void doRun();
};
} // namespace concurrency

Wyświetl plik

@ -1,20 +0,0 @@
#include "debug.h"
#include <cstdint>
#include "freertosinc.h"
#include "configuration.h"
namespace meshtastic
{
void printThreadInfo(const char *extra)
{
#ifndef NO_ESP32
uint32_t taskHandle = reinterpret_cast<uint32_t>(xTaskGetCurrentTaskHandle());
DEBUG_MSG("printThreadInfo(%s) task: %" PRIx32 " core id: %u min free stack: %u\n", extra, taskHandle, xPortGetCoreID(),
uxTaskGetStackHighWaterMark(nullptr));
#endif
}
} // namespace meshtastic

Wyświetl plik

@ -1,10 +0,0 @@
#pragma once
namespace meshtastic
{
/// Dumps out which core we are running on, and min level of remaining stack
/// seen.
void printThreadInfo(const char *extra);
} // namespace meshtastic

Wyświetl plik

@ -3,21 +3,22 @@
#include "CallbackCharacteristic.h"
#include "RadioLibInterface.h"
#include "configuration.h"
#include "lock.h"
#include "../concurrency/LockGuard.h"
#include "../timing.h"
#include <Arduino.h>
#include <BLE2902.h>
#include <CRC32.h>
#include <Update.h>
#include <esp_gatt_defs.h>
using namespace meshtastic;
//using namespace meshtastic;
CRC32 crc;
uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
uint32_t updateExpectedSize, updateActualSize;
Lock *updateLock;
concurrency::Lock *updateLock;
class TotalSizeCharacteristic : public CallbackCharacteristic
{
@ -30,7 +31,7 @@ class TotalSizeCharacteristic : public CallbackCharacteristic
void onWrite(BLECharacteristic *c)
{
LockGuard g(updateLock);
concurrency::LockGuard g(updateLock);
// Check if there is enough to OTA Update
uint32_t len = getValue32(c, 0);
updateExpectedSize = len;
@ -65,7 +66,7 @@ class DataCharacteristic : public CallbackCharacteristic
void onWrite(BLECharacteristic *c)
{
LockGuard g(updateLock);
concurrency::LockGuard g(updateLock);
std::string value = c->getValue();
uint32_t len = value.length();
assert(len <= MAX_BLOCKSIZE);
@ -89,7 +90,7 @@ class CRC32Characteristic : public CallbackCharacteristic
void onWrite(BLECharacteristic *c)
{
LockGuard g(updateLock);
concurrency::LockGuard g(updateLock);
uint32_t expectedCRC = getValue32(c, 0);
uint32_t actualCRC = crc.finalize();
DEBUG_MSG("expected CRC %u\n", expectedCRC);
@ -106,7 +107,7 @@ class CRC32Characteristic : public CallbackCharacteristic
} else {
if (Update.end()) {
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
rebootAtMsec = millis() + 5000;
rebootAtMsec = timing::millis() + 5000;
} else {
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
}
@ -124,7 +125,7 @@ class CRC32Characteristic : public CallbackCharacteristic
void bluetoothRebootCheck()
{
if (rebootAtMsec && millis() > rebootAtMsec) {
if (rebootAtMsec && timing::millis() > rebootAtMsec) {
DEBUG_MSG("Rebooting for update\n");
ESP.restart();
}
@ -137,7 +138,7 @@ See bluetooth-api.md
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion)
{
if (!updateLock)
updateLock = new Lock();
updateLock = new concurrency::Lock();
// Create the BLE Service
BLEService *service = server->createService(BLEUUID("cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"), 25, 0);

Wyświetl plik

@ -1,9 +1,9 @@
#include "GPS.h"
#include "configuration.h"
#include "time.h"
#include "timing.h"
#include <assert.h>
#include <sys/time.h>
#include <time.h>
#ifdef GPS_RX_PIN
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
@ -27,7 +27,7 @@ void readFromRTC()
struct timeval tv; /* btw settimeofday() is helpfull here too*/
if (!gettimeofday(&tv, NULL)) {
uint32_t now = millis();
uint32_t now = timing::millis();
DEBUG_MSG("Read RTC time as %ld (cur millis %u) valid=%d\n", tv.tv_sec, now, timeSetFromGPS);
timeStartMsec = now;
@ -68,11 +68,9 @@ void perhapsSetRTC(struct tm &t)
perhapsSetRTC(&tv);
}
#include <time.h>
uint32_t getTime()
{
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
return ((timing::millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
}
uint32_t getValidTime()

Wyświetl plik

@ -2,7 +2,7 @@
#include "Observer.h"
#include "GPSStatus.h"
#include "PeriodicTask.h"
#include "../concurrency/PeriodicTask.h"
#include "sys/time.h"
/// If we haven't yet set our RTC this boot, set it from a GPS derived time

Wyświetl plik

@ -1,5 +1,6 @@
#include "NEMAGPS.h"
#include "configuration.h"
#include "timing.h"
static int32_t toDegInt(RawDegrees d)
{
@ -19,7 +20,7 @@ void NEMAGPS::loop()
reader.encode(c);
}
uint32_t now = millis();
uint32_t now = timing::millis();
if ((now - lastUpdateMsec) > 20 * 1000) { // Ugly hack for now - limit update checks to once every 20 secs (but still consume
// serial chars at whatever rate)
lastUpdateMsec = now;

Wyświetl plik

@ -2,7 +2,7 @@
#include "GPS.h"
#include "Observer.h"
#include "PeriodicTask.h"
#include "../concurrency/PeriodicTask.h"
#include "TinyGPS++.h"
/**

Wyświetl plik

@ -2,7 +2,7 @@
#include "sleep.h"
#include <assert.h>
UBloxGPS::UBloxGPS() : PeriodicTask()
UBloxGPS::UBloxGPS() : concurrency::PeriodicTask()
{
notifySleepObserver.observe(&notifySleep);
}
@ -55,7 +55,7 @@ bool UBloxGPS::setup()
ok = ublox.saveConfiguration(3000);
assert(ok);
PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
return true;
} else {

Wyświetl plik

@ -2,7 +2,7 @@
#include "GPS.h"
#include "Observer.h"
#include "PeriodicTask.h"
#include "../concurrency/PeriodicTask.h"
#include "SparkFun_Ublox_Arduino_Library.h"
/**
@ -10,7 +10,7 @@
*
* When new data is available it will notify observers.
*/
class UBloxGPS : public GPS, public PeriodicTask
class UBloxGPS : public GPS, public concurrency::PeriodicTask
{
SFE_UBLOX_GPS ublox;

Wyświetl plik

@ -1,50 +0,0 @@
#include "lock.h"
#include <cassert>
namespace meshtastic
{
#ifdef configUSE_PREEMPTION
Lock::Lock()
{
handle = xSemaphoreCreateBinary();
assert(handle);
assert(xSemaphoreGive(handle));
}
void Lock::lock()
{
assert(xSemaphoreTake(handle, portMAX_DELAY));
}
void Lock::unlock()
{
assert(xSemaphoreGive(handle));
}
#else
Lock::Lock()
{
}
void Lock::lock()
{
}
void Lock::unlock()
{
}
#endif
LockGuard::LockGuard(Lock *lock) : lock(lock)
{
lock->lock();
}
LockGuard::~LockGuard()
{
lock->unlock();
}
} // namespace meshtastic

Wyświetl plik

@ -1,47 +0,0 @@
#pragma once
#include "freertosinc.h"
namespace meshtastic
{
// Simple wrapper around FreeRTOS API for implementing a mutex lock.
class Lock
{
public:
Lock();
Lock(const Lock &) = delete;
Lock &operator=(const Lock &) = delete;
/// Locks the lock.
//
// Must not be called from an ISR.
void lock();
// Unlocks the lock.
//
// Must not be called from an ISR.
void unlock();
private:
#ifdef configUSE_PREEMPTION
SemaphoreHandle_t handle;
#endif
};
// RAII lock guard.
class LockGuard
{
public:
LockGuard(Lock *lock);
~LockGuard();
LockGuard(const LockGuard &) = delete;
LockGuard &operator=(const LockGuard &) = delete;
private:
Lock *lock;
};
} // namespace meshtastic

Wyświetl plik

@ -25,7 +25,7 @@
#include "MeshService.h"
#include "NEMAGPS.h"
#include "NodeDB.h"
#include "Periodic.h"
#include "concurrency/Periodic.h"
#include "PowerFSM.h"
#include "UBloxGPS.h"
#include "configuration.h"
@ -37,6 +37,7 @@
#include "main.h"
#include "screen.h"
#include "sleep.h"
#include "timing.h"
#include <OneButton.h>
#include <Wire.h>
// #include <driver/rtc_io.h>
@ -130,7 +131,7 @@ static uint32_t ledBlinker()
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 2 : 1000);
}
Periodic ledPeriodic(ledBlinker);
concurrency::Periodic ledPeriodic(ledBlinker);
// Prepare for button presses
#ifdef BUTTON_PIN
@ -333,7 +334,7 @@ uint32_t axpDebugRead()
return 30 * 1000;
}
Periodic axpDebugOutput(axpDebugRead);
concurrency::Periodic axpDebugOutput(axpDebugRead);
axpDebugOutput.setup();
#endif
@ -346,7 +347,7 @@ void loop()
powerFSM.run_machine();
service.loop();
periodicScheduler.loop();
concurrency::periodicScheduler.loop();
// axpDebugOutput.loop();
#ifdef DEBUG_PORT
@ -371,21 +372,21 @@ void loop()
// Show boot screen for first 3 seconds, then switch to normal operation.
static bool showingBootScreen = true;
if (showingBootScreen && (millis() > 3000)) {
if (showingBootScreen && (timing::millis() > 3000)) {
screen.stopBootScreen();
showingBootScreen = false;
}
#ifdef DEBUG_STACK
static uint32_t lastPrint = 0;
if (millis() - lastPrint > 10 * 1000L) {
lastPrint = millis();
if (timing::millis() - lastPrint > 10 * 1000L) {
lastPrint = timing::millis();
meshtastic::printThreadInfo("main");
}
#endif
// Update the screen last, after we've figured out what to show.
screen.debug()->setChannelNameStatus(channelSettings.name);
screen.debug_info()->setChannelNameStatus(channelSettings.name);
// screen.debug()->setPowerStatus(powerStatus);
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)

Wyświetl plik

@ -1,7 +1,7 @@
#pragma once
#include "PacketHistory.h"
#include "PeriodicTask.h"
#include "../concurrency/PeriodicTask.h"
#include "Router.h"
/**

Wyświetl plik

@ -7,12 +7,13 @@
//#include "MeshBluetoothService.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "Periodic.h"
#include "../concurrency/Periodic.h"
#include "PowerFSM.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "power.h"
#include "BluetoothUtil.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
#include "timing.h"
/*
receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone.
@ -55,7 +56,7 @@ static uint32_t sendOwnerCb()
return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
}
static Periodic sendOwnerPeriod(sendOwnerCb);
static concurrency::Periodic sendOwnerPeriod(sendOwnerCb);
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
{
@ -308,7 +309,7 @@ int MeshService::onGPSChanged(void *unused)
// We limit our GPS broadcasts to a max rate
static uint32_t lastGpsSend;
uint32_t now = millis();
uint32_t now = timing::millis();
if (lastGpsSend == 0 || now - lastGpsSend > radioConfig.preferences.position_broadcast_secs * 1000) {
lastGpsSend = now;
DEBUG_MSG("Sending position to mesh\n");

Wyświetl plik

@ -1,6 +1,7 @@
#include "PacketHistory.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include "../timing.h"
PacketHistory::PacketHistory()
{
@ -18,7 +19,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
return false; // Not a floodable message ID, so we don't care
}
uint32_t now = millis();
uint32_t now = timing::millis();
for (size_t i = 0; i < recentPackets.size();) {
PacketRecord &r = recentPackets[i];

Wyświetl plik

@ -4,6 +4,7 @@
#include "PowerFSM.h"
#include "RadioInterface.h"
#include "GPS.h"
#include "timing.h"
#include <assert.h>
PhoneAPI::PhoneAPI()
@ -20,7 +21,7 @@ void PhoneAPI::init()
void PhoneAPI::checkConnectionTimeout()
{
if (isConnected) {
bool newConnected = (millis() - lastContactMsec < radioConfig.preferences.phone_timeout_secs * 1000L);
bool newConnected = (timing::millis() - lastContactMsec < radioConfig.preferences.phone_timeout_secs * 1000L);
if (!newConnected) {
isConnected = false;
onConnectionChanged(isConnected);
@ -34,7 +35,7 @@ void PhoneAPI::checkConnectionTimeout()
void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
{
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep
lastContactMsec = millis();
lastContactMsec = timing::millis();
if (!isConnected) {
isConnected = true;
onConnectionChanged(isConnected);

Wyświetl plik

@ -6,6 +6,7 @@
#include "assert.h"
#include "configuration.h"
#include "sleep.h"
#include "timing.h"
#include <assert.h>
#include <pb_decode.h>
#include <pb_encode.h>
@ -155,7 +156,7 @@ size_t RadioInterface::beginSending(MeshPacket *p)
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
lastTxStart = millis();
lastTxStart = timing::millis();
PacketHeader *h = (PacketHeader *)radiobuf;

Wyświetl plik

@ -4,7 +4,7 @@
#include "MeshTypes.h"
#include "Observer.h"
#include "PointerQueue.h"
#include "WorkerThread.h"
#include "../concurrency/NotifiedWorkerThread.h"
#include "mesh.pb.h"
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
@ -43,7 +43,7 @@ typedef enum {
*
* This defines the SOLE API for talking to radios (because soon we will have alternate radio implementations)
*/
class RadioInterface : protected NotifiedWorkerThread
class RadioInterface : protected concurrency::NotifiedWorkerThread
{
friend class MeshRadio; // for debugging we let that class touch pool
PointerQueue<MeshPacket> *rxDest = NULL;

Wyświetl plik

@ -10,7 +10,7 @@ static SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
SPIClass &spi, PhysicalLayer *_iface)
: PeriodicTask(0), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
: concurrency::PeriodicTask(0), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
{
assert(!instance); // We assume only one for now
instance = this;

Wyświetl plik

@ -1,6 +1,6 @@
#pragma once
#include "PeriodicTask.h"
#include "../concurrency/PeriodicTask.h"
#include "RadioInterface.h"
#ifdef CubeCell_BoardPlus
@ -16,7 +16,7 @@
#define INTERRUPT_ATTR
#endif
class RadioLibInterface : public RadioInterface, private PeriodicTask
class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTask
{
/// Used as our notification from the ISR
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };

Wyświetl plik

@ -2,6 +2,7 @@
#include "MeshTypes.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include "timing.h"
// ReliableRouter::ReliableRouter() {}
@ -162,7 +163,7 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
*/
void ReliableRouter::doRetransmissions()
{
uint32_t now = millis();
uint32_t now = timing::millis();
// FIXME, we should use a better datastructure rather than walking through this map.
// for(auto el: pending) {

Wyświetl plik

@ -1,7 +1,8 @@
#pragma once
#include "FloodingRouter.h"
#include "PeriodicTask.h"
#include "../concurrency/PeriodicTask.h"
#include "../timing.h"
#include <unordered_map>
/**
@ -48,7 +49,7 @@ struct PendingPacket {
PendingPacket() {}
PendingPacket(MeshPacket *p);
void setNextTx() { nextTxMsec = millis() + random(20 * 1000L, 22 * 1000L); }
void setNextTx() { nextTxMsec = timing::millis() + random(20 * 1000L, 22 * 1000L); }
};
class GlobalPacketIdHashFunction

Wyświetl plik

@ -1,5 +1,5 @@
#pragma once
#include "PeriodicTask.h"
#include "concurrency/PeriodicTask.h"
#include "PowerStatus.h"
/**
@ -15,7 +15,7 @@
#define BAT_MILLIVOLTS_FULL 4100
#define BAT_MILLIVOLTS_EMPTY 3500
class Power : public PeriodicTask
class Power : public concurrency::PeriodicTask
{
public:

Wyświetl plik

@ -565,7 +565,7 @@ void Screen::handleSetOn(bool on)
void Screen::setup()
{
PeriodicTask::setup();
concurrency::PeriodicTask::setup();
// We don't set useDisplay until setup() is called, because some boards have a declaration of this object but the device
// is never found when probing i2c and therefore we don't call setup and never want to do (invalid) accesses to this device.
@ -780,7 +780,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
char channelStr[20];
{
LockGuard guard(&lock);
concurrency::LockGuard guard(&lock);
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
// Display power status

Wyświetl plik

@ -10,13 +10,10 @@
#include <SSD1306Wire.h>
#endif
#include "PeriodicTask.h"
#include "concurrency/PeriodicTask.h"
#include "TypedQueue.h"
#include "lock.h"
#include "PowerStatus.h"
#include "GPSStatus.h"
#include "NodeStatus.h"
#include "Observer.h"
#include "concurrency/LockGuard.h"
#include "power.h"
#include <string>
namespace meshtastic
@ -35,7 +32,7 @@ class DebugInfo
/// Sets the name of the channel.
void setChannelNameStatus(const char *name)
{
LockGuard guard(&lock);
concurrency::LockGuard guard(&lock);
channelName = name;
}
@ -50,7 +47,7 @@ class DebugInfo
std::string channelName;
/// Protects all of internal state.
Lock lock;
concurrency::Lock lock;
};
/// Deals with showing things on the screen of the device.
@ -60,7 +57,7 @@ class DebugInfo
//
// This class is thread-safe (as long as drawFrame is not called multiple times
// simultaneously).
class Screen : public PeriodicTask
class Screen : public concurrency::PeriodicTask
{
CallbackObserver<Screen, const Status *> powerStatusObserver = CallbackObserver<Screen, const Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const Status *> gpsStatusObserver = CallbackObserver<Screen, const Status *>(this, &Screen::handleStatusUpdate);
@ -160,7 +157,7 @@ class Screen : public PeriodicTask
/// Returns a handle to the DebugInfo screen.
//
// Use this handle to set things like battery status, user count, GPS status, etc.
DebugInfo *debug() { return &debugInfo; }
DebugInfo* debug_info() { return &debugInfo; }
int handleStatusUpdate(const meshtastic::Status *arg);

Wyświetl plik

@ -5,7 +5,7 @@
#include "NodeDB.h"
#include "configuration.h"
#include "error.h"
#include "timing.h"
#include "main.h"
#include "target_specific.h"
@ -123,11 +123,11 @@ bool doPreflightSleep()
/// Tell devices we are going to sleep and wait for them to handle things
static void waitEnterSleep()
{
uint32_t now = millis();
uint32_t now = timing::millis();
while (!doPreflightSleep()) {
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
if (timing::millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
recordCriticalError(ErrSleepEnterWait);
assert(0); // FIXME - for now we just restart, need to fix bug #167
break;

10
src/timing.cpp 100644
Wyświetl plik

@ -0,0 +1,10 @@
#include "timing.h"
#include "freertosinc.h"
namespace timing {
uint32_t millis() {
return xTaskGetTickCount();
}
} // namespace timing

9
src/timing.h 100644
Wyświetl plik

@ -0,0 +1,9 @@
#pragma once
#include <cstdint>
namespace timing {
uint32_t millis();
} // namespace timing