Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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,191 @@
|
|||||||
|
#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 "lvgl.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
static const char *TAG = "motion";
|
||||||
|
|
||||||
|
#define SAMPLE_RATE_HZ 100
|
||||||
|
#define THRESHOLD_LOW_G 0.6f
|
||||||
|
#define THRESHOLD_MED_G 0.8f
|
||||||
|
#define THRESHOLD_HIGH_G 1.0f
|
||||||
|
#define WINDOW_MS 300
|
||||||
|
#define MIN_SAMPLES 5
|
||||||
|
#define COOLDOWN_MS 800
|
||||||
|
#define CALIBRATION_MS 500
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
static float s_ref_x;
|
||||||
|
static float s_ref_y;
|
||||||
|
static float s_ref_z;
|
||||||
|
static bool s_calibrated;
|
||||||
|
static int64_t s_cal_start_us;
|
||||||
|
static int s_cal_count;
|
||||||
|
static float s_cal_sum_x;
|
||||||
|
static float s_cal_sum_y;
|
||||||
|
static float s_cal_sum_z;
|
||||||
|
|
||||||
|
static int s_over_count;
|
||||||
|
static int64_t s_window_start_us;
|
||||||
|
static int64_t s_cooldown_until_us;
|
||||||
|
|
||||||
|
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 float magnitude(float ax, float ay, float az)
|
||||||
|
{
|
||||||
|
return sqrtf(ax * ax + ay * ay + az * az);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 run_calibration(float ax, float ay, float az)
|
||||||
|
{
|
||||||
|
s_cal_sum_x += ax;
|
||||||
|
s_cal_sum_y += ay;
|
||||||
|
s_cal_sum_z += az;
|
||||||
|
s_cal_count++;
|
||||||
|
|
||||||
|
int64_t elapsed_us = esp_timer_get_time() - s_cal_start_us;
|
||||||
|
if (elapsed_us >= (int64_t)CALIBRATION_MS * 1000) {
|
||||||
|
if (s_cal_count > 0) {
|
||||||
|
s_ref_x = s_cal_sum_x / (float)s_cal_count;
|
||||||
|
s_ref_y = s_cal_sum_y / (float)s_cal_count;
|
||||||
|
s_ref_z = s_cal_sum_z / (float)s_cal_count;
|
||||||
|
}
|
||||||
|
s_calibrated = true;
|
||||||
|
ESP_LOGI(TAG, "Baseline calibrated (ref=%.3f,%.3f,%.3f g)", s_ref_x, s_ref_y,
|
||||||
|
s_ref_z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_window(void)
|
||||||
|
{
|
||||||
|
s_over_count = 0;
|
||||||
|
s_window_start_us = 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_state == GESTURE_COOLDOWN) {
|
||||||
|
if (now_us >= s_cooldown_until_us) {
|
||||||
|
s_state = GESTURE_IDLE;
|
||||||
|
reset_window();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dx = ax - s_ref_x;
|
||||||
|
float dy = ay - s_ref_y;
|
||||||
|
float dz = az - s_ref_z;
|
||||||
|
float mag = magnitude(dx, dy, dz);
|
||||||
|
|
||||||
|
if (mag > s_threshold) {
|
||||||
|
if (s_over_count == 0) {
|
||||||
|
s_window_start_us = now_us;
|
||||||
|
}
|
||||||
|
s_over_count++;
|
||||||
|
|
||||||
|
int64_t window_us = (int64_t)WINDOW_MS * 1000;
|
||||||
|
if ((now_us - s_window_start_us) > window_us) {
|
||||||
|
s_over_count = 1;
|
||||||
|
s_window_start_us = now_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_over_count >= MIN_SAMPLES) {
|
||||||
|
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 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_cal_start_us = esp_timer_get_time();
|
||||||
|
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:
|
||||||
|
LV_ASSERT_MSG(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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#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);
|
||||||
@@ -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
|
||||||
|
|||||||
+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);
|
||||||
|
|||||||
@@ -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