From bed7d8a6197fd8ec0b5dc8a3a900680f8a7b6a95 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 8 Oct 2020 13:32:34 +0800 Subject: [PATCH 1/8] threads: begin change to cooperative threading --- src/concurrency/BaseThread.cpp | 2 +- src/concurrency/{Thread.h => OSThread.h} | 4 ++-- src/concurrency/WorkerThread.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/concurrency/{Thread.h => OSThread.h} (73%) diff --git a/src/concurrency/BaseThread.cpp b/src/concurrency/BaseThread.cpp index ab39a8c9f..5f304b5fe 100644 --- a/src/concurrency/BaseThread.cpp +++ b/src/concurrency/BaseThread.cpp @@ -1,4 +1,4 @@ -#include "Thread.h" +#include "BaseThread.h" #include namespace concurrency diff --git a/src/concurrency/Thread.h b/src/concurrency/OSThread.h similarity index 73% rename from src/concurrency/Thread.h rename to src/concurrency/OSThread.h index f9fa60fde..52ecdf525 100644 --- a/src/concurrency/Thread.h +++ b/src/concurrency/OSThread.h @@ -7,11 +7,11 @@ namespace concurrency { #ifdef HAS_FREE_RTOS -typedef FreeRtosThread Thread; +typedef FreeRtosThread OSThread; #endif #ifdef __unix__ -typedef PosixThread Thread; +typedef PosixThread OSThread; #endif } // namespace concurrency diff --git a/src/concurrency/WorkerThread.h b/src/concurrency/WorkerThread.h index 66111e7d7..c9b2c6b35 100644 --- a/src/concurrency/WorkerThread.h +++ b/src/concurrency/WorkerThread.h @@ -1,6 +1,6 @@ #pragma once -#include "Thread.h" +#include "OSThread.h" namespace concurrency { @@ -10,7 +10,7 @@ namespace concurrency { * * @link https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html */ -class WorkerThread : public Thread +class WorkerThread : public OSThread { protected: /** From dd6a402ea098c733f8aca9f8a676054d57069190 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Fri, 9 Oct 2020 09:10:44 +0800 Subject: [PATCH 2/8] coroutine: wip --- platformio.ini | 1 + src/concurrency/BaseNotifiedWorkerThread.h | 44 ----------------- src/concurrency/BaseThread.cpp | 12 ----- src/concurrency/BaseThread.h | 47 ------------------- .../FreeRtosNotifiedWorkerThread.cpp | 23 --------- .../FreeRtosNotifiedWorkerThread.h | 40 ---------------- src/concurrency/FreeRtosThread.cpp | 45 ------------------ src/concurrency/FreeRtosThread.h | 44 ----------------- src/concurrency/NotifiedWorkerThread.h | 38 +++++++++++---- src/concurrency/OSThread.cpp | 7 +++ src/concurrency/OSThread.h | 38 +++++++++++---- src/concurrency/PeriodicScheduler.cpp | 34 -------------- src/concurrency/PeriodicScheduler.h | 40 ---------------- src/concurrency/PeriodicTask.h | 4 +- src/concurrency/PosixNotifiedWorkerThread.cpp | 19 -------- src/concurrency/PosixNotifiedWorkerThread.h | 26 ---------- src/concurrency/PosixThread.h | 33 ------------- src/concurrency/WorkerThread.h | 14 ++---- 18 files changed, 73 insertions(+), 436 deletions(-) delete mode 100644 src/concurrency/BaseNotifiedWorkerThread.h delete mode 100644 src/concurrency/BaseThread.cpp delete mode 100644 src/concurrency/BaseThread.h delete mode 100644 src/concurrency/FreeRtosNotifiedWorkerThread.cpp delete mode 100644 src/concurrency/FreeRtosNotifiedWorkerThread.h delete mode 100644 src/concurrency/FreeRtosThread.cpp delete mode 100644 src/concurrency/FreeRtosThread.h create mode 100644 src/concurrency/OSThread.cpp delete mode 100644 src/concurrency/PeriodicScheduler.cpp delete mode 100644 src/concurrency/PeriodicScheduler.h delete mode 100644 src/concurrency/PosixNotifiedWorkerThread.cpp delete mode 100644 src/concurrency/PosixNotifiedWorkerThread.h delete mode 100644 src/concurrency/PosixThread.h diff --git a/platformio.ini b/platformio.ini index cf255f380..0b7ec863e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -68,6 +68,7 @@ lib_deps = https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460 Wire ; explicitly needed here because the AXP202 library forgets to add it SPI + https://github.com/geeksville/ArduinoThread.git ; Common settings for conventional (non Portduino) Ardino targets [arduino_base] diff --git a/src/concurrency/BaseNotifiedWorkerThread.h b/src/concurrency/BaseNotifiedWorkerThread.h deleted file mode 100644 index 03b82c4b0..000000000 --- a/src/concurrency/BaseNotifiedWorkerThread.h +++ /dev/null @@ -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 diff --git a/src/concurrency/BaseThread.cpp b/src/concurrency/BaseThread.cpp deleted file mode 100644 index 5f304b5fe..000000000 --- a/src/concurrency/BaseThread.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "BaseThread.h" -#include - -namespace concurrency -{ - -void BaseThread::callRun(void *_this) -{ - ((BaseThread *)_this)->doRun(); -} - -} // namespace concurrency diff --git a/src/concurrency/BaseThread.h b/src/concurrency/BaseThread.h deleted file mode 100644 index b1947cf45..000000000 --- a/src/concurrency/BaseThread.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include - -#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 diff --git a/src/concurrency/FreeRtosNotifiedWorkerThread.cpp b/src/concurrency/FreeRtosNotifiedWorkerThread.cpp deleted file mode 100644 index 8fec432dc..000000000 --- a/src/concurrency/FreeRtosNotifiedWorkerThread.cpp +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/concurrency/FreeRtosNotifiedWorkerThread.h b/src/concurrency/FreeRtosNotifiedWorkerThread.h deleted file mode 100644 index c18009e43..000000000 --- a/src/concurrency/FreeRtosNotifiedWorkerThread.h +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/concurrency/FreeRtosThread.cpp b/src/concurrency/FreeRtosThread.cpp deleted file mode 100644 index 1fe7108e3..000000000 --- a/src/concurrency/FreeRtosThread.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "FreeRtosThread.h" - -#ifdef HAS_FREE_RTOS - -#include - -#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 \ No newline at end of file diff --git a/src/concurrency/FreeRtosThread.h b/src/concurrency/FreeRtosThread.h deleted file mode 100644 index 6f52119db..000000000 --- a/src/concurrency/FreeRtosThread.h +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/concurrency/NotifiedWorkerThread.h b/src/concurrency/NotifiedWorkerThread.h index dee92eb8a..2ec1ea78c 100644 --- a/src/concurrency/NotifiedWorkerThread.h +++ b/src/concurrency/NotifiedWorkerThread.h @@ -1,17 +1,37 @@ #pragma once -#include "FreeRtosNotifiedWorkerThread.h" -#include "PosixNotifiedWorkerThread.h" +#include "WorkerThread.h" -namespace concurrency +namespace concurrency { + +/** + * @brief A worker thread that waits on a freertos notification + */ +class NotifiedWorkerThread : public WorkerThread { + public: + /** + * Notify this thread so it can run + */ + virtual void notify(uint32_t v = 0, eNotifyAction action = eNoAction) = 0; -#ifdef HAS_FREE_RTOS -typedef FreeRtosNotifiedWorkerThread NotifiedWorkerThread; -#endif + /** + * 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; -#ifdef __unix__ -typedef PosixNotifiedWorkerThread NotifiedWorkerThread; -#endif + /** + * A method that should block execution - either waiting ona queue/mutex or a "task notification" + */ + virtual void block() = 0; +}; } // namespace concurrency diff --git a/src/concurrency/OSThread.cpp b/src/concurrency/OSThread.cpp new file mode 100644 index 000000000..0569567c5 --- /dev/null +++ b/src/concurrency/OSThread.cpp @@ -0,0 +1,7 @@ +#include "OSThread.h" +#include + +namespace concurrency +{ + +} // namespace concurrency diff --git a/src/concurrency/OSThread.h b/src/concurrency/OSThread.h index 52ecdf525..3045ba543 100644 --- a/src/concurrency/OSThread.h +++ b/src/concurrency/OSThread.h @@ -1,17 +1,39 @@ #pragma once -#include "FreeRtosThread.h" -#include "PosixThread.h" +#include +#include + +#include "Thread.h" +#include "freertosinc.h" namespace concurrency { -#ifdef HAS_FREE_RTOS -typedef FreeRtosThread OSThread; -#endif +/** + * @brief Base threading + * + * TODO FIXME @geeksville + * basic functionality + * sleeping the correct amount of time in main + * NotifiedWorkerThread set/clears enabled + * + * stopping sleep instantly as soon as an event occurs. + * use global functions delayTillWakeEvent(time), doWakeEvent(isInISR) - use freertos mutex or somesuch + * + * remove lock/lockguard + */ +class OSThread +{ + public: + virtual ~OSThread() {} -#ifdef __unix__ -typedef PosixThread OSThread; -#endif + // uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); } + + protected: + /** + * The method that will be called each time our thread gets a chance to run + */ + virtual void runOnce() = 0; +}; } // namespace concurrency diff --git a/src/concurrency/PeriodicScheduler.cpp b/src/concurrency/PeriodicScheduler.cpp deleted file mode 100644 index 5902ddd7a..000000000 --- a/src/concurrency/PeriodicScheduler.cpp +++ /dev/null @@ -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 diff --git a/src/concurrency/PeriodicScheduler.h b/src/concurrency/PeriodicScheduler.h deleted file mode 100644 index 943da17cb..000000000 --- a/src/concurrency/PeriodicScheduler.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "Lock.h" -#include -#include - -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 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 diff --git a/src/concurrency/PeriodicTask.h b/src/concurrency/PeriodicTask.h index 74d4c8a34..4dd127f5b 100644 --- a/src/concurrency/PeriodicTask.h +++ b/src/concurrency/PeriodicTask.h @@ -14,8 +14,6 @@ namespace concurrency { */ class PeriodicTask { - friend class PeriodicScheduler; - uint32_t lastMsec = 0; uint32_t period = 1; // call soon after creation @@ -47,7 +45,7 @@ class PeriodicTask /** * Syntatic sugar for suspending tasks */ - void disable() { setPeriod(0); } + void disable(); protected: virtual void doTask() = 0; diff --git a/src/concurrency/PosixNotifiedWorkerThread.cpp b/src/concurrency/PosixNotifiedWorkerThread.cpp deleted file mode 100644 index e759a871e..000000000 --- a/src/concurrency/PosixNotifiedWorkerThread.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "PosixNotifiedWorkerThread.h" - -#ifdef __unix__ - -#include - -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 \ No newline at end of file diff --git a/src/concurrency/PosixNotifiedWorkerThread.h b/src/concurrency/PosixNotifiedWorkerThread.h deleted file mode 100644 index d75b74dd8..000000000 --- a/src/concurrency/PosixNotifiedWorkerThread.h +++ /dev/null @@ -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 diff --git a/src/concurrency/PosixThread.h b/src/concurrency/PosixThread.h deleted file mode 100644 index 3f46ebc08..000000000 --- a/src/concurrency/PosixThread.h +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/concurrency/WorkerThread.h b/src/concurrency/WorkerThread.h index c9b2c6b35..841c41458 100644 --- a/src/concurrency/WorkerThread.h +++ b/src/concurrency/WorkerThread.h @@ -2,10 +2,11 @@ #include "OSThread.h" -namespace concurrency { +namespace concurrency +{ /** - * @brief This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting + * @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 @@ -14,16 +15,11 @@ class WorkerThread : public OSThread { protected: /** - * A method that should block execution - either waiting ona queue/mutex or a "task notification" + * Return true if this thread is ready to run - either waiting ona queue/mutex or a "task notification" */ - virtual void block() = 0; + virtual bool shouldRun() = 0; virtual void loop() = 0; - - /** - * The method that will be called when start is called. - */ - virtual void doRun(); }; } // namespace concurrency From 2044427e9732d4b36370e8a05c087deabd042a92 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Fri, 9 Oct 2020 14:16:51 +0800 Subject: [PATCH 3/8] coroutines: wip compiles but does not link --- platformio.ini | 1 + src/Power.cpp | 89 ++++++++++++-------------- src/concurrency/NotifiedWorkerThread.h | 34 ++++++---- src/concurrency/OSThread.h | 23 +++++-- src/concurrency/Periodic.h | 16 ++--- src/concurrency/PeriodicTask.cpp | 16 ----- src/concurrency/PeriodicTask.h | 54 ---------------- src/concurrency/WorkerThread.cpp | 31 --------- src/concurrency/WorkerThread.h | 25 -------- src/gps/NMEAGPS.h | 1 - src/graphics/Screen.cpp | 23 +++---- src/graphics/Screen.h | 8 +-- src/main.cpp | 34 +--------- src/mesh/FloodingRouter.h | 1 - src/mesh/MeshService.cpp | 8 +-- src/mesh/RadioInterface.cpp | 7 +- src/mesh/RadioInterface.h | 2 - src/mesh/RadioLibInterface.cpp | 23 ++----- src/mesh/RadioLibInterface.h | 10 +-- src/mesh/ReliableRouter.h | 5 +- src/mesh/Router.cpp | 6 +- src/mesh/Router.h | 5 +- src/power.h | 9 +-- 23 files changed, 132 insertions(+), 299 deletions(-) delete mode 100644 src/concurrency/PeriodicTask.cpp delete mode 100644 src/concurrency/PeriodicTask.h delete mode 100644 src/concurrency/WorkerThread.cpp delete mode 100644 src/concurrency/WorkerThread.h diff --git a/platformio.ini b/platformio.ini index 0b7ec863e..453c6ecb1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,6 +30,7 @@ build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/n -DHW_VERSION_${sysenv.COUNTRY} -DAPP_VERSION=${sysenv.APP_VERSION} -DHW_VERSION=${sysenv.HW_VERSION} + -DUSE_THREAD_NAMES ; leave this commented out to avoid breaking Windows ;upload_port = /dev/ttyUSB0 diff --git a/src/Power.cpp b/src/Power.cpp index 90e988167..089bbe14c 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -62,6 +62,8 @@ class AnalogBatteryLevel : public HasBatteryLevel virtual bool isBatteryConnect() { return getBattVoltage() != -1; } } analogLevel; +Power::Power() : OSThread("Power") {} + bool Power::analogInit() { #ifdef BATTERY_PIN @@ -86,10 +88,7 @@ bool Power::setup() if (!found) { found = analogInit(); } - if (found) { - concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device - setPeriod(1); - } + enabled = found; return found; } @@ -135,13 +134,46 @@ void Power::readPowerStatus() } } -void Power::doTask() +uint32_t Power::runOnce() { readPowerStatus(); +#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"); + } + axp.clearIRQ(); + } +#endif + // Only read once every 20 seconds once the power status for the app has been initialized - if (statusHandler && statusHandler->isInitialized()) - setPeriod(1000 * 20); + return (statusHandler && statusHandler->isInitialized()) ? (1000 * 20) : 0; } /** @@ -208,8 +240,7 @@ bool Power::axp192Init() // 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 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, - 1); + axp.enableIRQ(AXP202_BATT_CONNECT_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ, 1); axp.clearIRQ(); #endif @@ -226,43 +257,3 @@ bool Power::axp192Init() return false; #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 -} diff --git a/src/concurrency/NotifiedWorkerThread.h b/src/concurrency/NotifiedWorkerThread.h index 2ec1ea78c..6ef11220e 100644 --- a/src/concurrency/NotifiedWorkerThread.h +++ b/src/concurrency/NotifiedWorkerThread.h @@ -1,37 +1,45 @@ #pragma once -#include "WorkerThread.h" +#include "OSThread.h" -namespace concurrency { +namespace concurrency +{ /** * @brief A worker thread that waits on a freertos notification */ -class NotifiedWorkerThread : public WorkerThread +class NotifiedWorkerThread : public OSThread { public: + NotifiedWorkerThread(const char *name) : OSThread(name) {} + /** * Notify this thread so it can run */ - virtual void notify(uint32_t v = 0, eNotifyAction action = eNoAction) = 0; + void notify(uint32_t v, bool overwrite); /** * 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; + void notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite) { notify(v, overwrite); } /** - * A method that should block execution - either waiting ona queue/mutex or a "task notification" + * Schedule a notification to fire in delay msecs */ - virtual void block() = 0; + void notifyLater(uint32_t delay, uint32_t v, bool overwrite); + + protected: + virtual void onNotify(uint32_t notification) = 0; + + virtual uint32_t runOnce(); + + private: + /** + * The notification that was most recently used to wake the thread. Read from runOnce() + */ + uint32_t notification = 0; }; } // namespace concurrency diff --git a/src/concurrency/OSThread.h b/src/concurrency/OSThread.h index 3045ba543..0df86a133 100644 --- a/src/concurrency/OSThread.h +++ b/src/concurrency/OSThread.h @@ -4,11 +4,13 @@ #include #include "Thread.h" -#include "freertosinc.h" +#include "ThreadController.h" namespace concurrency { +extern ThreadController mainController, timerController; + /** * @brief Base threading * @@ -17,23 +19,32 @@ namespace concurrency * sleeping the correct amount of time in main * NotifiedWorkerThread set/clears enabled * + * notifyLater should start now - not relative to last start time + * clear notification before calling handler + * * stopping sleep instantly as soon as an event occurs. * use global functions delayTillWakeEvent(time), doWakeEvent(isInISR) - use freertos mutex or somesuch * + * have router thread block on its message queue in runOnce + * * remove lock/lockguard */ -class OSThread +class OSThread : public Thread { - public: - virtual ~OSThread() {} + ThreadController *controller; - // uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); } + public: + OSThread(const char *name, uint32_t period = 0, ThreadController *controller = &mainController); + + virtual ~OSThread(); protected: /** * The method that will be called each time our thread gets a chance to run + * + * Returns desired period for next invocation (or 0 for no change) */ - virtual void runOnce() = 0; + virtual uint32_t runOnce() = 0; }; } // namespace concurrency diff --git a/src/concurrency/Periodic.h b/src/concurrency/Periodic.h index e380eb34f..da09c1e1d 100644 --- a/src/concurrency/Periodic.h +++ b/src/concurrency/Periodic.h @@ -1,26 +1,24 @@ #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? */ -class Periodic : public PeriodicTask +class Periodic : public OSThread { uint32_t (*callback)(); public: // 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, uint32_t (*_callback)()) : OSThread(name), callback(_callback) {} protected: - void doTask() { - uint32_t p = callback(); - setPeriod(p); - } + uint32_t runOnce() { return callback(); } }; } // namespace concurrency diff --git a/src/concurrency/PeriodicTask.cpp b/src/concurrency/PeriodicTask.cpp deleted file mode 100644 index 40c6a4edd..000000000 --- a/src/concurrency/PeriodicTask.cpp +++ /dev/null @@ -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 diff --git a/src/concurrency/PeriodicTask.h b/src/concurrency/PeriodicTask.h deleted file mode 100644 index 4dd127f5b..000000000 --- a/src/concurrency/PeriodicTask.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include -#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 -{ - 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(); - - protected: - virtual void doTask() = 0; -}; - -} // namespace concurrency diff --git a/src/concurrency/WorkerThread.cpp b/src/concurrency/WorkerThread.cpp deleted file mode 100644 index b2ec18d81..000000000 --- a/src/concurrency/WorkerThread.cpp +++ /dev/null @@ -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 diff --git a/src/concurrency/WorkerThread.h b/src/concurrency/WorkerThread.h deleted file mode 100644 index 841c41458..000000000 --- a/src/concurrency/WorkerThread.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "OSThread.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 OSThread -{ - protected: - /** - * Return true if this thread is ready to run - either waiting ona queue/mutex or a "task notification" - */ - virtual bool shouldRun() = 0; - - virtual void loop() = 0; -}; - -} // namespace concurrency diff --git a/src/gps/NMEAGPS.h b/src/gps/NMEAGPS.h index f411e4d16..2fd29d408 100644 --- a/src/gps/NMEAGPS.h +++ b/src/gps/NMEAGPS.h @@ -1,6 +1,5 @@ #pragma once -#include "../concurrency/PeriodicTask.h" #include "GPS.h" #include "Observer.h" #include "TinyGPS++.h" diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 2ea03c637..cc092e05c 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -561,7 +561,7 @@ void _screen_header() } #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) {} void Screen::handleSetOn(bool on) { @@ -573,9 +573,12 @@ void Screen::handleSetOn(bool on) DEBUG_MSG("Turning on screen\n"); dispdev.displayOn(); dispdev.displayOn(); + enabled = true; + setInterval(0); // Draw ASAP } else { DEBUG_MSG("Turning off screen\n"); dispdev.displayOff(); + enabled = false; } screenOn = on; } @@ -583,8 +586,6 @@ void Screen::handleSetOn(bool on) 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 // 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; @@ -642,12 +643,12 @@ void Screen::setup() nodeStatusObserver.observe(&nodeStatus->onNewStatus); } -void Screen::doTask() +uint32_t Screen::runOnce() { // If we don't have a screen, don't ever spend any CPU for us. if (!useDisplay) { - setPeriod(0); - return; + enabled = false; + return 0; } // Process incoming commands. @@ -684,8 +685,8 @@ void Screen::doTask() if (!screenOn) { // If we didn't just wake and the screen is still off, then // stop updating until it is on again - setPeriod(0); - return; + enabled = false; + return 0; } // Switch to a low framerate (to save CPU) when we are not in transition @@ -711,7 +712,7 @@ void Screen::doTask() // 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 // the correct framerate - setPeriod(1000 / targetFramerate); + return (1000 / targetFramerate); } void Screen::drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) @@ -801,7 +802,7 @@ void Screen::handleOnPress() // 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 (ui.getUiState()->frameState == FIXED) { - setPeriod(1); // redraw ASAP + setInterval(0); // redraw ASAP ui.nextFrame(); DEBUG_MSG("Setting fast framerate\n"); @@ -1062,7 +1063,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg) if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) { setFrames(); // Regen the list of screens prevFrame = -1; // Force a GUI update - setPeriod(1); // Update the screen right away + setInterval(0); // Update the screen right away } nodeDB.updateGUI = false; nodeDB.updateTextMessage = false; diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 6a7e0b524..5c01aacea 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -15,7 +15,7 @@ #include "TypedQueue.h" #include "commands.h" #include "concurrency/LockGuard.h" -#include "concurrency/PeriodicTask.h" +#include "concurrency/OSThread.h" #include "power.h" #include @@ -62,7 +62,7 @@ class DebugInfo * multiple times simultaneously. All state-changing calls are queued and executed * when the main loop calls us. */ -class Screen : public concurrency::PeriodicTask +class Screen : public concurrency::OSThread { CallbackObserver powerStatusObserver = CallbackObserver(this, &Screen::handleStatusUpdate); @@ -184,7 +184,7 @@ class Screen : public concurrency::PeriodicTask /// Updates the UI. // // Called periodically from the main loop. - void doTask() final; + uint32_t runOnce() final; private: struct ScreenCmd { @@ -202,7 +202,7 @@ class Screen : public concurrency::PeriodicTask return true; // claim success if our display is not in use else { bool success = cmdQueue.enqueue(cmd, 0); - setPeriod(1); // handle ASAP + setInterval(0); // handle ASAP return success; } } diff --git a/src/main.cpp b/src/main.cpp index 1e1f1e014..17d552084 100644 --- a/src/main.cpp +++ b/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 - - 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 . - -*/ #include "Air530GPS.h" #include "MeshRadio.h" @@ -27,7 +5,6 @@ #include "NodeDB.h" #include "PowerFSM.h" #include "UBloxGPS.h" -#include "concurrency/Periodic.h" #include "configuration.h" #include "error.h" #include "power.h" @@ -36,6 +13,7 @@ // #include "debug.h" #include "RTC.h" #include "SPILock.h" +#include "concurrency/Periodic.h" #include "graphics/Screen.h" #include "main.h" #include "meshwifi/meshhttp.h" @@ -134,7 +112,7 @@ static uint32_t ledBlinker() return powerStatus->getIsCharging() ? 1000 : (ledOn ? 2 : 1000); } -concurrency::Periodic ledPeriodic(ledBlinker); +concurrency::Periodic ledPeriodic("Blink", ledBlinker); // Prepare for button presses #ifdef BUTTON_PIN @@ -203,8 +181,6 @@ void setup() digitalWrite(LED_PIN, 1 ^ LED_INVERTED); // turn on for now #endif - ledPeriodic.setup(); - // Hello DEBUG_MSG("Meshtastic swver=%s, hwver=%s\n", optstr(APP_VERSION), optstr(HW_VERSION)); @@ -378,11 +354,8 @@ void 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(); #ifdef DEBUG_PORT @@ -394,9 +367,6 @@ void loop() #ifndef NO_ESP32 esp32Loop(); #endif -#ifdef TBEAM_V10 - power->loop(); -#endif #ifdef BUTTON_PIN userButton.tick(); diff --git a/src/mesh/FloodingRouter.h b/src/mesh/FloodingRouter.h index b35c3a484..ca7ee489f 100644 --- a/src/mesh/FloodingRouter.h +++ b/src/mesh/FloodingRouter.h @@ -1,7 +1,6 @@ #pragma once #include "PacketHistory.h" -#include "../concurrency/PeriodicTask.h" #include "Router.h" /** diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 399d9690b..d4ea6d93c 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -56,7 +56,7 @@ static uint32_t sendOwnerCb() return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000; } -static concurrency::Periodic sendOwnerPeriod(sendOwnerCb); +static concurrency::Periodic sendOwnerPeriod("SendOwner", sendOwnerCb); MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE) { @@ -65,7 +65,6 @@ MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE) void MeshService::init() { - sendOwnerPeriod.setup(); nodeDB.init(); if (gps) @@ -227,7 +226,7 @@ void MeshService::handleToRadio(MeshPacket &p) p.id = generatePacketId(); // If the phone didn't supply one, then pick one 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 @@ -285,7 +284,8 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies) p->decoded.which_payload = SubPacket_position_tag; p->decoded.position = node->position; p->decoded.want_response = wantReplies; - p->decoded.position.time = getValidTime(RTCQualityGPS); // 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); } diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 4ab071036..28e6b8c88 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -91,7 +91,7 @@ void printPacket(const char *prefix, const MeshPacket *p) DEBUG_MSG(")\n"); } -RadioInterface::RadioInterface() +RadioInterface::RadioInterface() : NotifiedWorkerThread("RadioIf") { 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 // radioIf.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor // 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; } diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 645ee5c05..70c5a090a 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -117,8 +117,6 @@ class RadioInterface : protected concurrency::NotifiedWorkerThread */ size_t beginSending(MeshPacket *p); - virtual void loop() {} // Idle processing - /** * Some regulatory regions limit xmit power. * This function should be called by subclasses after setting their desired power. It might lower it diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 30f7de50e..e7835deb9 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -19,7 +19,7 @@ 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, SPIClass &spi, PhysicalLayer *_iface) - : concurrency::PeriodicTask(0), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface) + : module(cs, irq, rst, busy, spi, spiSettings), iface(_iface) { instance = this; } @@ -41,7 +41,6 @@ void INTERRUPT_ATTR RadioLibInterface::isrLevel0Common(PendingISR cause) { instance->disableInterrupt(); - instance->pending = cause; BaseType_t xHigherPriorityTaskWoken; instance->notifyFromISR(&xHigherPriorityTaskWoken, cause, eSetValueWithOverwrite); @@ -191,10 +190,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. */ -void RadioLibInterface::loop() +void RadioLibInterface::onNotify(uint32_t notification) { - pending = ISR_NONE; - switch (notification) { case ISR_TX: handleTransmitInterrupt(); @@ -229,25 +226,15 @@ 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) { // 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 = !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("delaying %u\n", delay); - setPeriod(delay); + DEBUG_MSG("delaying %u\n", delay); + notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable } } diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index b1d62bdb7..80a4f191c 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -1,6 +1,6 @@ #pragma once -#include "../concurrency/PeriodicTask.h" +#include "../concurrency/OSThread.h" #include "RadioInterface.h" #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); }; -class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTask +class RadioLibInterface : public RadioInterface { /// Used as our notification from the ISR 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 */ @@ -155,7 +153,7 @@ class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTa static void timerCallback(void *p1, uint32_t p2); - virtual void doTask(); + virtual void onNotify(uint32_t notification); /** start an immediate transmit * This method is virtual so subclasses can hook as needed, subclasses should not call directly @@ -195,7 +193,5 @@ class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTa */ virtual void addReceiveMetadata(MeshPacket *mp) = 0; - virtual void loop(); // Idle processing - virtual void setStandby() = 0; }; \ No newline at end of file diff --git a/src/mesh/ReliableRouter.h b/src/mesh/ReliableRouter.h index f2e8774d8..da875ced3 100644 --- a/src/mesh/ReliableRouter.h +++ b/src/mesh/ReliableRouter.h @@ -1,7 +1,6 @@ #pragma once #include "FloodingRouter.h" -#include "../concurrency/PeriodicTask.h" #include /** @@ -80,10 +79,10 @@ class ReliableRouter : public FloodingRouter virtual ErrorCode send(MeshPacket *p); /** Do our retransmission handling */ - virtual void loop() + virtual uint32_t runOnce() { doRetransmissions(); - FloodingRouter::loop(); + return FloodingRouter::runOnce(); } protected: diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index e352a5b25..cd6005c91 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -34,7 +34,7 @@ Allocator &packetPool = staticPool; * * 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 @@ -47,12 +47,14 @@ Router::Router() : fromRadioQueue(MAX_RX_FROMRADIO) * do idle processing * Mostly looking in our incoming rxPacket queue and calling handleReceived. */ -void Router::loop() +uint32_t Router::runOnce() { MeshPacket *mp; while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) { perhapsHandleReceived(mp); } + + return 0; } /// Generate a unique packet id diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 6903b7d4e..6538cedab 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -5,12 +5,13 @@ #include "Observer.h" #include "PointerQueue.h" #include "RadioInterface.h" +#include "concurrency/OSThread.h" #include "mesh.pb.h" /** * A mesh aware router that supports multiple interfaces. */ -class Router +class Router : protected concurrency::OSThread { private: RadioInterface *iface; @@ -44,7 +45,7 @@ class Router * do idle processing * Mostly looking in our incoming rxPacket queue and calling handleReceived. */ - virtual void loop(); + virtual uint32_t runOnce(); /** * Works like send, but if we are sending to the local node, we directly put the message in the receive queue diff --git a/src/power.h b/src/power.h index a779089ad..b7cf93fc5 100644 --- a/src/power.h +++ b/src/power.h @@ -1,6 +1,6 @@ #pragma once #include "PowerStatus.h" -#include "concurrency/PeriodicTask.h" +#include "concurrency/OSThread.h" /** * Per @spattinson @@ -15,16 +15,17 @@ #define BAT_MILLIVOLTS_FULL 4100 #define BAT_MILLIVOLTS_EMPTY 3500 -class Power : public concurrency::PeriodicTask +class Power : private concurrency::OSThread { public: Observable newStatus; + Power(); + void readPowerStatus(); - void loop(); virtual bool setup(); - virtual void doTask(); + virtual uint32_t runOnce(); void setStatusHandler(meshtastic::PowerStatus *handler) { statusHandler = handler; } protected: From c46a88455878317e1232d58def6b9c915a6cc828 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 10 Oct 2020 08:28:00 +0800 Subject: [PATCH 4/8] concurrency wip --- src/concurrency/NotifiedWorkerThread.cpp | 35 ++++++++++++++++++++++++ src/concurrency/NotifiedWorkerThread.h | 13 ++++----- src/concurrency/OSThread.cpp | 33 ++++++++++++++++++++++ src/concurrency/OSThread.h | 9 ++++++ src/main.cpp | 5 ++++ 5 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 src/concurrency/NotifiedWorkerThread.cpp diff --git a/src/concurrency/NotifiedWorkerThread.cpp b/src/concurrency/NotifiedWorkerThread.cpp new file mode 100644 index 000000000..ae9f47ab6 --- /dev/null +++ b/src/concurrency/NotifiedWorkerThread.cpp @@ -0,0 +1,35 @@ +#include "NotifiedWorkerThread.h" +#include + +namespace concurrency +{ + +/** + * Notify this thread so it can run + */ +IRAM_ATTR void NotifiedWorkerThread::notify(uint32_t v, bool overwrite) { + +} + +/** + * Notify from an ISR + * + * This must be inline or IRAM_ATTR on ESP32 + */ +IRAM_ATTR void NotifiedWorkerThread::notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite) +{ + notify(v, overwrite); +} + +/** + * Schedule a notification to fire in delay msecs + */ +void NotifiedWorkerThread::notifyLater(uint32_t delay, uint32_t v, bool overwrite) { + +} + +uint32_t NotifiedWorkerThread::runOnce() { + +} + +} // namespace concurrency \ No newline at end of file diff --git a/src/concurrency/NotifiedWorkerThread.h b/src/concurrency/NotifiedWorkerThread.h index 6ef11220e..87f7ba83b 100644 --- a/src/concurrency/NotifiedWorkerThread.h +++ b/src/concurrency/NotifiedWorkerThread.h @@ -10,6 +10,11 @@ namespace concurrency */ class NotifiedWorkerThread : public OSThread { + /** + * The notification that was most recently used to wake the thread. Read from runOnce() + */ + uint32_t notification = 0; + public: NotifiedWorkerThread(const char *name) : OSThread(name) {} @@ -23,7 +28,7 @@ class NotifiedWorkerThread : public OSThread * * This must be inline or IRAM_ATTR on ESP32 */ - void notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite) { notify(v, overwrite); } + void notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite); /** * Schedule a notification to fire in delay msecs @@ -34,12 +39,6 @@ class NotifiedWorkerThread : public OSThread virtual void onNotify(uint32_t notification) = 0; virtual uint32_t runOnce(); - - private: - /** - * The notification that was most recently used to wake the thread. Read from runOnce() - */ - uint32_t notification = 0; }; } // namespace concurrency diff --git a/src/concurrency/OSThread.cpp b/src/concurrency/OSThread.cpp index 0569567c5..7efb0b275 100644 --- a/src/concurrency/OSThread.cpp +++ b/src/concurrency/OSThread.cpp @@ -4,4 +4,37 @@ namespace concurrency { +ThreadController mainController, timerController; + +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); +} + +void OSThread::run() +{ + auto newDelay = runOnce(); + + runned(); + + if (newDelay != 0) + setInterval(newDelay); +} + } // namespace concurrency diff --git a/src/concurrency/OSThread.h b/src/concurrency/OSThread.h index 0df86a133..47bc1a547 100644 --- a/src/concurrency/OSThread.h +++ b/src/concurrency/OSThread.h @@ -25,6 +25,10 @@ extern ThreadController mainController, timerController; * stopping sleep instantly as soon as an event occurs. * use global functions delayTillWakeEvent(time), doWakeEvent(isInISR) - use freertos mutex or somesuch * + * make everything use osthread + * + * Debug what is keeping us from sleeping + * * have router thread block on its message queue in runOnce * * remove lock/lockguard @@ -38,6 +42,8 @@ class OSThread : public Thread virtual ~OSThread(); + static void setup(); + protected: /** * The method that will be called each time our thread gets a chance to run @@ -45,6 +51,9 @@ class OSThread : public Thread * Returns desired period for next invocation (or 0 for no change) */ virtual uint32_t runOnce() = 0; + + // Do not override this + virtual void run(); }; } // namespace concurrency diff --git a/src/main.cpp b/src/main.cpp index 17d552084..ba197074a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,6 +13,7 @@ // #include "debug.h" #include "RTC.h" #include "SPILock.h" +#include "concurrency/OSThread.h" #include "concurrency/Periodic.h" #include "graphics/Screen.h" #include "main.h" @@ -155,6 +156,8 @@ void setup() digitalWrite(RESET_OLED, 1); #endif + concurrency::OSThread::setup(); + #ifdef I2C_SDA Wire.begin(I2C_SDA, I2C_SCL); #else @@ -375,6 +378,8 @@ void loop() userButtonAlt.tick(); #endif + concurrency::mainController.run(); + loopWifi(); // For debugging From 49b4ed2a89db5a7676bfbffe5d2dafd6281c95ee Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 10 Oct 2020 09:57:57 +0800 Subject: [PATCH 5/8] coroutine: kinda works now --- docs/software/power.md | 2 +- src/Power.cpp | 4 +- src/PowerFSM.cpp | 14 ++-- src/concurrency/InterruptableDelay.cpp | 45 +++++++++++++ src/concurrency/InterruptableDelay.h | 33 +++++++++ src/concurrency/NotifiedWorkerThread.cpp | 62 +++++++++++++++-- src/concurrency/NotifiedWorkerThread.h | 14 ++-- src/concurrency/OSThread.cpp | 41 ++++++++++- src/concurrency/OSThread.h | 46 +++++++++---- src/concurrency/Periodic.h | 6 +- src/esp32/WiFiServerAPI.cpp | 9 ++- src/esp32/WiFiServerAPI.h | 5 +- src/gps/GPS.cpp | 6 +- src/gps/GPS.h | 9 ++- src/graphics/Screen.cpp | 18 ++++- src/graphics/Screen.h | 2 +- src/main.cpp | 86 +++++++++++++----------- src/main.h | 18 ++--- src/mesh/MeshService.cpp | 18 ++--- src/mesh/RadioLibInterface.cpp | 13 ++-- src/mesh/RadioLibInterface.h | 4 -- src/mesh/ReliableRouter.cpp | 11 ++- src/mesh/ReliableRouter.h | 13 ++-- src/mesh/Router.cpp | 6 +- src/mesh/Router.h | 4 +- src/mesh/TypedQueue.h | 28 +++++++- src/meshwifi/meshwifi.cpp | 8 +-- src/meshwifi/meshwifi.h | 3 - src/nimble/BluetoothUtil.cpp | 10 +-- src/power.h | 2 +- src/sleep.cpp | 2 +- 31 files changed, 392 insertions(+), 150 deletions(-) create mode 100644 src/concurrency/InterruptableDelay.cpp create mode 100644 src/concurrency/InterruptableDelay.h diff --git a/docs/software/power.md b/docs/software/power.md index e98e5bd0d..4550350c4 100644 --- a/docs/software/power.md +++ b/docs/software/power.md @@ -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 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 onEntry: setBluetooth off, screen on diff --git a/src/Power.cpp b/src/Power.cpp index 089bbe14c..162047fa3 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -134,7 +134,7 @@ void Power::readPowerStatus() } } -uint32_t Power::runOnce() +int32_t Power::runOnce() { readPowerStatus(); @@ -173,7 +173,7 @@ uint32_t Power::runOnce() #endif // Only read once every 20 seconds once the power status for the app has been initialized - return (statusHandler && statusHandler->isInitialized()) ? (1000 * 20) : 0; + return (statusHandler && statusHandler->isInitialized()) ? (1000 * 20) : RUN_SAME; } /** diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 9a949638a..ede68258c 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -22,7 +22,7 @@ static uint32_t secsSlept; static void lsEnter() { 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 DEBUG_MSG("lsEnter end\n"); @@ -102,7 +102,7 @@ static void lsExit() static void nbEnter() { - screen.setOn(false); + screen->setOn(false); setBluetoothEnable(false); // FIXME - check if we already have packets for phone and immediately trigger EVENT_PACKETS_FOR_PHONE @@ -111,24 +111,24 @@ static void nbEnter() static void darkEnter() { setBluetoothEnable(true); - screen.setOn(false); + screen->setOn(false); } static void serialEnter() { setBluetoothEnable(false); - screen.setOn(true); + screen->setOn(true); } static void powerEnter() { - screen.setOn(true); + screen->setOn(true); setBluetoothEnable(true); } static void onEnter() { - screen.setOn(true); + screen->setOn(true); setBluetoothEnable(true); static uint32_t lastPingMs; @@ -144,7 +144,7 @@ static void onEnter() static void screenPress() { - screen.onPress(); + screen->onPress(); } static void bootEnter() {} diff --git a/src/concurrency/InterruptableDelay.cpp b/src/concurrency/InterruptableDelay.cpp new file mode 100644 index 000000000..dd7d6f86e --- /dev/null +++ b/src/concurrency/InterruptableDelay.cpp @@ -0,0 +1,45 @@ +#include "concurrency/InterruptableDelay.h" +#include "configuration.h" + +namespace concurrency +{ + +InterruptableDelay::InterruptableDelay() +{ + semaphore = xSemaphoreCreateBinary(); +} + +InterruptableDelay::~InterruptableDelay() +{ + vSemaphoreDelete(semaphore); +} + +/** + * Returns false if we were interrupted + */ +bool InterruptableDelay::delay(uint32_t msec) +{ + if (msec) { + DEBUG_MSG("delay %u ", msec); + + // sem take will return false if we timed out (i.e. were not interrupted) + bool r = xSemaphoreTake(semaphore, pdMS_TO_TICKS(msec)); + + DEBUG_MSG("interrupt=%d\n", r); + return !r; + } else { + return true; + } +} + +void InterruptableDelay::interrupt() +{ + xSemaphoreGive(semaphore); +} + +IRAM_ATTR void InterruptableDelay::interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken) +{ + xSemaphoreGiveFromISR(semaphore, pxHigherPriorityTaskWoken); +} + +} // namespace concurrency \ No newline at end of file diff --git a/src/concurrency/InterruptableDelay.h b/src/concurrency/InterruptableDelay.h new file mode 100644 index 000000000..b6428b25a --- /dev/null +++ b/src/concurrency/InterruptableDelay.h @@ -0,0 +1,33 @@ +#pragma once + +#include "../freertosinc.h" + +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 +{ + SemaphoreHandle_t semaphore; + + public: + InterruptableDelay(); + ~InterruptableDelay(); + + /** + * Returns false if we were interrupted + */ + bool delay(uint32_t msec); + + void interrupt(); + + void interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken); +}; + +} // namespace concurrency \ No newline at end of file diff --git a/src/concurrency/NotifiedWorkerThread.cpp b/src/concurrency/NotifiedWorkerThread.cpp index ae9f47ab6..aaac07f59 100644 --- a/src/concurrency/NotifiedWorkerThread.cpp +++ b/src/concurrency/NotifiedWorkerThread.cpp @@ -1,14 +1,43 @@ #include "NotifiedWorkerThread.h" +#include "configuration.h" #include namespace concurrency { +static bool debugNotification; + /** * Notify this thread so it can run */ -IRAM_ATTR void NotifiedWorkerThread::notify(uint32_t v, bool overwrite) { - +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; + } } /** @@ -16,20 +45,41 @@ IRAM_ATTR void NotifiedWorkerThread::notify(uint32_t v, bool overwrite) { * * This must be inline or IRAM_ATTR on ESP32 */ -IRAM_ATTR void NotifiedWorkerThread::notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite) +IRAM_ATTR bool NotifiedWorkerThread::notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite) { - notify(v, overwrite); + bool r = notifyCommon(v, overwrite); + if (r) + mainDelay.interruptFromISR(highPriWoken); + + return r; } /** * Schedule a notification to fire in delay msecs */ -void NotifiedWorkerThread::notifyLater(uint32_t delay, uint32_t v, bool overwrite) { +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; } -uint32_t NotifiedWorkerThread::runOnce() { +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 \ No newline at end of file diff --git a/src/concurrency/NotifiedWorkerThread.h b/src/concurrency/NotifiedWorkerThread.h index 87f7ba83b..cdb37c35b 100644 --- a/src/concurrency/NotifiedWorkerThread.h +++ b/src/concurrency/NotifiedWorkerThread.h @@ -21,24 +21,30 @@ class NotifiedWorkerThread : public OSThread /** * Notify this thread so it can run */ - void notify(uint32_t v, bool overwrite); + bool notify(uint32_t v, bool overwrite); /** * Notify from an ISR * * This must be inline or IRAM_ATTR on ESP32 */ - void notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite); + bool notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite); /** * Schedule a notification to fire in delay msecs */ - void notifyLater(uint32_t delay, uint32_t v, bool overwrite); + bool notifyLater(uint32_t delay, uint32_t v, bool overwrite); protected: virtual void onNotify(uint32_t notification) = 0; - virtual uint32_t runOnce(); + virtual int32_t runOnce(); + + private: + /** + * Notify this thread so it can run + */ + bool notifyCommon(uint32_t v, bool overwrite); }; } // namespace concurrency diff --git a/src/concurrency/OSThread.cpp b/src/concurrency/OSThread.cpp index 7efb0b275..916e68e15 100644 --- a/src/concurrency/OSThread.cpp +++ b/src/concurrency/OSThread.cpp @@ -1,10 +1,21 @@ #include "OSThread.h" +#include "configuration.h" #include 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() { @@ -27,13 +38,41 @@ OSThread::~OSThread() 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) + if (newDelay >= 0) setInterval(newDelay); } diff --git a/src/concurrency/OSThread.h b/src/concurrency/OSThread.h index 47bc1a547..3c0c62dc9 100644 --- a/src/concurrency/OSThread.h +++ b/src/concurrency/OSThread.h @@ -5,52 +5,72 @@ #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 * * TODO FIXME @geeksville - * basic functionality - * sleeping the correct amount of time in main - * NotifiedWorkerThread set/clears enabled * - * notifyLater should start now - not relative to last start time - * clear notification before calling handler - * - * stopping sleep instantly as soon as an event occurs. - * use global functions delayTillWakeEvent(time), doWakeEvent(isInISR) - use freertos mutex or somesuch + * make bluetooth wake cpu immediately (because it puts a message in a queue?) + * + * don't sleep at all if in POWER mode + * + * wake for serial character received * + * add concept of 'low priority' threads that are not used to block sleep? + * * make everything use osthread * - * Debug what is keeping us from sleeping - * - * have router thread block on its message queue in runOnce + * if we wake once because of a ble packet we might need to run loop multiple times before we can truely sleep * * 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 0 for no change) + * Returns desired period for next invocation (or RUN_SAME for no change) */ - virtual uint32_t runOnce() = 0; + virtual int32_t runOnce() = 0; // Do not override this virtual void run(); diff --git a/src/concurrency/Periodic.h b/src/concurrency/Periodic.h index da09c1e1d..bf60280de 100644 --- a/src/concurrency/Periodic.h +++ b/src/concurrency/Periodic.h @@ -11,14 +11,14 @@ namespace concurrency */ class Periodic : public OSThread { - uint32_t (*callback)(); + int32_t (*callback)(); public: // callback returns the period for the next callback invocation (or 0 if we should no longer be called) - Periodic(const char *name, uint32_t (*_callback)()) : OSThread(name), callback(_callback) {} + Periodic(const char *name, int32_t (*_callback)()) : OSThread(name), callback(_callback) {} protected: - uint32_t runOnce() { return callback(); } + int32_t runOnce() { return callback(); } }; } // namespace concurrency diff --git a/src/esp32/WiFiServerAPI.cpp b/src/esp32/WiFiServerAPI.cpp index aa5963882..632116bf3 100644 --- a/src/esp32/WiFiServerAPI.cpp +++ b/src/esp32/WiFiServerAPI.cpp @@ -40,7 +40,7 @@ void WiFiServerAPI::loop() #define MESHTASTIC_PORTNUM 4403 -WiFiServerPort::WiFiServerPort() : WiFiServer(MESHTASTIC_PORTNUM) {} +WiFiServerPort::WiFiServerPort() : WiFiServer(MESHTASTIC_PORTNUM), concurrency::OSThread("ApiServer") {} void WiFiServerPort::init() { @@ -48,7 +48,7 @@ void WiFiServerPort::init() begin(); } -void WiFiServerPort::loop() +int32_t WiFiServerPort::runOnce() { auto client = available(); if (client) { @@ -59,7 +59,10 @@ void WiFiServerPort::loop() openAPI = new WiFiServerAPI(client); } - if (openAPI) + if (openAPI) { // Allow idle processing so the API can read from its incoming stream openAPI->loop(); + return 0; // run fast while our API server is running + } else + return 100; // only check occasionally for incoming connections } \ No newline at end of file diff --git a/src/esp32/WiFiServerAPI.h b/src/esp32/WiFiServerAPI.h index a9b1e39b4..19f99cbc5 100644 --- a/src/esp32/WiFiServerAPI.h +++ b/src/esp32/WiFiServerAPI.h @@ -1,6 +1,7 @@ #pragma once #include "StreamAPI.h" +#include "concurrency/OSThread.h" #include /** @@ -27,7 +28,7 @@ class WiFiServerAPI : public StreamAPI /** * 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 * @@ -41,5 +42,5 @@ class WiFiServerPort : public WiFiServer void init(); - void loop(); + int32_t runOnce(); }; diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index b312ead16..f5bd9f3f0 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -143,7 +143,7 @@ void GPS::publishUpdate() } } -void GPS::loop() +int32_t GPS::runOnce() { if (whileIdle()) { // if we have received valid NMEA claim we are connected @@ -201,6 +201,10 @@ void GPS::loop() // 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) diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 5f10f2af6..7e752f509 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -2,6 +2,7 @@ #include "GPSStatus.h" #include "Observer.h" +#include "concurrency/OSThread.h" // Generate a string representation of DOP const char *getDOPString(uint32_t dop); @@ -11,7 +12,7 @@ const char *getDOPString(uint32_t dop); * * When new data is available it will notify observers. */ -class GPS +class GPS : private concurrency::OSThread { private: uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastWhileActiveMsec = 0; @@ -43,6 +44,8 @@ class GPS // scaling before use) uint32_t heading = 0; // Heading of motion, in degrees * 10^-5 + GPS() : concurrency::OSThread("GPS") {} + virtual ~GPS() {} // FIXME, we really should unregister our sleep observer /** We will notify this observable anytime GPS state has changed meaningfully */ @@ -53,8 +56,6 @@ class GPS */ virtual bool setup(); - virtual void loop(); - /// Returns ture if we have acquired GPS lock. bool hasLock() const { return hasValidLocation; } @@ -135,6 +136,8 @@ class GPS * Tell users we have new GPS readings */ void publishUpdate(); + + virtual int32_t runOnce(); }; extern GPS *gps; diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index cc092e05c..9e35b4f12 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -561,7 +561,9 @@ void _screen_header() } #endif -Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), 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) { @@ -643,14 +645,24 @@ void Screen::setup() nodeStatusObserver.observe(&nodeStatus->onNewStatus); } -uint32_t Screen::runOnce() +int32_t Screen::runOnce() { // If we don't have a screen, don't ever spend any CPU for us. if (!useDisplay) { enabled = false; - return 0; + 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. for (;;) { ScreenCmd cmd; diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 5c01aacea..96e18ac5b 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -184,7 +184,7 @@ class Screen : public concurrency::OSThread /// Updates the UI. // // Called periodically from the main loop. - uint32_t runOnce() final; + int32_t runOnce() final; private: struct ScreenCmd { diff --git a/src/main.cpp b/src/main.cpp index ba197074a..0bdb29205 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,8 +36,10 @@ #include "variant.h" #endif +using namespace concurrency; + // 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 meshtastic::PowerStatus *powerStatus = new meshtastic::PowerStatus(); @@ -51,8 +53,8 @@ meshtastic::NodeStatus *nodeStatus = new meshtastic::NodeStatus(); bool ssd1306_found; bool axp192_found; -DSRRouter realRouter; -Router &router = realRouter; // Users of router don't care what sort of subclass implements that API + +Router *router = NULL; // Users of router don't care what sort of subclass implements that API // ----------------------------------------------------------------------------- // Application @@ -102,7 +104,7 @@ const char *getDeviceName() return name; } -static uint32_t ledBlinker() +static int32_t ledBlinker() { static bool ledOn; ledOn ^= 1; @@ -110,10 +112,10 @@ static uint32_t ledBlinker() setLed(ledOn); // 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("Blink", ledBlinker); +static Periodic *ledPeriodic; // Prepare for button presses #ifdef BUTTON_PIN @@ -128,7 +130,22 @@ void userButtonPressed() } void userButtonPressedLong() { - screen.adjustBrightness(); + screen->adjustBrightness(); +} + +/** + * 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); } RadioInterface *rIf = NULL; @@ -156,7 +173,11 @@ void setup() digitalWrite(RESET_OLED, 1); #endif - concurrency::OSThread::setup(); + OSThread::setup(); + + ledPeriodic = new Periodic("Blink", ledBlinker); + + router = new DSRRouter(); #ifdef I2C_SDA Wire.begin(I2C_SDA, I2C_SCL); @@ -173,11 +194,13 @@ void setup() 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 #ifdef LED_PIN pinMode(LED_PIN, OUTPUT); @@ -216,14 +239,15 @@ void setup() #endif // 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) - screen.setup(); + screen->setup(); #else if (ssd1306_found) - screen.setup(); + screen->setup(); #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) @@ -321,7 +345,7 @@ void setup() if (!rIf) recordCriticalError(ErrNoRadio); 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 PowerFSM_setup(); // we will transition to ON in a couple of seconds, FIXME, only do this for cold boots, not waking from SDS @@ -347,16 +371,12 @@ uint32_t axpDebugRead() return 30 * 1000; } -concurrency::Periodic axpDebugOutput(axpDebugRead); +Periodic axpDebugOutput(axpDebugRead); axpDebugOutput.setup(); #endif 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 powerFSM.run_machine(); // axpDebugOutput.loop(); @@ -378,20 +398,9 @@ void loop() userButtonAlt.tick(); #endif - concurrency::mainController.run(); - - loopWifi(); - // For debugging // if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving(); - // 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 static uint32_t lastPrint = 0; if (millis() - lastPrint > 10 * 1000L) { @@ -400,19 +409,16 @@ void loop() } #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. 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); } diff --git a/src/main.h b/src/main.h index 366ef8610..bb2635188 100644 --- a/src/main.h +++ b/src/main.h @@ -1,28 +1,24 @@ #pragma once -#include "graphics/Screen.h" -#include "PowerStatus.h" #include "GPSStatus.h" #include "NodeStatus.h" +#include "PowerStatus.h" +#include "graphics/Screen.h" extern bool axp192_found; extern bool ssd1306_found; extern bool isCharging; extern bool isUSBPowered; - - // Global Screen singleton. -extern graphics::Screen screen; -//extern Observable newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class +extern graphics::Screen *screen; +// extern Observable newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class -//extern meshtastic::PowerStatus *powerStatus; -//extern meshtastic::GPSStatus *gpsStatus; -//extern meshtastic::NodeStatusHandler *nodeStatusHandler; +// extern meshtastic::PowerStatus *powerStatus; +// extern meshtastic::GPSStatus *gpsStatus; +// extern meshtastic::NodeStatusHandler *nodeStatusHandler; // Return a human readable string of the form "Meshtastic_ab13" const char *getDeviceName(); - - void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(); diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 0eed139d5..5be9feb8c 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -49,14 +49,14 @@ MeshService service; #include "Router.h" -static uint32_t sendOwnerCb() +static int32_t sendOwnerCb() { service.sendOurOwner(); return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000; } -static concurrency::Periodic sendOwnerPeriod("SendOwner", sendOwnerCb); +static concurrency::Periodic *sendOwnerPeriod; MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE) { @@ -65,16 +65,18 @@ MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE) void MeshService::init() { + sendOwnerPeriod = new concurrency::Periodic("SendOwner", sendOwnerCb); + nodeDB.init(); if (gps) gpsObserver.observe(&gps->newStatus); - packetReceivedObserver.observe(&router.notifyPacketReceived); + packetReceivedObserver.observe(&router->notifyPacketReceived); } void MeshService::sendOurOwner(NodeNum dest, bool wantReplies) { - MeshPacket *p = router.allocForSending(); + MeshPacket *p = router->allocForSending(); p->to = dest; p->decoded.want_response = wantReplies; p->decoded.which_payload = SubPacket_user_tag; @@ -121,7 +123,7 @@ const MeshPacket *MeshService::handleFromRadioUser(const MeshPacket *mp) sendOurOwner(mp->from); String lcd = String("Joined: ") + mp->decoded.user.long_name + "\n"; - screen.print(lcd.c_str()); + screen->print(lcd.c_str()); } return mp; @@ -257,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 - router.sendLocal(p); + router->sendLocal(p); } void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies) @@ -279,7 +281,7 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies) assert(node->has_position); // 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->decoded.which_payload = SubPacket_position_tag; p->decoded.position = node->position; @@ -293,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) - MeshPacket *p = router.allocForSending(); + MeshPacket *p = router->allocForSending(); p->decoded.which_payload = SubPacket_position_tag; Position &pos = p->decoded.position; diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index e7835deb9..5dab06e00 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -24,12 +24,6 @@ RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq instance = this; } -bool RadioLibInterface::init() -{ - setup(); // init our timer - return RadioInterface::init(); -} - #ifndef NO_ESP32 // ESP32 doesn't use that flag #define YIELD_FROM_ISR(x) portYIELD_FROM_ISR() @@ -42,7 +36,7 @@ void INTERRUPT_ATTR RadioLibInterface::isrLevel0Common(PendingISR cause) instance->disableInterrupt(); BaseType_t xHigherPriorityTaskWoken; - instance->notifyFromISR(&xHigherPriorityTaskWoken, cause, eSetValueWithOverwrite); + instance->notifyFromISR(&xHigherPriorityTaskWoken, cause, true); /* 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 @@ -206,6 +200,8 @@ void RadioLibInterface::onNotify(uint32_t notification) startTransmitTimer(); break; 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 // has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode? if (!txQueue.isEmpty()) { @@ -232,8 +228,7 @@ void RadioLibInterface::startTransmitTimer(bool withDelay) if (!txQueue.isEmpty()) { uint32_t delay = !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("delaying %u\n", delay); + // DEBUG_MSG("xmit timer %d\n", delay); notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable } } diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index 80a4f191c..755935e26 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -161,10 +161,6 @@ class RadioLibInterface : public RadioInterface virtual void startSend(MeshPacket *txp); 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 */ virtual void configHardwareForSend() {} diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index acec6b7f9..f3bf886b4 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -160,9 +160,10 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p) /** * 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(); + int32_t d = INT32_MAX; // FIXME, we should use a better datastructure rather than walking through this map. // for(auto el: pending) { @@ -192,5 +193,13 @@ void ReliableRouter::doRetransmissions() p.setNextTx(); } } + else { + // Not yet time + int32_t t = p.nextTxMsec - now; + + d = min(t, d); + } } + + return d; } \ No newline at end of file diff --git a/src/mesh/ReliableRouter.h b/src/mesh/ReliableRouter.h index da875ced3..caa5f1a55 100644 --- a/src/mesh/ReliableRouter.h +++ b/src/mesh/ReliableRouter.h @@ -79,10 +79,13 @@ class ReliableRouter : public FloodingRouter virtual ErrorCode send(MeshPacket *p); /** Do our retransmission handling */ - virtual uint32_t runOnce() + virtual int32_t runOnce() { - doRetransmissions(); - return FloodingRouter::runOnce(); + auto d = FloodingRouter::runOnce(); + + int32_t r = doRetransmissions(); + + return min(d, r); } protected: @@ -123,6 +126,8 @@ class ReliableRouter : public FloodingRouter /** * 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(); }; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index cd6005c91..9ea3ccebc 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -41,20 +41,22 @@ Router::Router() : concurrency::OSThread("Router"), fromRadioQueue(MAX_RX_FROMRA /* DEBUG_MSG("Size of NodeInfo %d\n", sizeof(NodeInfo)); DEBUG_MSG("Size of SubPacket %d\n", sizeof(SubPacket)); DEBUG_MSG("Size of MeshPacket %d\n", sizeof(MeshPacket)); */ + + fromRadioQueue.setReader(this); } /** * do idle processing * Mostly looking in our incoming rxPacket queue and calling handleReceived. */ -uint32_t Router::runOnce() +int32_t Router::runOnce() { MeshPacket *mp; while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) { perhapsHandleReceived(mp); } - return 0; + return INT32_MAX; // Wait a long time - until we get woken for the message queue } /// Generate a unique packet id diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 6538cedab..2bdb7231f 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -45,7 +45,7 @@ class Router : protected concurrency::OSThread * do idle processing * Mostly looking in our incoming rxPacket queue and calling handleReceived. */ - virtual uint32_t runOnce(); + 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 @@ -114,7 +114,7 @@ class Router : protected concurrency::OSThread void handleReceived(MeshPacket *p); }; -extern Router &router; +extern Router *router; /// Generate a unique packet id // FIXME, move this someplace better diff --git a/src/mesh/TypedQueue.h b/src/mesh/TypedQueue.h index a2b82626f..e35cb34bd 100644 --- a/src/mesh/TypedQueue.h +++ b/src/mesh/TypedQueue.h @@ -3,6 +3,7 @@ #include #include +#include "concurrency/OSThread.h" #include "freertosinc.h" #ifdef HAS_FREE_RTOS @@ -15,6 +16,7 @@ template class TypedQueue { static_assert(std::is_pod::value, "T must be pod"); QueueHandle_t h; + concurrency::OSThread *reader = NULL; public: TypedQueue(int maxElements) @@ -29,13 +31,35 @@ template class TypedQueue 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 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 diff --git a/src/meshwifi/meshwifi.cpp b/src/meshwifi/meshwifi.cpp index b7a8dace4..266cada69 100644 --- a/src/meshwifi/meshwifi.cpp +++ b/src/meshwifi/meshwifi.cpp @@ -121,13 +121,7 @@ void initWifi() 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() { diff --git a/src/meshwifi/meshwifi.h b/src/meshwifi/meshwifi.h index f22d69a63..ac03e29f5 100644 --- a/src/meshwifi/meshwifi.h +++ b/src/meshwifi/meshwifi.h @@ -12,9 +12,6 @@ void initWifi(); void deinitWifi(); -/// Perform idle loop processing required by the wifi layer -void loopWifi(); - bool isWifiAvailable(); void handleDNSResponse(); diff --git a/src/nimble/BluetoothUtil.cpp b/src/nimble/BluetoothUtil.cpp index f294802cd..0b6a30c60 100644 --- a/src/nimble/BluetoothUtil.cpp +++ b/src/nimble/BluetoothUtil.cpp @@ -3,16 +3,16 @@ #include "NimbleBluetoothAPI.h" #include "PhoneAPI.h" #include "PowerFSM.h" -#include #include "configuration.h" #include "esp_bt.h" #include "host/util/util.h" #include "main.h" +#include "meshwifi/meshwifi.h" #include "nimble/NimbleDefs.h" #include "services/gap/ble_svc_gap.h" #include "services/gatt/ble_svc_gatt.h" #include -#include "meshwifi/meshwifi.h" +#include static bool pinShowing; @@ -20,14 +20,14 @@ static void startCb(uint32_t pin) { pinShowing = true; powerFSM.trigger(EVENT_BLUETOOTH_PAIR); - screen.startBluetoothPinScreen(pin); + screen->startBluetoothPinScreen(pin); }; static void stopCb() { if (pinShowing) { pinShowing = false; - screen.stopBluetoothPinScreen(); + screen->stopBluetoothPinScreen(); } }; @@ -533,7 +533,7 @@ void setBluetoothEnable(bool on) return; } */ - + // shutdown wifi deinitWifi(); diff --git a/src/power.h b/src/power.h index b7cf93fc5..8b75834aa 100644 --- a/src/power.h +++ b/src/power.h @@ -25,7 +25,7 @@ class Power : private concurrency::OSThread void readPowerStatus(); virtual bool setup(); - virtual uint32_t runOnce(); + virtual int32_t runOnce(); void setStatusHandler(meshtastic::PowerStatus *handler) { statusHandler = handler; } protected: diff --git a/src/sleep.cpp b/src/sleep.cpp index d3a550628..ee90c149c 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -151,7 +151,7 @@ void doDeepSleep(uint64_t msecToWake) notifySleep.notifyObservers(NULL); // also tell the regular sleep handlers 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(); From 0c8e0efed2a01e3a47bffebcb22b2982c550b4cc Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 11 Oct 2020 08:12:53 +0800 Subject: [PATCH 6/8] new threading finished- saves about 10mA for the high activity states --- src/PowerFSM.h | 1 + src/concurrency/InterruptableDelay.cpp | 4 ++-- src/concurrency/OSThread.h | 15 +++--------- src/main.cpp | 33 +++++++++++++++++++++----- 4 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/PowerFSM.h b/src/PowerFSM.h index 4a219f570..d996acbd3 100644 --- a/src/PowerFSM.h +++ b/src/PowerFSM.h @@ -20,5 +20,6 @@ #define EVENT_POWER_DISCONNECTED 14 extern Fsm powerFSM; +extern State statePOWER; void PowerFSM_setup(); diff --git a/src/concurrency/InterruptableDelay.cpp b/src/concurrency/InterruptableDelay.cpp index dd7d6f86e..4b4ccc914 100644 --- a/src/concurrency/InterruptableDelay.cpp +++ b/src/concurrency/InterruptableDelay.cpp @@ -20,12 +20,12 @@ InterruptableDelay::~InterruptableDelay() bool InterruptableDelay::delay(uint32_t msec) { if (msec) { - DEBUG_MSG("delay %u ", msec); + // DEBUG_MSG("delay %u ", msec); // sem take will return false if we timed out (i.e. were not interrupted) bool r = xSemaphoreTake(semaphore, pdMS_TO_TICKS(msec)); - DEBUG_MSG("interrupt=%d\n", r); + // DEBUG_MSG("interrupt=%d\n", r); return !r; } else { return true; diff --git a/src/concurrency/OSThread.h b/src/concurrency/OSThread.h index 3c0c62dc9..dc600387e 100644 --- a/src/concurrency/OSThread.h +++ b/src/concurrency/OSThread.h @@ -17,21 +17,12 @@ extern InterruptableDelay mainDelay; /** * @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 * - * make bluetooth wake cpu immediately (because it puts a message in a queue?) - * - * don't sleep at all if in POWER mode - * - * wake for serial character received - * - * add concept of 'low priority' threads that are not used to block sleep? - * - * make everything use osthread - * - * if we wake once because of a ble packet we might need to run loop multiple times before we can truely sleep - * + * move more things into OSThreads * remove lock/lockguard * * move typedQueue into concurrency diff --git a/src/main.cpp b/src/main.cpp index 0bdb29205..175fbc607 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,7 +53,6 @@ meshtastic::NodeStatus *nodeStatus = new meshtastic::NodeStatus(); bool ssd1306_found; bool axp192_found; - Router *router = NULL; // Users of router don't care what sort of subclass implements that API // ----------------------------------------------------------------------------- @@ -115,7 +114,28 @@ static int32_t ledBlinker() return powerStatus->getIsCharging() ? 1000 : (ledOn ? 1 : 1000); } +/// 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) + canSleep = (powerFSM.getState() != &statePOWER); + + return 10; + } +}; + static Periodic *ledPeriodic; +static OSThread *powerFSMthread; // Prepare for button presses #ifdef BUTTON_PIN @@ -349,6 +369,7 @@ void setup() // 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 + powerFSMthread = new PowerFSMThread(); // setBluetoothEnable(false); we now don't start bluetooth until we enter the proper state setCPUFast(false); // 80MHz is fine for our slow peripherals @@ -377,8 +398,6 @@ axpDebugOutput.setup(); void loop() { - powerFSM.run_machine(); - // axpDebugOutput.loop(); #ifdef DEBUG_PORT @@ -416,9 +435,11 @@ void 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())); - + /* 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); } From 8330c3270e35c86191b74be0e200493395d6ba7d Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 11 Oct 2020 08:15:52 +0800 Subject: [PATCH 7/8] 1.1.3 --- bin/version.sh | 2 +- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/version.sh b/bin/version.sh index 75c088c98..feeef0162 100644 --- a/bin/version.sh +++ b/bin/version.sh @@ -1,3 +1,3 @@ -export VERSION=1.1.2 \ No newline at end of file +export VERSION=1.1.3 \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 453c6ecb1..89ff46e9b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -62,7 +62,7 @@ lib_deps = https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306 1260 ; OneButton library for non-blocking button debounce 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#31015a55e630a2df77d9d714669c621a5bf355ad https://github.com/meshtastic/RadioLib.git#8657380241bce681c33aab46598bbf13b11f876c https://github.com/meshtastic/TinyGPSPlus.git From 999b2927173a9790ad28db0582ae41221082d073 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 11 Oct 2020 09:18:47 +0800 Subject: [PATCH 8/8] fixes for the posix port --- platformio.ini | 2 +- src/concurrency/BinarySemaphoreFreeRTOS.cpp | 39 +++++++++++++++++++++ src/concurrency/BinarySemaphoreFreeRTOS.h | 31 ++++++++++++++++ src/concurrency/BinarySemaphorePosix.cpp | 36 +++++++++++++++++++ src/concurrency/BinarySemaphorePosix.h | 31 ++++++++++++++++ src/concurrency/InterruptableDelay.cpp | 8 ++--- src/concurrency/InterruptableDelay.h | 11 +++++- src/mesh/RadioInterface.cpp | 2 +- src/mesh/RadioInterface.h | 4 ++- src/mesh/RadioLibInterface.cpp | 2 +- src/mesh/RadioLibInterface.h | 2 +- src/mesh/TypedQueue.h | 8 +++++ 12 files changed, 165 insertions(+), 11 deletions(-) create mode 100644 src/concurrency/BinarySemaphoreFreeRTOS.cpp create mode 100644 src/concurrency/BinarySemaphoreFreeRTOS.h create mode 100644 src/concurrency/BinarySemaphorePosix.cpp create mode 100644 src/concurrency/BinarySemaphorePosix.h diff --git a/platformio.ini b/platformio.ini index 89ff46e9b..dbdf1051f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -69,7 +69,7 @@ lib_deps = https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460 Wire ; explicitly needed here because the AXP202 library forgets to add it SPI - https://github.com/geeksville/ArduinoThread.git + https://github.com/geeksville/ArduinoThread.git#333ffd09b596977c217ba25da4258f588b462ac6 ; Common settings for conventional (non Portduino) Ardino targets [arduino_base] diff --git a/src/concurrency/BinarySemaphoreFreeRTOS.cpp b/src/concurrency/BinarySemaphoreFreeRTOS.cpp new file mode 100644 index 000000000..983ee0955 --- /dev/null +++ b/src/concurrency/BinarySemaphoreFreeRTOS.cpp @@ -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 \ No newline at end of file diff --git a/src/concurrency/BinarySemaphoreFreeRTOS.h b/src/concurrency/BinarySemaphoreFreeRTOS.h new file mode 100644 index 000000000..b5e488fd2 --- /dev/null +++ b/src/concurrency/BinarySemaphoreFreeRTOS.h @@ -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 + +} \ No newline at end of file diff --git a/src/concurrency/BinarySemaphorePosix.cpp b/src/concurrency/BinarySemaphorePosix.cpp new file mode 100644 index 000000000..44cd741f1 --- /dev/null +++ b/src/concurrency/BinarySemaphorePosix.cpp @@ -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 \ No newline at end of file diff --git a/src/concurrency/BinarySemaphorePosix.h b/src/concurrency/BinarySemaphorePosix.h new file mode 100644 index 000000000..8a6368678 --- /dev/null +++ b/src/concurrency/BinarySemaphorePosix.h @@ -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 + +} \ No newline at end of file diff --git a/src/concurrency/InterruptableDelay.cpp b/src/concurrency/InterruptableDelay.cpp index 4b4ccc914..e7538235e 100644 --- a/src/concurrency/InterruptableDelay.cpp +++ b/src/concurrency/InterruptableDelay.cpp @@ -6,12 +6,10 @@ namespace concurrency InterruptableDelay::InterruptableDelay() { - semaphore = xSemaphoreCreateBinary(); } InterruptableDelay::~InterruptableDelay() { - vSemaphoreDelete(semaphore); } /** @@ -23,7 +21,7 @@ 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 = xSemaphoreTake(semaphore, pdMS_TO_TICKS(msec)); + bool r = semaphore.take(msec); // DEBUG_MSG("interrupt=%d\n", r); return !r; @@ -34,12 +32,12 @@ bool InterruptableDelay::delay(uint32_t msec) void InterruptableDelay::interrupt() { - xSemaphoreGive(semaphore); + semaphore.give(); } IRAM_ATTR void InterruptableDelay::interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken) { - xSemaphoreGiveFromISR(semaphore, pxHigherPriorityTaskWoken); + semaphore.giveFromISR(pxHigherPriorityTaskWoken); } } // namespace concurrency \ No newline at end of file diff --git a/src/concurrency/InterruptableDelay.h b/src/concurrency/InterruptableDelay.h index b6428b25a..b0d4b009f 100644 --- a/src/concurrency/InterruptableDelay.h +++ b/src/concurrency/InterruptableDelay.h @@ -2,6 +2,15 @@ #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 { @@ -14,7 +23,7 @@ namespace concurrency */ class InterruptableDelay { - SemaphoreHandle_t semaphore; + BinarySemaphore semaphore; public: InterruptableDelay(); diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 28e6b8c88..8fefd80f8 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -91,7 +91,7 @@ void printPacket(const char *prefix, const MeshPacket *p) DEBUG_MSG(")\n"); } -RadioInterface::RadioInterface() : NotifiedWorkerThread("RadioIf") +RadioInterface::RadioInterface() { assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 70c5a090a..1d2e4b750 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -36,7 +36,7 @@ typedef struct { * * 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 PointerQueue *rxDest = NULL; @@ -72,6 +72,8 @@ class RadioInterface : protected concurrency::NotifiedWorkerThread */ RadioInterface(); + virtual ~RadioInterface() {} + /** * Set where to deliver received packets. This method should only be used by the Router class */ diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 5dab06e00..41d000d69 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -19,7 +19,7 @@ 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, SPIClass &spi, PhysicalLayer *_iface) - : module(cs, irq, rst, busy, spi, spiSettings), iface(_iface) + : NotifiedWorkerThread("RadioIf"), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface) { instance = this; } diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index 755935e26..86dcb9701 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -59,7 +59,7 @@ class LockingModule : public Module virtual void SPItransfer(uint8_t cmd, uint8_t reg, uint8_t *dataOut, uint8_t *dataIn, uint8_t numBytes); }; -class RadioLibInterface : public RadioInterface +class RadioLibInterface : public RadioInterface, protected concurrency::NotifiedWorkerThread { /// Used as our notification from the ISR enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED }; diff --git a/src/mesh/TypedQueue.h b/src/mesh/TypedQueue.h index e35cb34bd..0b60e6cf7 100644 --- a/src/mesh/TypedQueue.h +++ b/src/mesh/TypedQueue.h @@ -73,6 +73,7 @@ template class TypedQueue template class TypedQueue { std::queue q; + concurrency::OSThread *reader = NULL; public: TypedQueue(int maxElements) {} @@ -83,6 +84,11 @@ template class TypedQueue bool enqueue(T x, TickType_t maxWait = portMAX_DELAY) { + if (reader) { + reader->setInterval(0); + concurrency::mainDelay.interrupt(); + } + q.push(x); return true; } @@ -101,5 +107,7 @@ template class TypedQueue } // bool dequeueFromISR(T *p, BaseType_t *higherPriWoken) { return xQueueReceiveFromISR(h, p, higherPriWoken); } + + void setReader(concurrency::OSThread *t) { reader = t; } }; #endif