diff --git a/applications/gesture_recognition/.clang-format b/applications/gesture_recognition/.clang-format
new file mode 100644
index 0000000..6c1aca7
--- /dev/null
+++ b/applications/gesture_recognition/.clang-format
@@ -0,0 +1,24 @@
+Standard: Cpp11
+BasedOnStyle: LLVM
+IndentWidth: 4
+ColumnLimit: 0
+AccessModifierOffset: -4
+NamespaceIndentation: All
+BreakBeforeBraces: Custom
+PointerAlignment: Left
+AllowShortCaseLabelsOnASingleLine: 'true'
+IndentCaseLabels: 'true'
+BraceWrapping:
+ AfterEnum: true
+ AfterStruct: true
+ AfterClass: true
+ SplitEmptyFunction: true
+ AfterControlStatement: true
+ AfterNamespace: false
+ AfterFunction: true
+ AfterUnion: true
+ AfterExternBlock: false
+ BeforeCatch: false
+ BeforeElse: false
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
diff --git a/applications/gesture_recognition/CMakeLists.txt b/applications/gesture_recognition/CMakeLists.txt
new file mode 100644
index 0000000..2b30cdc
--- /dev/null
+++ b/applications/gesture_recognition/CMakeLists.txt
@@ -0,0 +1,22 @@
+#
+# Copyright (c) 2024 Nordic Semiconductor
+#
+# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
+#
+
+cmake_minimum_required(VERSION 3.20.0)
+
+find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
+project(nrf_edgeai_thingy53)
+
+set(CMAKE_BUILD_TYPE Debug)
+
+file(GLOB_RECURSE APP_SOURCE_FILES
+ "${CMAKE_CURRENT_LIST_DIR}/src/**")
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/bsp)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/nrf_edgeai_lib)
+
+target_sources(app PRIVATE ${APP_SOURCE_FILES})
+
+zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)
diff --git a/applications/gesture_recognition/Kconfig b/applications/gesture_recognition/Kconfig
new file mode 100644
index 0000000..a6ca4b6
--- /dev/null
+++ b/applications/gesture_recognition/Kconfig
@@ -0,0 +1,11 @@
+#
+# Copyright (c) 2024 Nordic Semiconductor
+#
+# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
+#
+
+source "Kconfig.zephyr"
+
+config DATA_COLLECTION_MODE
+ bool "Enble Data Collection Mode (no inference run)"
+ default n
diff --git a/applications/gesture_recognition/Kconfig.sysbuild b/applications/gesture_recognition/Kconfig.sysbuild
new file mode 100644
index 0000000..58cf626
--- /dev/null
+++ b/applications/gesture_recognition/Kconfig.sysbuild
@@ -0,0 +1,13 @@
+#
+# Copyright (c) 2023 Nordic Semiconductor
+#
+# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
+#
+
+source "share/sysbuild/Kconfig"
+
+config NRF_DEFAULT_IPC_RADIO
+ default y
+
+config NETCORE_IPC_RADIO_BT_HCI_IPC
+ default y
diff --git a/applications/gesture_recognition/README.md b/applications/gesture_recognition/README.md
new file mode 100644
index 0000000..dff2dde
--- /dev/null
+++ b/applications/gesture_recognition/README.md
@@ -0,0 +1,199 @@
+# nRF Edge AI Nordic Thingy:53 Gesture Based BLE Remote Control Device
+
+- [Overview](#overview)
+- [Hardware Used](#hw-used)
+- [Setup Software Environment](#setup-sw-env)
+- [Setup Firmware Project](#setup-fw-proj)
+- [How The Project Works](#how-works)
+
+## Overview
+
+This project demonstrates a gesture based remote control device using [__Nordic Thingy:53__](https://www.nordicsemi.com/Products/Development-hardware/Nordic-Thingy-53). The development kit could be connected to the PC via Bluetooth as a HID device and using gestures the user can control media stream or slides of the presentation. Based on accelerometer and gyroscope data the nRF Edge AI model could recognize __8 classes__ of gestures: Swipe Right, Swipe Left, Double Shake, Double Tap, Rotation Clockwise and Counter clockwise, No Gestures(IDLE) and Unknown Gesture. Use-case demonstration [video](https://youtu.be/qDFdxapLbrA). Raw dataset used for model training, which you can use to train your own model, or augment it with your own data and train a more robust model is located [here](https://files.nordicsemi.com/artifactory/edge-ai/external/nordic53thingy_remote_ctrl_train_v101.csv).
+
+## Hardware Used
+
+[__Nordic Thingy:53 Multi-protocol IoT prototyping platform__](https://www.nordicsemi.com/Products/Development-hardware/Nordic-Thingy-53)
+
+The Nordic Thingy:53™ is an easy-to-use IoT prototyping platform. It makes it possible to create prototypes and proofs-of-concept without building custom hardware. The Thingy:53 is built around the nRF5340 SoC, the flagship dual-core wireless SoC. The processing power and memory size of its dual Arm Cortex-M33 processors enables it to run embedded machine learning (ML) models directly on the device.
+
+The Thingy:53 also includes many different integrated sensors, like environmental-, and color and light sensors, accelerometers, and a magnetometer, which all can be taken advantage of without additional hardware. It is powered by a rechargeable Li-Po battery that can be charged via USB-C. There is also an external 4-pin JST connector compatible with the Stemma/Qwiic/Grove standards for hardware accessories.
+
+
+
+## Setup Software Environment
+
+To set this project up, you will need to install the following software:
+- Visual Studio Code (https://code.visualstudio.com)
+- nRF Connect for VS Code (https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-VS-Code)
+- [**Optional**] TeraTerm Terminal (https://teratermproject.github.io/index-en.html)
+
+## Setup Firmware Project
+
+1. Clone this repository: https://github.com/Neuton-tinyML/neuton-nordic-thingy53-ble-remotecontrol
+2. In the VS code, `Open folder` where you did clone the repository.
+3. `Manage toolchain` install toolchain version **v3.1.0**. `Manage SDK` install version **v3.1.0** in nRF Connect tab.
+
+
+
+4. `Add Build configuration` for Nordic Thingy 53 dev kit
+
+
+
+5. Choose `thingy53_nrf5340_cpuapp` in the `Board` selector and click `Build Configuration`
+
+
+
+6. After your build will be configured you should see the following options:
+
+
+
+**IMPORTANT** If your `thingy53_nrf5340_cpuapp_defconfig` file does not has **`CONFIG_FPU=y`** you should add this because nRF Edge AI library is compiled with `-mfloat-abi=hard` flag
+
+
+
+8. Now turn on your Thingy 53 dev kit and connect to your PC via debugger and USB
+
+
+
+9. Build & Flash the device with firmware
+
+
+
+10. After successfull device programming you can open any serial port terminal and you should see the following messages:
+
+````
+*** Booting nRF Connect SDK 7a22da43c1d4 ***
+Set up button at gpio@842800 pin 14
+nRF Edge AI Gestures Recognition Demo:
+ Application version: 3.0.0
+ nRF Edge AI Runtime Version: 1.0.0
+ nRF Edge AI Lab Solution id: 84622
+Bluetooth initialized
+Advertising successfully started
+````
+
+MacOs CLI commands examples:
+- Check connected usb devices: `ls -l /dev/cu.usb*`
+- Output to serial port. Use your actual usb-device name: `stty -f /dev/cu.usbmodem101 115200 | cat /dev/cu.usbmodem101`
+- Save serial port output to file, if necessary: `stty -f /dev/cu.usbmodem101 115200 | cat /dev/cu.usbmodem101 | tee filename.csv`
+
+
+11. Explore the project and nRF Edge AI model capabilities!
+
+### Data collection firmware build
+
+It is possible to create a special build that will output the raw data of the acceleromater and gyro sensors in the serial port.
+This way it's possible to capture data for training new models, to test and implement new use cases.
+The output of the sensors will integers of 16 bits, separated by a comma, in the following order
+
+```
+,,,,,
+```
+*(Please note column headers are not included)*
+
+The output rate will be the configured sampling frequency, default value being 100Hz.
+
+To build this version, the following option must be enabled in the `prj.conf` file
+
+```
+CONFIG_DATA_COLLECTION_MODE=y
+```
+
+The project must be build and flashed again as described in the step **(9)**.
+
+No inference will be performed in this mode, it's just intended to simplify the capture of new datasets
+
+# How the project works
+
+Once the device is up and running, Bluetooth advertising starts as a HID device and waits for connection request from the PC.
+
+You can connect devices in the same way as, for example, a regular Bluetooth keyboard.
+
+1. For Windows 10 PC you can go to `Settings -> Bluetooth & other devices -> Add Bluetooth or other device`.
+
+
+
+2. The device should appear in `Add a device` window, choose the device for pairing.
+
+
+
+3. After Bluetooth pairing the device should appear in your `Mouse, keyboard, & pen` section
+
+
+
+4. In the serial port terminal you should se the following logs messages:
+
+```
+Connected 9C:B6:D0:C0:CE:FC (public)
+Security changed: 9C:B6:D0:C0:CE:FC (public) level 2
+Input CCCD enabled
+Input attribute handle: 0
+Consumer CCCD enabled
+```
+
+After Bluetooth connection the device will change LED indication from RED LED glowing to GREEN or BLUE LEDs glowing depending on __Keyboard control mode__.
+
+The project has two Keyboard control modes: __Presentation Control__ and __Music Control__. You can switch between control modes by pushing user button `BTN0`, for different control modes there is a different LED indication. If the device in __Presentation Control__ mode the LED glows BLUE color, if the device in __Music Control__ mode the LED glows GREEN color:
+
+__LED indication in different device states:__
+
+| No Bluetooth connection | Presentation Control mode | Music Control mode |
+| ------------------------ |---------------------------- | ----------------------- |
+|  | | |
+
+
+Depending on the control mode, recognized gestures will be mapped to different keyboard keys:
+
+__Gestures to Keyboard Keys Mapping__
+
+| | Presentation Control | Music Control |
+| ----------------------------- | ---------- | ----------------- |
+| Double Shake | F5 | Media Play/Pause |
+| Double Tap | ESCAPE | Media Mute |
+| Swipe Right | Arrow Right| Media Next |
+| Swipe Left | Arrow Left | Media Previous |
+| Rotation Clockwise | Not used | Media Volume Up |
+| Rotation Counter clockwise | Not used | Media Volume Down |
+
+__How to Make Gestures__
+> **_NOTE:_** The dataset for creating this model is immature and this affects the generalization of the model on different persons, so please follow the instructions for good gesture recognition.
+
+To begin with, please make sure that the default (initial) position of the device is the same as following:
+
+
+
+Next, follow the images on how to make gestures. For better recognition use your wrists more when making gestures, and not your whole hand:
+
+__Swipe Right & Left__
+| | |
+| -------------------------------------- | --------------------------------------------- |
+|  |  |
+| Swipe Right | Swipe Left |
+
+__Rotation Clockwise & Counter Clockwise__
+
+| | |
+| -------------------------------------- | --------------------------------------------- |
+|  |  |
+| Rotation Clockwise(Right) | Rotation Counter Clockwise(Left) |
+
+__Double Shake & Double Tap__
+
+| | |
+| -------------------------------------- | --------------------------------------------- |
+|  |  |
+| Double Shake | Double Tap |
+
+When performing gestures with the device, in the serial port terminal, you should see the similar messages:
+
+```
+Predicted class: DOUBLE SHAKE, with probability 96 %
+BLE HID Key 8 sent successfully
+Predicted class: SWIPE RIGHT, with probability 99 %
+BLE HID Key 32 sent successfully
+Predicted class: SWIPE LEFT, with probability 99 %
+BLE HID Key 16 sent successfully
+Predicted class: ROTATION RIGHT, with probability 93 %
+BLE HID Key 1 sent successfully
+```
+Have fun and use this model for your future gesture control projects!
diff --git a/applications/gesture_recognition/prj.conf b/applications/gesture_recognition/prj.conf
new file mode 100644
index 0000000..a372ac3
--- /dev/null
+++ b/applications/gesture_recognition/prj.conf
@@ -0,0 +1,103 @@
+#
+# Copyright (c) 2018 Nordic Semiconductor
+#
+# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
+#
+
+# nRF Edge AI
+CONFIG_NRF_EDGEAI=y
+# nRF Edge AI dependencies
+CONFIG_NEWLIB_LIBC=y
+CONFIG_FPU=y
+
+# Enable Bluetooth
+CONFIG_BT=y
+CONFIG_BT_PERIPHERAL=y
+CONFIG_BT_DEVICE_NAME="T53_sensor_hub"
+
+# Enable buttons and LEDs
+CONFIG_DK_LIBRARY=y
+
+CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
+
+# Configure Thingy:53 sensors
+CONFIG_STDOUT_CONSOLE=y
+CONFIG_I2C=y
+CONFIG_SPI=y
+CONFIG_BMI270_TRIGGER_GLOBAL_THREAD=y
+CONFIG_SENSOR=y
+CONFIG_ADC=y
+
+CONFIG_LOG=y
+CONFIG_LOG_DEFAULT_LEVEL=1
+
+################################################################################
+# Enable USB CDC ACM
+CONFIG_USB_DEVICE_REMOTE_WAKEUP=n
+CONFIG_USB_NRFX_WORK_QUEUE_STACK_SIZE=1200
+CONFIG_USB_DEVICE_PRODUCT="Thingy:53 Application"
+CONFIG_USB_DEVICE_VID=0x1915
+CONFIG_USB_DEVICE_PID=0x530C
+
+# Enable CDC ACM (USB-UART)
+CONFIG_USB_CDC_ACM=y
+CONFIG_UART_LINE_CTRL=y
+
+# Enable console
+CONFIG_CONSOLE=y
+CONFIG_UART_CONSOLE=y
+CONFIG_LOG_MODE_MINIMAL=n
+
+CONFIG_COUNTER=y
+
+# Enable FPU
+CONFIG_FPU=y
+
+# Bluetooth configuration
+CONFIG_NCS_SAMPLES_DEFAULTS=y
+
+CONFIG_BT=y
+CONFIG_BT_LOG_LEVEL_DBG=n
+CONFIG_BT_MAX_CONN=2
+CONFIG_BT_MAX_PAIRED=2
+CONFIG_BT_SMP=y
+CONFIG_BT_L2CAP_TX_BUF_COUNT=5
+CONFIG_BT_PERIPHERAL=y
+CONFIG_BT_DEVICE_NAME="Neuton NRF RemoteControl"
+CONFIG_BT_DEVICE_APPEARANCE=961
+
+CONFIG_BT_BAS=y
+CONFIG_BT_HIDS=y
+CONFIG_BT_HIDS_MAX_CLIENT_COUNT=1
+CONFIG_BT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y
+CONFIG_BT_GATT_UUID16_POOL_SIZE=40
+CONFIG_BT_GATT_CHRC_POOL_SIZE=20
+
+CONFIG_BT_CONN_CTX=y
+
+CONFIG_BT_DIS=y
+CONFIG_BT_DIS_PNP=y
+CONFIG_BT_DIS_MANUF="NordicSemiconductor"
+CONFIG_BT_DIS_PNP_VID_SRC=2
+CONFIG_BT_DIS_PNP_VID=0x1915
+CONFIG_BT_DIS_PNP_PID=0xEEEF
+CONFIG_BT_DIS_PNP_VER=0x0100
+
+CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
+
+CONFIG_BT_SETTINGS=y
+CONFIG_FLASH=y
+CONFIG_FLASH_PAGE_LAYOUT=y
+CONFIG_FLASH_MAP=y
+CONFIG_NVS=y
+CONFIG_SETTINGS=y
+
+CONFIG_DK_LIBRARY=y
+
+# Enable logging
+CONFIG_USB_DEVICE_LOG_LEVEL_ERR=y
+CONFIG_USB_DEVICE_LOG_LEVEL_DBG=n
+
+# LED configuration
+CONFIG_PWM=y
+CONFIG_DATA_COLLECTION_MODE=n
diff --git a/applications/gesture_recognition/resources/ble_connect_1.png b/applications/gesture_recognition/resources/ble_connect_1.png
new file mode 100644
index 0000000..a29b34d
Binary files /dev/null and b/applications/gesture_recognition/resources/ble_connect_1.png differ
diff --git a/applications/gesture_recognition/resources/connected-devkit-sw-install-step-7.jpg b/applications/gesture_recognition/resources/connected-devkit-sw-install-step-7.jpg
new file mode 100644
index 0000000..5d6cd43
Binary files /dev/null and b/applications/gesture_recognition/resources/connected-devkit-sw-install-step-7.jpg differ
diff --git a/applications/gesture_recognition/resources/device-led-ble-connect-music-mode.gif b/applications/gesture_recognition/resources/device-led-ble-connect-music-mode.gif
new file mode 100644
index 0000000..959b701
Binary files /dev/null and b/applications/gesture_recognition/resources/device-led-ble-connect-music-mode.gif differ
diff --git a/applications/gesture_recognition/resources/device-led-ble-connect-presentation-mode.gif b/applications/gesture_recognition/resources/device-led-ble-connect-presentation-mode.gif
new file mode 100644
index 0000000..68a630c
Binary files /dev/null and b/applications/gesture_recognition/resources/device-led-ble-connect-presentation-mode.gif differ
diff --git a/applications/gesture_recognition/resources/device-led-no-ble-connect.gif b/applications/gesture_recognition/resources/device-led-no-ble-connect.gif
new file mode 100644
index 0000000..c58d1a9
Binary files /dev/null and b/applications/gesture_recognition/resources/device-led-no-ble-connect.gif differ
diff --git a/applications/gesture_recognition/resources/device_ble_connected.jpg b/applications/gesture_recognition/resources/device_ble_connected.jpg
new file mode 100644
index 0000000..6476aa1
Binary files /dev/null and b/applications/gesture_recognition/resources/device_ble_connected.jpg differ
diff --git a/applications/gesture_recognition/resources/device_ble_scanning.jpg b/applications/gesture_recognition/resources/device_ble_scanning.jpg
new file mode 100644
index 0000000..17837cd
Binary files /dev/null and b/applications/gesture_recognition/resources/device_ble_scanning.jpg differ
diff --git a/applications/gesture_recognition/resources/double_shake.gif b/applications/gesture_recognition/resources/double_shake.gif
new file mode 100644
index 0000000..fe34312
Binary files /dev/null and b/applications/gesture_recognition/resources/double_shake.gif differ
diff --git a/applications/gesture_recognition/resources/double_tap.gif b/applications/gesture_recognition/resources/double_tap.gif
new file mode 100644
index 0000000..ee02364
Binary files /dev/null and b/applications/gesture_recognition/resources/double_tap.gif differ
diff --git a/applications/gesture_recognition/resources/initial_orientation.gif b/applications/gesture_recognition/resources/initial_orientation.gif
new file mode 100644
index 0000000..5f2707b
Binary files /dev/null and b/applications/gesture_recognition/resources/initial_orientation.gif differ
diff --git a/applications/gesture_recognition/resources/nordic_thingy.jpg b/applications/gesture_recognition/resources/nordic_thingy.jpg
new file mode 100644
index 0000000..9466332
Binary files /dev/null and b/applications/gesture_recognition/resources/nordic_thingy.jpg differ
diff --git a/applications/gesture_recognition/resources/rotation_left.gif b/applications/gesture_recognition/resources/rotation_left.gif
new file mode 100644
index 0000000..52767e0
Binary files /dev/null and b/applications/gesture_recognition/resources/rotation_left.gif differ
diff --git a/applications/gesture_recognition/resources/rotation_right.gif b/applications/gesture_recognition/resources/rotation_right.gif
new file mode 100644
index 0000000..122e693
Binary files /dev/null and b/applications/gesture_recognition/resources/rotation_right.gif differ
diff --git a/applications/gesture_recognition/resources/sw-install-step-3.jpg b/applications/gesture_recognition/resources/sw-install-step-3.jpg
new file mode 100644
index 0000000..5046d09
Binary files /dev/null and b/applications/gesture_recognition/resources/sw-install-step-3.jpg differ
diff --git a/applications/gesture_recognition/resources/sw-install-step-4.jpg b/applications/gesture_recognition/resources/sw-install-step-4.jpg
new file mode 100644
index 0000000..395af78
Binary files /dev/null and b/applications/gesture_recognition/resources/sw-install-step-4.jpg differ
diff --git a/applications/gesture_recognition/resources/sw-install-step-5-1-important.jpg b/applications/gesture_recognition/resources/sw-install-step-5-1-important.jpg
new file mode 100644
index 0000000..1896abd
Binary files /dev/null and b/applications/gesture_recognition/resources/sw-install-step-5-1-important.jpg differ
diff --git a/applications/gesture_recognition/resources/sw-install-step-5-2-important.jpg b/applications/gesture_recognition/resources/sw-install-step-5-2-important.jpg
new file mode 100644
index 0000000..3738700
Binary files /dev/null and b/applications/gesture_recognition/resources/sw-install-step-5-2-important.jpg differ
diff --git a/applications/gesture_recognition/resources/sw-install-step-5.jpg b/applications/gesture_recognition/resources/sw-install-step-5.jpg
new file mode 100644
index 0000000..8e0eaf5
Binary files /dev/null and b/applications/gesture_recognition/resources/sw-install-step-5.jpg differ
diff --git a/applications/gesture_recognition/resources/sw-install-step-6.jpg b/applications/gesture_recognition/resources/sw-install-step-6.jpg
new file mode 100644
index 0000000..d4158e4
Binary files /dev/null and b/applications/gesture_recognition/resources/sw-install-step-6.jpg differ
diff --git a/applications/gesture_recognition/resources/sw-install-step-8.jpg b/applications/gesture_recognition/resources/sw-install-step-8.jpg
new file mode 100644
index 0000000..e5bd674
Binary files /dev/null and b/applications/gesture_recognition/resources/sw-install-step-8.jpg differ
diff --git a/applications/gesture_recognition/resources/swipe_left.gif b/applications/gesture_recognition/resources/swipe_left.gif
new file mode 100644
index 0000000..d5abef0
Binary files /dev/null and b/applications/gesture_recognition/resources/swipe_left.gif differ
diff --git a/applications/gesture_recognition/resources/swipe_right.gif b/applications/gesture_recognition/resources/swipe_right.gif
new file mode 100644
index 0000000..3614694
Binary files /dev/null and b/applications/gesture_recognition/resources/swipe_right.gif differ
diff --git a/applications/gesture_recognition/sample.yaml b/applications/gesture_recognition/sample.yaml
new file mode 100644
index 0000000..bacc394
--- /dev/null
+++ b/applications/gesture_recognition/sample.yaml
@@ -0,0 +1,16 @@
+sample:
+ description: Hello World sample, the simplest Zephyr
+ application
+ name: hello world
+common:
+ tags: introduction
+ integration_platforms:
+ - native_posix
+ harness: console
+ harness_config:
+ type: one_line
+ regex:
+ - "Hello World! (.*)"
+tests:
+ sample.basic.helloworld:
+ tags: introduction
diff --git a/applications/gesture_recognition/src/app_version.h b/applications/gesture_recognition/src/app_version.h
new file mode 100644
index 0000000..3896dae
--- /dev/null
+++ b/applications/gesture_recognition/src/app_version.h
@@ -0,0 +1,39 @@
+/* 2023-06-09T11:22:25Z */
+
+/* ----------------------------------------------------------------------
+Copyright (c) 2022-2024 Neuton.AI, Inc.
+*
+The source code and its binary form are being made available on the following terms:
+Redistribution, use, and modification are permitted provided that the following
+conditions are met:
+*
+1. Redistributions of source code and/or its binary form must retain the above copyright notice,
+* this list of conditions (and the disclaimer) either in the body of the source code or in
+* the documentation and/or other materials provided with the distribution of the binary form, as
+applicable.
+*
+2. The name of the copyright holder may not be used to endorse or promote products derived from this
+* source code or its binary form without specific prior written permission of Neuton.AI, Inc.
+*
+3. Disclaimer. THIS SOURCE CODE AND ITS BINARY FORM ARE PROVIDED BY THE COPYRIGHT HOLDER "AS IS".
+* THE COPYRIGHT HOLDER HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+* PARTICULAR PURPOSE. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE HELD LIABLE
+* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+* SERVICES; LOSS OF USE, DATA, OR PROFITS; INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS
+* OF THIRD PARTIES; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+* IN ANY WAY OUT OF THE USE OF THIS SOURCE CODE OR ITS BINARY FORM, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------- */
+#ifndef __APPLICATION_VERSION_H__
+#define __APPLICATION_VERSION_H__
+
+
+#define APP_VERSION_MAJOR 3
+#define APP_VERSION_MINOR 0
+#define APP_VERSION_PATCH 1
+
+
+#endif /* __APPLICATION_VERSION_H__ */
diff --git a/applications/gesture_recognition/src/ble/hid/ble_hid.c b/applications/gesture_recognition/src/ble/hid/ble_hid.c
new file mode 100644
index 0000000..72ec9bd
--- /dev/null
+++ b/applications/gesture_recognition/src/ble/hid/ble_hid.c
@@ -0,0 +1,530 @@
+#include "ble_hid.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+//////////////////////////////////////////////////////////////////////////////
+
+#define KEY_ARROW_LEFT (0x50)
+#define KEY_ARROW_RIGHT (0x4F)
+#define KEY_F5 (0x3E)
+#define KEY_ESP (0x29)
+
+#define KEY_MEDIA_VOLUME_UP ( 1 << 0 )
+#define KEY_MEDIA_VOLUME_DOWN ( 1 << 1 )
+#define KEY_MEDIA_MUTE ( 1 << 2 )
+#define KEY_MEDIA_PLAY_PAUSE ( 1 << 3 )
+#define KEY_MEDIA_PREV_TRACK ( 1 << 4 )
+#define KEY_MEDIA_NEXT_TRACK ( 1 << 5 )
+
+#if CONFIG_SAMPLE_BT_USE_AUTHENTICATION
+/* Require encryption using authenticated link-key. */
+#define SAMPLE_BT_PERM_READ BT_GATT_PERM_READ_AUTHEN
+#define SAMPLE_BT_PERM_WRITE BT_GATT_PERM_WRITE_AUTHEN
+#else
+/* Require encryption. */
+#define SAMPLE_BT_PERM_READ BT_GATT_PERM_READ_ENCRYPT
+#define SAMPLE_BT_PERM_WRITE BT_GATT_PERM_WRITE_ENCRYPT
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+enum
+{
+ HIDS_REMOTE_WAKE = BIT(0),
+ HIDS_NORMALLY_CONNECTABLE = BIT(1),
+};
+
+struct hids_info
+{
+ uint16_t version; /* version number of base USB HID Specification */
+ uint8_t code; /* country HID Device hardware is localized for. */
+ uint8_t flags;
+} __packed;
+
+struct hids_report
+{
+ uint8_t id; /* report id */
+ uint8_t type; /* report type */
+} __packed;
+
+enum
+{
+ HIDS_INPUT = 0x01,
+ HIDS_OUTPUT = 0x02,
+ HIDS_FEATURE = 0x03,
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static const struct bt_data ad[] = {
+ BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
+ BT_DATA_BYTES(BT_DATA_UUID16_ALL,
+ BT_UUID_16_ENCODE(BT_UUID_HIDS_VAL),
+ BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)),
+};
+
+static const struct bt_data sd[] = {
+ BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
+};
+
+static struct hids_info info = {
+ .version = 0x0000,
+ .code = 0x00,
+ .flags = HIDS_NORMALLY_CONNECTABLE,
+};
+
+static struct hids_report input = {
+ .id = 0x01,
+ .type = HIDS_INPUT,
+};
+
+static struct hids_report input_consumer = {
+ .id = 0x02,
+ .type = HIDS_INPUT,
+};
+
+static bool ble_connected_ = false;
+static ble_connection_cb_t user_conn_callback_ = NULL;
+
+static bool ccc_enabled_ = false;
+static uint8_t ctrl_point;
+static uint8_t consumer_report;
+
+static uint8_t report_map[] = {
+ 0x05, 0x01, // Usage Page (Generic Desktop)
+ 0x09, 0x06, // Usage (Keyboard)
+ 0xA1, 0x01, // Collection (Application)
+
+ 0x05, 0x07, // Usage Page (Keyboard/Keypad)
+ 0x85, 0x01, // Report ID (1)
+ 0x19, 0xE0, // Usage Minimum (Keyboard Left Control)
+ 0x29, 0xE7, // Usage Maximum (Keyboard Right GUI)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x08, // Report Count (8)
+ 0x81, 0x02, // Input (Data, Variable, Absolute) ; Modifier keys
+
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x08, // Report Size (8)
+ 0x81, 0x01, // Input (Constant) ; Reserved byte
+
+ 0x95, 0x05, // Report Count (5)
+ 0x75, 0x01, // Report Size (1)
+ 0x05, 0x08, // Usage Page (LEDs)
+ 0x85, 0x01, // Report ID (1)
+ 0x19, 0x01, // Usage Minimum (Num Lock)
+ 0x29, 0x05, // Usage Maximum (Kana)
+ 0x91, 0x02, // Output (Data, Variable, Absolute) ; LED report
+
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x03, // Report Size (3)
+ 0x91, 0x03, // Output (Constant) ; LED report padding
+
+ 0x95, 0x06, // Report Count (6)
+ 0x75, 0x08, // Report Size (8)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x65, // Logical Maximum (101)
+ 0x05, 0x07, // Usage Page (Keyboard/Keypad)
+ 0x19, 0x00, // Usage Minimum (Reserved (no event indicated))
+ 0x29, 0x65, // Usage Maximum (Keyboard Application)
+ 0x81, 0x00, // Input (Data, Array) ; Key arrays (6 bytes)
+
+ 0xC0, // End Collection
+
+ // Consumer Control Report
+ 0x05, 0x0C, // Usage Page (Consumer Devices)
+ 0x09, 0x01, // Usage (Consumer Control)
+ 0xA1, 0x01, // Collection (Application)
+
+ 0x85, 0x02, // Report ID (2)
+ 0x05, 0x0C, // Usage Page (Consumer Devices)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+
+ 0x09, 0xE9, // Usage (Volume Up)
+ 0x09, 0xEA, // Usage (Volume Down)
+ 0x09, 0xE2, // Usage (Mute)
+ 0x09, 0xCD, // Usage (Play/Pause)
+ 0x19, 0xB5, // Usage Minimum (Scan Next Track)
+ 0x29, 0xB8, // Usage Maximum (Scan Previous Track)
+
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x08, // Report Count (8)
+ 0x81, 0x02, // Input (Data, Variable, Absolute) ; Media keys
+
+ 0xC0 // End Collection
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static ssize_t read_info(struct bt_conn* conn,
+ const struct bt_gatt_attr* attr, void* buf,
+ uint16_t len, uint16_t offset)
+{
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
+ sizeof(struct hids_info));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static ssize_t read_report_map(struct bt_conn* conn,
+ const struct bt_gatt_attr* attr, void* buf,
+ uint16_t len, uint16_t offset)
+{
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, report_map,
+ sizeof(report_map));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static ssize_t read_report(struct bt_conn* conn,
+ const struct bt_gatt_attr* attr, void* buf,
+ uint16_t len, uint16_t offset)
+{
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
+ sizeof(struct hids_report));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void input_ccc_changed(const struct bt_gatt_attr* attr, uint16_t value)
+{
+ printk("Input CCCD %s\n", value == BT_GATT_CCC_NOTIFY ? "enabled" : "disabled");
+ printk("Input attribute handle: %d\n", attr->handle);
+
+ ccc_enabled_ = (value == BT_GATT_CCC_NOTIFY);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static ssize_t read_input_report(struct bt_conn* conn,
+ const struct bt_gatt_attr* attr, void* buf,
+ uint16_t len, uint16_t offset)
+{
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, NULL, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static ssize_t write_ctrl_point(struct bt_conn* conn,
+ const struct bt_gatt_attr* attr,
+ const void* buf, uint16_t len, uint16_t offset,
+ uint8_t flags)
+{
+
+ uint8_t* value = attr->user_data;
+
+ printk("write_ctrl_point\n");
+
+ if (offset + len > sizeof(ctrl_point))
+ {
+ return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
+ }
+
+ memcpy(value + offset, buf, len);
+
+ return len;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static ssize_t read_consumer_report(struct bt_conn* conn,
+ const struct bt_gatt_attr* attr,
+ void* buf, uint16_t len, uint16_t offset)
+{
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, NULL, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void consumer_ccc_changed(const struct bt_gatt_attr* attr, uint16_t value)
+{
+ printk("Consumer CCCD %s\n", value == BT_GATT_CCC_NOTIFY ? "enabled" : "disabled");
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+BT_GATT_SERVICE_DEFINE(hog_svc,
+ BT_GATT_PRIMARY_SERVICE(BT_UUID_HIDS),
+ BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_INFO, BT_GATT_CHRC_READ,
+ BT_GATT_PERM_READ, read_info, NULL, &info),
+ BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT_MAP, BT_GATT_CHRC_READ,
+ BT_GATT_PERM_READ, read_report_map, NULL, NULL),
+
+ BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
+ BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
+ SAMPLE_BT_PERM_READ,
+ read_input_report, NULL, NULL),
+ BT_GATT_CCC(input_ccc_changed,
+ SAMPLE_BT_PERM_READ | SAMPLE_BT_PERM_WRITE),
+ BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ,
+ read_report, NULL, &input),
+
+ BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
+ BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
+ SAMPLE_BT_PERM_READ,
+ read_consumer_report, NULL, &consumer_report),
+ BT_GATT_CCC(consumer_ccc_changed,
+ SAMPLE_BT_PERM_READ | SAMPLE_BT_PERM_WRITE),
+ BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ,
+ read_report, NULL, &input_consumer),
+
+ BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT,
+ BT_GATT_CHRC_WRITE_WITHOUT_RESP,
+ BT_GATT_PERM_WRITE,
+ NULL, write_ctrl_point, &ctrl_point), );
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void connected(struct bt_conn* conn, uint8_t err)
+{
+ char addr[BT_ADDR_LE_STR_LEN];
+
+ bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
+ if (err)
+ {
+ printk("Failed to connect to %s (%u)\n", addr, err);
+ return;
+ }
+
+ printk("Connected %s\n", addr);
+
+ if (bt_conn_set_security(conn, BT_SECURITY_L2))
+ {
+ printk("Failed to set security\n");
+ }
+
+ ble_connected_ = true;
+
+ if (user_conn_callback_)
+ user_conn_callback_(ble_connected_);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void disconnected(struct bt_conn* conn, uint8_t reason)
+{
+ char addr[BT_ADDR_LE_STR_LEN];
+
+ bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
+ printk("Disconnected from %s (reason 0x%02x)\n", addr, reason);
+
+ ble_connected_ = false;
+ ccc_enabled_ = false;
+
+ if (user_conn_callback_)
+ user_conn_callback_(ble_connected_);
+
+ // If disconnected due to authentication failure, clear all pairing info
+ if (reason == BT_HCI_ERR_AUTH_FAIL || reason == BT_HCI_ERR_PIN_OR_KEY_MISSING) {
+ printk("Authentication related disconnect, clearing pairing info\n");
+ bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
+ }
+
+ // Restart advertising
+ int err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
+ if (err)
+ {
+ printk("Advertising failed to start (err %d)\n", err);
+ return;
+ }
+ printk("Advertising successfully started\n");
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void security_changed(struct bt_conn* conn, bt_security_t level,
+ enum bt_security_err err)
+{
+ char addr[BT_ADDR_LE_STR_LEN];
+
+ bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
+ if (!err)
+ {
+ printk("Security changed: %s level %u\n", addr, level);
+ } else
+ {
+ printk("Security failed: %s level %u err %d\n", addr, level,
+ err);
+
+ bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+BT_CONN_CB_DEFINE(conn_callbacks) = {
+ .connected = connected,
+ .disconnected = disconnected,
+ .security_changed = security_changed,
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void bt_ready(int err)
+{
+ if (err)
+ {
+ printk("Bluetooth init failed (err %d)\n", err);
+ return;
+ }
+
+ printk("Bluetooth initialized\n");
+
+ if (IS_ENABLED(CONFIG_SETTINGS))
+ {
+ settings_load();
+ }
+
+ err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
+ if (err)
+ {
+ printk("Advertising failed to start (err %d)\n", err);
+ return;
+ }
+
+ printk("Advertising successfully started\n");
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void auth_passkey_display(struct bt_conn* conn, unsigned int passkey)
+{
+ char addr[BT_ADDR_LE_STR_LEN];
+
+ bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
+ printk("Passkey for %s: %06u\n", addr, passkey);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void auth_cancel(struct bt_conn* conn)
+{
+ char addr[BT_ADDR_LE_STR_LEN];
+
+ bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
+ printk("Pairing cancelled: %s\n", addr);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static struct bt_conn_auth_cb auth_cb_display = {
+ .passkey_display = auth_passkey_display,
+ .passkey_entry = NULL,
+ .cancel = auth_cancel,
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+int ble_hid_init(ble_connection_cb_t cb)
+{
+ int err;
+ err = bt_enable(bt_ready);
+
+ if (err)
+ {
+ printk("Bluetooth init failed (err %d)\n", err);
+ return err;
+ }
+
+ user_conn_callback_ = cb;
+
+ if (IS_ENABLED(CONFIG_SAMPLE_BT_USE_AUTHENTICATION))
+ {
+ bt_conn_auth_cb_register(&auth_cb_display);
+ }
+
+ return err;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int ble_hid_send_key(ble_hid_key_t key)
+{
+ int res = -1;
+
+ if (!ble_connected_ || !ccc_enabled_)
+ return -1;
+
+ uint8_t report[8] = {0};
+ size_t report_len = sizeof(report);
+
+ int attr_index = 5;
+
+ switch (key)
+ {
+ case BLE_HID_KEY_ARROW_LEFT:
+ report[2] = KEY_ARROW_LEFT;
+ break;
+ case BLE_HID_KEY_ARROW_RIGHT:
+ report[2] = KEY_ARROW_RIGHT;
+ break;
+ case BLE_HID_KEY_F5:
+ report[2] = KEY_F5;
+ break;
+ case BLE_HID_KEY_ESC:
+ report[2] = KEY_ESP;
+ break;
+ case BLE_HID_KEY_MEDIA_PREV_TRACK:
+ report[0] = KEY_MEDIA_PREV_TRACK;
+ report_len = 2;
+ attr_index = 10;
+ break;
+ case BLE_HID_KEY_MEDIA_NEXT_TRACK:
+ report[0] = KEY_MEDIA_NEXT_TRACK;
+ report_len = 2;
+ attr_index = 10;
+ break;
+ case BLE_HID_KEY_MEDIA_MUTE:
+ report[0] = KEY_MEDIA_MUTE;
+ report_len = 2;
+ attr_index = 10;
+ break;
+ case BLE_HID_KEY_MEDIA_PLAY_PAUSE:
+ report[0] = KEY_MEDIA_PLAY_PAUSE;
+ report_len = 2;
+ attr_index = 10;
+ break;
+ case BLE_HID_KEY_MEDIA_VOLUME_UP:
+ report[0] = KEY_MEDIA_VOLUME_UP;
+ report_len = 2;
+ attr_index = 10;
+ break;
+ case BLE_HID_KEY_MEDIA_VOLUME_DOWN:
+ report[0] = KEY_MEDIA_VOLUME_DOWN;
+ report_len = 2;
+ attr_index = 10;
+ break;
+ default:
+ return res;
+ }
+
+ res = bt_gatt_notify(NULL, &hog_svc.attrs[attr_index], report, report_len);
+
+ if (res)
+ {
+ printk("Failed to send key, error = %d\n", res);
+ return false;
+ }
+ else
+ {
+ printk("BLE HID Key %d sent successfully\n", report[0]);
+ }
+
+ /* reset report */
+ memset(report, 0, sizeof(report));
+
+ res = bt_gatt_notify(NULL, &hog_svc.attrs[attr_index], report, report_len);
+ return res;
+}
\ No newline at end of file
diff --git a/applications/gesture_recognition/src/ble/hid/ble_hid.h b/applications/gesture_recognition/src/ble/hid/ble_hid.h
new file mode 100644
index 0000000..f09c51d
--- /dev/null
+++ b/applications/gesture_recognition/src/ble/hid/ble_hid.h
@@ -0,0 +1,75 @@
+/**
+ *
+ * @defgroup ble_hid Bluetooth HID interface
+ * @{
+ * @ingroup ble
+ *
+ *
+ */
+#ifndef __BLE_HID_H__
+#define __BLE_HID_H__
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/**
+ * @brief Supported HID keys to emulate keyboard
+ */
+typedef enum
+{
+ BLE_HID_KEY_ARROW_LEFT = 0,
+ BLE_HID_KEY_ARROW_RIGHT,
+ BLE_HID_KEY_F5,
+ BLE_HID_KEY_ESC,
+ BLE_HID_KEY_MEDIA_PREV_TRACK,
+ BLE_HID_KEY_MEDIA_NEXT_TRACK,
+ BLE_HID_KEY_MEDIA_PLAY_PAUSE,
+ BLE_HID_KEY_MEDIA_MUTE,
+ BLE_HID_KEY_MEDIA_VOLUME_UP,
+ BLE_HID_KEY_MEDIA_VOLUME_DOWN,
+
+ BLE_HID_KEYS_count
+} ble_hid_key_t;
+
+/**
+ * @brief BLE connection callback, this callback will be called when state of the connection is changed
+ *
+ * @param connected BLE connected state, true if connected, otherwise false
+ */
+typedef void (*ble_connection_cb_t)(bool connected);
+
+/**
+ * @brief Initialize BLE HID profile and start advertasing
+ *
+ * @param cb Connection callback @ref ble_connection_cb_t
+ *
+ * @return Operation status, 0 for success
+ */
+int ble_hid_init(ble_connection_cb_t cb);
+
+/**
+ * @brief Send keyboard key via HID profile
+ *
+ * @param key Keyboard key @ref ble_hid_key_t
+ *
+ * @return Operation status, 0 for success
+ */
+int ble_hid_send_key(ble_hid_key_t key);
+
+
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+
+#endif // __BLE_HID_H__
+
+/**
+ * @}
+ */
diff --git a/applications/gesture_recognition/src/bsp/bsp_common.h b/applications/gesture_recognition/src/bsp/bsp_common.h
new file mode 100644
index 0000000..eafb2d5
--- /dev/null
+++ b/applications/gesture_recognition/src/bsp/bsp_common.h
@@ -0,0 +1,244 @@
+/**
+ *
+ * @defgroup bsp_common Common
+ * @{
+ * @ingroup bsp
+ *
+ * @brief BSP common useful utils, types and macro.
+ *
+ */
+#ifndef __BSP_COMMON_H__
+#define __BSP_COMMON_H__
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/**
+ * @brief Macro for performing rounded integer division (as opposed to truncating the result).
+ *
+ * @param[in] A Numerator.
+ * @param[in] B Denominator.
+ *
+ * @return Rounded (integer) result of dividing A by B.
+ */
+#ifndef ROUNDED_DIV
+#define ROUNDED_DIV(A, B) (((A) + ((B) / 2)) / (B))
+#endif
+
+/**
+ * @brief Macro for counting items in an object.
+ *
+ * @param[in] x An object for which the counting will be made.
+ *
+ * @return A number of items in an object.
+ */
+#ifndef COUNT_OF
+#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
+#endif
+
+/**
+ * @brief Macro for getting offset for a field in the provided type.
+ *
+ * @param[in] type Provided type.
+ * @param[in] field Field in the provided type.
+ *
+ * @return Offset of a field in the provided type.
+ */
+#ifndef OFFSET_OF
+#define OFFSET_OF(type, field) ((unsigned long) &(((type *) 0)->field))
+#endif
+
+/**
+ * @brief Macro for obtaining the minimum value of two.
+ *
+ * @param[in] A First value.
+ * @param[in] B Second value.
+ *
+ * @return Minimum value of two set values.
+ */
+#ifndef MIN
+#define MIN(A, B) (( (A) < (B) ) ? (A) : (B))
+#endif
+
+/**
+ * @brief Macro for obtaining the maximum value of two.
+ *
+ * @param[in] A First value.
+ * @param[in] B Second value.
+ *
+ * @return Maximum value of two set values.
+ */
+#ifndef MAX
+#define MAX(A, B) (( (A) > (B) ) ? (A) : (B))
+#endif
+
+/**
+ * @brief Macro for obtaining the constraint value between min and max values.
+ *
+ * @param[in] val input value.
+ * @param[in] vmin lower border.
+ * @param[in] vmax higher border.
+ *
+ * @return Constraint value between min and max.
+ */
+#ifndef CONSTRAIN
+#define CONSTRAIN(val, vmin, vmax) ((val)>(vmax)?(vmax):(val)<(vmin)?(vmin):(val))
+#endif
+
+/**
+ * @brief Macro for unused argument.
+ *
+ * @param[in] x Unused argument.
+ *
+ * @return None.
+ */
+#ifndef UNUSED
+#define UNUSED(x) ((void)(x))
+#endif
+
+/** @brief Void value using for returns or argument for void-argument functions. */
+#define VOID_VALUE
+
+/**
+ * @brief Macro for checking argument for NULL.
+ * Macro will call return with the status BSP_STATUS_NULL_ARGUMENT.
+ *
+ * @param[in] x Argument to be checked.
+ *
+ */
+#define BSP_NULL_CHECK(x) \
+ do \
+ { \
+ if ( (x) == NULL ) \
+ { \
+ return BSP_STATUS_NULL_ARGUMENT; \
+ } \
+ } while(0)
+
+/**
+ * @brief Macro for verifying that the provided status is BSP_STATUS_SUCCESS. It will cause the exterior
+ * function to return an error code if it is not @ref BSP_STATUS_SUCCESS.
+ *
+ * @param[in] status Status to check vs BSP_STATUS_SUCCESS.
+ */
+#define BSP_VERIFY_SUCCESS(status) \
+do \
+{ \
+ if ((status) != BSP_STATUS_SUCCESS) \
+ { \
+ return (status); \
+ } \
+} while(0)
+
+/**
+ * @brief Macro for verifying that the provided argumets is valid. It will cause the exterior
+ * function to return an error code if it is not @ref BSP_STATUS_INVALID_ARGUMENT.
+ *
+ * @param[in] is_valid boolean comparison on the validity of the argument.
+ */
+#define BSP_VERIFY_VALID_ARG(is_valid) \
+do \
+{ \
+ if (!(is_valid)) \
+ { \
+ return BSP_STATUS_INVALID_ARGUMENT; \
+ } \
+} while(0)
+
+/**
+ * @brief Macro for verifying any boolean condition and returning status if condition failed
+ *
+ * @param[in] err_cond boolean condition to be checked.
+ * @param[in] err Return status if condition failed.
+ */
+#define BSP_RETURN_IF(err_cond, err) __RETURN_CONDITIONAL(err_cond, err)
+
+/**
+ * @brief Return if expr == true.
+ *
+ * @param[in] expr Expression for validating.
+ * @param[in] ret_val Returning value.
+ */
+#ifndef __RETURN_CONDITIONAL
+# define __RETURN_CONDITIONAL(expr, ret_val) \
+ do \
+ { \
+ if ((expr) == true) \
+ { \
+ return ret_val; \
+ } \
+ } \
+ while (0)
+#endif
+
+/** Generic callback type */
+typedef void(*bsp_generic_cb_t)(void);
+
+/** Base interrupt request handler type */
+typedef bsp_generic_cb_t bsp_irq_handler_t;
+
+/**
+ * @brief Base async data ready callback type
+ *
+ * @param[in] data A pointer to the data buffer that was passed to the async function
+ * @param[in] data_size Size of data that was passed to the async function, in bytes
+ */
+typedef void(*bsp_async_drdy_cb_t)(void* data, uint32_t data_size);
+
+/**
+ * Generic bsp operation status code
+ *
+ * This enumeration is used by various bsp subsystems to provide
+ * information on their status. It may also be used by functions as a
+ * return code.
+ */
+typedef enum bsp_status_e
+{
+ /** Operation successful */
+ BSP_STATUS_SUCCESS,
+
+ /** The operation failed because the module is already in the
+ * requested mode */
+ BSP_STATUS_ALREADY_IN_MODE,
+
+ /** There was an error communicating with hardware */
+ BSP_STATUS_HARDWARE_ERROR,
+
+ /** The operation failed with an unspecified error */
+ BSP_STATUS_UNSPECIFIED_ERROR,
+
+ /** The argument supplied to the operation was invalid */
+ BSP_STATUS_INVALID_ARGUMENT,
+
+ /** The argument supplied to the operation was NULL */
+ BSP_STATUS_NULL_ARGUMENT,
+
+ /** The operation failed because the module was busy */
+ BSP_STATUS_BUSY,
+
+ /** The requested operation was not available */
+ BSP_STATUS_UNAVAILABLE,
+
+ /** The operation or service not supported */
+ BSP_STATUS_NOT_SUPPORTED,
+
+ /** The requested operation timeout */
+ BSP_STATUS_TIMEOUT,
+} bsp_status_t;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __BSP_COMMON_H__ */
+
+/**
+ * @}
+ */
diff --git a/applications/gesture_recognition/src/bsp/button/bsp_button.c b/applications/gesture_recognition/src/bsp/button/bsp_button.c
new file mode 100644
index 0000000..64c2e68
--- /dev/null
+++ b/applications/gesture_recognition/src/bsp/button/bsp_button.c
@@ -0,0 +1,82 @@
+#include "bsp_button.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+//////////////////////////////////////////////////////////////////////////////
+
+#define SW0_NODE DT_ALIAS(sw0)
+#if !DT_NODE_HAS_STATUS(SW0_NODE, okay)
+#error "Unsupported board: sw0 devicetree alias is not defined"
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+static const struct gpio_dt_spec button_sw0_ = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
+static struct gpio_callback button_cb_data_;
+static bsp_button_click_handler_t button_click_handler_ = NULL;
+
+//////////////////////////////////////////////////////////////////////////////
+
+static bool is_pressed_(void)
+{
+ return gpio_pin_get_dt(&button_sw0_) > 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void button_interrupt(const struct device* dev, struct gpio_callback* cb, uint32_t pins)
+{
+ ARG_UNUSED(dev);
+ ARG_UNUSED(cb);
+ ARG_UNUSED(pins);
+
+ if (button_click_handler_)
+ {
+ button_click_handler_(is_pressed_());
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int bsp_button_init(void)
+{
+ int ret;
+
+ if (!device_is_ready(button_sw0_.port))
+ {
+ printk("Error: button device %s is not ready\n", button_sw0_.port->name);
+ return ENODEV;
+ }
+
+ ret = gpio_pin_configure_dt(&button_sw0_, GPIO_INPUT);
+ if (ret != 0)
+ {
+ printk("Error %d: failed to configure %s pin %d\n", ret, button_sw0_.port->name, button_sw0_.pin);
+ return ret;
+ }
+
+ ret = gpio_pin_interrupt_configure_dt(&button_sw0_, GPIO_INT_EDGE_BOTH);
+
+ if (ret != 0)
+ {
+ printk("Error %d: failed to configure interrupt on %s pin %d\n", ret, button_sw0_.port->name, button_sw0_.pin);
+ return ret;
+ }
+
+ gpio_init_callback(&button_cb_data_, button_interrupt, BIT(button_sw0_.pin));
+ gpio_add_callback(button_sw0_.port, &button_cb_data_);
+ printk("Set up button at %s pin %d\r\n", button_sw0_.port->name, button_sw0_.pin);
+
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void bsp_button_reg_click_handler(bsp_button_click_handler_t click_handler)
+{
+ button_click_handler_ = click_handler;
+}
\ No newline at end of file
diff --git a/applications/gesture_recognition/src/bsp/button/bsp_button.h b/applications/gesture_recognition/src/bsp/button/bsp_button.h
new file mode 100644
index 0000000..59b1a81
--- /dev/null
+++ b/applications/gesture_recognition/src/bsp/button/bsp_button.h
@@ -0,0 +1,43 @@
+/**
+ *
+ * @defgroup bsp_button Button control functions
+ * @{
+ * @ingroup bsp
+ *
+ *
+ */
+#ifndef __BSP_BUTTON_H__
+#define __BSP_BUTTON_H__
+
+#include
+
+typedef void (*bsp_button_click_handler_t)(bool is_pressed);
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @brief Initialize button module
+ *
+ * @return Operation status result, 0 for success
+ */
+int bsp_button_init(void);
+
+/**
+ * @brief Register button click handler
+ *
+ * @param click_handler Button click handler
+ */
+void bsp_button_reg_click_handler(bsp_button_click_handler_t click_handler);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __BSP_BUTTON_H__ */
+
+/**
+ * @}
+ */
\ No newline at end of file
diff --git a/applications/gesture_recognition/src/bsp/led/bsp_led.c b/applications/gesture_recognition/src/bsp/led/bsp_led.c
new file mode 100644
index 0000000..8150767
--- /dev/null
+++ b/applications/gesture_recognition/src/bsp/led/bsp_led.c
@@ -0,0 +1,191 @@
+#include "bsp_led.h"
+#include
+#include
+#include
+#include
+
+//////////////////////////////////////////////////////////////////////////////
+
+static const struct pwm_dt_spec red_pwm_led_ = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0));
+static const struct pwm_dt_spec green_pwm_led_ = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led1));
+static const struct pwm_dt_spec blue_pwm_led_ = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led2));
+
+//////////////////////////////////////////////////////////////////////////////
+
+// not calibrated, fixed for specific board
+#define PWM_PERIOD PWM_MSEC(20)
+
+//////////////////////////////////////////////////////////////////////////////
+
+static int init_led_(const struct pwm_dt_spec pwm_led)
+{
+ int ret = 0;
+ if (!device_is_ready(pwm_led.dev))
+ {
+ printk("PWM LED Init error '%s' device_is_ready()\n", pwm_led.dev->name);
+ return ret;
+ }
+
+ ret = pwm_set_dt(&pwm_led, PWM_PERIOD, 0);
+ if (ret)
+ {
+ printk("Error %d: failed to set pulse width for %s\n", ret, pwm_led.dev->name);
+ return ret;
+ }
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static int set_led_(const struct pwm_dt_spec pwm_led, float brightness)
+{
+ int ret = 0;
+ uint32_t pulse = brightness * PWM_PERIOD;
+ ret = pwm_set_pulse_dt(&pwm_led, pulse);
+ if (ret < 0)
+ {
+ printk("LED Init error pwm_set_pulse_dt()\n");
+ return ret;
+ }
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int bsp_led_init(void)
+{
+ int ret;
+ ret = init_led_(red_pwm_led_);
+ BSP_RETURN_IF(ret != 0, ret);
+
+ ret = init_led_(green_pwm_led_);
+ BSP_RETURN_IF(ret != 0, ret);
+
+ ret = init_led_(blue_pwm_led_);
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int bsp_led_set_red(float brightness)
+{
+ pwm_set_pulse_dt(&green_pwm_led_, 0);
+ pwm_set_pulse_dt(&blue_pwm_led_, 0);
+ return set_led_(red_pwm_led_, brightness);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int bsp_led_set_green(float brightness)
+{
+ pwm_set_pulse_dt(&red_pwm_led_, 0);
+ pwm_set_pulse_dt(&blue_pwm_led_, 0);
+ return set_led_(green_pwm_led_, brightness);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int bsp_led_set_blue(float brightness)
+{
+ pwm_set_pulse_dt(&red_pwm_led_, 0);
+ pwm_set_pulse_dt(&green_pwm_led_, 0);
+ return set_led_(blue_pwm_led_, brightness);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int bsp_led_set_rgb(float r, float g, float b)
+{
+ int ret;
+ uint32_t red = r * PWM_PERIOD;
+ uint32_t green = g * PWM_PERIOD;
+ uint32_t blue = b * PWM_PERIOD;
+ ret = pwm_set_pulse_dt(&red_pwm_led_, red);
+ BSP_RETURN_IF(ret != 0, ret);
+
+ ret = pwm_set_pulse_dt(&green_pwm_led_, green);
+ BSP_RETURN_IF(ret != 0, ret);
+
+ ret = pwm_set_pulse_dt(&blue_pwm_led_, blue);
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int bsp_led_off(void)
+{
+ int ret;
+ ret = pwm_set_pulse_dt(&red_pwm_led_, 0);
+ BSP_RETURN_IF(ret != 0, ret);
+
+ ret = pwm_set_pulse_dt(&green_pwm_led_, 0);
+ BSP_RETURN_IF(ret != 0, ret);
+
+ ret = pwm_set_pulse_dt(&blue_pwm_led_, 0);
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int bsp_led_blink_red(float brightness, int32_t on_ms, int32_t off_ms)
+{
+ int ret;
+ ret = bsp_led_set_red(brightness);
+ BSP_RETURN_IF(ret != 0, ret);
+
+ k_msleep(on_ms);
+
+ ret = bsp_led_off();
+
+ k_msleep(off_ms);
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int bsp_led_blink_green(float brightness, int32_t on_ms, int32_t off_ms)
+{
+ int ret;
+ ret = bsp_led_set_green(brightness);
+ BSP_RETURN_IF(ret != 0, ret);
+
+ k_msleep(on_ms);
+
+ ret = bsp_led_off();
+
+ k_msleep(off_ms);
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int bsp_led_blink_blue(float brightness, int32_t on_ms, int32_t off_ms)
+{
+ int ret;
+ ret = bsp_led_set_blue(brightness);
+ BSP_RETURN_IF(ret != 0, ret);
+
+ k_msleep(on_ms);
+
+ ret = bsp_led_off();
+
+ k_msleep(off_ms);
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int bsp_led_blink_rgb(float r, float g, float b, int32_t on_ms, int32_t off_ms)
+{
+ int ret;
+ ret = bsp_led_set_rgb(r, g, b);
+
+ BSP_RETURN_IF(ret != 0, ret);
+
+ k_msleep(on_ms);
+
+ ret = bsp_led_off();
+
+ k_msleep(off_ms);
+ return ret;
+}
\ No newline at end of file
diff --git a/applications/gesture_recognition/src/bsp/led/bsp_led.h b/applications/gesture_recognition/src/bsp/led/bsp_led.h
new file mode 100644
index 0000000..cd459b1
--- /dev/null
+++ b/applications/gesture_recognition/src/bsp/led/bsp_led.h
@@ -0,0 +1,127 @@
+/**
+ *
+ * @defgroup bsp_led LEDs control functions
+ * @{
+ * @ingroup bsp
+ *
+ * @brief This module provides LEDs control functions.
+ *
+ */
+#ifndef __BSP_LED_H__
+#define __BSP_LED_H__
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/**
+ * @brief Initialize LEDs
+ *
+ * @return Operation status, 0 for success
+ */
+int bsp_led_init(void);
+
+/**
+ * @brief Turn on red LED with specific brightness
+ *
+ * @param brightness LED brightness in range 0 - 1
+ *
+ * @return Operation status, 0 for success
+ */
+int bsp_led_set_red(float brightness);
+
+/**
+ * @brief Turn on green LED with specific brightness
+ *
+ * @param brightness LED brightness in range 0 - 1
+ *
+ * @return Operation status, 0 for success
+ */
+int bsp_led_set_green(float brightness);
+
+/**
+ * @brief Turn on blue LED with specific brightness
+ *
+ * @param brightness LED brightness in range 0 - 1
+ *
+ * @return Operation status, 0 for success
+ */
+int bsp_led_set_blue(float brightness);
+
+/**
+ * @brief Turn on RGB LED with specific brightness for each color
+ *
+ * @param r Red LED brightness in range 0 - 1
+ * @param g Green LED brightness in range 0 - 1
+ * @param b Blue LED brightness in range 0 - 1
+ *
+ * @return Operation status, 0 for success
+ */
+int bsp_led_set_rgb(float r, float g, float b);
+
+/**
+ * @brief Turn off all LEDs
+ *
+ * @return Operation status, 0 for success
+ */
+int bsp_led_off(void);
+
+/**
+ * @brief Blink red LED with specific brightness
+ *
+ * @param brightness LED brightness in range 0 - 1
+ * @param on_ms LED turns on time in milliseconds
+ * @param off_ms LED turns off time in milliseconds
+ *
+ * @return Operation status, 0 for success
+ */
+int bsp_led_blink_red(float brightness, int32_t on_ms, int32_t off_ms);
+
+/**
+ * @brief Blink green LED with specific brightness
+ *
+ * @param brightness LED brightness in range 0 - 1
+ * @param on_ms LED turns on time in milliseconds
+ * @param off_ms LED turns off time in milliseconds
+ *
+ * @return Operation status, 0 for success
+ */
+int bsp_led_blink_green(float brightness, int32_t on_ms, int32_t off_ms);
+
+/**
+ * @brief Blink blue LED with specific brightness
+ *
+ * @param brightness LED brightness in range 0 - 1
+ * @param on_ms LED turns on time in milliseconds
+ * @param off_ms LED turns off time in milliseconds
+ *
+ * @return Operation status, 0 for success
+ */
+int bsp_led_blink_blue(float brightness, int32_t on_ms, int32_t off_ms);
+
+/**
+ * @brief Blink RGB LED with specific brightness for each color
+ *
+ * @param r Red LED brightness in range 0 - 1
+ * @param g Green LED brightness in range 0 - 1
+ * @param b Blue LED brightness in range 0 - 1
+ * @param on_ms RGB LED turns on time in milliseconds
+ * @param off_ms RGB LED turns off time in milliseconds
+ *
+ * @return Operation status, 0 for success
+ */
+int bsp_led_blink_rgb(float r, float g, float b, int32_t on_ms, int32_t off_ms);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __BSP_LED_H__ */
+
+/**
+ * @}
+ */
diff --git a/applications/gesture_recognition/src/bsp/sensor/imu/bsp_imu.c b/applications/gesture_recognition/src/bsp/sensor/imu/bsp_imu.c
new file mode 100644
index 0000000..6ca9cf6
--- /dev/null
+++ b/applications/gesture_recognition/src/bsp/sensor/imu/bsp_imu.c
@@ -0,0 +1,122 @@
+#include "bsp_imu.h"
+
+#include
+#include
+#include
+#include
+
+//////////////////////////////////////////////////////////////////////////////
+
+static struct
+{
+ bool initialized;
+ bsp_generic_cb_t data_ready_cb;
+ const struct device* dev;
+} imu_ctx_ = {0};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void data_read_timer_handler(struct k_timer *timer)
+{
+ (void) timer;
+ if (imu_ctx_.data_ready_cb)
+ {
+ imu_ctx_.data_ready_cb();
+ }
+}
+
+K_TIMER_DEFINE(data_ready_timer_, data_read_timer_handler, NULL);
+
+//////////////////////////////////////////////////////////////////////////////
+
+bsp_status_t bsp_imu_init(const bsp_imu_config_t* p_config,
+ bsp_generic_cb_t data_ready_cb)
+{
+ BSP_NULL_CHECK(p_config);
+
+ if (imu_ctx_.dev == NULL)
+ imu_ctx_.dev = DEVICE_DT_GET_ONE(bosch_bmi270);
+
+ BSP_RETURN_IF(imu_ctx_.dev == NULL, BSP_STATUS_HARDWARE_ERROR);
+
+ int res = 0;
+ struct sensor_value full_scale = {0};
+ struct sensor_value sampling_freq = {0};
+ struct sensor_value oversampling = {0};
+
+ /* Setting scale in G, due to loss of precision if the SI unit m/s^2
+ * is used
+ */
+ full_scale.val1 = p_config->accel_fs_g; /* G */
+ full_scale.val2 = 0;
+ sampling_freq.val1 = p_config->data_rate_hz; /* Hz. Performance mode */
+ sampling_freq.val2 = 0;
+ oversampling.val1 = 1; /* Normal mode */
+ oversampling.val2 = 0;
+
+ res = sensor_attr_set(imu_ctx_.dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_FULL_SCALE, &full_scale);
+ BSP_RETURN_IF(res != 0, BSP_STATUS_HARDWARE_ERROR);
+
+ res = sensor_attr_set(imu_ctx_.dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_OVERSAMPLING, &oversampling);
+ BSP_RETURN_IF(res != 0, BSP_STATUS_HARDWARE_ERROR);
+
+ res = sensor_attr_set(imu_ctx_.dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &sampling_freq);
+ BSP_RETURN_IF(res != 0, BSP_STATUS_HARDWARE_ERROR);
+
+ /* Setting scale in degrees/s to match the sensor scale */
+ full_scale.val1 = p_config->gyro_fs_dps; /* dps */
+ full_scale.val2 = 0;
+ sampling_freq.val1 = p_config->data_rate_hz; /* Hz. Performance mode */
+ sampling_freq.val2 = 0;
+ oversampling.val1 = 1; /* Normal mode */
+ oversampling.val2 = 0;
+
+ res = sensor_attr_set(imu_ctx_.dev, SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_FULL_SCALE, &full_scale);
+ BSP_RETURN_IF(res != 0, BSP_STATUS_HARDWARE_ERROR);
+
+ res = sensor_attr_set(imu_ctx_.dev, SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_OVERSAMPLING, &oversampling);
+ BSP_RETURN_IF(res != 0, BSP_STATUS_HARDWARE_ERROR);
+
+ imu_ctx_.data_ready_cb = data_ready_cb;
+ const uint32_t data_ready_timer_period = 1000 / p_config->data_rate_hz;
+ k_timer_start(&data_ready_timer_, K_MSEC(data_ready_timer_period), K_MSEC(data_ready_timer_period));
+
+ /* Set sampling frequency last as this also sets the appropriate
+ * power mode. If already sampling, change sampling frequency to
+ * 0.0Hz before changing other attributes
+ */
+ res = sensor_attr_set(imu_ctx_.dev, SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &sampling_freq);
+ BSP_RETURN_IF(res != 0, BSP_STATUS_HARDWARE_ERROR);
+
+ return BSP_STATUS_SUCCESS;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bsp_status_t bsp_imu_read(bsp_imu_data_t* const p_data)
+{
+ BSP_NULL_CHECK(p_data);
+ BSP_RETURN_IF(imu_ctx_.dev == NULL, BSP_STATUS_HARDWARE_ERROR);
+
+ struct sensor_value acc[3], gyr[3];
+
+ int res = sensor_sample_fetch(imu_ctx_.dev);
+ BSP_RETURN_IF(res != 0, BSP_STATUS_HARDWARE_ERROR);
+
+ res = sensor_channel_get(imu_ctx_.dev, SENSOR_CHAN_ACCEL_XYZ, acc);
+ BSP_RETURN_IF(res != 0, BSP_STATUS_HARDWARE_ERROR);
+
+ res = sensor_channel_get(imu_ctx_.dev, SENSOR_CHAN_GYRO_XYZ, gyr);
+ BSP_RETURN_IF(res != 0, BSP_STATUS_HARDWARE_ERROR);
+
+ for (int i = 0; i < 3; i++)
+ {
+ p_data->accel[i].phys = (float)sensor_value_to_double(&acc[i]);
+ p_data->accel[i].raw = (p_data->accel[i].phys * 1000);
+
+ p_data->gyro[i].phys = (float)sensor_value_to_double(&gyr[i]);
+ p_data->gyro[i].raw = (p_data->gyro[i].phys * 1000);
+ }
+
+ return BSP_STATUS_SUCCESS;
+}
\ No newline at end of file
diff --git a/applications/gesture_recognition/src/bsp/sensor/imu/bsp_imu.h b/applications/gesture_recognition/src/bsp/sensor/imu/bsp_imu.h
new file mode 100644
index 0000000..c77a9f8
--- /dev/null
+++ b/applications/gesture_recognition/src/bsp/sensor/imu/bsp_imu.h
@@ -0,0 +1,92 @@
+/**
+ *
+ * @defgroup bsp_imu Inertial Measurement Unit (IMU)
+ * @{
+ * @ingroup bsp
+ *
+ * @brief This module provides IMU sensor control functions.
+ *
+ */
+#ifndef __BSP_SENSOR_IMU_H__
+#define __BSP_SENSOR_IMU_H__
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** Accelerometer full scale variants */
+#define BSP_IMU_ACCEL_SCALE_2G (2)
+#define BSP_IMU_ACCEL_SCALE_4G (4)
+#define BSP_IMU_ACCEL_SCALE_8G (8)
+#define BSP_IMU_ACCEL_SCALE_16G (16)
+
+/** Gyroscope full scale variants */
+#define BSP_IMU_ACCEL_SCALE_125DPS (125)
+#define BSP_IMU_ACCEL_SCALE_250DPS (250)
+#define BSP_IMU_ACCEL_SCALE_500DPS (500)
+#define BSP_IMU_ACCEL_SCALE_1000DPS (1000)
+#define BSP_IMU_ACCEL_SCALE_2000DPS (2000)
+
+/**
+ * @brief IMU sensor configurations
+ */
+typedef struct bsp_imu_config_s
+{
+ /** Accelerometer full scale in G */
+ int32_t accel_fs_g;
+
+ /** Gyroscope full scale in DPS */
+ int32_t gyro_fs_dps;
+
+ /** IMU data rate in Hz */
+ int32_t data_rate_hz;
+} bsp_imu_config_t;
+
+/** Inertial sensor data */
+typedef struct bsp_imu_data_s
+{
+ /** Accelerometer data */
+ struct
+ {
+ int16_t raw;
+ float phys;
+ } accel[3];
+ /** Gyroscope data */
+ struct
+ {
+ int16_t raw;
+ float phys;
+ } gyro[3];
+} bsp_imu_data_t;
+
+/**
+ * @brief Initialize and start generation of IMU sensor data
+ *
+ * @param p_config IMU configuration settings @ref bsp_imu_config_t
+ * @param data_ready_cb Data ready callback, provided callback will be called when new data sample is ready for reading
+ *
+ * @return Operation status @ref bsp_status_t
+ */
+bsp_status_t bsp_imu_init(const bsp_imu_config_t* p_config,
+ bsp_generic_cb_t data_ready_cb);
+
+/**
+ * @brief Read IMU sensor data
+ *
+ * @param p_data Pointer to data to be filled @ref bsp_imu_data_t
+ *
+ * @return Operation status @ref bsp_status_t
+ */
+bsp_status_t bsp_imu_read(bsp_imu_data_t* const p_data);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __BSP_SENSOR_IMU_H__ */
+
+/**
+ * @}
+ */
diff --git a/applications/gesture_recognition/src/inference_postprocessing.c b/applications/gesture_recognition/src/inference_postprocessing.c
new file mode 100644
index 0000000..7516bfc
--- /dev/null
+++ b/applications/gesture_recognition/src/inference_postprocessing.c
@@ -0,0 +1,161 @@
+// ///////////////////////// Package Header Files ////////////////////////////
+#include "inference_postprocessing.h"
+
+// ////////////////////// Standard C++ Header Files //////////////////////////
+// /////////////////////// Standard C Header Files ///////////////////////////
+#include
+
+///
+#define PREVIOUS_PREDICTION_NUM (3)
+///
+
+//////////////////////////////////////////////////////////////////////////////
+
+typedef struct prediction_ctx_s
+{
+ /** Prediction target */
+ uint16_t target;
+
+ /** Prediction probability */
+ float probability;
+} prediction_ctx_t;
+
+typedef struct prediction_tracer_s
+{
+ /** Current prediction index */
+ int index;
+
+ /** Current prediction target */
+ uint16_t target;
+
+ /** Previous predictions context */
+ prediction_ctx_t prev[PREVIOUS_PREDICTION_NUM];
+} prediction_tracer_t;
+
+/**
+ * @brief Class prediction conditions for postprocessing
+ *
+ */
+typedef struct class_prediction_condition_s
+{
+ /** Minimum number of repetitions of a class for prediction */
+ uint16_t min_repeat_count;
+
+ /** Minimum probability threshold for prediction */
+ float probability_threshold;
+} class_prediction_condition_t;
+
+//////////////////////////////////////////////////////////////////////////////
+
+static const class_prediction_condition_t* get_class_condition_(uint8_t predicted_target);
+static const char* get_name_by_target_(uint8_t predicted_target);
+
+//////////////////////////////////////////////////////////////////////////////
+
+void inference_postprocess(const uint16_t predicted_target,
+ const float prob,
+ const bool do_postprocessing,
+ inference_postprocess_cb_t callback)
+{
+ uint16_t target = predicted_target;
+ float probability = prob;
+
+ static prediction_tracer_t tracer_ = {0U};
+
+ if (do_postprocessing) {
+
+ if ((target == CLASS_LABEL_UNKNOWN) || (target == CLASS_LABEL_IDLE)) {
+ /** Reset tracer for UNKNOWN and IDLE classes */
+ tracer_.index = 0;
+ tracer_.target = target;
+ } else {
+ if (tracer_.index >= PREVIOUS_PREDICTION_NUM)
+ tracer_.index = 0;
+
+ /** Reset tracer if predicted class is not the same as previous */
+ if (tracer_.target != target) {
+ tracer_.index = 0;
+ tracer_.target = target;
+ }
+
+ tracer_.prev[tracer_.index].probability = probability;
+ tracer_.index++;
+
+ const class_prediction_condition_t* class_condition = get_class_condition_(target);
+ if (class_condition == NULL) {
+ return;
+ }
+
+ /** Сlass is labled as CLASS_LABEL_UNKNOWN if the number of repetitions does not exceed the threshold */
+ if (tracer_.index >= class_condition->min_repeat_count) {
+ /** Calculate average probability for last N predictions of the same class */
+ float average_prob = 0.0f;
+
+ for (int i = 0; i < tracer_.index; ++i)
+ average_prob += tracer_.prev[i].probability;
+
+ average_prob = average_prob / tracer_.index;
+
+ /** If average probability is less the class probability threshold,
+ * the class is labled as CLASS_LABEL_UNKNOWN */
+ if (average_prob < class_condition->probability_threshold)
+ target = CLASS_LABEL_UNKNOWN;
+ else
+ probability = average_prob;
+
+ /** Reset tracer index for non-repetative classes */
+ if ((target != CLASS_LABEL_ROTATION_RIGHT) || (target != CLASS_LABEL_ROTATION_LEFT))
+ tracer_.index = 0;
+ } else {
+ target = CLASS_LABEL_UNKNOWN;
+ }
+ }
+ }
+
+ /** Provide result to user callback */
+ if (callback)
+ callback(target, probability, get_name_by_target_(target), !do_postprocessing);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static const char* get_name_by_target_(uint8_t predicted_target)
+{
+
+ static const char* LABEL_VS_NAME[] =
+ {
+ [CLASS_LABEL_IDLE] = "IDLE",
+ [CLASS_LABEL_UNKNOWN] = "UNKNOWN",
+ [CLASS_LABEL_SWIPE_LEFT] = "SWIPE LEFT",
+ [CLASS_LABEL_SWIPE_RIGHT] = "SWIPE RIGHT",
+ [CLASS_LABEL_DOUBLE_SHAKE] = "DOUBLE SHAKE",
+ [CLASS_LABEL_DOUBLE_THUMB] = "DOUBLE THUMB",
+ [CLASS_LABEL_ROTATION_RIGHT] = "ROTATION RIGHT",
+ [CLASS_LABEL_ROTATION_LEFT] = "ROTATION LEFT"
+ };
+
+ static const uint8_t LABELS_CNT = sizeof(LABEL_VS_NAME) / sizeof(LABEL_VS_NAME[0]);
+
+ return (predicted_target < LABELS_CNT) ? LABEL_VS_NAME[predicted_target] : NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static const class_prediction_condition_t* get_class_condition_(uint8_t predicted_target)
+{
+ static const class_prediction_condition_t LABEL_VS_CONFIG[] =
+ {
+ [CLASS_LABEL_IDLE] = {0, 0.0},
+ [CLASS_LABEL_UNKNOWN] = {0, 0.0},
+ [CLASS_LABEL_SWIPE_LEFT] = {2, 0.8},
+ [CLASS_LABEL_SWIPE_RIGHT] = {2, 0.8},
+ [CLASS_LABEL_DOUBLE_SHAKE] = {2, 0.7},
+ [CLASS_LABEL_DOUBLE_THUMB] = {2, 0.7},
+ [CLASS_LABEL_ROTATION_RIGHT] = {2, 0.7},
+ [CLASS_LABEL_ROTATION_LEFT] = {2, 0.7},
+ };
+
+ static const uint8_t LABELS_CNT = sizeof(LABEL_VS_CONFIG) / sizeof(LABEL_VS_CONFIG[0]);
+
+ return (predicted_target < LABELS_CNT) ? &LABEL_VS_CONFIG[predicted_target] : NULL;
+}
diff --git a/applications/gesture_recognition/src/inference_postprocessing.h b/applications/gesture_recognition/src/inference_postprocessing.h
new file mode 100644
index 0000000..5a12740
--- /dev/null
+++ b/applications/gesture_recognition/src/inference_postprocessing.h
@@ -0,0 +1,52 @@
+/*
+* Copyright (c) 2021 Nordic Semiconductor ASA
+* SPDX-License-Identifier: Apache-2.0
+*/
+#ifndef INFERENCE_POSTPROCESSING_H__
+#define INFERENCE_POSTPROCESSING_H__
+
+#include
+#include
+
+typedef enum
+{
+ CLASS_LABEL_IDLE, ///< CLASS_LABEL_IDLE
+ CLASS_LABEL_UNKNOWN, ///< CLASS_LABEL_UNKNOWN
+ CLASS_LABEL_SWIPE_RIGHT, ///< CLASS_LABEL_SWIPE_RIGHT
+ CLASS_LABEL_SWIPE_LEFT, ///< CLASS_LABEL_SWIPE_LEFT
+ CLASS_LABEL_DOUBLE_SHAKE, ///< CLASS_LABEL_DOUBLE_SHAKE
+ CLASS_LABEL_DOUBLE_THUMB, ///< CLASS_LABEL_DOUBLE_THUMB
+ CLASS_LABEL_ROTATION_RIGHT, ///< CLASS_LABEL_ROTATION_RIGHT
+ CLASS_LABEL_ROTATION_LEFT, /// < CLASS_LABEL_ROTATION_LEFT
+} class_label_t;
+
+/**
+ * @brief Inference Result (prediction) postprocessing callback
+ *
+ * @param[in] class_label Label of the predicted class @ref class_label_t
+ * @param[in] probability Probability of the predicted class
+ * @param[in] class_name Name of predicted class, null-terminated string
+ * @param[in] is_raw If true the postprocessing logic was not applied to this prediction
+ *
+ */
+typedef void (*inference_postprocess_cb_t)(const class_label_t class_label,
+ const float probability,
+ const char* class_name,
+ const bool is_raw);
+
+/**
+ * @brief Postprocess the Neuton library RAW inference output
+ *
+ * @param[in] predicted_target Predicted target(class)
+ * @param[in] probability Predicted probability of the target
+ * @param[in] do_postprocessing If false, no postprocessing is applied and the raw prediction goes to the user callback unchanged
+ * @param[in] callback Inference Result (prediction) ready user callback, @ref inference_postprocess_cb_t
+ */
+void inference_postprocess(const uint16_t predicted_target,
+ const float probability,
+ const bool do_postprocessing,
+ inference_postprocess_cb_t callback);
+
+
+
+#endif /* INFERENCE_POSTPROCESSING_H__ */
diff --git a/applications/gesture_recognition/src/main.c b/applications/gesture_recognition/src/main.c
new file mode 100644
index 0000000..37a9b28
--- /dev/null
+++ b/applications/gesture_recognition/src/main.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2018 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include