User manual

LwBTN is simple button manager library, with great focus on embedded systems. Motivation behind start of development was linked to several on-going projects including some input reading (button handling), each of them demanding little differences in process.

LwBTN is therefore relatively simple and lightweight, yet it can provide pretty comprehensive processing of your application buttons.

How it works

User must define buttons array and pass it to the library. Next to that, 2 more functions are required:

  • Function to read the architecture button state

  • Function to receive various button events

User shall later periodically call processing function with current system time as simple parameter and get ready to receive various events.

A simple example for win32 is below:

Win32 example code
  1#include "lwbtn/lwbtn.h"
  2#include "windows.h"
  3#include <stdio.h>
  4#include <stdlib.h>
  5
  6static LARGE_INTEGER freq, sys_start_time;
  7static uint32_t get_tick(void);
  8
  9/* User defined settings */
 10const int keys[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
 11uint32_t last_time_keys[sizeof(keys) / sizeof(keys[0])] = {0};
 12
 13/* List of buttons to process with assigned custom arguments for callback functions */
 14static lwbtn_btn_t btns[] = {{.arg = (void*)&keys[0]}, {.arg = (void*)&keys[1]}, {.arg = (void*)&keys[2]},
 15                             {.arg = (void*)&keys[3]}, {.arg = (void*)&keys[4]}, {.arg = (void*)&keys[5]},
 16                             {.arg = (void*)&keys[6]}, {.arg = (void*)&keys[7]}, {.arg = (void*)&keys[8]},
 17                             {.arg = (void*)&keys[9]}};
 18
 19/**
 20 * \brief           Get input state callback 
 21 * \param           lw: LwBTN instance
 22 * \param           btn: Button instance
 23 * \return          `1` if button active, `0` otherwise
 24 */
 25uint8_t
 26prv_btn_get_state(struct lwbtn* lw, struct lwbtn_btn* btn) {
 27    (void)lw;
 28
 29    /*
 30     * Function will return negative number if button is pressed,
 31     * or zero if button is releases
 32     */
 33    return GetAsyncKeyState(*(int*)btn->arg) < 0;
 34}
 35
 36/**
 37 * \brief           Button event
 38 * 
 39 * \param           lw: LwBTN instance
 40 * \param           btn: Button instance
 41 * \param           evt: Button event
 42 */
 43void
 44prv_btn_event(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt) {
 45    const char* s;
 46    uint32_t color;
 47    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
 48    uint32_t* diff_time_ptr = &last_time_keys[(*(int*)btn->arg) - '0'];
 49    uint32_t diff_time = get_tick() - *diff_time_ptr;
 50
 51    /* This is for purpose of test and timing validation */
 52    if (diff_time > 2000) {
 53        diff_time = 0;
 54    }
 55    *diff_time_ptr = get_tick(); /* Set current date as last one */
 56
 57    /* Get event string */
 58    if (evt == LWBTN_EVT_KEEPALIVE) {
 59        s = "KEEPALIVE";
 60        color = FOREGROUND_RED;
 61    } else if (evt == LWBTN_EVT_ONPRESS) {
 62        s = "  ONPRESS";
 63        color = FOREGROUND_GREEN;
 64    } else if (evt == LWBTN_EVT_ONRELEASE) {
 65        s = "ONRELEASE";
 66        color = FOREGROUND_BLUE;
 67    } else if (evt == LWBTN_EVT_ONCLICK) {
 68        s = "  ONCLICK";
 69        color = FOREGROUND_RED | FOREGROUND_GREEN;
 70    } else {
 71        s = "  UNKNOWN";
 72        color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
 73    }
 74    SetConsoleTextAttribute(hConsole, color);
 75    printf("[%7u][%6u] CH: %c, evt: %s, keep-alive cnt: %3u, click cnt: %3u\r\n", (unsigned)get_tick(),
 76           (unsigned)diff_time, *(int*)btn->arg, s, (unsigned)btn->keepalive.cnt, (unsigned)btn->click.cnt);
 77    SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
 78    (void)lw;
 79}
 80
 81/**
 82 * \brief           Example function
 83 */
 84int
 85example_win32(void) {
 86    printf("Application running\r\n");
 87    QueryPerformanceFrequency(&freq);
 88    QueryPerformanceCounter(&sys_start_time);
 89
 90    /* Define buttons */
 91    lwbtn_init_ex(NULL, btns, sizeof(btns) / sizeof(btns[0]), prv_btn_get_state, prv_btn_event);
 92
 93    while (1) {
 94        /* Process forever */
 95        lwbtn_process_ex(NULL, get_tick());
 96
 97        /* Artificial sleep to offload win process */
 98        Sleep(5);
 99    }
100    return 0;
101}
102
103/**
104 * \brief           Get current tick in ms from start of program
105 * \return          uint32_t: Tick in ms
106 */
107static uint32_t
108get_tick(void) {
109    LONGLONG ret;
110    LARGE_INTEGER now;
111
112    QueryPerformanceFrequency(&freq);
113    QueryPerformanceCounter(&now);
114    ret = now.QuadPart - sys_start_time.QuadPart;
115    return (uint32_t)((ret * 1000) / freq.QuadPart);
116}

Input events

During button (or input if you will) lifetime, application can expect some of these events (but not limited to):

  • LWBTN_EVT_ONPRESS event is sent to application whenever input goes from inactive to active state and minimum debounce time passes by

  • LWBTN_EVT_ONRELEASE event is sent to application whenever input sent onpress event prior to that and when input goes from active to inactive state

  • LWBTN_EVT_KEEPALIVE event is periodically sent between onpress and onrelease events

  • LWBTN_EVT_ONCLICK event is sent after onrelease and only if active button state was within allowed window for valid click event.

On-Press event

Onpress event is the first in a row when input is detected active. With nature of embedded systems and various buttons connected to devices, it is necessary to filter out potential noise to ignore unintential multiple presses. This is done by checking line to be at stable level for at least some minimum time, normally called debounce time, usually it takes around 20ms.

On-Press event trigger after minimum debounce time

On-Press event trigger after minimum debounce time

On-Release event

Onrelease event is triggered immediately when input goes from active to inactive state, and only if onpress event has been detected prior to that.

On-Release event trigger

On-Release event trigger

On-Click event

Onclick event is triggered after a combination of multiple events:

  • Onpress event shall be detected properly, indicating button has been pressed

  • Onrelease event shall be detected, indicating button has been released

  • Time between onpress and onrelease events has to be within time window

When conditions are met, onclick event is sent, either immediately after onrelease or after certain timeout after onrelease event.

Sequence for valid click event

Sequence for valid click event

A windows-test program demonstration of events is visible below.

Click event test program

Click event test program

Second number for each line is a milliseconds difference between events. OnClick is reported approximately (windows real-time issue) 400 ms after on-release event.

Tip

Timeout window between last onrelease event and onclick event is configurable

Multi-click events

Multi-click feature is where timeout for onclick event comes into play. Idea behind timeout feature is to allow multiple presses and to only send onclick once for all presses, including the number of detected presses during that time. This let’s the application to react only once with known number of presses. This eliminates the problem where in case of double click trigger, you also receive single-click event, while you do not know yet, if second (or third) event will be triggered after.

Note

Imagine having a button that toggles one light on single click and turns off all lights in a room on double click. With timeout feature and single onclick notification, user will only receive the onclick once and will, based on the consecutive presses number value, perform appropriate action if it was single or multi click.

Simplified diagram for multi-click, ignoring debounce time indicators, is below. cp indicates number of detected consecutive onclick press events, to be reported in the final onclick event

Multi-click event example - with 3 consecutive presses

Multi-click event example - with 3 consecutive presses

A windows-test program demonstration of events is visible below.

Multi-click event test program

Multi-click event test program

Multi-click event with onclick event reported only after second press after minimum timeout of 400ms.

Note

Number of consecutive clicks can be upper-limited to the desired value.

When user makes more (or equal) consecutive clicks than maximum, an onclick event is sent immediately after onrelease event for last detected click.

Max number of onclick events

Max number of onclick events, onclick is sent immediately after onrelease

There is no need to wait timeout expiration since upper clicks limit has been reached.

Tip

It is possible to control the behavior of onclick event (when consecutive number reaches maximum set value) timing using LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY configuration. When enabled, behavior is as illustrated above. When disabled, onclick event it sent in timeout (or in case of new onpress), even if max allowed clicks has been reached.

Illustration below shows what happens during multiple clicks

  • Max number of consecutive clicks is 3

  • User makes 4 consecutive clicks

Multi-click events with too many clicks - consecutive send immediately is enabled

Multi-click events with too many clicks - consecutive send immediately is enabled - it is sent after 3rd onrelease

Multi-click events with too many clicks - consecutive send immediately is disabled - it is sent before 4th onpress

Multi-click events with too many clicks - consecutive send immediately is disabled

Image below illustrates when send immediately is enabled. It is visible how first onclick is sent just after onrelease event (when max consecutive is set to 3).

5 presses detected with 3 set as maximum

5 presses detected with 3 set as maximum. First on-click is sent immediately, while second is sent after timeout

When multi-click feature is disabled, onclick event is sent after every valid sequence of onpress and onrelease events.

Tip

If you do not want multi-click feature, set max number of consecutive clicks to 1. This will eliminate timeout feature since every click event will trigger maximum clicks detected and therefore send the event immediately after onrelease

Multi-click events disabled with cp == 1

Multi-click events disabled with cp == 1

Demo log text, with fast pressing of button, and events reported after every onrelease

Multi-click events disabled with cp == 1

Multi-click events disabled with cp == 1

Keep alive event

Keep-alive event is sent periodically between onpress and onrelease events. It can be used to detect application is still alive and provides counter how many keep-alive events have been sent up to the point of event.

Feature can be used to make a trigger at specific time if button is in active state (a hold event).

Keep alive events with 2 successful click events

Keep alive events with 2 successful click events

Keep alive events when button is kept pressed

Keep alive events when button is kept pressed