a47e20cb64
2. 稍微优化了一下系统配置类 3. 增加了系统版本号,便于区分系统版本,方便OTA 4. 重写OTA的逻辑,完成了Cpp的OTA封装,测试通过
460 lines
16 KiB
C++
460 lines
16 KiB
C++
//
|
|
// Created by misaki on 2025/9/2.
|
|
//
|
|
#include "CppHandle.h"
|
|
|
|
#include "OTAClass.h"
|
|
#include "PetBaseClass.h"
|
|
#include "PetDao.h"
|
|
#include <iostream>
|
|
|
|
void testPetSystem() {
|
|
std::cout << "Test point1" << std::endl;
|
|
// 创建阶段策略
|
|
auto stageStrategy = std::make_unique<PetStageStrategy>();
|
|
stageStrategy->addStage(PetStageType::PET_STAGE_YOUNG, "models/young.obj");
|
|
stageStrategy->addStage(PetStageType::PET_STAGE_ADULT, "models/adult.obj");
|
|
stageStrategy->addStage(PetStageType::PET_STAGE_OLD, "models/old.obj");
|
|
stageStrategy->addStageAudio(PetStageType::PET_STAGE_YOUNG, "audio/young.mp3");
|
|
stageStrategy->addStageAudio(PetStageType::PET_STAGE_ADULT, "audio/adult.mp3");
|
|
stageStrategy->addStageAudio(PetStageType::PET_STAGE_OLD, "audio/old.mp3");
|
|
|
|
// 创建动作策略
|
|
auto actionStrategy = std::make_unique<PetActionStrategy>();
|
|
actionStrategy->addAction(PetActionType::PET_ACTION_EAT, "models/eat.obj");
|
|
actionStrategy->addAction(PetActionType::PET_ACTION_HAPPY, "models/happy.obj");
|
|
actionStrategy->addAction(PetActionType::PET_ACTION_SLEEP, "models/sleep.obj");
|
|
actionStrategy->addAction(PetActionType::PET_ACTION_ANGRY, "models/angry.obj");
|
|
actionStrategy->addAction(PetActionType::PET_ACTION_SAD, "models/sad.obj");
|
|
actionStrategy->addAction(PetActionType::PET_ACTION_EVOLVE, "models/evolve.obj");
|
|
actionStrategy->addAction(PetActionType::PET_ACTION_TOUCH, "models/touch.obj");
|
|
|
|
actionStrategy->addActionAudio(PetActionType::PET_ACTION_EAT, "audio/eat.mp3");
|
|
actionStrategy->addActionAudio(PetActionType::PET_ACTION_HAPPY, "audio/happy.mp3");
|
|
actionStrategy->addActionAudio(PetActionType::PET_ACTION_SLEEP, "audio/sleep.mp3");
|
|
std::cout << "Test point2" << std::endl;
|
|
|
|
// 创建宠物信息
|
|
PetBaseInfo info;
|
|
info.pet_name = "芝士雪豹";
|
|
info.pet_hp = 100;
|
|
info.pet_density = 50;
|
|
info.pet_identity = "我是顶真";
|
|
|
|
// 创建宠物
|
|
auto pet = std::make_shared<PetBase>(info, std::move(stageStrategy), std::move(actionStrategy));
|
|
std::cout << "Test point3" << std::endl;
|
|
|
|
// 创建音频观察者
|
|
auto audioStrategy = std::make_shared<PetAudioStrategy>();
|
|
audioStrategy->setAudioCallback([](const std::string& audioPath) {
|
|
std::cout << "Playing audio: " << audioPath << std::endl;
|
|
});
|
|
audioStrategy->subscribe(pet);
|
|
std::cout << "Test point4" << std::endl;
|
|
|
|
// 创建渲染器观察者
|
|
auto rendererStrategy = std::make_shared<PetRendererStrategy>(
|
|
pet->getStageStrategy(),
|
|
pet->getActionStrategy()
|
|
);
|
|
rendererStrategy->setRenderCallback([](const std::string& modelPath) {
|
|
std::cout << "Rendering model: " << modelPath << std::endl;
|
|
});
|
|
rendererStrategy->subscribe(pet);
|
|
std::cout << "Test point5" << std::endl;
|
|
|
|
// 执行一些动作
|
|
std::cout << "=== Testing basic actions ===" << std::endl;
|
|
pet->feed();
|
|
pet->play();
|
|
pet->touch();
|
|
std::cout << "Test point6" << std::endl;
|
|
|
|
// 检查当前状态
|
|
std::cout << "Current HP: " << pet->getPetInfo().pet_hp << std::endl;
|
|
std::cout << "Current density: " << pet->getPetInfo().pet_density << std::endl;
|
|
std::cout << "Current stage: " << static_cast<int>(pet->getCurrentStage()) << std::endl;
|
|
std::cout << "Current action: " << static_cast<int>(pet->getCurrentAction()) << std::endl;
|
|
std::cout << "Test point7" << std::endl;
|
|
|
|
// 测试进化
|
|
std::cout << "\n=== Testing evolution ===" << std::endl;
|
|
// 直接修改亲密度来测试进化
|
|
PetBaseInfo newInfo = pet->getPetInfo();
|
|
newInfo.pet_density = 100; // 达到进化条件
|
|
pet->setPetInfo(newInfo);
|
|
std::cout << "Test point8" << std::endl;
|
|
|
|
if (pet->checkEvolution()) {
|
|
std::cout << "Evolution successful!" << std::endl;
|
|
} else {
|
|
std::cout << "Evolution failed!" << std::endl;
|
|
}
|
|
|
|
// 进一步增加亲密度到150,尝试再次进化
|
|
newInfo.pet_density = 150;
|
|
pet->setPetInfo(newInfo);
|
|
if (pet->checkEvolution()) {
|
|
std::cout << "Second evolution successful!" << std::endl;
|
|
} else {
|
|
std::cout << "Second evolution failed!" << std::endl;
|
|
}
|
|
|
|
std::cout << "Final stage: " << static_cast<int>(pet->getCurrentStage()) << std::endl;
|
|
|
|
PetDAO petDAO(SDFileManager::getInstance());
|
|
petDAO.savePet(pet, "my_pet.json");
|
|
// 列出所有宠物文件
|
|
auto petFiles = petDAO.listPetFiles();
|
|
for (const auto& file : petFiles) {
|
|
std::cout << "Pet file: " << file << std::endl;
|
|
}
|
|
std::cout << SDFileManager::getInstance()->catCommand("/sdcard/pet_data/my_pet.json") << std::endl;
|
|
}
|
|
|
|
|
|
#include "SpeechRecognizer.h"
|
|
#include <nvs.h>
|
|
#include <nvs_flash.h>
|
|
// 命令回调函数
|
|
void commandCallback(int command_id, const std::string& phrase, float probability) {
|
|
ESP_LOGI("Example", "Received command: ID=%d, Phrase='%s', Probability=%.2f",
|
|
command_id, phrase.c_str(), probability);
|
|
|
|
// 根据命令执行相应操作
|
|
switch (command_id) {
|
|
case 0:
|
|
ESP_LOGI("Example", "执行命令0");
|
|
// 执行命令0的操作
|
|
break;
|
|
case 1:
|
|
ESP_LOGI("Example", "执行命令1");
|
|
// 执行命令1的操作
|
|
break;
|
|
case 2:
|
|
ESP_LOGI("Example", "执行命令2");
|
|
// 执行命令2的操作
|
|
break;
|
|
default:
|
|
ESP_LOGI("Example", "未知的命令ID: %d", command_id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 状态回调函数
|
|
void stateCallback(const std::string& state) {
|
|
ESP_LOGI("Example", "状态改变到: %s", state.c_str());
|
|
}
|
|
#include "SDFileManager.h"
|
|
void testMIC() {
|
|
// 初始化NVS
|
|
esp_err_t ret = nvs_flash_init();
|
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
|
ret = nvs_flash_init();
|
|
}
|
|
ESP_ERROR_CHECK(ret);
|
|
|
|
// 初始化SD卡管理器
|
|
SDFileManager::getInstance()->tryInitSDCard();
|
|
|
|
// 获取SpeechRecognizer实例
|
|
SpeechRecognizer* recognizer = SpeechRecognizer::getInstance();
|
|
|
|
// 配置识别器
|
|
SpeechRecognizerConfig config;
|
|
config.enable_vad = true;
|
|
config.vad_mode = VAD_MODE_3; // 更高的VAD灵敏度
|
|
config.model_path = "/sdcard/srmodels";
|
|
|
|
// 初始化
|
|
if (!recognizer->init(config)) {
|
|
ESP_LOGE("main", "Failed to initialize speech recognizer");
|
|
return;
|
|
}
|
|
|
|
// 添加自定义命令
|
|
std::vector<std::pair<int, std::string>> commands = {
|
|
{0, "kai deng"}, // 开灯
|
|
{1, "guan deng"}, // 关灯
|
|
{2, "ti gao liang du"}, // 提高亮度
|
|
{3, "jiang di liang du"}, // 降低亮度
|
|
{4, "bo fang yin yue"}, // 播放音乐
|
|
{5, "ting zhi bo fang"} // 停止播放
|
|
};
|
|
|
|
if (!recognizer->addCommands(commands)) {
|
|
ESP_LOGE("main", "Failed to add some commands");
|
|
}
|
|
|
|
// 注册回调函数
|
|
recognizer->registerCommandCallback(commandCallback);
|
|
recognizer->registerStateCallback(stateCallback);
|
|
|
|
// 开始识别
|
|
if (!recognizer->start()) {
|
|
ESP_LOGE("main", "Failed to start speech recognition");
|
|
return;
|
|
}
|
|
|
|
ESP_LOGI("main", "Speech recognition system started successfully");
|
|
|
|
}
|
|
|
|
|
|
#include "ToolsClass.h"
|
|
#include "WifiConnectors.h"
|
|
#include "CommClass.h"
|
|
#include "sys_conf_singleton.h"
|
|
#include "HttpOtaUpdater.h"
|
|
using namespace std::chrono;
|
|
const auto sleep_time = seconds{
|
|
5
|
|
};
|
|
// OTA相关
|
|
HttpOtaUpdater otaUpdater;
|
|
void setupOtaCallbacks() {
|
|
// 设置进度回调
|
|
otaUpdater.setProgressCallback([](int progress, int total) {
|
|
ESP_LOGI("OTA", "Progress: %d%%", progress);
|
|
});
|
|
|
|
// 设置状态回调
|
|
otaUpdater.setStateCallback([](HttpOtaUpdater::OtaState state, const std::string& message) {
|
|
const char* stateNames[] = {
|
|
"IDLE", "CONNECTING", "DOWNLOADING", "VERIFYING", "SUCCESS", "FAILED"
|
|
};
|
|
ESP_LOGI("OTA", "State: %s - %s", stateNames[static_cast<int>(state)], message.c_str());
|
|
});
|
|
|
|
// 设置完成回调
|
|
otaUpdater.setFinishCallback([](bool success, const std::string& message) {
|
|
if (success) {
|
|
ESP_LOGI("OTA", "Completed successfully: %s", message.c_str());
|
|
} else {
|
|
ESP_LOGE("OTA", "Failed: %s", message.c_str());
|
|
}
|
|
});
|
|
|
|
// 如果需要HTTPS,可以在这里设置证书(保留供后期使用)
|
|
// otaUpdater.setCACert(my_ca_cert_pem);
|
|
// otaUpdater.skipCertCommonNameCheck(true); // 仅用于测试
|
|
}
|
|
|
|
// JSON 数据回调函数
|
|
/** 交互所使用的json内容
|
|
{
|
|
"type": "xxx", // 消息类型
|
|
"xxx":"xxx", // 其他数据
|
|
......
|
|
}
|
|
*/
|
|
void onJsonData(const cppjson::Json& json)
|
|
{
|
|
// 打印收到的 JSON
|
|
ESP_LOGI("JSON_CALLBACK", "收到JSON数据: %s", json.dump().c_str());
|
|
|
|
// 解析消息类型
|
|
const cppjson::Json& type = json["type"];
|
|
if (!type.isString()) return;
|
|
|
|
std::string typeStr = type.asString();
|
|
|
|
if (typeStr == "greeting") {
|
|
ESP_LOGI("JSON_CALLBACK", "收到服务器问候消息");
|
|
} else if (typeStr == "heartbeat") {
|
|
ESP_LOGI("JSON_CALLBACK", "收到心跳响应");
|
|
} else if (typeStr == "response") {
|
|
ESP_LOGI("JSON_CALLBACK", "收到服务器响应");
|
|
} else if (typeStr == "echo") {
|
|
ESP_LOGI("JSON_CALLBACK", "收到回显消息");
|
|
} else if (typeStr == "broadcast") {
|
|
ESP_LOGI("JSON_CALLBACK", "收到广播消息");
|
|
} else if (typeStr == "ota") {
|
|
ESP_LOGI("JSON_CALLBACK", "收到OTA消息");
|
|
// 进一步处理OTA消息
|
|
// 获取OTA中的版本信息
|
|
const cppjson::Json& version = json["version"];
|
|
if (!version.isString()) return;
|
|
std::string versionStr = version.asString();
|
|
// 获取OTA中的HTTP URL
|
|
const cppjson::Json& url = json["url"];
|
|
if (!url.isString()) return;
|
|
std::string urlStr = url.asString();
|
|
// 告诉服务端,升级开始
|
|
cppjson::Json response = cppjson::Json::object();
|
|
response.set("type", cppjson::Json("ota_start"));
|
|
WebSocketManager::getInstance()->sendJson(response);
|
|
otaUpdater.start(urlStr);
|
|
}
|
|
|
|
}
|
|
|
|
// WebSocket事件回调函数
|
|
void onWebSocketEvent(WebSocketEvent event, const std::string& message) {
|
|
switch (event) {
|
|
case WebSocketEvent::CONNECTED:
|
|
ESP_LOGI("EVENT_CALLBACK", "WebSocket已连接: %s", message.c_str());
|
|
break;
|
|
case WebSocketEvent::DISCONNECTED:
|
|
ESP_LOGI("EVENT_CALLBACK", "WebSocket已断开: %s", message.c_str());
|
|
break;
|
|
case WebSocketEvent::DATA_RECEIVED:
|
|
ESP_LOGI("EVENT_CALLBACK", "收到原始数据: %s", message.c_str());
|
|
break;
|
|
case WebSocketEvent::ERROR:
|
|
ESP_LOGE("EVENT_CALLBACK", "WebSocket错误: %s", message.c_str());
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 发送状态信息函数
|
|
void sendStatus()
|
|
{
|
|
cppjson::Json status = cppjson::Json::object();
|
|
status.set("type", cppjson::Json("status"));
|
|
|
|
cppjson::Json data = cppjson::Json::object();
|
|
data.set("free_heap", cppjson::Json(esp_get_free_heap_size()))
|
|
.set("uptime", cppjson::Json(xTaskGetTickCount() * portTICK_PERIOD_MS / 1000));
|
|
|
|
status.set("data", data); // 嵌套对象
|
|
|
|
if (WebSocketManager::getInstance()->sendJson(status)) {
|
|
ESP_LOGI("SEND", "已发送状态信息");
|
|
} else {
|
|
ESP_LOGE("SEND", "发送状态信息失败");
|
|
}
|
|
}
|
|
|
|
// 发送问候消息函数
|
|
void sendGreeting()
|
|
{
|
|
cppjson::Json greeting = cppjson::Json::object();
|
|
greeting.set("type", cppjson::Json("greeting"))
|
|
.set("message", cppjson::Json("Hello from ESP32-S3"))
|
|
.set("timestamp", cppjson::Json(xTaskGetTickCount() * portTICK_PERIOD_MS / 1000));
|
|
|
|
if (WebSocketManager::getInstance()->sendJson(greeting)) {
|
|
ESP_LOGI("SEND", "已发送问候消息");
|
|
} else {
|
|
ESP_LOGE("SEND", "发送问候消息失败");
|
|
}
|
|
}
|
|
void websocket_task() {
|
|
TickType_t lastStatusTime = 0;
|
|
TickType_t lastHeartbeatTime = 0;
|
|
TickType_t lastGreetingTime = 0;
|
|
|
|
while (true) {
|
|
TickType_t currentTime = xTaskGetTickCount();
|
|
|
|
// 检查连接状态
|
|
if (!WebSocketManager::getInstance()->isConnected()) {
|
|
ESP_LOGI("APP_TASK", "WebSocket未连接,尝试重新连接...");
|
|
|
|
// 确保WiFi已连接
|
|
if (!WifiConnectors::getInstance()->isWifiConnect()) {
|
|
ESP_LOGI("APP_TASK", "WiFi未连接,等待WiFi连接...");
|
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
|
continue;
|
|
}
|
|
|
|
if (WebSocketManager::getInstance()->connect()) {
|
|
ESP_LOGI("APP_TASK", "重新连接成功");
|
|
} else {
|
|
ESP_LOGI("APP_TASK", "重新连接失败");
|
|
}
|
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
|
continue;
|
|
}
|
|
// 每10秒发送状态信息
|
|
if (currentTime - lastStatusTime > (10000 / portTICK_PERIOD_MS)) {
|
|
sendStatus();
|
|
lastStatusTime = currentTime;
|
|
}
|
|
// 每60秒发送问候
|
|
if (currentTime - lastGreetingTime > (60000 / portTICK_PERIOD_MS)) {
|
|
sendGreeting();
|
|
lastGreetingTime = currentTime;
|
|
}
|
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
|
}
|
|
}
|
|
|
|
void createWebSocket() {
|
|
// 等待WiFi连接成功后再连接WebSocket
|
|
ESP_LOGI("APP_TASK", "等待WiFi连接...");
|
|
while (!WifiConnectors::getInstance()->isWifiConnect()) {
|
|
ESP_LOGI("APP_TASK", "WiFi未连接,等待中...");
|
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
}
|
|
// 保存SN
|
|
SysConfJson::getInstance()->saveSN(ToolsClass::GenerateSN(ToolsClass::getChipMAC(), ToolsClass::getChipSerialNumber()));
|
|
// 读取SN
|
|
std::string sn = SysConfJson::getInstance()->loadSN();
|
|
ESP_LOGI("conf", "loaded sn = %s", sn.c_str());
|
|
|
|
// 配置WebSocket
|
|
WebSocketConfig config;
|
|
config.uri = "ws://" + std::string("192.168.1.11") + ":" + std::to_string(8080) + "/ws";
|
|
config.auto_reconnect = true; // 自动重连
|
|
config.reconnect_interval = 5000; // 重连间隔(毫秒)
|
|
config.heartbeat_interval = 30000; // 心跳间隔(毫秒)
|
|
config.max_reconnect_attempts = 10; // 最大重连尝试次数
|
|
// TODO: 此处通信类存在线程重复创建bug,似乎是来自esp-idf的bug,待查证
|
|
// 初始化WebSocket管理器
|
|
if (!WebSocketManager::getInstance()->initialize(config)) {
|
|
ESP_LOGE("APP_TASK", "WebSocket管理器初始化失败");
|
|
vTaskDelete(NULL);
|
|
return;
|
|
}
|
|
|
|
// 设置回调函数
|
|
WebSocketManager::getInstance()->setJsonCallback(onJsonData);
|
|
WebSocketManager::getInstance()->setEventCallback(onWebSocketEvent);
|
|
|
|
// 连接WebSocket服务器
|
|
ESP_LOGI("APP_TASK", "正在连接WebSocket服务器: %s", config.uri.c_str());
|
|
if (!WebSocketManager::getInstance()->connect()) {
|
|
ESP_LOGE("APP_TASK", "WebSocket连接失败");
|
|
}
|
|
|
|
// 创建WebSocket任务
|
|
ThreadConfig websocket_config;
|
|
websocket_config.core_id = 0;
|
|
websocket_config.inherit_cfg = true;
|
|
websocket_config.name = "websocket_task";
|
|
websocket_config.priority = 5;
|
|
websocket_config.stack_size = 4096;
|
|
std::thread websocket_thread = ThreadManager::createThread(websocket_config, websocket_task);
|
|
websocket_thread.detach();
|
|
}
|
|
|
|
void Cpp_Hand() {
|
|
// testMIC();
|
|
// testPetSystem();
|
|
|
|
ESP_LOGI("CppHandle::Cpp_Hand", "当前固件版本 %s:", ToolsClass::getDeviceVersion().c_str());
|
|
|
|
ESP_LOGI("CppHandle::Cpp_Hand", "当前设备MAC地址 %s:", ToolsClass::getChipMAC().c_str());
|
|
ESP_LOGI("CppHandle::Cpp_Hand", "当前设备固件序列号 %s:", ToolsClass::getChipSerialNumber().c_str());
|
|
|
|
// 连接wifi
|
|
WifiConnectors::getInstance()->connectWifi("Misaki-2.4G", "88888888", 5);
|
|
|
|
// 创建WebSocket
|
|
createWebSocket();
|
|
|
|
// 设置OTA回调
|
|
setupOtaCallbacks();
|
|
|
|
while (true) { // 主线程线程循环
|
|
// ThreadManager::print_sys_memory(); // 打印系统内存使用情况
|
|
// ThreadManager::stats_task(); // 打印任务统计信息
|
|
|
|
std::this_thread::sleep_for(sleep_time); // 休眠5秒
|
|
}
|
|
}
|