Symptom
On the assembled PCB the physical button (Button1) works unreliably: sometimes a press is ignored, sometimes phantom/false presses fire on their own. Right after a fresh re-solder of the ESP mount (which had a ground pad shorting to an unused, unconnected pin) the button worked perfectly — then regressed after a couple of days. This points strongly at a hardware/solder root cause, but the firmware also has no protection against marginal input.
Hardware hypotheses (likely primary)
- Cold / marginal solder joints on the button trace or the ESP mount that drift with thermal cycling and time.
- Flux residue or oxidation creating high-resistance leakage paths around GPIO12.
- The internal pull-up alone may be too weak to hold the line high if GPIO12 leaks toward GND through a contaminated joint.
- Floating neighbouring pins coupling noise into the button net.
Suggested checks: reflow and clean (IPA) the ESP mount and button traces, verify there are no solder bridges, confirm a solid ground, and consider an external ~10k pull-up plus a small (~100nF) hardware debounce cap to GND.
Firmware contributing factors
The firmware does nothing to reject electrical noise, which makes marginal hardware much worse:
- No software debounce.
ButtonState::update (src/button.rs:52-79) only records pressed_at on the first "down" and fires the callback on release (<500ms = Short, >=500ms = Long). Any contact bounce or noise on the line can register as spurious presses.
- Variable polling cadence. The main loop polls
button1.is_low() every iteration (src/setup.rs:200) but only yields via FreeRtos::delay_ms(0) (src/setup.rs:321), so the effective sampling rate floats with render / UART / MQTT load — presses can be missed or doubled.
- The
InterruptType::NegEdge interrupt is registered (src/setup.rs:98-110) but never actually used; input is pure polling.
Proposed firmware mitigations (future, not in this issue)
- Add software debounce: require the level to be stable for ~20–30ms before accepting a transition.
- Filter press/release edges explicitly instead of firing on a single release sample.
- Optionally wire up the already-registered NegEdge interrupt.
Notes
This may share an electrical root cause (noise / marginal shared ground from the solder work) with #30. Filed separately for tracking.
Symptom
On the assembled PCB the physical button (Button1) works unreliably: sometimes a press is ignored, sometimes phantom/false presses fire on their own. Right after a fresh re-solder of the ESP mount (which had a ground pad shorting to an unused, unconnected pin) the button worked perfectly — then regressed after a couple of days. This points strongly at a hardware/solder root cause, but the firmware also has no protection against marginal input.
Hardware hypotheses (likely primary)
Suggested checks: reflow and clean (IPA) the ESP mount and button traces, verify there are no solder bridges, confirm a solid ground, and consider an external ~10k pull-up plus a small (~100nF) hardware debounce cap to GND.
Firmware contributing factors
The firmware does nothing to reject electrical noise, which makes marginal hardware much worse:
ButtonState::update(src/button.rs:52-79) only recordspressed_aton the first "down" and fires the callback on release (<500ms= Short,>=500ms= Long). Any contact bounce or noise on the line can register as spurious presses.button1.is_low()every iteration (src/setup.rs:200) but only yields viaFreeRtos::delay_ms(0)(src/setup.rs:321), so the effective sampling rate floats with render / UART / MQTT load — presses can be missed or doubled.InterruptType::NegEdgeinterrupt is registered (src/setup.rs:98-110) but never actually used; input is pure polling.Proposed firmware mitigations (future, not in this issue)
Notes
This may share an electrical root cause (noise / marginal shared ground from the solder work) with #30. Filed separately for tracking.