Skip to content
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

drivers/touch_dev_gestures: add gesture recognition for touch devices #19884

Merged
merged 4 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ rsource "ili9341/Kconfig"
rsource "lcd/Kconfig"
rsource "st7735/Kconfig"
rsource "touch_dev/Kconfig"
rsource "touch_dev_gestures/Kconfig"
endmenu # Display Device Drivers

menu "Miscellaneous Device Drivers"
Expand Down
30 changes: 30 additions & 0 deletions drivers/include/touch_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ extern "C" {
#include <stdint.h>
#include <stdbool.h>

/**
* @brief Invalid touch value
*/
#define TOUCH_DEV_VALUE_INVALID ((touch_t){ UINT16_MAX, UINT16_MAX })

/**
* @brief Forward declaration for touch device struct
*/
Expand Down Expand Up @@ -71,6 +76,18 @@ typedef struct {
*/
uint16_t (*width)(const touch_dev_t *dev);

/**
* @brief Get the maximum number of touches the touch device supports
*
* This function pointer can be NULL. In this case, the maximum number of
* touches is assumed to be 1.
*
* @param[in] dev Pointer to the touch device
*
* @return number of touches
*/
uint8_t (*max_numof)(const touch_dev_t *dev);

/**
* @brief Get the current touches on the touch device
*
Expand Down Expand Up @@ -152,6 +169,19 @@ uint16_t touch_dev_height(const touch_dev_t *dev);
*/
uint16_t touch_dev_width(const touch_dev_t *dev);

/**
* @brief Get the maximum number of touches the touch device supports
*
* @param[in] dev Pointer to the touch device
*
* @return number of touches
*/
static inline uint8_t touch_dev_max_numof(const touch_dev_t *dev)
{
assert(dev);
return (dev->driver->max_numof) ? dev->driver->max_numof(dev) : 1;
}

/**
* @brief Get the current touches on the touch device
*
Expand Down
249 changes: 249 additions & 0 deletions drivers/include/touch_dev_gestures.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
/*
* Copyright (C) 2023 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @defgroup drivers_touch_dev_gestures Touch device gesture recognition
* @ingroup drivers_misc
*
* @brief Gesture recognition for touch devices
* @{
*
* This driver implements a simple gesture recognition with a maximum of two
* touches for touch devices that use the generic touch device API.
*
* The application that receives the events from the touch device via the
* callback function registered with @ref touch_dev_set_touch_event_callback
* must first create and initialize a touch device gesture context of type
* @ref touch_dev_gesture_ctx_t. For each touch event received from the
* touch device, it then calls @ref touch_dev_recognize_gesture function
* with this context so that the gesture recognition fetches the data from
* the touch device to detect the gestures, for example:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
* static void _touch_event_cb(void *arg)
* {
* // indicate that a touch event occurred
* mutex_unlock(arg);
* }
*
* void *_input_task(void *arg)
* {
* ...
* mutex_t lock = MUTEX_INIT_LOCKED;
*
* touch_dev_t *dev = touch_dev_reg_find_screen(0).dev;
* touch_dev_gesture_ctx_t ctx;
*
* // set the event callback function and initialize the touch device gesture context
* touch_dev_set_touch_event_callback(dev, _touch_event_cb, &lock);
* touch_dev_init_gesture(dev, &ctx);
*
* while (1) {
* // wait for the indication of a touch event
* mutex_lock(&lock);
*
* // call the gesture recognition
* touch_t pos;
* touch_dev_gesture_t gesture = touch_dev_recognize_gesture(&ctx, &pos);
*
* // process recognized gestures
* switch (gesture) {
* case TOUCH_DEV_GEST_SINGLE_TAP:
* if ((pos.x == 50) && (pos.y == 75)) {
* ...
* case TOUCH_DEV_GEST_DOUBLE_TAP:
* ...
* ...
* }
* }
* return NULL;
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @note To use this event-driven approach the driver for the touch device
* has to report the following touch events by interrupt:
* - a new touch is detected,
* - a touch is released, and
* - regularly the current touch positions as long as there are touches.
*
* If the event-driven approach cannot be used because either the touch
* device does not support all these touch events or the application wants
* to use the touch device in polling mode, the application must call the
* @ref touch_dev_recognize_gesture function with the gesture context
* of the touch device at regular intervals, for example:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
* #ifndef TOUCH_DEV_POLLING_PERIOD
* #define TOUCH_DEV_POLLING_PERIOD 50
* #endif
* ...
*
* void *_input_task(void *arg)
* {
* ...
*
* touch_dev_t *dev = touch_dev_reg_find_screen(0).dev;
* touch_dev_gesture_ctx_t ctx;
*
* // initialize the touch device gesture context
* touch_dev_init_gesture(dev, &ctx);
*
* while (1) {
* // call the gesture recognition
* touch_t pos;
* touch_dev_gesture_t gesture = touch_dev_recognize_gesture(&ctx, &pos);
*
* // process recognized gestures
* switch (gesture) {
* case TOUCH_DEV_GEST_SINGLE_TAP:
* if ((pos.x == 50) && (pos.y == 75)) {
* ...
* case TOUCH_DEV_GEST_DOUBLE_TAP:
* ...
* ...
* }
*
* // wait the period time for polling
* ztimer_sleep(ZTIMER_MSEC, TOUCH_DEV_POLLING_PERIOD);
* }
* return NULL;
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* The following gestures are supported by the driver:
*
* - @ref TOUCH_DEV_GEST_SINGLE_TAP : a single tap with one touch at the given position
* - @ref TOUCH_DEV_GEST_DOUBLE_TAP : a double tap with one touch at the given position
* - @ref TOUCH_DEV_GEST_PRESSED : a long press with one touch at the given position
* - @ref TOUCH_DEV_GEST_RELEASED : one touch released after a long press at given position
* - @ref TOUCH_DEV_GEST_MOVE : moving while pressed with current position
* - @ref TOUCH_DEV_GEST_SWIPE_LEFT : swiping left with one touch
* - @ref TOUCH_DEV_GEST_SWIPE_RIGHT : swiping right with one touch
* - @ref TOUCH_DEV_GEST_SWIPE_UP : swiping up with one touch
* - @ref TOUCH_DEV_GEST_SWIPE_DOWN : swiping down with one touch
* - @ref TOUCH_DEV_GEST_ZOOM_IN : zooming in (spreading) with two touches
* - @ref TOUCH_DEV_GEST_ZOOM_OUT : zooming out (pinching) with two touches
*
* @note
* - For technical reasons, a double-tap event is always preceded by a
* single-tap event, i.e. for a double-tap, the application always
* receives a single-tap event first and then a double-tap event if
* the second tap follows.
* - Zooming gestures are only available if the touch device supports two
* touches.
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/

#ifndef TOUCH_DEV_GESTURES_H
#define TOUCH_DEV_GESTURES_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

#include "touch_dev.h"

/**
* @brief Maximum number of touches supported by gesture recognition
*/
#define TOUCH_DEV_TOUCHES_MAX_NUMOF 2

/**
* @brief Minimum distance in one direction to recognize a swipe gesture
*/
#ifndef CONFIG_TOUCH_DEV_SWIPE_TRESH
#define CONFIG_TOUCH_DEV_SWIPE_TRESH 5
#endif

/**
* @brief Minimum touch time in milliseconds to detect a long press gesture
*/
#ifndef CONFIG_TOUCH_DEV_PRESS_TIME_MS
#define CONFIG_TOUCH_DEV_PRESS_TIME_MS 600
#endif

/**
* @brief Maximum time in milliseconds between two taps to detect a double tap
*/
#ifndef CONFIG_TOUCH_DEV_DOUBLE_TIME_MS
#define CONFIG_TOUCH_DEV_DOUBLE_TIME_MS 400
#endif

/**
* @brief Touch device states used for gesture recognition
*/
typedef enum {
TOUCH_DEV_STATE_RELEASED, /**< no touches detected, default state */
TOUCH_DEV_STATE_TAPPED_SINGLE, /**< a single touch is detected */
TOUCH_DEV_STATE_TAPPED_MULTIPLE, /**< a second touch is detected */
TOUCH_DEV_STATE_PRESSED, /**< a long press is detected */
TOUCH_DEV_STATE_WAIT_FOR_RELEASE, /**< gesture detected, waiting for releasing touches */
} touch_dev_state_t;

/**
* @brief Touch gesture events
*/
typedef enum {
TOUCH_DEV_GEST_NONE, /**< No gesture recognized */
TOUCH_DEV_GEST_SINGLE_TAP, /**< Single tap recognized at the given position */
TOUCH_DEV_GEST_DOUBLE_TAP, /**< Double tap recognized at the given position */
TOUCH_DEV_GEST_PRESSED, /**< Long press recognized at the given position */
TOUCH_DEV_GEST_RELEASED, /**< Release after a long press at given position */
TOUCH_DEV_GEST_MOVE, /**< Moving while pressed recognized, current position is given */
TOUCH_DEV_GEST_SWIPE_LEFT, /**< Swipe left recognized, no position is given */
TOUCH_DEV_GEST_SWIPE_RIGHT, /**< Swipe right recognized, no position is given */
TOUCH_DEV_GEST_SWIPE_UP, /**< Swipe up recognized, no position is given */
TOUCH_DEV_GEST_SWIPE_DOWN, /**< Swipe down recognized, no position is given */
TOUCH_DEV_GEST_ZOOM_IN, /**< Zoom in (spread) recognized, no position is given */
TOUCH_DEV_GEST_ZOOM_OUT, /**< Zoom out (pinch) recognized, no position is given */
} touch_dev_gesture_t;

/**
* @brief Context information for a touch device needed for gesture recognition
*/
typedef struct {
touch_dev_t *dev; /**< Pointer to the touch device */
uint32_t t_changed; /**< Time of last state change in ms */
uint32_t t_prev_tap; /**< Time of previous tap */
touch_t prev[TOUCH_DEV_TOUCHES_MAX_NUMOF]; /**< Previous set of touches */
uint8_t prev_num; /**< Previous number of touches */
touch_dev_state_t state; /**< State of touch device */
} touch_dev_gesture_ctx_t;

/**
* @brief Initialize gesture recognition
*
* @param[in] dev Pointer to the touch device
* @param[in] ctx Pointer to the context information for the touch device
*/
void touch_dev_init_gesture(touch_dev_t *dev,
touch_dev_gesture_ctx_t *ctx);

/**
* @brief Recognize gestures by handling next touch device event
*
* @param[in] ctx Pointer to the context information for the touch device
* @param[out] pos Position of the gesture if interested in it (can be NULL)
*
* return the gesture of type @ref touch_dev_gesture_t if one was
* recognized or @ref TOUCH_DEV_GEST_NONE
*/
touch_dev_gesture_t touch_dev_recognize_gesture(touch_dev_gesture_ctx_t *ctx,
touch_t *pos);

#ifdef __cplusplus
}
#endif

#endif /* TOUCH_DEV_GESTURES_H */
/** @} */
40 changes: 40 additions & 0 deletions drivers/touch_dev_gestures/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright (c) 2023 Gunar Schorcht
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#

menuconfig MODULE_TOUCH_DEV_GESTURES
bool "Touch device gesture recognition"
depends on TEST_KCONFIG
select MODULE_TOUCH_DEV
select MODULE_ZTIMER_MSEC
help
Gesture recognition for touch devices that are accessed using the
generic touch device API.

if MODULE_TOUCH_DEV_GESTURES

config TOUCH_DEV_SWIPE_TRESH
int "Swipe threshold"
range 0 50
default 5
help
Minimum distance in one direction to recognize a swipe gesture.

config TOUCH_DEV_PRESS_TIME_MS
int "Press time in milliseconds"
range 0 2000
default 600
help
Minimum touch time in milliseconds to detect a long press gesture.

config TOUCH_DEV_DOUBLE_TIME_MS
int "Double tap maximum delay in milliseconds"
range 0 1000
default 400
help
Maximum time in milliseconds between two taps to detect a double tap.

endif
1 change: 1 addition & 0 deletions drivers/touch_dev_gestures/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base
2 changes: 2 additions & 0 deletions drivers/touch_dev_gestures/Makefile.dep
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
USEMODULE += touch_dev
USEMODULE += ztimer_msec
Loading