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
+

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

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 @@

Buttons

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

Show Advanced Button Settings
-