Skip to content

Commit c48a64e

Browse files
authored
Merge pull request #7986 from WillyJL/fix/tlora-pager-rotary-amplifier
T-Lora Pager: Fully fix rotary encoder and speaker fuzzing/popping
2 parents a62e1cf + e954591 commit c48a64e

File tree

9 files changed

+133
-32
lines changed

9 files changed

+133
-32
lines changed

src/AudioThread.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
#include <AudioOutputI2S.h>
1212
#include <ESP8266SAM.h>
1313

14+
#ifdef USE_XL9555
15+
#include "ExtensionIOXL9555.hpp"
16+
extern ExtensionIOXL9555 io;
17+
#endif
18+
1419
#define AUDIO_THREAD_INTERVAL_MS 100
1520

1621
class AudioThread : public concurrency::OSThread
@@ -20,6 +25,9 @@ class AudioThread : public concurrency::OSThread
2025

2126
void beginRttl(const void *data, uint32_t len)
2227
{
28+
#ifdef T_LORA_PAGER
29+
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
30+
#endif
2331
setCPUFast(true);
2432
rtttlFile = new AudioFileSourcePROGMEM(data, len);
2533
i2sRtttl = new AudioGeneratorRTTTL();
@@ -46,6 +54,9 @@ class AudioThread : public concurrency::OSThread
4654
rtttlFile = nullptr;
4755

4856
setCPUFast(false);
57+
#ifdef T_LORA_PAGER
58+
io.digitalWrite(EXPANDS_AMP_EN, LOW);
59+
#endif
4960
}
5061

5162
void readAloud(const char *text)
@@ -56,10 +67,16 @@ class AudioThread : public concurrency::OSThread
5667
i2sRtttl = nullptr;
5768
}
5869

70+
#ifdef T_LORA_PAGER
71+
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
72+
#endif
5973
ESP8266SAM *sam = new ESP8266SAM;
6074
sam->Say(audioOut, text);
6175
delete sam;
6276
setCPUFast(false);
77+
#ifdef T_LORA_PAGER
78+
io.digitalWrite(EXPANDS_AMP_EN, LOW);
79+
#endif
6380
}
6481

6582
protected:

src/input/InputBroker.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,66 @@
33

44
InputBroker *inputBroker = nullptr;
55

6-
InputBroker::InputBroker(){};
6+
InputBroker::InputBroker()
7+
{
8+
#ifdef HAS_FREE_RTOS
9+
inputEventQueue = xQueueCreate(5, sizeof(InputEvent));
10+
pollSoonQueue = xQueueCreate(5, sizeof(InputPollable *));
11+
xTaskCreate(pollSoonWorker, "input-pollSoon", 2 * 1024, this, 10, &pollSoonTask);
12+
#endif
13+
}
714

815
void InputBroker::registerSource(Observable<const InputEvent *> *source)
916
{
1017
this->inputEventObserver.observe(source);
1118
}
1219

20+
#ifdef HAS_FREE_RTOS
21+
void InputBroker::requestPollSoon(InputPollable *pollable)
22+
{
23+
if (xPortInIsrContext() == pdTRUE) {
24+
xQueueSendFromISR(pollSoonQueue, &pollable, NULL);
25+
} else {
26+
xQueueSend(pollSoonQueue, &pollable, 0);
27+
}
28+
}
29+
30+
void InputBroker::queueInputEvent(const InputEvent *event)
31+
{
32+
if (xPortInIsrContext() == pdTRUE) {
33+
xQueueSendFromISR(inputEventQueue, event, NULL);
34+
} else {
35+
xQueueSend(inputEventQueue, event, portMAX_DELAY);
36+
}
37+
}
38+
39+
void InputBroker::processInputEventQueue()
40+
{
41+
InputEvent event;
42+
while (xQueueReceive(inputEventQueue, &event, 0)) {
43+
handleInputEvent(&event);
44+
}
45+
}
46+
#endif
47+
1348
int InputBroker::handleInputEvent(const InputEvent *event)
1449
{
1550
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
1651
this->notifyObservers(event);
1752
return 0;
18-
}
53+
}
54+
55+
#ifdef HAS_FREE_RTOS
56+
void InputBroker::pollSoonWorker(void *p)
57+
{
58+
InputBroker *instance = (InputBroker *)p;
59+
while (true) {
60+
InputPollable *pollable = NULL;
61+
xQueueReceive(instance->pollSoonQueue, &pollable, portMAX_DELAY);
62+
if (pollable) {
63+
pollable->pollOnce();
64+
}
65+
}
66+
vTaskDelete(NULL);
67+
}
68+
#endif

src/input/InputBroker.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
2+
23
#include "Observer.h"
4+
#include "freertosinc.h"
35

46
enum input_broker_event {
57
INPUT_BROKER_NONE = 0,
@@ -41,6 +43,13 @@ typedef struct _InputEvent {
4143
uint16_t touchX;
4244
uint16_t touchY;
4345
} InputEvent;
46+
47+
class InputPollable
48+
{
49+
public:
50+
virtual void pollOnce() = 0;
51+
};
52+
4453
class InputBroker : public Observable<const InputEvent *>
4554
{
4655
CallbackObserver<InputBroker, const InputEvent *> inputEventObserver =
@@ -50,9 +59,22 @@ class InputBroker : public Observable<const InputEvent *>
5059
InputBroker();
5160
void registerSource(Observable<const InputEvent *> *source);
5261
void injectInputEvent(const InputEvent *event) { handleInputEvent(event); }
62+
#ifdef HAS_FREE_RTOS
63+
void requestPollSoon(InputPollable *pollable);
64+
void queueInputEvent(const InputEvent *event);
65+
void processInputEventQueue();
66+
#endif
5367

5468
protected:
5569
int handleInputEvent(const InputEvent *event);
70+
71+
private:
72+
#ifdef HAS_FREE_RTOS
73+
QueueHandle_t inputEventQueue;
74+
QueueHandle_t pollSoonQueue;
75+
TaskHandle_t pollSoonTask;
76+
static void pollSoonWorker(void *p);
77+
#endif
5678
};
5779

5880
extern InputBroker *inputBroker;

src/input/RotaryEncoderImpl.cpp

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
RotaryEncoderImpl *rotaryEncoderImpl;
1010

11-
RotaryEncoderImpl::RotaryEncoderImpl() : concurrency::OSThread(ORIGIN_NAME), originName(ORIGIN_NAME)
11+
RotaryEncoderImpl::RotaryEncoderImpl()
1212
{
1313
rotary = nullptr;
1414
}
@@ -18,7 +18,6 @@ bool RotaryEncoderImpl::init()
1818
if (!moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.inputbroker_pin_a == 0 ||
1919
moduleConfig.canned_message.inputbroker_pin_b == 0) {
2020
// Input device is disabled.
21-
disable();
2221
return false;
2322
}
2423

@@ -30,44 +29,48 @@ bool RotaryEncoderImpl::init()
3029
moduleConfig.canned_message.inputbroker_pin_press);
3130
rotary->resetButton();
3231

33-
inputBroker->registerSource(this);
32+
interruptInstance = this;
33+
auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); };
34+
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE);
35+
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE);
36+
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE);
3437

3538
LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a,
3639
moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw,
3740
eventPressed);
3841
return true;
3942
}
4043

41-
int32_t RotaryEncoderImpl::runOnce()
44+
void RotaryEncoderImpl::pollOnce()
4245
{
43-
InputEvent e{originName, INPUT_BROKER_NONE, 0, 0, 0};
46+
InputEvent e{ORIGIN_NAME, INPUT_BROKER_NONE, 0, 0, 0};
47+
4448
static uint32_t lastPressed = millis();
4549
if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) {
4650
if (lastPressed + 200 < millis()) {
4751
LOG_DEBUG("Rotary event Press");
4852
lastPressed = millis();
4953
e.inputEvent = this->eventPressed;
50-
}
51-
} else {
52-
switch (rotary->process()) {
53-
case RotaryEncoder::DIRECTION_CW:
54-
LOG_DEBUG("Rotary event CW");
55-
e.inputEvent = this->eventCw;
56-
break;
57-
case RotaryEncoder::DIRECTION_CCW:
58-
LOG_DEBUG("Rotary event CCW");
59-
e.inputEvent = this->eventCcw;
60-
break;
61-
default:
62-
break;
54+
inputBroker->queueInputEvent(&e);
6355
}
6456
}
6557

66-
if (e.inputEvent != INPUT_BROKER_NONE) {
67-
this->notifyObservers(&e);
58+
switch (rotary->process()) {
59+
case RotaryEncoder::DIRECTION_CW:
60+
LOG_DEBUG("Rotary event CW");
61+
e.inputEvent = this->eventCw;
62+
inputBroker->queueInputEvent(&e);
63+
break;
64+
case RotaryEncoder::DIRECTION_CCW:
65+
LOG_DEBUG("Rotary event CCW");
66+
e.inputEvent = this->eventCcw;
67+
inputBroker->queueInputEvent(&e);
68+
break;
69+
default:
70+
break;
6871
}
69-
70-
return 10;
7172
}
7273

74+
RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance;
75+
7376
#endif

src/input/RotaryEncoderImpl.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
#pragma once
22

3-
// This is a non-interrupt version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library)
3+
// This is a version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library)
44

55
#include "InputBroker.h"
66
#include "concurrency/OSThread.h"
77
#include "mesh/NodeDB.h"
88

99
class RotaryEncoder;
1010

11-
class RotaryEncoderImpl : public Observable<const InputEvent *>, public concurrency::OSThread
11+
class RotaryEncoderImpl : public InputPollable
1212
{
1313
public:
1414
RotaryEncoderImpl();
1515
bool init(void);
16+
virtual void pollOnce() override;
1617

1718
protected:
18-
virtual int32_t runOnce() override;
19+
static RotaryEncoderImpl *interruptInstance;
1920

2021
input_broker_event eventCw = INPUT_BROKER_NONE;
2122
input_broker_event eventCcw = INPUT_BROKER_NONE;
2223
input_broker_event eventPressed = INPUT_BROKER_NONE;
2324

2425
RotaryEncoder *rotary;
25-
const char *originName;
2626
};
2727

2828
extern RotaryEncoderImpl *rotaryEncoderImpl;

src/main.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ void setup()
381381
io.pinMode(EXPANDS_DRV_EN, OUTPUT);
382382
io.digitalWrite(EXPANDS_DRV_EN, HIGH);
383383
io.pinMode(EXPANDS_AMP_EN, OUTPUT);
384-
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
384+
io.digitalWrite(EXPANDS_AMP_EN, LOW);
385385
io.pinMode(EXPANDS_LORA_EN, OUTPUT);
386386
io.digitalWrite(EXPANDS_LORA_EN, HIGH);
387387
io.pinMode(EXPANDS_GPS_EN, OUTPUT);
@@ -1600,6 +1600,9 @@ void loop()
16001600
#endif
16011601

16021602
service->loop();
1603+
#if !MESHTASTIC_EXCLUDE_INPUTBROKER && defined(HAS_FREE_RTOS)
1604+
inputBroker->processInputEventQueue();
1605+
#endif
16031606
#if defined(LGFX_SDL)
16041607
if (screen) {
16051608
auto dispdev = screen->getDisplayDevice();

src/platform/nrf52/architecture.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,6 @@
151151
// No serial ports on this board - ONLY use segger in memory console
152152
#define USE_SEGGER
153153
#endif
154+
155+
// Detect if running in ISR context (ARM Cortex-M4)
156+
#define xPortInIsrContext() ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) == 0 ? pdFALSE : pdTRUE)

src/platform/rp2xx0/architecture.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,7 @@
3535
#define HW_VENDOR meshtastic_HardwareModel_RP2040_FEATHER_RFM95
3636
#elif defined(PRIVATE_HW)
3737
#define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW
38-
#endif
38+
#endif
39+
40+
// Detect if running in ISR context (ARM Cortex-M33 / RISC-V)
41+
#define xPortInIsrContext() (__get_current_exception() == 0 ? pdFALSE : pdTRUE)

variants/esp32s3/tlora-pager/platformio.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ build_flags = ${esp32s3_base.build_flags}
1515
-D SDCARD_USE_SPI1
1616
-D ENABLE_ROTARY_PULLUP
1717
-D ENABLE_BUTTON_PULLUP
18-
-D HALF_STEP
18+
-D ROTARY_BUXTRONICS
1919

2020
lib_deps = ${esp32s3_base.lib_deps}
2121
@@ -26,7 +26,7 @@ lib_deps = ${esp32s3_base.lib_deps}
2626
2727
https://github.com/pschatzmann/arduino-audio-driver/archive/refs/tags/v0.1.3.zip
2828
https://github.com/mverch67/BQ27220/archive/07d92be846abd8a0258a50c23198dac0858b22ed.zip
29-
https://github.com/mverch67/RotaryEncoder/archive/25a59d5745a6645536f921427d80b08e78f886d4.zip
29+
https://github.com/mverch67/RotaryEncoder/archive/da958a21389cbcd485989705df602a33e092dd88.zip
3030

3131
[env:tlora-pager-tft]
3232
board_level = extra

0 commit comments

Comments
 (0)