feat: 实现设置页面及其功能

This commit is contained in:
2026-07-05 20:52:04 +08:00
parent 9a355e540c
commit 6a6ee41a07
15 changed files with 676 additions and 11 deletions
+121
View File
@@ -0,0 +1,121 @@
#include "settings_store.h"
#include "display.h"
#include "esp_log.h"
#include "nvs.h"
#include "nvs_flash.h"
static const char *TAG = "settings_store";
static const char *SETTINGS_NAMESPACE = "settings";
static const settings_data_t SETTINGS_DEFAULTS = {
.notification_enabled = false,
.brightness_pct = 70,
.volume_pct = 50,
.mute = false,
};
static settings_data_t s_data;
static void load_key_u8(nvs_handle_t handle, const char *key, uint8_t *out,
uint8_t default_val)
{
uint8_t val = default_val;
if (nvs_get_u8(handle, key, &val) != ESP_OK) {
val = default_val;
}
*out = val;
}
void settings_store_load(void)
{
s_data = SETTINGS_DEFAULTS;
nvs_handle_t handle;
esp_err_t err = nvs_open(SETTINGS_NAMESPACE, NVS_READONLY, &handle);
if (err != ESP_OK) {
ESP_LOGI(TAG, "No saved settings, using defaults");
return;
}
uint8_t val;
load_key_u8(handle, "notif", &val, SETTINGS_DEFAULTS.notification_enabled);
s_data.notification_enabled = val != 0;
load_key_u8(handle, "bright", &s_data.brightness_pct,
SETTINGS_DEFAULTS.brightness_pct);
load_key_u8(handle, "vol", &s_data.volume_pct, SETTINGS_DEFAULTS.volume_pct);
load_key_u8(handle, "mute", &val, SETTINGS_DEFAULTS.mute);
s_data.mute = val != 0;
nvs_close(handle);
ESP_LOGI(TAG, "Settings loaded (bright=%u vol=%u)", s_data.brightness_pct,
s_data.volume_pct);
}
const settings_data_t *settings_store_get(void)
{
return &s_data;
}
void settings_store_update(const settings_data_t *data)
{
s_data = *data;
}
esp_err_t settings_store_commit(void)
{
nvs_handle_t handle;
esp_err_t err = nvs_open(SETTINGS_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "nvs_open failed: %s", esp_err_to_name(err));
return err;
}
err = nvs_set_u8(handle, "notif", s_data.notification_enabled ? 1 : 0);
if (err == ESP_OK) {
err = nvs_set_u8(handle, "bright", s_data.brightness_pct);
}
if (err == ESP_OK) {
err = nvs_set_u8(handle, "vol", s_data.volume_pct);
}
if (err == ESP_OK) {
err = nvs_set_u8(handle, "mute", s_data.mute ? 1 : 0);
}
if (err == ESP_OK) {
err = nvs_commit(handle);
}
nvs_close(handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "commit failed: %s", esp_err_to_name(err));
}
return err;
}
esp_err_t settings_store_factory_reset(void)
{
nvs_handle_t handle;
esp_err_t err = nvs_open(SETTINGS_NAMESPACE, NVS_READWRITE, &handle);
if (err == ESP_OK) {
err = nvs_erase_all(handle);
if (err == ESP_OK) {
err = nvs_commit(handle);
}
nvs_close(handle);
}
s_data = SETTINGS_DEFAULTS;
esp_err_t commit_err = settings_store_commit();
if (err != ESP_OK) {
return err;
}
return commit_err;
}
void settings_store_apply(void)
{
uint16_t duty = (uint16_t)((s_data.brightness_pct * 1023U) / 100U);
display_backlight_set_brightness(duty);
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "esp_err.h"
#include "settings_types.h"
void settings_store_load(void);
const settings_data_t *settings_store_get(void);
void settings_store_update(const settings_data_t *data);
esp_err_t settings_store_commit(void);
esp_err_t settings_store_factory_reset(void);
void settings_store_apply(void);
+194
View File
@@ -0,0 +1,194 @@
#include "settings_tabs.h"
#include "ui_handlers.h"
static lv_obj_t *s_notif_switch;
static lv_obj_t *s_brightness_slider;
static lv_obj_t *s_brightness_pct_label;
static lv_obj_t *s_volume_slider;
static lv_obj_t *s_mute_switch;
static lv_obj_t *create_status_bar(lv_obj_t *content, const char *title)
{
lv_obj_t *status_bar = lv_obj_create(content);
lv_obj_set_size(status_bar, 230, 25);
lv_obj_set_pos(status_bar, 0, 0);
lv_obj_set_style_pad_all(status_bar, 2, 0);
lv_obj_set_style_border_width(status_bar, 1, 0);
lv_obj_set_style_border_side(status_bar, LV_BORDER_SIDE_BOTTOM, 0);
lv_obj_remove_flag(status_bar, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t *title_label = lv_label_create(status_bar);
lv_label_set_text(title_label, title);
lv_obj_align(title_label, LV_ALIGN_LEFT_MID, 2, 0);
lv_obj_t *time_label = lv_label_create(status_bar);
lv_label_set_text(time_label, "--:--");
lv_obj_align(time_label, LV_ALIGN_RIGHT_MID, -2, 0);
return status_bar;
}
static lv_obj_t *create_settings_body(lv_obj_t *content)
{
lv_obj_t *body = lv_obj_create(content);
lv_obj_set_size(body, 230, 145);
lv_obj_set_pos(body, 0, 25);
lv_obj_set_style_pad_all(body, 2, 0);
lv_obj_set_style_border_width(body, 0, 0);
lv_obj_remove_flag(body, LV_OBJ_FLAG_SCROLLABLE);
return body;
}
void load_system_settings(lv_obj_t *content, const settings_data_t *data)
{
s_notif_switch = NULL;
create_status_bar(content, "System Settings");
lv_obj_t *body = create_settings_body(content);
lv_obj_t *notif_label = lv_label_create(body);
lv_label_set_text(notif_label, "Enable Notification");
lv_obj_set_pos(notif_label, 4, 4);
lv_obj_set_size(notif_label, 120, 28);
s_notif_switch = lv_switch_create(body);
lv_obj_set_pos(s_notif_switch, 180, 4);
lv_obj_set_size(s_notif_switch, 40, 28);
if (data->notification_enabled) {
lv_obj_add_state(s_notif_switch, LV_STATE_CHECKED);
}
lv_obj_add_event_cb(s_notif_switch, settings_on_value_changed,
LV_EVENT_VALUE_CHANGED,
(void *)(intptr_t)SETTINGS_FIELD_NOTIFICATION);
lv_obj_t *reset_btn = lv_button_create(body);
lv_obj_set_pos(reset_btn, 4, 112);
lv_obj_set_size(reset_btn, 100, 28);
lv_obj_set_style_text_color(reset_btn, lv_palette_main(LV_PALETTE_RED), 0);
lv_obj_t *reset_label = lv_label_create(reset_btn);
lv_label_set_text(reset_label, "Factory Reset");
lv_obj_center(reset_label);
lv_obj_add_event_cb(reset_btn, settings_on_factory_reset, LV_EVENT_CLICKED,
NULL);
}
void load_display_settings(lv_obj_t *content, const settings_data_t *data)
{
s_brightness_slider = NULL;
s_brightness_pct_label = NULL;
create_status_bar(content, "Display Settings");
lv_obj_t *body = create_settings_body(content);
lv_obj_t *bright_label = lv_label_create(body);
lv_label_set_text(bright_label, "Brightness");
lv_obj_set_pos(bright_label, 4, 4);
lv_obj_set_size(bright_label, 80, 28);
s_brightness_slider = lv_slider_create(body);
lv_obj_set_pos(s_brightness_slider, 88, 8);
lv_obj_set_size(s_brightness_slider, 100, 12);
lv_slider_set_range(s_brightness_slider, 0, 100);
lv_slider_set_value(s_brightness_slider, data->brightness_pct, LV_ANIM_OFF);
s_brightness_pct_label = lv_label_create(body);
lv_label_set_text_fmt(s_brightness_pct_label, "%u%%", data->brightness_pct);
lv_obj_set_pos(s_brightness_pct_label, 196, 4);
lv_obj_set_size(s_brightness_pct_label, 30, 28);
lv_obj_set_user_data(s_brightness_slider, s_brightness_pct_label);
lv_obj_add_event_cb(s_brightness_slider, settings_on_brightness_changed,
LV_EVENT_VALUE_CHANGED, NULL);
}
void load_audio_settings(lv_obj_t *content, const settings_data_t *data)
{
s_volume_slider = NULL;
s_mute_switch = NULL;
create_status_bar(content, "Audio Settings");
lv_obj_t *body = create_settings_body(content);
lv_obj_t *vol_label = lv_label_create(body);
lv_label_set_text(vol_label, "Volume");
lv_obj_set_pos(vol_label, 4, 4);
lv_obj_set_size(vol_label, 80, 28);
s_volume_slider = lv_slider_create(body);
lv_obj_set_pos(s_volume_slider, 88, 8);
lv_obj_set_size(s_volume_slider, 100, 12);
lv_slider_set_range(s_volume_slider, 0, 100);
lv_slider_set_value(s_volume_slider, data->volume_pct, LV_ANIM_OFF);
lv_obj_add_event_cb(s_volume_slider, settings_on_value_changed,
LV_EVENT_VALUE_CHANGED,
(void *)(intptr_t)SETTINGS_FIELD_VOLUME);
lv_obj_t *mute_label = lv_label_create(body);
lv_label_set_text(mute_label, "Mute");
lv_obj_set_pos(mute_label, 4, 40);
lv_obj_set_size(mute_label, 80, 28);
s_mute_switch = lv_switch_create(body);
lv_obj_set_pos(s_mute_switch, 180, 40);
lv_obj_set_size(s_mute_switch, 40, 28);
if (data->mute) {
lv_obj_add_state(s_mute_switch, LV_STATE_CHECKED);
}
lv_obj_add_event_cb(s_mute_switch, settings_on_value_changed,
LV_EVENT_VALUE_CHANGED,
(void *)(intptr_t)SETTINGS_FIELD_MUTE);
}
void load_network_settings(lv_obj_t *content, const settings_data_t *data)
{
(void)data;
create_status_bar(content, "Network Settings");
lv_obj_t *body = create_settings_body(content);
lv_obj_t *wifi_label = lv_label_create(body);
lv_label_set_text(wifi_label, "WiFi: Disabled");
lv_obj_set_pos(wifi_label, 4, 4);
lv_obj_set_size(wifi_label, 140, 28);
lv_obj_t *scan_btn = lv_button_create(body);
lv_obj_set_pos(scan_btn, 4, 40);
lv_obj_set_size(scan_btn, 80, 28);
lv_obj_t *scan_label = lv_label_create(scan_btn);
lv_label_set_text(scan_label, "Scan");
lv_obj_center(scan_label);
lv_obj_add_event_cb(scan_btn, settings_on_network_scan_clicked,
LV_EVENT_CLICKED, NULL);
}
void settings_tabs_collect(settings_tab_t tab, settings_data_t *out)
{
switch (tab) {
case SETTINGS_TAB_SYSTEM:
if (s_notif_switch != NULL) {
out->notification_enabled =
lv_obj_has_state(s_notif_switch, LV_STATE_CHECKED);
}
break;
case SETTINGS_TAB_DISPLAY:
if (s_brightness_slider != NULL) {
out->brightness_pct =
(uint8_t)lv_slider_get_value(s_brightness_slider);
}
break;
case SETTINGS_TAB_AUDIO:
if (s_volume_slider != NULL) {
out->volume_pct = (uint8_t)lv_slider_get_value(s_volume_slider);
}
if (s_mute_switch != NULL) {
out->mute = lv_obj_has_state(s_mute_switch, LV_STATE_CHECKED);
}
break;
case SETTINGS_TAB_NETWORK:
break;
default:
LV_ASSERT_MSG(false, "unexpected settings_tab_t");
break;
}
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include "lvgl.h"
#include "settings_types.h"
typedef enum {
SETTINGS_FIELD_NOTIFICATION = 0,
SETTINGS_FIELD_VOLUME,
SETTINGS_FIELD_MUTE,
} settings_field_t;
void load_system_settings(lv_obj_t *content, const settings_data_t *data);
void load_display_settings(lv_obj_t *content, const settings_data_t *data);
void load_audio_settings(lv_obj_t *content, const settings_data_t *data);
void load_network_settings(lv_obj_t *content, const settings_data_t *data);
void settings_tabs_collect(settings_tab_t tab, settings_data_t *out);
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
typedef enum {
SETTINGS_TAB_SYSTEM = 0,
SETTINGS_TAB_DISPLAY,
SETTINGS_TAB_AUDIO,
SETTINGS_TAB_NETWORK,
} settings_tab_t;
typedef struct {
bool notification_enabled;
uint8_t brightness_pct;
uint8_t volume_pct;
bool mute;
} settings_data_t;