From f1339e97a33714266480a1d41fd32e2019b67797 Mon Sep 17 00:00:00 2001 From: Adam Matthews <52178922+adamsthws@users.noreply.github.com> Date: Fri, 11 Sep 2026 13:59:17 +0100 Subject: [PATCH 1/2] Make button timing constants configurable via UI WLED_DEBOUNCE_THRESHOLD, WLED_LONG_PRESS and WLED_DOUBLE_PRESS were plain #defines with no way to change them without recompiling. Per discussion on wled/WLED#5844, expose them as runtime settings on the LED Preferences > Buttons page. Limits (Debounce 0-250ms, Long press 100-4000ms, Double press 0-1000ms) follow similar timing conventions used by Tasmota, OneButton and ESPHome for the same settings. Long press is capped below WLED_LONG_AP (5000ms) so a user-configured value can't collide with button 0's existing AP-mode/factory-reset hold thresholds. --- wled00/button.cpp | 17 +++++++---------- wled00/cfg.cpp | 6 ++++++ wled00/const.h | 4 ++++ wled00/data/settings_leds.htm | 8 +++++++- wled00/set.cpp | 6 ++++++ wled00/wled.h | 3 +++ wled00/xml.cpp | 3 +++ 7 files changed, 36 insertions(+), 11 deletions(-) diff --git a/wled00/button.cpp b/wled00/button.cpp index ef56cea7c6..148f3ce15e 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -4,9 +4,6 @@ * Physical IO */ -#define WLED_DEBOUNCE_THRESHOLD 50 // only consider button input of at least 50ms as valid (debouncing) -#define WLED_LONG_PRESS 600 // long press if button is released after held for at least 600ms -#define WLED_DOUBLE_PRESS 350 // double press if another press within 350ms after a short press #define WLED_LONG_REPEATED_ACTION 400 // how often a repeated action (e.g. dimming) is fired on long press on button IDs >0 #define WLED_LONG_AP 5000 // how long button 0 needs to be held to activate WLED-AP #define WLED_LONG_FACTORY_RESET 10000 // how long button 0 needs to be held to trigger a factory reset @@ -136,7 +133,7 @@ void handleSwitch(uint8_t b) if (buttons[b].longPressed == buttons[b].pressedBefore) return; - if (millis() - buttons[b].pressedTime > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce) + if (millis() - buttons[b].pressedTime > buttonDebounceMs) { //fire edge event only after debounce threshold without change DEBUG_PRINTF_P(PSTR("Switch: Activating %u\n"), b); if (!buttons[b].pressedBefore) { // on -> off DEBUG_PRINTF_P(PSTR("Switch: On -> Off (%u)\n"), b); @@ -304,7 +301,7 @@ void handleButton() if (!buttons[b].pressedBefore) buttons[b].pressedTime = now; buttons[b].pressedBefore = true; - if (now - buttons[b].pressedTime > WLED_LONG_PRESS) { //long press + if (now - buttons[b].pressedTime > buttonLongPressMs) { //long press if (!buttons[b].longPressed) { buttonBriDirection = !buttonBriDirection; //toggle brightness direction on long press longPressAction(b); @@ -320,11 +317,11 @@ void handleButton() // released after rising-edge short press action if (buttons[b].macroButton && buttons[b].macroButton == buttons[b].macroLongPress && buttons[b].macroButton == buttons[b].macroDoublePress) { - if (dur > WLED_DEBOUNCE_THRESHOLD) buttons[b].pressedBefore = false; // debounce, blocks button for 50 ms once it has been released + if (dur > buttonDebounceMs) buttons[b].pressedBefore = false; // debounce, blocks button once it has been released continue; } - if (dur < WLED_DEBOUNCE_THRESHOLD) {buttons[b].pressedBefore = false; continue;} // too short "press", debounce + if (dur < buttonDebounceMs) {buttons[b].pressedBefore = false; continue;} // too short "press", debounce bool doublePress = buttons[b].waitTime; //did we have a short press before? buttons[b].waitTime = 0; @@ -339,7 +336,7 @@ void handleButton() //NOTE: this interferes with double click handling in usermods so usermod needs to implement full button handling if (b != 1 && !buttons[b].macroDoublePress) { //don't wait for double press on buttons without a default action if no double press macro set shortPressAction(b); - } else { //double press if less than 350 ms between current press and previous short press release (buttonWaitTime!=0) + } else { //double press if within double press window of current press and previous short press release (buttonWaitTime!=0) if (doublePress) { doublePressAction(b); } else { @@ -351,8 +348,8 @@ void handleButton() buttons[b].longPressed = false; } - //if 350ms elapsed since last short press release it is a short press - if (buttons[b].waitTime && now - buttons[b].waitTime > WLED_DOUBLE_PRESS && !buttons[b].pressedBefore) { + //if double press window elapsed since last short press release it is a short press + if (buttons[b].waitTime && now - buttons[b].waitTime > buttonDoublePressMs && !buttons[b].pressedBefore) { buttons[b].waitTime = 0; shortPressAction(b); } diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index bfb6242883..f4c0ac28fc 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -451,6 +451,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { } CJSON(buttonPublishMqtt, btn_obj["mqtt"]); + CJSON(buttonDebounceMs, btn_obj[F("dbnc")]); + CJSON(buttonLongPressMs, btn_obj[F("lp")]); + CJSON(buttonDoublePressMs, btn_obj[F("dp")]); #ifndef WLED_DISABLE_INFRARED int hw_ir_pin = hw["ir"]["pin"] | -2; // 4 @@ -1048,6 +1051,9 @@ void serializeConfig(JsonObject root) { hw_btn[F("tt")] = touchThreshold; hw_btn["mqtt"] = buttonPublishMqtt; + hw_btn[F("dbnc")] = buttonDebounceMs; + hw_btn[F("lp")] = buttonLongPressMs; + hw_btn[F("dp")] = buttonDoublePressMs; JsonObject hw_ir = hw.createNestedObject("ir"); #ifndef WLED_DISABLE_INFRARED diff --git a/wled00/const.h b/wled00/const.h index 00a6b4d226..5f1133a8b7 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -651,6 +651,10 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit"); #define TOUCH_THRESHOLD 32 // limit to recognize a touch, higher value means more sensitive +#define WLED_DEBOUNCE_THRESHOLD 50 // default: only consider button input of at least 50ms as valid (debouncing) +#define WLED_LONG_PRESS 600 // default: long press if button is released after held for at least 600ms +#define WLED_DOUBLE_PRESS 350 // default: double press if another press within 350ms after a short press + // Size of buffer for API JSON object (increase for more segments) #ifdef ESP8266 #define JSON_BUFFER_SIZE 10240 diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 03fae259c4..ad98dee546 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -1093,7 +1093,13 @@

Buttons

Disable internal pull-up/down:
- Touch threshold:

+ Touch threshold:
+ Debounce time: ms (0-250)
+ Ignores presses shorter than this (filters switch/contact noise)
+ Long press time: ms (100-4000)
+ How long to hold before a long press triggers
+ Double press time: ms (0-1000)
+ Max gap between two clicks to count as a double press

IR Remote

diff --git a/wled00/set.cpp b/wled00/set.cpp index 5c459e5570..4ee0dfa273 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -332,6 +332,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) disablePullUp = (bool)request->hasArg(F("IP")); touchThreshold = request->arg(F("TT")).toInt(); + t = request->arg(F("DB")).toInt(); + if (t >= 0 && t <= 250) buttonDebounceMs = t; + t = request->arg(F("LP")).toInt(); + if (t >= 100 && t <= 4000) buttonLongPressMs = t; // stay below WLED_LONG_AP so button 0's AP-mode/factory-reset thresholds are unaffected + t = request->arg(F("DP")).toInt(); + if (t >= 0 && t <= 1000) buttonDoublePressMs = t; for (int i = 0; i < WLED_MAX_BUTTONS; i++) { int offset = i < 10 ? '0' : 'A' - 10; char bt[4] = "BT"; bt[2] = offset+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10) diff --git a/wled00/wled.h b/wled00/wled.h index 5d2a6e75b1..a89b7709fc 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -647,6 +647,9 @@ WLED_GLOBAL std::vector
Disable internal pull-up/down:
Touch threshold:
- Debounce time: ms (0-250)
+ Debounce time: ms (0-100)
Ignores presses shorter than this (filters switch/contact noise)
- Long press time: ms (100-4000)
+ Long press time: ms (200-4000)
How long to hold before a long press triggers
- Double press time: ms (0-1000)
+ Double press time: ms (100-1000)
Max gap between two clicks to count as a double press

diff --git a/wled00/set.cpp b/wled00/set.cpp index 4ee0dfa273..ae7b2f6d73 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -333,11 +333,11 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) disablePullUp = (bool)request->hasArg(F("IP")); touchThreshold = request->arg(F("TT")).toInt(); t = request->arg(F("DB")).toInt(); - if (t >= 0 && t <= 250) buttonDebounceMs = t; + if (t >= 0 && t <= 100) buttonDebounceMs = t; t = request->arg(F("LP")).toInt(); - if (t >= 100 && t <= 4000) buttonLongPressMs = t; // stay below WLED_LONG_AP so button 0's AP-mode/factory-reset thresholds are unaffected + if (t >= 200 && t <= 4000) buttonLongPressMs = t; // stay below WLED_LONG_AP so button 0's AP-mode/factory-reset thresholds are unaffected; floor kept above the debounce max (100) so long press can never be shorter than debounce t = request->arg(F("DP")).toInt(); - if (t >= 0 && t <= 1000) buttonDoublePressMs = t; + if (t >= 100 && t <= 1000) buttonDoublePressMs = t; for (int i = 0; i < WLED_MAX_BUTTONS; i++) { int offset = i < 10 ? '0' : 'A' - 10; char bt[4] = "BT"; bt[2] = offset+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)