Audio working
This commit is contained in:
44
.gitignore
vendored
44
.gitignore
vendored
@@ -1,23 +1,23 @@
|
|||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|
||||||
# esp-idf built binaries
|
# esp-idf built binaries
|
||||||
build/
|
build/
|
||||||
build_*_*/
|
build_*_*/
|
||||||
#sdkconfig
|
#sdkconfig
|
||||||
|
|
||||||
# idf-ci build run output
|
# idf-ci build run output
|
||||||
build_summary_*.xml
|
build_summary_*.xml
|
||||||
app_info_*.txt
|
app_info_*.txt
|
||||||
size_info_*.txt
|
size_info_*.txt
|
||||||
|
|
||||||
# pytest-embedded log folder
|
# pytest-embedded log folder
|
||||||
pytest_embedded_log/
|
pytest_embedded_log/
|
||||||
|
|
||||||
# idf-component-manager output
|
# idf-component-manager output
|
||||||
dependencies.lock
|
dependencies.lock
|
||||||
|
|
||||||
.devcontainer
|
.devcontainer
|
||||||
|
|
||||||
managed_components
|
managed_components
|
||||||
components
|
components
|
||||||
23
.vscode/c_cpp_properties.json
vendored
23
.vscode/c_cpp_properties.json
vendored
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "ESP-IDF",
|
|
||||||
"compilerPath": "${config:idf.toolsPathWin}\\tools\\xtensa-esp-elf\\esp-13.2.0_20240530\\xtensa-esp-elf\\bin\\xtensa-esp32-elf-gcc.exe",
|
|
||||||
"compileCommands": "${config:idf.buildPath}/compile_commands.json",
|
|
||||||
"includePath": [
|
|
||||||
"${config:idf.espIdfPath}/components/**",
|
|
||||||
"${config:idf.espIdfPathWin}/components/**",
|
|
||||||
"${workspaceFolder}/**"
|
|
||||||
],
|
|
||||||
"browse": {
|
|
||||||
"path": [
|
|
||||||
"${config:idf.espIdfPath}/components",
|
|
||||||
"${config:idf.espIdfPathWin}/components",
|
|
||||||
"${workspaceFolder}"
|
|
||||||
],
|
|
||||||
"limitSymbolsToIncludedHeaders": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"version": 4
|
|
||||||
}
|
|
||||||
15
.vscode/launch.json
vendored
15
.vscode/launch.json
vendored
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "gdbtarget",
|
|
||||||
"request": "attach",
|
|
||||||
"name": "Eclipse CDT GDB Adapter"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "espidf",
|
|
||||||
"name": "Launch",
|
|
||||||
"request": "launch"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
42
.vscode/settings.json
vendored
42
.vscode/settings.json
vendored
@@ -1,41 +1,3 @@
|
|||||||
{
|
{
|
||||||
"C_Cpp.intelliSenseEngine": "default",
|
"idf.pythonInstallPath": "/usr/bin/python"
|
||||||
"idf.espIdfPathWin": "C:\\Users\\brent.RPX\\esp\\v5.3.1\\esp-idf",
|
}
|
||||||
"idf.openOcdConfigs": [
|
|
||||||
"board/esp32-wrover-kit-3.3v.cfg"
|
|
||||||
],
|
|
||||||
"idf.portWin": "COM4",
|
|
||||||
"idf.toolsPathWin": "C:\\Users\\brent.RPX\\.espressif\\tools",
|
|
||||||
"idf.flashType": "UART",
|
|
||||||
"files.associations": {
|
|
||||||
"esp_system.h": "c",
|
|
||||||
"task.h": "c",
|
|
||||||
"bt_app_core.h": "c",
|
|
||||||
"math.h": "c",
|
|
||||||
"esp_log.h": "c",
|
|
||||||
"freertos.h": "c",
|
|
||||||
"queue.h": "c",
|
|
||||||
"unistd.h": "c",
|
|
||||||
"inttypes.h": "c",
|
|
||||||
"esp_idf_version.h": "c",
|
|
||||||
"timers.h": "c",
|
|
||||||
"lvgl.h": "c",
|
|
||||||
"esp_timer.h": "c",
|
|
||||||
"esp_bt_device.h": "c",
|
|
||||||
"esp_lvgl_port.h": "c",
|
|
||||||
"lv_global.h": "c",
|
|
||||||
"stdio.h": "c",
|
|
||||||
"compare": "c",
|
|
||||||
"cstdint": "c",
|
|
||||||
"spi_master.h": "c",
|
|
||||||
"bitset": "c",
|
|
||||||
"format": "c",
|
|
||||||
"system.h": "c",
|
|
||||||
"esp_lcd_panel_ops.h": "c",
|
|
||||||
"semphr.h": "c",
|
|
||||||
"lv_spinbox.h": "c",
|
|
||||||
"lv_slider.h": "c",
|
|
||||||
"lv_menu.h": "c"
|
|
||||||
},
|
|
||||||
"git.ignoreLimitWarning": true
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# The following lines of boilerplate have to be in your project's
|
# The following lines of boilerplate have to be in your project's
|
||||||
# CMakeLists in this exact order for cmake to work correctly
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
project(level-shot)
|
project(level-shot)
|
||||||
|
|||||||
90
README.md
Normal file
90
README.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
| Supported Targets | ESP32 |
|
||||||
|
| ----------------- | ----- |
|
||||||
|
|
||||||
|
A2DP-SOURCE EXAMPLE
|
||||||
|
========================
|
||||||
|
|
||||||
|
Example of A2DP audio source role
|
||||||
|
|
||||||
|
This is the example of using Advanced Audio Distribution Profile (A2DP) APIs to transmit audio stream. Application can take advantage of this example to implement portable audio players or microphones to transmit audio stream to A2DP sink devices.
|
||||||
|
|
||||||
|
## How to use this example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
This example is able to run on any commonly available ESP32 development board, and is supposed to connect to [A2DP sink example](../a2dp_sink) in ESP-IDF.
|
||||||
|
|
||||||
|
### Configure the project
|
||||||
|
|
||||||
|
```
|
||||||
|
idf.py menuconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
* Enable Classic Bluetooth and A2DP under Component config --> Bluetooth --> Bluedroid Enable
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Build the project and flash it to the board, then run monitor tool to view serial output.
|
||||||
|
|
||||||
|
```
|
||||||
|
idf.py -p PORT flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(Replace PORT with the name of the serial port to use.)
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
For the first step, this example performs device discovery to search for a target device (A2DP sink) whose device name is "ESP_SPEAKER" and whose "Rendering" bit of its Service Class field is set in its Class of Device (COD). If a candidate target is found, the local device will initiate connection with it.
|
||||||
|
|
||||||
|
After connection with A2DP sink is established, the example performs the following running loop 1-2-3-4-1:
|
||||||
|
1. audio transmission starts and lasts for a while
|
||||||
|
2. audio transmission stops
|
||||||
|
3. disconnect with target device
|
||||||
|
4. reconnect to target device
|
||||||
|
|
||||||
|
The example implements an event loop triggered by a periodic "heart beat" timer and events from Bluetooth protocol stack callback functions.
|
||||||
|
|
||||||
|
After the local device discovers the target device and initiates connection, there will be logging message like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
I (4090) BT_AV: Found a target device, address xx:xx:xx:xx:xx:xx, name ESP_SPEAKER
|
||||||
|
I (4090) BT_AV: Cancel device discovery ...
|
||||||
|
I (4100) BT_AV: Device discovery stopped.
|
||||||
|
I (4100) BT_AV: a2dp connecting to peer: ESP_SPEAKER
|
||||||
|
```
|
||||||
|
|
||||||
|
If connection is set up successfully, there will be such message:
|
||||||
|
|
||||||
|
```
|
||||||
|
I (5100) BT_AV: a2dp connected
|
||||||
|
```
|
||||||
|
|
||||||
|
Starting of audio transmission has the following notification message:
|
||||||
|
|
||||||
|
```
|
||||||
|
I (10880) BT_AV: a2dp media ready checking ...
|
||||||
|
...
|
||||||
|
I (10880) BT_AV: a2dp media ready, starting ...
|
||||||
|
...
|
||||||
|
I (11400) BT_AV: a2dp media start successfully.
|
||||||
|
```
|
||||||
|
|
||||||
|
Stop of audio transmission, and disconnection with remote device generate the following notification message:
|
||||||
|
|
||||||
|
```
|
||||||
|
I (110880) BT_AV: a2dp media stopping...
|
||||||
|
...
|
||||||
|
I (110920) BT_AV: a2dp media stopped successfully, disconnecting...
|
||||||
|
...
|
||||||
|
I (111040) BT_AV: a2dp disconnected
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
* For current stage, the supported audio codec in ESP32 A2DP is SBC. SBC audio stream is encoded from PCM data normally formatted as 44.1kHz sampling rate, two-channel 16-bit sample data.
|
||||||
|
* The raw PCM media stream in the example is generated by a sequence of random number, so the sound played on the sink side will be piercing noise.
|
||||||
|
* As a usage limitation, ESP32 A2DP source can support at most one connection with remote A2DP sink devices. Also, A2DP source cannot be used together with A2DP sink at the same time, but can be used with other profiles such as SPP and HFP.
|
||||||
|
* Usually, the `ACL_CONN_NUM` is set to `2` but not `1` as one is used for a2dp connection and the other is used for avrcp connection.
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
idf_component_register(SRCS "system.c" "bubble.c" "keypad.c" "main.c"
|
idf_component_register(SRCS "bt_app.c" "system.c" "bubble.c" "keypad.c" "main.c"
|
||||||
"bt_app_core.c"
|
|
||||||
"gui.c"
|
"gui.c"
|
||||||
"lsm6dsv.c"
|
"lsm6dsv.c"
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES "driver"
|
REQUIRES "driver"
|
||||||
"esp_lcd"
|
"esp_lcd"
|
||||||
"lvgl"
|
"lvgl"
|
||||||
"esp_lvgl_port"
|
"esp_lvgl_port"
|
||||||
"esp_timer"
|
"esp_timer"
|
||||||
"nvs_flash"
|
"nvs_flash"
|
||||||
"bt")
|
"bt")
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
menu "A2DP Example Configuration"
|
menu "A2DP Example Configuration"
|
||||||
config EXAMPLE_SSP_ENABLED
|
config EXAMPLE_SSP_ENABLED
|
||||||
bool "Secure Simple Pairing"
|
bool "Secure Simple Pairing"
|
||||||
depends on BT_CLASSIC_ENABLED
|
depends on BT_CLASSIC_ENABLED
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
This enables the Secure Simple Pairing. If disable this option,
|
This enables the Secure Simple Pairing. If disable this option,
|
||||||
Bluedroid will only support Legacy Pairing
|
Bluedroid will only support Legacy Pairing
|
||||||
endmenu
|
endmenu
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,70 +1,70 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __BT_APP_CORE_H__
|
#ifndef __BT_APP_CORE_H__
|
||||||
#define __BT_APP_CORE_H__
|
#define __BT_APP_CORE_H__
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
/* log tag */
|
/* log tag */
|
||||||
#define BT_APP_CORE_TAG "BT_APP_CORE"
|
#define BT_APP_CORE_TAG "BT_APP_CORE"
|
||||||
|
|
||||||
/* signal for dispatcher */
|
/* signal for dispatcher */
|
||||||
#define BT_APP_SIG_WORK_DISPATCH (0x01)
|
#define BT_APP_SIG_WORK_DISPATCH (0x01)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief handler for the dispatched work
|
* @brief handler for the dispatched work
|
||||||
*
|
*
|
||||||
* @param [in] event message event id
|
* @param [in] event message event id
|
||||||
* @param [in] param pointer to the parameter
|
* @param [in] param pointer to the parameter
|
||||||
*/
|
*/
|
||||||
typedef void (* bt_app_cb_t) (uint16_t event, void *param);
|
typedef void (* bt_app_cb_t) (uint16_t event, void *param);
|
||||||
|
|
||||||
/* message to be sent */
|
/* message to be sent */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t sig; /*!< signal to bt_app_task */
|
uint16_t sig; /*!< signal to bt_app_task */
|
||||||
uint16_t event; /*!< message event id */
|
uint16_t event; /*!< message event id */
|
||||||
bt_app_cb_t cb; /*!< context switch callback */
|
bt_app_cb_t cb; /*!< context switch callback */
|
||||||
void *param; /*!< parameter area needs to be last */
|
void *param; /*!< parameter area needs to be last */
|
||||||
} bt_app_msg_t;
|
} bt_app_msg_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief parameter deep-copy function to be customized
|
* @brief parameter deep-copy function to be customized
|
||||||
*
|
*
|
||||||
* @param [in] p_dest pointer to the destination
|
* @param [in] p_dest pointer to the destination
|
||||||
* @param [in] p_src pointer to the source
|
* @param [in] p_src pointer to the source
|
||||||
* @param [in] len data length in byte
|
* @param [in] len data length in byte
|
||||||
*/
|
*/
|
||||||
typedef void (* bt_app_copy_cb_t) (void *p_dest, void *p_src, int len);
|
typedef void (* bt_app_copy_cb_t) (void *p_dest, void *p_src, int len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief work dispatcher for the application task
|
* @brief work dispatcher for the application task
|
||||||
*
|
*
|
||||||
* @param [in] p_cback handler for the dispatched work (event handler)
|
* @param [in] p_cback handler for the dispatched work (event handler)
|
||||||
* @param [in] event message event id
|
* @param [in] event message event id
|
||||||
* @param [in] p_params pointer to the parameter
|
* @param [in] p_params pointer to the parameter
|
||||||
* @param [in] param_len length of the parameter
|
* @param [in] param_len length of the parameter
|
||||||
* @param [in] p_copy_cback parameter deep-copy function
|
* @param [in] p_copy_cback parameter deep-copy function
|
||||||
*
|
*
|
||||||
* @return true if work dispatch successfully, false otherwise
|
* @return true if work dispatch successfully, false otherwise
|
||||||
*/
|
*/
|
||||||
bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback);
|
bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief start up the application task
|
* @brief start up the application task
|
||||||
*/
|
*/
|
||||||
void bt_app_task_start_up(void);
|
void bt_app_task_start_up(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief shut down the application task
|
* @brief shut down the application task
|
||||||
*/
|
*/
|
||||||
void bt_app_task_shut_down(void);
|
void bt_app_task_shut_down(void);
|
||||||
|
|
||||||
void bt_app_init(void);
|
void bt_app_init(void);
|
||||||
|
|
||||||
#endif /* __BT_APP_CORE_H__ */
|
#endif /* __BT_APP_CORE_H__ */
|
||||||
264
main/bubble.c
264
main/bubble.c
@@ -1,132 +1,132 @@
|
|||||||
#include "bubble.h"
|
#include "bubble.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float min_val;
|
float min_val;
|
||||||
float max_val;
|
float max_val;
|
||||||
float cur_val;
|
float cur_val;
|
||||||
} bubble_level_dsc_t;
|
} bubble_level_dsc_t;
|
||||||
|
|
||||||
|
|
||||||
static void bubble_draw_cb(lv_event_t * e)
|
static void bubble_draw_cb(lv_event_t * e)
|
||||||
{
|
{
|
||||||
lvgl_port_lock(0);
|
lvgl_port_lock(0);
|
||||||
|
|
||||||
lv_obj_t * obj = lv_event_get_target(e);
|
lv_obj_t * obj = lv_event_get_target(e);
|
||||||
bubble_level_dsc_t * d = lv_obj_get_user_data(obj);
|
bubble_level_dsc_t * d = lv_obj_get_user_data(obj);
|
||||||
if(!d) return;
|
if(!d) return;
|
||||||
|
|
||||||
// 1) Get the LVGL draw layer (clipping is automatic)
|
// 1) Get the LVGL draw layer (clipping is automatic)
|
||||||
lv_layer_t * layer = lv_event_get_layer(e);
|
lv_layer_t * layer = lv_event_get_layer(e);
|
||||||
|
|
||||||
// 2) Figure out the object's inner rectangle
|
// 2) Figure out the object's inner rectangle
|
||||||
lv_area_t coords;
|
lv_area_t coords;
|
||||||
lv_obj_get_coords(obj, &coords);
|
lv_obj_get_coords(obj, &coords);
|
||||||
lv_area_t inner = {
|
lv_area_t inner = {
|
||||||
.x1 = coords.x1 + 2, .y1 = coords.y1 + 2,
|
.x1 = coords.x1 + 2, .y1 = coords.y1 + 2,
|
||||||
.x2 = coords.x2 - 2, .y2 = coords.y2 - 2
|
.x2 = coords.x2 - 2, .y2 = coords.y2 - 2
|
||||||
};
|
};
|
||||||
|
|
||||||
// 3) Draw the border
|
// 3) Draw the border
|
||||||
lv_draw_rect_dsc_t rect_dsc;
|
lv_draw_rect_dsc_t rect_dsc;
|
||||||
lv_draw_rect_dsc_init(&rect_dsc);
|
lv_draw_rect_dsc_init(&rect_dsc);
|
||||||
rect_dsc.border_color = lv_color_hex(0xAAAAAA);
|
rect_dsc.border_color = lv_color_hex(0xAAAAAA);
|
||||||
rect_dsc.border_width = 2;
|
rect_dsc.border_width = 2;
|
||||||
rect_dsc.bg_opa = LV_OPA_TRANSP;
|
rect_dsc.bg_opa = LV_OPA_TRANSP;
|
||||||
lv_draw_rect(layer, &rect_dsc, &inner);
|
lv_draw_rect(layer, &rect_dsc, &inner);
|
||||||
|
|
||||||
// 4) Draw guide lines at the center
|
// 4) Draw guide lines at the center
|
||||||
lv_coord_t mid_x = (inner.x1 + inner.x2) / 2;
|
lv_coord_t mid_x = (inner.x1 + inner.x2) / 2;
|
||||||
lv_coord_t mid_y = (inner.y1 + inner.y2) / 2;
|
lv_coord_t mid_y = (inner.y1 + inner.y2) / 2;
|
||||||
|
|
||||||
lv_draw_line_dsc_t line_dsc;
|
lv_draw_line_dsc_t line_dsc;
|
||||||
lv_draw_line_dsc_init(&line_dsc);
|
lv_draw_line_dsc_init(&line_dsc);
|
||||||
line_dsc.color = lv_color_hex(0x444444);
|
line_dsc.color = lv_color_hex(0x444444);
|
||||||
line_dsc.width = 1;
|
line_dsc.width = 1;
|
||||||
|
|
||||||
// Set up points and convert to 'precise' type
|
// Set up points and convert to 'precise' type
|
||||||
lv_point_t tl = { inner.x1 + 10, mid_y };
|
lv_point_t tl = { inner.x1 + 10, mid_y };
|
||||||
lv_point_t tr = { inner.x2 - 10, mid_y };
|
lv_point_t tr = { inner.x2 - 10, mid_y };
|
||||||
line_dsc.p1 = lv_point_to_precise(&tl);
|
line_dsc.p1 = lv_point_to_precise(&tl);
|
||||||
line_dsc.p2 = lv_point_to_precise(&tr);
|
line_dsc.p2 = lv_point_to_precise(&tr);
|
||||||
lv_draw_line(layer, &line_dsc);
|
lv_draw_line(layer, &line_dsc);
|
||||||
|
|
||||||
lv_point_t tt = { mid_x, inner.y1 + 10 };
|
lv_point_t tt = { mid_x, inner.y1 + 10 };
|
||||||
lv_point_t tb = { mid_x, inner.y2 - 10 };
|
lv_point_t tb = { mid_x, inner.y2 - 10 };
|
||||||
line_dsc.p1 = lv_point_to_precise(&tt);
|
line_dsc.p1 = lv_point_to_precise(&tt);
|
||||||
line_dsc.p2 = lv_point_to_precise(&tb);
|
line_dsc.p2 = lv_point_to_precise(&tb);
|
||||||
lv_draw_line(layer, &line_dsc);
|
lv_draw_line(layer, &line_dsc);
|
||||||
|
|
||||||
// 5) Compute bubble position (normalized between min_val → max_val)
|
// 5) Compute bubble position (normalized between min_val → max_val)
|
||||||
float norm = (d->cur_val - d->min_val) / (d->max_val - d->min_val);
|
float norm = (d->cur_val - d->min_val) / (d->max_val - d->min_val);
|
||||||
norm = norm < 0 ? 0 : (norm > 1 ? 1 : norm);
|
norm = norm < 0 ? 0 : (norm > 1 ? 1 : norm);
|
||||||
|
|
||||||
const lv_coord_t bubble_d = 16;
|
const lv_coord_t bubble_d = 16;
|
||||||
lv_coord_t avail_w = (inner.x2 - inner.x1 + 1) - bubble_d;
|
lv_coord_t avail_w = (inner.x2 - inner.x1 + 1) - bubble_d;
|
||||||
lv_coord_t bx = inner.x1 + (lv_coord_t)(norm * avail_w);
|
lv_coord_t bx = inner.x1 + (lv_coord_t)(norm * avail_w);
|
||||||
lv_coord_t by = mid_y - (bubble_d / 2);
|
lv_coord_t by = mid_y - (bubble_d / 2);
|
||||||
|
|
||||||
// 6) Draw the bubble as a filled circle (rounded rect)
|
// 6) Draw the bubble as a filled circle (rounded rect)
|
||||||
lv_draw_rect_dsc_t circ_dsc;
|
lv_draw_rect_dsc_t circ_dsc;
|
||||||
lv_draw_rect_dsc_init(&circ_dsc);
|
lv_draw_rect_dsc_init(&circ_dsc);
|
||||||
circ_dsc.bg_color = lv_color_hex(0x00CC00);
|
circ_dsc.bg_color = lv_color_hex(0x00CC00);
|
||||||
circ_dsc.bg_opa = LV_OPA_COVER;
|
circ_dsc.bg_opa = LV_OPA_COVER;
|
||||||
circ_dsc.radius = bubble_d / 2;
|
circ_dsc.radius = bubble_d / 2;
|
||||||
circ_dsc.border_opa = LV_OPA_TRANSP;
|
circ_dsc.border_opa = LV_OPA_TRANSP;
|
||||||
|
|
||||||
lv_area_t circ_area = {
|
lv_area_t circ_area = {
|
||||||
.x1 = bx,
|
.x1 = bx,
|
||||||
.y1 = by,
|
.y1 = by,
|
||||||
.x2 = bx + bubble_d - 1,
|
.x2 = bx + bubble_d - 1,
|
||||||
.y2 = by + bubble_d - 1
|
.y2 = by + bubble_d - 1
|
||||||
};
|
};
|
||||||
lv_draw_rect(layer, &circ_dsc, &circ_area);
|
lv_draw_rect(layer, &circ_dsc, &circ_area);
|
||||||
|
|
||||||
lvgl_port_unlock();
|
lvgl_port_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
lv_obj_t * bubble_create(lv_obj_t * parent,
|
lv_obj_t * bubble_create(lv_obj_t * parent,
|
||||||
lv_coord_t width, lv_coord_t height,
|
lv_coord_t width, lv_coord_t height,
|
||||||
float min_val, float max_val, float initial)
|
float min_val, float max_val, float initial)
|
||||||
{
|
{
|
||||||
// 1) Create a container object (no special style)
|
// 1) Create a container object (no special style)
|
||||||
lv_obj_t * obj = lv_obj_create(parent);
|
lv_obj_t * obj = lv_obj_create(parent);
|
||||||
lv_obj_set_size(obj, width, height);
|
lv_obj_set_size(obj, width, height);
|
||||||
|
|
||||||
// (Optional) center it or let the caller position it:
|
// (Optional) center it or let the caller position it:
|
||||||
// lv_obj_center(obj);
|
// lv_obj_center(obj);
|
||||||
|
|
||||||
// 2) Allocate or attach our descriptor
|
// 2) Allocate or attach our descriptor
|
||||||
// In LVGL v9 you can use USER_DATA: need to enable LV_USE_USER_DATA = 1
|
// In LVGL v9 you can use USER_DATA: need to enable LV_USE_USER_DATA = 1
|
||||||
bubble_level_dsc_t * dsc = lv_malloc(sizeof(bubble_level_dsc_t));
|
bubble_level_dsc_t * dsc = lv_malloc(sizeof(bubble_level_dsc_t));
|
||||||
if(!dsc) return NULL; /* handle out-of-memory */
|
if(!dsc) return NULL; /* handle out-of-memory */
|
||||||
dsc->min_val = min_val;
|
dsc->min_val = min_val;
|
||||||
dsc->max_val = max_val;
|
dsc->max_val = max_val;
|
||||||
dsc->cur_val = initial;
|
dsc->cur_val = initial;
|
||||||
lv_obj_set_user_data(obj, dsc);
|
lv_obj_set_user_data(obj, dsc);
|
||||||
|
|
||||||
// 3) Register our draw callback
|
// 3) Register our draw callback
|
||||||
lv_obj_add_event_cb(obj, bubble_draw_cb, LV_EVENT_DRAW_MAIN, NULL);
|
lv_obj_add_event_cb(obj, bubble_draw_cb, LV_EVENT_DRAW_MAIN, NULL);
|
||||||
|
|
||||||
// 4) Make sure the object does NOT get a default background fill
|
// 4) Make sure the object does NOT get a default background fill
|
||||||
lv_obj_set_style_bg_opa(obj, LV_OPA_TRANSP, 0);
|
lv_obj_set_style_bg_opa(obj, LV_OPA_TRANSP, 0);
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void bubble_setValue(lv_obj_t * bubble, float v)
|
void bubble_setValue(lv_obj_t * bubble, float v)
|
||||||
{
|
{
|
||||||
lvgl_port_lock(0);
|
lvgl_port_lock(0);
|
||||||
// 1) Retrieve the descriptor
|
// 1) Retrieve the descriptor
|
||||||
bubble_level_dsc_t * d = lv_obj_get_user_data(bubble);
|
bubble_level_dsc_t * d = lv_obj_get_user_data(bubble);
|
||||||
if(!d) return;
|
if(!d) return;
|
||||||
|
|
||||||
// 2) Update the stored value
|
// 2) Update the stored value
|
||||||
d->cur_val = v;
|
d->cur_val = v;
|
||||||
|
|
||||||
// 3) Tell LVGL that object must be re‐drawn
|
// 3) Tell LVGL that object must be re‐drawn
|
||||||
lv_obj_invalidate(bubble);
|
lv_obj_invalidate(bubble);
|
||||||
|
|
||||||
lvgl_port_unlock();
|
lvgl_port_unlock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
#ifndef BUBBLE_H
|
#ifndef BUBBLE_H
|
||||||
#define BUBBLE_H
|
#define BUBBLE_H
|
||||||
|
|
||||||
#include "lvgl.h"
|
#include "lvgl.h"
|
||||||
#include "esp_lvgl_port.h"
|
#include "esp_lvgl_port.h"
|
||||||
|
|
||||||
lv_obj_t * bubble_create(lv_obj_t * parent,
|
lv_obj_t * bubble_create(lv_obj_t * parent,
|
||||||
lv_coord_t width, lv_coord_t height,
|
lv_coord_t width, lv_coord_t height,
|
||||||
float min_val, float max_val, float initial);
|
float min_val, float max_val, float initial);
|
||||||
|
|
||||||
void bubble_setValue(lv_obj_t * bubble, float v);
|
void bubble_setValue(lv_obj_t * bubble, float v);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
55
main/gpio.h
55
main/gpio.h
@@ -1,28 +1,29 @@
|
|||||||
#ifndef GPIO_H
|
#ifndef GPIO_H
|
||||||
#define GPIO_H
|
#define GPIO_H
|
||||||
|
|
||||||
// Define keypad buttons
|
// Define keypad buttons
|
||||||
#define PIN_NUM_BUTTON_0 36
|
#define PIN_NUM_BUTTON_0 36
|
||||||
#define PIN_NUM_BUTTON_1 39
|
#define PIN_NUM_BUTTON_1 39
|
||||||
#define PIN_NUM_LED_1 32
|
#define PIN_NUM_LED_1 32
|
||||||
#define PIN_NUM_nON 26
|
#define PIN_NUM_LED_2 33
|
||||||
|
#define PIN_NUM_nON 26
|
||||||
|
|
||||||
// Define GPIO pins
|
|
||||||
#ifdef DEVKIT
|
// Define GPIO pins
|
||||||
#define PIN_NUM_MOSI 23 // SDA pin for LCD
|
#ifdef DEVKIT
|
||||||
#define PIN_NUM_CLK 18 // SCL pin for LCD
|
#define PIN_NUM_MOSI 23 // SDA pin for LCD
|
||||||
#define PIN_NUM_CS 2 // CS pin
|
#define PIN_NUM_CLK 18 // SCL pin for LCD
|
||||||
#define PIN_NUM_DC 12 // Data/Command pin (RS)
|
#define PIN_NUM_CS 2 // CS pin
|
||||||
#define PIN_NUM_RST 13 // Reset pin
|
#define PIN_NUM_DC 12 // Data/Command pin (RS)
|
||||||
#define PIN_NUM_BK_LIGHT -1 // Backlight control pin, -1 if not used
|
#define PIN_NUM_RST 13 // Reset pin
|
||||||
#else
|
#define PIN_NUM_BK_LIGHT -1 // Backlight control pin, -1 if not used
|
||||||
#define PIN_NUM_MOSI 23 // SDA pin for LCD
|
#else
|
||||||
#define PIN_NUM_CLK 18 // SCL pin for LCD
|
#define PIN_NUM_MOSI 23 // SDA pin for LCD
|
||||||
#define PIN_NUM_CS 12 // CS pin
|
#define PIN_NUM_CLK 18 // SCL pin for LCD
|
||||||
#define PIN_NUM_DC 15 // Data/Command pin (RS)
|
#define PIN_NUM_CS 12 // CS pin
|
||||||
#define PIN_NUM_RST 13 // Reset pin
|
#define PIN_NUM_DC 15 // Data/Command pin (RS)
|
||||||
#define PIN_NUM_BK_LIGHT 5 // Backlight control pin, -1 if not used
|
#define PIN_NUM_RST 13 // Reset pin
|
||||||
#endif
|
#define PIN_NUM_BK_LIGHT 5 // Backlight control pin, -1 if not used
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
1266
main/gui.c
1266
main/gui.c
File diff suppressed because it is too large
Load Diff
26
main/gui.h
26
main/gui.h
@@ -1,13 +1,13 @@
|
|||||||
#ifndef GUI_H
|
#ifndef GUI_H
|
||||||
#define GUI_H
|
#define GUI_H
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
GUI_MAIN = 0,
|
GUI_MAIN = 0,
|
||||||
GUI_MENU,
|
GUI_MENU,
|
||||||
GUI_BUBBLE
|
GUI_BUBBLE
|
||||||
} GuiMode_t;
|
} GuiMode_t;
|
||||||
|
|
||||||
void gui_start(void);
|
void gui_start(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
lvgl/lvgl:
|
lvgl/lvgl:
|
||||||
version: "^9.2.0"
|
version: "^9.2.0"
|
||||||
espressif/esp_lvgl_port: "^2.6.0"
|
espressif/esp_lvgl_port: "^2.6.0"
|
||||||
|
|||||||
400
main/keypad.c
400
main/keypad.c
@@ -1,200 +1,200 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "keypad.h"
|
#include "keypad.h"
|
||||||
|
|
||||||
// ───────────── CONFIGURATION ─────────────
|
// ───────────── CONFIGURATION ─────────────
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BUTTON1_GPIO = GPIO_NUM_36,
|
BUTTON1_GPIO = GPIO_NUM_36,
|
||||||
BUTTON2_GPIO = GPIO_NUM_39,
|
BUTTON2_GPIO = GPIO_NUM_39,
|
||||||
} button_gpio_t;
|
} button_gpio_t;
|
||||||
|
|
||||||
#define BUTTON_COUNT 2
|
#define BUTTON_COUNT 2
|
||||||
|
|
||||||
// If your buttons are active‐low, set this to 0. If active‐high, set it to 1.
|
// If your buttons are active‐low, set this to 0. If active‐high, set it to 1.
|
||||||
#define KEYPAD_INACTIVE 1
|
#define KEYPAD_INACTIVE 1
|
||||||
#define KEYPAD_ACTIVE 0
|
#define KEYPAD_ACTIVE 0
|
||||||
|
|
||||||
// Now we run the task every 50 ms and assume that is enough to debounce:
|
// Now we run the task every 50 ms and assume that is enough to debounce:
|
||||||
#define DEBOUNCE_INTERVAL_MS 50 // Poll once every 50 ms
|
#define DEBOUNCE_INTERVAL_MS 50 // Poll once every 50 ms
|
||||||
// No more stable‐count logic—one sample/50 ms is considered “debounced.”
|
// No more stable‐count logic—one sample/50 ms is considered “debounced.”
|
||||||
|
|
||||||
// Short vs. long press threshold (in ms):
|
// Short vs. long press threshold (in ms):
|
||||||
#define LONG_PRESS_MS 1000 // ≥1000 ms = “long press”
|
#define LONG_PRESS_MS 1000 // ≥1000 ms = “long press”
|
||||||
|
|
||||||
// Queue length for pending button events:
|
// Queue length for pending button events:
|
||||||
#define BTN_EVT_QUEUE_LEN 10
|
#define BTN_EVT_QUEUE_LEN 10
|
||||||
|
|
||||||
// ───────────── EVENT ENUM & QUEUE HANDLE ─────────────
|
// ───────────── EVENT ENUM & QUEUE HANDLE ─────────────
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
keycode_t key;
|
keycode_t key;
|
||||||
gpio_num_t gpio;
|
gpio_num_t gpio;
|
||||||
int prevState;
|
int prevState;
|
||||||
int currentState;
|
int currentState;
|
||||||
TickType_t activeTime;
|
TickType_t activeTime;
|
||||||
uint32_t event;
|
uint32_t event;
|
||||||
} button_t;
|
} button_t;
|
||||||
|
|
||||||
static button_t _buttons[2];
|
static button_t _buttons[2];
|
||||||
|
|
||||||
// The queue handle that the debounce task will push events into:
|
// The queue handle that the debounce task will push events into:
|
||||||
static QueueHandle_t s_btn_evt_queue = NULL;
|
static QueueHandle_t s_btn_evt_queue = NULL;
|
||||||
|
|
||||||
// Call this to get the queue, so the main task can xQueueReceive():
|
// Call this to get the queue, so the main task can xQueueReceive():
|
||||||
QueueHandle_t keypad_getQueue(void) {
|
QueueHandle_t keypad_getQueue(void) {
|
||||||
return s_btn_evt_queue;
|
return s_btn_evt_queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ───────────── INTERNAL STATE ─────────────
|
// ───────────── INTERNAL STATE ─────────────
|
||||||
|
|
||||||
// Current debounced state: 0 = released, 1 = pressed
|
// Current debounced state: 0 = released, 1 = pressed
|
||||||
static int s_debounced[2] = { 0, 0 };
|
static int s_debounced[2] = { 0, 0 };
|
||||||
|
|
||||||
// Tick count when each button was debounced to “pressed”
|
// Tick count when each button was debounced to “pressed”
|
||||||
static TickType_t s_press_tick[2] = { 0, 0 };
|
static TickType_t s_press_tick[2] = { 0, 0 };
|
||||||
|
|
||||||
// If we suppress an individual‐button event (because it was part of a “both” press), this is true
|
// If we suppress an individual‐button event (because it was part of a “both” press), this is true
|
||||||
static bool s_suppress_event[2] = { false, false };
|
static bool s_suppress_event[2] = { false, false };
|
||||||
|
|
||||||
// Tracks if we are currently in “both buttons pressed” phase:
|
// Tracks if we are currently in “both buttons pressed” phase:
|
||||||
static bool s_was_both = false;
|
static bool s_was_both = false;
|
||||||
|
|
||||||
// Tick count when “both buttons” first became debounced‐pressed:
|
// Tick count when “both buttons” first became debounced‐pressed:
|
||||||
static TickType_t s_both_press_tick = 0;
|
static TickType_t s_both_press_tick = 0;
|
||||||
|
|
||||||
// ───────────── TAG FOR LOGGING ─────────────
|
// ───────────── TAG FOR LOGGING ─────────────
|
||||||
|
|
||||||
static const char *TAG = "btn_debounce";
|
static const char *TAG = "btn_debounce";
|
||||||
|
|
||||||
// ───────────── GPIO SETUP ─────────────
|
// ───────────── GPIO SETUP ─────────────
|
||||||
|
|
||||||
static void init_button_gpio(void)
|
static void init_button_gpio(void)
|
||||||
{
|
{
|
||||||
// Configure both as inputs with pull‐up (assuming active‐low buttons)
|
// Configure both as inputs with pull‐up (assuming active‐low buttons)
|
||||||
gpio_config_t io_conf = {
|
gpio_config_t io_conf = {
|
||||||
.pin_bit_mask = (1ULL << BUTTON1_GPIO) | (1ULL << BUTTON2_GPIO),
|
.pin_bit_mask = (1ULL << BUTTON1_GPIO) | (1ULL << BUTTON2_GPIO),
|
||||||
.mode = GPIO_MODE_INPUT,
|
.mode = GPIO_MODE_INPUT,
|
||||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
.intr_type = GPIO_INTR_DISABLE
|
.intr_type = GPIO_INTR_DISABLE
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(gpio_config(&io_conf));
|
ESP_ERROR_CHECK(gpio_config(&io_conf));
|
||||||
|
|
||||||
_buttons[0].activeTime = 0;
|
_buttons[0].activeTime = 0;
|
||||||
_buttons[0].gpio = BUTTON1_GPIO;
|
_buttons[0].gpio = BUTTON1_GPIO;
|
||||||
_buttons[0].key = KEY0;
|
_buttons[0].key = KEY0;
|
||||||
_buttons[0].prevState = KEYPAD_INACTIVE;
|
_buttons[0].prevState = KEYPAD_INACTIVE;
|
||||||
_buttons[0].currentState = KEYPAD_INACTIVE;
|
_buttons[0].currentState = KEYPAD_INACTIVE;
|
||||||
|
|
||||||
_buttons[1].activeTime = 0;
|
_buttons[1].activeTime = 0;
|
||||||
_buttons[1].gpio = BUTTON2_GPIO;
|
_buttons[1].gpio = BUTTON2_GPIO;
|
||||||
_buttons[1].key = KEY1;
|
_buttons[1].key = KEY1;
|
||||||
_buttons[1].prevState = KEYPAD_INACTIVE;
|
_buttons[1].prevState = KEYPAD_INACTIVE;
|
||||||
_buttons[1].currentState = KEYPAD_INACTIVE;
|
_buttons[1].currentState = KEYPAD_INACTIVE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ───────────── BUTTON DEBOUNCE & PRESS‐TYPE TASK ─────────────
|
// ───────────── BUTTON DEBOUNCE & PRESS‐TYPE TASK ─────────────
|
||||||
|
|
||||||
static void vButtonDebounceTask(void *arg)
|
static void vButtonDebounceTask(void *arg)
|
||||||
{
|
{
|
||||||
(void)arg;
|
(void)arg;
|
||||||
|
|
||||||
TickType_t now = xTaskGetTickCount();
|
TickType_t now = xTaskGetTickCount();
|
||||||
TickType_t xLastWake = now;
|
TickType_t xLastWake = now;
|
||||||
const TickType_t long_press_thresh = pdMS_TO_TICKS(LONG_PRESS_MS);
|
const TickType_t long_press_thresh = pdMS_TO_TICKS(LONG_PRESS_MS);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
now = xTaskGetTickCount();
|
now = xTaskGetTickCount();
|
||||||
|
|
||||||
for (int i=0; i < BUTTON_COUNT; ++i)
|
for (int i=0; i < BUTTON_COUNT; ++i)
|
||||||
{
|
{
|
||||||
button_t *button = &_buttons[i];
|
button_t *button = &_buttons[i];
|
||||||
|
|
||||||
button->currentState = gpio_get_level(button->gpio);
|
button->currentState = gpio_get_level(button->gpio);
|
||||||
|
|
||||||
if (button->currentState == KEYPAD_ACTIVE)
|
if (button->currentState == KEYPAD_ACTIVE)
|
||||||
{
|
{
|
||||||
// just pressed
|
// just pressed
|
||||||
if (button->prevState == KEYPAD_INACTIVE)
|
if (button->prevState == KEYPAD_INACTIVE)
|
||||||
{
|
{
|
||||||
button->activeTime = now;
|
button->activeTime = now;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((now - button->activeTime) >= long_press_thresh)
|
if ((now - button->activeTime) >= long_press_thresh)
|
||||||
{
|
{
|
||||||
if (button->event == 0)
|
if (button->event == 0)
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG, "%d LONG", button->key);
|
ESP_LOGI(TAG, "%d LONG", button->key);
|
||||||
button->event = (button->key << KEY_LONG_PRESS);
|
button->event = (button->key << KEY_LONG_PRESS);
|
||||||
xQueueSend(s_btn_evt_queue, &button->event, 0);
|
xQueueSend(s_btn_evt_queue, &button->event, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (button->currentState == KEYPAD_INACTIVE)
|
if (button->currentState == KEYPAD_INACTIVE)
|
||||||
{
|
{
|
||||||
// just released
|
// just released
|
||||||
if (button->prevState == KEYPAD_ACTIVE)
|
if (button->prevState == KEYPAD_ACTIVE)
|
||||||
{
|
{
|
||||||
if (button->event == 0)
|
if (button->event == 0)
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG, "%d SHORT", button->key);
|
ESP_LOGI(TAG, "%d SHORT", button->key);
|
||||||
button->event = (button->key << KEY_SHORT_PRESS);
|
button->event = (button->key << KEY_SHORT_PRESS);
|
||||||
xQueueSend(s_btn_evt_queue, &button->event, 0);
|
xQueueSend(s_btn_evt_queue, &button->event, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG, "%d LONG RELEASE", button->key);
|
ESP_LOGI(TAG, "%d LONG RELEASE", button->key);
|
||||||
}
|
}
|
||||||
button->event = 0;
|
button->event = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
button->activeTime = 0;
|
button->activeTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
button->prevState = button->currentState;
|
button->prevState = button->currentState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 7) Wait 50 ms and repeat
|
// 7) Wait 50 ms and repeat
|
||||||
vTaskDelayUntil(&xLastWake, pdMS_TO_TICKS(DEBOUNCE_INTERVAL_MS));
|
vTaskDelayUntil(&xLastWake, pdMS_TO_TICKS(DEBOUNCE_INTERVAL_MS));
|
||||||
xLastWake = now;
|
xLastWake = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ───────────── PUBLIC “START” FUNCTION ─────────────
|
// ───────────── PUBLIC “START” FUNCTION ─────────────
|
||||||
|
|
||||||
void keypad_start(void)
|
void keypad_start(void)
|
||||||
{
|
{
|
||||||
// 1) Create the queue for button events
|
// 1) Create the queue for button events
|
||||||
s_btn_evt_queue = xQueueCreate(BTN_EVT_QUEUE_LEN, sizeof(uint32_t));
|
s_btn_evt_queue = xQueueCreate(BTN_EVT_QUEUE_LEN, sizeof(uint32_t));
|
||||||
if (s_btn_evt_queue == NULL) {
|
if (s_btn_evt_queue == NULL) {
|
||||||
ESP_LOGE(TAG, "Failed to create button event queue");
|
ESP_LOGE(TAG, "Failed to create button event queue");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Initialize GPIOs & initial state
|
// 2) Initialize GPIOs & initial state
|
||||||
init_button_gpio();
|
init_button_gpio();
|
||||||
|
|
||||||
// 3) Launch the FreeRTOS task (low priority)
|
// 3) Launch the FreeRTOS task (low priority)
|
||||||
xTaskCreate(vButtonDebounceTask,
|
xTaskCreate(vButtonDebounceTask,
|
||||||
"btn_debounce",
|
"btn_debounce",
|
||||||
2048,
|
2048,
|
||||||
NULL,
|
NULL,
|
||||||
tskIDLE_PRIORITY + 1,
|
tskIDLE_PRIORITY + 1,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
#ifndef _KEYPAD_H
|
#ifndef _KEYPAD_H
|
||||||
#define _KEYPAD_H
|
#define _KEYPAD_H
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
KEY0 = (1 << 0),
|
KEY0 = (1 << 0),
|
||||||
KEY1 = (1 << 1),
|
KEY1 = (1 << 1),
|
||||||
} keycode_t;
|
} keycode_t;
|
||||||
|
|
||||||
|
|
||||||
#define KEY_SHORT_PRESS 0
|
#define KEY_SHORT_PRESS 0
|
||||||
#define KEY_LONG_PRESS 4
|
#define KEY_LONG_PRESS 4
|
||||||
|
|
||||||
|
#define KEY_UP KEY0
|
||||||
|
#define KEY_DOWN KEY1
|
||||||
void keypad_start(void);
|
|
||||||
QueueHandle_t keypad_getQueue(void);
|
void keypad_start(void);
|
||||||
|
QueueHandle_t keypad_getQueue(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
696
main/lsm6dsv.c
696
main/lsm6dsv.c
@@ -1,349 +1,349 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "lsm6dsv.h"
|
#include "lsm6dsv.h"
|
||||||
#include "driver/i2c.h"
|
#include "driver/i2c.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
static const char *TAG = "LSM6DSV";
|
static const char *TAG = "LSM6DSV";
|
||||||
static const int I2C_PORT = 0;
|
static const int I2C_PORT = 0;
|
||||||
|
|
||||||
// I2C helper functions
|
// I2C helper functions
|
||||||
static esp_err_t i2c_write_reg(uint8_t reg_addr, uint8_t data) {
|
static esp_err_t i2c_write_reg(uint8_t reg_addr, uint8_t data) {
|
||||||
uint8_t write_buf[2] = {reg_addr, data};
|
uint8_t write_buf[2] = {reg_addr, data};
|
||||||
return i2c_master_write_to_device(I2C_PORT, LSM6DSV_ADDR, write_buf, sizeof(write_buf), pdMS_TO_TICKS(100));
|
return i2c_master_write_to_device(I2C_PORT, LSM6DSV_ADDR, write_buf, sizeof(write_buf), pdMS_TO_TICKS(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t i2c_read_reg(uint8_t reg_addr, uint8_t *data) {
|
static esp_err_t i2c_read_reg(uint8_t reg_addr, uint8_t *data) {
|
||||||
return i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®_addr, 1, data, 1, pdMS_TO_TICKS(100));
|
return i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®_addr, 1, data, 1, pdMS_TO_TICKS(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Converts uint16_t half-precision number into a uint32_t single-precision number.
|
* @brief Converts uint16_t half-precision number into a uint32_t single-precision number.
|
||||||
* @param h half-precision number
|
* @param h half-precision number
|
||||||
* @param f pointer where the result of the conversion is written
|
* @param f pointer where the result of the conversion is written
|
||||||
* @retval 0
|
* @retval 0
|
||||||
*/
|
*/
|
||||||
esp_err_t npy_halfbits_to_floatbits(uint16_t h, uint32_t *f)
|
esp_err_t npy_halfbits_to_floatbits(uint16_t h, uint32_t *f)
|
||||||
{
|
{
|
||||||
uint16_t h_exp, h_sig;
|
uint16_t h_exp, h_sig;
|
||||||
uint32_t f_sgn, f_exp, f_sig;
|
uint32_t f_sgn, f_exp, f_sig;
|
||||||
|
|
||||||
h_exp = (h & 0x7c00u);
|
h_exp = (h & 0x7c00u);
|
||||||
f_sgn = ((uint32_t)h & 0x8000u) << 16;
|
f_sgn = ((uint32_t)h & 0x8000u) << 16;
|
||||||
switch (h_exp) {
|
switch (h_exp) {
|
||||||
case 0x0000u: /* 0 or subnormal */
|
case 0x0000u: /* 0 or subnormal */
|
||||||
h_sig = (h & 0x03ffu);
|
h_sig = (h & 0x03ffu);
|
||||||
/* Signed zero */
|
/* Signed zero */
|
||||||
if (h_sig == 0) {
|
if (h_sig == 0) {
|
||||||
*f = f_sgn;
|
*f = f_sgn;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
/* Subnormal */
|
/* Subnormal */
|
||||||
h_sig <<= 1;
|
h_sig <<= 1;
|
||||||
while ((h_sig & 0x0400u) == 0) {
|
while ((h_sig & 0x0400u) == 0) {
|
||||||
h_sig <<= 1;
|
h_sig <<= 1;
|
||||||
h_exp++;
|
h_exp++;
|
||||||
}
|
}
|
||||||
f_exp = ((uint32_t)(127 - 15 - h_exp)) << 23;
|
f_exp = ((uint32_t)(127 - 15 - h_exp)) << 23;
|
||||||
f_sig = ((uint32_t)(h_sig & 0x03ffu)) << 13;
|
f_sig = ((uint32_t)(h_sig & 0x03ffu)) << 13;
|
||||||
*f = f_sgn + f_exp + f_sig;
|
*f = f_sgn + f_exp + f_sig;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
case 0x7c00u: /* inf or NaN */
|
case 0x7c00u: /* inf or NaN */
|
||||||
/* All-ones exponent and a copy of the significand */
|
/* All-ones exponent and a copy of the significand */
|
||||||
*f = f_sgn + 0x7f800000u + (((uint32_t)(h & 0x03ffu)) << 13);
|
*f = f_sgn + 0x7f800000u + (((uint32_t)(h & 0x03ffu)) << 13);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
default: /* normalized */
|
default: /* normalized */
|
||||||
/* Just need to adjust the exponent and shift */
|
/* Just need to adjust the exponent and shift */
|
||||||
*f = f_sgn + (((uint32_t)(h & 0x7fffu) + 0x1c000u) << 13);
|
*f = f_sgn + (((uint32_t)(h & 0x7fffu) + 0x1c000u) << 13);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t npy_half_to_float(uint16_t h, float *f)
|
esp_err_t npy_half_to_float(uint16_t h, float *f)
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
float ret;
|
float ret;
|
||||||
uint32_t retbits;
|
uint32_t retbits;
|
||||||
} conv;
|
} conv;
|
||||||
npy_halfbits_to_floatbits(h, &conv.retbits);
|
npy_halfbits_to_floatbits(h, &conv.retbits);
|
||||||
*f = conv.ret;
|
*f = conv.ret;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Compute quaternions.
|
* @brief Compute quaternions.
|
||||||
* @param quat results of the computation
|
* @param quat results of the computation
|
||||||
* @param sflp raw value of the quaternions
|
* @param sflp raw value of the quaternions
|
||||||
* @retval 0
|
* @retval 0
|
||||||
*/
|
*/
|
||||||
esp_err_t sflp2q(float quat[4], uint16_t sflp[3])
|
esp_err_t sflp2q(float quat[4], uint16_t sflp[3])
|
||||||
{
|
{
|
||||||
float sumsq = 0;
|
float sumsq = 0;
|
||||||
|
|
||||||
npy_half_to_float(sflp[0], &quat[0]);
|
npy_half_to_float(sflp[0], &quat[0]);
|
||||||
npy_half_to_float(sflp[1], &quat[1]);
|
npy_half_to_float(sflp[1], &quat[1]);
|
||||||
npy_half_to_float(sflp[2], &quat[2]);
|
npy_half_to_float(sflp[2], &quat[2]);
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 3; i++) {
|
for (uint8_t i = 0; i < 3; i++) {
|
||||||
sumsq += quat[i] * quat[i];
|
sumsq += quat[i] * quat[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sumsq > 1.0f) {
|
if (sumsq > 1.0f) {
|
||||||
float n = sqrtf(sumsq);
|
float n = sqrtf(sumsq);
|
||||||
quat[0] /= n;
|
quat[0] /= n;
|
||||||
quat[1] /= n;
|
quat[1] /= n;
|
||||||
quat[2] /= n;
|
quat[2] /= n;
|
||||||
sumsq = 1.0f;
|
sumsq = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
quat[3] = sqrtf(1.0f - sumsq);
|
quat[3] = sqrtf(1.0f - sumsq);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void quaternion_to_euler(float qx, float qy, float qz, float qw, float *roll, float *pitch, float *yaw) {
|
void quaternion_to_euler(float qx, float qy, float qz, float qw, float *roll, float *pitch, float *yaw) {
|
||||||
// Roll (X-axis rotation)
|
// Roll (X-axis rotation)
|
||||||
*roll = atan2f(2.0f * (qw * qx + qy * qz), 1.0f - 2.0f * (qx * qx + qy * qy));
|
*roll = atan2f(2.0f * (qw * qx + qy * qz), 1.0f - 2.0f * (qx * qx + qy * qy));
|
||||||
|
|
||||||
// Pitch (Y-axis rotation)
|
// Pitch (Y-axis rotation)
|
||||||
*pitch = asinf(2.0f * (qw * qy - qz * qx));
|
*pitch = asinf(2.0f * (qw * qy - qz * qx));
|
||||||
|
|
||||||
// Yaw (Z-axis rotation)
|
// Yaw (Z-axis rotation)
|
||||||
*yaw = atan2f(2.0f * (qw * qz + qx * qy), 1.0f - 2.0f * (qy * qy + qz * qz));
|
*yaw = atan2f(2.0f * (qw * qz + qx * qy), 1.0f - 2.0f * (qy * qy + qz * qz));
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t lsm6dsv_init(int scl_pin, int sda_pin) {
|
esp_err_t lsm6dsv_init(int scl_pin, int sda_pin) {
|
||||||
// Configure I2C
|
// Configure I2C
|
||||||
i2c_config_t conf = {
|
i2c_config_t conf = {
|
||||||
.mode = I2C_MODE_MASTER,
|
.mode = I2C_MODE_MASTER,
|
||||||
.sda_io_num = sda_pin,
|
.sda_io_num = sda_pin,
|
||||||
.scl_io_num = scl_pin,
|
.scl_io_num = scl_pin,
|
||||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
.master.clk_speed = 400000 // 400 KHz
|
.master.clk_speed = 400000 // 400 KHz
|
||||||
};
|
};
|
||||||
|
|
||||||
esp_err_t ret = i2c_param_config(I2C_PORT, &conf);
|
esp_err_t ret = i2c_param_config(I2C_PORT, &conf);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "I2C parameter configuration failed");
|
ESP_LOGE(TAG, "I2C parameter configuration failed");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = i2c_driver_install(I2C_PORT, I2C_MODE_MASTER, 0, 0, 0);
|
ret = i2c_driver_install(I2C_PORT, I2C_MODE_MASTER, 0, 0, 0);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "I2C driver installation failed");
|
ESP_LOGE(TAG, "I2C driver installation failed");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify device ID
|
// Verify device ID
|
||||||
uint8_t who_am_i;
|
uint8_t who_am_i;
|
||||||
ret = i2c_read_reg(LSM6DSV_WHO_AM_I, &who_am_i);
|
ret = i2c_read_reg(LSM6DSV_WHO_AM_I, &who_am_i);
|
||||||
if (ret != ESP_OK || who_am_i != 0x70) { // 0x70 is the expected WHO_AM_I value
|
if (ret != ESP_OK || who_am_i != 0x70) { // 0x70 is the expected WHO_AM_I value
|
||||||
ESP_LOGE(TAG, "Failed to verify device ID or incorrect device (expected: 0x70, got: 0x%02x)", who_am_i);
|
ESP_LOGE(TAG, "Failed to verify device ID or incorrect device (expected: 0x70, got: 0x%02x)", who_am_i);
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure accelerometer (±2g, 104 Hz)
|
// Configure accelerometer (±2g, 104 Hz)
|
||||||
ret = i2c_write_reg(LSM6DSV_CTRL1, 0x44); // ODR = 104 Hz (0x04), ±2g (0x40)
|
ret = i2c_write_reg(LSM6DSV_CTRL1, 0x44); // ODR = 104 Hz (0x04), ±2g (0x40)
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Failed to configure accelerometer");
|
ESP_LOGE(TAG, "Failed to configure accelerometer");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify accelerometer configuration
|
// Verify accelerometer configuration
|
||||||
uint8_t ctrl1_val;
|
uint8_t ctrl1_val;
|
||||||
ret = i2c_read_reg(LSM6DSV_CTRL1, &ctrl1_val);
|
ret = i2c_read_reg(LSM6DSV_CTRL1, &ctrl1_val);
|
||||||
if (ret == ESP_OK) {
|
if (ret == ESP_OK) {
|
||||||
ESP_LOGI(TAG, "CTRL1 value: 0x%02x", ctrl1_val);
|
ESP_LOGI(TAG, "CTRL1 value: 0x%02x", ctrl1_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure gyroscope (±250 dps, 104 Hz)
|
// Configure gyroscope (±250 dps, 104 Hz)
|
||||||
ret = i2c_write_reg(LSM6DSV_CTRL2, 0x04); // ODR = 104 Hz (0x04), ±250 dps (0x40)
|
ret = i2c_write_reg(LSM6DSV_CTRL2, 0x04); // ODR = 104 Hz (0x04), ±250 dps (0x40)
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Failed to configure gyroscope");
|
ESP_LOGE(TAG, "Failed to configure gyroscope");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify gyroscope configuration
|
// Verify gyroscope configuration
|
||||||
uint8_t regval, tmp1;
|
uint8_t regval, tmp1;
|
||||||
ret = i2c_read_reg(LSM6DSV_CTRL2, ®val);
|
ret = i2c_read_reg(LSM6DSV_CTRL2, ®val);
|
||||||
if (ret == ESP_OK) {
|
if (ret == ESP_OK) {
|
||||||
ESP_LOGI(TAG, "CTRL2 value: 0x%02x", regval);
|
ESP_LOGI(TAG, "CTRL2 value: 0x%02x", regval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ret = i2c_read_reg(LSM6DSV_SFLP_ODR, ®val);
|
// ret = i2c_read_reg(LSM6DSV_SFLP_ODR, ®val);
|
||||||
// regval |= SFLP_ODR_15HZ;
|
// regval |= SFLP_ODR_15HZ;
|
||||||
// ret = i2c_write_reg(LSM6DSV_SFLP_ODR, regval);
|
// ret = i2c_write_reg(LSM6DSV_SFLP_ODR, regval);
|
||||||
|
|
||||||
// // get current value of FUNC_CFG_ACCESS
|
// // get current value of FUNC_CFG_ACCESS
|
||||||
// ret = i2c_read_reg(LSM6DSV_FUNC_CFG_ACCESS, ®val);
|
// ret = i2c_read_reg(LSM6DSV_FUNC_CFG_ACCESS, ®val);
|
||||||
|
|
||||||
// // enable embedded function access
|
// // enable embedded function access
|
||||||
// regval |= LSM6DSV_EMB_FUNC_REG_ACCESS;
|
// regval |= LSM6DSV_EMB_FUNC_REG_ACCESS;
|
||||||
// ret = i2c_write_reg(LSM6DSV_FUNC_CFG_ACCESS, regval);
|
// ret = i2c_write_reg(LSM6DSV_FUNC_CFG_ACCESS, regval);
|
||||||
|
|
||||||
|
|
||||||
// // enable game vector FIDO
|
// // enable game vector FIDO
|
||||||
// ret = i2c_write_reg(LSM6DSV_EMB_FUNC_FIFO_EN_A, SFLP_GAME_FIFO_EN);
|
// ret = i2c_write_reg(LSM6DSV_EMB_FUNC_FIFO_EN_A, SFLP_GAME_FIFO_EN);
|
||||||
|
|
||||||
// // enable sensor fusion algorithm
|
// // enable sensor fusion algorithm
|
||||||
// ret = i2c_write_reg(LSM6DSV_EMB_FUNC_EN_A, SFLP_GAME_EN);
|
// ret = i2c_write_reg(LSM6DSV_EMB_FUNC_EN_A, SFLP_GAME_EN);
|
||||||
// ret = i2c_write_reg(LSM6DSV_EMB_FUNC_INIT_A, SFLP_GAME_INIT);
|
// ret = i2c_write_reg(LSM6DSV_EMB_FUNC_INIT_A, SFLP_GAME_INIT);
|
||||||
|
|
||||||
|
|
||||||
// // restore FUNC_CFG_ACCESS
|
// // restore FUNC_CFG_ACCESS
|
||||||
// regval &= ~LSM6DSV_EMB_FUNC_REG_ACCESS;
|
// regval &= ~LSM6DSV_EMB_FUNC_REG_ACCESS;
|
||||||
// ret = i2c_write_reg(LSM6DSV_FUNC_CFG_ACCESS, regval);
|
// ret = i2c_write_reg(LSM6DSV_FUNC_CFG_ACCESS, regval);
|
||||||
|
|
||||||
|
|
||||||
// ret = i2c_write_reg(LSM6DSV_FIFO_CTRL4, 0x06);
|
// ret = i2c_write_reg(LSM6DSV_FIFO_CTRL4, 0x06);
|
||||||
|
|
||||||
// ret = i2c_read_reg(LSM6DSV_FIFO_CTRL4, ®val);
|
// ret = i2c_read_reg(LSM6DSV_FIFO_CTRL4, ®val);
|
||||||
// if (ret == ESP_OK) {
|
// if (ret == ESP_OK) {
|
||||||
// ESP_LOGI(TAG, "FIFO_CTRL4 value: 0x%02x", regval);
|
// ESP_LOGI(TAG, "FIFO_CTRL4 value: 0x%02x", regval);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Add a small delay after configuration
|
// Add a small delay after configuration
|
||||||
vTaskDelay(pdMS_TO_TICKS(200));
|
vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
|
||||||
ESP_LOGI(TAG, "LSM6DSV initialized successfully");
|
ESP_LOGI(TAG, "LSM6DSV initialized successfully");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t lsm6dsv_read_data(lsm6dsv_data_t *data) {
|
esp_err_t lsm6dsv_read_data(lsm6dsv_data_t *data) {
|
||||||
if (data == NULL) {
|
if (data == NULL) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
uint8_t buffer[12];
|
uint8_t buffer[12];
|
||||||
uint8_t reg = LSM6DSV_OUTX_L_A;
|
uint8_t reg = LSM6DSV_OUTX_L_A;
|
||||||
|
|
||||||
// Check status register
|
// Check status register
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
ret = i2c_read_reg(LSM6DSV_STATUS, &status);
|
ret = i2c_read_reg(LSM6DSV_STATUS, &status);
|
||||||
if (ret == ESP_OK) {
|
if (ret == ESP_OK) {
|
||||||
ESP_LOGD(TAG, "Status register: 0x%02x", status);
|
ESP_LOGD(TAG, "Status register: 0x%02x", status);
|
||||||
if (!(status & 0x01)) { // Check if accelerometer data is ready
|
if (!(status & 0x01)) { // Check if accelerometer data is ready
|
||||||
ESP_LOGW(TAG, "Accelerometer data not ready");
|
ESP_LOGW(TAG, "Accelerometer data not ready");
|
||||||
return ESP_ERR_NOT_FOUND;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read accelerometer data (6 bytes)
|
// Read accelerometer data (6 bytes)
|
||||||
ret = i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®, 1, buffer, 6, pdMS_TO_TICKS(100));
|
ret = i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®, 1, buffer, 6, pdMS_TO_TICKS(100));
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Failed to read accelerometer data");
|
ESP_LOGE(TAG, "Failed to read accelerometer data");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGD(TAG, "ACC raw: x=0x%02x%02x y=0x%02x%02x z=0x%02x%02x",
|
ESP_LOGD(TAG, "ACC raw: x=0x%02x%02x y=0x%02x%02x z=0x%02x%02x",
|
||||||
buffer[1], buffer[0], buffer[3], buffer[2], buffer[5], buffer[4]);
|
buffer[1], buffer[0], buffer[3], buffer[2], buffer[5], buffer[4]);
|
||||||
|
|
||||||
// Check if gyroscope data is ready
|
// Check if gyroscope data is ready
|
||||||
ret = i2c_read_reg(LSM6DSV_STATUS, &status);
|
ret = i2c_read_reg(LSM6DSV_STATUS, &status);
|
||||||
if (ret == ESP_OK) {
|
if (ret == ESP_OK) {
|
||||||
if (!(status & 0x02)) { // Check if gyroscope data is ready
|
if (!(status & 0x02)) { // Check if gyroscope data is ready
|
||||||
ESP_LOGW(TAG, "Gyroscope data not ready");
|
ESP_LOGW(TAG, "Gyroscope data not ready");
|
||||||
return ESP_ERR_NOT_FOUND;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read gyroscope data (6 bytes)
|
// Read gyroscope data (6 bytes)
|
||||||
reg = LSM6DSV_OUTX_L_G;
|
reg = LSM6DSV_OUTX_L_G;
|
||||||
ret = i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®, 1, buffer + 6, 6, pdMS_TO_TICKS(100));
|
ret = i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®, 1, buffer + 6, 6, pdMS_TO_TICKS(100));
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Failed to read gyroscope data");
|
ESP_LOGE(TAG, "Failed to read gyroscope data");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGD(TAG, "GYRO raw: x=0x%02x%02x y=0x%02x%02x z=0x%02x%02x",
|
ESP_LOGD(TAG, "GYRO raw: x=0x%02x%02x y=0x%02x%02x z=0x%02x%02x",
|
||||||
buffer[7], buffer[6], buffer[9], buffer[8], buffer[11], buffer[10]);
|
buffer[7], buffer[6], buffer[9], buffer[8], buffer[11], buffer[10]);
|
||||||
|
|
||||||
// Convert accelerometer data (±2g scale)
|
// Convert accelerometer data (±2g scale)
|
||||||
const float acc_scale = 2.0f / 32768.0f; // ±2g range
|
const float acc_scale = 2.0f / 32768.0f; // ±2g range
|
||||||
data->acc_x = -(int16_t)(buffer[0] | (buffer[1] << 8)) * acc_scale;
|
data->acc_x = -(int16_t)(buffer[0] | (buffer[1] << 8)) * acc_scale;
|
||||||
data->acc_y = -(int16_t)(buffer[2] | (buffer[3] << 8)) * acc_scale;
|
data->acc_y = -(int16_t)(buffer[2] | (buffer[3] << 8)) * acc_scale;
|
||||||
data->acc_z = -(int16_t)(buffer[4] | (buffer[5] << 8)) * acc_scale;
|
data->acc_z = -(int16_t)(buffer[4] | (buffer[5] << 8)) * acc_scale;
|
||||||
|
|
||||||
// Convert gyroscope data (±250 dps scale)
|
// Convert gyroscope data (±250 dps scale)
|
||||||
const float gyro_scale = 250.0f / 32768.0f; // ±250 dps range
|
const float gyro_scale = 250.0f / 32768.0f; // ±250 dps range
|
||||||
data->gyro_x = (int16_t)(buffer[6] | (buffer[7] << 8)) * gyro_scale;
|
data->gyro_x = (int16_t)(buffer[6] | (buffer[7] << 8)) * gyro_scale;
|
||||||
data->gyro_y = (int16_t)(buffer[8] | (buffer[9] << 8)) * gyro_scale;
|
data->gyro_y = (int16_t)(buffer[8] | (buffer[9] << 8)) * gyro_scale;
|
||||||
data->gyro_z = (int16_t)(buffer[10] | (buffer[11] << 8)) * gyro_scale;
|
data->gyro_z = (int16_t)(buffer[10] | (buffer[11] << 8)) * gyro_scale;
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t lsm6dsv_fifo_ready(void)
|
uint8_t lsm6dsv_fifo_ready(void)
|
||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
|
|
||||||
// Check if fifo data is ready
|
// Check if fifo data is ready
|
||||||
ret = i2c_read_reg(LSM6DSV_FIFO_STATUS2, &status);
|
ret = i2c_read_reg(LSM6DSV_FIFO_STATUS2, &status);
|
||||||
if (ret == ESP_OK) {
|
if (ret == ESP_OK) {
|
||||||
//ESP_LOGI(TAG, "FIFO_STATUS2: %d", status);
|
//ESP_LOGI(TAG, "FIFO_STATUS2: %d", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = i2c_read_reg(LSM6DSV_FIFO_STATUS1, &status);
|
ret = i2c_read_reg(LSM6DSV_FIFO_STATUS1, &status);
|
||||||
if (ret == ESP_OK) {
|
if (ret == ESP_OK) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
esp_err_t lsm6dsv_fifo_read(lsm6dsv_fifo_t *fifo)
|
esp_err_t lsm6dsv_fifo_read(lsm6dsv_fifo_t *fifo)
|
||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
uint8_t data;
|
uint8_t data;
|
||||||
uint8_t reg;
|
uint8_t reg;
|
||||||
ret = i2c_read_reg(LSM6DSV_FIFO_DATA_OUT_TAG, &data);
|
ret = i2c_read_reg(LSM6DSV_FIFO_DATA_OUT_TAG, &data);
|
||||||
|
|
||||||
reg = LSM6DSV_FIFO_DATA_OUT_X_L;
|
reg = LSM6DSV_FIFO_DATA_OUT_X_L;
|
||||||
ret = i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®, 1, fifo->data, 6, pdMS_TO_TICKS(100));
|
ret = i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®, 1, fifo->data, 6, pdMS_TO_TICKS(100));
|
||||||
|
|
||||||
if (ret == ESP_OK)
|
if (ret == ESP_OK)
|
||||||
{
|
{
|
||||||
fifo->count = (data & 0x06) >> 1;
|
fifo->count = (data & 0x06) >> 1;
|
||||||
fifo->tag = (data & 0xf8) >> 3;
|
fifo->tag = (data & 0xf8) >> 3;
|
||||||
|
|
||||||
fifo->v[0] = (uint16_t)(fifo->data[0] | (fifo->data[1] << 8));
|
fifo->v[0] = (uint16_t)(fifo->data[0] | (fifo->data[1] << 8));
|
||||||
fifo->v[1] = (uint16_t)(fifo->data[2] | (fifo->data[3] << 8));
|
fifo->v[1] = (uint16_t)(fifo->data[2] | (fifo->data[3] << 8));
|
||||||
fifo->v[2] = (uint16_t)(fifo->data[4] | (fifo->data[5] << 8));
|
fifo->v[2] = (uint16_t)(fifo->data[4] | (fifo->data[5] << 8));
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t lsm6dsv_getAttitude(lsm6dsv_fifo_t *fifo, float *roll, float *pitch, float *yaw)
|
esp_err_t lsm6dsv_getAttitude(lsm6dsv_fifo_t *fifo, float *roll, float *pitch, float *yaw)
|
||||||
{
|
{
|
||||||
float q[4];
|
float q[4];
|
||||||
sflp2q(q, fifo->v);
|
sflp2q(q, fifo->v);
|
||||||
|
|
||||||
float w = q[3];
|
float w = q[3];
|
||||||
float x = q[0];
|
float x = q[0];
|
||||||
float y = q[1];
|
float y = q[1];
|
||||||
float z = q[2];
|
float z = q[2];
|
||||||
|
|
||||||
// Yaw (z-axis rotation)
|
// Yaw (z-axis rotation)
|
||||||
*yaw = atan2(2.0f * (x * y + w * z), w * w + x * x - y * y - z * z);
|
*yaw = atan2(2.0f * (x * y + w * z), w * w + x * x - y * y - z * z);
|
||||||
|
|
||||||
// Pitch (y-axis rotation)
|
// Pitch (y-axis rotation)
|
||||||
*pitch = -asin(2.0f * (x * z - w * y));
|
*pitch = -asin(2.0f * (x * z - w * y));
|
||||||
|
|
||||||
// Roll (x-axis rotation)
|
// Roll (x-axis rotation)
|
||||||
*roll = atan2(2.0f * (w * x + y * z), w * w - x * x - y * y + z * z);
|
*roll = atan2(2.0f * (w * x + y * z), w * w - x * x - y * y + z * z);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
|
||||||
}
|
}
|
||||||
198
main/lsm6dsv.h
198
main/lsm6dsv.h
@@ -1,99 +1,99 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
|
||||||
// LSM6DSV Register Addresses
|
// LSM6DSV Register Addresses
|
||||||
#define LSM6DSV_ADDR 0x6A // I2C address (SA0=0)
|
#define LSM6DSV_ADDR 0x6A // I2C address (SA0=0)
|
||||||
|
|
||||||
#define LSM6DSV_EMB_FUNC_EN_A 0x04
|
#define LSM6DSV_EMB_FUNC_EN_A 0x04
|
||||||
#define SFLP_GAME_EN (1 << 1)
|
#define SFLP_GAME_EN (1 << 1)
|
||||||
|
|
||||||
#define LSM6DSV_FUNC_CFG_ACCESS 0x01
|
#define LSM6DSV_FUNC_CFG_ACCESS 0x01
|
||||||
#define LSM6DSV_EMB_FUNC_REG_ACCESS (1 << 7)
|
#define LSM6DSV_EMB_FUNC_REG_ACCESS (1 << 7)
|
||||||
|
|
||||||
// FIFO Registers
|
// FIFO Registers
|
||||||
#define LSM6DSV_FIFO_CTRL1 0x07 // FIFO Control Register 1
|
#define LSM6DSV_FIFO_CTRL1 0x07 // FIFO Control Register 1
|
||||||
#define LSM6DSV_FIFO_CTRL2 0x08 // FIFO Control Register 2
|
#define LSM6DSV_FIFO_CTRL2 0x08 // FIFO Control Register 2
|
||||||
#define LSM6DSV_FIFO_CTRL3 0x09 // FIFO Control Register 3
|
#define LSM6DSV_FIFO_CTRL3 0x09 // FIFO Control Register 3
|
||||||
#define LSM6DSV_FIFO_CTRL4 0x0A // FIFO Control Register 4
|
#define LSM6DSV_FIFO_CTRL4 0x0A // FIFO Control Register 4
|
||||||
|
|
||||||
#define FIFO_MODE_CONTINUOUS 0x06
|
#define FIFO_MODE_CONTINUOUS 0x06
|
||||||
|
|
||||||
#define LSM6DSV_FIFO_STATUS1 0x1B // FIFO Status Register 1
|
#define LSM6DSV_FIFO_STATUS1 0x1B // FIFO Status Register 1
|
||||||
#define LSM6DSV_FIFO_STATUS2 0x1C // FIFO Status Register 2
|
#define LSM6DSV_FIFO_STATUS2 0x1C // FIFO Status Register 2
|
||||||
|
|
||||||
#define LSM6DSV_FIFO_DATA_OUT_TAG 0x78 // FIFO Data Output Register
|
#define LSM6DSV_FIFO_DATA_OUT_TAG 0x78 // FIFO Data Output Register
|
||||||
#define LSM6DSV_FIFO_DATA_OUT_X_L 0x79 // FIFO Data Output Register
|
#define LSM6DSV_FIFO_DATA_OUT_X_L 0x79 // FIFO Data Output Register
|
||||||
#define LSM6DSV_FIFO_DATA_OUT_X_H 0x7A // FIFO Data Output Register
|
#define LSM6DSV_FIFO_DATA_OUT_X_H 0x7A // FIFO Data Output Register
|
||||||
#define LSM6DSV_FIFO_DATA_OUT_Y_L 0x7B // FIFO Data Output Register
|
#define LSM6DSV_FIFO_DATA_OUT_Y_L 0x7B // FIFO Data Output Register
|
||||||
#define LSM6DSV_FIFO_DATA_OUT_Y_H 0x7C // FIFO Data Output Register
|
#define LSM6DSV_FIFO_DATA_OUT_Y_H 0x7C // FIFO Data Output Register
|
||||||
#define LSM6DSV_FIFO_DATA_OUT_Z_L 0x7D // FIFO Data Output Register
|
#define LSM6DSV_FIFO_DATA_OUT_Z_L 0x7D // FIFO Data Output Register
|
||||||
#define LSM6DSV_FIFO_DATA_OUT_Z_H 0x7E // FIFO Data Output Register
|
#define LSM6DSV_FIFO_DATA_OUT_Z_H 0x7E // FIFO Data Output Register
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define LSM6DSV_WHO_AM_I 0x0F // Device ID register
|
#define LSM6DSV_WHO_AM_I 0x0F // Device ID register
|
||||||
#define LSM6DSV_CTRL1 0x10 // Control register 1
|
#define LSM6DSV_CTRL1 0x10 // Control register 1
|
||||||
#define LSM6DSV_CTRL2 0x11 // Control register 2
|
#define LSM6DSV_CTRL2 0x11 // Control register 2
|
||||||
#define LSM6DSV_FIFO_STATUS1 0x1B
|
#define LSM6DSV_FIFO_STATUS1 0x1B
|
||||||
#define LSM6DSV_STATUS 0x1E // Status register
|
#define LSM6DSV_STATUS 0x1E // Status register
|
||||||
#define LSM6DSV_OUTX_L_A 0x28 // Accelerometer X-axis LSB
|
#define LSM6DSV_OUTX_L_A 0x28 // Accelerometer X-axis LSB
|
||||||
#define LSM6DSV_OUTX_H_A 0x29 // Accelerometer X-axis MSB
|
#define LSM6DSV_OUTX_H_A 0x29 // Accelerometer X-axis MSB
|
||||||
#define LSM6DSV_OUTY_L_A 0x2A // Accelerometer Y-axis LSB
|
#define LSM6DSV_OUTY_L_A 0x2A // Accelerometer Y-axis LSB
|
||||||
#define LSM6DSV_OUTY_H_A 0x2B // Accelerometer Y-axis MSB
|
#define LSM6DSV_OUTY_H_A 0x2B // Accelerometer Y-axis MSB
|
||||||
#define LSM6DSV_OUTZ_L_A 0x2C // Accelerometer Z-axis LSB
|
#define LSM6DSV_OUTZ_L_A 0x2C // Accelerometer Z-axis LSB
|
||||||
#define LSM6DSV_OUTZ_H_A 0x2D // Accelerometer Z-axis MSB
|
#define LSM6DSV_OUTZ_H_A 0x2D // Accelerometer Z-axis MSB
|
||||||
#define LSM6DSV_OUTX_L_G 0x22 // Gyroscope X-axis LSB
|
#define LSM6DSV_OUTX_L_G 0x22 // Gyroscope X-axis LSB
|
||||||
#define LSM6DSV_OUTX_H_G 0x23 // Gyroscope X-axis MSB
|
#define LSM6DSV_OUTX_H_G 0x23 // Gyroscope X-axis MSB
|
||||||
#define LSM6DSV_OUTY_L_G 0x24 // Gyroscope Y-axis LSB
|
#define LSM6DSV_OUTY_L_G 0x24 // Gyroscope Y-axis LSB
|
||||||
#define LSM6DSV_OUTY_H_G 0x25 // Gyroscope Y-axis MSB
|
#define LSM6DSV_OUTY_H_G 0x25 // Gyroscope Y-axis MSB
|
||||||
#define LSM6DSV_OUTZ_L_G 0x26 // Gyroscope Z-axis LSB
|
#define LSM6DSV_OUTZ_L_G 0x26 // Gyroscope Z-axis LSB
|
||||||
#define LSM6DSV_OUTZ_H_G 0x27 // Gyroscope Z-axis MSB
|
#define LSM6DSV_OUTZ_H_G 0x27 // Gyroscope Z-axis MSB
|
||||||
|
|
||||||
#define LSM6DSV_SFLP_ODR 0x5E
|
#define LSM6DSV_SFLP_ODR 0x5E
|
||||||
#define SFLP_ODR_15HZ (0 << 3)
|
#define SFLP_ODR_15HZ (0 << 3)
|
||||||
#define SFLP_ODR_30HZ (1 << 3)
|
#define SFLP_ODR_30HZ (1 << 3)
|
||||||
#define SFLP_ODR_60HZ (2 << 3)
|
#define SFLP_ODR_60HZ (2 << 3)
|
||||||
#define SFLP_ODR_120HZ (3 << 3)
|
#define SFLP_ODR_120HZ (3 << 3)
|
||||||
#define SFLP_ODR_240HZ (4 << 3)
|
#define SFLP_ODR_240HZ (4 << 3)
|
||||||
#define SFLP_ODR_480HZ (5 << 3)
|
#define SFLP_ODR_480HZ (5 << 3)
|
||||||
|
|
||||||
|
|
||||||
#define LSM6DSV_EMB_FUNC_FIFO_EN_A 0x44
|
#define LSM6DSV_EMB_FUNC_FIFO_EN_A 0x44
|
||||||
#define SFLP_GAME_FIFO_EN (1 << 1)
|
#define SFLP_GAME_FIFO_EN (1 << 1)
|
||||||
|
|
||||||
#define LSM6DSV_EMB_FUNC_INIT_A 0x66
|
#define LSM6DSV_EMB_FUNC_INIT_A 0x66
|
||||||
#define SFLP_GAME_INIT (1 << 1)
|
#define SFLP_GAME_INIT (1 << 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct lsm6dsv_fifo_s
|
typedef struct lsm6dsv_fifo_s
|
||||||
{
|
{
|
||||||
uint8_t tag;
|
uint8_t tag;
|
||||||
uint8_t count;
|
uint8_t count;
|
||||||
uint8_t data[6];
|
uint8_t data[6];
|
||||||
uint16_t v[3];
|
uint16_t v[3];
|
||||||
|
|
||||||
} lsm6dsv_fifo_t;
|
} lsm6dsv_fifo_t;
|
||||||
|
|
||||||
// Sensor data structure
|
// Sensor data structure
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float acc_x;
|
float acc_x;
|
||||||
float acc_y;
|
float acc_y;
|
||||||
float acc_z;
|
float acc_z;
|
||||||
float gyro_x;
|
float gyro_x;
|
||||||
float gyro_y;
|
float gyro_y;
|
||||||
float gyro_z;
|
float gyro_z;
|
||||||
} lsm6dsv_data_t;
|
} lsm6dsv_data_t;
|
||||||
|
|
||||||
// Function declarations
|
// Function declarations
|
||||||
esp_err_t lsm6dsv_init(int scl_pin, int sda_pin);
|
esp_err_t lsm6dsv_init(int scl_pin, int sda_pin);
|
||||||
esp_err_t lsm6dsv_read_data(lsm6dsv_data_t *data);
|
esp_err_t lsm6dsv_read_data(lsm6dsv_data_t *data);
|
||||||
|
|
||||||
uint8_t lsm6dsv_fifo_ready(void);
|
uint8_t lsm6dsv_fifo_ready(void);
|
||||||
|
|
||||||
esp_err_t lsm6dsv_fifo_read(lsm6dsv_fifo_t *fifo);
|
esp_err_t lsm6dsv_fifo_read(lsm6dsv_fifo_t *fifo);
|
||||||
|
|
||||||
esp_err_t lsm6dsv_getAttitude(lsm6dsv_fifo_t *fifo, float *roll, float *pitch, float *yaw);
|
esp_err_t lsm6dsv_getAttitude(lsm6dsv_fifo_t *fifo, float *roll, float *pitch, float *yaw);
|
||||||
|
|||||||
2256
main/lv_conf.h
2256
main/lv_conf.h
File diff suppressed because it is too large
Load Diff
557
main/main.c
557
main/main.c
@@ -1,266 +1,291 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include "math.h"
|
#include "math.h"
|
||||||
|
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/timers.h"
|
#include "freertos/timers.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "nvs.h"
|
#include "nvs.h"
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
|
|
||||||
|
|
||||||
#include "bt_app_core.h"
|
#include "bt_app.h"
|
||||||
#include "lsm6dsv.h"
|
#include "lsm6dsv.h"
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include "gpio.h"
|
#include "gpio.h"
|
||||||
#include "keypad.h"
|
#include "keypad.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************
|
/*********************************
|
||||||
* STATIC FUNCTION DECLARATIONS
|
* STATIC FUNCTION DECLARATIONS
|
||||||
********************************/
|
********************************/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float alpha; // smoothing factor, 0<alpha<1
|
float alpha; // smoothing factor, 0<alpha<1
|
||||||
float prev_output; // y[n-1]
|
float prev_output; // y[n-1]
|
||||||
} LowPassFilter;
|
} LowPassFilter;
|
||||||
|
|
||||||
// Initialize the filter. alpha = smoothing factor.
|
// Initialize the filter. alpha = smoothing factor.
|
||||||
// e.g. alpha = dt/(RC+dt) if you derive from cutoff freq & sample time.
|
// e.g. alpha = dt/(RC+dt) if you derive from cutoff freq & sample time.
|
||||||
// init_output can be 0 or your first sample to avoid startup glitch.
|
// init_output can be 0 or your first sample to avoid startup glitch.
|
||||||
static inline void LPF_Init(LowPassFilter *f, float alpha, float init_output) {
|
static inline void LPF_Init(LowPassFilter *f, float alpha, float init_output) {
|
||||||
f->alpha = alpha;
|
f->alpha = alpha;
|
||||||
f->prev_output = init_output;
|
f->prev_output = init_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run one input sample through the filter.
|
// Run one input sample through the filter.
|
||||||
// Returns y[n] = alpha * x[n] + (1-alpha) * y[n-1].
|
// Returns y[n] = alpha * x[n] + (1-alpha) * y[n-1].
|
||||||
static inline float LPF_Update(LowPassFilter *f, float input) {
|
static inline float LPF_Update(LowPassFilter *f, float input) {
|
||||||
float out = f->alpha * input + (1.0f - f->alpha) * f->prev_output;
|
float out = f->alpha * input + (1.0f - f->alpha) * f->prev_output;
|
||||||
f->prev_output = out;
|
f->prev_output = out;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********************************
|
/*********************************
|
||||||
* STATIC VARIABLE DEFINITIONS
|
* STATIC VARIABLE DEFINITIONS
|
||||||
********************************/
|
********************************/
|
||||||
static const char *TAG = "main";
|
static const char *TAG = "main";
|
||||||
|
|
||||||
/*********************************
|
/*********************************
|
||||||
* STATIC FUNCTION DEFINITIONS
|
* STATIC FUNCTION DEFINITIONS
|
||||||
********************************/
|
********************************/
|
||||||
|
|
||||||
static void init_gpio(void)
|
static void init_gpio(void)
|
||||||
{
|
{
|
||||||
gpio_config_t io_conf;
|
gpio_config_t io_conf;
|
||||||
|
|
||||||
|
|
||||||
// Configure output GPIO
|
// Configure output GPIO
|
||||||
io_conf.pin_bit_mask = (1ULL << PIN_NUM_LED_1);
|
io_conf.pin_bit_mask = (1ULL << PIN_NUM_LED_1) | (1ULL << PIN_NUM_LED_2);
|
||||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||||
gpio_config(&io_conf);
|
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||||
|
gpio_config(&io_conf);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
// Configure output power signal
|
#if 1
|
||||||
gpio_set_level(PIN_NUM_nON, 1);
|
// Configure output power signal
|
||||||
|
gpio_set_level(PIN_NUM_nON, 1);
|
||||||
io_conf.pin_bit_mask = (1ULL << PIN_NUM_nON);
|
|
||||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
io_conf.pin_bit_mask = (1ULL << PIN_NUM_nON);
|
||||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||||
gpio_config(&io_conf);
|
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||||
#endif
|
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||||
|
gpio_config(&io_conf);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Configure LCD Backlight GPIO
|
|
||||||
io_conf.pin_bit_mask = (1ULL << PIN_NUM_BK_LIGHT);
|
|
||||||
|
// Configure LCD Backlight GPIO
|
||||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
io_conf.pin_bit_mask = (1ULL << PIN_NUM_BK_LIGHT);
|
||||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||||
gpio_config(&io_conf);
|
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||||
}
|
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||||
|
gpio_config(&io_conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// IMU task to read sensor data
|
|
||||||
static void imu_task(void *pvParameters) {
|
|
||||||
lsm6dsv_data_t imu_data;
|
// IMU task to read sensor data
|
||||||
char data_str[128];
|
static void imu_task(void *pvParameters) {
|
||||||
|
lsm6dsv_data_t imu_data;
|
||||||
float roll, pitch, yaw;
|
char data_str[128];
|
||||||
lsm6dsv_fifo_t fifo;
|
|
||||||
|
float roll, pitch, yaw;
|
||||||
ImuData_t imu;
|
lsm6dsv_fifo_t fifo;
|
||||||
|
|
||||||
LowPassFilter lpf;
|
ImuData_t imu;
|
||||||
LPF_Init(&lpf, 0.2, 0);
|
|
||||||
|
LowPassFilter lpf;
|
||||||
while (1) {
|
LPF_Init(&lpf, FILTER_COEFF, 0);
|
||||||
|
|
||||||
// uint8_t samples = lsm6dsv_fifo_ready();
|
while (1) {
|
||||||
|
|
||||||
//ESP_LOGI(TAG, "Samples: %d", samples);
|
// uint8_t samples = lsm6dsv_fifo_ready();
|
||||||
// while (samples)
|
|
||||||
// {
|
//ESP_LOGI(TAG, "Samples: %d", samples);
|
||||||
// lsm6dsv_fifo_read(&fifo);
|
// while (samples)
|
||||||
|
// {
|
||||||
|
// lsm6dsv_fifo_read(&fifo);
|
||||||
|
|
||||||
// // snprintf(data_str, sizeof(data_str),
|
|
||||||
// // "tag: %d, count: %d, (%d, %d, %d)",
|
|
||||||
// // fifo.tag, fifo.count, x, y, z);
|
// // snprintf(data_str, sizeof(data_str),
|
||||||
|
// // "tag: %d, count: %d, (%d, %d, %d)",
|
||||||
// // ESP_LOGI(TAG, "%s", data_str);
|
// // fifo.tag, fifo.count, x, y, z);
|
||||||
|
|
||||||
// samples--;
|
// // ESP_LOGI(TAG, "%s", data_str);
|
||||||
// }
|
|
||||||
|
// samples--;
|
||||||
// lsm6dsv_getAttitude(&fifo, &roll, &pitch, &yaw);
|
// }
|
||||||
|
|
||||||
// snprintf(data_str, sizeof(data_str),
|
// lsm6dsv_getAttitude(&fifo, &roll, &pitch, &yaw);
|
||||||
// "r: %0.3f, p: %0.3f, y: %0.3f",
|
|
||||||
// roll, pitch, yaw);
|
// snprintf(data_str, sizeof(data_str),
|
||||||
|
// "r: %0.3f, p: %0.3f, y: %0.3f",
|
||||||
// ESP_LOGI(TAG, "%s", data_str);
|
// roll, pitch, yaw);
|
||||||
|
|
||||||
float xz;
|
// ESP_LOGI(TAG, "%s", data_str);
|
||||||
float yz;
|
|
||||||
float xy;
|
float xz;
|
||||||
|
float yz;
|
||||||
#if 1
|
float xy;
|
||||||
if (lsm6dsv_read_data(&imu_data) == ESP_OK)
|
|
||||||
{
|
#if 1
|
||||||
// snprintf(data_str, sizeof(data_str),
|
if (lsm6dsv_read_data(&imu_data) == ESP_OK)
|
||||||
// "Acc: %.2f, %.2f, %.2f; "
|
{
|
||||||
// "Gyro: %.2f, %.2f, %.2f (%d)\n",
|
// snprintf(data_str, sizeof(data_str),
|
||||||
// imu_data.acc_x, imu_data.acc_y, imu_data.acc_z,
|
// "Acc: %.2f, %.2f, %.2f; "
|
||||||
// imu_data.gyro_x, imu_data.gyro_y, imu_data.gyro_z, lsm6dsv_fifo_ready());
|
// "Gyro: %.2f, %.2f, %.2f (%d)\n",
|
||||||
|
// imu_data.acc_x, imu_data.acc_y, imu_data.acc_z,
|
||||||
|
// imu_data.gyro_x, imu_data.gyro_y, imu_data.gyro_z, lsm6dsv_fifo_ready());
|
||||||
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
imu.raw[ANGLE_XZ] = atan2f((float)imu_data.acc_z, (float)imu_data.acc_x) * 180 / M_PI;
|
|
||||||
imu.raw[ANGLE_YZ] = atan2f((float)imu_data.acc_z, (float)imu_data.acc_y) * 180 / M_PI;
|
#if 1
|
||||||
imu.raw[ANGLE_XY] = atan2f((float)imu_data.acc_x, (float)imu_data.acc_y) * 180 / M_PI;
|
imu.raw[ANGLE_XZ] = atan2f((float)imu_data.acc_z, (float)imu_data.acc_x) * 180 / M_PI;
|
||||||
|
imu.raw[ANGLE_YZ] = atan2f((float)imu_data.acc_z, (float)imu_data.acc_y) * 180 / M_PI;
|
||||||
|
imu.raw[ANGLE_XY] = atan2f((float)imu_data.acc_x, (float)imu_data.acc_y) * 180 / M_PI;
|
||||||
float angle;
|
|
||||||
|
|
||||||
if (imu.raw[ANGLE_XY] > MAX_INDICATION_ANGLE)
|
float angle;
|
||||||
{
|
|
||||||
angle = MAX_INDICATION_ANGLE;
|
|
||||||
}
|
angle = system_getAngle();
|
||||||
else
|
|
||||||
if (imu.raw[ANGLE_XY] > MAX_INDICATION_ANGLE)
|
if (angle > MAX_INDICATION_ANGLE)
|
||||||
{
|
{
|
||||||
angle = -MAX_INDICATION_ANGLE;
|
angle = MAX_INDICATION_ANGLE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
if (angle < -MAX_INDICATION_ANGLE)
|
||||||
angle = imu.raw[ANGLE_XY];
|
{
|
||||||
}
|
angle = -MAX_INDICATION_ANGLE;
|
||||||
|
}
|
||||||
imu.angle = LPF_Update(&lpf, angle);
|
|
||||||
|
// low pass filter the angle measurement
|
||||||
//imu.filtered[ANGLE_XY] = LPF_Update(&lpf, imu.raw[ANGLE_XY]);
|
imu.angle = LPF_Update(&lpf, angle);
|
||||||
|
|
||||||
system_setImuData(imu);
|
//imu.filtered[ANGLE_XY] = LPF_Update(&lpf, imu.raw[ANGLE_XY]);
|
||||||
|
|
||||||
// snprintf(data_str, sizeof(data_str),
|
system_setImuData(imu);
|
||||||
// "(%.2f, %.2f, %.2f)", xy, yz, xy);
|
|
||||||
#endif
|
// snprintf(data_str, sizeof(data_str),
|
||||||
|
// "(%.2f, %.2f, %.2f)", xy, yz, xy);
|
||||||
#if 0
|
#endif
|
||||||
snprintf(data_str, sizeof(data_str),
|
|
||||||
"(%.1f, %.1f, %.1f) (%.2f, %.2f, %.2f)",
|
#if 0
|
||||||
xz, yz, xy,
|
snprintf(data_str, sizeof(data_str),
|
||||||
(float)imu_data.acc_x, (float)imu_data.acc_y, (float)imu_data.acc_z);
|
"(%.1f, %.1f, %.1f) (%.2f, %.2f, %.2f)",
|
||||||
ESP_LOGI(TAG, "%s", data_str);
|
imu.raw[ANGLE_XZ], imu.raw[ANGLE_YZ], imu.raw[ANGLE_XY],
|
||||||
#endif
|
(float)imu_data.acc_x, (float)imu_data.acc_y, (float)imu_data.acc_z);
|
||||||
// Update label text in LVGL task
|
ESP_LOGI(TAG, "%s", data_str);
|
||||||
//lv_label_set_text(imu_label, data_str);
|
#endif
|
||||||
|
// Update label text in LVGL task
|
||||||
}
|
//lv_label_set_text(imu_label, data_str);
|
||||||
#endif
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(100)); // Update every 100ms
|
}
|
||||||
}
|
#endif
|
||||||
}
|
vTaskDelay(pdMS_TO_TICKS(100)); // Update every 100ms
|
||||||
|
}
|
||||||
/*********************************
|
}
|
||||||
* MAIN ENTRY POINT
|
|
||||||
********************************/
|
/*********************************
|
||||||
|
* MAIN ENTRY POINT
|
||||||
void app_main(void)
|
********************************/
|
||||||
{
|
|
||||||
|
void app_main(void)
|
||||||
/* initialize NVS — it is used to store PHY calibration data */
|
{
|
||||||
// esp_err_t ret = nvs_flash_init();
|
|
||||||
// if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
/* initialize NVS — it is used to store PHY calibration data */
|
||||||
// ESP_ERROR_CHECK(nvs_flash_erase());
|
esp_err_t ret = nvs_flash_init();
|
||||||
// ret = nvs_flash_init();
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||||
// }
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||||
// ESP_ERROR_CHECK(ret);
|
ret = nvs_flash_init();
|
||||||
|
}
|
||||||
init_gpio();
|
ESP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
system_init();
|
init_gpio();
|
||||||
|
|
||||||
|
system_init();
|
||||||
|
|
||||||
// Initialize IMU
|
|
||||||
ESP_ERROR_CHECK(lsm6dsv_init(22, 21)); // SCL = IO14, SDA = IO15
|
|
||||||
|
// Initialize IMU
|
||||||
|
ESP_ERROR_CHECK(lsm6dsv_init(22, 21)); // SCL = IO14, SDA = IO15
|
||||||
|
|
||||||
// Create IMU task
|
|
||||||
TaskHandle_t h = xTaskCreate(imu_task, "imu_task", 4096, NULL, 5, NULL);
|
|
||||||
|
// Create IMU task
|
||||||
|
TaskHandle_t h = xTaskCreate(imu_task, "imu_task", 4096, NULL, 5, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gui_start();
|
bt_app_init();
|
||||||
|
|
||||||
|
gui_start();
|
||||||
|
|
||||||
//bt_app_init();
|
gpio_set_level(PIN_NUM_LED_2, 1);
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
// Main loop - LVGL task will run automatically
|
|
||||||
while (1) {
|
keypad_start();
|
||||||
|
QueueHandle_t q = keypad_getQueue();
|
||||||
// int level = gpio_get_level(PIN_NUM_BUTTON_1); // Read input GPIO
|
uint32_t ev = 0;
|
||||||
// gpio_set_level(PIN_NUM_LED_1, level);
|
|
||||||
|
//gpio_set_level(PIN_NUM_LED_1, 1);
|
||||||
//gpio_set_level(PIN_NUM_nON, (level ? 0 : 1));
|
gpio_set_level(PIN_NUM_LED_2, 1);
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(100));
|
// Main loop - LVGL task will run automatically
|
||||||
|
while (1) {
|
||||||
//gui_service();
|
|
||||||
}
|
if (xQueueReceive(q, &ev, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||||
}
|
{
|
||||||
|
switch (ev)
|
||||||
|
{
|
||||||
|
case (KEY_UP << KEY_LONG_PRESS):
|
||||||
|
{
|
||||||
|
system_setZeroAngle();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case (KEY_DOWN << KEY_LONG_PRESS):
|
||||||
|
{
|
||||||
|
system_clearZeroAngle();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
271
main/system.c
271
main/system.c
@@ -1,104 +1,169 @@
|
|||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
static SystemState_t _systemState;
|
static SystemState_t _systemState;
|
||||||
|
|
||||||
static EventGroupHandle_t _systemEvent;
|
static EventGroupHandle_t _systemEvent;
|
||||||
static EventManager_t _eventManager;
|
static EventManager_t _eventManager;
|
||||||
|
|
||||||
void system_init(void)
|
void system_init(void)
|
||||||
{
|
{
|
||||||
EventGroupHandle_t evt = xEventGroupCreate();
|
_systemState.zeroAngle = 0.0f;
|
||||||
|
_systemState.primaryAxis = PRIMARY_AXIS;
|
||||||
_eventManager.count = 0;
|
|
||||||
_eventManager.mutex = xSemaphoreCreateMutex();
|
EventGroupHandle_t evt = xEventGroupCreate();
|
||||||
}
|
|
||||||
|
_eventManager.count = 0;
|
||||||
void system_setImuData(ImuData_t imu)
|
_eventManager.mutex = xSemaphoreCreateMutex();
|
||||||
{
|
}
|
||||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
|
||||||
|
int system_getPrimaryAxis(void)
|
||||||
_systemState.imu = imu;
|
{
|
||||||
|
int axis;
|
||||||
xSemaphoreGive(_eventManager.mutex);
|
|
||||||
|
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||||
//ESP_LOGI("g", "New IMU Data: %.2f", xy);
|
axis = _systemState.primaryAxis;
|
||||||
|
xSemaphoreGive(_eventManager.mutex);
|
||||||
system_notifyAll(EM_EVENT_NEW_DATA);
|
|
||||||
}
|
return axis;
|
||||||
|
}
|
||||||
ImuData_t system_getImuData(void)
|
|
||||||
{
|
float system_getAngle(void)
|
||||||
ImuData_t imu;
|
{
|
||||||
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
float angle;
|
||||||
|
|
||||||
imu = _systemState.imu;
|
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||||
|
|
||||||
xSemaphoreGive(_eventManager.mutex);
|
|
||||||
|
angle = _systemState.imu.raw[_systemState.primaryAxis] - _systemState.zeroAngle;
|
||||||
return imu;
|
|
||||||
}
|
|
||||||
|
xSemaphoreGive(_eventManager.mutex);
|
||||||
void system_waitForEvent(void)
|
|
||||||
{
|
return angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemState_t *system_getState(void)
|
void system_setZeroAngle(void)
|
||||||
{
|
{
|
||||||
return &_systemState;
|
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||||
}
|
|
||||||
|
|
||||||
|
_systemState.zeroAngle = _systemState.imu.raw[_systemState.primaryAxis];
|
||||||
BaseType_t system_subscribe(TaskHandle_t task) {
|
|
||||||
|
ESP_LOGI("system", "Zero: %.1f", _systemState.zeroAngle);
|
||||||
EventManager_t *em = &_eventManager;
|
|
||||||
|
xSemaphoreGive(_eventManager.mutex);
|
||||||
ESP_LOGI("g", "Subscribe: %p", task);
|
}
|
||||||
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdFALSE) {
|
|
||||||
return pdFAIL;
|
void system_clearZeroAngle(void)
|
||||||
}
|
{
|
||||||
if (em->count < EM_MAX_SUBSCRIBERS) {
|
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||||
// avoid duplicates?
|
|
||||||
ESP_LOGI("g", "PASS");
|
_systemState.zeroAngle = 0.0f;
|
||||||
em->subscribers[em->count++] = task;
|
|
||||||
xSemaphoreGive(em->mutex);
|
ESP_LOGI("system", "Zero: %.1f", _systemState.zeroAngle);
|
||||||
return pdPASS;
|
|
||||||
}
|
xSemaphoreGive(_eventManager.mutex);
|
||||||
xSemaphoreGive(em->mutex);
|
}
|
||||||
return pdFAIL; // full
|
|
||||||
}
|
float system_getZeroAngle(void)
|
||||||
|
{
|
||||||
BaseType_t system_unsubscribe(TaskHandle_t task) {
|
float angle;
|
||||||
|
|
||||||
EventManager_t *em = &_eventManager;
|
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||||
|
|
||||||
BaseType_t removed = pdFAIL;
|
angle = _systemState.zeroAngle;
|
||||||
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdTRUE) {
|
|
||||||
for (size_t i = 0; i < em->count; ++i) {
|
xSemaphoreGive(_eventManager.mutex);
|
||||||
if (em->subscribers[i] == task) {
|
|
||||||
// shift down
|
return angle;
|
||||||
for (size_t j = i; j + 1 < em->count; ++j)
|
}
|
||||||
em->subscribers[j] = em->subscribers[j+1];
|
|
||||||
em->count--;
|
void system_setImuData(ImuData_t imu)
|
||||||
removed = pdPASS;
|
{
|
||||||
break;
|
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||||
}
|
|
||||||
}
|
_systemState.imu = imu;
|
||||||
xSemaphoreGive(em->mutex);
|
|
||||||
}
|
xSemaphoreGive(_eventManager.mutex);
|
||||||
return removed;
|
|
||||||
}
|
//ESP_LOGI("g", "New IMU Data: %.2f", xy);
|
||||||
|
|
||||||
void system_notifyAll(uint32_t eventBits) {
|
system_notifyAll(EM_EVENT_NEW_DATA);
|
||||||
EventManager_t *em = &_eventManager;
|
}
|
||||||
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdTRUE) {
|
|
||||||
for (size_t i = 0; i < em->count; ++i) {
|
ImuData_t system_getImuData(void)
|
||||||
// set the bits in each task's notification value
|
{
|
||||||
//ESP_LOGI("g", "Notify: %p", em->subscribers[i]);
|
ImuData_t imu;
|
||||||
xTaskNotify(em->subscribers[i],
|
xSemaphoreTake(_eventManager.mutex, portMAX_DELAY);
|
||||||
eventBits,
|
|
||||||
eSetBits);
|
imu = _systemState.imu;
|
||||||
}
|
|
||||||
xSemaphoreGive(em->mutex);
|
xSemaphoreGive(_eventManager.mutex);
|
||||||
}
|
|
||||||
|
return imu;
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_waitForEvent(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemState_t *system_getState(void)
|
||||||
|
{
|
||||||
|
return &_systemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BaseType_t system_subscribe(TaskHandle_t task) {
|
||||||
|
|
||||||
|
EventManager_t *em = &_eventManager;
|
||||||
|
|
||||||
|
ESP_LOGI("g", "Subscribe: %p", task);
|
||||||
|
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdFALSE) {
|
||||||
|
return pdFAIL;
|
||||||
|
}
|
||||||
|
if (em->count < EM_MAX_SUBSCRIBERS) {
|
||||||
|
// avoid duplicates?
|
||||||
|
ESP_LOGI("g", "PASS");
|
||||||
|
em->subscribers[em->count++] = task;
|
||||||
|
xSemaphoreGive(em->mutex);
|
||||||
|
return pdPASS;
|
||||||
|
}
|
||||||
|
xSemaphoreGive(em->mutex);
|
||||||
|
return pdFAIL; // full
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseType_t system_unsubscribe(TaskHandle_t task) {
|
||||||
|
|
||||||
|
EventManager_t *em = &_eventManager;
|
||||||
|
|
||||||
|
BaseType_t removed = pdFAIL;
|
||||||
|
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdTRUE) {
|
||||||
|
for (size_t i = 0; i < em->count; ++i) {
|
||||||
|
if (em->subscribers[i] == task) {
|
||||||
|
// shift down
|
||||||
|
for (size_t j = i; j + 1 < em->count; ++j)
|
||||||
|
em->subscribers[j] = em->subscribers[j+1];
|
||||||
|
em->count--;
|
||||||
|
removed = pdPASS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xSemaphoreGive(em->mutex);
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void system_notifyAll(uint32_t eventBits) {
|
||||||
|
EventManager_t *em = &_eventManager;
|
||||||
|
if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdTRUE) {
|
||||||
|
for (size_t i = 0; i < em->count; ++i) {
|
||||||
|
// set the bits in each task's notification value
|
||||||
|
//ESP_LOGI("g", "Notify: %p", em->subscribers[i]);
|
||||||
|
xTaskNotify(em->subscribers[i],
|
||||||
|
eventBits,
|
||||||
|
eSetBits);
|
||||||
|
}
|
||||||
|
xSemaphoreGive(em->mutex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
135
main/system.h
135
main/system.h
@@ -1,62 +1,75 @@
|
|||||||
#ifndef SYSTEM_H
|
#ifndef SYSTEM_H
|
||||||
#define SYSTEM_H
|
#define SYSTEM_H
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
enum
|
#define DISABLE_GUI
|
||||||
{
|
|
||||||
ANGLE_XY = 0,
|
enum
|
||||||
ANGLE_XZ,
|
{
|
||||||
ANGLE_YZ,
|
ANGLE_XY = 0,
|
||||||
};
|
ANGLE_XZ,
|
||||||
|
ANGLE_YZ,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
float raw[3];
|
typedef struct
|
||||||
float filtered[3];
|
{
|
||||||
float angle;
|
float raw[3];
|
||||||
} ImuData_t;
|
float filtered[3];
|
||||||
|
float angle;
|
||||||
typedef struct SystemState_s
|
} ImuData_t;
|
||||||
{
|
|
||||||
ImuData_t imu;
|
typedef struct SystemState_s
|
||||||
|
{
|
||||||
EventGroupHandle_t event;
|
ImuData_t imu;
|
||||||
|
|
||||||
|
EventGroupHandle_t event;
|
||||||
} SystemState_t;
|
|
||||||
|
float zeroAngle;
|
||||||
|
int primaryAxis;
|
||||||
#define MAX_INDICATION_ANGLE 10.0f
|
|
||||||
|
} SystemState_t;
|
||||||
#define EM_MAX_SUBSCRIBERS 8 // tweak as needed
|
|
||||||
#define EM_EVENT_NEW_DATA (1UL<<0)
|
|
||||||
#define EM_EVENT_ERROR (1UL<<1)
|
#define MAX_INDICATION_ANGLE 5.0f
|
||||||
// …add more event bit-masks here…
|
#define FILTER_COEFF 0.2f // 0 to 1 Smaller number is heavier filter
|
||||||
|
#define PRIMARY_AXIS ANGLE_YZ
|
||||||
typedef struct {
|
|
||||||
TaskHandle_t subscribers[EM_MAX_SUBSCRIBERS];
|
#define EM_MAX_SUBSCRIBERS 8 // tweak as needed
|
||||||
size_t count;
|
#define EM_EVENT_NEW_DATA (1UL<<0)
|
||||||
SemaphoreHandle_t mutex;
|
#define EM_EVENT_ERROR (1UL<<1)
|
||||||
} EventManager_t;
|
// …add more event bit-masks here…
|
||||||
|
|
||||||
void system_init(void);
|
typedef struct {
|
||||||
void system_setImuData(ImuData_t imu);
|
TaskHandle_t subscribers[EM_MAX_SUBSCRIBERS];
|
||||||
|
size_t count;
|
||||||
ImuData_t system_getImuData(void);
|
SemaphoreHandle_t mutex;
|
||||||
|
} EventManager_t;
|
||||||
// Subscribe (register) current task to receive events
|
|
||||||
BaseType_t system_subscribe(TaskHandle_t task);
|
void system_init(void);
|
||||||
|
void system_setImuData(ImuData_t imu);
|
||||||
// Unsubscribe if you ever need
|
|
||||||
BaseType_t system_unsubscribe(TaskHandle_t task);
|
ImuData_t system_getImuData(void);
|
||||||
|
|
||||||
// Notify all subscribers of one or more event bits
|
int system_getPrimaryAxis(void);
|
||||||
void system_notifyAll(uint32_t eventBits);
|
float system_getAngle(void);
|
||||||
|
|
||||||
|
void system_setZeroAngle(void);
|
||||||
|
void system_clearZeroAngle(void);
|
||||||
|
float system_getZeroAngle(void);
|
||||||
|
|
||||||
|
// Subscribe (register) current task to receive events
|
||||||
|
BaseType_t system_subscribe(TaskHandle_t task);
|
||||||
|
|
||||||
|
// Unsubscribe if you ever need
|
||||||
|
BaseType_t system_unsubscribe(TaskHandle_t task);
|
||||||
|
|
||||||
|
// Notify all subscribers of one or more event bits
|
||||||
|
void system_notifyAll(uint32_t eventBits);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Name, Type, SubType, Offset, Size, Flags
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap,,,,
|
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap,,,,
|
||||||
nvs, data, nvs, , 0x8000,
|
nvs, data, nvs, , 0x8000,
|
||||||
otadata, data, ota, , 0x2000,
|
otadata, data, ota, , 0x2000,
|
||||||
phy_init, data, phy, , 0x1000,
|
phy_init, data, phy, , 0x1000,
|
||||||
factory, app, factory, , 0x1B6000,
|
factory, app, factory, , 0x1B6000,
|
||||||
|
|||||||
|
25
sdkconfig
25
sdkconfig
@@ -353,14 +353,14 @@ CONFIG_ESPTOOLPY_FLASHFREQ_40M=y
|
|||||||
# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set
|
# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set
|
||||||
CONFIG_ESPTOOLPY_FLASHFREQ="40m"
|
CONFIG_ESPTOOLPY_FLASHFREQ="40m"
|
||||||
# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set
|
# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set
|
||||||
CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y
|
# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set
|
||||||
# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set
|
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||||
# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set
|
# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set
|
||||||
# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set
|
# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set
|
||||||
# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set
|
# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set
|
||||||
# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set
|
# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set
|
||||||
# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set
|
# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set
|
||||||
CONFIG_ESPTOOLPY_FLASHSIZE="2MB"
|
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
|
||||||
# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set
|
# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set
|
||||||
CONFIG_ESPTOOLPY_BEFORE_RESET=y
|
CONFIG_ESPTOOLPY_BEFORE_RESET=y
|
||||||
# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set
|
# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set
|
||||||
@@ -393,8 +393,8 @@ CONFIG_EXAMPLE_SSP_ENABLED=y
|
|||||||
#
|
#
|
||||||
# Compiler options
|
# Compiler options
|
||||||
#
|
#
|
||||||
CONFIG_COMPILER_OPTIMIZATION_DEBUG=y
|
# CONFIG_COMPILER_OPTIMIZATION_DEBUG is not set
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set
|
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_PERF is not set
|
# CONFIG_COMPILER_OPTIMIZATION_PERF is not set
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_NONE is not set
|
# CONFIG_COMPILER_OPTIMIZATION_NONE is not set
|
||||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
|
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
|
||||||
@@ -1448,7 +1448,6 @@ CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1
|
|||||||
#
|
#
|
||||||
# Port
|
# Port
|
||||||
#
|
#
|
||||||
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
|
|
||||||
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
|
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
|
||||||
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
|
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
|
||||||
# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set
|
# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set
|
||||||
@@ -1497,7 +1496,7 @@ CONFIG_HEAP_TRACING_OFF=y
|
|||||||
# CONFIG_HEAP_TRACING_TOHOST is not set
|
# CONFIG_HEAP_TRACING_TOHOST is not set
|
||||||
# CONFIG_HEAP_USE_HOOKS is not set
|
# CONFIG_HEAP_USE_HOOKS is not set
|
||||||
# CONFIG_HEAP_TASK_TRACKING is not set
|
# CONFIG_HEAP_TASK_TRACKING is not set
|
||||||
# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set
|
CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS=y
|
||||||
# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set
|
# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set
|
||||||
# end of Heap memory debugging
|
# end of Heap memory debugging
|
||||||
|
|
||||||
@@ -1561,7 +1560,7 @@ CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y
|
|||||||
# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set
|
# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set
|
||||||
CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y
|
CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y
|
||||||
# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set
|
# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set
|
||||||
CONFIG_LWIP_DHCP_OPTIONS_LEN=68
|
CONFIG_LWIP_DHCP_OPTIONS_LEN=69
|
||||||
CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=0
|
CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=0
|
||||||
CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1
|
CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1
|
||||||
|
|
||||||
@@ -2496,11 +2495,11 @@ CONFIG_LOG_BOOTLOADER_LEVEL=3
|
|||||||
CONFIG_FLASHMODE_DIO=y
|
CONFIG_FLASHMODE_DIO=y
|
||||||
# CONFIG_FLASHMODE_DOUT is not set
|
# CONFIG_FLASHMODE_DOUT is not set
|
||||||
CONFIG_MONITOR_BAUD=115200
|
CONFIG_MONITOR_BAUD=115200
|
||||||
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
|
# CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set
|
||||||
CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y
|
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set
|
||||||
CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y
|
# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set
|
||||||
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
|
CONFIG_OPTIMIZATION_LEVEL_RELEASE=y
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
|
CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
|
||||||
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
|
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
|
||||||
# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
|
# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
|
||||||
# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
|
# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
|
||||||
|
|||||||
9
sdkconfig.defaults
Normal file
9
sdkconfig.defaults
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Override some defaults so BT stack is enabled and
|
||||||
|
# Classic BT is enabled
|
||||||
|
CONFIG_BT_ENABLED=y
|
||||||
|
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n
|
||||||
|
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y
|
||||||
|
CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||||
|
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||||
|
CONFIG_BT_CLASSIC_ENABLED=y
|
||||||
|
CONFIG_BT_A2DP_ENABLE=y
|
||||||
2888
sdkconfig.old
Normal file
2888
sdkconfig.old
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user