mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-13 00:22:10 +00:00
Make ButtonThread an InputBroker
This commit is contained in:
parent
80af8e76f0
commit
a115c78840
@ -47,7 +47,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "graphics/SharedUIDisplay.h"
|
||||
#include "graphics/emotes.h"
|
||||
#include "graphics/images.h"
|
||||
#include "input/ButtonThread.h"
|
||||
#include "input/ScanAndSelect.h"
|
||||
#include "input/TouchScreenImpl1.h"
|
||||
#include "main.h"
|
||||
@ -836,7 +835,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
||||
if (on != screenOn) {
|
||||
if (on) {
|
||||
LOG_INFO("Turn on screen");
|
||||
buttonThread->setScreenFlag(true);
|
||||
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
|
||||
#ifdef T_WATCH_S3
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
||||
@ -880,8 +878,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
||||
// eInkScreensaver parameter is usually NULL (default argument), default frame used instead
|
||||
setScreensaverFrames(einkScreensaver);
|
||||
#endif
|
||||
LOG_INFO("Turn off screen");
|
||||
buttonThread->setScreenFlag(false);
|
||||
#ifdef ELECROW_ThinkNode_M1
|
||||
if (digitalRead(PIN_EINK_EN) == HIGH) {
|
||||
digitalWrite(PIN_EINK_EN, LOW);
|
||||
@ -1761,6 +1757,7 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
|
||||
|
||||
int Screen::handleInputEvent(const InputEvent *event)
|
||||
{
|
||||
LOG_WARN("event %u", event->inputEvent);
|
||||
|
||||
if (NotificationRenderer::isOverlayBannerShowing()) {
|
||||
NotificationRenderer::inEvent = event->inputEvent;
|
||||
|
@ -27,17 +27,14 @@
|
||||
|
||||
using namespace concurrency;
|
||||
|
||||
ButtonThread *buttonThread; // Declared extern in header
|
||||
#if HAS_SCREEN
|
||||
extern CannedMessageModule *cannedMessageModule;
|
||||
#endif
|
||||
volatile ButtonThread::ButtonEventType ButtonThread::btnEvent = ButtonThread::BUTTON_EVENT_NONE;
|
||||
|
||||
#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN)
|
||||
OneButton ButtonThread::userButton; // Get reference to static member
|
||||
#endif
|
||||
ButtonThread::ButtonThread() : OSThread("Button")
|
||||
ButtonThread::ButtonThread(const char *name) : OSThread(name)
|
||||
{
|
||||
_originName = name;
|
||||
#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN)
|
||||
|
||||
#if defined(ARCH_PORTDUINO)
|
||||
@ -55,7 +52,7 @@ ButtonThread::ButtonThread() : OSThread("Button")
|
||||
#if defined(HELTEC_CAPSULE_SENSOR_V3) || defined(HELTEC_SENSOR_HUB)
|
||||
this->userButton = OneButton(pin, false, false);
|
||||
#elif defined(BUTTON_ACTIVE_LOW)
|
||||
this->userButton = OneButton(pin, BUTTON_ACTIVE_LOW, BUTTON_ACTIVE_PULLUP);
|
||||
userButton = OneButton(pin, BUTTON_ACTIVE_LOW, BUTTON_ACTIVE_PULLUP);
|
||||
#else
|
||||
this->userButton = OneButton(pin, true, true);
|
||||
#endif
|
||||
@ -72,20 +69,20 @@ ButtonThread::ButtonThread() : OSThread("Button")
|
||||
#endif
|
||||
|
||||
#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN)
|
||||
userButton.attachClick(userButtonPressed);
|
||||
userButton.setPressMs(BUTTON_LONGPRESS_MS);
|
||||
userButton.attachClick([]() { btnEvent = BUTTON_EVENT_PRESSED; });
|
||||
|
||||
userButton.setDebounceMs(1);
|
||||
if (screen) {
|
||||
userButton.setClickMs(20);
|
||||
userButton.setPressMs(500);
|
||||
} else {
|
||||
userButton.setPressMs(BUTTON_LONGPRESS_MS);
|
||||
userButton.setClickMs(BUTTON_CLICK_MS);
|
||||
userButton.attachDoubleClick(userButtonDoublePressed);
|
||||
userButton.attachMultiClick(userButtonMultiPressed,
|
||||
this); // Reference to instance: get click count from non-static OneButton
|
||||
}
|
||||
#if !defined(T_DECK) && \
|
||||
!defined( \
|
||||
ELECROW_ThinkNode_M2) // T-Deck immediately wakes up after shutdown, Thinknode M2 has this on the smaller ALT button
|
||||
#if !defined(ELECROW_ThinkNode_M2) // T-Deck immediately wakes up after shutdown, Thinknode M2 has this on the smaller ALT button
|
||||
userButton.attachLongPressStart(userButtonPressedLongStart);
|
||||
userButton.attachLongPressStop(userButtonPressedLongStop);
|
||||
#endif
|
||||
@ -230,10 +227,13 @@ int32_t ButtonThread::runOnce()
|
||||
playBoop();
|
||||
|
||||
// Forward single press to InputBroker (but NOT as DOWN/SELECT, just forward a "button press" event)
|
||||
if (inputBroker) {
|
||||
InputEvent evt = {"button", INPUT_BROKER_USER_PRESS, 0, 0, 0};
|
||||
inputBroker->injectInputEvent(&evt);
|
||||
}
|
||||
InputEvent evt;
|
||||
evt.source = _originName;
|
||||
evt.kbchar = 0;
|
||||
evt.touchX = 0;
|
||||
evt.touchY = 0;
|
||||
evt.inputEvent = INPUT_BROKER_USER_PRESS;
|
||||
this->notifyObservers(&evt);
|
||||
break;
|
||||
}
|
||||
case BUTTON_EVENT_LONG_PRESSED: {
|
||||
@ -245,7 +245,7 @@ int32_t ButtonThread::runOnce()
|
||||
// Forward long press to InputBroker (but NOT as DOWN/SELECT, just forward a "button long press" event)
|
||||
if (inputBroker) {
|
||||
InputEvent evt = {"button", INPUT_BROKER_SELECT, 0, 0, 0};
|
||||
inputBroker->injectInputEvent(&evt);
|
||||
this->notifyObservers(&evt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -269,10 +269,6 @@ int32_t ButtonThread::runOnce()
|
||||
externalNotificationModule->stopNow();
|
||||
break;
|
||||
}
|
||||
#ifdef ELECROW_ThinkNode_M1
|
||||
sendAdHocPosition();
|
||||
break;
|
||||
#endif
|
||||
|
||||
// Start tracking for potential combination
|
||||
waitingForLongPress = true;
|
||||
@ -300,10 +296,6 @@ int32_t ButtonThread::runOnce()
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
break;
|
||||
#endif
|
||||
// turn screen on or off
|
||||
screen_flag = !screen_flag;
|
||||
if (screen)
|
||||
screen->setOn(screen_flag);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -325,7 +317,8 @@ int32_t ButtonThread::runOnce()
|
||||
sendAdHocPosition();
|
||||
|
||||
// Show temporary on-screen confirmation banner for 3 seconds
|
||||
screen->showOverlayBanner("Ad-hoc Ping Sent", 3000);
|
||||
if (screen)
|
||||
screen->showOverlayBanner("Ad-hoc Ping Sent", 3000);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -344,25 +337,8 @@ int32_t ButtonThread::runOnce()
|
||||
case 3:
|
||||
if (!config.device.disable_triple_click && (gps != nullptr)) {
|
||||
gps->toggleGpsMode();
|
||||
|
||||
const char *statusMsg = (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED)
|
||||
? "GPS Enabled"
|
||||
: "GPS Disabled";
|
||||
|
||||
if (screen) {
|
||||
screen->forceDisplay(true); // Force a new UI frame, then force an EInk update
|
||||
screen->showOverlayBanner(statusMsg, 3000);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#elif defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M2)
|
||||
case 3:
|
||||
LOG_INFO("3 clicks: toggle buzzer");
|
||||
buzzer_flag = !buzzer_flag;
|
||||
if (!buzzer_flag)
|
||||
noTone(PIN_BUZZER);
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(USE_EINK) && defined(PIN_EINK_EN) && !defined(ELECROW_ThinkNode_M1) // i.e. T-Echo
|
||||
@ -370,20 +346,6 @@ int32_t ButtonThread::runOnce()
|
||||
case 4:
|
||||
digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW);
|
||||
break;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_SCREEN && HAS_SCREEN
|
||||
// 5 clicks: start accelerometer/magenetometer calibration for 30 seconds
|
||||
case 5:
|
||||
if (accelerometerThread) {
|
||||
accelerometerThread->calibrate(30);
|
||||
}
|
||||
break;
|
||||
// 6 clicks: start accelerometer/magenetometer calibration for 60 seconds
|
||||
case 6:
|
||||
if (accelerometerThread) {
|
||||
accelerometerThread->calibrate(60);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
// No valid multipress action
|
||||
default:
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "InputBroker.h"
|
||||
#include "OneButton.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
@ -29,9 +30,10 @@
|
||||
#define BUTTON_LEADUP_MS 2200 // Play lead-up sound after 2.5 seconds of holding
|
||||
#endif
|
||||
|
||||
class ButtonThread : public concurrency::OSThread
|
||||
class ButtonThread : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
const char *_originName;
|
||||
static const uint32_t c_holdOffTime = 30000; // hold off 30s after boot
|
||||
|
||||
enum ButtonEventType {
|
||||
@ -46,15 +48,11 @@ class ButtonThread : public concurrency::OSThread
|
||||
BUTTON_EVENT_COMBO_SHORT_LONG,
|
||||
};
|
||||
|
||||
ButtonThread();
|
||||
ButtonThread(const char *name);
|
||||
int32_t runOnce() override;
|
||||
void attachButtonInterrupts();
|
||||
void detachButtonInterrupts();
|
||||
void storeClickCount();
|
||||
bool isBuzzing() { return buzzer_flag; }
|
||||
void setScreenFlag(bool flag) { screen_flag = flag; }
|
||||
bool getScreenFlag() { return screen_flag; }
|
||||
bool isInterceptingAndFocused();
|
||||
bool isButtonPressed(int buttonPin)
|
||||
{
|
||||
#ifdef BUTTON_ACTIVE_LOW
|
||||
@ -90,8 +88,6 @@ class ButtonThread : public concurrency::OSThread
|
||||
|
||||
// set during IRQ
|
||||
static volatile ButtonEventType btnEvent;
|
||||
bool buzzer_flag = false;
|
||||
bool screen_flag = true;
|
||||
|
||||
// Store click count during callback, for later use
|
||||
volatile int multipressClickCount = 0;
|
||||
|
17
src/input/ButtonThreadImpl.cpp
Normal file
17
src/input/ButtonThreadImpl.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "ButtonThreadImpl.h"
|
||||
#include "InputBroker.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#if defined(BUTTON_PIN)
|
||||
|
||||
ButtonThreadImpl *aButtonThreadImpl;
|
||||
|
||||
ButtonThreadImpl::ButtonThreadImpl() : ButtonThread("UserButton") {}
|
||||
|
||||
void ButtonThreadImpl::init() // init should give the pin number and the action to perform and whether to do the legacy actions
|
||||
{
|
||||
if (inputBroker)
|
||||
inputBroker->registerSource(this);
|
||||
}
|
||||
|
||||
#endif // INPUTBROKER_SERIAL_TYPE
|
19
src/input/ButtonThreadImpl.h
Normal file
19
src/input/ButtonThreadImpl.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "ButtonThread.h"
|
||||
#include "main.h"
|
||||
|
||||
/**
|
||||
* @brief The idea behind this class to have static methods for the event handlers.
|
||||
* Check attachInterrupt() at RotaryEncoderInteruptBase.cpp
|
||||
* Technically you can have as many rotary encoders hardver attached
|
||||
* to your device as you wish, but you always need to have separate event
|
||||
* handlers, thus you need to have a RotaryEncoderInterrupt implementation.
|
||||
*/
|
||||
class ButtonThreadImpl : public ButtonThread
|
||||
{
|
||||
public:
|
||||
ButtonThreadImpl();
|
||||
void init();
|
||||
};
|
||||
|
||||
extern ButtonThreadImpl *aButtonThreadImpl;
|
73
src/main.cpp
73
src/main.cpp
@ -99,7 +99,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
|
||||
#endif
|
||||
|
||||
#if HAS_BUTTON || defined(ARCH_PORTDUINO)
|
||||
#include "input/ButtonThread.h"
|
||||
#include "input/ButtonThreadImpl.h"
|
||||
#endif
|
||||
|
||||
#include "AmbientLightingThread.h"
|
||||
@ -220,64 +220,6 @@ const char *getDeviceName()
|
||||
return name;
|
||||
}
|
||||
|
||||
#if defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M2)
|
||||
static int32_t ledBlinkCount = 0;
|
||||
|
||||
static int32_t elecrowLedBlinker()
|
||||
{
|
||||
// are we in alert buzzer mode?
|
||||
#if HAS_BUTTON
|
||||
if (buttonThread->isBuzzing()) {
|
||||
// blink LED three times for 3 seconds, then 3 times for a second, with one second pause
|
||||
if (ledBlinkCount % 2) { // odd means LED OFF
|
||||
ledBlink.set(false);
|
||||
ledBlinkCount++;
|
||||
if (ledBlinkCount >= 12)
|
||||
ledBlinkCount = 0;
|
||||
noTone(PIN_BUZZER);
|
||||
return 1000;
|
||||
} else {
|
||||
if (ledBlinkCount < 6) {
|
||||
ledBlink.set(true);
|
||||
tone(PIN_BUZZER, 4000, 3000);
|
||||
ledBlinkCount++;
|
||||
return 3000;
|
||||
} else {
|
||||
ledBlink.set(true);
|
||||
tone(PIN_BUZZER, 4000, 1000);
|
||||
ledBlinkCount++;
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
ledBlinkCount = 0;
|
||||
if (config.device.led_heartbeat_disabled)
|
||||
return 1000;
|
||||
|
||||
static bool ledOn;
|
||||
// remain on when fully charged or discharging above 10%
|
||||
if ((powerStatus->getIsCharging() && powerStatus->getBatteryChargePercent() >= 100) ||
|
||||
(!powerStatus->getIsCharging() && powerStatus->getBatteryChargePercent() >= 10)) {
|
||||
ledOn = true;
|
||||
} else {
|
||||
ledOn ^= 1;
|
||||
}
|
||||
ledBlink.set(ledOn);
|
||||
// when charging, blink 0.5Hz square wave rate to indicate that
|
||||
if (powerStatus->getIsCharging()) {
|
||||
return 500;
|
||||
}
|
||||
// Blink rapidly when almost empty or if battery is not connected
|
||||
if ((!powerStatus->getIsCharging() && powerStatus->getBatteryChargePercent() < 10) || !powerStatus->getHasBattery()) {
|
||||
return 250;
|
||||
}
|
||||
#if HAS_BUTTON
|
||||
}
|
||||
#endif
|
||||
return 1000;
|
||||
}
|
||||
#else
|
||||
static int32_t ledBlinker()
|
||||
{
|
||||
// Still set up the blinking (heartbeat) interval but skip code path below, so LED will blink if
|
||||
@ -293,7 +235,6 @@ static int32_t ledBlinker()
|
||||
// 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 ? 1 : 1000);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t timeLastPowered = 0;
|
||||
|
||||
@ -861,11 +802,6 @@ void setup()
|
||||
}
|
||||
#endif // HAS_SCREEN
|
||||
|
||||
#if HAS_BUTTON || defined(ARCH_PORTDUINO)
|
||||
// Buttons. Moved here cause we need NodeDB to be initialized
|
||||
buttonThread = new ButtonThread();
|
||||
#endif
|
||||
|
||||
// setup TZ prior to time actions.
|
||||
#if !MESHTASTIC_EXCLUDE_TZ
|
||||
LOG_DEBUG("Use compiled/slipstreamed %s", slipstreamTZString); // important, removing this clobbers our magic string
|
||||
@ -932,6 +868,13 @@ void setup()
|
||||
// Now that the mesh service is created, create any modules
|
||||
setupModules();
|
||||
|
||||
// buttons are now inputBroker, so have to come after setupModules
|
||||
#if HAS_BUTTON || defined(ARCH_PORTDUINO)
|
||||
// Buttons. Moved here cause we need NodeDB to be initialized
|
||||
aButtonThreadImpl = new ButtonThreadImpl();
|
||||
aButtonThreadImpl->init();
|
||||
#endif
|
||||
|
||||
#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
|
||||
// After modules are setup, so we can observe modules
|
||||
setupNicheGraphics();
|
||||
|
@ -310,8 +310,6 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
||||
|
||||
case CANNED_MESSAGE_RUN_STATE_INACTIVE:
|
||||
if (isSelect) {
|
||||
// When inactive, call the onebutton shortpress instead. Activate module only on up/down
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
return 0; // Main button press no longer runs through powerFSM
|
||||
}
|
||||
// Let LEFT/RIGHT pass through so frame navigation works
|
||||
|
Loading…
Reference in New Issue
Block a user