feat(display): 添加蓝牙连接状态显示功能

- 引入ble_common_event头文件支持蓝牙事件处理
- 在UI模型中添加蓝牙连接状态字段,包括隐藏、搜索中、已连接三种状态
- 实现蓝牙事件处理器,响应蓝牙对等设备搜索和连接事件
- 当切换到非BLE模式时自动隐藏蓝牙连接状态
- 在主界面UI中添加蓝牙连接状态显示组件(包装器、旋转动画、蓝牙图标)
- 根据蓝牙连接状态动态更新UI显示:搜索时显示旋转动画,连接时显示蓝色蓝牙图标,其他情况隐藏
- 订阅蓝牙对等设备相关事件以实时更新连接状态
This commit is contained in:
2026-04-25 17:49:22 +08:00
parent 07fe70becd
commit d27df7f3bf
3 changed files with 108 additions and 0 deletions

View File

@@ -5,6 +5,7 @@
#define MODULE display_module
#include <caf/events/module_state_event.h>
#include <caf/events/ble_common_event.h>
#include <caf/events/power_event.h>
#include <lvgl_zephyr.h>
@@ -76,6 +77,7 @@ static struct display_module_ctx ctx = {
BLINKY_THEME_DEFAULT_B),
.inactive_border_color = LV_COLOR_MAKE(0x3A, 0x44, 0x52),
.mode = MODE_SWITCH_BLE,
.ble_link_state = UI_BLE_LINK_HIDDEN,
},
.date_text = "1970/01/01",
.time_text = "00:00:00",
@@ -210,6 +212,9 @@ static bool app_event_handler(const struct app_event_header *aeh)
const struct mode_switch_event *event = cast_mode_switch_event(aeh);
ctx.ui_model.mode = event->mode;
if (ctx.ui_model.mode != MODE_SWITCH_BLE) {
ctx.ui_model.ble_link_state = UI_BLE_LINK_HIDDEN;
}
refresh_ui();
return false;
}
@@ -219,6 +224,45 @@ static bool app_event_handler(const struct app_event_header *aeh)
return false;
}
if (is_ble_peer_search_event(aeh)) {
const struct ble_peer_search_event *event =
cast_ble_peer_search_event(aeh);
if (ctx.ui_model.mode == MODE_SWITCH_BLE) {
ctx.ui_model.ble_link_state = event->active ?
UI_BLE_LINK_SEARCHING : UI_BLE_LINK_HIDDEN;
refresh_ui();
}
return false;
}
if (is_ble_peer_event(aeh)) {
const struct ble_peer_event *event = cast_ble_peer_event(aeh);
if (ctx.ui_model.mode != MODE_SWITCH_BLE) {
return false;
}
switch (event->state) {
case PEER_STATE_CONNECTED:
case PEER_STATE_SECURED:
ctx.ui_model.ble_link_state = UI_BLE_LINK_CONNECTED;
break;
case PEER_STATE_DISCONNECTED:
case PEER_STATE_CONN_FAILED:
ctx.ui_model.ble_link_state = UI_BLE_LINK_SEARCHING;
break;
default:
return false;
}
refresh_ui();
return false;
}
if (is_hid_led_event(aeh)) {
const struct hid_led_event *event = cast_hid_led_event(aeh);
@@ -323,6 +367,8 @@ static bool app_event_handler(const struct app_event_header *aeh)
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, bat_state_event);
APP_EVENT_SUBSCRIBE(MODULE, ble_bond_multi_event);
APP_EVENT_SUBSCRIBE(MODULE, ble_peer_event);
APP_EVENT_SUBSCRIBE(MODULE, ble_peer_search_event);
APP_EVENT_SUBSCRIBE(MODULE, datetime_event);
APP_EVENT_SUBSCRIBE(MODULE, hid_led_event);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);

View File

@@ -25,6 +25,9 @@ struct ui_main_ctx {
lv_obj_t *battery_icon;
lv_obj_t *battery_label;
lv_obj_t *battery_state_label;
lv_obj_t *ble_link_wrap;
lv_obj_t *ble_link_spinner;
lv_obj_t *ble_link_icon;
lv_obj_t *date_label;
lv_obj_t *time_label;
};
@@ -42,6 +45,8 @@ static const char *const status_texts[UI_STATUS_COUNT] = {
"A",
};
#define UI_BLE_LINK_BLUE lv_color_hex(0x0082FC)
static lv_color_t ui_main_get_battery_color(uint8_t battery_level)
{
if (battery_level > 70U) {
@@ -176,6 +181,31 @@ void ui_main_refresh_battery(const struct ui_main_model *model)
lv_label_set_text(g_ui.battery_label, battery_text);
lv_label_set_text(g_ui.battery_state_label, state_symbol);
lv_obj_set_style_text_color(g_ui.battery_state_label, state_color, 0);
if ((g_ui.ble_link_wrap == NULL) || (g_ui.ble_link_spinner == NULL) ||
(g_ui.ble_link_icon == NULL)) {
return;
}
switch (model->ble_link_state) {
case UI_BLE_LINK_SEARCHING:
lv_obj_clear_flag(g_ui.ble_link_wrap, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(g_ui.ble_link_spinner, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(g_ui.ble_link_icon, LV_OBJ_FLAG_HIDDEN);
break;
case UI_BLE_LINK_CONNECTED:
lv_obj_clear_flag(g_ui.ble_link_wrap, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(g_ui.ble_link_spinner, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(g_ui.ble_link_icon, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_style_text_color(g_ui.ble_link_icon, UI_BLE_LINK_BLUE, 0);
break;
case UI_BLE_LINK_HIDDEN:
default:
lv_obj_add_flag(g_ui.ble_link_wrap, LV_OBJ_FLAG_HIDDEN);
break;
}
}
void ui_main_refresh_datetime(const char *date_text, const char *time_text)
@@ -270,6 +300,31 @@ void ui_main_init(const struct ui_main_model *model,
g_ui.battery_state_label = lv_label_create(battery_wrap);
lv_obj_set_style_text_font(g_ui.battery_state_label, &lv_font_montserrat_14, 0);
g_ui.ble_link_wrap = lv_obj_create(battery_wrap);
lv_obj_remove_style_all(g_ui.ble_link_wrap);
lv_obj_set_size(g_ui.ble_link_wrap, 18, 18);
lv_obj_add_flag(g_ui.ble_link_wrap, LV_OBJ_FLAG_HIDDEN);
g_ui.ble_link_spinner = lv_spinner_create(g_ui.ble_link_wrap);
lv_obj_center(g_ui.ble_link_spinner);
lv_obj_set_size(g_ui.ble_link_spinner, 16, 16);
lv_spinner_set_anim_params(g_ui.ble_link_spinner, 2000, 200);
lv_obj_set_style_arc_width(g_ui.ble_link_spinner, 2, LV_PART_MAIN);
lv_obj_set_style_arc_width(g_ui.ble_link_spinner, 2, LV_PART_INDICATOR);
lv_obj_set_style_arc_color(g_ui.ble_link_spinner, lv_color_hex(0x2A3442),
LV_PART_MAIN);
lv_obj_set_style_arc_color(g_ui.ble_link_spinner, model->theme_color,
LV_PART_INDICATOR);
lv_obj_remove_style(g_ui.ble_link_spinner, NULL, LV_PART_KNOB);
lv_obj_clear_flag(g_ui.ble_link_spinner, LV_OBJ_FLAG_CLICKABLE);
g_ui.ble_link_icon = lv_label_create(g_ui.ble_link_wrap);
lv_label_set_text(g_ui.ble_link_icon, LV_SYMBOL_BLUETOOTH);
lv_obj_center(g_ui.ble_link_icon);
lv_obj_set_style_text_font(g_ui.ble_link_icon, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(g_ui.ble_link_icon, UI_BLE_LINK_BLUE, 0);
lv_obj_add_flag(g_ui.ble_link_icon, LV_OBJ_FLAG_HIDDEN);
middle_row = lv_obj_create(content);
lv_obj_remove_style_all(middle_row);
lv_obj_set_width(middle_row, LV_PCT(100));

View File

@@ -13,11 +13,18 @@
extern "C" {
#endif
enum ui_ble_link_state {
UI_BLE_LINK_HIDDEN = 0,
UI_BLE_LINK_SEARCHING,
UI_BLE_LINK_CONNECTED,
};
struct ui_main_model {
lv_color_t theme_color;
lv_color_t inactive_border_color;
uint8_t battery_level;
enum mode_switch_mode mode;
enum ui_ble_link_state ble_link_state;
uint8_t led_mask;
bool charging;
bool full;