Merge pull request #7986 from WillyJL/fix/tlora-pager-rotary-amplifier
Some checks are pending
CI / setup (check) (push) Waiting to run
CI / setup (esp32) (push) Waiting to run
CI / setup (esp32c3) (push) Waiting to run
CI / setup (esp32c6) (push) Waiting to run
CI / setup (esp32s3) (push) Waiting to run
CI / setup (nrf52840) (push) Waiting to run
CI / setup (rp2040) (push) Waiting to run
CI / setup (rp2350) (push) Waiting to run
CI / setup (stm32) (push) Waiting to run
CI / version (push) Waiting to run
CI / check (push) Blocked by required conditions
CI / build-esp32 (push) Blocked by required conditions
CI / build-esp32s3 (push) Blocked by required conditions
CI / build-esp32c3 (push) Blocked by required conditions
CI / build-esp32c6 (push) Blocked by required conditions
CI / build-nrf52840 (push) Blocked by required conditions
CI / build-rp2040 (push) Blocked by required conditions
CI / build-rp2350 (push) Blocked by required conditions
CI / build-stm32 (push) Blocked by required conditions
CI / build-debian-src (push) Waiting to run
CI / package-pio-deps-native-tft (push) Waiting to run
CI / test-native (push) Waiting to run
CI / docker-deb-amd64 (push) Waiting to run
CI / docker-deb-amd64-tft (push) Waiting to run
CI / docker-alp-amd64 (push) Waiting to run
CI / docker-alp-amd64-tft (push) Waiting to run
CI / docker-deb-arm64 (push) Waiting to run
CI / docker-deb-armv7 (push) Waiting to run
CI / gather-artifacts (esp32) (push) Blocked by required conditions
CI / gather-artifacts (esp32c3) (push) Blocked by required conditions
CI / gather-artifacts (esp32c6) (push) Blocked by required conditions
CI / gather-artifacts (esp32s3) (push) Blocked by required conditions
CI / gather-artifacts (nrf52840) (push) Blocked by required conditions
CI / gather-artifacts (rp2040) (push) Blocked by required conditions
CI / gather-artifacts (rp2350) (push) Blocked by required conditions
CI / gather-artifacts (stm32) (push) Blocked by required conditions
CI / release-artifacts (push) Blocked by required conditions
CI / release-firmware (esp32) (push) Blocked by required conditions
CI / release-firmware (esp32c3) (push) Blocked by required conditions
CI / release-firmware (esp32c6) (push) Blocked by required conditions
CI / release-firmware (esp32s3) (push) Blocked by required conditions
CI / release-firmware (nrf52840) (push) Blocked by required conditions
CI / release-firmware (rp2040) (push) Blocked by required conditions
CI / release-firmware (rp2350) (push) Blocked by required conditions
CI / release-firmware (stm32) (push) Blocked by required conditions
CI / publish-firmware (push) Blocked by required conditions

T-Lora Pager: Fully fix rotary encoder and speaker fuzzing/popping
This commit is contained in:
Ben Meadors 2025-10-02 14:59:26 -05:00 committed by GitHub
commit c48a64e183
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 133 additions and 32 deletions

View File

@ -11,6 +11,11 @@
#include <AudioOutputI2S.h> #include <AudioOutputI2S.h>
#include <ESP8266SAM.h> #include <ESP8266SAM.h>
#ifdef USE_XL9555
#include "ExtensionIOXL9555.hpp"
extern ExtensionIOXL9555 io;
#endif
#define AUDIO_THREAD_INTERVAL_MS 100 #define AUDIO_THREAD_INTERVAL_MS 100
class AudioThread : public concurrency::OSThread class AudioThread : public concurrency::OSThread
@ -20,6 +25,9 @@ class AudioThread : public concurrency::OSThread
void beginRttl(const void *data, uint32_t len) void beginRttl(const void *data, uint32_t len)
{ {
#ifdef T_LORA_PAGER
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
#endif
setCPUFast(true); setCPUFast(true);
rtttlFile = new AudioFileSourcePROGMEM(data, len); rtttlFile = new AudioFileSourcePROGMEM(data, len);
i2sRtttl = new AudioGeneratorRTTTL(); i2sRtttl = new AudioGeneratorRTTTL();
@ -46,6 +54,9 @@ class AudioThread : public concurrency::OSThread
rtttlFile = nullptr; rtttlFile = nullptr;
setCPUFast(false); setCPUFast(false);
#ifdef T_LORA_PAGER
io.digitalWrite(EXPANDS_AMP_EN, LOW);
#endif
} }
void readAloud(const char *text) void readAloud(const char *text)
@ -56,10 +67,16 @@ class AudioThread : public concurrency::OSThread
i2sRtttl = nullptr; i2sRtttl = nullptr;
} }
#ifdef T_LORA_PAGER
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
#endif
ESP8266SAM *sam = new ESP8266SAM; ESP8266SAM *sam = new ESP8266SAM;
sam->Say(audioOut, text); sam->Say(audioOut, text);
delete sam; delete sam;
setCPUFast(false); setCPUFast(false);
#ifdef T_LORA_PAGER
io.digitalWrite(EXPANDS_AMP_EN, LOW);
#endif
} }
protected: protected:

View File

@ -3,16 +3,66 @@
InputBroker *inputBroker = nullptr; 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<const InputEvent *> *source) void InputBroker::registerSource(Observable<const InputEvent *> *source)
{ {
this->inputEventObserver.observe(source); this->inputEventObserver.observe(source);
} }
#ifdef HAS_FREE_RTOS
void InputBroker::requestPollSoon(InputPollable *pollable)
{
if (xPortInIsrContext() == pdTRUE) {
xQueueSendFromISR(pollSoonQueue, &pollable, NULL);
} else {
xQueueSend(pollSoonQueue, &pollable, 0);
}
}
void InputBroker::queueInputEvent(const InputEvent *event)
{
if (xPortInIsrContext() == pdTRUE) {
xQueueSendFromISR(inputEventQueue, event, NULL);
} else {
xQueueSend(inputEventQueue, event, portMAX_DELAY);
}
}
void InputBroker::processInputEventQueue()
{
InputEvent event;
while (xQueueReceive(inputEventQueue, &event, 0)) {
handleInputEvent(&event);
}
}
#endif
int InputBroker::handleInputEvent(const InputEvent *event) int InputBroker::handleInputEvent(const InputEvent *event)
{ {
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
this->notifyObservers(event); this->notifyObservers(event);
return 0; return 0;
} }
#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

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "Observer.h" #include "Observer.h"
#include "freertosinc.h"
enum input_broker_event { enum input_broker_event {
INPUT_BROKER_NONE = 0, INPUT_BROKER_NONE = 0,
@ -41,6 +43,13 @@ typedef struct _InputEvent {
uint16_t touchX; uint16_t touchX;
uint16_t touchY; uint16_t touchY;
} InputEvent; } InputEvent;
class InputPollable
{
public:
virtual void pollOnce() = 0;
};
class InputBroker : public Observable<const InputEvent *> class InputBroker : public Observable<const InputEvent *>
{ {
CallbackObserver<InputBroker, const InputEvent *> inputEventObserver = CallbackObserver<InputBroker, const InputEvent *> inputEventObserver =
@ -50,9 +59,22 @@ class InputBroker : public Observable<const InputEvent *>
InputBroker(); InputBroker();
void registerSource(Observable<const InputEvent *> *source); void registerSource(Observable<const InputEvent *> *source);
void injectInputEvent(const InputEvent *event) { handleInputEvent(event); } void injectInputEvent(const InputEvent *event) { handleInputEvent(event); }
#ifdef HAS_FREE_RTOS
void requestPollSoon(InputPollable *pollable);
void queueInputEvent(const InputEvent *event);
void processInputEventQueue();
#endif
protected: protected:
int handleInputEvent(const InputEvent *event); 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; extern InputBroker *inputBroker;

View File

@ -8,7 +8,7 @@
RotaryEncoderImpl *rotaryEncoderImpl; RotaryEncoderImpl *rotaryEncoderImpl;
RotaryEncoderImpl::RotaryEncoderImpl() : concurrency::OSThread(ORIGIN_NAME), originName(ORIGIN_NAME) RotaryEncoderImpl::RotaryEncoderImpl()
{ {
rotary = nullptr; rotary = nullptr;
} }
@ -18,7 +18,6 @@ bool RotaryEncoderImpl::init()
if (!moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.inputbroker_pin_a == 0 || if (!moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.inputbroker_pin_a == 0 ||
moduleConfig.canned_message.inputbroker_pin_b == 0) { moduleConfig.canned_message.inputbroker_pin_b == 0) {
// Input device is disabled. // Input device is disabled.
disable();
return false; return false;
} }
@ -30,7 +29,11 @@ bool RotaryEncoderImpl::init()
moduleConfig.canned_message.inputbroker_pin_press); moduleConfig.canned_message.inputbroker_pin_press);
rotary->resetButton(); rotary->resetButton();
inputBroker->registerSource(this); interruptInstance = this;
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);
LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a, 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, moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw,
@ -38,36 +41,36 @@ bool RotaryEncoderImpl::init()
return true; return true;
} }
int32_t RotaryEncoderImpl::runOnce() void RotaryEncoderImpl::pollOnce()
{ {
InputEvent e{originName, INPUT_BROKER_NONE, 0, 0, 0}; InputEvent e{ORIGIN_NAME, INPUT_BROKER_NONE, 0, 0, 0};
static uint32_t lastPressed = millis(); static uint32_t lastPressed = millis();
if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) { if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) {
if (lastPressed + 200 < millis()) { if (lastPressed + 200 < millis()) {
LOG_DEBUG("Rotary event Press"); LOG_DEBUG("Rotary event Press");
lastPressed = millis(); lastPressed = millis();
e.inputEvent = this->eventPressed; e.inputEvent = this->eventPressed;
} inputBroker->queueInputEvent(&e);
} 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;
} }
} }
if (e.inputEvent != INPUT_BROKER_NONE) { switch (rotary->process()) {
this->notifyObservers(&e); case RotaryEncoder::DIRECTION_CW:
LOG_DEBUG("Rotary event CW");
e.inputEvent = this->eventCw;
inputBroker->queueInputEvent(&e);
break;
case RotaryEncoder::DIRECTION_CCW:
LOG_DEBUG("Rotary event CCW");
e.inputEvent = this->eventCcw;
inputBroker->queueInputEvent(&e);
break;
default:
break;
} }
return 10;
} }
RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance;
#endif #endif

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
// This is a non-interrupt version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library) // This is a version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library)
#include "InputBroker.h" #include "InputBroker.h"
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
@ -8,21 +8,21 @@
class RotaryEncoder; class RotaryEncoder;
class RotaryEncoderImpl : public Observable<const InputEvent *>, public concurrency::OSThread class RotaryEncoderImpl : public InputPollable
{ {
public: public:
RotaryEncoderImpl(); RotaryEncoderImpl();
bool init(void); bool init(void);
virtual void pollOnce() override;
protected: protected:
virtual int32_t runOnce() override; static RotaryEncoderImpl *interruptInstance;
input_broker_event eventCw = INPUT_BROKER_NONE; input_broker_event eventCw = INPUT_BROKER_NONE;
input_broker_event eventCcw = INPUT_BROKER_NONE; input_broker_event eventCcw = INPUT_BROKER_NONE;
input_broker_event eventPressed = INPUT_BROKER_NONE; input_broker_event eventPressed = INPUT_BROKER_NONE;
RotaryEncoder *rotary; RotaryEncoder *rotary;
const char *originName;
}; };
extern RotaryEncoderImpl *rotaryEncoderImpl; extern RotaryEncoderImpl *rotaryEncoderImpl;

View File

@ -381,7 +381,7 @@ void setup()
io.pinMode(EXPANDS_DRV_EN, OUTPUT); io.pinMode(EXPANDS_DRV_EN, OUTPUT);
io.digitalWrite(EXPANDS_DRV_EN, HIGH); io.digitalWrite(EXPANDS_DRV_EN, HIGH);
io.pinMode(EXPANDS_AMP_EN, OUTPUT); 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.pinMode(EXPANDS_LORA_EN, OUTPUT);
io.digitalWrite(EXPANDS_LORA_EN, HIGH); io.digitalWrite(EXPANDS_LORA_EN, HIGH);
io.pinMode(EXPANDS_GPS_EN, OUTPUT); io.pinMode(EXPANDS_GPS_EN, OUTPUT);
@ -1600,6 +1600,9 @@ void loop()
#endif #endif
service->loop(); service->loop();
#if !MESHTASTIC_EXCLUDE_INPUTBROKER && defined(HAS_FREE_RTOS)
inputBroker->processInputEventQueue();
#endif
#if defined(LGFX_SDL) #if defined(LGFX_SDL)
if (screen) { if (screen) {
auto dispdev = screen->getDisplayDevice(); auto dispdev = screen->getDisplayDevice();

View File

@ -151,3 +151,6 @@
// No serial ports on this board - ONLY use segger in memory console // No serial ports on this board - ONLY use segger in memory console
#define USE_SEGGER #define USE_SEGGER
#endif #endif
// Detect if running in ISR context (ARM Cortex-M4)
#define xPortInIsrContext() ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) == 0 ? pdFALSE : pdTRUE)

View File

@ -36,3 +36,6 @@
#elif defined(PRIVATE_HW) #elif defined(PRIVATE_HW)
#define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW #define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW
#endif #endif
// Detect if running in ISR context (ARM Cortex-M33 / RISC-V)
#define xPortInIsrContext() (__get_current_exception() == 0 ? pdFALSE : pdTRUE)

View File

@ -15,7 +15,7 @@ build_flags = ${esp32s3_base.build_flags}
-D SDCARD_USE_SPI1 -D SDCARD_USE_SPI1
-D ENABLE_ROTARY_PULLUP -D ENABLE_ROTARY_PULLUP
-D ENABLE_BUTTON_PULLUP -D ENABLE_BUTTON_PULLUP
-D HALF_STEP -D ROTARY_BUXTRONICS
lib_deps = ${esp32s3_base.lib_deps} lib_deps = ${esp32s3_base.lib_deps}
lovyan03/LovyanGFX@1.2.7 lovyan03/LovyanGFX@1.2.7
@ -26,7 +26,7 @@ lib_deps = ${esp32s3_base.lib_deps}
lewisxhe/SensorLib@0.3.1 lewisxhe/SensorLib@0.3.1
https://github.com/pschatzmann/arduino-audio-driver/archive/refs/tags/v0.1.3.zip 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/BQ27220/archive/07d92be846abd8a0258a50c23198dac0858b22ed.zip
https://github.com/mverch67/RotaryEncoder/archive/25a59d5745a6645536f921427d80b08e78f886d4.zip https://github.com/mverch67/RotaryEncoder/archive/da958a21389cbcd485989705df602a33e092dd88.zip
[env:tlora-pager-tft] [env:tlora-pager-tft]
board_level = extra board_level = extra