Skip to content

Commit 5117e25

Browse files
committed
modules: button: Add support for long press detection
Add capability to distinguish between short and long button press. Signed-off-by: Jan Tore Guggedal <jantore.guggedal@nordicsemi.no>
1 parent 152d26e commit 5117e25

File tree

8 files changed

+307
-106
lines changed

8 files changed

+307
-106
lines changed

app/src/modules/button/Kconfig.button

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ config APP_BUTTON_SHELL
1212
help
1313
Enable shell for the button module.
1414

15+
config APP_BUTTON_LONG_PRESS_TIMEOUT_MS
16+
int "Long press timeout in milliseconds"
17+
default 3000
18+
help
19+
Time in milliseconds that a button must be held to be considered
20+
a long press. Short presses are detected when the button is
21+
released before this timeout expires.
22+
1523
module = APP_BUTTON
1624
module-str = Button
1725
source "subsys/logging/Kconfig.template.log_config"

app/src/modules/button/button.c

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,104 @@
1616
/* Register log module */
1717
LOG_MODULE_REGISTER(button, CONFIG_APP_BUTTON_LOG_LEVEL);
1818

19+
/* Long press timeout in milliseconds */
20+
#define LONG_PRESS_TIMEOUT_MS CONFIG_APP_BUTTON_LONG_PRESS_TIMEOUT_MS
21+
22+
/* Button state structure */
23+
static struct {
24+
uint32_t pressed_buttons;
25+
struct k_work_delayable long_press_work;
26+
} button_state;
27+
1928
/* Define channels provided by this module */
2029
ZBUS_CHAN_DEFINE(BUTTON_CHAN,
21-
uint8_t,
30+
struct button_msg,
2231
NULL,
2332
NULL,
2433
ZBUS_OBSERVERS_EMPTY,
2534
ZBUS_MSG_INIT(0)
2635
);
2736

28-
/* Button handler called when a user pushes a button */
29-
static void button_handler(uint32_t button_states, uint32_t has_changed)
37+
/* Work handler for long press detection */
38+
static void long_press_work_handler(struct k_work *work)
3039
{
3140
int err;
32-
uint8_t button_number = 1;
41+
struct button_msg msg;
3342

34-
if (has_changed & button_states & DK_BTN1_MSK) {
35-
LOG_DBG("Button 1 pressed!");
43+
ARG_UNUSED(work);
3644

37-
err = zbus_chan_pub(&BUTTON_CHAN, &button_number, K_SECONDS(1));
45+
/* Check if button is still pressed */
46+
if (button_state.pressed_buttons & DK_BTN1_MSK) {
47+
LOG_DBG("Button 1 long press detected!");
48+
49+
msg.button_number = 1;
50+
msg.type = BUTTON_PRESS_LONG;
51+
52+
err = zbus_chan_pub(&BUTTON_CHAN, &msg, K_SECONDS(1));
3853
if (err) {
39-
LOG_ERR("zbus_chan_pub, error: %d", err);
54+
LOG_ERR("zbus_chan_pub long press, error: %d", err);
4055
SEND_FATAL_ERROR();
41-
return;
56+
}
57+
}
58+
}
59+
60+
/* Publish short press message */
61+
static void publish_short_press(uint8_t button_number)
62+
{
63+
int err;
64+
struct button_msg msg;
65+
66+
msg.button_number = button_number;
67+
msg.type = BUTTON_PRESS_SHORT;
68+
69+
LOG_DBG("Button %d short press", button_number);
70+
71+
err = zbus_chan_pub(&BUTTON_CHAN, &msg, K_SECONDS(1));
72+
if (err) {
73+
LOG_ERR("zbus_chan_pub short press, error: %d", err);
74+
SEND_FATAL_ERROR();
75+
}
76+
}
77+
78+
/* Button handler called when a user pushes a button */
79+
static void button_handler(uint32_t button_states, uint32_t has_changed)
80+
{
81+
/* Handle button 1 press */
82+
if (!(has_changed & DK_BTN1_MSK)) {
83+
return;
84+
}
85+
86+
if (button_states & DK_BTN1_MSK) {
87+
button_state.pressed_buttons |= DK_BTN1_MSK;
88+
89+
/* Start long press timer */
90+
k_work_schedule(&button_state.long_press_work, K_MSEC(LONG_PRESS_TIMEOUT_MS));
91+
} else {
92+
button_state.pressed_buttons &= ~DK_BTN1_MSK;
93+
94+
/* Cancel long press timer if it was running and send short press */
95+
if (k_work_delayable_is_pending(&button_state.long_press_work)) {
96+
(void)k_work_cancel_delayable(&button_state.long_press_work);
97+
98+
/* Timer was running, this is a short press */
99+
publish_short_press(1);
42100
}
43101
}
44102
}
45103

46104
static int button_init(void)
47105
{
106+
int err;
107+
48108
LOG_DBG("button_init");
49109

50-
int err = dk_buttons_init(button_handler);
110+
/* Initialize button state */
111+
button_state.pressed_buttons = 0;
112+
113+
k_work_init_delayable(&button_state.long_press_work,
114+
long_press_work_handler);
51115

116+
err = dk_buttons_init(button_handler);
52117
if (err) {
53118
LOG_ERR("dk_buttons_init, error: %d", err);
54119
SEND_FATAL_ERROR();

app/src/modules/button/button.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,26 @@
1414
extern "C" {
1515
#endif
1616

17+
/** @brief Button message types */
18+
enum button_msg_type {
19+
/* Output message types */
20+
21+
/** Short button press detected */
22+
BUTTON_PRESS_SHORT = 0x1,
23+
24+
/** Long button press detected */
25+
BUTTON_PRESS_LONG,
26+
};
27+
28+
/** @brief Button message data structure */
29+
struct button_msg {
30+
enum button_msg_type type;
31+
uint8_t button_number;
32+
};
33+
34+
/** @brief Cast a pointer to a message to a button message */
35+
#define MSG_TO_BUTTON_MSG(_msg) (*(const struct button_msg *)_msg)
36+
1737
/* Channels provided by this module */
1838
ZBUS_CHAN_DECLARE(
1939
BUTTON_CHAN

app/src/modules/button/button_shell.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ static int cmd_button_press(const struct shell *sh, size_t argc, char **argv)
1717
{
1818
int err;
1919
uint8_t button_number;
20+
struct button_msg msg = {
21+
.type = BUTTON_PRESS_SHORT
22+
};
2023

2124
if (argc != 2) {
2225
(void)shell_print(sh, "Invalid number of arguments (%d)", argc);
@@ -31,9 +34,9 @@ static int cmd_button_press(const struct shell *sh, size_t argc, char **argv)
3134
return 1;
3235
}
3336

34-
LOG_DBG("Button %d pressed", button_number);
37+
msg.button_number = button_number;
3538

36-
err = zbus_chan_pub(&BUTTON_CHAN, &button_number, K_SECONDS(1));
39+
err = zbus_chan_pub(&BUTTON_CHAN, &msg, K_SECONDS(1));
3740
if (err) {
3841
(void)shell_print(sh, "zbus_chan_pub, error: %d", err);
3942
return 1;

tests/module/button/CMakeLists.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ cmake_minimum_required(VERSION 3.20.0)
99
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
1010
project(environmental_module_test)
1111

12-
test_runner_generate(src/main.c)
12+
test_runner_generate(src/button_module_test.c)
1313

1414
target_sources(app
1515
PRIVATE
16-
src/main.c
16+
src/button_module_test.c
1717
../../../app/src/modules/button/button.c
1818
)
1919

@@ -27,5 +27,6 @@ target_link_options(app PRIVATE --whole-archive)
2727
# Options that cannot be passed through Kconfig fragments
2828
target_compile_definitions(app PRIVATE
2929
-DCONFIG_APP_CLOUD_PAYLOAD_BUFFER_MAX_SIZE=100
30-
-DCONFIG_APP_BUTTON_LOG_LEVEL=4
30+
-DCONFIG_APP_BUTTON_LOG_LEVEL=0
31+
-DCONFIG_APP_BUTTON_LONG_PRESS_TIMEOUT_MS=3000
3132
)

tests/module/button/prj.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ CONFIG_HEAP_MEM_POOL_SIZE=40000
1919
CONFIG_SMF=y
2020
CONFIG_SMF_ANCESTOR_SUPPORT=y
2121
CONFIG_SMF_INITIAL_TRANSITION=y
22+
23+
CONFIG_NATIVE_SIM_SLOWDOWN_TO_REAL_TIME=n

0 commit comments

Comments
 (0)