display.cc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. #include <esp_log.h>
  2. #include <esp_err.h>
  3. #include <string>
  4. #include <cstdlib>
  5. #include <cstring>
  6. #include "display.h"
  7. #include "board.h"
  8. #include "application.h"
  9. #include "font_awesome_symbols.h"
  10. #include "audio_codec.h"
  11. #include "settings.h"
  12. #include "assets/lang_config.h"
  13. #define TAG "Display"
  14. Display::Display() {
  15. // Notification timer
  16. esp_timer_create_args_t notification_timer_args = {
  17. .callback = [](void *arg) {
  18. Display *display = static_cast<Display*>(arg);
  19. DisplayLockGuard lock(display);
  20. lv_obj_add_flag(display->notification_label_, LV_OBJ_FLAG_HIDDEN);
  21. lv_obj_clear_flag(display->status_label_, LV_OBJ_FLAG_HIDDEN);
  22. },
  23. .arg = this,
  24. .dispatch_method = ESP_TIMER_TASK,
  25. .name = "notification_timer",
  26. .skip_unhandled_events = false,
  27. };
  28. ESP_ERROR_CHECK(esp_timer_create(&notification_timer_args, &notification_timer_));
  29. // Update display timer
  30. esp_timer_create_args_t update_display_timer_args = {
  31. .callback = [](void *arg) {
  32. Display *display = static_cast<Display*>(arg);
  33. display->Update();
  34. },
  35. .arg = this,
  36. .dispatch_method = ESP_TIMER_TASK,
  37. .name = "display_update_timer",
  38. .skip_unhandled_events = true,
  39. };
  40. ESP_ERROR_CHECK(esp_timer_create(&update_display_timer_args, &update_timer_));
  41. ESP_ERROR_CHECK(esp_timer_start_periodic(update_timer_, 1000000));
  42. // Create a power management lock
  43. auto ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "display_update", &pm_lock_);
  44. if (ret == ESP_ERR_NOT_SUPPORTED) {
  45. ESP_LOGI(TAG, "Power management not supported");
  46. } else {
  47. ESP_ERROR_CHECK(ret);
  48. }
  49. }
  50. Display::~Display() {
  51. if (notification_timer_ != nullptr) {
  52. esp_timer_stop(notification_timer_);
  53. esp_timer_delete(notification_timer_);
  54. }
  55. if (update_timer_ != nullptr) {
  56. esp_timer_stop(update_timer_);
  57. esp_timer_delete(update_timer_);
  58. }
  59. if (network_label_ != nullptr) {
  60. lv_obj_del(network_label_);
  61. lv_obj_del(notification_label_);
  62. lv_obj_del(status_label_);
  63. lv_obj_del(mute_label_);
  64. lv_obj_del(battery_label_);
  65. lv_obj_del(emotion_label_);
  66. }
  67. if (pm_lock_ != nullptr) {
  68. esp_pm_lock_delete(pm_lock_);
  69. }
  70. }
  71. void Display::SetStatus(const char* status) {
  72. DisplayLockGuard lock(this);
  73. if (status_label_ == nullptr) {
  74. return;
  75. }
  76. lv_label_set_text(status_label_, status);
  77. lv_obj_clear_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
  78. lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
  79. }
  80. void Display::ShowNotification(const std::string &notification, int duration_ms) {
  81. ShowNotification(notification.c_str(), duration_ms);
  82. }
  83. void Display::ShowNotification(const char* notification, int duration_ms) {
  84. DisplayLockGuard lock(this);
  85. if (notification_label_ == nullptr) {
  86. return;
  87. }
  88. lv_label_set_text(notification_label_, notification);
  89. lv_obj_clear_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
  90. lv_obj_add_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
  91. esp_timer_stop(notification_timer_);
  92. ESP_ERROR_CHECK(esp_timer_start_once(notification_timer_, duration_ms * 1000));
  93. }
  94. void Display::Update() {
  95. auto& board = Board::GetInstance();
  96. auto codec = board.GetAudioCodec();
  97. {
  98. DisplayLockGuard lock(this);
  99. if (mute_label_ == nullptr) {
  100. return;
  101. }
  102. // 如果静音状态改变,则更新图标
  103. if (codec->output_volume() == 0 && !muted_) {
  104. muted_ = true;
  105. lv_label_set_text(mute_label_, FONT_AWESOME_VOLUME_MUTE);
  106. } else if (codec->output_volume() > 0 && muted_) {
  107. muted_ = false;
  108. lv_label_set_text(mute_label_, "");
  109. }
  110. }
  111. esp_pm_lock_acquire(pm_lock_);
  112. // 更新电池图标
  113. int battery_level;
  114. bool charging, discharging;
  115. const char* icon = nullptr;
  116. if (board.GetBatteryLevel(battery_level, charging, discharging)) {
  117. if (charging) {
  118. icon = FONT_AWESOME_BATTERY_CHARGING;
  119. } else {
  120. const char* levels[] = {
  121. FONT_AWESOME_BATTERY_EMPTY, // 0-19%
  122. FONT_AWESOME_BATTERY_1, // 20-39%
  123. FONT_AWESOME_BATTERY_2, // 40-59%
  124. FONT_AWESOME_BATTERY_3, // 60-79%
  125. FONT_AWESOME_BATTERY_FULL, // 80-99%
  126. FONT_AWESOME_BATTERY_FULL, // 100%
  127. };
  128. icon = levels[battery_level / 20];
  129. }
  130. DisplayLockGuard lock(this);
  131. if (battery_label_ != nullptr && battery_icon_ != icon) {
  132. battery_icon_ = icon;
  133. lv_label_set_text(battery_label_, battery_icon_);
  134. }
  135. if (low_battery_popup_ != nullptr) {
  136. if (strcmp(icon, FONT_AWESOME_BATTERY_EMPTY) == 0 && discharging) {
  137. if (lv_obj_has_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN)) { // 如果低电量提示框隐藏,则显示
  138. lv_obj_clear_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN);
  139. auto& app = Application::GetInstance();
  140. app.PlaySound(Lang::Sounds::P3_LOW_BATTERY);
  141. }
  142. } else {
  143. // Hide the low battery popup when the battery is not empty
  144. if (!lv_obj_has_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN)) { // 如果低电量提示框显示,则隐藏
  145. lv_obj_add_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN);
  146. }
  147. }
  148. }
  149. }
  150. // 升级固件时,不读取 4G 网络状态,避免占用 UART 资源
  151. auto device_state = Application::GetInstance().GetDeviceState();
  152. static const std::vector<DeviceState> allowed_states = {
  153. kDeviceStateIdle,
  154. kDeviceStateStarting,
  155. kDeviceStateWifiConfiguring,
  156. kDeviceStateListening,
  157. };
  158. if (std::find(allowed_states.begin(), allowed_states.end(), device_state) != allowed_states.end()) {
  159. icon = board.GetNetworkStateIcon();
  160. if (network_label_ != nullptr && icon != nullptr && network_icon_ != icon) {
  161. DisplayLockGuard lock(this);
  162. network_icon_ = icon;
  163. lv_label_set_text(network_label_, network_icon_);
  164. }
  165. }
  166. esp_pm_lock_release(pm_lock_);
  167. }
  168. void Display::SetEmotion(const char* emotion) {
  169. struct Emotion {
  170. const char* icon;
  171. const char* text;
  172. };
  173. static const std::vector<Emotion> emotions = {
  174. {FONT_AWESOME_EMOJI_NEUTRAL, "neutral"},
  175. {FONT_AWESOME_EMOJI_HAPPY, "happy"},
  176. {FONT_AWESOME_EMOJI_LAUGHING, "laughing"},
  177. {FONT_AWESOME_EMOJI_FUNNY, "funny"},
  178. {FONT_AWESOME_EMOJI_SAD, "sad"},
  179. {FONT_AWESOME_EMOJI_ANGRY, "angry"},
  180. {FONT_AWESOME_EMOJI_CRYING, "crying"},
  181. {FONT_AWESOME_EMOJI_LOVING, "loving"},
  182. {FONT_AWESOME_EMOJI_EMBARRASSED, "embarrassed"},
  183. {FONT_AWESOME_EMOJI_SURPRISED, "surprised"},
  184. {FONT_AWESOME_EMOJI_SHOCKED, "shocked"},
  185. {FONT_AWESOME_EMOJI_THINKING, "thinking"},
  186. {FONT_AWESOME_EMOJI_WINKING, "winking"},
  187. {FONT_AWESOME_EMOJI_COOL, "cool"},
  188. {FONT_AWESOME_EMOJI_RELAXED, "relaxed"},
  189. {FONT_AWESOME_EMOJI_DELICIOUS, "delicious"},
  190. {FONT_AWESOME_EMOJI_KISSY, "kissy"},
  191. {FONT_AWESOME_EMOJI_CONFIDENT, "confident"},
  192. {FONT_AWESOME_EMOJI_SLEEPY, "sleepy"},
  193. {FONT_AWESOME_EMOJI_SILLY, "silly"},
  194. {FONT_AWESOME_EMOJI_CONFUSED, "confused"}
  195. };
  196. // 查找匹配的表情
  197. std::string_view emotion_view(emotion);
  198. auto it = std::find_if(emotions.begin(), emotions.end(),
  199. [&emotion_view](const Emotion& e) { return e.text == emotion_view; });
  200. DisplayLockGuard lock(this);
  201. if (emotion_label_ == nullptr) {
  202. return;
  203. }
  204. // 如果找到匹配的表情就显示对应图标,否则显示默认的neutral表情
  205. if (it != emotions.end()) {
  206. lv_label_set_text(emotion_label_, it->icon);
  207. } else {
  208. lv_label_set_text(emotion_label_, FONT_AWESOME_EMOJI_NEUTRAL);
  209. }
  210. }
  211. void Display::SetIcon(const char* icon) {
  212. DisplayLockGuard lock(this);
  213. if (emotion_label_ == nullptr) {
  214. return;
  215. }
  216. lv_label_set_text(emotion_label_, icon);
  217. }
  218. void Display::SetChatMessage(const char* role, const char* content) {
  219. DisplayLockGuard lock(this);
  220. if (chat_message_label_ == nullptr) {
  221. return;
  222. }
  223. lv_label_set_text(chat_message_label_, content);
  224. }