Example code: Improved button handling
응답
15. 9. 8 오전 7:25
Hi everyone,

having spent some time on button handling, I'd like to share that with you (for overall increased Bosch efficiency of course ;).

Task: Switch configuration of a virtual device (sensor data sent from XDK instead of real device) directly at XDK, feedback via LEDs.

I wanted to distinguish between single button "click" (pressed <~1s) and "hold" (pressed >~1s) as well as react to both buttons pressed (as well "click" and "hold").

Approach: Have a dedicated Button-Flag and a timer to measure button down time. Probably not the smoothest approach, but it does work for me. Any improvement suggestions are of course very welcome =)

Code: Further variables and functions:
static uint8_t HMI_ButtonHeld = 0;
static uint8_t HMI_ButtonStatus = 0; // 1 = Button 1, 2 = Button 2, 3 = both buttons...
static OS_timerHandle_tp ADT_ButtonPressedTimerHandle; // variable to store timer handle for Pressed Button Timer
New functions (must be declared also in ADT_bleAccelData_ch.h):
/** The function to set the status of all three LEDs at once
 * @brief Sets all three XDK LEDS at once
 * @param[in] LEDbits : binary presentation of 0b00000|red|orange|yellow LED status (1: ON, 0: OFF)
 */
static void adtSetLED(uint8_t LEDbits)
{
    uint8_t returnValue = HMI_ZERO_VALUE;
    returnValue += LED_setState(HMI_redLedHandle, LEDbits & 4 ? LED_SET_ON : LED_SET_OFF);
    returnValue += LED_setState(HMI_orangeLedHandle, LEDbits & 2 ? LED_SET_ON : LED_SET_OFF);
    returnValue += LED_setState(HMI_yellowLedHandle, LEDbits & 1 ? LED_SET_ON : LED_SET_OFF);
    if (returnValue != HMI_ZERO_VALUE)
    {
        printf("LEDs set fail: %d\n\r", (short int) returnValue);
    }
}

/** The function to switch a bool if button was held longer than 1200ms
 * @brief Sets static global variable about Button Hold
 * @param[in]      pvParameters RTOS task should be defined with the type void *(as argument)
 */
static void adtButtonHold(void *pvParameters)
{
    UNUSED_PARAMETER(pvParameters);

    // Hold timer finished --> set bool and switch off LEDs for visualization
    HMI_ButtonHeld = 1;
    adtSetLED(0); OS_taskDelay(100);
}
And last not least: The button handling (must also be declared):
Notes:
  • The button press event should not be used for user functions, only the release event
  • The BLE timer send event is blocked while a button is pressed
  • The first button down starts the button-pressed-timer, which toggles a bool after the specified time to distinguish button click or button hold
  • If the dual button event was handled, a fourth bit-flag is set.
  • Dual button event must be managed at two places, for PB1 and PB2 - at least for improved user experience ;)
  • The button statuses are printf'ed if the XDK is connected to Workbench
  • The variables below "Alert_Status", "Example_Status" and "XDK_Status" are undeclared in the code shared here. They are included at the respective lines only to provide the exemplary LED code. In my code:
    • XDK_Status (0-7) is shown as respective bits with all three LEDs by default and decreased by a PB1 click
    • Alert_Status is changed and shown by blinking LEDs after PB1 hold.
    • Example_Status is toggled and shown by filling or emptying LED strip twice after PB2 hold
    • Dual button click resets the configuration
/** The function to handle the status of Button I and II
 * @brief Handle button statuses
 * @param[in] pvParameters Rtos task should be defined with the type void *(as argument)
 */
static void adtHandleButtonStatus(void *handle, uint32_t userParameter)
{
    int8_t appTimerInitReturn;

    switch (userParameter)
    {
    case HMI_CALLBACK_PARAMETER_PB1: // Button 1
        if (BUTTON_isPressed(handle))
        {
            // Process dual button event. If event processed, mark via flag in HMI_ButtonStatus
            HMI_ButtonStatus |= 1; // Set Button 1 status pressed
            if (HMI_ButtonStatus >= 3)
            {
                if (HMI_ButtonStatus == 3) { printf("Both buttons pressed @PB1 last\n\r"); }
                break; // Timers already processed, skip the rest
            }

            // Stop BLE Send Timer during button press
            appTimerInitReturn = OS_timerStop(ADT_bleTransmitTimerHandle, 10);
            if (appTimerInitReturn != OS_ERR_NO_ERROR)
            {
                printf("BLE Timer Stop FAIL\n\r");
            }

            // Start Timer for Button Hold
            HMI_ButtonHeld = 0;
            appTimerInitReturn = OS_timerStart(ADT_ButtonPressedTimerHandle, 10);
            if (appTimerInitReturn != OS_ERR_NO_ERROR)
            {
                printf("ButtonPress1 Timer start FAIL\n\r");
            }
            printf("PB1 pressed.\n\r");
        }

        if (BUTTON_isReleased(handle)) // Button 1
        {
            // Stop Button Hold Timer
            appTimerInitReturn = OS_timerStop(ADT_ButtonPressedTimerHandle, 10);
            if (appTimerInitReturn != OS_ERR_NO_ERROR)
            {
                printf("ButtonPress Timer stop FAIL\n\r");
            }

            // Process dual button event
            if (HMI_ButtonStatus == 3)
            {
                HMI_ButtonStatus |= 4; // Set both button pressed flag to filter the respective release events
                if (HMI_ButtonHeld == 0) // Both button pressed: <YOUR ACTION BELOW>
                {
                    // <YOUR CLICK ACTION HERE>
                    printf("Both button click @PB1 release\n\r");
                }
                else
                {
                    // <YOUR HOLD ACTION HERE>
                    printf("Both button held @PB1 release\n\r");
                }
            }

            HMI_ButtonStatus &= 254; // Set Button 1 status to Released
            if (HMI_ButtonStatus >= 4) // If both buttons event already processed, skip the rest
            {
                if (HMI_ButtonStatus == 4) { HMI_ButtonStatus &= 251; } // Release dual button flag
            }
            else
            {
                // Button 1 click / hold: <YOUR ACTION BELOW>
                if (HMI_ButtonHeld == 0)
                {
                     <YOUR CLICK ACTION HERE>
                    printf("PB1 click. \n\r");
                }
                else
                {
                     <YOUR HOLD ACTION HERE, EXEMPLARY LED BLINKING BELOW>
                    adtSetLED(Alert_Status == 0 ? 7 : Alert_Status); OS_taskDelay(100);
                    adtSetLED(0); OS_taskDelay(100);
                    adtSetLED(Alert_Status == 0 ? 7 : Alert_Status); OS_taskDelay(100);
                    adtSetLED(0); OS_taskDelay(100);
                    adtSetLED(Alert_Status == 0 ? 7 : Alert_Status); OS_taskDelay(100);
                    adtSetLED(0); OS_taskDelay(100);
                    printf("PB1 hold. \n\r");
                }
            }

            // ReStart BLE Send Timer after button press
            if (HMI_ButtonStatus == 0)
            {
                appTimerInitReturn = OS_timerStart(ADT_bleTransmitTimerHandle, 10);
                if (appTimerInitReturn != OS_ERR_NO_ERROR)
                {
                    printf("BLE Timer Start FAIL\n\r");
                }
            }
        }
        break;

    case HMI_CALLBACK_PARAMETER_PB2:
        if (BUTTON_isPressed(handle))
        {
            // Process dual button event
            HMI_ButtonStatus |= 2; // Set Button 2 status to Pressed
            if (HMI_ButtonStatus >= 3)
            {
                if (HMI_ButtonStatus == 3) { printf("Both buttons pressed @PB2 last\n\r"); }
                break; // Timers already processed, skip the rest
            }

            // Stop BLE Send Timer during button press
            appTimerInitReturn = OS_timerStop(ADT_bleTransmitTimerHandle, 10);
            if (appTimerInitReturn != OS_ERR_NO_ERROR)
            {
                printf("BLE Timer Stop FAIL\n\r");
            }

            // Start Button Hold Timer
            HMI_ButtonHeld = 0;
            appTimerInitReturn = OS_timerStart(ADT_ButtonPressedTimerHandle, 10);
            if (appTimerInitReturn != OS_ERR_NO_ERROR)
            {
                printf("ButtonPress2 Timer start FAIL\n\r");
            }
            printf("PB2 pressed.\n\r");
        }

        if (BUTTON_isReleased(handle)) // Button 2
        {
            // Stop Button Hold Timer
            appTimerInitReturn = OS_timerStop(ADT_ButtonPressedTimerHandle, 10);
            if (appTimerInitReturn != OS_ERR_NO_ERROR)
            {
                printf("ButtonPress Timer stop FAIL\n\r");
            }

            // Process dual button event
            if (HMI_ButtonStatus == 3)
            {
                HMI_ButtonStatus |= 4; // Set both button pressed flag to filter the respective release events
                if (HMI_ButtonHeld == 0) // Both button pressed: <YOUR ACTION BELOW>
                {
                    <YOUR CLICK ACTION HERE>
                    printf("Both button click @PB2 release\n\r");
                }
                else
                {
                    <YOUR HOLD ACTION HERE>
                    printf("Both button held @PB2 release \n\r");
                }
            }

            HMI_ButtonStatus &= 253; // Set Button 2 status to Released
            if (HMI_ButtonStatus >= 4) // If both buttons event already processed, skip the rest
            {
                if (HMI_ButtonStatus == 4) { HMI_ButtonStatus &= 251; } // Release dual button flag
            }
            else
            {
                // Button 2 click / hold : <YOUR ACTION BELOW>
                if (HMI_ButtonHeld == 0)
                {
                     <YOUR ACTION HERE, EXEMPLARY PACKET SENDING BELOW>
                     adtBleAccelDataTransmit((uint32_t) 0);
                    printf("PB2 click. Packet sent\n\r");
                }
                else
                {
                    <YOUR ACTION HERE, EXEMPLARY LED ACTION BELOW>
                    Example_Status = Example_Status == 0 ? 1 : 0;
                    adtSetLED(Example_Status == 0 ? 7 : 0); OS_taskDelay(100);
                    adtSetLED(Example_Status == 0 ? 3 : 1); OS_taskDelay(100);
                    adtSetLED(Example_Status == 0 ? 1 : 3); OS_taskDelay(100);
                    adtSetLED(Example_Status == 0 ? 0 : 7); OS_taskDelay(100);
                    adtSetLED(Example_Status == 0 ? 7 : 0); OS_taskDelay(100);
                    adtSetLED(Example_Status == 0 ? 3 : 1); OS_taskDelay(100);
                    adtSetLED(Example_Status == 0 ? 1 : 3); OS_taskDelay(100);
                    adtSetLED(Example_Status == 0 ? 0 : 7); OS_taskDelay(100);
                    printf("PB2 hold. Example: %d\n\r", (short int) Example_Status);
                }
            }

            // ReStart BLE Send Timer after button press
            if (HMI_ButtonStatus == 0)
            {
                appTimerInitReturn = OS_timerStart(ADT_bleTransmitTimerHandle, 10);
                if (appTimerInitReturn != OS_ERR_NO_ERROR)
                {
                    printf("BLE Timer Start FAIL\n\r");
                }
            }
        }
        break;

    default:
        printf("Button not available \n\r");
        break;
    }

    <YOUR CONCLUDING CODE HERE, EXAMPLE LED SETTING BELOW>
    adtSetLED(XDK_Status);

}
And last not least, the PressedButtonTimer has to be set up towards the end in ADT_init, right after creation of BLE timer "bleAccelDataTransmit":
        ADT_ButtonPressedTimerHandle = OS_timerCreate((const int8_t *) "ButtonPressed", 1200, OS_AUTORELOAD_OFF, NULL, adtButtonHold);
        if (ADT_bleTransmitTimerHandle != NULL)
        {
            printf("ButtonPressed Timer created SUCCESS\n\r");
        }
        else
        {
            printf("ButtonPressed Timer creation FAILED\n\r");
        }

Hope this helps!
 
+1 (1 투표)
Example code: Improved button handling
응답
15. 9. 10 오전 8:20 as a reply to T. Pirk.
Thank you very much for sharing this! I am sure this will be helpful for some people.
Sharing ideas and achievements is what this Community is all about. We would love to see some more code snippets from you in the future :)
0 (0 투표)