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 01/12] 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)
From 0749e552c399a59647d1990c07ab830baeeca4b0 Mon Sep 17 00:00:00 2001
From: Adam Matthews <52178922+adamsthws@users.noreply.github.com>
Date: Fri, 25 Sep 2026 16:42:33 +0100
Subject: [PATCH 03/12] Name button timing limits and clamp consistently in
cfg/set
Replace the duplicated min/max literals in cfg.cpp and set.cpp with
named constants in const.h, and enforce the invariants with
static_asserts instead of comments (long press > debounce, long press
< WLED_LONG_AP).
set.cpp now clamps out-of-range values like cfg.cpp instead of silently
ignoring them, and leaves a timing unchanged when its field is absent
from the request (e.g. an older cached settings page).
Drop the "(min-max)" hints from the settings page: no other number
field shows its range, the inputs already enforce it via min/max, and
the hints would duplicate the C constants.
---
wled00/button.cpp | 2 ++
wled00/cfg.cpp | 7 ++++---
wled00/const.h | 6 ++++++
wled00/data/settings_leds.htm | 6 +++---
wled00/set.cpp | 9 +++------
5 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/wled00/button.cpp b/wled00/button.cpp
index 148f3ce15e..6d63c297a1 100644
--- a/wled00/button.cpp
+++ b/wled00/button.cpp
@@ -9,6 +9,8 @@
#define WLED_LONG_FACTORY_RESET 10000 // how long button 0 needs to be held to trigger a factory reset
#define WLED_LONG_BRI_STEPS 16 // how much to increase/decrease the brightness with each long press repetition
+static_assert(WLED_LONG_PRESS_MAX < WLED_LONG_AP, "long press must not reach the AP-mode/factory-reset hold time of button 0");
+
static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage
// Runtime state private to this file - previously WLED_GLOBAL, a leftover from
diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp
index 8b3c732cfb..79a1954178 100644
--- a/wled00/cfg.cpp
+++ b/wled00/cfg.cpp
@@ -451,12 +451,13 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
}
CJSON(buttonPublishMqtt, btn_obj["mqtt"]);
+ // CJSON keeps the previous value if the JSON value doesn't fit uint16_t (e.g. negative), so no wraparound before the clamps
CJSON(buttonDebounceMs, btn_obj[F("dbnc")]);
CJSON(buttonLongPressMs, btn_obj[F("lp")]);
CJSON(buttonDoublePressMs, btn_obj[F("dp")]);
- buttonDebounceMs = constrain(buttonDebounceMs, 0, 100);
- buttonLongPressMs = constrain(buttonLongPressMs, 200, 4000); // floor kept above the debounce max (100) so long press can never be shorter than debounce
- buttonDoublePressMs = constrain(buttonDoublePressMs, 100, 1000);
+ buttonDebounceMs = min(buttonDebounceMs, (uint16_t)WLED_DEBOUNCE_MAX);
+ buttonLongPressMs = constrain(buttonLongPressMs, WLED_LONG_PRESS_MIN, WLED_LONG_PRESS_MAX);
+ buttonDoublePressMs = constrain(buttonDoublePressMs, WLED_DOUBLE_PRESS_MIN, WLED_DOUBLE_PRESS_MAX);
#ifndef WLED_DISABLE_INFRARED
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
diff --git a/wled00/const.h b/wled00/const.h
index 37d201c471..48760cd2b8 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -654,6 +654,12 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
#define WLED_DEBOUNCE_THRESHOLD 50 // default: only consider button input of at least 50ms as valid (debouncing)
#define WLED_LONG_PRESS 600 // default: trigger long press after the button remains pressed for 600ms
#define WLED_DOUBLE_PRESS 350 // default: double press if another press within 350ms after a short press
+#define WLED_DEBOUNCE_MAX 100 // settings limits for the button timings above
+#define WLED_LONG_PRESS_MIN 200
+#define WLED_LONG_PRESS_MAX 4000 // must stay below WLED_LONG_AP (button.cpp)
+#define WLED_DOUBLE_PRESS_MIN 100
+#define WLED_DOUBLE_PRESS_MAX 1000
+static_assert(WLED_LONG_PRESS_MIN > WLED_DEBOUNCE_MAX, "long press must be longer than the debounce time");
// Size of buffer for API JSON object (increase for more segments)
#ifdef ESP8266
diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm
index 62459fce16..31be04b971 100644
--- a/wled00/data/settings_leds.htm
+++ b/wled00/data/settings_leds.htm
@@ -1097,11 +1097,11 @@
Buttons
Disable internal pull-up/down:
Touch threshold:
- Debounce time: ms (0-100)
+ Debounce time: ms
Ignores presses shorter than this (filters switch/contact noise)
- Long press time: ms (200-4000)
+ Long press time: ms
How long to hold before a long press triggers
- Double press time: ms (100-1000)
+ Double press time: ms
Max gap between two clicks to count as a double press
diff --git a/wled00/set.cpp b/wled00/set.cpp
index ae7b2f6d73..9b92ca451b 100644
--- a/wled00/set.cpp
+++ b/wled00/set.cpp
@@ -332,12 +332,9 @@ 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 <= 100) buttonDebounceMs = t;
- t = request->arg(F("LP")).toInt();
- 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 >= 100 && t <= 1000) buttonDoublePressMs = t;
+ if (request->hasArg(F("DB"))) buttonDebounceMs = constrain(request->arg(F("DB")).toInt(), 0, WLED_DEBOUNCE_MAX);
+ if (request->hasArg(F("LP"))) buttonLongPressMs = constrain(request->arg(F("LP")).toInt(), WLED_LONG_PRESS_MIN, WLED_LONG_PRESS_MAX);
+ if (request->hasArg(F("DP"))) buttonDoublePressMs = constrain(request->arg(F("DP")).toInt(), WLED_DOUBLE_PRESS_MIN, WLED_DOUBLE_PRESS_MAX);
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)
From 1304e3f5f79fbb7b24ea3824f13c35d95b049d3f Mon Sep 17 00:00:00 2001
From: Adam Matthews <52178922+adamsthws@users.noreply.github.com>
Date: Fri, 25 Sep 2026 16:28:40 +0100
Subject: [PATCH 04/12] Use runtime button timings in multi_relay and
pixels_dice_tray usermods
Both usermods implement their own button handling with private copies
of the old timing defines (and hard-coded 600/350 in multi_relay), so
they ignored the configurable debounce, long press and double press
times. Use buttonDebounceMs, buttonLongPressMs and buttonDoublePressMs
instead, and drop the duplicate defines.
---
usermods/multi_relay/multi_relay.cpp | 14 ++++++--------
usermods/pixels_dice_tray/pixels_dice_tray.cpp | 17 +++++------------
2 files changed, 11 insertions(+), 20 deletions(-)
diff --git a/usermods/multi_relay/multi_relay.cpp b/usermods/multi_relay/multi_relay.cpp
index 4cbdb2fe39..a3a814ea21 100644
--- a/usermods/multi_relay/multi_relay.cpp
+++ b/usermods/multi_relay/multi_relay.cpp
@@ -35,8 +35,6 @@
#define MULTI_RELAY_INVERTS false
#endif
-#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
-
#define ON true
#define OFF false
@@ -590,7 +588,7 @@ bool MultiRelay::handleButton(uint8_t b) {
if (buttons[b].longPressed == buttons[b].pressedBefore) return handled;
- if (now - buttons[b].pressedTime > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
+ if (now - buttons[b].pressedTime > buttonDebounceMs) { //fire edge event only after debounce time without change
for (int i=0; i 600) { //long press
+ if (now - buttons[b].pressedTime > buttonLongPressMs) { //long press
//longPressAction(b); //not exposed
//handled = false; //use if you want to pass to default behaviour
buttons[b].longPressed = true;
@@ -616,7 +614,7 @@ bool MultiRelay::handleButton(uint8_t b) {
} else if (!isButtonPressed(b) && buttons[b].pressedBefore) { //released
long dur = now - buttons[b].pressedTime;
- if (dur < WLED_DEBOUNCE_THRESHOLD) {
+ if (dur < buttonDebounceMs) {
buttons[b].pressedBefore = false;
return handled;
} //too short "press", debounce
@@ -624,7 +622,7 @@ bool MultiRelay::handleButton(uint8_t b) {
buttons[b].waitTime = 0;
if (!buttons[b].longPressed) { //short press
- // if this is second release within 350ms it is a double press (buttonWaitTime!=0)
+ // if this is second release within double press time it is a double press (buttonWaitTime!=0)
if (doublePress) {
//doublePressAction(b); //not exposed
//handled = false; //use if you want to pass to default behaviour
@@ -635,8 +633,8 @@ bool MultiRelay::handleButton(uint8_t b) {
buttons[b].pressedBefore = false;
buttons[b].longPressed = false;
}
- // if 350ms elapsed since last press/release it is a short press
- if (buttons[b].waitTime && now - buttons[b].waitTime > 350 && !buttons[b].pressedBefore) {
+ // if double press time elapsed since last 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); //not exposed
for (int i=0; i WLED_LONG_PRESS) { //long press
+ if (now - buttons[b].pressedTime > buttonLongPressMs) { //long press
menu_ctrl.HandleButton(ButtonType::LONG, b);
buttons[b].longPressed = true;
return true;
@@ -490,7 +483,7 @@ class PixelsDiceTrayUsermod : public Usermod {
} else if (!isButtonPressed(b) && buttons[b].pressedBefore) { //released
long dur = now - buttons[b].pressedTime;
- if (dur < WLED_DEBOUNCE_THRESHOLD) {
+ if (dur < buttonDebounceMs) {
buttons[b].pressedBefore = false;
return true;
} //too short "press", debounce
@@ -499,7 +492,7 @@ class PixelsDiceTrayUsermod : public Usermod {
buttons[b].waitTime = 0;
if (!buttons[b].longPressed) { //short press
- // if this is second release within 350ms it is a double press (buttonWaitTime!=0)
+ // if this is second release within double press time it is a double press (buttonWaitTime!=0)
if (doublePress) {
menu_ctrl.HandleButton(ButtonType::DOUBLE, b);
} else {
@@ -509,8 +502,8 @@ class PixelsDiceTrayUsermod : public Usermod {
buttons[b].pressedBefore = false;
buttons[b].longPressed = false;
}
- // if 350ms elapsed since last press/release it is a short press
- if (buttons[b].waitTime && now - buttons[b].waitTime > WLED_DOUBLE_PRESS &&
+ // if double press time elapsed since last press/release it is a short press
+ if (buttons[b].waitTime && now - buttons[b].waitTime > buttonDoublePressMs &&
!buttons[b].pressedBefore) {
buttons[b].waitTime = 0;
menu_ctrl.HandleButton(ButtonType::SINGLE, b);
From c74fba3437673ac3541f7262d6368aa3aa391274 Mon Sep 17 00:00:00 2001
From: Adam Matthews <52178922+adamsthws@users.noreply.github.com>
Date: Fri, 25 Sep 2026 16:42:33 +0100
Subject: [PATCH 05/12] Fix long-press repeat timing with configurable long
press time
Repeated long-press actions on buttons >0 (e.g. dimming) were timed
against the long press threshold, which was harmless while it was a
fixed 600ms but breaks once it is configurable:
- repeats used a fixed 400ms head start, so the repeat interval was
(long press time - 400ms): every loop iteration at <=400ms, and 3.6s
at 4000ms.
- the pause between the first action and the first repeat was a full
long press time (longPressAction() resets pressedTime), e.g. 4s hold,
one step, another 4s wait, then repeats.
Once the first long press action has fired, repeats are now timed with
a fixed WLED_LONG_REPEAT_DELAY (600ms) pause followed by a fixed
WLED_LONG_REPEATED_ACTION (200ms) interval, independent of the long
press time. Behaviour of the default dimming at the default long press
time is unchanged.
The pause now starts from the first action for all buttons >0, not
only for the built-in dimming on button 1: a long-press preset on
buttons >0 previously fired twice back-to-back and then repeated.
Button 0 is unaffected: its pressedTime is never moved, so the AP-mode
and factory-reset hold times still count from the start of the press.
Releases after a long press are no longer treated as debounce. A
release within the debounce time after the first action skipped the
longPressed reset, which swallowed the next short press and skipped
the brightness direction toggle on the next long press.
---
wled00/button.cpp | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/wled00/button.cpp b/wled00/button.cpp
index 6d63c297a1..509ac7bcd1 100644
--- a/wled00/button.cpp
+++ b/wled00/button.cpp
@@ -4,12 +4,14 @@
* Physical IO
*/
-#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_REPEATED_ACTION 200 // how often a repeated action (e.g. dimming) is fired on long press on button IDs >0
+#define WLED_LONG_REPEAT_DELAY 600 // pause after the first long press action before repeating starts 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
#define WLED_LONG_BRI_STEPS 16 // how much to increase/decrease the brightness with each long press repetition
static_assert(WLED_LONG_PRESS_MAX < WLED_LONG_AP, "long press must not reach the AP-mode/factory-reset hold time of button 0");
+static_assert(WLED_LONG_REPEAT_DELAY > WLED_LONG_REPEATED_ACTION, "repeat delay must be longer than the repeated action interval");
static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage
@@ -303,15 +305,17 @@ void handleButton()
if (!buttons[b].pressedBefore) buttons[b].pressedTime = now;
buttons[b].pressedBefore = true;
- if (now - buttons[b].pressedTime > buttonLongPressMs) { //long press
- if (!buttons[b].longPressed) {
+ // first long press fires after buttonLongPressMs, repeats are timed from the last action
+ if (!buttons[b].longPressed) {
+ if (now - buttons[b].pressedTime > buttonLongPressMs) { //long press
buttonBriDirection = !buttonBriDirection; //toggle brightness direction on long press
longPressAction(b);
- } else if (b) { //repeatable action (~5 times per s) on button > 0
- longPressAction(b);
- buttons[b].pressedTime = now - WLED_LONG_REPEATED_ACTION; //200ms
+ if (b) buttons[b].pressedTime = now; // pause before first repeat; button 0 keeps press start for AP timing
+ buttons[b].longPressed = true;
}
- buttons[b].longPressed = true;
+ } else if (b && now - buttons[b].pressedTime > WLED_LONG_REPEAT_DELAY) { //repeatable action (~5 times per s) on button > 0
+ longPressAction(b);
+ buttons[b].pressedTime = now - (WLED_LONG_REPEAT_DELAY - WLED_LONG_REPEATED_ACTION); // next repeat after WLED_LONG_REPEATED_ACTION
}
} else if (buttons[b].pressedBefore) { //released
@@ -323,7 +327,7 @@ void handleButton()
continue;
}
- if (dur < buttonDebounceMs) {buttons[b].pressedBefore = false; continue;} // too short "press", debounce
+ if (dur < buttonDebounceMs && !buttons[b].longPressed) {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;
From 840006f5792f5278895cc18a0f1c66214151b888 Mon Sep 17 00:00:00 2001
From: Adam Matthews <52178922+adamsthws@users.noreply.github.com>
Date: Tue, 29 Sep 2026 20:51:42 +0100
Subject: [PATCH 06/12] Inline button timing defaults, extend static validation
of limits
The default debounce/long press/double press times are only used to
initialise the globals and are not overridable, so inline them in
wled.h instead of keeping WLED_DEBOUNCE_THRESHOLD, WLED_LONG_PRESS and
WLED_DOUBLE_PRESS macros. Defaults are unchanged (50/600/350ms).
Add static_asserts for the remaining invariants:
- min < max for long press and double press, debounce max > 0
- the button 0 AP-mode hold is shorter than the factory reset hold: the
release handler checks the factory reset time inside the AP-mode
branch, so otherwise every AP-mode hold would format the filesystem
---
wled00/button.cpp | 1 +
wled00/const.h | 9 ++++-----
wled00/wled.h | 6 +++---
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/wled00/button.cpp b/wled00/button.cpp
index 509ac7bcd1..70897d0f17 100644
--- a/wled00/button.cpp
+++ b/wled00/button.cpp
@@ -11,6 +11,7 @@
#define WLED_LONG_BRI_STEPS 16 // how much to increase/decrease the brightness with each long press repetition
static_assert(WLED_LONG_PRESS_MAX < WLED_LONG_AP, "long press must not reach the AP-mode/factory-reset hold time of button 0");
+static_assert(WLED_LONG_AP < WLED_LONG_FACTORY_RESET, "AP mode hold must be shorter than factory reset hold");
static_assert(WLED_LONG_REPEAT_DELAY > WLED_LONG_REPEATED_ACTION, "repeat delay must be longer than the repeated action interval");
static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage
diff --git a/wled00/const.h b/wled00/const.h
index 48760cd2b8..9d2c6b9746 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -651,16 +651,15 @@ 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: trigger long press after the button remains pressed for 600ms
-#define WLED_DOUBLE_PRESS 350 // default: double press if another press within 350ms after a short press
-#define WLED_DEBOUNCE_MAX 100 // settings limits for the button timings above
+#define WLED_DEBOUNCE_MAX 100 // settings limits for button timings (buttonDebounceMs, buttonLongPressMs, buttonDoublePressMs)
#define WLED_LONG_PRESS_MIN 200
#define WLED_LONG_PRESS_MAX 4000 // must stay below WLED_LONG_AP (button.cpp)
#define WLED_DOUBLE_PRESS_MIN 100
#define WLED_DOUBLE_PRESS_MAX 1000
+static_assert(WLED_DEBOUNCE_MAX > 0, "debounce max must be positive");
+static_assert(WLED_LONG_PRESS_MIN < WLED_LONG_PRESS_MAX, "long press min must be below max");
+static_assert(WLED_DOUBLE_PRESS_MIN < WLED_DOUBLE_PRESS_MAX, "double press min must be below max");
static_assert(WLED_LONG_PRESS_MIN > WLED_DEBOUNCE_MAX, "long press must be longer than the debounce time");
-
// Size of buffer for API JSON object (increase for more segments)
#ifdef ESP8266
#define JSON_BUFFER_SIZE 10240
diff --git a/wled00/wled.h b/wled00/wled.h
index a89b7709fc..14031f0e3d 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -647,9 +647,9 @@ WLED_GLOBAL std::vector
Disable internal pull-up/down:
Touch threshold:
- Debounce time: ms
- Ignores presses shorter than this (filters switch/contact noise)
- Long press time: ms
- How long to hold before a long press triggers
- Double press time: ms
- Max gap between two clicks to count as a double press
+
+ Show Advanced Button Settings
+
+ Debounce time: ms
+ Long press time: ms
+ Double press time: ms
+
IR Remote
From 6c674f67c8e92b7d0bc77177b6eb32c404e2a861 Mon Sep 17 00:00:00 2001
From: Adam Matthews <52178922+adamsthws@users.noreply.github.com>
Date: Tue, 29 Sep 2026 22:51:59 +0100
Subject: [PATCH 08/12] Rename WLED_LONG_REPEATED_ACTION to
WLED_LONG_REPEAT_INTERVAL
Its meaning changed from a head start subtracted from the long press
time (400ms, giving a 200ms interval at the default 600ms long press)
to the actual interval between repeats (200ms). The new name says what
it is, and pairs with WLED_LONG_REPEAT_DELAY.
---
wled00/button.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/wled00/button.cpp b/wled00/button.cpp
index 70897d0f17..6fc63c87b6 100644
--- a/wled00/button.cpp
+++ b/wled00/button.cpp
@@ -4,7 +4,7 @@
* Physical IO
*/
-#define WLED_LONG_REPEATED_ACTION 200 // how often a repeated action (e.g. dimming) is fired on long press on button IDs >0
+#define WLED_LONG_REPEAT_INTERVAL 200 // interval between repeated actions (e.g. dimming) on long press on button IDs >0
#define WLED_LONG_REPEAT_DELAY 600 // pause after the first long press action before repeating starts 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
@@ -12,7 +12,7 @@
static_assert(WLED_LONG_PRESS_MAX < WLED_LONG_AP, "long press must not reach the AP-mode/factory-reset hold time of button 0");
static_assert(WLED_LONG_AP < WLED_LONG_FACTORY_RESET, "AP mode hold must be shorter than factory reset hold");
-static_assert(WLED_LONG_REPEAT_DELAY > WLED_LONG_REPEATED_ACTION, "repeat delay must be longer than the repeated action interval");
+static_assert(WLED_LONG_REPEAT_DELAY > WLED_LONG_REPEAT_INTERVAL, "repeat delay must be longer than the repeat interval");
static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage
@@ -316,7 +316,7 @@ void handleButton()
}
} else if (b && now - buttons[b].pressedTime > WLED_LONG_REPEAT_DELAY) { //repeatable action (~5 times per s) on button > 0
longPressAction(b);
- buttons[b].pressedTime = now - (WLED_LONG_REPEAT_DELAY - WLED_LONG_REPEATED_ACTION); // next repeat after WLED_LONG_REPEATED_ACTION
+ buttons[b].pressedTime = now - (WLED_LONG_REPEAT_DELAY - WLED_LONG_REPEAT_INTERVAL); // next repeat after WLED_LONG_REPEAT_INTERVAL
}
} else if (buttons[b].pressedBefore) { //released
From 5a11e98777baaba7fcabcd93784aaacdf87ce8a4 Mon Sep 17 00:00:00 2001
From: Adam Matthews <52178922+adamsthws@users.noreply.github.com>
Date: Wed, 30 Sep 2026 19:09:53 +0100
Subject: [PATCH 09/12] Make long-press repeat delay and interval configurable
Replace WLED_LONG_REPEAT_DELAY and WLED_LONG_REPEAT_INTERVAL with the
settings "Long-press repeat delay" (default 600ms) and "Long-press
repeat interval" (default 200ms), 100-4000ms each, in the advanced
button settings. Defaults are unchanged.
The repeat delay must be at least the repeat interval (otherwise the
repeat timing would underflow and fire on every loop):
- the settings page marks the delay invalid and blocks saving
- cfg.json / JSON API / form input raise the delay to the interval
when the settings are read, so the corrected value is what is saved
and shown
Also hyphenate the compound modifiers in the button labels
("Long-press time", "Double-press time").
---
wled00/button.cpp | 7 ++-----
wled00/cfg.cpp | 7 +++++++
wled00/const.h | 9 ++++++++-
wled00/data/settings_leds.htm | 10 ++++++++--
wled00/set.cpp | 3 +++
wled00/wled.h | 2 ++
wled00/xml.cpp | 2 ++
7 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/wled00/button.cpp b/wled00/button.cpp
index 6fc63c87b6..17bf207b04 100644
--- a/wled00/button.cpp
+++ b/wled00/button.cpp
@@ -4,15 +4,12 @@
* Physical IO
*/
-#define WLED_LONG_REPEAT_INTERVAL 200 // interval between repeated actions (e.g. dimming) on long press on button IDs >0
-#define WLED_LONG_REPEAT_DELAY 600 // pause after the first long press action before repeating starts 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
#define WLED_LONG_BRI_STEPS 16 // how much to increase/decrease the brightness with each long press repetition
static_assert(WLED_LONG_PRESS_MAX < WLED_LONG_AP, "long press must not reach the AP-mode/factory-reset hold time of button 0");
static_assert(WLED_LONG_AP < WLED_LONG_FACTORY_RESET, "AP mode hold must be shorter than factory reset hold");
-static_assert(WLED_LONG_REPEAT_DELAY > WLED_LONG_REPEAT_INTERVAL, "repeat delay must be longer than the repeat interval");
static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage
@@ -314,9 +311,9 @@ void handleButton()
if (b) buttons[b].pressedTime = now; // pause before first repeat; button 0 keeps press start for AP timing
buttons[b].longPressed = true;
}
- } else if (b && now - buttons[b].pressedTime > WLED_LONG_REPEAT_DELAY) { //repeatable action (~5 times per s) on button > 0
+ } else if (b && now - buttons[b].pressedTime > buttonRepeatDelayMs) { //repeatable action on button > 0
longPressAction(b);
- buttons[b].pressedTime = now - (WLED_LONG_REPEAT_DELAY - WLED_LONG_REPEAT_INTERVAL); // next repeat after WLED_LONG_REPEAT_INTERVAL
+ buttons[b].pressedTime = now - (buttonRepeatDelayMs - buttonRepeatIntervalMs); // next repeat after buttonRepeatIntervalMs (delay >= interval is enforced where settings are read)
}
} else if (buttons[b].pressedBefore) { //released
diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp
index 79a1954178..d8af9b9b8a 100644
--- a/wled00/cfg.cpp
+++ b/wled00/cfg.cpp
@@ -455,9 +455,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(buttonDebounceMs, btn_obj[F("dbnc")]);
CJSON(buttonLongPressMs, btn_obj[F("lp")]);
CJSON(buttonDoublePressMs, btn_obj[F("dp")]);
+ CJSON(buttonRepeatDelayMs, btn_obj[F("brpd")]);
+ CJSON(buttonRepeatIntervalMs, btn_obj[F("brpi")]);
buttonDebounceMs = min(buttonDebounceMs, (uint16_t)WLED_DEBOUNCE_MAX);
buttonLongPressMs = constrain(buttonLongPressMs, WLED_LONG_PRESS_MIN, WLED_LONG_PRESS_MAX);
buttonDoublePressMs = constrain(buttonDoublePressMs, WLED_DOUBLE_PRESS_MIN, WLED_DOUBLE_PRESS_MAX);
+ buttonRepeatIntervalMs = constrain(buttonRepeatIntervalMs, WLED_LONG_REPEAT_INTERVAL_MIN, WLED_LONG_REPEAT_INTERVAL_MAX);
+ buttonRepeatDelayMs = constrain(buttonRepeatDelayMs, WLED_LONG_REPEAT_DELAY_MIN, WLED_LONG_REPEAT_DELAY_MAX);
+ buttonRepeatDelayMs = max(buttonRepeatDelayMs, buttonRepeatIntervalMs); // repeat delay must be at least the repeat interval
#ifndef WLED_DISABLE_INFRARED
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
@@ -1058,6 +1063,8 @@ void serializeConfig(JsonObject root) {
hw_btn[F("dbnc")] = buttonDebounceMs;
hw_btn[F("lp")] = buttonLongPressMs;
hw_btn[F("dp")] = buttonDoublePressMs;
+ hw_btn[F("brpd")] = buttonRepeatDelayMs;
+ hw_btn[F("brpi")] = buttonRepeatIntervalMs;
JsonObject hw_ir = hw.createNestedObject("ir");
#ifndef WLED_DISABLE_INFRARED
diff --git a/wled00/const.h b/wled00/const.h
index 9d2c6b9746..17799bb42d 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -651,7 +651,7 @@ 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_MAX 100 // settings limits for button timings (buttonDebounceMs, buttonLongPressMs, buttonDoublePressMs)
+#define WLED_DEBOUNCE_MAX 100 // settings limits for button timings (buttonDebounceMs, buttonLongPressMs, buttonDoublePressMs, buttonRepeatDelayMs, buttonRepeatIntervalMs)
#define WLED_LONG_PRESS_MIN 200
#define WLED_LONG_PRESS_MAX 4000 // must stay below WLED_LONG_AP (button.cpp)
#define WLED_DOUBLE_PRESS_MIN 100
@@ -660,6 +660,13 @@ static_assert(WLED_DEBOUNCE_MAX > 0, "debounce max must be positive");
static_assert(WLED_LONG_PRESS_MIN < WLED_LONG_PRESS_MAX, "long press min must be below max");
static_assert(WLED_DOUBLE_PRESS_MIN < WLED_DOUBLE_PRESS_MAX, "double press min must be below max");
static_assert(WLED_LONG_PRESS_MIN > WLED_DEBOUNCE_MAX, "long press must be longer than the debounce time");
+#define WLED_LONG_REPEAT_DELAY_MIN 100 // buttonRepeatDelayMs must also be >= buttonRepeatIntervalMs (enforced where settings are read)
+#define WLED_LONG_REPEAT_DELAY_MAX 4000
+#define WLED_LONG_REPEAT_INTERVAL_MIN 100
+#define WLED_LONG_REPEAT_INTERVAL_MAX 4000
+static_assert(WLED_LONG_REPEAT_DELAY_MIN < WLED_LONG_REPEAT_DELAY_MAX, "repeat delay min must be below max");
+static_assert(WLED_LONG_REPEAT_INTERVAL_MIN < WLED_LONG_REPEAT_INTERVAL_MAX, "repeat interval min must be below max");
+static_assert(WLED_LONG_REPEAT_INTERVAL_MAX <= WLED_LONG_REPEAT_DELAY_MAX, "raising the repeat delay to the repeat interval must stay within the delay range");
// 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 91100520d7..2607b0e0f7 100644
--- a/wled00/data/settings_leds.htm
+++ b/wled00/data/settings_leds.htm
@@ -511,6 +511,8 @@
gId('psu2').innerHTML = s2;
gId("json").style.display = d.Sf.IT.value==8 ? "" : "none";
// advanced button settings only when enabled, or if invalid (a hidden invalid field would silently block saving)
+ // long-press repeat delay must be at least the repeat interval (blocks saving an invalid pair)
+ d.Sf.BRPD.setCustomValidity(parseInt(d.Sf.BRPD.value) < parseInt(d.Sf.BRPI.value) ? "Must not be shorter than the long-press repeat interval" : "");
let btInv = [...gId("btnAdv").querySelectorAll("input")].some(e => !e.checkValidity());
gId("btnAdv").style.display = (gId("btnAS").checked || btInv) ? "" : "none";
@@ -807,6 +809,8 @@
if (b.dbnc !== undefined) d.getElementsByName("DB")[0].value = b.dbnc;
if (b.lp !== undefined) d.getElementsByName("LP")[0].value = b.lp;
if (b.dp !== undefined) d.getElementsByName("DP")[0].value = b.dp;
+ if (b.brpd !== undefined) d.getElementsByName("BRPD")[0].value = b.brpd;
+ if (b.brpi !== undefined) d.getElementsByName("BRPI")[0].value = b.brpi;
}
let ir = c.hw.ir;
if (ir) {
@@ -1105,8 +1109,10 @@
Buttons
Show Advanced Button Settings
Debounce time: ms
- Long press time: ms
- Double press time: ms
+ Long-press time: ms
+ Double-press time: ms
+ Long-press repeat delay: ms
+ Long-press repeat interval: ms
diff --git a/wled00/set.cpp b/wled00/set.cpp
index 9b92ca451b..ccb5d471cb 100644
--- a/wled00/set.cpp
+++ b/wled00/set.cpp
@@ -335,6 +335,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (request->hasArg(F("DB"))) buttonDebounceMs = constrain(request->arg(F("DB")).toInt(), 0, WLED_DEBOUNCE_MAX);
if (request->hasArg(F("LP"))) buttonLongPressMs = constrain(request->arg(F("LP")).toInt(), WLED_LONG_PRESS_MIN, WLED_LONG_PRESS_MAX);
if (request->hasArg(F("DP"))) buttonDoublePressMs = constrain(request->arg(F("DP")).toInt(), WLED_DOUBLE_PRESS_MIN, WLED_DOUBLE_PRESS_MAX);
+ if (request->hasArg(F("BRPI"))) buttonRepeatIntervalMs = constrain(request->arg(F("BRPI")).toInt(), WLED_LONG_REPEAT_INTERVAL_MIN, WLED_LONG_REPEAT_INTERVAL_MAX);
+ if (request->hasArg(F("BRPD"))) buttonRepeatDelayMs = constrain(request->arg(F("BRPD")).toInt(), WLED_LONG_REPEAT_DELAY_MIN, WLED_LONG_REPEAT_DELAY_MAX);
+ buttonRepeatDelayMs = max(buttonRepeatDelayMs, buttonRepeatIntervalMs); // repeat delay must be at least the repeat interval
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 14031f0e3d..26d4389220 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -650,6 +650,8 @@ WLED_GLOBAL byte touchThreshold _INIT(TOUCH_THRESH
WLED_GLOBAL uint16_t buttonDebounceMs _INIT(50); // only consider button input of at least this long as valid (debouncing)
WLED_GLOBAL uint16_t buttonLongPressMs _INIT(600); // long press if button is held for at least this long
WLED_GLOBAL uint16_t buttonDoublePressMs _INIT(350); // double press if another press follows a short press within this time
+WLED_GLOBAL uint16_t buttonRepeatDelayMs _INIT(600); // pause after the first long-press action before it repeats (buttons >0)
+WLED_GLOBAL uint16_t buttonRepeatIntervalMs _INIT(200); // interval between repeated long-press actions (buttons >0)
// notifications
WLED_GLOBAL bool sendNotifications _INIT(false); // master notification switch
diff --git a/wled00/xml.cpp b/wled00/xml.cpp
index 10d5bf3948..1ad055cc9f 100644
--- a/wled00/xml.cpp
+++ b/wled00/xml.cpp
@@ -464,6 +464,8 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormValue(settingsScript,PSTR("DB"),buttonDebounceMs);
printSetFormValue(settingsScript,PSTR("LP"),buttonLongPressMs);
printSetFormValue(settingsScript,PSTR("DP"),buttonDoublePressMs);
+ printSetFormValue(settingsScript,PSTR("BRPD"),buttonRepeatDelayMs);
+ printSetFormValue(settingsScript,PSTR("BRPI"),buttonRepeatIntervalMs);
#ifndef WLED_DISABLE_INFRARED
printSetFormValue(settingsScript,PSTR("IR"),irPin);
printSetFormValue(settingsScript,PSTR("IT"),irEnabled);
From 8cdedac9fa7150b17be927de132613a68e5112fd Mon Sep 17 00:00:00 2001
From: Adam Matthews <52178922+adamsthws@users.noreply.github.com>
Date: Thu, 1 Oct 2026 23:31:19 +0100
Subject: [PATCH 10/12] Remove divider between button list and +/- buttons
The +/- buttons add and remove entries in the button list directly
above them, so a separator line between the two is unnecessary.
---
wled00/data/settings_leds.htm | 1 -
1 file changed, 1 deletion(-)
diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm
index 2607b0e0f7..2046e95085 100644
--- a/wled00/data/settings_leds.htm
+++ b/wled00/data/settings_leds.htm
@@ -1099,7 +1099,6 @@
Hardware setup
Buttons
-
From ec08d17ecc3488051d4263425573c6ebd3ed9f25 Mon Sep 17 00:00:00 2001
From: Adam Matthews <52178922+adamsthws@users.noreply.github.com>
Date: Thu, 1 Oct 2026 23:31:24 +0100
Subject: [PATCH 11/12] Move pull-up and touch threshold into advanced button
settings
Move "Disable internal pull-up/down" and "Touch threshold" to live under "Show Advanced Button Settings" alongside the button timings.
Also add a small gap between the checkbox and the advanced settings.
---
wled00/data/settings_leds.htm | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm
index 2046e95085..2216ba94db 100644
--- a/wled00/data/settings_leds.htm
+++ b/wled00/data/settings_leds.htm
@@ -1102,11 +1102,11 @@
+ Disable internal pull-up/down:
+ Touch threshold:
Debounce time: ms
Long-press time: ms
Double-press time: ms
From 1794997d4ae85a63c860084e9646a133ab7d0845 Mon Sep 17 00:00:00 2001
From: Adam Matthews <52178922+adamsthws@users.noreply.github.com>
Date: Sat, 3 Oct 2026 11:52:47 +0100
Subject: [PATCH 12/12] Group long-press settings together in advanced button
settings
Show "Double-press time" before "Long-press time" so the long-press
time, repeat delay and repeat interval settings sit next to each other.
---
wled00/data/settings_leds.htm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm
index 2216ba94db..56b9e2f19c 100644
--- a/wled00/data/settings_leds.htm
+++ b/wled00/data/settings_leds.htm
@@ -1108,8 +1108,8 @@
Buttons
Disable internal pull-up/down:
Touch threshold:
Debounce time: ms
- Long-press time: ms
Double-press time: ms
+ Long-press time: ms
Long-press repeat delay: ms
Long-press repeat interval: ms