mirror of
https://github.com/meshtastic/firmware.git
synced 2026-02-03 15:42:02 +00:00
* Move input init to an init function in iInputBroker * Unbreak targets with EXCLUDE_INPUTBROKER * Update src/input/InputBroker.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix conditional compilation for input broker * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Trunk --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
387 lines
13 KiB
C++
387 lines
13 KiB
C++
#include "InputBroker.h"
|
|
#include "PowerFSM.h" // needed for event trigger
|
|
#include "configuration.h"
|
|
#include "graphics/Screen.h"
|
|
#include "modules/ExternalNotificationModule.h"
|
|
|
|
#if ARCH_PORTDUINO
|
|
#include "input/LinuxInputImpl.h"
|
|
#include "input/SeesawRotary.h"
|
|
#include "platform/portduino/PortduinoGlue.h"
|
|
#endif
|
|
|
|
#if !MESHTASTIC_EXCLUDE_INPUTBROKER
|
|
#include "input/ExpressLRSFiveWay.h"
|
|
#include "input/RotaryEncoderImpl.h"
|
|
#include "input/RotaryEncoderInterruptImpl1.h"
|
|
#include "input/SerialKeyboardImpl.h"
|
|
#include "input/UpDownInterruptImpl1.h"
|
|
#include "input/i2cButton.h"
|
|
#if HAS_TRACKBALL
|
|
#include "input/TrackballInterruptImpl1.h"
|
|
#endif
|
|
|
|
#include "modules/StatusLEDModule.h"
|
|
|
|
#if !MESHTASTIC_EXCLUDE_I2C
|
|
#include "input/cardKbI2cImpl.h"
|
|
#endif
|
|
#include "input/kbMatrixImpl.h"
|
|
#endif
|
|
|
|
#if HAS_BUTTON || defined(ARCH_PORTDUINO)
|
|
#include "input/ButtonThread.h"
|
|
|
|
#if defined(BUTTON_PIN_TOUCH)
|
|
ButtonThread *TouchButtonThread = nullptr;
|
|
#if defined(TTGO_T_ECHO_PLUS) && defined(PIN_EINK_EN)
|
|
static bool touchBacklightWasOn = false;
|
|
static bool touchBacklightActive = false;
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO)
|
|
ButtonThread *UserButtonThread = nullptr;
|
|
#endif
|
|
|
|
#if defined(ALT_BUTTON_PIN)
|
|
ButtonThread *BackButtonThread = nullptr;
|
|
#endif
|
|
|
|
#if defined(CANCEL_BUTTON_PIN)
|
|
ButtonThread *CancelButtonThread = nullptr;
|
|
#endif
|
|
|
|
#endif
|
|
|
|
InputBroker *inputBroker = nullptr;
|
|
|
|
InputBroker::InputBroker()
|
|
{
|
|
#if defined(HAS_FREE_RTOS) && !defined(ARCH_RP2040)
|
|
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)
|
|
{
|
|
this->inputEventObserver.observe(source);
|
|
}
|
|
|
|
#if defined(HAS_FREE_RTOS) && !defined(ARCH_RP2040)
|
|
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)
|
|
{
|
|
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
|
|
|
|
if (event && event->inputEvent != INPUT_BROKER_NONE && externalNotificationModule &&
|
|
moduleConfig.external_notification.enabled && externalNotificationModule->nagging()) {
|
|
externalNotificationModule->stopNow();
|
|
}
|
|
|
|
this->notifyObservers(event);
|
|
return 0;
|
|
}
|
|
|
|
#if defined(HAS_FREE_RTOS) && !defined(ARCH_RP2040)
|
|
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
|
|
|
|
void InputBroker::Init()
|
|
{
|
|
|
|
#ifdef BUTTON_PIN
|
|
#ifdef ARCH_ESP32
|
|
|
|
#if ESP_ARDUINO_VERSION_MAJOR >= 3
|
|
#ifdef BUTTON_NEED_PULLUP
|
|
pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT_PULLUP);
|
|
#else
|
|
pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT); // default to BUTTON_PIN
|
|
#endif
|
|
#else
|
|
pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT); // default to BUTTON_PIN
|
|
#ifdef BUTTON_NEED_PULLUP
|
|
gpio_pullup_en((gpio_num_t)(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN));
|
|
delay(10);
|
|
#endif
|
|
#ifdef BUTTON_NEED_PULLUP2
|
|
gpio_pullup_en((gpio_num_t)BUTTON_NEED_PULLUP2);
|
|
delay(10);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
// buttons are now inputBroker, so have to come after setupModules
|
|
#if HAS_BUTTON
|
|
int pullup_sense = 0;
|
|
#ifdef INPUT_PULLUP_SENSE
|
|
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
|
|
#ifdef BUTTON_SENSE_TYPE
|
|
pullup_sense = BUTTON_SENSE_TYPE;
|
|
#else
|
|
pullup_sense = INPUT_PULLUP_SENSE;
|
|
#endif
|
|
#endif
|
|
#if defined(ARCH_PORTDUINO)
|
|
|
|
if (portduino_config.userButtonPin.enabled) {
|
|
|
|
LOG_DEBUG("Use GPIO%02d for button", portduino_config.userButtonPin.pin);
|
|
UserButtonThread = new ButtonThread("UserButton");
|
|
if (screen) {
|
|
ButtonConfig config;
|
|
config.pinNumber = (uint8_t)portduino_config.userButtonPin.pin;
|
|
config.activeLow = true;
|
|
config.activePullup = true;
|
|
config.pullupSense = INPUT_PULLUP;
|
|
config.intRoutine = []() {
|
|
UserButtonThread->userButton.tick();
|
|
UserButtonThread->setIntervalFromNow(0);
|
|
runASAP = true;
|
|
BaseType_t higherWake = 0;
|
|
concurrency::mainDelay.interruptFromISR(&higherWake);
|
|
};
|
|
config.singlePress = INPUT_BROKER_USER_PRESS;
|
|
config.longPress = INPUT_BROKER_SELECT;
|
|
UserButtonThread->initButton(config);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUTTON_PIN_TOUCH
|
|
TouchButtonThread = new ButtonThread("BackButton");
|
|
ButtonConfig touchConfig;
|
|
touchConfig.pinNumber = BUTTON_PIN_TOUCH;
|
|
touchConfig.activeLow = true;
|
|
touchConfig.activePullup = true;
|
|
touchConfig.pullupSense = pullup_sense;
|
|
touchConfig.intRoutine = []() {
|
|
TouchButtonThread->userButton.tick();
|
|
TouchButtonThread->setIntervalFromNow(0);
|
|
runASAP = true;
|
|
BaseType_t higherWake = 0;
|
|
concurrency::mainDelay.interruptFromISR(&higherWake);
|
|
};
|
|
touchConfig.singlePress = INPUT_BROKER_NONE;
|
|
touchConfig.longPress = INPUT_BROKER_BACK;
|
|
#if defined(TTGO_T_ECHO_PLUS) && defined(PIN_EINK_EN)
|
|
// On T-Echo Plus the touch pad should only drive the backlight, not UI navigation/sounds
|
|
touchConfig.longPress = INPUT_BROKER_NONE;
|
|
touchConfig.suppressLeadUpSound = true;
|
|
touchConfig.onPress = []() {
|
|
touchBacklightWasOn = uiconfig.screen_brightness == 1;
|
|
if (!touchBacklightWasOn) {
|
|
digitalWrite(PIN_EINK_EN, HIGH);
|
|
}
|
|
touchBacklightActive = true;
|
|
};
|
|
touchConfig.onRelease = []() {
|
|
if (touchBacklightActive && !touchBacklightWasOn) {
|
|
digitalWrite(PIN_EINK_EN, LOW);
|
|
}
|
|
touchBacklightActive = false;
|
|
};
|
|
#endif
|
|
TouchButtonThread->initButton(touchConfig);
|
|
#endif
|
|
|
|
#if defined(CANCEL_BUTTON_PIN)
|
|
// Buttons. Moved here cause we need NodeDB to be initialized
|
|
CancelButtonThread = new ButtonThread("CancelButton");
|
|
ButtonConfig cancelConfig;
|
|
cancelConfig.pinNumber = CANCEL_BUTTON_PIN;
|
|
cancelConfig.activeLow = CANCEL_BUTTON_ACTIVE_LOW;
|
|
cancelConfig.activePullup = CANCEL_BUTTON_ACTIVE_PULLUP;
|
|
cancelConfig.pullupSense = pullup_sense;
|
|
cancelConfig.intRoutine = []() {
|
|
CancelButtonThread->userButton.tick();
|
|
CancelButtonThread->setIntervalFromNow(0);
|
|
runASAP = true;
|
|
BaseType_t higherWake = 0;
|
|
concurrency::mainDelay.interruptFromISR(&higherWake);
|
|
};
|
|
cancelConfig.singlePress = INPUT_BROKER_CANCEL;
|
|
cancelConfig.longPress = INPUT_BROKER_SHUTDOWN;
|
|
cancelConfig.longPressTime = 4000;
|
|
CancelButtonThread->initButton(cancelConfig);
|
|
#endif
|
|
|
|
#if defined(ALT_BUTTON_PIN)
|
|
// Buttons. Moved here cause we need NodeDB to be initialized
|
|
BackButtonThread = new ButtonThread("BackButton");
|
|
ButtonConfig backConfig;
|
|
backConfig.pinNumber = ALT_BUTTON_PIN;
|
|
backConfig.activeLow = ALT_BUTTON_ACTIVE_LOW;
|
|
backConfig.activePullup = ALT_BUTTON_ACTIVE_PULLUP;
|
|
backConfig.pullupSense = pullup_sense;
|
|
backConfig.intRoutine = []() {
|
|
BackButtonThread->userButton.tick();
|
|
BackButtonThread->setIntervalFromNow(0);
|
|
runASAP = true;
|
|
BaseType_t higherWake = 0;
|
|
concurrency::mainDelay.interruptFromISR(&higherWake);
|
|
};
|
|
backConfig.singlePress = INPUT_BROKER_ALT_PRESS;
|
|
backConfig.longPress = INPUT_BROKER_ALT_LONG;
|
|
backConfig.longPressTime = 500;
|
|
BackButtonThread->initButton(backConfig);
|
|
#endif
|
|
|
|
#if defined(BUTTON_PIN)
|
|
#if defined(USERPREFS_BUTTON_PIN)
|
|
int _pinNum = config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN;
|
|
#else
|
|
int _pinNum = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN;
|
|
#endif
|
|
#ifndef BUTTON_ACTIVE_LOW
|
|
#define BUTTON_ACTIVE_LOW true
|
|
#endif
|
|
#ifndef BUTTON_ACTIVE_PULLUP
|
|
#define BUTTON_ACTIVE_PULLUP true
|
|
#endif
|
|
|
|
// Buttons. Moved here cause we need NodeDB to be initialized
|
|
// If your variant.h has a BUTTON_PIN defined, go ahead and define BUTTON_ACTIVE_LOW and BUTTON_ACTIVE_PULLUP
|
|
UserButtonThread = new ButtonThread("UserButton");
|
|
if (screen) {
|
|
ButtonConfig userConfig;
|
|
userConfig.pinNumber = (uint8_t)_pinNum;
|
|
userConfig.activeLow = BUTTON_ACTIVE_LOW;
|
|
userConfig.activePullup = BUTTON_ACTIVE_PULLUP;
|
|
userConfig.pullupSense = pullup_sense;
|
|
userConfig.intRoutine = []() {
|
|
UserButtonThread->userButton.tick();
|
|
UserButtonThread->setIntervalFromNow(0);
|
|
runASAP = true;
|
|
BaseType_t higherWake = 0;
|
|
concurrency::mainDelay.interruptFromISR(&higherWake);
|
|
};
|
|
userConfig.singlePress = INPUT_BROKER_USER_PRESS;
|
|
userConfig.longPress = INPUT_BROKER_SELECT;
|
|
userConfig.longPressTime = 500;
|
|
userConfig.longLongPress = INPUT_BROKER_SHUTDOWN;
|
|
UserButtonThread->initButton(userConfig);
|
|
} else {
|
|
ButtonConfig userConfigNoScreen;
|
|
userConfigNoScreen.pinNumber = (uint8_t)_pinNum;
|
|
userConfigNoScreen.activeLow = BUTTON_ACTIVE_LOW;
|
|
userConfigNoScreen.activePullup = BUTTON_ACTIVE_PULLUP;
|
|
userConfigNoScreen.pullupSense = pullup_sense;
|
|
userConfigNoScreen.intRoutine = []() {
|
|
UserButtonThread->userButton.tick();
|
|
UserButtonThread->setIntervalFromNow(0);
|
|
runASAP = true;
|
|
BaseType_t higherWake = 0;
|
|
concurrency::mainDelay.interruptFromISR(&higherWake);
|
|
};
|
|
userConfigNoScreen.singlePress = INPUT_BROKER_USER_PRESS;
|
|
userConfigNoScreen.longPress = INPUT_BROKER_NONE;
|
|
userConfigNoScreen.longPressTime = 500;
|
|
userConfigNoScreen.longLongPress = INPUT_BROKER_SHUTDOWN;
|
|
userConfigNoScreen.doublePress = INPUT_BROKER_SEND_PING;
|
|
userConfigNoScreen.triplePress = INPUT_BROKER_GPS_TOGGLE;
|
|
UserButtonThread->initButton(userConfigNoScreen);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
|
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
|
#if defined(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;
|
|
}
|
|
#elif defined(INPUTDRIVER_ENCODER_TYPE) && (INPUTDRIVER_ENCODER_TYPE == 2)
|
|
upDownInterruptImpl1 = new UpDownInterruptImpl1();
|
|
if (!upDownInterruptImpl1->init()) {
|
|
delete upDownInterruptImpl1;
|
|
upDownInterruptImpl1 = nullptr;
|
|
}
|
|
#else
|
|
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
|
|
if (!rotaryEncoderInterruptImpl1->init()) {
|
|
delete rotaryEncoderInterruptImpl1;
|
|
rotaryEncoderInterruptImpl1 = nullptr;
|
|
}
|
|
#endif
|
|
cardKbI2cImpl = new CardKbI2cImpl();
|
|
cardKbI2cImpl->init();
|
|
#if defined(M5STACK_UNITC6L)
|
|
i2cButton = new i2cButtonThread("i2cButtonThread");
|
|
#endif
|
|
#ifdef INPUTBROKER_MATRIX_TYPE
|
|
kbMatrixImpl = new KbMatrixImpl();
|
|
kbMatrixImpl->init();
|
|
#endif // INPUTBROKER_MATRIX_TYPE
|
|
#ifdef INPUTBROKER_SERIAL_TYPE
|
|
aSerialKeyboardImpl = new SerialKeyboardImpl();
|
|
aSerialKeyboardImpl->init();
|
|
#endif // INPUTBROKER_MATRIX_TYPE
|
|
}
|
|
#endif // HAS_BUTTON
|
|
#if ARCH_PORTDUINO
|
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR && portduino_config.i2cdev != "") {
|
|
seesawRotary = new SeesawRotary("SeesawRotary");
|
|
if (!seesawRotary->init()) {
|
|
delete seesawRotary;
|
|
seesawRotary = nullptr;
|
|
}
|
|
aLinuxInputImpl = new LinuxInputImpl();
|
|
aLinuxInputImpl->init();
|
|
}
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_INPUTBROKER && HAS_TRACKBALL
|
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
|
trackballInterruptImpl1 = new TrackballInterruptImpl1();
|
|
trackballInterruptImpl1->init(TB_DOWN, TB_UP, TB_LEFT, TB_RIGHT, TB_PRESS);
|
|
}
|
|
#endif
|
|
#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE
|
|
expressLRSFiveWayInput = new ExpressLRSFiveWay();
|
|
#endif
|
|
}
|