Update to 2.0.0

This commit is contained in:
2025-09-13 23:40:38 +08:00
parent 5a929f5b06
commit 63e404d610
247 changed files with 13586 additions and 11497 deletions

View File

@@ -85,7 +85,7 @@ Led* Board::GetLed() {
return &led;
}
std::string Board::GetJson() {
std::string Board::GetSystemInfoJson() {
/*
{
"version": 2,
@@ -178,4 +178,13 @@ std::string Board::GetJson() {
// Close the JSON object
json += R"(})";
return json;
}
Assets* Board::GetAssets() {
#ifdef DEFAULT_ASSETS
static Assets assets(DEFAULT_ASSETS);
return &assets;
#else
return nullptr;
#endif
}

View File

@@ -12,6 +12,8 @@
#include "backlight.h"
#include "camera.h"
#include "music.h"
#include "assets.h"
void* create_board();
class AudioCodec;
@@ -30,14 +32,13 @@ protected:
// 音乐播放器实例
Music* music_;
public:
static Board& GetInstance() {
static Board* instance = static_cast<Board*>(create_board());
return *instance;
}
virtual ~Board();
virtual ~Board(); // 改为非默认析构函数,用于清理 music_
virtual std::string GetBoardType() = 0;
virtual std::string GetUuid() { return uuid_; }
virtual Backlight* GetBacklight() { return nullptr; }
@@ -51,10 +52,11 @@ public:
virtual void StartNetwork() = 0;
virtual const char* GetNetworkStateIcon() = 0;
virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging);
virtual std::string GetJson();
virtual std::string GetSystemInfoJson();
virtual void SetPowerSaveMode(bool enabled) = 0;
virtual std::string GetBoardJson() = 0;
virtual std::string GetDeviceStatusJson() = 0;
virtual Assets* GetAssets();
};
#define DECLARE_BOARD(BOARD_CLASS_NAME) \
@@ -62,4 +64,4 @@ void* create_board() { \
return new BOARD_CLASS_NAME(); \
}
#endif // BOARD_H
#endif // BOARD_H

View File

@@ -3,6 +3,7 @@
#include "display.h"
#include "board.h"
#include "system_info.h"
#include "lvgl_display.h"
#include <esp_log.h>
#include <esp_heap_caps.h>
@@ -23,48 +24,6 @@ Esp32Camera::Esp32Camera(const camera_config_t& config) {
if (s->id.PID == GC0308_PID) {
s->set_hmirror(s, 0); // 这里控制摄像头镜像 写1镜像 写0不镜像
}
// 初始化预览图片的内存
memset(&preview_image_, 0, sizeof(preview_image_));
preview_image_.header.magic = LV_IMAGE_HEADER_MAGIC;
preview_image_.header.cf = LV_COLOR_FORMAT_RGB565;
preview_image_.header.flags = 0;
switch (config.frame_size) {
case FRAMESIZE_SVGA:
preview_image_.header.w = 800;
preview_image_.header.h = 600;
break;
case FRAMESIZE_VGA:
preview_image_.header.w = 640;
preview_image_.header.h = 480;
break;
case FRAMESIZE_QVGA:
preview_image_.header.w = 320;
preview_image_.header.h = 240;
break;
case FRAMESIZE_128X128:
preview_image_.header.w = 128;
preview_image_.header.h = 128;
break;
case FRAMESIZE_240X240:
preview_image_.header.w = 240;
preview_image_.header.h = 240;
break;
default:
ESP_LOGE(TAG, "Unsupported frame size: %d, image preview will not be shown", config.frame_size);
preview_image_.data_size = 0;
preview_image_.data = nullptr;
return;
}
preview_image_.header.stride = preview_image_.header.w * 2;
preview_image_.data_size = preview_image_.header.w * preview_image_.header.h * 2;
preview_image_.data = (uint8_t*)heap_caps_malloc(preview_image_.data_size, MALLOC_CAP_SPIRAM);
if (preview_image_.data == nullptr) {
ESP_LOGE(TAG, "Failed to allocate memory for preview image");
return;
}
}
Esp32Camera::~Esp32Camera() {
@@ -72,10 +31,6 @@ Esp32Camera::~Esp32Camera() {
esp_camera_fb_return(fb_);
fb_ = nullptr;
}
if (preview_image_.data) {
heap_caps_free((void*)preview_image_.data);
preview_image_.data = nullptr;
}
esp_camera_deinit();
}
@@ -89,6 +44,7 @@ bool Esp32Camera::Capture() {
encoder_thread_.join();
}
auto start_time = esp_timer_get_time();
int frames_to_get = 2;
// Try to get a stable frame
for (int i = 0; i < frames_to_get; i++) {
@@ -101,28 +57,36 @@ bool Esp32Camera::Capture() {
return false;
}
}
auto end_time = esp_timer_get_time();
ESP_LOGI(TAG, "Camera captured %d frames in %d ms", frames_to_get, int((end_time - start_time) / 1000));
// 如果预览图片 buffer 为空,则跳过预览
// 但仍返回 true因为此时图像可以上传至服务器
if (preview_image_.data_size == 0) {
ESP_LOGW(TAG, "Skip preview because of unsupported frame size");
return true;
}
if (preview_image_.data == nullptr) {
ESP_LOGE(TAG, "Preview image data is not initialized");
return true;
}
// 显示预览图片
auto display = Board::GetInstance().GetDisplay();
auto display = dynamic_cast<LvglDisplay*>(Board::GetInstance().GetDisplay());
if (display != nullptr) {
// Create a new preview image
auto img_dsc = (lv_img_dsc_t*)heap_caps_calloc(1, sizeof(lv_img_dsc_t), MALLOC_CAP_8BIT);
img_dsc->header.magic = LV_IMAGE_HEADER_MAGIC;
img_dsc->header.cf = LV_COLOR_FORMAT_RGB565;
img_dsc->header.flags = 0;
img_dsc->header.w = fb_->width;
img_dsc->header.h = fb_->height;
img_dsc->header.stride = fb_->width * 2;
img_dsc->data_size = fb_->width * fb_->height * 2;
img_dsc->data = (uint8_t*)heap_caps_malloc(img_dsc->data_size, MALLOC_CAP_SPIRAM);
if (img_dsc->data == nullptr) {
ESP_LOGE(TAG, "Failed to allocate memory for preview image");
heap_caps_free(img_dsc);
return false;
}
auto src = (uint16_t*)fb_->buf;
auto dst = (uint16_t*)preview_image_.data;
auto dst = (uint16_t*)img_dsc->data;
size_t pixel_count = fb_->len / 2;
for (size_t i = 0; i < pixel_count; i++) {
// 交换每个16位字内的字节
dst[i] = __builtin_bswap16(src[i]);
}
display->SetPreviewImage(&preview_image_);
display->SetPreviewImage(img_dsc);
}
return true;
}
@@ -185,14 +149,14 @@ bool Esp32Camera::SetVFlip(bool enabled) {
*/
std::string Esp32Camera::Explain(const std::string& question) {
if (explain_url_.empty()) {
return "{\"success\": false, \"message\": \"Image explain URL or token is not set\"}";
throw std::runtime_error("Image explain URL or token is not set");
}
// 创建局部的 JPEG 队列, 40 entries is about to store 512 * 40 = 20480 bytes of JPEG data
QueueHandle_t jpeg_queue = xQueueCreate(40, sizeof(JpegChunk));
if (jpeg_queue == nullptr) {
ESP_LOGE(TAG, "Failed to create JPEG queue");
return "{\"success\": false, \"message\": \"Failed to create JPEG queue\"}";
throw std::runtime_error("Failed to create JPEG queue");
}
// We spawn a thread to encode the image to JPEG
@@ -235,7 +199,7 @@ std::string Esp32Camera::Explain(const std::string& question) {
}
}
vQueueDelete(jpeg_queue);
return "{\"success\": false, \"message\": \"Failed to connect to explain URL\"}";
throw std::runtime_error("Failed to connect to explain URL");
}
{
@@ -288,7 +252,7 @@ std::string Esp32Camera::Explain(const std::string& question) {
if (http->GetStatusCode() != 200) {
ESP_LOGE(TAG, "Failed to upload photo, status code: %d", http->GetStatusCode());
return "{\"success\": false, \"message\": \"Failed to upload photo\"}";
throw std::runtime_error("Failed to upload photo");
}
std::string result = http->ReadAll();

View File

@@ -19,7 +19,6 @@ struct JpegChunk {
class Esp32Camera : public Camera {
private:
camera_fb_t* fb_ = nullptr;
lv_img_dsc_t preview_image_;
std::string explain_url_;
std::string explain_token_;
std::thread encoder_thread_;

File diff suppressed because it is too large Load Diff

View File

@@ -38,6 +38,7 @@ private:
std::string current_music_url_;
std::string current_song_name_;
bool song_name_displayed_;
std::atomic<bool> stop_flag_{false}; // 停止播放标志位
// 歌词相关
std::string current_lyric_url_;

View File

@@ -153,7 +153,10 @@ std::string Ml307Board::GetDeviceStatusJson() {
}
auto display = board.GetDisplay();
if (display && display->height() > 64) { // For LCD display only
cJSON_AddStringToObject(screen, "theme", display->GetTheme().c_str());
auto theme = display->GetTheme();
if (theme != nullptr) {
cJSON_AddStringToObject(screen, "theme", theme->name().c_str());
}
}
cJSON_AddItemToObject(root, "screen", screen);

View File

@@ -216,7 +216,10 @@ std::string WifiBoard::GetDeviceStatusJson() {
}
auto display = board.GetDisplay();
if (display && display->height() > 64) { // For LCD display only
cJSON_AddStringToObject(screen, "theme", display->GetTheme().c_str());
auto theme = display->GetTheme();
if (theme != nullptr) {
cJSON_AddStringToObject(screen, "theme", theme->name().c_str());
}
}
cJSON_AddItemToObject(root, "screen", screen);