From 3d86c99c259a91da4c32d832b8f290a9ee6534b8 Mon Sep 17 00:00:00 2001 From: WillyJL Date: Fri, 12 Sep 2025 05:53:35 +0200 Subject: [PATCH 01/13] T-Lora Pager: Interrupt based rotary encoder --- src/input/RotaryEncoderImpl.cpp | 65 +++++++++++++++------ src/input/RotaryEncoderImpl.h | 7 +++ variants/esp32s3/tlora-pager/platformio.ini | 4 +- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/src/input/RotaryEncoderImpl.cpp b/src/input/RotaryEncoderImpl.cpp index 7d638dd71..cede1b87c 100644 --- a/src/input/RotaryEncoderImpl.cpp +++ b/src/input/RotaryEncoderImpl.cpp @@ -6,6 +6,8 @@ #define ORIGIN_NAME "RotaryEncoder" +#define ROTARY_INTERRUPT_FLAG _BV(0) + RotaryEncoderImpl *rotaryEncoderImpl; RotaryEncoderImpl::RotaryEncoderImpl() : concurrency::OSThread(ORIGIN_NAME), originName(ORIGIN_NAME) @@ -30,6 +32,17 @@ bool RotaryEncoderImpl::init() moduleConfig.canned_message.inputbroker_pin_press); rotary->resetButton(); + inputQueue = xQueueCreate(5, sizeof(input_broker_event)); + interruptFlag = xEventGroupCreate(); + interruptInstance = this; + auto interruptHandler = []() { + xEventGroupSetBits(interruptInstance->interruptFlag, ROTARY_INTERRUPT_FLAG); + }; + attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE); + attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE); + attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE); + xTaskCreate(inputWorker, "rotary", 2 * 1024, this, 10, &inputWorkerTask); + inputBroker->registerSource(this); LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a, @@ -38,35 +51,49 @@ bool RotaryEncoderImpl::init() return true; } -int32_t RotaryEncoderImpl::runOnce() +void RotaryEncoderImpl::dispatchInputs() { - InputEvent e{originName, INPUT_BROKER_NONE, 0, 0, 0}; static uint32_t lastPressed = millis(); if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) { if (lastPressed + 200 < millis()) { - LOG_DEBUG("Rotary event Press"); + // LOG_DEBUG("Rotary event Press"); lastPressed = millis(); - e.inputEvent = this->eventPressed; - } - } else { - switch (rotary->process()) { - case RotaryEncoder::DIRECTION_CW: - LOG_DEBUG("Rotary event CW"); - e.inputEvent = this->eventCw; - break; - case RotaryEncoder::DIRECTION_CCW: - LOG_DEBUG("Rotary event CCW"); - e.inputEvent = this->eventCcw; - break; - default: - break; + xQueueSend(inputQueue, &this->eventPressed, portMAX_DELAY); } } - if (e.inputEvent != INPUT_BROKER_NONE) { + switch (rotary->process()) { + case RotaryEncoder::DIRECTION_CW: + // LOG_DEBUG("Rotary event CW"); + xQueueSend(inputQueue, &this->eventCw, portMAX_DELAY); + break; + case RotaryEncoder::DIRECTION_CCW: + // LOG_DEBUG("Rotary event CCW"); + xQueueSend(inputQueue, &this->eventCcw, portMAX_DELAY); + break; + default: + break; + } +} + +void RotaryEncoderImpl::inputWorker(void *p) +{ + RotaryEncoderImpl* instance = (RotaryEncoderImpl*)p; + while (true) { + xEventGroupWaitBits(instance->interruptFlag, ROTARY_INTERRUPT_FLAG, pdTRUE, pdTRUE, portMAX_DELAY); + instance->dispatchInputs(); + } + vTaskDelete(NULL); +} + +RotaryEncoderImpl* RotaryEncoderImpl::interruptInstance; + +int32_t RotaryEncoderImpl::runOnce() +{ + InputEvent e{originName, INPUT_BROKER_NONE, 0, 0, 0}; + while(xQueueReceive(inputQueue, &e.inputEvent, 0) == pdPASS) { this->notifyObservers(&e); } - return 10; } diff --git a/src/input/RotaryEncoderImpl.h b/src/input/RotaryEncoderImpl.h index ae2a7c6fd..1d92617d5 100644 --- a/src/input/RotaryEncoderImpl.h +++ b/src/input/RotaryEncoderImpl.h @@ -17,6 +17,13 @@ class RotaryEncoderImpl : public Observable, public concurre protected: virtual int32_t runOnce() override; + QueueHandle_t inputQueue; + void dispatchInputs(void); + TaskHandle_t inputWorkerTask; + static void inputWorker(void *p); + EventGroupHandle_t interruptFlag; + static RotaryEncoderImpl* interruptInstance; + input_broker_event eventCw = INPUT_BROKER_NONE; input_broker_event eventCcw = INPUT_BROKER_NONE; input_broker_event eventPressed = INPUT_BROKER_NONE; diff --git a/variants/esp32s3/tlora-pager/platformio.ini b/variants/esp32s3/tlora-pager/platformio.ini index 312d46259..9800161bb 100644 --- a/variants/esp32s3/tlora-pager/platformio.ini +++ b/variants/esp32s3/tlora-pager/platformio.ini @@ -15,7 +15,7 @@ build_flags = ${esp32s3_base.build_flags} -D SDCARD_USE_SPI1 -D ENABLE_ROTARY_PULLUP -D ENABLE_BUTTON_PULLUP - -D HALF_STEP + -D ROTARY_BUXTRONICS lib_deps = ${esp32s3_base.lib_deps} lovyan03/LovyanGFX@1.2.7 @@ -26,7 +26,7 @@ lib_deps = ${esp32s3_base.lib_deps} lewisxhe/SensorLib@0.3.1 https://github.com/pschatzmann/arduino-audio-driver/archive/refs/tags/v0.1.3.zip https://github.com/mverch67/BQ27220/archive/07d92be846abd8a0258a50c23198dac0858b22ed.zip - https://github.com/mverch67/RotaryEncoder/archive/25a59d5745a6645536f921427d80b08e78f886d4.zip + https://github.com/mverch67/RotaryEncoder/archive/da958a21389cbcd485989705df602a33e092dd88.zip [env:tlora-pager-tft] board_level = extra From 42e4759634c88b5429d69e02c6a630976362a077 Mon Sep 17 00:00:00 2001 From: WillyJL Date: Sun, 14 Sep 2025 04:22:01 +0200 Subject: [PATCH 02/13] T-Lora Pager: Fix amplifier fuzzing/popping --- src/AudioThread.h | 17 +++++++++++++++++ src/main.cpp | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/AudioThread.h b/src/AudioThread.h index 286729909..f794d3a79 100644 --- a/src/AudioThread.h +++ b/src/AudioThread.h @@ -11,6 +11,11 @@ #include #include +#ifdef USE_XL9555 +#include "ExtensionIOXL9555.hpp" +extern ExtensionIOXL9555 io; +#endif + #define AUDIO_THREAD_INTERVAL_MS 100 class AudioThread : public concurrency::OSThread @@ -20,6 +25,9 @@ class AudioThread : public concurrency::OSThread void beginRttl(const void *data, uint32_t len) { +#ifdef USE_XL9555 + io.digitalWrite(EXPANDS_AMP_EN, HIGH); +#endif setCPUFast(true); rtttlFile = new AudioFileSourcePROGMEM(data, len); i2sRtttl = new AudioGeneratorRTTTL(); @@ -45,6 +53,9 @@ class AudioThread : public concurrency::OSThread rtttlFile = nullptr; setCPUFast(false); +#ifdef USE_XL9555 + io.digitalWrite(EXPANDS_AMP_EN, LOW); +#endif } void readAloud(const char *text) @@ -55,10 +66,16 @@ class AudioThread : public concurrency::OSThread i2sRtttl = nullptr; } +#ifdef USE_XL9555 + io.digitalWrite(EXPANDS_AMP_EN, HIGH); +#endif ESP8266SAM *sam = new ESP8266SAM; sam->Say(audioOut, text); delete sam; setCPUFast(false); +#ifdef USE_XL9555 + io.digitalWrite(EXPANDS_AMP_EN, LOW); +#endif } protected: diff --git a/src/main.cpp b/src/main.cpp index 8d576f008..b821310ce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -374,7 +374,7 @@ void setup() io.pinMode(EXPANDS_DRV_EN, OUTPUT); io.digitalWrite(EXPANDS_DRV_EN, HIGH); io.pinMode(EXPANDS_AMP_EN, OUTPUT); - io.digitalWrite(EXPANDS_AMP_EN, HIGH); + io.digitalWrite(EXPANDS_AMP_EN, LOW); io.pinMode(EXPANDS_LORA_EN, OUTPUT); io.digitalWrite(EXPANDS_LORA_EN, HIGH); io.pinMode(EXPANDS_GPS_EN, OUTPUT); From 20f68929c8b6da3f8f3fc797149437c2012fc687 Mon Sep 17 00:00:00 2001 From: WillyJL Date: Sun, 14 Sep 2025 20:17:24 +0200 Subject: [PATCH 03/13] Fix build for other variants --- src/input/RotaryEncoderImpl.h | 6 +++++- src/modules/Modules.cpp | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/input/RotaryEncoderImpl.h b/src/input/RotaryEncoderImpl.h index 1d92617d5..4922b4333 100644 --- a/src/input/RotaryEncoderImpl.h +++ b/src/input/RotaryEncoderImpl.h @@ -1,6 +1,8 @@ #pragma once -// This is a non-interrupt version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library) +#ifdef T_LORA_PAGER + +// This is a version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library) #include "InputBroker.h" #include "concurrency/OSThread.h" @@ -33,3 +35,5 @@ class RotaryEncoderImpl : public Observable, public concurre }; extern RotaryEncoderImpl *rotaryEncoderImpl; + +#endif diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index d4beb6824..65139fb6c 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -3,7 +3,9 @@ #include "buzz/BuzzerFeedbackThread.h" #include "input/ExpressLRSFiveWay.h" #include "input/InputBroker.h" +#ifdef T_LORA_PAGER #include "input/RotaryEncoderImpl.h" +#endif #include "input/RotaryEncoderInterruptImpl1.h" #include "input/SerialKeyboardImpl.h" #include "input/UpDownInterruptImpl1.h" From 6c932d51ec1288eeba38805f64103169f1f2144a Mon Sep 17 00:00:00 2001 From: WillyJL Date: Mon, 15 Sep 2025 17:49:03 +0200 Subject: [PATCH 04/13] Fix defines --- src/AudioThread.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AudioThread.h b/src/AudioThread.h index f794d3a79..8073ee51b 100644 --- a/src/AudioThread.h +++ b/src/AudioThread.h @@ -25,7 +25,7 @@ class AudioThread : public concurrency::OSThread void beginRttl(const void *data, uint32_t len) { -#ifdef USE_XL9555 +#ifdef T_LORA_PAGER io.digitalWrite(EXPANDS_AMP_EN, HIGH); #endif setCPUFast(true); @@ -53,7 +53,7 @@ class AudioThread : public concurrency::OSThread rtttlFile = nullptr; setCPUFast(false); -#ifdef USE_XL9555 +#ifdef T_LORA_PAGER io.digitalWrite(EXPANDS_AMP_EN, LOW); #endif } @@ -66,14 +66,14 @@ class AudioThread : public concurrency::OSThread i2sRtttl = nullptr; } -#ifdef USE_XL9555 +#ifdef T_LORA_PAGER io.digitalWrite(EXPANDS_AMP_EN, HIGH); #endif ESP8266SAM *sam = new ESP8266SAM; sam->Say(audioOut, text); delete sam; setCPUFast(false); -#ifdef USE_XL9555 +#ifdef T_LORA_PAGER io.digitalWrite(EXPANDS_AMP_EN, LOW); #endif } From d427b477e39f77434e08b6d7b9f1199b995c9d62 Mon Sep 17 00:00:00 2001 From: WillyJL Date: Wed, 17 Sep 2025 02:07:24 +0200 Subject: [PATCH 05/13] Format --- src/input/RotaryEncoderImpl.cpp | 10 ++++------ src/input/RotaryEncoderImpl.h | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/input/RotaryEncoderImpl.cpp b/src/input/RotaryEncoderImpl.cpp index cede1b87c..216e92382 100644 --- a/src/input/RotaryEncoderImpl.cpp +++ b/src/input/RotaryEncoderImpl.cpp @@ -35,9 +35,7 @@ bool RotaryEncoderImpl::init() inputQueue = xQueueCreate(5, sizeof(input_broker_event)); interruptFlag = xEventGroupCreate(); interruptInstance = this; - auto interruptHandler = []() { - xEventGroupSetBits(interruptInstance->interruptFlag, ROTARY_INTERRUPT_FLAG); - }; + auto interruptHandler = []() { xEventGroupSetBits(interruptInstance->interruptFlag, ROTARY_INTERRUPT_FLAG); }; attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE); attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE); attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE); @@ -78,7 +76,7 @@ void RotaryEncoderImpl::dispatchInputs() void RotaryEncoderImpl::inputWorker(void *p) { - RotaryEncoderImpl* instance = (RotaryEncoderImpl*)p; + RotaryEncoderImpl *instance = (RotaryEncoderImpl *)p; while (true) { xEventGroupWaitBits(instance->interruptFlag, ROTARY_INTERRUPT_FLAG, pdTRUE, pdTRUE, portMAX_DELAY); instance->dispatchInputs(); @@ -86,12 +84,12 @@ void RotaryEncoderImpl::inputWorker(void *p) vTaskDelete(NULL); } -RotaryEncoderImpl* RotaryEncoderImpl::interruptInstance; +RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance; int32_t RotaryEncoderImpl::runOnce() { InputEvent e{originName, INPUT_BROKER_NONE, 0, 0, 0}; - while(xQueueReceive(inputQueue, &e.inputEvent, 0) == pdPASS) { + while (xQueueReceive(inputQueue, &e.inputEvent, 0) == pdPASS) { this->notifyObservers(&e); } return 10; diff --git a/src/input/RotaryEncoderImpl.h b/src/input/RotaryEncoderImpl.h index 4922b4333..e5ff251e8 100644 --- a/src/input/RotaryEncoderImpl.h +++ b/src/input/RotaryEncoderImpl.h @@ -23,8 +23,8 @@ class RotaryEncoderImpl : public Observable, public concurre void dispatchInputs(void); TaskHandle_t inputWorkerTask; static void inputWorker(void *p); - EventGroupHandle_t interruptFlag; - static RotaryEncoderImpl* interruptInstance; + EventGroupHandle_t interruptFlag; + static RotaryEncoderImpl *interruptInstance; input_broker_event eventCw = INPUT_BROKER_NONE; input_broker_event eventCcw = INPUT_BROKER_NONE; From 0e26702c4600a4c39e9ef2249e09e35c9763fd73 Mon Sep 17 00:00:00 2001 From: WillyJL Date: Fri, 19 Sep 2025 19:08:38 +0200 Subject: [PATCH 06/13] InputPollable: System for polling after interrupts --- src/input/InputBroker.cpp | 46 +++++++++++++++++++++++++++++++++++++-- src/input/InputBroker.h | 22 +++++++++++++++++++ src/main.cpp | 3 +++ 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/input/InputBroker.cpp b/src/input/InputBroker.cpp index ef6d8df91..5ca890b43 100644 --- a/src/input/InputBroker.cpp +++ b/src/input/InputBroker.cpp @@ -3,16 +3,58 @@ InputBroker *inputBroker = nullptr; -InputBroker::InputBroker(){}; +InputBroker::InputBroker() +{ +#ifdef HAS_FREE_RTOS + inputEventQueue = xQueueCreate(5, sizeof(InputEvent)); + pollSoonQueue = xQueueCreate(5, sizeof(InputPollable *)); + xTaskCreate(pollSoonWorker, "input-pollSoon", 2 * 1024, this, 10, &pollSoonTask); +#endif +} void InputBroker::registerSource(Observable *source) { this->inputEventObserver.observe(source); } +#ifdef HAS_FREE_RTOS +void InputBroker::pollSoonRequestFromIsr(InputPollable *pollable) +{ + xQueueSendFromISR(pollSoonQueue, &pollable, NULL); +} + +void InputBroker::queueInputEvent(const InputEvent *event) +{ + xQueueSend(inputEventQueue, event, portMAX_DELAY); +} + +void InputBroker::processInputEventQueue() +{ + InputEvent event; + while (xQueueReceive(inputEventQueue, &event, 0)) { + handleInputEvent(&event); + } +} +#endif + int InputBroker::handleInputEvent(const InputEvent *event) { powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release this->notifyObservers(event); return 0; -} \ No newline at end of file +} + +#ifdef HAS_FREE_RTOS +void InputBroker::pollSoonWorker(void *p) +{ + InputBroker *instance = (InputBroker *)p; + while (true) { + InputPollable *pollable = NULL; + xQueueReceive(instance->pollSoonQueue, &pollable, portMAX_DELAY); + if (pollable) { + pollable->pollOnce(); + } + } + vTaskDelete(NULL); +} +#endif diff --git a/src/input/InputBroker.h b/src/input/InputBroker.h index 2cdfa2ae2..82af184f3 100644 --- a/src/input/InputBroker.h +++ b/src/input/InputBroker.h @@ -1,5 +1,7 @@ #pragma once + #include "Observer.h" +#include "freertosinc.h" enum input_broker_event { INPUT_BROKER_NONE = 0, @@ -41,6 +43,13 @@ typedef struct _InputEvent { uint16_t touchX; uint16_t touchY; } InputEvent; + +class InputPollable +{ + public: + virtual void pollOnce() = 0; +}; + class InputBroker : public Observable { CallbackObserver inputEventObserver = @@ -50,9 +59,22 @@ class InputBroker : public Observable InputBroker(); void registerSource(Observable *source); void injectInputEvent(const InputEvent *event) { handleInputEvent(event); } +#ifdef HAS_FREE_RTOS + void pollSoonRequestFromIsr(InputPollable *pollable); + void queueInputEvent(const InputEvent *event); + void processInputEventQueue(); +#endif protected: int handleInputEvent(const InputEvent *event); + + private: +#ifdef HAS_FREE_RTOS + QueueHandle_t inputEventQueue; + QueueHandle_t pollSoonQueue; + TaskHandle_t pollSoonTask; + static void pollSoonWorker(void *p); +#endif }; extern InputBroker *inputBroker; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b821310ce..510d2b898 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1602,6 +1602,9 @@ void loop() #endif service->loop(); +#if !MESHTASTIC_EXCLUDE_INPUTBROKER + inputBroker->processInputEventQueue(); +#endif #if defined(LGFX_SDL) if (screen) { auto dispdev = screen->getDisplayDevice(); From 54f9f7a5917711adf9971bcd44dcdddd8f40812d Mon Sep 17 00:00:00 2001 From: WillyJL Date: Fri, 19 Sep 2025 22:05:18 +0200 Subject: [PATCH 07/13] T-Lora Pager: Use InputPollable for RotaryEncoderImpl --- src/input/RotaryEncoderImpl.cpp | 50 +++++++++------------------------ src/input/RotaryEncoderImpl.h | 11 ++------ 2 files changed, 16 insertions(+), 45 deletions(-) diff --git a/src/input/RotaryEncoderImpl.cpp b/src/input/RotaryEncoderImpl.cpp index 216e92382..213dd4faa 100644 --- a/src/input/RotaryEncoderImpl.cpp +++ b/src/input/RotaryEncoderImpl.cpp @@ -6,11 +6,9 @@ #define ORIGIN_NAME "RotaryEncoder" -#define ROTARY_INTERRUPT_FLAG _BV(0) - RotaryEncoderImpl *rotaryEncoderImpl; -RotaryEncoderImpl::RotaryEncoderImpl() : concurrency::OSThread(ORIGIN_NAME), originName(ORIGIN_NAME) +RotaryEncoderImpl::RotaryEncoderImpl() { rotary = nullptr; } @@ -20,7 +18,6 @@ bool RotaryEncoderImpl::init() if (!moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.inputbroker_pin_a == 0 || moduleConfig.canned_message.inputbroker_pin_b == 0) { // Input device is disabled. - disable(); return false; } @@ -32,16 +29,11 @@ bool RotaryEncoderImpl::init() moduleConfig.canned_message.inputbroker_pin_press); rotary->resetButton(); - inputQueue = xQueueCreate(5, sizeof(input_broker_event)); - interruptFlag = xEventGroupCreate(); interruptInstance = this; - auto interruptHandler = []() { xEventGroupSetBits(interruptInstance->interruptFlag, ROTARY_INTERRUPT_FLAG); }; + auto interruptHandler = []() { inputBroker->pollSoonRequestFromIsr(interruptInstance); }; attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE); attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE); attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE); - xTaskCreate(inputWorker, "rotary", 2 * 1024, this, 10, &inputWorkerTask); - - inputBroker->registerSource(this); LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw, @@ -49,50 +41,36 @@ bool RotaryEncoderImpl::init() return true; } -void RotaryEncoderImpl::dispatchInputs() +void RotaryEncoderImpl::pollOnce() { + InputEvent e{ORIGIN_NAME, INPUT_BROKER_NONE, 0, 0, 0}; + static uint32_t lastPressed = millis(); if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) { if (lastPressed + 200 < millis()) { - // LOG_DEBUG("Rotary event Press"); + LOG_DEBUG("Rotary event Press"); lastPressed = millis(); - xQueueSend(inputQueue, &this->eventPressed, portMAX_DELAY); + e.inputEvent = this->eventPressed; + inputBroker->queueInputEvent(&e); } } switch (rotary->process()) { case RotaryEncoder::DIRECTION_CW: - // LOG_DEBUG("Rotary event CW"); - xQueueSend(inputQueue, &this->eventCw, portMAX_DELAY); + LOG_DEBUG("Rotary event CW"); + e.inputEvent = this->eventCw; + inputBroker->queueInputEvent(&e); break; case RotaryEncoder::DIRECTION_CCW: - // LOG_DEBUG("Rotary event CCW"); - xQueueSend(inputQueue, &this->eventCcw, portMAX_DELAY); + LOG_DEBUG("Rotary event CCW"); + e.inputEvent = this->eventCcw; + inputBroker->queueInputEvent(&e); break; default: break; } } -void RotaryEncoderImpl::inputWorker(void *p) -{ - RotaryEncoderImpl *instance = (RotaryEncoderImpl *)p; - while (true) { - xEventGroupWaitBits(instance->interruptFlag, ROTARY_INTERRUPT_FLAG, pdTRUE, pdTRUE, portMAX_DELAY); - instance->dispatchInputs(); - } - vTaskDelete(NULL); -} - RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance; -int32_t RotaryEncoderImpl::runOnce() -{ - InputEvent e{originName, INPUT_BROKER_NONE, 0, 0, 0}; - while (xQueueReceive(inputQueue, &e.inputEvent, 0) == pdPASS) { - this->notifyObservers(&e); - } - return 10; -} - #endif \ No newline at end of file diff --git a/src/input/RotaryEncoderImpl.h b/src/input/RotaryEncoderImpl.h index e5ff251e8..af70d1bf4 100644 --- a/src/input/RotaryEncoderImpl.h +++ b/src/input/RotaryEncoderImpl.h @@ -10,20 +10,14 @@ class RotaryEncoder; -class RotaryEncoderImpl : public Observable, public concurrency::OSThread +class RotaryEncoderImpl : public InputPollable { public: RotaryEncoderImpl(); bool init(void); + virtual void pollOnce() override; protected: - virtual int32_t runOnce() override; - - QueueHandle_t inputQueue; - void dispatchInputs(void); - TaskHandle_t inputWorkerTask; - static void inputWorker(void *p); - EventGroupHandle_t interruptFlag; static RotaryEncoderImpl *interruptInstance; input_broker_event eventCw = INPUT_BROKER_NONE; @@ -31,7 +25,6 @@ class RotaryEncoderImpl : public Observable, public concurre input_broker_event eventPressed = INPUT_BROKER_NONE; RotaryEncoder *rotary; - const char *originName; }; extern RotaryEncoderImpl *rotaryEncoderImpl; From a76cc88dc2cae630eb7793eb7b14cb223f9ddd8d Mon Sep 17 00:00:00 2001 From: WillyJL Date: Sat, 20 Sep 2025 18:53:30 +0200 Subject: [PATCH 08/13] Rename RotaryEncoderImpl to TLoraPagerRotaryEncoder --- ...ryEncoderImpl.cpp => TLoraPagerRotaryEncoder.cpp} | 12 ++++++------ ...RotaryEncoderImpl.h => TLoraPagerRotaryEncoder.h} | 8 ++++---- src/modules/Modules.cpp | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) rename src/input/{RotaryEncoderImpl.cpp => TLoraPagerRotaryEncoder.cpp} (89%) rename src/input/{RotaryEncoderImpl.h => TLoraPagerRotaryEncoder.h} (73%) diff --git a/src/input/RotaryEncoderImpl.cpp b/src/input/TLoraPagerRotaryEncoder.cpp similarity index 89% rename from src/input/RotaryEncoderImpl.cpp rename to src/input/TLoraPagerRotaryEncoder.cpp index 213dd4faa..1b99defee 100644 --- a/src/input/RotaryEncoderImpl.cpp +++ b/src/input/TLoraPagerRotaryEncoder.cpp @@ -1,19 +1,19 @@ #ifdef T_LORA_PAGER -#include "RotaryEncoderImpl.h" +#include "TLoraPagerRotaryEncoder.h" #include "InputBroker.h" #include "RotaryEncoder.h" #define ORIGIN_NAME "RotaryEncoder" -RotaryEncoderImpl *rotaryEncoderImpl; +TLoraPagerRotaryEncoder *tLoraPagerRotaryEncoder; -RotaryEncoderImpl::RotaryEncoderImpl() +TLoraPagerRotaryEncoder::TLoraPagerRotaryEncoder() { rotary = nullptr; } -bool RotaryEncoderImpl::init() +bool TLoraPagerRotaryEncoder::init() { if (!moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.inputbroker_pin_a == 0 || moduleConfig.canned_message.inputbroker_pin_b == 0) { @@ -41,7 +41,7 @@ bool RotaryEncoderImpl::init() return true; } -void RotaryEncoderImpl::pollOnce() +void TLoraPagerRotaryEncoder::pollOnce() { InputEvent e{ORIGIN_NAME, INPUT_BROKER_NONE, 0, 0, 0}; @@ -71,6 +71,6 @@ void RotaryEncoderImpl::pollOnce() } } -RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance; +TLoraPagerRotaryEncoder *TLoraPagerRotaryEncoder::interruptInstance; #endif \ No newline at end of file diff --git a/src/input/RotaryEncoderImpl.h b/src/input/TLoraPagerRotaryEncoder.h similarity index 73% rename from src/input/RotaryEncoderImpl.h rename to src/input/TLoraPagerRotaryEncoder.h index af70d1bf4..d5e2edf4e 100644 --- a/src/input/RotaryEncoderImpl.h +++ b/src/input/TLoraPagerRotaryEncoder.h @@ -10,15 +10,15 @@ class RotaryEncoder; -class RotaryEncoderImpl : public InputPollable +class TLoraPagerRotaryEncoder : public InputPollable { public: - RotaryEncoderImpl(); + TLoraPagerRotaryEncoder(); bool init(void); virtual void pollOnce() override; protected: - static RotaryEncoderImpl *interruptInstance; + static TLoraPagerRotaryEncoder *interruptInstance; input_broker_event eventCw = INPUT_BROKER_NONE; input_broker_event eventCcw = INPUT_BROKER_NONE; @@ -27,6 +27,6 @@ class RotaryEncoderImpl : public InputPollable RotaryEncoder *rotary; }; -extern RotaryEncoderImpl *rotaryEncoderImpl; +extern TLoraPagerRotaryEncoder *tLoraPagerRotaryEncoder; #endif diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index abafce070..35e509b83 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -4,7 +4,7 @@ #include "input/ExpressLRSFiveWay.h" #include "input/InputBroker.h" #ifdef T_LORA_PAGER -#include "input/RotaryEncoderImpl.h" +#include "input/TLoraPagerRotaryEncoder.h" #endif #include "input/RotaryEncoderInterruptImpl1.h" #include "input/SerialKeyboardImpl.h" @@ -185,10 +185,10 @@ void setupModules() } #ifdef T_LORA_PAGER // use a special FSM based rotary encoder version for T-LoRa Pager - rotaryEncoderImpl = new RotaryEncoderImpl(); - if (!rotaryEncoderImpl->init()) { - delete rotaryEncoderImpl; - rotaryEncoderImpl = nullptr; + tLoraPagerRotaryEncoder = new TLoraPagerRotaryEncoder(); + if (!tLoraPagerRotaryEncoder->init()) { + delete tLoraPagerRotaryEncoder; + tLoraPagerRotaryEncoder = nullptr; } #else upDownInterruptImpl1 = new UpDownInterruptImpl1(); From 4100ba83a365be2a3edba673d0d2e6da0b192e5f Mon Sep 17 00:00:00 2001 From: WillyJL Date: Sun, 21 Sep 2025 03:23:16 +0200 Subject: [PATCH 09/13] Revert "Rename RotaryEncoderImpl to TLoraPagerRotaryEncoder" This reverts commit a76cc88dc2cae630eb7793eb7b14cb223f9ddd8d. --- ...aPagerRotaryEncoder.cpp => RotaryEncoderImpl.cpp} | 12 ++++++------ ...TLoraPagerRotaryEncoder.h => RotaryEncoderImpl.h} | 8 ++++---- src/modules/Modules.cpp | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) rename src/input/{TLoraPagerRotaryEncoder.cpp => RotaryEncoderImpl.cpp} (89%) rename src/input/{TLoraPagerRotaryEncoder.h => RotaryEncoderImpl.h} (73%) diff --git a/src/input/TLoraPagerRotaryEncoder.cpp b/src/input/RotaryEncoderImpl.cpp similarity index 89% rename from src/input/TLoraPagerRotaryEncoder.cpp rename to src/input/RotaryEncoderImpl.cpp index 1b99defee..213dd4faa 100644 --- a/src/input/TLoraPagerRotaryEncoder.cpp +++ b/src/input/RotaryEncoderImpl.cpp @@ -1,19 +1,19 @@ #ifdef T_LORA_PAGER -#include "TLoraPagerRotaryEncoder.h" +#include "RotaryEncoderImpl.h" #include "InputBroker.h" #include "RotaryEncoder.h" #define ORIGIN_NAME "RotaryEncoder" -TLoraPagerRotaryEncoder *tLoraPagerRotaryEncoder; +RotaryEncoderImpl *rotaryEncoderImpl; -TLoraPagerRotaryEncoder::TLoraPagerRotaryEncoder() +RotaryEncoderImpl::RotaryEncoderImpl() { rotary = nullptr; } -bool TLoraPagerRotaryEncoder::init() +bool RotaryEncoderImpl::init() { if (!moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.inputbroker_pin_a == 0 || moduleConfig.canned_message.inputbroker_pin_b == 0) { @@ -41,7 +41,7 @@ bool TLoraPagerRotaryEncoder::init() return true; } -void TLoraPagerRotaryEncoder::pollOnce() +void RotaryEncoderImpl::pollOnce() { InputEvent e{ORIGIN_NAME, INPUT_BROKER_NONE, 0, 0, 0}; @@ -71,6 +71,6 @@ void TLoraPagerRotaryEncoder::pollOnce() } } -TLoraPagerRotaryEncoder *TLoraPagerRotaryEncoder::interruptInstance; +RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance; #endif \ No newline at end of file diff --git a/src/input/TLoraPagerRotaryEncoder.h b/src/input/RotaryEncoderImpl.h similarity index 73% rename from src/input/TLoraPagerRotaryEncoder.h rename to src/input/RotaryEncoderImpl.h index d5e2edf4e..af70d1bf4 100644 --- a/src/input/TLoraPagerRotaryEncoder.h +++ b/src/input/RotaryEncoderImpl.h @@ -10,15 +10,15 @@ class RotaryEncoder; -class TLoraPagerRotaryEncoder : public InputPollable +class RotaryEncoderImpl : public InputPollable { public: - TLoraPagerRotaryEncoder(); + RotaryEncoderImpl(); bool init(void); virtual void pollOnce() override; protected: - static TLoraPagerRotaryEncoder *interruptInstance; + static RotaryEncoderImpl *interruptInstance; input_broker_event eventCw = INPUT_BROKER_NONE; input_broker_event eventCcw = INPUT_BROKER_NONE; @@ -27,6 +27,6 @@ class TLoraPagerRotaryEncoder : public InputPollable RotaryEncoder *rotary; }; -extern TLoraPagerRotaryEncoder *tLoraPagerRotaryEncoder; +extern RotaryEncoderImpl *rotaryEncoderImpl; #endif diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 35e509b83..abafce070 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -4,7 +4,7 @@ #include "input/ExpressLRSFiveWay.h" #include "input/InputBroker.h" #ifdef T_LORA_PAGER -#include "input/TLoraPagerRotaryEncoder.h" +#include "input/RotaryEncoderImpl.h" #endif #include "input/RotaryEncoderInterruptImpl1.h" #include "input/SerialKeyboardImpl.h" @@ -185,10 +185,10 @@ void setupModules() } #ifdef T_LORA_PAGER // use a special FSM based rotary encoder version for T-LoRa Pager - tLoraPagerRotaryEncoder = new TLoraPagerRotaryEncoder(); - if (!tLoraPagerRotaryEncoder->init()) { - delete tLoraPagerRotaryEncoder; - tLoraPagerRotaryEncoder = nullptr; + rotaryEncoderImpl = new RotaryEncoderImpl(); + if (!rotaryEncoderImpl->init()) { + delete rotaryEncoderImpl; + rotaryEncoderImpl = nullptr; } #else upDownInterruptImpl1 = new UpDownInterruptImpl1(); From d558df8a3a57d93fa8d358cabf05884b296bb2af Mon Sep 17 00:00:00 2001 From: WillyJL Date: Sun, 21 Sep 2025 03:29:52 +0200 Subject: [PATCH 10/13] Revert unnecessary ifdefs --- src/input/RotaryEncoderImpl.h | 4 ---- src/modules/Modules.cpp | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/input/RotaryEncoderImpl.h b/src/input/RotaryEncoderImpl.h index af70d1bf4..6f8e9fe5f 100644 --- a/src/input/RotaryEncoderImpl.h +++ b/src/input/RotaryEncoderImpl.h @@ -1,7 +1,5 @@ #pragma once -#ifdef T_LORA_PAGER - // This is a version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library) #include "InputBroker.h" @@ -28,5 +26,3 @@ class RotaryEncoderImpl : public InputPollable }; extern RotaryEncoderImpl *rotaryEncoderImpl; - -#endif diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index abafce070..757753d45 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -3,9 +3,7 @@ #include "buzz/BuzzerFeedbackThread.h" #include "input/ExpressLRSFiveWay.h" #include "input/InputBroker.h" -#ifdef T_LORA_PAGER #include "input/RotaryEncoderImpl.h" -#endif #include "input/RotaryEncoderInterruptImpl1.h" #include "input/SerialKeyboardImpl.h" #include "input/UpDownInterruptImpl1.h" From a1ca553bc0ac5695d512879ac2bffe1c858cbb1d Mon Sep 17 00:00:00 2001 From: WillyJL Date: Tue, 23 Sep 2025 22:30:01 +0200 Subject: [PATCH 11/13] Fix desktop build --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 5f2501cc8..48ecc14c7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1596,7 +1596,7 @@ void loop() #endif service->loop(); -#if !MESHTASTIC_EXCLUDE_INPUTBROKER +#if !MESHTASTIC_EXCLUDE_INPUTBROKER && defined(HAS_FREE_RTOS) inputBroker->processInputEventQueue(); #endif #if defined(LGFX_SDL) From 060a1299953166c77414f759ddba2558ae23a175 Mon Sep 17 00:00:00 2001 From: WillyJL Date: Wed, 24 Sep 2025 03:05:26 +0200 Subject: [PATCH 12/13] More flexible InputPollable paradigm --- src/input/InputBroker.cpp | 14 +++++++++++--- src/input/InputBroker.h | 2 +- src/input/RotaryEncoderImpl.cpp | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/input/InputBroker.cpp b/src/input/InputBroker.cpp index 5ca890b43..c588a9a0f 100644 --- a/src/input/InputBroker.cpp +++ b/src/input/InputBroker.cpp @@ -18,14 +18,22 @@ void InputBroker::registerSource(Observable *source) } #ifdef HAS_FREE_RTOS -void InputBroker::pollSoonRequestFromIsr(InputPollable *pollable) +void InputBroker::requestPollSoon(InputPollable *pollable) { - xQueueSendFromISR(pollSoonQueue, &pollable, NULL); + if (xPortInIsrContext() == pdTRUE) { + xQueueSendFromISR(pollSoonQueue, &pollable, NULL); + } else { + xQueueSend(pollSoonQueue, &pollable, 0); + } } void InputBroker::queueInputEvent(const InputEvent *event) { - xQueueSend(inputEventQueue, event, portMAX_DELAY); + if (xPortInIsrContext() == pdTRUE) { + xQueueSendFromISR(inputEventQueue, event, NULL); + } else { + xQueueSend(inputEventQueue, event, portMAX_DELAY); + } } void InputBroker::processInputEventQueue() diff --git a/src/input/InputBroker.h b/src/input/InputBroker.h index 82af184f3..192bd7e8b 100644 --- a/src/input/InputBroker.h +++ b/src/input/InputBroker.h @@ -60,7 +60,7 @@ class InputBroker : public Observable void registerSource(Observable *source); void injectInputEvent(const InputEvent *event) { handleInputEvent(event); } #ifdef HAS_FREE_RTOS - void pollSoonRequestFromIsr(InputPollable *pollable); + void requestPollSoon(InputPollable *pollable); void queueInputEvent(const InputEvent *event); void processInputEventQueue(); #endif diff --git a/src/input/RotaryEncoderImpl.cpp b/src/input/RotaryEncoderImpl.cpp index 213dd4faa..7b43fa256 100644 --- a/src/input/RotaryEncoderImpl.cpp +++ b/src/input/RotaryEncoderImpl.cpp @@ -30,7 +30,7 @@ bool RotaryEncoderImpl::init() rotary->resetButton(); interruptInstance = this; - auto interruptHandler = []() { inputBroker->pollSoonRequestFromIsr(interruptInstance); }; + auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); }; attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE); attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE); attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE); From edb5c0f88ed9adbf0d0b30bdbc89f69046d6ce26 Mon Sep 17 00:00:00 2001 From: WillyJL Date: Wed, 24 Sep 2025 04:19:11 +0200 Subject: [PATCH 13/13] Custom xPortInIsrContext() for nRF52/RP2xx0 --- src/platform/nrf52/architecture.h | 3 +++ src/platform/rp2xx0/architecture.h | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index c9938062e..56b46088a 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -149,3 +149,6 @@ // No serial ports on this board - ONLY use segger in memory console #define USE_SEGGER #endif + +// Detect if running in ISR context (ARM Cortex-M4) +#define xPortInIsrContext() ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) == 0 ? pdFALSE : pdTRUE) diff --git a/src/platform/rp2xx0/architecture.h b/src/platform/rp2xx0/architecture.h index 506c19c83..0c168ceee 100644 --- a/src/platform/rp2xx0/architecture.h +++ b/src/platform/rp2xx0/architecture.h @@ -35,4 +35,7 @@ #define HW_VENDOR meshtastic_HardwareModel_RP2040_FEATHER_RFM95 #elif defined(PRIVATE_HW) #define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW -#endif \ No newline at end of file +#endif + +// Detect if running in ISR context (ARM Cortex-M33 / RISC-V) +#define xPortInIsrContext() (__get_current_exception() == 0 ? pdFALSE : pdTRUE)