diff --git a/Bionic_Core/OTAClass/OTAClass.cpp b/Bionic_Core/OTAClass/OTAClass.cpp index d034129..2bd532f 100644 --- a/Bionic_Core/OTAClass/OTAClass.cpp +++ b/Bionic_Core/OTAClass/OTAClass.cpp @@ -63,15 +63,19 @@ esp_pthread_cfg_t create_config(const char *name, int core_id, int stack, int pr #include "ThreadManager.h" #include "WifiConnectors.h" #include + +#include "LVGLRender.h" +#include "SDFileManager.h" void OTAClass::Init() { ESP_LOGI("OTA", "Init"); ESP_LOGI("OTAClass::Init", "当前固件版本 1.0.1"); - // 测试Wifi - // WifiConnectors::getInstance()->log(); - // - // WifiConnectors::getInstance()->connectWifi("Misaki-2.4G", "88888888"); + // 列出当前目录内容 + std::string listing = SDFileManager::getInstance()->lsCommand(".", false, true); + ESP_LOGI("SD", "%s", listing.c_str()); + + LVGLRender::getInstance()->RenderGif("sequence01.gif"); // 1. 创建普通函数一个可以运行在任意核上的线程 ThreadConfig config1; diff --git a/Bionic_Core/PetBaseClass/PetBaseClass.h b/Bionic_Core/PetBaseClass/PetBaseClass.h index 5cc45c7..651ed7a 100644 --- a/Bionic_Core/PetBaseClass/PetBaseClass.h +++ b/Bionic_Core/PetBaseClass/PetBaseClass.h @@ -2,3 +2,5 @@ // Created by misaki on 2025/9/2. // #pragma once + +#include "PetInterface.h" diff --git a/Bionic_Core/PetBaseClass/PetInterface.cpp b/Bionic_Core/PetBaseClass/PetInterface.cpp new file mode 100644 index 0000000..0ed5b56 --- /dev/null +++ b/Bionic_Core/PetBaseClass/PetInterface.cpp @@ -0,0 +1,5 @@ +// +// Created by misaki on 2025/9/8. +// + +#include "PetInterface.h" \ No newline at end of file diff --git a/Bionic_Core/PetBaseClass/PetInterface.h b/Bionic_Core/PetBaseClass/PetInterface.h new file mode 100644 index 0000000..dfc40a2 --- /dev/null +++ b/Bionic_Core/PetBaseClass/PetInterface.h @@ -0,0 +1,54 @@ +// +// Created by misaki on 2025/9/8. +// + +#pragma once +#include +#include +#include + +// 资源句柄,防止裸指针到处飞 +template +using Handle = std::shared_ptr; /// 宠物句柄 + +// 前向声明 +class IPet; /// 宠物接口 +using IPetPtr = Handle; /// 宠物句柄 + +// 事件类型 +enum class Emotion { + Feed, /// 喂食 + Happy, /// 高兴 + Angry, /// 生气 + Upset /// 沮丧 +}; + +// 成长阶段 +enum class Stage { + Baby, /// 幼年 + Teen, /// 青年 + Adult /// 成长 +}; + +// 数值封装 +struct Vitals { + int hp = 100; // 0-100 /// 生命值 + int intimacy = 0; // 0-100 /// 亲密度 +}; + +// 身份设定 +struct Persona { + std::string systemPrompt; /// 系统提示 + std::string greet; /// 默认欢迎语 + // …可扩展 +}; + +// 动物元数据(只读) +struct AnimalManifest { + std::string id; // "snow_leopard" /// ID + std::string displayName; /// 显示名称 + Persona persona; /// 身份设定 + std::unordered_map modelPath; /// 阶段→模型 + std::unordered_map audioPath; /// 阶段→音频 + std::string version; /// 版本 +}; \ No newline at end of file diff --git a/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.cpp b/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.cpp new file mode 100644 index 0000000..61d4636 --- /dev/null +++ b/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.cpp @@ -0,0 +1,142 @@ +// +// Created by misaki on 2025/9/8. +// + +#include "ThreadManager.h" +#include "LVGLRender.h" +#include "I2C_Driver.h" +#include "ST77916.h" +#include "LVGL_Driver.h" + +#include + +LVGLRender* LVGLRender::LVGLRenderInstance = nullptr; +std::mutex LVGLRender::instance_mutex; +uint16_t LVGLRender::fps = 30; + +LVGLRender* LVGLRender::getInstance() { + // 双检锁(DCLP),C++11 起 atomic+mutex 组合保证线程安全 + LVGLRender* tmp = LVGLRenderInstance; + if (tmp == nullptr) { + std::lock_guard lock(instance_mutex); + tmp = LVGLRenderInstance; + if (tmp == nullptr) { + tmp = new LVGLRender(); + LVGLRenderInstance = tmp; + } + } + return tmp; +} + +void LVGLRender::LVGL_Update() { + while (true) { + vTaskDelay(LVGL_DELAY_FROM_FPS(LVGLRender::fps)); + lv_timer_handler(); + } +} + + +// 构造函数 +LVGLRender::LVGLRender() { + ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...初始化媒体驱动..."); + I2C_Init(); + LCD_Init(); + LVGL_Init(); + ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...初始化媒体驱动成功..."); + + ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...创建LVGL心跳..."); + + ThreadConfig trickConfig; + trickConfig.core_id = 1; // 渲染分配给核1 + trickConfig.name = "LVGL_Render_Heartbeat"; + trickConfig.priority = 5; + trickConfig.stack_size = 4096; // 给LVGL一个较大的堆栈,避免栈溢出 + + std::thread tick_thread = ThreadManager::createMemberThread(trickConfig, this, &LVGLRender::LVGL_Update); + + tick_thread.detach(); // 线程分离 生命周期跟随主线程结束,线程结束后自动销毁 + + ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...创建LVGL心跳成功..."); +} + +LVGLRender::~LVGLRender() { + +} + +void LVGLRender::log() { + ESP_LOGI("LVGL_Render", "LVGL_Render log..."); +} + +// 静态:拼出完整路径 +std::string LVGLRender::makeFullPath(const std::string& filename) +{ + return "/sdcard/" + filename; +} + +#include "SDFileManager.h" +std::vector LVGLRender::readWholeFile(const std::string& path) +{ + ESP_LOGI("LVGLRender", "开始读取文件: %s", path.c_str()); + + // 直接用 SDFileManager 同步读整个文件 TODO: 考虑修改为异步读 + std::string content = SDFileManager::getInstance()->readFileSync(path.c_str()); + if (content.empty()) { + ESP_LOGE("LVGLRender", "readFileSync 失败或文件为空"); + return {}; + } + + // string -> vector,零拷贝 move + return {content.begin(), content.end()}; +} + +bool LVGLRender::getGifWH(const uint8_t* raw, uint32_t& w, uint32_t& h) +{ + if (!raw || memcmp(raw, "GIF", 3) != 0) { + ESP_LOGE("LVGLRender", "不是合法 GIF 文件头"); + return false; + } + // GIF87a/89a 宽高偏移 6~9 字节,小端 + w = raw[6] | (raw[7] << 8); + h = raw[8] | (raw[9] << 8); + ESP_LOGI("LVGLRender", "GIF 尺寸: %lu x %lu", w, h); + return true; +} +void LVGLRender::renderGifInternal(const std::vector& data, + uint32_t w, uint32_t h) +{ + // 构造 lv_img_dsc_t —— 数据指针直接指向 vector 内部 + static lv_img_dsc_t gif_desc; + gif_desc.header.cf = LV_IMG_CF_RAW_CHROMA_KEYED; + gif_desc.header.always_zero = 0; + gif_desc.header.reserved = 0; + gif_desc.header.w = (lv_coord_t)w; + gif_desc.header.h = (lv_coord_t)h; + gif_desc.data_size = data.size(); + gif_desc.data = data.data(); // 指向 vector 内部 + + // 创建 lv_gif 对象 + lv_obj_t* gif = lv_gif_create(lv_scr_act()); + lv_gif_set_src(gif, &gif_desc); + lv_obj_center(gif); + + ESP_LOGI("LVGLRender", "GIF 已渲染到屏幕"); +} + +void LVGLRender::RenderGif(const std::string &filename) { + std::string fullPath = makeFullPath(filename); + + // 读文件 + std::vector gifBin = readWholeFile(fullPath); + if (gifBin.empty()) return; + + // 校验并解析宽高 + uint32_t w = 0, h = 0; + if (!getGifWH(gifBin.data(), w, h)) return; + + // LVGL 渲染函数 + renderGifInternal(gifBin, w, h); +} + + + + diff --git a/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.h b/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.h new file mode 100644 index 0000000..5e4f683 --- /dev/null +++ b/Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.h @@ -0,0 +1,61 @@ +// +// Created by misaki on 2025/9/8. +// +/** + * 本类为单例类,用于实现LVGL的渲染 + * 封装了一整套的LVGL渲染流 + * 同时兼顾了底层的显示驱动 + */ +#pragma once + +#define LVGL_DELAY_FROM_FPS(fps) pdMS_TO_TICKS(1000 / (fps)) + +#include +#include + +class LVGLRender { +public: + static std::string makeFullPath(const std::string& filename); + +public: + static LVGLRender* getInstance(); + + /** + * 全屏渲染gif动画 + * @brief 渲染GIF文件 + * @param filename GIF文件路径 + */ + void RenderGif(const std::string &filename); + + void log(); + + // gif渲染 +private: + /* 同步读整个文件到 vector */ + std::vector readWholeFile(const std::string& path); + + /* 从原始数据解析 GIF 宽高 */ + bool getGifWH(const uint8_t* raw, uint32_t& w, uint32_t& h); + + /* 真正的渲染实现(空壳,先打印日志) */ + void renderGifInternal(const std::vector& data, + uint32_t w, uint32_t h); + +private: + explicit LVGLRender(); // 构造函数私有化 + ~LVGLRender(); + + LVGLRender(LVGLRender const&) = delete; // 拷贝构造函数私有化 + LVGLRender& operator=(LVGLRender const&) = delete;// 赋值运算符私有化 + + void LVGL_Update(); // 渲染lvgl上下文(持久性线程) + +private: + static LVGLRender* LVGLRenderInstance; /// 单例实例 + static std::mutex instance_mutex; /// 单例锁 + static uint16_t fps; /// 帧率 +}; + + + + diff --git a/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.cpp b/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.cpp new file mode 100644 index 0000000..48f26bd --- /dev/null +++ b/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.cpp @@ -0,0 +1,505 @@ +// +// Created by misaki on 2025/9/9. +// + +#include "SDFileManager.h" +#include "SD_MMC.h" +#include +#include +#include +#include + +SDFileManager* SDFileManager::SDFileInstance = nullptr; +std::mutex SDFileManager::instance_mutex; + +SDFileManager *SDFileManager::getInstance() { + // 双检锁(DCLP),C++11 起 atomic+mutex 组合保证线程安全 + SDFileManager* tmp = SDFileInstance; + if (tmp == nullptr) { + std::lock_guard lock(instance_mutex); + tmp = SDFileInstance; + if (tmp == nullptr) { + tmp = new SDFileManager(); + SDFileInstance = tmp; + } + } + return tmp; +} + +SDFileManager::SDFileManager() : is_initialized(false), current_directory(MOUNT_POINT) { + // 初始化SD卡 + init(); + // 获取Flash大小 + Flash_Searching(); +} + +SDFileManager::~SDFileManager() { + // 清理资源 +} + +void SDFileManager::init() { + SD_Init(); + is_initialized = true; +} + +bool SDFileManager::writeFileSync(const char* path, const char* data) { + std::lock_guard lock(file_operation_mutex); + + if (!is_initialized) { + ESP_LOGE("SDFileManager", "SD card not initialized"); + return false; + } + + esp_err_t result = s_example_write_file(path, const_cast(data)); + return result == ESP_OK; +} + +std::string SDFileManager::readFileSync(const char* path) { + std::lock_guard lock(file_operation_mutex); + + if (!is_initialized) { + ESP_LOGE("SDFileManager", "SD card not initialized"); + return ""; + } + + FILE* file = fopen(path, "r"); + if (file == nullptr) { + ESP_LOGE("SDFileManager", "Failed to open file: %s", path); + return ""; + } + + // 获取文件大小 + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); + + // 读取文件内容 + std::string content; + content.resize(size); + size_t bytes_read = fread(&content[0], 1, size, file); + fclose(file); + + if (bytes_read != static_cast(size)) { + ESP_LOGE("SDFileManager", "Failed to read entire file: %s", path); + return ""; + } + + return content; +} + +std::vector SDFileManager::listFilesSync(const char* directory, const char* extension) { + std::lock_guard lock(file_operation_mutex); + + if (!is_initialized) { + ESP_LOGE("SDFileManager", "SD card not initialized"); + return {}; + } + + const int max_files = 50; + char file_names[max_files][100]; + uint16_t file_count = Folder_retrieval(directory, extension, file_names, max_files); + + std::vector files; + for (uint16_t i = 0; i < file_count; ++i) { + files.emplace_back(file_names[i]); + } + + return files; +} + +FILE* SDFileManager::openFileSync(const char* path, const char* mode) { + std::lock_guard lock(file_operation_mutex); + + if (!is_initialized) { + ESP_LOGE("SDFileManager", "SD card not initialized"); + return nullptr; + } + + return Open_File(path); +} + +bool SDFileManager::closeFileSync(FILE* file) { + if (file != nullptr) { + return fclose(file) == 0; + } + return false; +} + +void SDFileManager::asyncWriteFile(const char* path, const char* data, WriteCallback callback) { + ThreadConfig config = getThreadConfig("write_file"); + + ThreadManager::createThread(config, [this, path = std::string(path), + data = std::string(data), callback]() { + bool success = this->writeFileSync(path.c_str(), data.c_str()); + if (callback) { + callback(success, path.c_str()); + } + }).detach(); // 分离线程,使其独立运行 +} + +void SDFileManager::asyncReadFile(const char* path, ReadCallback callback) { + if (!callback) { + return; // 没有回调函数,异步读取无意义 + } + + ThreadConfig config = getThreadConfig("read_file"); + + ThreadManager::createThread(config, [this, path = std::string(path), callback]() { + std::string content = this->readFileSync(path.c_str()); + bool success = !content.empty(); + callback(success, path.c_str(), content); + }).detach(); +} + +void SDFileManager::asyncListFiles(const char* directory, const char* extension, ListCallback callback) { + if (!callback) { + return; // 没有回调函数,异步列出文件无意义 + } + + ThreadConfig config = getThreadConfig("list_files"); + + ThreadManager::createThread(config, [this, directory = std::string(directory), + extension = std::string(extension ? extension : ""), + callback]() { + std::vector files = this->listFilesSync( + directory.c_str(), + extension.empty() ? nullptr : extension.c_str() + ); + bool success = !files.empty(); + callback(success, files); + }).detach(); +} + +void SDFileManager::asyncOpenFile(const char* path, const char* mode, OpenCallback callback) { + if (!callback) { + return; // 没有回调函数,异步打开文件无意义 + } + + ThreadConfig config = getThreadConfig("open_file"); + + ThreadManager::createThread(config, [this, path = std::string(path), + mode = std::string(mode), callback]() { + FILE* file = this->openFileSync(path.c_str(), mode.c_str()); + bool success = (file != nullptr); + callback(success, file); + }).detach(); +} + +ThreadConfig SDFileManager::getThreadConfig(const char* operation) { + ThreadConfig config; + config.name = "sd_" + std::string(operation); + config.core_id = -1; // 不绑定核心 + config.stack_size = 4096; + config.priority = 5; + config.inherit_cfg = false; + return config; +} + +// 实现ls命令 +std::string SDFileManager::lsCommand(const char* path, bool recursive, bool showDetails) { + std::lock_guard lock(file_operation_mutex); + + if (!is_initialized) { + ESP_LOGE("SDFileManager", "SD card not initialized"); + return "Error: SD card not initialized"; + } + + std::string fullPath = path; + if (fullPath.empty() || fullPath == ".") { + fullPath = current_directory; + } else if (fullPath[0] != '/') { + // 相对路径 + fullPath = current_directory + "/" + fullPath; + } + + DIR* dir = opendir(fullPath.c_str()); + if (dir == nullptr) { + return "Error: Cannot open directory " + fullPath; + } + + std::stringstream result; + struct dirent* entry; + struct stat st; + + result << "Directory listing for: " << fullPath << "\n"; + result << "----------------------------------------\n"; + + while ((entry = readdir(dir)) != nullptr) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + std::string entryPath = fullPath + "/" + entry->d_name; + if (stat(entryPath.c_str(), &st) != 0) { + continue; + } + + if (showDetails) { + result << getFileInfoString(entry->d_name, &st); + } else { + result << entry->d_name; + } + + if (S_ISDIR(st.st_mode)) { + result << "/"; + } + result << "\n"; + + // 递归列出子目录 + if (recursive && S_ISDIR(st.st_mode)) { + std::string subDirResult = lsCommand(entryPath.c_str(), true, showDetails); + result << subDirResult; + } + } + + closedir(dir); + return result.str(); +} + +// 实现cat命令 +std::string SDFileManager::catCommand(const char* path, bool lineNumbers) { + std::lock_guard lock(file_operation_mutex); + + if (!is_initialized) { + ESP_LOGE("SDFileManager", "SD card not initialized"); + return "Error: SD card not initialized"; + } + + std::string fullPath = path; + if (fullPath[0] != '/') { + // 相对路径 + fullPath = current_directory + "/" + fullPath; + } + + FILE* file = fopen(fullPath.c_str(), "r"); + if (file == nullptr) { + return "Error: Cannot open file " + fullPath; + } + + std::stringstream result; + char buffer[256]; + int lineNumber = 1; + + while (fgets(buffer, sizeof(buffer), file) != nullptr) { + if (lineNumbers) { + result << std::setw(4) << lineNumber << ": "; + } + result << buffer; + lineNumber++; + } + + fclose(file); + return result.str(); +} + +// 实现pwd命令 +std::string SDFileManager::pwdCommand() { + return current_directory; +} + +// 实现cd命令 +bool SDFileManager::cdCommand(const char* path) { + std::lock_guard lock(file_operation_mutex); + + if (!is_initialized) { + ESP_LOGE("SDFileManager", "SD card not initialized"); + return false; + } + + std::string newPath; + if (path[0] == '/') { + // 绝对路径 + newPath = path; + } else if (strcmp(path, "..") == 0) { + // 上级目录 + size_t pos = current_directory.find_last_of('/'); + if (pos != std::string::npos && pos > 0) { + newPath = current_directory.substr(0, pos); + } else { + newPath = "/"; + } + } else { + // 相对路径 + newPath = current_directory + "/" + path; + } + + // 检查路径是否存在且是目录 + struct stat st; + if (stat(newPath.c_str(), &st) != 0 || !S_ISDIR(st.st_mode)) { + return false; + } + + current_directory = newPath; + return true; +} + +// 实现mkdir命令 +bool SDFileManager::mkdirCommand(const char* path) { + std::lock_guard lock(file_operation_mutex); + + if (!is_initialized) { + ESP_LOGE("SDFileManager", "SD card not initialized"); + return false; + } + + std::string fullPath = path; + if (fullPath[0] != '/') { + // 相对路径 + fullPath = current_directory + "/" + fullPath; + } + + // 创建目录 + int result = mkdir(fullPath.c_str(), 0777); + return result == 0; +} + +// 实现rm命令 +bool SDFileManager::rmCommand(const char* path, bool recursive) { + std::lock_guard lock(file_operation_mutex); + + if (!is_initialized) { + ESP_LOGE("SDFileManager", "SD card not initialized"); + return false; + } + + std::string fullPath = path; + if (fullPath[0] != '/') { + // 相对路径 + fullPath = current_directory + "/" + fullPath; + } + + struct stat st; + if (stat(fullPath.c_str(), &st) != 0) { + return false; + } + + if (S_ISDIR(st.st_mode)) { + if (recursive) { + return removeDirectoryRecursive(fullPath.c_str()); + } else { + // 非递归删除空目录 + return rmdir(fullPath.c_str()) == 0; + } + } else { + // 删除文件 + return remove(fullPath.c_str()) == 0; + } +} + +// 实现stat命令 +std::string SDFileManager::statCommand(const char* path) { + std::lock_guard lock(file_operation_mutex); + + if (!is_initialized) { + ESP_LOGE("SDFileManager", "SD card not initialized"); + return "Error: SD card not initialized"; + } + + std::string fullPath = path; + if (fullPath[0] != '/') { + // 相对路径 + fullPath = current_directory + "/" + fullPath; + } + + struct stat st; + if (stat(fullPath.c_str(), &st) != 0) { + return "Error: Cannot get file status for " + fullPath; + } + + return getFileInfoString(fullPath.c_str(), &st); +} + +// 辅助方法:递归删除目录 +bool SDFileManager::removeDirectoryRecursive(const char* path) { + DIR* dir = opendir(path); + if (dir == nullptr) { + return false; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + std::string entryPath = std::string(path) + "/" + entry->d_name; + struct stat st; + if (stat(entryPath.c_str(), &st) != 0) { + continue; + } + + if (S_ISDIR(st.st_mode)) { + if (!removeDirectoryRecursive(entryPath.c_str())) { + closedir(dir); + return false; + } + } else { + if (remove(entryPath.c_str()) != 0) { + closedir(dir); + return false; + } + } + } + + closedir(dir); + return rmdir(path) == 0; +} + +// 辅助方法:获取文件信息字符串 +std::string SDFileManager::getFileInfoString(const char* path, const struct stat* st) { + std::stringstream info; + + // 文件类型和权限 + info << getFileTypeString(st->st_mode); + info << ((st->st_mode & S_IRUSR) ? "r" : "-"); + info << ((st->st_mode & S_IWUSR) ? "w" : "-"); + info << ((st->st_mode & S_IXUSR) ? "x" : "-"); + info << ((st->st_mode & S_IRGRP) ? "r" : "-"); + info << ((st->st_mode & S_IWGRP) ? "w" : "-"); + info << ((st->st_mode & S_IXGRP) ? "x" : "-"); + info << ((st->st_mode & S_IROTH) ? "r" : "-"); + info << ((st->st_mode & S_IWOTH) ? "w" : "-"); + info << ((st->st_mode & S_IXOTH) ? "x" : "-"); + + // 文件大小 + info << " " << std::setw(10) << formatFileSize(st->st_size); + + // 最后修改时间 + struct tm* timeinfo = localtime(&st->st_mtime); + char timeBuf[20]; + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M", timeinfo); + info << " " << timeBuf; + + // 文件名 + info << " " << path; + + return info.str(); +} + +// 辅助方法:获取文件类型字符串 +std::string SDFileManager::getFileTypeString(mode_t mode) { + if (S_ISREG(mode)) return "-"; // 普通文件 + if (S_ISDIR(mode)) return "d"; // 目录 + if (S_ISCHR(mode)) return "c"; // 字符设备 + if (S_ISBLK(mode)) return "b"; // 块设备 + if (S_ISFIFO(mode)) return "p"; // 管道 + if (S_ISLNK(mode)) return "l"; // 符号链接 + if (S_ISSOCK(mode)) return "s"; // 套接字 + return "?"; // 未知类型 +} + +// 辅助方法:格式化文件大小 +std::string SDFileManager::formatFileSize(size_t size) { + std::stringstream ss; + if (size < 1024) { + ss << size << " B"; + } else if (size < 1024 * 1024) { + ss << std::fixed << std::setprecision(1) << (size / 1024.0) << " KB"; + } else if (size < 1024 * 1024 * 1024) { + ss << std::fixed << std::setprecision(1) << (size / (1024.0 * 1024.0)) << " MB"; + } else { + ss << std::fixed << std::setprecision(1) << (size / (1024.0 * 1024.0 * 1024.0)) << " GB"; + } + return ss.str(); +} + diff --git a/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.h b/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.h new file mode 100644 index 0000000..bef25a7 --- /dev/null +++ b/Bionic_Core/ToolsClass/SDFileManager/SDFileManager.h @@ -0,0 +1,178 @@ +// +// Created by misaki on 2025/9/9. +// +/** + * 本模块用于管理SD卡文件 + * 同样为单例类 + * 支持异步读取和写入 + */ +#pragma once + +#include +#include +#include +#include +#include "ThreadManager.h" +#include "SD_MMC.h" + +class SDFileManager { +public: + // 文件操作回调函数类型定义 + using WriteCallback = std::function; + using ReadCallback = std::function; + using ListCallback = std::function& files)>; + using OpenCallback = std::function; + + static SDFileManager* getInstance(); + + // 同步文件操作 + /** + * 同步写入文件 + * @param path 文件路径 + * @param data 数据 + * @return 是否成功 + */ + bool writeFileSync(const char* path, const char* data); + /** + * 同步读取文件 + * @param path 文件路径 + * @return 文件内容 + */ + std::string readFileSync(const char* path); + + /** + * 同步列出目录下的文件 + * @param directory 目录路径 + * @param extension 文件扩展名 + * @return 文件列表 + */ + std::vector listFilesSync(const char* directory, const char* extension = nullptr); + + /** + * 同步打开文件 + * @param path 文件路径 + * @param mode 模式 + * @return 文件指针 + */ + FILE* openFileSync(const char* path, const char* mode = "rb"); + + /** + * 同步关闭文件 + * @param file 文件指针 + * @return 是否成功 + */ + bool closeFileSync(FILE* file); + + // 异步文件操作 + /** + * 异步写入文件 + * @param path 文件路径 + * @param data 数据 + * @param callback 回调函数 + */ + void asyncWriteFile(const char* path, const char* data, WriteCallback callback = nullptr); + /** + * 异步读取文件 + * @param path 文件路径 + * @param callback 回调函数 + */ + void asyncReadFile(const char* path, ReadCallback callback = nullptr); + /** + * 异步列出目录下的文件 + * @param directory 目录路径 + * @param extension 文件扩展名 + * @param callback 回调函数 + */ + void asyncListFiles(const char* directory, const char* extension, ListCallback callback = nullptr); + /** + * 异步打开文件 + * @param path 文件路径 + * @param mode 模式 + * @param callback 回调函数 + */ + void asyncOpenFile(const char* path, const char* mode, OpenCallback callback = nullptr); + + /** + * 类似Linux的ls命令,列出目录内容 + * @param path 目录路径 + * @param recursive 是否递归列出子目录 + * @param showDetails 是否显示详细信息(大小、类型等) + * @return 格式化后的目录列表字符串 + */ + std::string lsCommand(const char* path, bool recursive = false, bool showDetails = false); + + /** + * 类似Linux的cat命令,显示文件内容 + * @param path 文件路径 + * @param lineNumbers 是否显示行号 + * @return 文件内容字符串 + */ + std::string catCommand(const char* path, bool lineNumbers = false); + + /** + * 类似Linux的pwd命令,获取当前工作目录 + * @return 当前工作目录 + */ + std::string pwdCommand(); + + /** + * 类似Linux的cd命令,改变当前工作目录 + * @param path 目标目录路径 + * @return 是否成功 + */ + bool cdCommand(const char* path); + + /** + * 类似Linux的mkdir命令,创建目录 + * @param path 目录路径 + * @return 是否成功 + */ + bool mkdirCommand(const char* path); + + /** + * 类似Linux的rm命令,删除文件或目录 + * @param path 文件或目录路径 + * @param recursive 是否递归删除目录 + * @return 是否成功 + */ + bool rmCommand(const char* path, bool recursive = false); + + /** + * 类似Linux的stat命令,获取文件/目录信息 + * @param path 文件或目录路径 + * @return 文件信息字符串 + */ + std::string statCommand(const char* path); + + // 获取SD卡和Flash信息 + uint32_t getSDCardSize() const { return SDCard_Size; } + uint32_t getFlashSize() const { return Flash_Size; } + +private: + explicit SDFileManager(); // 私有构造函数,确保单例 + ~SDFileManager(); + + SDFileManager(SDFileManager const&) = delete; // 禁止拷贝构造 + SDFileManager& operator=(SDFileManager const&) = delete; // 禁止赋值构造 + + // 初始化SD卡 + void init(); + + + // 辅助方法 + std::string getFileInfoString(const char* path, const struct stat* st); + std::string getFileTypeString(mode_t mode); + std::string formatFileSize(size_t size); + bool removeDirectoryRecursive(const char* path); + + // 线程配置 + ThreadConfig getThreadConfig(const char* operation); + +private: + static SDFileManager* SDFileInstance; // 单例实例 + static std::mutex instance_mutex; // 实例互斥锁 + std::mutex file_operation_mutex; // 文件操作互斥锁 + bool is_initialized; // SD卡初始化状态 + + std::string current_directory; // 当前工作目录 +}; \ No newline at end of file diff --git a/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h b/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h index 68b6fad..1f3bf02 100644 --- a/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h +++ b/Bionic_Core/ToolsClass/ThreadManager/ThreadManager.h @@ -124,9 +124,11 @@ public: if (extra) { ss << extra; } - ss << "Core id: " << xPortGetCoreID() - << ", prio: " << uxTaskPriorityGet(nullptr) - << ", minimum free stack: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes."; + ss + << ", 任务名: " << pcTaskGetName(nullptr) + << "核心 id: " << xPortGetCoreID() + << ", 优先级: " << uxTaskPriorityGet(nullptr) + << ", 栈剩余空间: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes."; ESP_LOGI(pcTaskGetName(nullptr), "%s", ss.str().c_str()); } diff --git a/Bionic_Core/ToolsClass/WifiConnectors/WifiConnectors.cpp b/Bionic_Core/ToolsClass/WifiConnectors/WifiConnectors.cpp index 39e4a87..082a343 100644 --- a/Bionic_Core/ToolsClass/WifiConnectors/WifiConnectors.cpp +++ b/Bionic_Core/ToolsClass/WifiConnectors/WifiConnectors.cpp @@ -68,7 +68,5 @@ bool WifiConnectors::isWifiConnect() { void WifiConnectors::log() { ESP_LOGI("WifiConnectors", "WifiConnectors log"); - - WIFI_Scan(); } diff --git a/Bionic_Core/ToolsClass/WifiConnectors/WifiConnectors.h b/Bionic_Core/ToolsClass/WifiConnectors/WifiConnectors.h index 24aeb33..55d29ef 100644 --- a/Bionic_Core/ToolsClass/WifiConnectors/WifiConnectors.h +++ b/Bionic_Core/ToolsClass/WifiConnectors/WifiConnectors.h @@ -5,6 +5,8 @@ #include #include + + class WifiConnectors{ // 显然,Wifi连接必须是单例的,否则必然出现冲突 diff --git a/Lib/Display/LCD_Driver/ST77916.c b/Lib/Display/LCD_Driver/ST77916.c index 2e3d608..9305da0 100644 --- a/Lib/Display/LCD_Driver/ST77916.c +++ b/Lib/Display/LCD_Driver/ST77916.c @@ -1,5 +1,5 @@ #include "ST77916.h" - +#include "esp_lcd_panel_io.h" #define LCD_OPCODE_WRITE_CMD (0x02ULL) #define LCD_OPCODE_READ_CMD (0x0BULL) diff --git a/Lib/Display/LCD_Driver/ST77916.h b/Lib/Display/LCD_Driver/ST77916.h index feaac4d..0407c49 100644 --- a/Lib/Display/LCD_Driver/ST77916.h +++ b/Lib/Display/LCD_Driver/ST77916.h @@ -14,7 +14,6 @@ extern "C" { #include "driver/gpio.h" #include "driver/spi_master.h" #include "esp_timer.h" -#include "esp_lcd_panel_io.h" #include "esp_lcd_panel_io_interface.h" #include "esp_intr_alloc.h" #include "esp_lcd_panel_ops.h" diff --git a/Lib/Display/LVGL_Driver/LVGL_Driver.c b/Lib/Display/LVGL_Driver/LVGL_Driver.c index bfa90a7..d7caa83 100644 --- a/Lib/Display/LVGL_Driver/LVGL_Driver.c +++ b/Lib/Display/LVGL_Driver/LVGL_Driver.c @@ -1,5 +1,5 @@ #include "LVGL_Driver.h" - +#include "ST77916.h" static const char *TAG_LVGL = "LVGL"; diff --git a/Lib/Display/LVGL_Driver/LVGL_Driver.h b/Lib/Display/LVGL_Driver/LVGL_Driver.h index 25913e8..f09d8bd 100644 --- a/Lib/Display/LVGL_Driver/LVGL_Driver.h +++ b/Lib/Display/LVGL_Driver/LVGL_Driver.h @@ -13,7 +13,7 @@ extern "C" { #include "lvgl.h" #include "demos/lv_demos.h" -#include "ST77916.h" + #define LVGL_BUF_LEN (EXAMPLE_LCD_WIDTH * EXAMPLE_LCD_HEIGHT / 20) #define EXAMPLE_LVGL_TICK_PERIOD_MS 2 diff --git a/Lib/Display/LVGL_UI/LVGL_Example.c b/Lib/Display/LVGL_UI/LVGL_Example.c index 934db1c..deb6aea 100644 --- a/Lib/Display/LVGL_UI/LVGL_Example.c +++ b/Lib/Display/LVGL_UI/LVGL_Example.c @@ -1,5 +1,6 @@ #include "LVGL_Example.h" #include "LVGL_Music.h" +#include "ST77916.h" #include // #include // #include diff --git a/Lib/Display/Touch_Driver/CST816.c b/Lib/Display/Touch_Driver/CST816.c index 01c2efc..d373481 100644 --- a/Lib/Display/Touch_Driver/CST816.c +++ b/Lib/Display/Touch_Driver/CST816.c @@ -1,5 +1,5 @@ #include "CST816.h" - +#include "esp_lcd_panel_io.h" #define POINT_NUM_MAX (1) diff --git a/Lib/Display/Touch_Driver/CST816.h b/Lib/Display/Touch_Driver/CST816.h index b6f4d79..efb28e7 100644 --- a/Lib/Display/Touch_Driver/CST816.h +++ b/Lib/Display/Touch_Driver/CST816.h @@ -21,7 +21,7 @@ extern "C" { #include "esp_err.h" #include "esp_log.h" #include "esp_check.h" -#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_io_interface.h" #include "esp_lcd_touch.h" #include "TCA9554PWR.h" diff --git a/Lib/Display/Touch_Driver/esp_lcd_touch/esp_lcd_touch.h b/Lib/Display/Touch_Driver/esp_lcd_touch/esp_lcd_touch.h index 4549ac9..d4ec5fe 100644 --- a/Lib/Display/Touch_Driver/esp_lcd_touch/esp_lcd_touch.h +++ b/Lib/Display/Touch_Driver/esp_lcd_touch/esp_lcd_touch.h @@ -15,7 +15,7 @@ #include "sdkconfig.h" #include "esp_err.h" #include "driver/gpio.h" -#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_io_interface.h" // Misaki 2025.9 fixed the bus is #include "esp_lcd_panel_io.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" diff --git a/Lib/OTA_Driver/ota_ws.c b/Lib/OTA_Driver/ota_ws.c index 01ac4dc..4b528b3 100644 --- a/Lib/OTA_Driver/ota_ws.c +++ b/Lib/OTA_Driver/ota_ws.c @@ -25,7 +25,9 @@ static void ota_start(const char *url) .url = url, .keep_alive_enable = true, }; - esp_https_ota_config_t ota_cfg = { .http_config = &config }; + esp_https_ota_config_t ota_cfg = { + .http_config = &config + }; ESP_LOGI(TAG, "开始下载固件..."); esp_err_t ret = esp_https_ota(&ota_cfg); if (ret == ESP_OK) { diff --git a/Lib/Wireless/Wireless.c b/Lib/Wireless/Wireless.c index e34b2cf..c74c865 100644 --- a/Lib/Wireless/Wireless.c +++ b/Lib/Wireless/Wireless.c @@ -11,7 +11,7 @@ wifi_ap_info_t *wifi_ap_list = NULL; uint16_t wifi_ap_count = 0; ble_device_info_t *ble_device_list = NULL; -void Wireless_Init(void) +esp_err_t Wireless_Init(void) { // Initialize NVS. esp_err_t ret = nvs_flash_init(); // 初始化Flash @@ -20,6 +20,7 @@ void Wireless_Init(void) ret = nvs_flash_init(); // 重新初始化Flash } ESP_ERROR_CHECK( ret ); // 检查错误 + // WiFi 手动调用Wifi初始化 // xTaskCreatePinnedToCore( // WIFI_Init, @@ -38,9 +39,10 @@ void Wireless_Init(void) // 2, // NULL, // 0); + return ret; } -void WIFI_Init(void *arg) +esp_err_t WIFI_Init(void *arg) { esp_netif_init(); // 初始化网络接口 esp_event_loop_create_default(); // 创建事件循环 @@ -48,7 +50,8 @@ void WIFI_Init(void *arg) wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); // 创建WiFi初始化配置(使用默认配置) esp_wifi_init(&cfg); // 初始化WiFi esp_wifi_set_mode(WIFI_MODE_STA); // 设置WiFi模式为Station - esp_wifi_start(); // 启动WiFi + esp_err_t ret = esp_wifi_start(); // 启动WiFi + return ret; } diff --git a/Lib/Wireless/Wireless.h b/Lib/Wireless/Wireless.h index 88aa518..e43aca3 100644 --- a/Lib/Wireless/Wireless.h +++ b/Lib/Wireless/Wireless.h @@ -35,8 +35,8 @@ extern uint16_t BLE_NUM; extern uint16_t WIFI_NUM; extern bool Scan_finish; -void Wireless_Init(void); -void WIFI_Init(void *arg); +esp_err_t Wireless_Init(void); +esp_err_t WIFI_Init(void *arg); uint16_t WIFI_Scan(void); void BLE_Init(void *arg); uint16_t BLE_Scan(void); diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 1766f26..726fe09 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -29,9 +29,12 @@ idf_component_register(SRCS "Bionic_sphere.c" "../Lib/OTA_Driver/ota_ws.c" # 业务代码(使用Cpp编写) "../Bionic_Core/PetBaseClass/PetBaseClass.cpp" # 宠物基类库 + "../Bionic_Core/PetBaseClass/PetInterface.cpp" # 宠物接口层 "../Bionic_Core/OTAClass/OTAClass.cpp" # OTA类库 "../Bionic_Core/CommClass/CommClass.cpp" # 通信类库 "../Bionic_Core/ToolsClass/ToolsClass.cpp" # 工具类库 + "../Bionic_Core/ToolsClass/LVGL_Render/LVGLRender.cpp" # LVGL渲染类库 + "../Bionic_Core/ToolsClass/SDFileManager/SDFileManager.cpp" # SD文件管理类库 "../Bionic_Core/ToolsClass/WifiConnectors/WifiConnectors.cpp" # WIFI连接类库 "../Bionic_Core/ToolsClass/ThreadManager/ThreadManager.cpp" # 线程管理类库 "../Bionic_Core/CppHandle/CppHandle.cpp" # C++&C兼容库 @@ -64,6 +67,8 @@ idf_component_register(SRCS "Bionic_sphere.c" "../Bionic_Core/OTAClass" "../Bionic_Core/CommClass" "../Bionic_Core/ToolsClass" + "../Bionic_Core/ToolsClass/LVGL_Render" + "../Bionic_Core/ToolsClass/SDFileManager" "../Bionic_Core/ToolsClass/WifiConnectors" "../Bionic_Core/ToolsClass/ThreadManager" "../Bionic_Core/CppHandle" diff --git a/项目开发日志.md b/项目开发日志.md index d051ff3..39c06b3 100644 --- a/项目开发日志.md +++ b/项目开发日志.md @@ -195,3 +195,14 @@ - [x] 2. 封装了一个模板线程类,支持创建来自单例类的成员函数线程,普通类的线程,普通函数线程 - [x] 3. 封装了一个Wifi模块类,支持Wifi的各种基本配置 + +#### Day12 2025.9.9(前三天在打数学建模国赛) +##### 主要目标:完成具体业务开发&各种优化 +实际完成任务: +- [x] 1. 完整封装并拓展了SD卡文件管理类,支持基本文件管理功能 + +- [x] 2. 简单封装了LVGL渲染类,已经封装好了gif渲染功能 + +- [x] 3. 修复了硬件厂商提供的驱动的Bug + +- [x] 4. 初步定义了宠物基类的抽象信息