Compare commits
5 Commits
e5e1d5b85e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 92bead72b9 | |||
| 5ef6c20862 | |||
| b154c3fa20 | |||
| efb3c8f304 | |||
| ff7d82286a |
@@ -13,12 +13,12 @@
|
|||||||
| | 叠封 PSRAM | **8MB** | 用于运行代码,性能优异 |
|
| | 叠封 PSRAM | **8MB** | 用于运行代码,性能优异 |
|
||||||
| | 外接 Flash | **16MB** (W25Q128) | 存储程序和数据 |
|
| | 外接 Flash | **16MB** (W25Q128) | 存储程序和数据 |
|
||||||
| **显示与触摸** | LCD 屏幕 | 1.9英寸,170×320分辨率,262K彩色 | 驱动IC为ST7789V2 |
|
| **显示与触摸** | LCD 屏幕 | 1.9英寸,170×320分辨率,262K彩色 | 驱动IC为ST7789V2 |
|
||||||
| | 触摸控制芯片 | **CST816** (电容式) | **触控版特有**,通过I2C接口 |
|
| | 触摸控制芯片 | **CST816** (电容式) | **触控版特有**,I2C 与 IMU 共用 IO47/IO48 |
|
||||||
| **无线通信** | Wi-Fi | 2.4GHz, 802.11 b/g/n | |
|
| **无线通信** | Wi-Fi | 2.4GHz, 802.11 b/g/n | |
|
||||||
| | 蓝牙 | Bluetooth 5 (BLE) | |
|
| | 蓝牙 | Bluetooth 5 (BLE) | |
|
||||||
| | 板载天线 | 贴片陶瓷天线 | |
|
| | 板载天线 | 贴片陶瓷天线 | |
|
||||||
| | 外部天线接口 | **IPEX-1 连接器** | **触控版特有**,需修改焊盘启用 |
|
| | 外部天线接口 | **IPEX-1 连接器** | **触控版特有**,需修改焊盘启用 |
|
||||||
| **传感器** | 六轴惯性测量单元 | **QMI8658** | 包含3轴加速度计和3轴陀螺仪 |
|
| **传感器** | 六轴惯性测量单元 | **QMI8658A** | 与 CST816 共用 I2C 总线(IO47/IO48) |
|
||||||
| **存储扩展** | Micro SD 卡槽 | 支持Micro SD卡 | 位于PCB背面 |
|
| **存储扩展** | Micro SD 卡槽 | 支持Micro SD卡 | 位于PCB背面 |
|
||||||
| **电源管理** | 电池接口 | MX1.25 连接器 | 支持3.7V锂电池**充放电** |
|
| **电源管理** | 电池接口 | MX1.25 连接器 | 支持3.7V锂电池**充放电** |
|
||||||
| | USB供电 | USB Type-C 接口 | 同时用于下载调试 |
|
| | USB供电 | USB Type-C 接口 | 同时用于下载调试 |
|
||||||
@@ -37,18 +37,18 @@
|
|||||||
| | | IO12 | LCD\_CS(片选) |
|
| | | IO12 | LCD\_CS(片选) |
|
||||||
| | | IO13 | LCD\_DIN(数据输入) |
|
| | | IO13 | LCD\_DIN(数据输入) |
|
||||||
| | | IO14 | LCD\_BL(背光控制,PWM) |
|
| | | IO14 | LCD\_BL(背光控制,PWM) |
|
||||||
| **触摸屏** | I2C | IO47 | TP\_SDA(数据线) |
|
| **触摸屏 / I2C 总线** | I2C | IO47 | TP\_SDA / IMU\_SDA(与 IMU 共用) |
|
||||||
| | | IO48 | TP\_SCL(时钟线) |
|
| (CST816 + QMI8658) | | IO48 | TP\_SCL / IMU\_SCL(与 IMU 共用) |
|
||||||
| | GPIO | IO17 | TP\_RESET(复位) |
|
| | GPIO | IO17 | TP\_RESET(复位) |
|
||||||
| | | IO21 | TP\_INT(中断) |
|
| | | IO21 | TP\_INT(中断) |
|
||||||
| **SD 卡** | SPI | IO5 | SD\_CS(片选) |
|
| **SD 卡** | SPI | IO5 | SD\_CS(片选) |
|
||||||
| | | IO39 | SD\_CLK(时钟) |
|
| | | IO39 | SD\_CLK(时钟) |
|
||||||
| | | IO40 | SD\_MISO(主入从出) |
|
| | | IO40 | SD\_MISO(主入从出) |
|
||||||
| | | IO41 | SD\_MOSI(主出从入) |
|
| | | IO41 | SD\_MOSI(主出从入) |
|
||||||
| **IMU 传感器** | I2C | IO8 | IMU\_SDA(数据线) |
|
| **IMU 传感器** | I2C | IO47 | IMU\_SDA(与 TP 共用,见上) |
|
||||||
| (QMI8658A) | | IO7 | IMU\_SCL(时钟线) |
|
| (QMI8658A) | | IO48 | IMU\_SCL(与 TP 共用,见上) |
|
||||||
| | GPIO | IO6 | IMU\_INT1(中断1) |
|
| | GPIO | IO8 | IMU\_INT1(中断1) |
|
||||||
| | | IO15 | IMU\_INT2(中断2) |
|
| | | IO7 | IMU\_INT2(中断2) |
|
||||||
| **UART0 串口** | UART | IO43 | U0\_TX(发送) |
|
| **UART0 串口** | UART | IO43 | U0\_TX(发送) |
|
||||||
| | | IO44 | U0\_RX(接收) |
|
| | | IO44 | U0\_RX(接收) |
|
||||||
| **用户按键** | GPIO | IO0 | BOOT0(启动/下载模式) |
|
| **用户按键** | GPIO | IO0 | BOOT0(启动/下载模式) |
|
||||||
@@ -63,6 +63,17 @@
|
|||||||
| **扩展排针 J3** | 混合 | IO0~IO14 | 20Pin 排针,引出主要 GPIO |
|
| **扩展排针 J3** | 混合 | IO0~IO14 | 20Pin 排针,引出主要 GPIO |
|
||||||
| **扩展排针 J4** | 混合 | IO14~IO48等 | 20Pin 排针,含 USB/3V3/地 |
|
| **扩展排针 J4** | 混合 | IO14~IO48等 | 20Pin 排针,含 USB/3V3/地 |
|
||||||
|
|
||||||
|
### I2C 设备与固件配置
|
||||||
|
|
||||||
|
触控版 PCB 上 **CST816(触摸)** 与 **QMI8658A(IMU)** 挂载在同一条 I2C 总线(`I2C_NUM_0`,SDA=IO47,SCL=IO48)。固件在 `main/bsp/board_config.h` 中定义引脚,触摸与 IMU 驱动共用该总线句柄。
|
||||||
|
|
||||||
|
| 设备 | I2C 地址(7-bit) | 说明 |
|
||||||
|
| :------- | :---------------- | :---------------------------- |
|
||||||
|
| CST816S | 由驱动自动探测 | 电容触摸 |
|
||||||
|
| QMI8658A | `0x6A` 或 `0x6B` | 由 SDO 电平决定;本板实测多为 `0x6B` |
|
||||||
|
|
||||||
|
IMU 中断引脚(IO8 / IO7)当前固件采用轮询采样,Phase 4 可改为中断驱动。
|
||||||
|
|
||||||
## Wi-Fi Station Example
|
## Wi-Fi Station Example
|
||||||
|
|
||||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
idf_component_register(SRCS
|
idf_component_register(SRCS
|
||||||
"app/app_main.c"
|
"app/app_main.c"
|
||||||
"app/ui_handlers.c"
|
"app/ui_handlers.c"
|
||||||
|
"app/ui_nav.c"
|
||||||
|
"app/motion_gesture.c"
|
||||||
"bsp/display.c"
|
"bsp/display.c"
|
||||||
"bsp/touch.c"
|
"bsp/touch.c"
|
||||||
"bsp/lvgl_port.c"
|
"bsp/lvgl_port.c"
|
||||||
"bsp/boot_button.c"
|
"bsp/boot_button.c"
|
||||||
|
"bsp/imu.c"
|
||||||
"services/wifi_manager.c"
|
"services/wifi_manager.c"
|
||||||
"services/ble_manager.c"
|
"services/ble_manager.c"
|
||||||
"services/ble_hid.c"
|
"services/ble_hid.c"
|
||||||
|
|||||||
@@ -3,11 +3,15 @@
|
|||||||
|
|
||||||
#include "board_config.h"
|
#include "board_config.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
#include "imu.h"
|
||||||
#include "lvgl_port.h"
|
#include "lvgl_port.h"
|
||||||
|
#include "motion_gesture.h"
|
||||||
#include "touch.h"
|
#include "touch.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "ui_handlers.h"
|
#include "ui_handlers.h"
|
||||||
|
#include "ui_nav.h"
|
||||||
#include "ble_manager.h"
|
#include "ble_manager.h"
|
||||||
|
#include "settings/settings_store.h"
|
||||||
|
|
||||||
#if BOARD_WIFI_ENABLED
|
#if BOARD_WIFI_ENABLED
|
||||||
#include "wifi_manager.h"
|
#include "wifi_manager.h"
|
||||||
@@ -38,10 +42,25 @@ void app_main(void)
|
|||||||
|
|
||||||
lvgl_port_lock();
|
lvgl_port_lock();
|
||||||
ui_init();
|
ui_init();
|
||||||
|
ui_nav_init();
|
||||||
lvgl_port_unlock();
|
lvgl_port_unlock();
|
||||||
|
|
||||||
|
settings_store_load();
|
||||||
|
settings_store_apply();
|
||||||
|
|
||||||
ui_handlers_init();
|
ui_handlers_init();
|
||||||
|
|
||||||
|
motion_gesture_init(NULL);
|
||||||
|
|
||||||
|
#if BOARD_IMU_ENABLED
|
||||||
|
ret = imu_init();
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
imu_start_task(motion_gesture_on_sample);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "IMU init failed, shake navigation unavailable");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if BOARD_WIFI_ENABLED
|
#if BOARD_WIFI_ENABLED
|
||||||
ESP_LOGI(TAG, "Starting WiFi");
|
ESP_LOGI(TAG, "Starting WiFi");
|
||||||
wifi_manager_init();
|
wifi_manager_init();
|
||||||
|
|||||||
@@ -0,0 +1,295 @@
|
|||||||
|
#include "motion_gesture.h"
|
||||||
|
|
||||||
|
#include "ui_nav.h"
|
||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
static const char *TAG = "motion";
|
||||||
|
|
||||||
|
#define SAMPLE_PERIOD_MS 10
|
||||||
|
#define SAMPLE_RATE_HZ 100
|
||||||
|
|
||||||
|
/* Calibration: keep a sliding window of the last 500 ms and finish only when
|
||||||
|
* the window is stable (low variance). This avoids calibrating while the user
|
||||||
|
* is picking up or moving the device. */
|
||||||
|
#define CALIBRATION_MS 500
|
||||||
|
#define CALIBRATION_WINDOW_SAMPLES (CALIBRATION_MS / SAMPLE_PERIOD_MS)
|
||||||
|
#define CALIBRATION_MAX_VAR_G2 0.010f
|
||||||
|
|
||||||
|
/* Shake is detected on the horizontal plane (X/Y) so that picking the device
|
||||||
|
* up or putting it down (Z-axis jolt) does not trigger navigation. */
|
||||||
|
#define THRESHOLD_LOW_G 0.6f
|
||||||
|
#define THRESHOLD_MED_G 0.8f
|
||||||
|
#define THRESHOLD_HIGH_G 1.0f
|
||||||
|
|
||||||
|
/* A valid shake must produce enough strong samples within a short window. */
|
||||||
|
#define WINDOW_MS 300
|
||||||
|
#define MIN_SAMPLES 5
|
||||||
|
|
||||||
|
/* A real shake oscillates; require at least two zero-crossings on the
|
||||||
|
* dominant horizontal axis to distinguish it from a single jolt. */
|
||||||
|
#define MIN_ZERO_CROSSINGS 2
|
||||||
|
|
||||||
|
/* Cooldown prevents one physical shake from generating multiple events. */
|
||||||
|
#define COOLDOWN_MS 350
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GESTURE_IDLE = 0,
|
||||||
|
GESTURE_COOLDOWN,
|
||||||
|
} gesture_state_t;
|
||||||
|
|
||||||
|
static QueueHandle_t s_nav_queue;
|
||||||
|
static bool s_enabled = true;
|
||||||
|
static float s_threshold = THRESHOLD_MED_G;
|
||||||
|
static gesture_state_t s_state = GESTURE_IDLE;
|
||||||
|
|
||||||
|
/* Baseline calibration */
|
||||||
|
static float s_ref_x;
|
||||||
|
static float s_ref_y;
|
||||||
|
static float s_ref_z;
|
||||||
|
static bool s_calibrated;
|
||||||
|
|
||||||
|
static float s_cal_buf_x[CALIBRATION_WINDOW_SAMPLES];
|
||||||
|
static float s_cal_buf_y[CALIBRATION_WINDOW_SAMPLES];
|
||||||
|
static float s_cal_buf_z[CALIBRATION_WINDOW_SAMPLES];
|
||||||
|
static int s_cal_head;
|
||||||
|
static int s_cal_count;
|
||||||
|
|
||||||
|
/* Detection window state */
|
||||||
|
static int s_over_count;
|
||||||
|
static int64_t s_window_start_us;
|
||||||
|
static int s_zero_crossings;
|
||||||
|
static int s_last_x_sign;
|
||||||
|
static int s_last_y_sign;
|
||||||
|
static int64_t s_cooldown_until_us;
|
||||||
|
|
||||||
|
/* Last horizontal magnitude, exposed for UI/debug. */
|
||||||
|
static float s_last_horizontal_mag;
|
||||||
|
|
||||||
|
static void nav_handler_task(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
motion_nav_event_t ev;
|
||||||
|
|
||||||
|
while (xQueueReceive(s_nav_queue, &ev, portMAX_DELAY) == pdTRUE) {
|
||||||
|
if (ev == MOTION_NAV_NEXT) {
|
||||||
|
ui_nav_next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void post_nav_next(void)
|
||||||
|
{
|
||||||
|
motion_nav_event_t ev = MOTION_NAV_NEXT;
|
||||||
|
if (xQueueSend(s_nav_queue, &ev, 0) != pdTRUE) {
|
||||||
|
ESP_LOGW(TAG, "nav queue full");
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "Shake detected -> next screen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_window(void)
|
||||||
|
{
|
||||||
|
s_over_count = 0;
|
||||||
|
s_window_start_us = 0;
|
||||||
|
s_zero_crossings = 0;
|
||||||
|
s_last_x_sign = 0;
|
||||||
|
s_last_y_sign = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_calibration(float ax, float ay, float az)
|
||||||
|
{
|
||||||
|
s_cal_buf_x[s_cal_head] = ax;
|
||||||
|
s_cal_buf_y[s_cal_head] = ay;
|
||||||
|
s_cal_buf_z[s_cal_head] = az;
|
||||||
|
s_cal_head = (s_cal_head + 1) % CALIBRATION_WINDOW_SAMPLES;
|
||||||
|
if (s_cal_count < CALIBRATION_WINDOW_SAMPLES) {
|
||||||
|
s_cal_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_cal_count < CALIBRATION_WINDOW_SAMPLES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sum_x = 0.0f;
|
||||||
|
float sum_y = 0.0f;
|
||||||
|
float sum_z = 0.0f;
|
||||||
|
float sum_sq_x = 0.0f;
|
||||||
|
float sum_sq_y = 0.0f;
|
||||||
|
float sum_sq_z = 0.0f;
|
||||||
|
|
||||||
|
for (int i = 0; i < CALIBRATION_WINDOW_SAMPLES; i++) {
|
||||||
|
sum_x += s_cal_buf_x[i];
|
||||||
|
sum_y += s_cal_buf_y[i];
|
||||||
|
sum_z += s_cal_buf_z[i];
|
||||||
|
sum_sq_x += s_cal_buf_x[i] * s_cal_buf_x[i];
|
||||||
|
sum_sq_y += s_cal_buf_y[i] * s_cal_buf_y[i];
|
||||||
|
sum_sq_z += s_cal_buf_z[i] * s_cal_buf_z[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const int n = CALIBRATION_WINDOW_SAMPLES;
|
||||||
|
float var_x = (sum_sq_x / n) - (sum_x / n) * (sum_x / n);
|
||||||
|
float var_y = (sum_sq_y / n) - (sum_y / n) * (sum_y / n);
|
||||||
|
float var_z = (sum_sq_z / n) - (sum_z / n) * (sum_z / n);
|
||||||
|
float max_var = fmaxf(var_x, fmaxf(var_y, var_z));
|
||||||
|
|
||||||
|
if (max_var > CALIBRATION_MAX_VAR_G2) {
|
||||||
|
static int64_t s_last_cal_warn_us;
|
||||||
|
int64_t now_us = esp_timer_get_time();
|
||||||
|
if ((now_us - s_last_cal_warn_us) >= 1000 * 1000) {
|
||||||
|
s_last_cal_warn_us = now_us;
|
||||||
|
ESP_LOGW(TAG, "Calibration: motion detected (var=%.3f), waiting", max_var);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_ref_x = sum_x / n;
|
||||||
|
s_ref_y = sum_y / n;
|
||||||
|
s_ref_z = sum_z / n;
|
||||||
|
s_calibrated = true;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Baseline calibrated (ref=%.3f,%.3f,%.3f g)", s_ref_x, s_ref_y, s_ref_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int axis_sign(float value)
|
||||||
|
{
|
||||||
|
if (value > 0.0f) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (value < 0.0f) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_sample(float ax, float ay, float az)
|
||||||
|
{
|
||||||
|
int64_t now_us = esp_timer_get_time();
|
||||||
|
|
||||||
|
if (!s_calibrated) {
|
||||||
|
run_calibration(ax, ay, az);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dx = ax - s_ref_x;
|
||||||
|
float dy = ay - s_ref_y;
|
||||||
|
float dz = az - s_ref_z;
|
||||||
|
|
||||||
|
/* Use only horizontal magnitude for shake detection. Z-axis is dominated by
|
||||||
|
* gravity and is the main axis for pick-up/put-down false triggers. */
|
||||||
|
float horizontal_mag = sqrtf(dx * dx + dy * dy);
|
||||||
|
s_last_horizontal_mag = horizontal_mag;
|
||||||
|
|
||||||
|
if (s_state == GESTURE_COOLDOWN) {
|
||||||
|
if (now_us >= s_cooldown_until_us) {
|
||||||
|
s_state = GESTURE_IDLE;
|
||||||
|
reset_window();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (horizontal_mag > s_threshold) {
|
||||||
|
if (s_over_count == 0) {
|
||||||
|
s_window_start_us = now_us;
|
||||||
|
}
|
||||||
|
s_over_count++;
|
||||||
|
|
||||||
|
int x_sign = axis_sign(dx);
|
||||||
|
int y_sign = axis_sign(dy);
|
||||||
|
if (s_last_x_sign != 0 && x_sign != s_last_x_sign) {
|
||||||
|
s_zero_crossings++;
|
||||||
|
}
|
||||||
|
if (s_last_y_sign != 0 && y_sign != s_last_y_sign) {
|
||||||
|
s_zero_crossings++;
|
||||||
|
}
|
||||||
|
s_last_x_sign = x_sign;
|
||||||
|
s_last_y_sign = y_sign;
|
||||||
|
|
||||||
|
int64_t window_us = (int64_t)WINDOW_MS * 1000;
|
||||||
|
if ((now_us - s_window_start_us) > window_us) {
|
||||||
|
/* Window expired: start fresh with the current sample. */
|
||||||
|
reset_window();
|
||||||
|
s_window_start_us = now_us;
|
||||||
|
s_over_count = 1;
|
||||||
|
s_last_x_sign = axis_sign(dx);
|
||||||
|
s_last_y_sign = axis_sign(dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_over_count >= MIN_SAMPLES && s_zero_crossings >= MIN_ZERO_CROSSINGS) {
|
||||||
|
if (s_enabled) {
|
||||||
|
post_nav_next();
|
||||||
|
}
|
||||||
|
s_state = GESTURE_COOLDOWN;
|
||||||
|
s_cooldown_until_us = now_us + (int64_t)COOLDOWN_MS * 1000;
|
||||||
|
reset_window();
|
||||||
|
}
|
||||||
|
} else if (s_over_count > 0) {
|
||||||
|
int64_t window_us = (int64_t)WINDOW_MS * 1000;
|
||||||
|
if ((now_us - s_window_start_us) > window_us) {
|
||||||
|
reset_window();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)dz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void motion_gesture_init(motion_nav_cb_t cb)
|
||||||
|
{
|
||||||
|
(void)cb;
|
||||||
|
|
||||||
|
s_nav_queue = xQueueCreate(4, sizeof(motion_nav_event_t));
|
||||||
|
xTaskCreate(nav_handler_task, "nav_handler", 2048, NULL, 5, NULL);
|
||||||
|
|
||||||
|
s_calibrated = false;
|
||||||
|
s_cal_head = 0;
|
||||||
|
s_cal_count = 0;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Motion gesture ready (calibrating %d ms)", CALIBRATION_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void motion_gesture_set_enabled(bool enabled)
|
||||||
|
{
|
||||||
|
s_enabled = enabled;
|
||||||
|
ESP_LOGI(TAG, "Shake navigation %s", enabled ? "enabled" : "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
void motion_gesture_set_sensitivity(shake_sensitivity_t sens)
|
||||||
|
{
|
||||||
|
switch (sens) {
|
||||||
|
case SHAKE_SENS_LOW:
|
||||||
|
s_threshold = THRESHOLD_LOW_G;
|
||||||
|
break;
|
||||||
|
case SHAKE_SENS_MED:
|
||||||
|
s_threshold = THRESHOLD_MED_G;
|
||||||
|
break;
|
||||||
|
case SHAKE_SENS_HIGH:
|
||||||
|
s_threshold = THRESHOLD_HIGH_G;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false && "unexpected shake_sensitivity_t");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Sensitivity set (threshold=%.2f g)", s_threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
void motion_gesture_on_sample(float ax_g, float ay_g, float az_g)
|
||||||
|
{
|
||||||
|
process_sample(ax_g, ay_g, az_g);
|
||||||
|
}
|
||||||
|
|
||||||
|
float motion_gesture_get_last_magnitude(void)
|
||||||
|
{
|
||||||
|
return s_last_horizontal_mag;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool motion_gesture_is_calibrated(void)
|
||||||
|
{
|
||||||
|
return s_calibrated;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "settings_types.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MOTION_NAV_NEXT = 0,
|
||||||
|
} motion_nav_event_t;
|
||||||
|
|
||||||
|
typedef void (*motion_nav_cb_t)(motion_nav_event_t event);
|
||||||
|
|
||||||
|
void motion_gesture_init(motion_nav_cb_t cb);
|
||||||
|
void motion_gesture_set_enabled(bool enabled);
|
||||||
|
void motion_gesture_set_sensitivity(shake_sensitivity_t sens);
|
||||||
|
void motion_gesture_on_sample(float ax_g, float ay_g, float az_g);
|
||||||
|
|
||||||
|
float motion_gesture_get_last_magnitude(void);
|
||||||
|
bool motion_gesture_is_calibrated(void);
|
||||||
@@ -105,6 +105,12 @@ void settings_on_value_changed(lv_event_t *e)
|
|||||||
case SETTINGS_FIELD_MUTE:
|
case SETTINGS_FIELD_MUTE:
|
||||||
buf.mute = lv_obj_has_state(target, LV_STATE_CHECKED);
|
buf.mute = lv_obj_has_state(target, LV_STATE_CHECKED);
|
||||||
break;
|
break;
|
||||||
|
case SETTINGS_FIELD_SHAKE_NAV:
|
||||||
|
buf.shake_nav_enabled = lv_obj_has_state(target, LV_STATE_CHECKED);
|
||||||
|
break;
|
||||||
|
case SETTINGS_FIELD_SHAKE_SENS:
|
||||||
|
buf.shake_sensitivity = (shake_sensitivity_t)lv_dropdown_get_selected(target);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LV_ASSERT_MSG(false, "unexpected settings_field_t");
|
LV_ASSERT_MSG(false, "unexpected settings_field_t");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#include "ui_nav.h"
|
||||||
|
|
||||||
|
#include "lvgl_port.h"
|
||||||
|
#include "screens/ui_Screen1.h"
|
||||||
|
#include "screens/ui_Screen2.h"
|
||||||
|
#include "screens/ui_Screen3.h"
|
||||||
|
#include "screens/ui_Settings.h"
|
||||||
|
#include "ui_helpers.h"
|
||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
static const char *TAG = "ui_nav";
|
||||||
|
|
||||||
|
static lv_obj_t *s_nav_screens[4];
|
||||||
|
static const char *const s_nav_names[] = {
|
||||||
|
"Settings",
|
||||||
|
"Screen1",
|
||||||
|
"Screen2",
|
||||||
|
"Screen3",
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NAV_SCREEN_COUNT (sizeof(s_nav_screens) / sizeof(s_nav_screens[0]))
|
||||||
|
|
||||||
|
static uint8_t s_nav_index;
|
||||||
|
|
||||||
|
void ui_nav_init(void)
|
||||||
|
{
|
||||||
|
s_nav_screens[0] = ui_Settings;
|
||||||
|
s_nav_screens[1] = ui_Screen1;
|
||||||
|
s_nav_screens[2] = ui_Screen2;
|
||||||
|
s_nav_screens[3] = ui_Screen3;
|
||||||
|
s_nav_index = 0;
|
||||||
|
ESP_LOGI(TAG, "Navigation ready (%u screens, start=%s)", (unsigned)NAV_SCREEN_COUNT,
|
||||||
|
s_nav_names[s_nav_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_nav_goto(uint8_t index)
|
||||||
|
{
|
||||||
|
if (index >= NAV_SCREEN_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lvgl_port_lock();
|
||||||
|
s_nav_index = index;
|
||||||
|
lv_obj_t **target = &s_nav_screens[s_nav_index];
|
||||||
|
_ui_screen_change(target, LV_SCR_LOAD_ANIM_FADE_IN, 300, 0, NULL);
|
||||||
|
lvgl_port_unlock();
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Goto %s (index %u)", s_nav_names[s_nav_index], s_nav_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_nav_next(void)
|
||||||
|
{
|
||||||
|
lvgl_port_lock();
|
||||||
|
s_nav_index = (s_nav_index + 1) % NAV_SCREEN_COUNT;
|
||||||
|
lv_obj_t **target = &s_nav_screens[s_nav_index];
|
||||||
|
_ui_screen_change(target, LV_SCR_LOAD_ANIM_FADE_IN, 300, 0, NULL);
|
||||||
|
lvgl_port_unlock();
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Next -> %s (index %u)", s_nav_names[s_nav_index], s_nav_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_t *ui_nav_current(void)
|
||||||
|
{
|
||||||
|
return s_nav_screens[s_nav_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ui_nav_current_index(void)
|
||||||
|
{
|
||||||
|
return s_nav_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ui_nav_current_name(void)
|
||||||
|
{
|
||||||
|
return s_nav_names[s_nav_index];
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "lvgl.h"
|
||||||
|
|
||||||
|
void ui_nav_init(void);
|
||||||
|
void ui_nav_next(void);
|
||||||
|
void ui_nav_goto(uint8_t index);
|
||||||
|
|
||||||
|
lv_obj_t *ui_nav_current(void);
|
||||||
|
uint8_t ui_nav_current_index(void);
|
||||||
|
const char *ui_nav_current_name(void);
|
||||||
@@ -49,6 +49,15 @@
|
|||||||
#define BOARD_TOUCH_I2C_FREQ_HZ 400000
|
#define BOARD_TOUCH_I2C_FREQ_HZ 400000
|
||||||
#define BOARD_TOUCH_ENABLED 1
|
#define BOARD_TOUCH_ENABLED 1
|
||||||
|
|
||||||
|
/* IMU (QMI8658) — shares I2C bus with touch on IO47/IO48 */
|
||||||
|
#define BOARD_IMU_I2C_NUM I2C_NUM_0
|
||||||
|
#define BOARD_PIN_IMU_SDA 47
|
||||||
|
#define BOARD_PIN_IMU_SCL 48
|
||||||
|
#define BOARD_PIN_IMU_INT1 8
|
||||||
|
#define BOARD_PIN_IMU_INT2 7
|
||||||
|
#define BOARD_IMU_I2C_FREQ_HZ 400000
|
||||||
|
#define BOARD_IMU_ENABLED 1
|
||||||
|
|
||||||
/* User IO */
|
/* User IO */
|
||||||
#define BOARD_PIN_BOOT 0
|
#define BOARD_PIN_BOOT 0
|
||||||
#define BOARD_PIN_KEY 4
|
#define BOARD_PIN_KEY 4
|
||||||
|
|||||||
+1
-4
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "board_config.h"
|
#include "board_config.h"
|
||||||
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
#include "driver/ledc.h"
|
#include "driver/ledc.h"
|
||||||
#include "driver/spi_master.h"
|
#include "driver/spi_master.h"
|
||||||
#include "esp_lcd_panel_ops.h"
|
#include "esp_lcd_panel_ops.h"
|
||||||
@@ -49,6 +48,7 @@ esp_err_t display_init(esp_lcd_panel_io_handle_t *io_handle_out,
|
|||||||
.gpio_num = BOARD_PIN_BK_LIGHT,
|
.gpio_num = BOARD_PIN_BK_LIGHT,
|
||||||
.duty = 0,
|
.duty = 0,
|
||||||
.hpoint = 0,
|
.hpoint = 0,
|
||||||
|
.flags.output_invert = 1,
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||||
|
|
||||||
@@ -90,9 +90,6 @@ esp_err_t display_init(esp_lcd_panel_io_handle_t *io_handle_out,
|
|||||||
ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, BOARD_LCD_GAP_X, BOARD_LCD_GAP_Y));
|
ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, BOARD_LCD_GAP_X, BOARD_LCD_GAP_Y));
|
||||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
|
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Turn on LCD backlight");
|
|
||||||
gpio_set_level(BOARD_PIN_BK_LIGHT, BOARD_LCD_BK_LIGHT_ON_LEVEL);
|
|
||||||
|
|
||||||
*io_handle_out = io_handle;
|
*io_handle_out = io_handle;
|
||||||
*panel_handle_out = panel_handle;
|
*panel_handle_out = panel_handle;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
|||||||
+308
@@ -0,0 +1,308 @@
|
|||||||
|
#include "imu.h"
|
||||||
|
|
||||||
|
#include "board_config.h"
|
||||||
|
#include "touch.h"
|
||||||
|
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
static const char *TAG = "imu";
|
||||||
|
|
||||||
|
#if BOARD_IMU_ENABLED
|
||||||
|
|
||||||
|
#define QMI8658_ADDR_SDO_LOW 0x6A
|
||||||
|
#define QMI8658_ADDR_SDO_HIGH 0x6B
|
||||||
|
#define QMI8658_WHO_AM_I 0x00
|
||||||
|
#define QMI8658_WHO_AM_I_ID 0x05
|
||||||
|
#define QMI8658_CTRL1 0x02
|
||||||
|
#define QMI8658_CTRL2 0x03
|
||||||
|
#define QMI8658_CTRL3 0x04
|
||||||
|
#define QMI8658_CTRL5 0x06
|
||||||
|
#define QMI8658_CTRL6 0x07
|
||||||
|
#define QMI8658_CTRL7 0x08
|
||||||
|
#define QMI8658_RESET 0x60
|
||||||
|
#define QMI8658_ACC_X_L 0x35
|
||||||
|
|
||||||
|
#define QMI8658_RESET_VALUE 0xB0
|
||||||
|
/* Little-endian + address auto-increment (matches int16 parse below) */
|
||||||
|
#define QMI8658_CTRL1_VALUE 0x40
|
||||||
|
/* ±8g, 1000 Hz ODR */
|
||||||
|
#define QMI8658_CTRL2_VALUE 0x23
|
||||||
|
/* Gyro ±512 dps, 1000 Hz — required even when only reading accel */
|
||||||
|
#define QMI8658_CTRL3_VALUE 0x53
|
||||||
|
#define QMI8658_CTRL5_VALUE 0x11
|
||||||
|
#define QMI8658_CTRL6_VALUE 0x00
|
||||||
|
#define QMI8658_CTRL7_VALUE 0x03
|
||||||
|
|
||||||
|
#define QMI8658_ACCEL_SENS_8G 4096.0f
|
||||||
|
|
||||||
|
#define IMU_SAMPLE_PERIOD_MS 10
|
||||||
|
#define IMU_DEBUG_LOG_INTERVAL_MS 1000
|
||||||
|
|
||||||
|
static i2c_master_bus_handle_t s_imu_bus;
|
||||||
|
static i2c_master_dev_handle_t s_imu_dev;
|
||||||
|
static imu_sample_cb_t s_sample_cb;
|
||||||
|
|
||||||
|
static esp_err_t imu_read_raw(int16_t *rx, int16_t *ry, int16_t *rz);
|
||||||
|
|
||||||
|
static int16_t imu_raw16_le(const uint8_t *lo, const uint8_t *hi)
|
||||||
|
{
|
||||||
|
return (int16_t)((*hi << 8) | *lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t imu_write_u8(uint8_t reg, uint8_t val)
|
||||||
|
{
|
||||||
|
uint8_t buf[2] = {reg, val};
|
||||||
|
return i2c_master_transmit(s_imu_dev, buf, sizeof(buf), 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t imu_read_u8(uint8_t reg, uint8_t *val)
|
||||||
|
{
|
||||||
|
return i2c_master_transmit_receive(s_imu_dev, ®, 1, val, 1, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t imu_read_bytes(uint8_t reg, uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
return i2c_master_transmit_receive(s_imu_dev, ®, 1, data, len, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t imu_probe_device(uint8_t addr)
|
||||||
|
{
|
||||||
|
i2c_device_config_t dev_cfg = {
|
||||||
|
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||||
|
.device_address = addr,
|
||||||
|
.scl_speed_hz = BOARD_IMU_I2C_FREQ_HZ,
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t err = i2c_master_bus_add_device(s_imu_bus, &dev_cfg, &s_imu_dev);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t who_am_i = 0;
|
||||||
|
err = imu_read_u8(QMI8658_WHO_AM_I, &who_am_i);
|
||||||
|
if (err != ESP_OK || who_am_i != QMI8658_WHO_AM_I_ID) {
|
||||||
|
i2c_master_bus_rm_device(s_imu_dev);
|
||||||
|
s_imu_dev = NULL;
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "WHO_AM_I mismatch at 0x%02X: 0x%02X", addr, who_am_i);
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "QMI8658 found at 0x%02X (WHO_AM_I=0x%02X)", addr, who_am_i);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t imu_configure(void)
|
||||||
|
{
|
||||||
|
esp_err_t err = imu_write_u8(QMI8658_RESET, QMI8658_RESET_VALUE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(50));
|
||||||
|
|
||||||
|
err = imu_write_u8(QMI8658_CTRL1, QMI8658_CTRL1_VALUE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = imu_write_u8(QMI8658_CTRL2, QMI8658_CTRL2_VALUE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = imu_write_u8(QMI8658_CTRL3, QMI8658_CTRL3_VALUE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = imu_write_u8(QMI8658_CTRL5, QMI8658_CTRL5_VALUE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = imu_write_u8(QMI8658_CTRL6, QMI8658_CTRL6_VALUE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = imu_write_u8(QMI8658_CTRL7, QMI8658_CTRL7_VALUE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(50));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void imu_log_sample(const char *label, float ax, float ay, float az,
|
||||||
|
int16_t rx, int16_t ry, int16_t rz)
|
||||||
|
{
|
||||||
|
float mag = sqrtf(ax * ax + ay * ay + az * az);
|
||||||
|
ESP_LOGI(TAG, "%s raw=(%d,%d,%d) g=(%.3f,%.3f,%.3f) |mag|=%.3f",
|
||||||
|
label, rx, ry, rz, ax, ay, az, mag);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void imu_task(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
int64_t last_log_us = esp_timer_get_time();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
float ax = 0.0f;
|
||||||
|
float ay = 0.0f;
|
||||||
|
float az = 0.0f;
|
||||||
|
int16_t rx = 0;
|
||||||
|
int16_t ry = 0;
|
||||||
|
int16_t rz = 0;
|
||||||
|
|
||||||
|
esp_err_t err = imu_read_raw(&rx, &ry, &rz);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
ax = (float)rx / QMI8658_ACCEL_SENS_8G;
|
||||||
|
ay = (float)ry / QMI8658_ACCEL_SENS_8G;
|
||||||
|
az = (float)rz / QMI8658_ACCEL_SENS_8G;
|
||||||
|
|
||||||
|
int64_t now_us = esp_timer_get_time();
|
||||||
|
if ((now_us - last_log_us) >= (int64_t)IMU_DEBUG_LOG_INTERVAL_MS * 1000) {
|
||||||
|
imu_log_sample("sample", ax, ay, az, rx, ry, rz);
|
||||||
|
last_log_us = now_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_sample_cb != NULL) {
|
||||||
|
s_sample_cb(ax, ay, az);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(IMU_SAMPLE_PERIOD_MS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t imu_init(void)
|
||||||
|
{
|
||||||
|
#if BOARD_TOUCH_ENABLED
|
||||||
|
s_imu_bus = touch_get_i2c_bus();
|
||||||
|
if (s_imu_bus == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Shared I2C bus not ready (touch not initialized?)");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Using shared I2C bus (SDA=%d SCL=%d)", BOARD_PIN_IMU_SDA,
|
||||||
|
BOARD_PIN_IMU_SCL);
|
||||||
|
#else
|
||||||
|
ESP_LOGI(TAG, "Initialize dedicated I2C for IMU");
|
||||||
|
i2c_master_bus_config_t bus_config = {
|
||||||
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||||
|
.i2c_port = BOARD_IMU_I2C_NUM,
|
||||||
|
.sda_io_num = BOARD_PIN_IMU_SDA,
|
||||||
|
.scl_io_num = BOARD_PIN_IMU_SCL,
|
||||||
|
.glitch_ignore_cnt = 7,
|
||||||
|
.flags.enable_internal_pullup = true,
|
||||||
|
};
|
||||||
|
esp_err_t bus_err = i2c_new_master_bus(&bus_config, &s_imu_bus);
|
||||||
|
if (bus_err != ESP_OK) {
|
||||||
|
return bus_err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_err_t err = imu_probe_device(QMI8658_ADDR_SDO_LOW);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
err = imu_probe_device(QMI8658_ADDR_SDO_HIGH);
|
||||||
|
}
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "QMI8658 not found");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = imu_configure();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "IMU configure failed: %s", esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ax = 0.0f;
|
||||||
|
float ay = 0.0f;
|
||||||
|
float az = 0.0f;
|
||||||
|
int16_t rx = 0;
|
||||||
|
int16_t ry = 0;
|
||||||
|
int16_t rz = 0;
|
||||||
|
if (imu_read_accel(&ax, &ay, &az) == ESP_OK) {
|
||||||
|
imu_read_raw(&rx, &ry, &rz);
|
||||||
|
imu_log_sample("init check", ax, ay, az, rx, ry, rz);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Init check read failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "IMU initialized (±8g, 1000 Hz ODR)");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t imu_read_raw(int16_t *rx, int16_t *ry, int16_t *rz)
|
||||||
|
{
|
||||||
|
if (s_imu_dev == NULL || rx == NULL || ry == NULL || rz == NULL) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t raw[6];
|
||||||
|
esp_err_t err = imu_read_bytes(QMI8658_ACC_X_L, raw, sizeof(raw));
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*rx = imu_raw16_le(&raw[0], &raw[1]);
|
||||||
|
*ry = imu_raw16_le(&raw[2], &raw[3]);
|
||||||
|
*rz = imu_raw16_le(&raw[4], &raw[5]);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t imu_read_accel(float *ax, float *ay, float *az)
|
||||||
|
{
|
||||||
|
int16_t rx = 0;
|
||||||
|
int16_t ry = 0;
|
||||||
|
int16_t rz = 0;
|
||||||
|
|
||||||
|
esp_err_t err = imu_read_raw(&rx, &ry, &rz);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ax = (float)rx / QMI8658_ACCEL_SENS_8G;
|
||||||
|
*ay = (float)ry / QMI8658_ACCEL_SENS_8G;
|
||||||
|
*az = (float)rz / QMI8658_ACCEL_SENS_8G;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void imu_start_task(imu_sample_cb_t cb)
|
||||||
|
{
|
||||||
|
s_sample_cb = cb;
|
||||||
|
xTaskCreate(imu_task, "imu_task", 3072, NULL, 4, NULL);
|
||||||
|
ESP_LOGI(TAG, "IMU sample task started");
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
esp_err_t imu_init(void)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t imu_read_accel(float *ax, float *ay, float *az)
|
||||||
|
{
|
||||||
|
(void)ax;
|
||||||
|
(void)ay;
|
||||||
|
(void)az;
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void imu_start_task(imu_sample_cb_t cb)
|
||||||
|
{
|
||||||
|
(void)cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
typedef void (*imu_sample_cb_t)(float ax_g, float ay_g, float az_g);
|
||||||
|
|
||||||
|
esp_err_t imu_init(void);
|
||||||
|
esp_err_t imu_read_accel(float *ax, float *ay, float *az);
|
||||||
|
void imu_start_task(imu_sample_cb_t cb);
|
||||||
@@ -103,6 +103,11 @@ esp_err_t touch_init(lv_display_t *display)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i2c_master_bus_handle_t touch_get_i2c_bus(void)
|
||||||
|
{
|
||||||
|
return s_touch_i2c_bus;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
esp_err_t touch_init(lv_display_t *display)
|
esp_err_t touch_init(lv_display_t *display)
|
||||||
@@ -111,4 +116,9 @@ esp_err_t touch_init(lv_display_t *display)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i2c_master_bus_handle_t touch_get_i2c_bus(void)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "lvgl.h"
|
#include "lvgl.h"
|
||||||
|
|
||||||
esp_err_t touch_init(lv_display_t *display);
|
esp_err_t touch_init(lv_display_t *display);
|
||||||
|
i2c_master_bus_handle_t touch_get_i2c_bus(void);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
// Project name: HeavenlyHeroStar
|
// Project name: HeavenlyHeroStar
|
||||||
|
|
||||||
#include "../ui.h"
|
#include "../ui.h"
|
||||||
|
#include "board_config.h"
|
||||||
|
|
||||||
lv_obj_t * uic_Calendar1;
|
lv_obj_t * uic_Calendar1;
|
||||||
lv_obj_t * ui_Screen1 = NULL;
|
lv_obj_t * ui_Screen1 = NULL;
|
||||||
@@ -15,12 +16,13 @@ lv_obj_t * ui_Calendar1 = NULL;
|
|||||||
void ui_Screen1_screen_init(void)
|
void ui_Screen1_screen_init(void)
|
||||||
{
|
{
|
||||||
ui_Screen1 = lv_obj_create(NULL);
|
ui_Screen1 = lv_obj_create(NULL);
|
||||||
|
lv_obj_set_size(ui_Screen1, BOARD_LCD_LOGIC_H_RES, BOARD_LCD_LOGIC_V_RES);
|
||||||
lv_obj_remove_flag(ui_Screen1, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
lv_obj_remove_flag(ui_Screen1, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
||||||
|
|
||||||
ui_Calendar1 = lv_calendar_create(ui_Screen1);
|
ui_Calendar1 = lv_calendar_create(ui_Screen1);
|
||||||
lv_obj_t * ui_Calendar1_header = lv_calendar_header_arrow_create(ui_Calendar1);
|
lv_obj_t * ui_Calendar1_header = lv_calendar_header_arrow_create(ui_Calendar1);
|
||||||
lv_obj_set_width(ui_Calendar1, 170);
|
lv_obj_set_width(ui_Calendar1, BOARD_LCD_LOGIC_H_RES);
|
||||||
lv_obj_set_height(ui_Calendar1, 320);
|
lv_obj_set_height(ui_Calendar1, BOARD_LCD_LOGIC_V_RES);
|
||||||
lv_obj_set_align(ui_Calendar1, LV_ALIGN_CENTER);
|
lv_obj_set_align(ui_Calendar1, LV_ALIGN_CENTER);
|
||||||
|
|
||||||
uic_Calendar1 = ui_Calendar1;
|
uic_Calendar1 = ui_Calendar1;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
// Project name: HeavenlyHeroStar
|
// Project name: HeavenlyHeroStar
|
||||||
|
|
||||||
#include "../ui.h"
|
#include "../ui.h"
|
||||||
|
#include "board_config.h"
|
||||||
|
|
||||||
lv_obj_t * ui_Screen2 = NULL;
|
lv_obj_t * ui_Screen2 = NULL;
|
||||||
lv_obj_t * ui_Button1 = NULL;
|
lv_obj_t * ui_Button1 = NULL;
|
||||||
@@ -45,11 +46,14 @@ void ui_event_Button3(lv_event_t * e)
|
|||||||
void ui_Screen2_screen_init(void)
|
void ui_Screen2_screen_init(void)
|
||||||
{
|
{
|
||||||
ui_Screen2 = lv_obj_create(NULL);
|
ui_Screen2 = lv_obj_create(NULL);
|
||||||
|
lv_obj_set_size(ui_Screen2, BOARD_LCD_LOGIC_H_RES, BOARD_LCD_LOGIC_V_RES);
|
||||||
lv_obj_remove_flag(ui_Screen2, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
lv_obj_remove_flag(ui_Screen2, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
||||||
|
|
||||||
ui_Button1 = lv_button_create(ui_Screen2);
|
ui_Button1 = lv_button_create(ui_Screen2);
|
||||||
lv_obj_set_width(ui_Button1, 100);
|
lv_obj_set_width(ui_Button1, 100);
|
||||||
lv_obj_set_height(ui_Button1, 80);
|
lv_obj_set_height(ui_Button1, 80);
|
||||||
|
lv_obj_set_x(ui_Button1, -100);
|
||||||
|
lv_obj_set_y(ui_Button1, 0);
|
||||||
lv_obj_set_align(ui_Button1, LV_ALIGN_CENTER);
|
lv_obj_set_align(ui_Button1, LV_ALIGN_CENTER);
|
||||||
lv_obj_add_flag(ui_Button1, LV_OBJ_FLAG_SCROLL_ON_FOCUS); /// Flags
|
lv_obj_add_flag(ui_Button1, LV_OBJ_FLAG_SCROLL_ON_FOCUS); /// Flags
|
||||||
lv_obj_remove_flag(ui_Button1, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
lv_obj_remove_flag(ui_Button1, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
||||||
@@ -58,7 +62,7 @@ void ui_Screen2_screen_init(void)
|
|||||||
lv_obj_set_width(ui_Button2, 100);
|
lv_obj_set_width(ui_Button2, 100);
|
||||||
lv_obj_set_height(ui_Button2, 80);
|
lv_obj_set_height(ui_Button2, 80);
|
||||||
lv_obj_set_x(ui_Button2, 0);
|
lv_obj_set_x(ui_Button2, 0);
|
||||||
lv_obj_set_y(ui_Button2, 100);
|
lv_obj_set_y(ui_Button2, 0);
|
||||||
lv_obj_set_align(ui_Button2, LV_ALIGN_CENTER);
|
lv_obj_set_align(ui_Button2, LV_ALIGN_CENTER);
|
||||||
lv_obj_add_flag(ui_Button2, LV_OBJ_FLAG_SCROLL_ON_FOCUS); /// Flags
|
lv_obj_add_flag(ui_Button2, LV_OBJ_FLAG_SCROLL_ON_FOCUS); /// Flags
|
||||||
lv_obj_remove_flag(ui_Button2, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
lv_obj_remove_flag(ui_Button2, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
||||||
@@ -66,8 +70,8 @@ void ui_Screen2_screen_init(void)
|
|||||||
ui_Button3 = lv_button_create(ui_Screen2);
|
ui_Button3 = lv_button_create(ui_Screen2);
|
||||||
lv_obj_set_width(ui_Button3, 100);
|
lv_obj_set_width(ui_Button3, 100);
|
||||||
lv_obj_set_height(ui_Button3, 80);
|
lv_obj_set_height(ui_Button3, 80);
|
||||||
lv_obj_set_x(ui_Button3, 0);
|
lv_obj_set_x(ui_Button3, 100);
|
||||||
lv_obj_set_y(ui_Button3, -100);
|
lv_obj_set_y(ui_Button3, 0);
|
||||||
lv_obj_set_align(ui_Button3, LV_ALIGN_CENTER);
|
lv_obj_set_align(ui_Button3, LV_ALIGN_CENTER);
|
||||||
lv_obj_add_flag(ui_Button3, LV_OBJ_FLAG_SCROLL_ON_FOCUS); /// Flags
|
lv_obj_add_flag(ui_Button3, LV_OBJ_FLAG_SCROLL_ON_FOCUS); /// Flags
|
||||||
lv_obj_remove_flag(ui_Button3, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
lv_obj_remove_flag(ui_Button3, LV_OBJ_FLAG_SCROLLABLE); /// Flags
|
||||||
@@ -75,6 +79,8 @@ void ui_Screen2_screen_init(void)
|
|||||||
ui_Label2 = lv_label_create(ui_Screen2);
|
ui_Label2 = lv_label_create(ui_Screen2);
|
||||||
lv_obj_set_width(ui_Label2, LV_SIZE_CONTENT); /// 1
|
lv_obj_set_width(ui_Label2, LV_SIZE_CONTENT); /// 1
|
||||||
lv_obj_set_height(ui_Label2, LV_SIZE_CONTENT); /// 1
|
lv_obj_set_height(ui_Label2, LV_SIZE_CONTENT); /// 1
|
||||||
|
lv_obj_set_x(ui_Label2, -100);
|
||||||
|
lv_obj_set_y(ui_Label2, 0);
|
||||||
lv_obj_set_align(ui_Label2, LV_ALIGN_CENTER);
|
lv_obj_set_align(ui_Label2, LV_ALIGN_CENTER);
|
||||||
lv_label_set_text(ui_Label2, "Ctrl+C");
|
lv_label_set_text(ui_Label2, "Ctrl+C");
|
||||||
|
|
||||||
@@ -82,15 +88,15 @@ void ui_Screen2_screen_init(void)
|
|||||||
lv_obj_set_width(ui_Label4, LV_SIZE_CONTENT); /// 1
|
lv_obj_set_width(ui_Label4, LV_SIZE_CONTENT); /// 1
|
||||||
lv_obj_set_height(ui_Label4, LV_SIZE_CONTENT); /// 1
|
lv_obj_set_height(ui_Label4, LV_SIZE_CONTENT); /// 1
|
||||||
lv_obj_set_x(ui_Label4, 0);
|
lv_obj_set_x(ui_Label4, 0);
|
||||||
lv_obj_set_y(ui_Label4, 100);
|
lv_obj_set_y(ui_Label4, 0);
|
||||||
lv_obj_set_align(ui_Label4, LV_ALIGN_CENTER);
|
lv_obj_set_align(ui_Label4, LV_ALIGN_CENTER);
|
||||||
lv_label_set_text(ui_Label4, "Ctrl+V");
|
lv_label_set_text(ui_Label4, "Ctrl+V");
|
||||||
|
|
||||||
ui_Label5 = lv_label_create(ui_Screen2);
|
ui_Label5 = lv_label_create(ui_Screen2);
|
||||||
lv_obj_set_width(ui_Label5, LV_SIZE_CONTENT); /// 1
|
lv_obj_set_width(ui_Label5, LV_SIZE_CONTENT); /// 1
|
||||||
lv_obj_set_height(ui_Label5, LV_SIZE_CONTENT); /// 1
|
lv_obj_set_height(ui_Label5, LV_SIZE_CONTENT); /// 1
|
||||||
lv_obj_set_x(ui_Label5, 0);
|
lv_obj_set_x(ui_Label5, 100);
|
||||||
lv_obj_set_y(ui_Label5, -100);
|
lv_obj_set_y(ui_Label5, 0);
|
||||||
lv_obj_set_align(ui_Label5, LV_ALIGN_CENTER);
|
lv_obj_set_align(ui_Label5, LV_ALIGN_CENTER);
|
||||||
lv_label_set_text(ui_Label5, "Ctrl+X");
|
lv_label_set_text(ui_Label5, "Ctrl+X");
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
// Project name: HeavenlyHeroStar
|
// Project name: HeavenlyHeroStar
|
||||||
|
|
||||||
#include "../ui.h"
|
#include "../ui.h"
|
||||||
|
#include "board_config.h"
|
||||||
|
|
||||||
lv_obj_t * ui_Screen3 = NULL;
|
lv_obj_t * ui_Screen3 = NULL;
|
||||||
lv_obj_t * ui_BtnVolDown = NULL;
|
lv_obj_t * ui_BtnVolDown = NULL;
|
||||||
@@ -23,8 +24,8 @@ static void ui_screen3_create_button(lv_obj_t **btn, lv_obj_t **label, int32_t x
|
|||||||
const char *text)
|
const char *text)
|
||||||
{
|
{
|
||||||
*btn = lv_button_create(ui_Screen3);
|
*btn = lv_button_create(ui_Screen3);
|
||||||
lv_obj_set_width(*btn, 75);
|
lv_obj_set_width(*btn, 90);
|
||||||
lv_obj_set_height(*btn, 90);
|
lv_obj_set_height(*btn, 45);
|
||||||
lv_obj_set_x(*btn, x);
|
lv_obj_set_x(*btn, x);
|
||||||
lv_obj_set_y(*btn, y);
|
lv_obj_set_y(*btn, y);
|
||||||
lv_obj_set_align(*btn, LV_ALIGN_CENTER);
|
lv_obj_set_align(*btn, LV_ALIGN_CENTER);
|
||||||
@@ -100,14 +101,15 @@ void ui_event_BtnNext(lv_event_t * e)
|
|||||||
void ui_Screen3_screen_init(void)
|
void ui_Screen3_screen_init(void)
|
||||||
{
|
{
|
||||||
ui_Screen3 = lv_obj_create(NULL);
|
ui_Screen3 = lv_obj_create(NULL);
|
||||||
|
lv_obj_set_size(ui_Screen3, BOARD_LCD_LOGIC_H_RES, BOARD_LCD_LOGIC_V_RES);
|
||||||
lv_obj_remove_flag(ui_Screen3, LV_OBJ_FLAG_SCROLLABLE);
|
lv_obj_remove_flag(ui_Screen3, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
|
||||||
ui_screen3_create_button(&ui_BtnVolDown, &ui_LabelVolDown, -42, -107, "Vol-");
|
ui_screen3_create_button(&ui_BtnVolDown, &ui_LabelVolDown, -50, -55, "Vol-");
|
||||||
ui_screen3_create_button(&ui_BtnVolUp, &ui_LabelVolUp, 42, -107, "Vol+");
|
ui_screen3_create_button(&ui_BtnVolUp, &ui_LabelVolUp, 50, -55, "Vol+");
|
||||||
ui_screen3_create_button(&ui_BtnMute, &ui_LabelMute, -42, 0, "Mute");
|
ui_screen3_create_button(&ui_BtnMute, &ui_LabelMute, -50, 0, "Mute");
|
||||||
ui_screen3_create_button(&ui_BtnPlayPause, &ui_LabelPlayPause, 42, 0, "Play");
|
ui_screen3_create_button(&ui_BtnPlayPause, &ui_LabelPlayPause, 50, 0, "Play");
|
||||||
ui_screen3_create_button(&ui_BtnPrev, &ui_LabelPrev, -42, 107, "Prev");
|
ui_screen3_create_button(&ui_BtnPrev, &ui_LabelPrev, -50, 55, "Prev");
|
||||||
ui_screen3_create_button(&ui_BtnNext, &ui_LabelNext, 42, 107, "Next");
|
ui_screen3_create_button(&ui_BtnNext, &ui_LabelNext, 50, 55, "Next");
|
||||||
|
|
||||||
lv_obj_add_event_cb(ui_BtnVolDown, ui_event_BtnVolDown, LV_EVENT_ALL, NULL);
|
lv_obj_add_event_cb(ui_BtnVolDown, ui_event_BtnVolDown, LV_EVENT_ALL, NULL);
|
||||||
lv_obj_add_event_cb(ui_BtnVolUp, ui_event_BtnVolUp, LV_EVENT_ALL, NULL);
|
lv_obj_add_event_cb(ui_BtnVolUp, ui_event_BtnVolUp, LV_EVENT_ALL, NULL);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "settings_store.h"
|
#include "settings_store.h"
|
||||||
|
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
#include "motion_gesture.h"
|
||||||
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "nvs.h"
|
#include "nvs.h"
|
||||||
@@ -15,6 +16,8 @@ static const settings_data_t SETTINGS_DEFAULTS = {
|
|||||||
.brightness_pct = 70,
|
.brightness_pct = 70,
|
||||||
.volume_pct = 50,
|
.volume_pct = 50,
|
||||||
.mute = false,
|
.mute = false,
|
||||||
|
.shake_nav_enabled = true,
|
||||||
|
.shake_sensitivity = SHAKE_SENS_MED,
|
||||||
};
|
};
|
||||||
|
|
||||||
static settings_data_t s_data;
|
static settings_data_t s_data;
|
||||||
@@ -48,6 +51,10 @@ void settings_store_load(void)
|
|||||||
load_key_u8(handle, "vol", &s_data.volume_pct, SETTINGS_DEFAULTS.volume_pct);
|
load_key_u8(handle, "vol", &s_data.volume_pct, SETTINGS_DEFAULTS.volume_pct);
|
||||||
load_key_u8(handle, "mute", &val, SETTINGS_DEFAULTS.mute);
|
load_key_u8(handle, "mute", &val, SETTINGS_DEFAULTS.mute);
|
||||||
s_data.mute = val != 0;
|
s_data.mute = val != 0;
|
||||||
|
load_key_u8(handle, "shake_en", &val, SETTINGS_DEFAULTS.shake_nav_enabled);
|
||||||
|
s_data.shake_nav_enabled = val != 0;
|
||||||
|
load_key_u8(handle, "shake_sens", &val, SETTINGS_DEFAULTS.shake_sensitivity);
|
||||||
|
s_data.shake_sensitivity = (shake_sensitivity_t)val;
|
||||||
|
|
||||||
nvs_close(handle);
|
nvs_close(handle);
|
||||||
ESP_LOGI(TAG, "Settings loaded (bright=%u vol=%u)", s_data.brightness_pct,
|
ESP_LOGI(TAG, "Settings loaded (bright=%u vol=%u)", s_data.brightness_pct,
|
||||||
@@ -83,6 +90,12 @@ esp_err_t settings_store_commit(void)
|
|||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
err = nvs_set_u8(handle, "mute", s_data.mute ? 1 : 0);
|
err = nvs_set_u8(handle, "mute", s_data.mute ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
err = nvs_set_u8(handle, "shake_en", s_data.shake_nav_enabled ? 1 : 0);
|
||||||
|
}
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
err = nvs_set_u8(handle, "shake_sens", (uint8_t)s_data.shake_sensitivity);
|
||||||
|
}
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
err = nvs_commit(handle);
|
err = nvs_commit(handle);
|
||||||
}
|
}
|
||||||
@@ -118,4 +131,7 @@ void settings_store_apply(void)
|
|||||||
{
|
{
|
||||||
uint16_t duty = (uint16_t)((s_data.brightness_pct * 1023U) / 100U);
|
uint16_t duty = (uint16_t)((s_data.brightness_pct * 1023U) / 100U);
|
||||||
display_backlight_set_brightness(duty);
|
display_backlight_set_brightness(duty);
|
||||||
|
|
||||||
|
motion_gesture_set_enabled(s_data.shake_nav_enabled);
|
||||||
|
motion_gesture_set_sensitivity(s_data.shake_sensitivity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include "ui_handlers.h"
|
#include "ui_handlers.h"
|
||||||
|
|
||||||
static lv_obj_t *s_notif_switch;
|
static lv_obj_t *s_notif_switch;
|
||||||
|
static lv_obj_t *s_shake_nav_switch;
|
||||||
|
static lv_obj_t *s_shake_sens_dropdown;
|
||||||
static lv_obj_t *s_brightness_slider;
|
static lv_obj_t *s_brightness_slider;
|
||||||
static lv_obj_t *s_brightness_pct_label;
|
static lv_obj_t *s_brightness_pct_label;
|
||||||
static lv_obj_t *s_volume_slider;
|
static lv_obj_t *s_volume_slider;
|
||||||
@@ -43,6 +45,8 @@ static lv_obj_t *create_settings_body(lv_obj_t *content)
|
|||||||
void load_system_settings(lv_obj_t *content, const settings_data_t *data)
|
void load_system_settings(lv_obj_t *content, const settings_data_t *data)
|
||||||
{
|
{
|
||||||
s_notif_switch = NULL;
|
s_notif_switch = NULL;
|
||||||
|
s_shake_nav_switch = NULL;
|
||||||
|
s_shake_sens_dropdown = NULL;
|
||||||
|
|
||||||
create_status_bar(content, "System Settings");
|
create_status_bar(content, "System Settings");
|
||||||
lv_obj_t *body = create_settings_body(content);
|
lv_obj_t *body = create_settings_body(content);
|
||||||
@@ -62,6 +66,35 @@ void load_system_settings(lv_obj_t *content, const settings_data_t *data)
|
|||||||
LV_EVENT_VALUE_CHANGED,
|
LV_EVENT_VALUE_CHANGED,
|
||||||
(void *)(intptr_t)SETTINGS_FIELD_NOTIFICATION);
|
(void *)(intptr_t)SETTINGS_FIELD_NOTIFICATION);
|
||||||
|
|
||||||
|
lv_obj_t *shake_nav_label = lv_label_create(body);
|
||||||
|
lv_label_set_text(shake_nav_label, "Shake Navigation");
|
||||||
|
lv_obj_set_pos(shake_nav_label, 4, 40);
|
||||||
|
lv_obj_set_size(shake_nav_label, 120, 28);
|
||||||
|
|
||||||
|
s_shake_nav_switch = lv_switch_create(body);
|
||||||
|
lv_obj_set_pos(s_shake_nav_switch, 180, 40);
|
||||||
|
lv_obj_set_size(s_shake_nav_switch, 40, 28);
|
||||||
|
if (data->shake_nav_enabled) {
|
||||||
|
lv_obj_add_state(s_shake_nav_switch, LV_STATE_CHECKED);
|
||||||
|
}
|
||||||
|
lv_obj_add_event_cb(s_shake_nav_switch, settings_on_value_changed,
|
||||||
|
LV_EVENT_VALUE_CHANGED,
|
||||||
|
(void *)(intptr_t)SETTINGS_FIELD_SHAKE_NAV);
|
||||||
|
|
||||||
|
lv_obj_t *shake_sens_label = lv_label_create(body);
|
||||||
|
lv_label_set_text(shake_sens_label, "Shake Sensitivity");
|
||||||
|
lv_obj_set_pos(shake_sens_label, 4, 76);
|
||||||
|
lv_obj_set_size(shake_sens_label, 120, 28);
|
||||||
|
|
||||||
|
s_shake_sens_dropdown = lv_dropdown_create(body);
|
||||||
|
lv_obj_set_pos(s_shake_sens_dropdown, 120, 76);
|
||||||
|
lv_obj_set_size(s_shake_sens_dropdown, 100, 28);
|
||||||
|
lv_dropdown_set_options(s_shake_sens_dropdown, "Low\nMed\nHigh");
|
||||||
|
lv_dropdown_set_selected(s_shake_sens_dropdown, data->shake_sensitivity);
|
||||||
|
lv_obj_add_event_cb(s_shake_sens_dropdown, settings_on_value_changed,
|
||||||
|
LV_EVENT_VALUE_CHANGED,
|
||||||
|
(void *)(intptr_t)SETTINGS_FIELD_SHAKE_SENS);
|
||||||
|
|
||||||
lv_obj_t *reset_btn = lv_button_create(body);
|
lv_obj_t *reset_btn = lv_button_create(body);
|
||||||
lv_obj_set_pos(reset_btn, 4, 112);
|
lv_obj_set_pos(reset_btn, 4, 112);
|
||||||
lv_obj_set_size(reset_btn, 100, 28);
|
lv_obj_set_size(reset_btn, 100, 28);
|
||||||
@@ -170,6 +203,14 @@ void settings_tabs_collect(settings_tab_t tab, settings_data_t *out)
|
|||||||
out->notification_enabled =
|
out->notification_enabled =
|
||||||
lv_obj_has_state(s_notif_switch, LV_STATE_CHECKED);
|
lv_obj_has_state(s_notif_switch, LV_STATE_CHECKED);
|
||||||
}
|
}
|
||||||
|
if (s_shake_nav_switch != NULL) {
|
||||||
|
out->shake_nav_enabled =
|
||||||
|
lv_obj_has_state(s_shake_nav_switch, LV_STATE_CHECKED);
|
||||||
|
}
|
||||||
|
if (s_shake_sens_dropdown != NULL) {
|
||||||
|
out->shake_sensitivity =
|
||||||
|
(shake_sensitivity_t)lv_dropdown_get_selected(s_shake_sens_dropdown);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SETTINGS_TAB_DISPLAY:
|
case SETTINGS_TAB_DISPLAY:
|
||||||
if (s_brightness_slider != NULL) {
|
if (s_brightness_slider != NULL) {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ typedef enum {
|
|||||||
SETTINGS_FIELD_NOTIFICATION = 0,
|
SETTINGS_FIELD_NOTIFICATION = 0,
|
||||||
SETTINGS_FIELD_VOLUME,
|
SETTINGS_FIELD_VOLUME,
|
||||||
SETTINGS_FIELD_MUTE,
|
SETTINGS_FIELD_MUTE,
|
||||||
|
SETTINGS_FIELD_SHAKE_NAV,
|
||||||
|
SETTINGS_FIELD_SHAKE_SENS,
|
||||||
} settings_field_t;
|
} settings_field_t;
|
||||||
|
|
||||||
void load_system_settings(lv_obj_t *content, const settings_data_t *data);
|
void load_system_settings(lv_obj_t *content, const settings_data_t *data);
|
||||||
|
|||||||
@@ -10,9 +10,17 @@ typedef enum {
|
|||||||
SETTINGS_TAB_NETWORK,
|
SETTINGS_TAB_NETWORK,
|
||||||
} settings_tab_t;
|
} settings_tab_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SHAKE_SENS_LOW = 0,
|
||||||
|
SHAKE_SENS_MED,
|
||||||
|
SHAKE_SENS_HIGH,
|
||||||
|
} shake_sensitivity_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool notification_enabled;
|
bool notification_enabled;
|
||||||
uint8_t brightness_pct;
|
uint8_t brightness_pct;
|
||||||
uint8_t volume_pct;
|
uint8_t volume_pct;
|
||||||
bool mute;
|
bool mute;
|
||||||
|
bool shake_nav_enabled;
|
||||||
|
shake_sensitivity_t shake_sensitivity;
|
||||||
} settings_data_t;
|
} settings_data_t;
|
||||||
|
|||||||
Reference in New Issue
Block a user