-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix(devkit2): Add button deboucing and long press haptic feedback #4245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| uint32_t stable_time = (current_time - last_state_change_time) * BUTTON_CHECK_INTERVAL; | ||
| if (stable_time >= DEBOUNCE_TIME_MS) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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)) {
TuEmb
left a comment
There was a problem hiding this 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) { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
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