⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content

Conversation

@kevtae
Copy link
Contributor

@kevtae kevtae commented Jan 16, 2026

Fixed a bug where a long press on the button could accidentally power off the device instead of only sending a BLE notification.

Problem: Physical buttons can “bounce,” meaning the signal rapidly flickers on/off before settling. The firmware was interpreting that noise as a quick tap, which triggered the power-off behavior.

Solution: added a 20ms debounce filter to wait for the signal to stabilize before counting a press and also added haptic feedback (a vibration) when a long press is recognized so you get clear confirmation it worked

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a debouncing mechanism for the button to prevent spurious presses and adds haptic feedback for long presses. The changes look good and directly address the issue described. I've provided a couple of suggestions to further improve the robustness of the new debouncing logic: ensuring correct access to the was_pressed variable shared between an ISR and a thread, and preventing a potential integer overflow in the time calculation. Addressing these points will make the implementation more robust.


u_int8_t btn_state = was_pressed ? BUTTON_PRESSED : BUTTON_RELEASED;
// Debounce logic - filter out electrical noise/contact bounce
bool raw_state = was_pressed;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The global variable was_pressed is written in an interrupt service routine (button_pressed_callback) and read here in a workqueue thread context. To prevent incorrect compiler optimizations and ensure the latest value is always read, it should be declared as volatile.

For example, in its declaration:

static volatile bool was_pressed = false;

For even greater safety in a concurrent environment like Zephyr, consider using atomic variables (atomic_t) for was_pressed to guarantee atomic access and proper memory ordering between the ISR and the thread.

Comment on lines +197 to +198
uint32_t stable_time = (current_time - last_state_change_time) * BUTTON_CHECK_INTERVAL;
if (stable_time >= DEBOUNCE_TIME_MS) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The calculation of stable_time involves a multiplication that can overflow if the button state remains unchanged for a long period (e.g., over ~49 days), as current_time - last_state_change_time could become very large. This overflow would cause stable_time to wrap around to a small value, potentially leading to incorrect debounce behavior.

To make this more robust, it's safer to perform the comparison in 'ticks' rather than milliseconds. This avoids the large multiplication and removes the intermediate stable_time variable.

    if ((current_time - last_state_change_time) >= ((DEBOUNCE_TIME_MS + BUTTON_CHECK_INTERVAL - 1) / BUTTON_CHECK_INTERVAL)) {

@beastoin
Copy link
Collaborator

thank @kevtae it's nice, btw @TuEmb can you help ?

Copy link
Contributor

@TuEmb TuEmb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevtae thanks for your PR,

Could you take a look at BUTTON_CHECK_INTERVAL, we already have ~40ms debounce. do the button flickers happen on your side ? it's good if you can capture the issue.


// Only update debounced state after stable for DEBOUNCE_TIME_MS
uint32_t stable_time = (current_time - last_state_change_time) * BUTTON_CHECK_INTERVAL;
if (stable_time >= DEBOUNCE_TIME_MS) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this condition never occurs. Basically, the button is checked with a 40 ms period, and your debounce time is shorter than one cycle.

LOG_PRINTK("long press detected\n");
btn_last_event = event;
notify_long_tap();
play_haptic_milli(100); // Haptic feedback for long press
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behavior is not our intention. We only play the haptic on power-up and power-down. If you prefer this behavior, you can keep it locally or implement a solution that allows customization from the phone app based on user preferences.

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.

3 participants