1. 完成了语音识别的C++业务层封装,测试通过
2. 试着测试了一下LVGL_GIF渲染+音乐播放+语音识别的组合简单优化后,
发现lvgl渲染略显卡顿,语音识别有缓冲区空警告,不过无伤大雅,还需要进一步深度优化。
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/22.
|
||||
//
|
||||
|
||||
#include "sys_conf_singleton.h"
|
||||
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/22.
|
||||
//
|
||||
#pragma once
|
||||
#include "cpp_json.h"
|
||||
#include <esp_vfs_fat.h>
|
||||
#include <wear_levelling.h>
|
||||
#include <esp_log.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
|
||||
static constexpr char kSysConfPartName[] = "sys_conf";
|
||||
|
||||
template <const char* PartitionLabel>
|
||||
class SysConfJson {
|
||||
public:
|
||||
static SysConfJson& instance() {
|
||||
static SysConfJson inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
/* 把 sn 持久化到 <mount>/sn.json,成功返回 true */
|
||||
bool saveSN(const std::string& sn)
|
||||
{
|
||||
cppjson::Json doc = cppjson::Json::object();
|
||||
doc.set("sn", cppjson::Json(sn)); // {"sn":"xxxxxx"}
|
||||
return write("sn", doc); // 实际文件 = <mount>/sn.json
|
||||
}
|
||||
|
||||
/* 读取 sn,如果文件/字段不存在返回空串(绝不抛异常) */
|
||||
std::string loadSN()
|
||||
{
|
||||
cppjson::Json j = read("sn"); // 读 <mount>/sn.json
|
||||
if (!j.isObject()) return ""; // 文件不存在或解析失败
|
||||
cppjson::Json snNode = j["sn"];
|
||||
return snNode.isString() ? snNode.asString() : "";
|
||||
}
|
||||
|
||||
/* 直接覆盖写,无 tmp */
|
||||
bool write(const char* key, const cppjson::Json& j) {
|
||||
const std::string path = buildPath(key);
|
||||
const std::string txt = j.dump();
|
||||
return writeRaw(path, txt);
|
||||
}
|
||||
|
||||
/* 读 */
|
||||
cppjson::Json read(const char* key) const {
|
||||
std::string content;
|
||||
if (!readRaw(key, content)) return {};
|
||||
try {
|
||||
return cppjson::Json::parse(content);
|
||||
} catch (...) {
|
||||
ESP_LOGE(TAG, "parse %s fail", key);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/* 删文件 */
|
||||
bool remove(const char* key) const {
|
||||
const std::string path = buildPath(key);
|
||||
return ::unlink(path.c_str()) == 0;
|
||||
}
|
||||
|
||||
/* 列文件 */
|
||||
[[nodiscard]] std::vector<std::string> ls() const {
|
||||
std::vector<std::string> names;
|
||||
DIR* dir = ::opendir(kMount);
|
||||
if (!dir) return names;
|
||||
struct dirent* entry;
|
||||
while ((entry = ::readdir(dir)) != nullptr) {
|
||||
if (entry->d_name[0] == '.') continue;
|
||||
names.emplace_back(entry->d_name);
|
||||
}
|
||||
::closedir(dir);
|
||||
return names;
|
||||
}
|
||||
|
||||
/* 格式化 */
|
||||
[[nodiscard]] bool format() const {
|
||||
ESP_LOGW(TAG, "format <%s>", PartitionLabel);
|
||||
return esp_vfs_fat_spiflash_format_rw_wl(kMount, PartitionLabel) == ESP_OK;
|
||||
}
|
||||
|
||||
/* 禁止拷贝 */
|
||||
SysConfJson(const SysConfJson&) = delete;
|
||||
SysConfJson& operator=(const SysConfJson&) = delete;
|
||||
|
||||
private:
|
||||
constexpr static const char* TAG = "SysConfJson";
|
||||
constexpr static const char* kMount = "/sys_conf";
|
||||
wl_handle_t wl_ = WL_INVALID_HANDLE;
|
||||
bool mounted_ = false;
|
||||
static constexpr const char* kFileName = "sys_conf.json";
|
||||
std::mutex mtx_;
|
||||
|
||||
SysConfJson() { mount(); }
|
||||
~SysConfJson() { unmount(); }
|
||||
|
||||
void mount() {
|
||||
esp_vfs_fat_mount_config_t cfg = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 8,
|
||||
.allocation_unit_size = CONFIG_WL_SECTOR_SIZE
|
||||
};
|
||||
esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl(kMount, PartitionLabel, &cfg, &wl_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "mount fail <%s>", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
mounted_ = true;
|
||||
ESP_LOGI(TAG, "FAT mounted at %s", kMount);
|
||||
}
|
||||
|
||||
void unmount() {
|
||||
if (!mounted_) return;
|
||||
esp_vfs_fat_spiflash_unmount_rw_wl(kMount, wl_);
|
||||
wl_ = WL_INVALID_HANDLE;
|
||||
mounted_ = false;
|
||||
}
|
||||
|
||||
bool writeRaw(const std::string& path, const std::string& txt) {
|
||||
FILE* f = std::fopen(path.c_str(), "wb");
|
||||
if (!f) return false;
|
||||
bool ok = std::fwrite(txt.data(), 1, txt.size(), f) == txt.size();
|
||||
std::fclose(f);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool readRaw(const char* key, std::string& out) const {
|
||||
const std::string path = buildPath(key);
|
||||
FILE* f = std::fopen(path.c_str(), "rb");
|
||||
if (!f) return false;
|
||||
std::fseek(f, 0, SEEK_END);
|
||||
size_t sz = std::ftell(f);
|
||||
std::fseek(f, 0, SEEK_SET);
|
||||
out.resize(sz);
|
||||
bool ok = std::fread(&out[0], 1, sz, f) == sz;
|
||||
std::fclose(f);
|
||||
return ok;
|
||||
}
|
||||
|
||||
std::string buildPath(const char* key) const {
|
||||
return std::string(kMount) + "/" + key;
|
||||
}
|
||||
};
|
||||
|
||||
#define SYS_CONF_JSON() SysConfJson<kSysConfPartName>::instance()
|
||||
Reference in New Issue
Block a user