Files
xiaozhi-esp32/main/display/display.cc

230 lines
7.4 KiB
C++
Raw Normal View History

2024-11-18 06:17:39 +08:00
#include <esp_log.h>
#include <esp_err.h>
#include <string>
#include <cstdlib>
#include "display.h"
#include "board.h"
#include "application.h"
#include "font_awesome_symbols.h"
#include "audio_codec.h"
2025-02-14 01:15:10 +08:00
#include "settings.h"
2024-11-18 06:17:39 +08:00
#define TAG "Display"
Display::Display() {
2025-02-14 01:15:10 +08:00
// Load brightness from settings
Settings settings("display");
brightness_ = settings.GetInt("brightness", 100);
2024-11-18 06:17:39 +08:00
// Notification timer
esp_timer_create_args_t notification_timer_args = {
.callback = [](void *arg) {
Display *display = static_cast<Display*>(arg);
DisplayLockGuard lock(display);
lv_obj_add_flag(display->notification_label_, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(display->status_label_, LV_OBJ_FLAG_HIDDEN);
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
2025-02-24 14:41:34 +08:00
.name = "notification_timer",
2024-11-18 06:17:39 +08:00
.skip_unhandled_events = false,
};
ESP_ERROR_CHECK(esp_timer_create(&notification_timer_args, &notification_timer_));
// Update display timer
esp_timer_create_args_t update_display_timer_args = {
.callback = [](void *arg) {
Display *display = static_cast<Display*>(arg);
display->Update();
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
2025-02-24 14:41:34 +08:00
.name = "update_display_timer",
2025-02-03 23:43:07 +08:00
.skip_unhandled_events = true,
2024-11-18 06:17:39 +08:00
};
ESP_ERROR_CHECK(esp_timer_create(&update_display_timer_args, &update_timer_));
ESP_ERROR_CHECK(esp_timer_start_periodic(update_timer_, 1000000));
}
Display::~Display() {
2025-02-03 23:43:07 +08:00
if (notification_timer_ != nullptr) {
esp_timer_stop(notification_timer_);
esp_timer_delete(notification_timer_);
}
if (update_timer_ != nullptr) {
esp_timer_stop(update_timer_);
esp_timer_delete(update_timer_);
}
2024-11-18 06:17:39 +08:00
if (network_label_ != nullptr) {
lv_obj_del(network_label_);
lv_obj_del(notification_label_);
lv_obj_del(status_label_);
lv_obj_del(mute_label_);
lv_obj_del(battery_label_);
2025-02-03 23:43:07 +08:00
lv_obj_del(emotion_label_);
2024-11-18 06:17:39 +08:00
}
}
2025-02-19 23:54:59 +08:00
void Display::SetStatus(const char* status) {
2025-02-04 14:24:03 +08:00
DisplayLockGuard lock(this);
2024-11-18 06:17:39 +08:00
if (status_label_ == nullptr) {
return;
}
2025-02-19 23:54:59 +08:00
lv_label_set_text(status_label_, status);
2025-01-12 10:25:43 +08:00
lv_obj_clear_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
2024-11-18 06:17:39 +08:00
}
void Display::ShowNotification(const std::string &notification, int duration_ms) {
2025-02-19 23:54:59 +08:00
ShowNotification(notification.c_str(), duration_ms);
}
void Display::ShowNotification(const char* notification, int duration_ms) {
2025-02-04 14:24:03 +08:00
DisplayLockGuard lock(this);
2024-11-18 06:17:39 +08:00
if (notification_label_ == nullptr) {
return;
}
2025-02-19 23:54:59 +08:00
lv_label_set_text(notification_label_, notification);
2024-11-18 06:17:39 +08:00
lv_obj_clear_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
esp_timer_stop(notification_timer_);
ESP_ERROR_CHECK(esp_timer_start_once(notification_timer_, duration_ms * 1000));
}
void Display::Update() {
auto& board = Board::GetInstance();
auto codec = board.GetAudioCodec();
2025-02-01 23:08:41 +08:00
{
DisplayLockGuard lock(this);
2025-02-04 14:24:03 +08:00
if (mute_label_ == nullptr) {
return;
}
2025-02-01 23:08:41 +08:00
// 如果静音状态改变,则更新图标
if (codec->output_volume() == 0 && !muted_) {
muted_ = true;
lv_label_set_text(mute_label_, FONT_AWESOME_VOLUME_MUTE);
} else if (codec->output_volume() > 0 && muted_) {
muted_ = false;
lv_label_set_text(mute_label_, "");
2025-02-03 23:43:07 +08:00
}
2024-11-18 06:17:39 +08:00
}
// 更新电池图标
int battery_level;
bool charging;
const char* icon = nullptr;
if (board.GetBatteryLevel(battery_level, charging)) {
if (charging) {
icon = FONT_AWESOME_BATTERY_CHARGING;
} else {
const char* levels[] = {
FONT_AWESOME_BATTERY_EMPTY, // 0-19%
FONT_AWESOME_BATTERY_1, // 20-39%
FONT_AWESOME_BATTERY_2, // 40-59%
FONT_AWESOME_BATTERY_3, // 60-79%
FONT_AWESOME_BATTERY_FULL, // 80-99%
FONT_AWESOME_BATTERY_FULL, // 100%
};
icon = levels[battery_level / 20];
}
2025-02-04 14:24:03 +08:00
DisplayLockGuard lock(this);
if (battery_label_ != nullptr && battery_icon_ != icon) {
2024-11-18 06:17:39 +08:00
battery_icon_ = icon;
lv_label_set_text(battery_label_, battery_icon_);
}
}
2025-02-01 23:08:41 +08:00
// 升级固件时,不读取 4G 网络状态,避免占用 UART 资源
2025-01-05 19:34:28 +08:00
auto device_state = Application::GetInstance().GetDeviceState();
2025-02-01 23:08:41 +08:00
static const std::vector<DeviceState> allowed_states = {
kDeviceStateIdle,
kDeviceStateStarting,
kDeviceStateWifiConfiguring,
kDeviceStateListening,
};
if (std::find(allowed_states.begin(), allowed_states.end(), device_state) != allowed_states.end()) {
2024-11-18 06:17:39 +08:00
icon = board.GetNetworkStateIcon();
2025-02-04 14:24:03 +08:00
if (network_label_ != nullptr && network_icon_ != icon) {
2025-02-01 23:08:41 +08:00
DisplayLockGuard lock(this);
2024-11-18 06:17:39 +08:00
network_icon_ = icon;
lv_label_set_text(network_label_, network_icon_);
}
}
}
2025-02-19 23:54:59 +08:00
void Display::SetEmotion(const char* emotion) {
2024-11-18 06:17:39 +08:00
struct Emotion {
const char* icon;
const char* text;
};
static const std::vector<Emotion> emotions = {
{FONT_AWESOME_EMOJI_NEUTRAL, "neutral"},
{FONT_AWESOME_EMOJI_HAPPY, "happy"},
{FONT_AWESOME_EMOJI_LAUGHING, "laughing"},
{FONT_AWESOME_EMOJI_FUNNY, "funny"},
{FONT_AWESOME_EMOJI_SAD, "sad"},
{FONT_AWESOME_EMOJI_ANGRY, "angry"},
{FONT_AWESOME_EMOJI_CRYING, "crying"},
{FONT_AWESOME_EMOJI_LOVING, "loving"},
{FONT_AWESOME_EMOJI_EMBARRASSED, "embarrassed"},
{FONT_AWESOME_EMOJI_SURPRISED, "surprised"},
{FONT_AWESOME_EMOJI_SHOCKED, "shocked"},
{FONT_AWESOME_EMOJI_THINKING, "thinking"},
{FONT_AWESOME_EMOJI_WINKING, "winking"},
{FONT_AWESOME_EMOJI_COOL, "cool"},
{FONT_AWESOME_EMOJI_RELAXED, "relaxed"},
{FONT_AWESOME_EMOJI_DELICIOUS, "delicious"},
{FONT_AWESOME_EMOJI_KISSY, "kissy"},
{FONT_AWESOME_EMOJI_CONFIDENT, "confident"},
{FONT_AWESOME_EMOJI_SLEEPY, "sleepy"},
{FONT_AWESOME_EMOJI_SILLY, "silly"},
{FONT_AWESOME_EMOJI_CONFUSED, "confused"}
};
// 查找匹配的表情
2025-02-19 23:54:59 +08:00
std::string_view emotion_view(emotion);
2024-11-18 06:17:39 +08:00
auto it = std::find_if(emotions.begin(), emotions.end(),
2025-02-19 23:54:59 +08:00
[&emotion_view](const Emotion& e) { return e.text == emotion_view; });
2024-11-18 06:17:39 +08:00
2025-02-03 23:43:07 +08:00
DisplayLockGuard lock(this);
2025-02-04 14:24:03 +08:00
if (emotion_label_ == nullptr) {
return;
}
2024-11-18 06:17:39 +08:00
// 如果找到匹配的表情就显示对应图标否则显示默认的neutral表情
if (it != emotions.end()) {
lv_label_set_text(emotion_label_, it->icon);
} else {
lv_label_set_text(emotion_label_, FONT_AWESOME_EMOJI_NEUTRAL);
}
}
void Display::SetIcon(const char* icon) {
2025-02-04 14:24:03 +08:00
DisplayLockGuard lock(this);
2024-11-18 06:17:39 +08:00
if (emotion_label_ == nullptr) {
return;
}
lv_label_set_text(emotion_label_, icon);
}
2025-02-19 23:54:59 +08:00
void Display::SetChatMessage(const char* role, const char* content) {
DisplayLockGuard lock(this);
if (chat_message_label_ == nullptr) {
return;
}
2025-02-19 23:54:59 +08:00
lv_label_set_text(chat_message_label_, content);
2024-11-18 06:17:39 +08:00
}
2025-02-14 01:15:10 +08:00
void Display::SetBacklight(uint8_t brightness) {
Settings settings("display", true);
settings.SetInt("brightness", brightness);
brightness_ = brightness;
}