From ce31fd7c572d025ca96fab9e2a87fa705fba570c Mon Sep 17 00:00:00 2001 From: taco Date: Wed, 3 Sep 2025 08:25:59 +1000 Subject: [PATCH 1/7] multi click support including buzzer toggle --- examples/companion_radio/ui-new/UITask.cpp | 46 +++++++++++++++++++--- examples/companion_radio/ui-new/UITask.h | 6 +++ src/helpers/ui/MomentaryButton.cpp | 38 +++++++++++++++++- src/helpers/ui/MomentaryButton.h | 6 +++ 4 files changed, 89 insertions(+), 7 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index c751eaf5..d3fc5bad 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -495,6 +495,10 @@ void UITask::loop() { c = checkDisplayOn(KEY_SELECT); } else if (ev == BUTTON_EVENT_LONG_PRESS) { c = handleLongPress(KEY_ENTER); + } else if (ev == BUTTON_EVENT_DOUBLE_CLICK) { + c = handleDoubleClick(KEY_ENTER); + } else if (ev == BUTTON_EVENT_TRIPLE_CLICK) { + c = handleTripleClick(KEY_ENTER); } #endif #if defined(WIO_TRACKER_L1) @@ -604,10 +608,43 @@ char UITask::handleLongPress(char c) { return c; } -/* -void UITask::handleButtonTriplePress() { - MESH_DEBUG_PRINTLN("UITask: triple press triggered"); - // Toggle buzzer quiet mode +char UITask::handleDoubleClick(char c) { + MESH_DEBUG_PRINTLN("UITask: double click triggered"); + c = 0; + return c; +} + +char UITask::handleTripleClick(char c) { + MESH_DEBUG_PRINTLN("UITask: triple click triggered"); + toggleBuzzer(); + c = 0; + return c; +} + +void UITask::toggleGPS() { + if (_sensors != NULL) { + // toggle GPS on/off + int num = _sensors->getNumSettings(); + for (int i = 0; i < num; i++) { + if (strcmp(_sensors->getSettingName(i), "gps") == 0) { + if (strcmp(_sensors->getSettingValue(i), "1") == 0) { + _sensors->setSettingValue("gps", "0"); + soundBuzzer(UIEventType::ack); + showAlert("GPS: Disabled", 600); + } else { + _sensors->setSettingValue("gps", "1"); + soundBuzzer(UIEventType::ack); + showAlert("GPS: Enabled", 600); + } + _next_refresh = 0; + break; + } + } + } +} + +void UITask::toggleBuzzer() { + // Toggle buzzer quiet mode #ifdef PIN_BUZZER if (buzzer.isQuiet()) { buzzer.quiet(false); @@ -620,4 +657,3 @@ void UITask::handleButtonTriplePress() { _next_refresh = 0; // trigger refresh #endif } -*/ diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index f9e01550..a96abf89 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -37,6 +37,8 @@ class UITask : public AbstractUITask { // Button action handlers char checkDisplayOn(char c); char handleLongPress(char c); + char handleDoubleClick(char c); + char handleTripleClick(char c); void setCurrScreen(UIScreen* c); @@ -55,6 +57,10 @@ public: bool hasDisplay() const { return _display != NULL; } bool isButtonPressed() const; + void toggleBuzzer(); + void toggleGPS(); + + // from AbstractUITask void msgRead(int msgcount) override; void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override; diff --git a/src/helpers/ui/MomentaryButton.cpp b/src/helpers/ui/MomentaryButton.cpp index 36309600..4b87705a 100644 --- a/src/helpers/ui/MomentaryButton.cpp +++ b/src/helpers/ui/MomentaryButton.cpp @@ -9,6 +9,10 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse cancel = 0; _long_millis = long_press_millis; _threshold = 0; + _click_count = 0; + _last_click_time = 0; + _multi_click_window = 500; + _pending_click = false; } MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, int analog_threshold) { @@ -20,6 +24,10 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, int analog_t cancel = 0; _long_millis = long_press_millis; _threshold = analog_threshold; + _click_count = 0; + _last_click_time = 0; + _multi_click_window = 500; + _pending_click = false; } void MomentaryButton::begin() { @@ -35,6 +43,8 @@ bool MomentaryButton::isPressed() const { void MomentaryButton::cancelClick() { cancel = 1; + _click_count = 0; + _pending_click = false; } bool MomentaryButton::isPressed(int level) const { @@ -60,10 +70,14 @@ int MomentaryButton::check(bool repeat_click) { // button UP if (_long_millis > 0) { if (down_at > 0 && (unsigned long)(millis() - down_at) < _long_millis) { // only a CLICK if still within the long_press millis - event = BUTTON_EVENT_CLICK; + _click_count++; + _last_click_time = millis(); + _pending_click = true; } } else { - event = BUTTON_EVENT_CLICK; // any UP results in CLICK event when NOT using long_press feature + _click_count++; + _last_click_time = millis(); + _pending_click = true; } if (event == BUTTON_EVENT_CLICK && cancel) { event = BUTTON_EVENT_NONE; @@ -87,5 +101,25 @@ int MomentaryButton::check(bool repeat_click) { } } + if (_pending_click && (millis() - _last_click_time) >= _multi_click_window) { + switch (_click_count) { + case 1: + event = BUTTON_EVENT_CLICK; + break; + case 2: + event = BUTTON_EVENT_DOUBLE_CLICK; + break; + case 3: + event = BUTTON_EVENT_TRIPLE_CLICK; + break; + default: + // For 4+ clicks, treat as triple click? + event = BUTTON_EVENT_TRIPLE_CLICK; + break; + } + _click_count = 0; + _pending_click = false; + } + return event; } \ No newline at end of file diff --git a/src/helpers/ui/MomentaryButton.h b/src/helpers/ui/MomentaryButton.h index d002f652..1122e56a 100644 --- a/src/helpers/ui/MomentaryButton.h +++ b/src/helpers/ui/MomentaryButton.h @@ -5,6 +5,8 @@ #define BUTTON_EVENT_NONE 0 #define BUTTON_EVENT_CLICK 1 #define BUTTON_EVENT_LONG_PRESS 2 +#define BUTTON_EVENT_DOUBLE_CLICK 3 +#define BUTTON_EVENT_TRIPLE_CLICK 4 class MomentaryButton { int8_t _pin; @@ -13,6 +15,10 @@ class MomentaryButton { int _long_millis; int _threshold; // analog mode unsigned long down_at; + uint8_t _click_count; + unsigned long _last_click_time; + int _multi_click_window; + bool _pending_click; bool isPressed(int level) const; From 43c3105bf1d0fc8c0682960352bf0f3aedb64af1 Mon Sep 17 00:00:00 2001 From: taco Date: Wed, 3 Sep 2025 08:31:38 +1000 Subject: [PATCH 2/7] wake screen on double and triple clicks --- examples/companion_radio/ui-new/UITask.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index d3fc5bad..1c565a8a 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -610,12 +610,14 @@ char UITask::handleLongPress(char c) { char UITask::handleDoubleClick(char c) { MESH_DEBUG_PRINTLN("UITask: double click triggered"); + checkDisplayOn(c); c = 0; return c; } char UITask::handleTripleClick(char c) { MESH_DEBUG_PRINTLN("UITask: triple click triggered"); + checkDisplayOn(c); toggleBuzzer(); c = 0; return c; From 5de0dc1fd6152a7f3bb5a34f0f8e0679138093c8 Mon Sep 17 00:00:00 2001 From: taco Date: Wed, 3 Sep 2025 12:03:31 +1000 Subject: [PATCH 3/7] sliding multiclick window --- src/helpers/ui/MomentaryButton.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/helpers/ui/MomentaryButton.cpp b/src/helpers/ui/MomentaryButton.cpp index 4b87705a..0446c639 100644 --- a/src/helpers/ui/MomentaryButton.cpp +++ b/src/helpers/ui/MomentaryButton.cpp @@ -11,7 +11,7 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse _threshold = 0; _click_count = 0; _last_click_time = 0; - _multi_click_window = 500; + _multi_click_window = 260; _pending_click = false; } @@ -26,7 +26,7 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, int analog_t _threshold = analog_threshold; _click_count = 0; _last_click_time = 0; - _multi_click_window = 500; + _multi_click_window = 260; _pending_click = false; } @@ -81,6 +81,9 @@ int MomentaryButton::check(bool repeat_click) { } if (event == BUTTON_EVENT_CLICK && cancel) { event = BUTTON_EVENT_NONE; + _click_count = 0; + _last_click_time = 0; + _pending_click = false; } down_at = 0; } @@ -93,6 +96,9 @@ int MomentaryButton::check(bool repeat_click) { if (_long_millis > 0 && down_at > 0 && (unsigned long)(millis() - down_at) >= _long_millis) { event = BUTTON_EVENT_LONG_PRESS; down_at = 0; + _click_count = 0; + _last_click_time = 0; + _pending_click = false; } if (down_at > 0 && repeat_click) { unsigned long diff = (unsigned long)(millis() - down_at); @@ -118,6 +124,7 @@ int MomentaryButton::check(bool repeat_click) { break; } _click_count = 0; + _last_click_time = 0; _pending_click = false; } From 9f185303b4448df72c2fa38d844a6d6b8ecdae72 Mon Sep 17 00:00:00 2001 From: taco Date: Wed, 3 Sep 2025 12:29:20 +1000 Subject: [PATCH 4/7] long press cancels multi click --- src/helpers/ui/MomentaryButton.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/helpers/ui/MomentaryButton.cpp b/src/helpers/ui/MomentaryButton.cpp index 0446c639..0ea4b027 100644 --- a/src/helpers/ui/MomentaryButton.cpp +++ b/src/helpers/ui/MomentaryButton.cpp @@ -1,5 +1,7 @@ #include "MomentaryButton.h" +#define MULTI_CLICK_WINDOW_MS 280 + MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse, bool pulldownup) { _pin = pin; _reverse = reverse; @@ -11,7 +13,7 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse _threshold = 0; _click_count = 0; _last_click_time = 0; - _multi_click_window = 260; + _multi_click_window = MULTI_CLICK_WINDOW_MS; _pending_click = false; } @@ -26,7 +28,7 @@ MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, int analog_t _threshold = analog_threshold; _click_count = 0; _last_click_time = 0; - _multi_click_window = 260; + _multi_click_window = MULTI_CLICK_WINDOW_MS; _pending_click = false; } @@ -43,7 +45,9 @@ bool MomentaryButton::isPressed() const { void MomentaryButton::cancelClick() { cancel = 1; + down_at = 0; _click_count = 0; + _last_click_time = 0; _pending_click = false; } @@ -94,11 +98,16 @@ int MomentaryButton::check(bool repeat_click) { } if (_long_millis > 0 && down_at > 0 && (unsigned long)(millis() - down_at) >= _long_millis) { - event = BUTTON_EVENT_LONG_PRESS; - down_at = 0; - _click_count = 0; - _last_click_time = 0; - _pending_click = false; + if (_pending_click) { + // long press during multi-click detection - cancel pending clicks + cancelClick(); + } else { + event = BUTTON_EVENT_LONG_PRESS; + down_at = 0; + _click_count = 0; + _last_click_time = 0; + _pending_click = false; + } } if (down_at > 0 && repeat_click) { unsigned long diff = (unsigned long)(millis() - down_at); @@ -108,6 +117,10 @@ int MomentaryButton::check(bool repeat_click) { } if (_pending_click && (millis() - _last_click_time) >= _multi_click_window) { + if (down_at > 0) { + // still pressed - wait for button release before processing clicks + return event; + } switch (_click_count) { case 1: event = BUTTON_EVENT_CLICK; From a9ab1f072a78e0eea51e7f469af00be9536b16f4 Mon Sep 17 00:00:00 2001 From: taco Date: Wed, 3 Sep 2025 14:02:35 +1000 Subject: [PATCH 5/7] increase gps/buzzer alert times 600 is a bit short for eink --- examples/companion_radio/ui-new/UITask.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 1c565a8a..39eb26b3 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -632,11 +632,11 @@ void UITask::toggleGPS() { if (strcmp(_sensors->getSettingValue(i), "1") == 0) { _sensors->setSettingValue("gps", "0"); soundBuzzer(UIEventType::ack); - showAlert("GPS: Disabled", 600); + showAlert("GPS: Disabled", 800); } else { _sensors->setSettingValue("gps", "1"); soundBuzzer(UIEventType::ack); - showAlert("GPS: Enabled", 600); + showAlert("GPS: Enabled", 800); } _next_refresh = 0; break; @@ -651,10 +651,10 @@ void UITask::toggleBuzzer() { if (buzzer.isQuiet()) { buzzer.quiet(false); soundBuzzer(UIEventType::ack); - showAlert("Buzzer: ON", 600); + showAlert("Buzzer: ON", 800); } else { buzzer.quiet(true); - showAlert("Buzzer: OFF", 600); + showAlert("Buzzer: OFF", 800); } _next_refresh = 0; // trigger refresh #endif From afbfc6c6edd223a9f7bfd4eae0305394afa026b6 Mon Sep 17 00:00:00 2001 From: taco Date: Wed, 3 Sep 2025 15:48:50 +1000 Subject: [PATCH 6/7] add new keycodes --- src/helpers/ui/UIScreen.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/helpers/ui/UIScreen.h b/src/helpers/ui/UIScreen.h index 6faa591a..6aa1d69c 100644 --- a/src/helpers/ui/UIScreen.h +++ b/src/helpers/ui/UIScreen.h @@ -2,13 +2,17 @@ #include "DisplayDriver.h" -#define KEY_LEFT 0xB4 -#define KEY_UP 0xB5 -#define KEY_DOWN 0xB6 -#define KEY_RIGHT 0xB7 -#define KEY_SELECT 10 -#define KEY_ENTER 13 -#define KEY_BACK 27 // Esc +#define KEY_LEFT 0xB4 +#define KEY_UP 0xB5 +#define KEY_DOWN 0xB6 +#define KEY_RIGHT 0xB7 +#define KEY_SELECT 10 +#define KEY_ENTER 13 +#define KEY_CANCEL 27 // Esc +#define KEY_HOME 0xF0 +#define KEY_NEXT 0xF1 +#define KEY_PREV 0xF2 +#define KEY_CONTEXT_MENU 0xF3 class UIScreen { protected: From 96a71bb21b6f93fd8f7c22496ff4999ec406249a Mon Sep 17 00:00:00 2001 From: taco Date: Wed, 3 Sep 2025 16:28:58 +1000 Subject: [PATCH 7/7] alter keycode keycode handling --- examples/companion_radio/ui-new/UITask.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 39eb26b3..dfc545d0 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -223,11 +223,11 @@ public: } bool handleInput(char c) override { - if (c == KEY_LEFT) { + if (c == KEY_LEFT || c == KEY_PREV) { _page = (_page + HomePage::Count - 1) % HomePage::Count; return true; } - if (c == KEY_RIGHT || c == KEY_SELECT) { + if (c == KEY_NEXT || c == KEY_RIGHT) { _page = (_page + 1) % HomePage::Count; if (_page == HomePage::RECENT) { _task->showAlert("Recent adverts", 800); @@ -325,7 +325,7 @@ public: } bool handleInput(char c) override { - if (c == KEY_SELECT || c == KEY_RIGHT) { + if (c == KEY_NEXT || c == KEY_RIGHT) { num_unread--; if (num_unread == 0) { _task->gotoHomeScreen(); @@ -492,13 +492,13 @@ void UITask::loop() { #if defined(PIN_USER_BTN) int ev = user_btn.check(); if (ev == BUTTON_EVENT_CLICK) { - c = checkDisplayOn(KEY_SELECT); + c = checkDisplayOn(KEY_NEXT); } else if (ev == BUTTON_EVENT_LONG_PRESS) { c = handleLongPress(KEY_ENTER); } else if (ev == BUTTON_EVENT_DOUBLE_CLICK) { - c = handleDoubleClick(KEY_ENTER); + c = handleDoubleClick(KEY_PREV); } else if (ev == BUTTON_EVENT_TRIPLE_CLICK) { - c = handleTripleClick(KEY_ENTER); + c = handleTripleClick(KEY_SELECT); } #endif #if defined(WIO_TRACKER_L1) @@ -518,9 +518,13 @@ void UITask::loop() { #if defined(PIN_USER_BTN_ANA) ev = analog_btn.check(); if (ev == BUTTON_EVENT_CLICK) { - c = checkDisplayOn(KEY_SELECT); + c = checkDisplayOn(KEY_NEXT); } else if (ev == BUTTON_EVENT_LONG_PRESS) { c = handleLongPress(KEY_ENTER); + } else if (ev == BUTTON_EVENT_DOUBLE_CLICK) { + c = handleDoubleClick(KEY_PREV); + } else if (ev == BUTTON_EVENT_TRIPLE_CLICK) { + c = handleTripleClick(KEY_SELECT); } #endif @@ -611,7 +615,6 @@ char UITask::handleLongPress(char c) { char UITask::handleDoubleClick(char c) { MESH_DEBUG_PRINTLN("UITask: double click triggered"); checkDisplayOn(c); - c = 0; return c; }