Skip to content

fix(layer): debounce the bi-directional switch so one flick moves one layer - #19

Merged
DriftKingTW merged 2 commits into
masterfrom
fix/layer-switch-debounce
Aug 22, 2026
Merged

fix(layer): debounce the bi-directional switch so one flick moves one layer#19
DriftKingTW merged 2 commits into
masterfrom
fix/layer-switch-debounce

Conversation

@DriftKingTW

Copy link
Copy Markdown
Owner

Problem

Flicking the bi-directional switch to change layer sometimes advanced two layers at once.

The handler waited for the pin to read inactive exactly once, with no debounce, so it returned mid-bounce on release:

while (digitalRead(BD_SW_CW) == ACTIVE) {   // exits on the FIRST inactive sample
    delay(10);
}

loop() comes back around in roughly 1 ms — well inside a mechanical switch's 1–10 ms release chatter — so the still-bouncing contact was read as a brand-new flick. The pins are bare INPUT_PULLUP with no hardware debounce.

It only showed up on the switch because switchLayout(int) has no guard time, unlike the no-arg switchLayout(), whose trailing delay(300) masked the problem on the FN-key path.

Fix

  • readSwitchPressed() — re-samples the press edge after a short settling delay, rejecting contact chatter and electrical glitches.
  • waitForSwitchRelease() — blocks until the contact has read inactive continuously for SW_DEBOUNCE_MS (25 ms), restarting the quiet period on any activity.
  • All three switch directions (CW / CCW / push) routed through both.

Cost is nil while the switch is idle: readSwitchPressed() returns on its first digitalRead, so the settling delay only runs on an actual press.

Also guards switchLayout(int) against layoutLength == 0, where wrapping stored layoutLength - 1 == -1 as 255 and indexed past the end of keyConfig.

Testing

  • pio run builds clean; no new warnings from src/.
  • Flashed to hardware and confirmed by the author: one flick now moves exactly one layer.

DriftKingTW and others added 2 commits August 22, 2026 21:15
… layer

Flicking the switch sometimes advanced two layers at once. The handler waited
for the pin to read inactive exactly once, with no debounce, so it returned
mid-bounce on release: loop() comes back around in roughly 1 ms, well inside a
mechanical switch's 1-10 ms release chatter, and read the same flick as a second
press. The pins are bare INPUT_PULLUP with no hardware debounce, and the
switchLayout(int) overload has no guard time (unlike the no-arg switchLayout(),
whose trailing delay(300) hid the problem on the FN-key path).

Add readSwitchPressed(), which re-samples the press edge to reject glitches, and
waitForSwitchRelease(), which blocks until the contact has read inactive
continuously for SW_DEBOUNCE_MS. Route all three switch directions through them.

Also guard switchLayout(int) against layoutLength == 0, where wrapping stored
layoutLength - 1 == -1 as 255 and indexed past the end of keyConfig.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Version 0.0.1 is no longer on the PlatformIO registry (only 0.0.2-0.0.4
remain), and `^0.0.1` resolves to `>=0.0.1 <0.0.2`, so a clean checkout fails
with:

    UnknownPackageError: Could not find the package with
    'jnthas/Improv WiFi Library @ ^0.0.1' requirements

Local builds kept working only because .pio/libdeps still held a cached 0.0.1
from 2023, so this went unnoticed until CI ran on a fresh runner.

Pin an exact version rather than a caret range: the range is what let the
dependency vanish silently, and NimBLE-Arduino is already pinned this way.

0.0.4 is API-compatible with what the firmware uses (ImprovWiFi,
setDeviceInfo, onImprovError, onImprovConnected, handleSerial); the header
changes are additive only. Flash grows by 292 bytes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@DriftKingTW
DriftKingTW merged commit 51265c4 into master Aug 22, 2026
1 check passed
@DriftKingTW
DriftKingTW deleted the fix/layer-switch-debounce branch August 22, 2026 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant