a47e20cb64
2. 稍微优化了一下系统配置类 3. 增加了系统版本号,便于区分系统版本,方便OTA 4. 重写OTA的逻辑,完成了Cpp的OTA封装,测试通过
248 lines
7.2 KiB
C++
248 lines
7.2 KiB
C++
//
|
|
// Created by misaki on 2025/9/24.
|
|
//
|
|
#include "HttpOtaUpdater.h"
|
|
#include <esp_http_client.h>
|
|
#include <cstring>
|
|
|
|
HttpOtaUpdater::HttpOtaUpdater()
|
|
: m_currentState(OtaState::IDLE)
|
|
, m_isUpdating(false)
|
|
, m_cert_pem(nullptr)
|
|
, m_skip_cert_common_name_check(false)
|
|
, m_totalSize(0)
|
|
, m_downloadedSize(0) {
|
|
}
|
|
|
|
HttpOtaUpdater::~HttpOtaUpdater() {
|
|
stop();
|
|
}
|
|
|
|
bool HttpOtaUpdater::start(const std::string& url) {
|
|
if (m_isUpdating) {
|
|
ESP_LOGE(TAG, "OTA update is already in progress");
|
|
return false;
|
|
}
|
|
|
|
if (url.empty()) {
|
|
ESP_LOGE(TAG, "URL is empty");
|
|
return false;
|
|
}
|
|
|
|
m_url = url;
|
|
m_errorMessage.clear();
|
|
m_totalSize = 0;
|
|
m_downloadedSize = 0;
|
|
|
|
// 配置线程参数
|
|
ThreadConfig config;
|
|
config.name = "ota_task";
|
|
config.stack_size = 8192;
|
|
config.priority = 6;
|
|
config.core_id = -1; // 不绑定特定核心
|
|
|
|
// 使用ThreadManager创建线程
|
|
m_otaThread = ThreadManager::createMemberThread(config, this, &HttpOtaUpdater::otaTask);
|
|
|
|
if (!m_otaThread.joinable()) {
|
|
ESP_LOGE(TAG, "Failed to create OTA thread");
|
|
updateState(OtaState::FAILED, "Failed to create thread");
|
|
return false;
|
|
}
|
|
|
|
m_isUpdating = true;
|
|
updateState(OtaState::CONNECTING, "Starting OTA update");
|
|
return true;
|
|
}
|
|
|
|
void HttpOtaUpdater::stop() {
|
|
if (m_isUpdating && m_otaThread.joinable()) {
|
|
// 注意:实际OTA过程很难安全中断,这里只是标记状态并分离线程
|
|
m_isUpdating = false;
|
|
m_otaThread.detach(); // 分离线程,让系统自动回收资源
|
|
}
|
|
}
|
|
|
|
bool HttpOtaUpdater::isUpdating() const {
|
|
return m_isUpdating;
|
|
}
|
|
|
|
void HttpOtaUpdater::setProgressCallback(const ProgressCallback &callback) {
|
|
m_progressCallback = callback;
|
|
}
|
|
|
|
void HttpOtaUpdater::setStateCallback(const StateCallback &callback) {
|
|
m_stateCallback = callback;
|
|
}
|
|
|
|
void HttpOtaUpdater::setFinishCallback(const FinishCallback &callback) {
|
|
m_finishCallback = callback;
|
|
}
|
|
|
|
void HttpOtaUpdater::setCACert(const char* cert_pem) {
|
|
m_cert_pem = cert_pem;
|
|
}
|
|
|
|
void HttpOtaUpdater::skipCertCommonNameCheck(bool skip) {
|
|
m_skip_cert_common_name_check = skip;
|
|
}
|
|
|
|
HttpOtaUpdater::OtaState HttpOtaUpdater::getCurrentState() const {
|
|
return m_currentState;
|
|
}
|
|
|
|
std::string HttpOtaUpdater::getErrorMessage() const {
|
|
return m_errorMessage;
|
|
}
|
|
|
|
void HttpOtaUpdater::otaTask() {
|
|
ThreadManager::printThreadInfo("OTA任务启动");
|
|
performOta(m_url);
|
|
}
|
|
|
|
void HttpOtaUpdater::performOta(const std::string& url) {
|
|
ESP_LOGI(TAG, "Starting OTA update from: %s", url.c_str());
|
|
|
|
// 配置HTTP客户端
|
|
esp_http_client_config_t http_config = {};
|
|
http_config.url = url.c_str();
|
|
http_config.event_handler = httpEventHandle;
|
|
http_config.user_data = this;
|
|
http_config.keep_alive_enable = true;
|
|
http_config.timeout_ms = 30000;
|
|
http_config.buffer_size = 2048;
|
|
|
|
// 设置证书(如果提供)- 保留供后期使用
|
|
if (m_cert_pem) {
|
|
http_config.cert_pem = m_cert_pem;
|
|
ESP_LOGI(TAG, "Using custom CA certificate for HTTPS");
|
|
}
|
|
|
|
// 设置证书检查 - 保留供后期使用
|
|
http_config.skip_cert_common_name_check = m_skip_cert_common_name_check;
|
|
if (m_skip_cert_common_name_check) {
|
|
ESP_LOGW(TAG, "Skipping certificate common name check");
|
|
}
|
|
|
|
// 配置HTTPS OTA
|
|
esp_https_ota_config_t ota_config = {};
|
|
ota_config.http_config = &http_config;
|
|
|
|
updateState(OtaState::DOWNLOADING, "Connecting to server");
|
|
|
|
esp_err_t ret = esp_https_ota(&ota_config);
|
|
|
|
if (ret == ESP_OK) {
|
|
ESP_LOGI(TAG, "OTA update successful");
|
|
updateState(OtaState::SUCCESS, "OTA update completed successfully");
|
|
|
|
if (m_finishCallback) {
|
|
m_finishCallback(true, "OTA update completed successfully");
|
|
}
|
|
|
|
// 等待一段时间后重启
|
|
ESP_LOGI(TAG, "Preparing to restart system...");
|
|
vTaskDelay(pdMS_TO_TICKS(2000));
|
|
esp_restart();
|
|
} else {
|
|
m_errorMessage = "OTA failed with error: " + std::string(esp_err_to_name(ret));
|
|
ESP_LOGE(TAG, "%s", m_errorMessage.c_str());
|
|
updateState(OtaState::FAILED, m_errorMessage);
|
|
|
|
if (m_finishCallback) {
|
|
m_finishCallback(false, m_errorMessage);
|
|
}
|
|
}
|
|
|
|
m_isUpdating = false;
|
|
}
|
|
|
|
void HttpOtaUpdater::updateState(OtaState state, const std::string& message) {
|
|
m_currentState = state;
|
|
|
|
if (m_stateCallback) {
|
|
m_stateCallback(state, message);
|
|
}
|
|
|
|
switch (state) {
|
|
case OtaState::CONNECTING:
|
|
ESP_LOGI(TAG, "State: CONNECTING - %s", message.c_str());
|
|
break;
|
|
case OtaState::DOWNLOADING:
|
|
ESP_LOGI(TAG, "State: DOWNLOADING - %s", message.c_str());
|
|
break;
|
|
case OtaState::VERIFYING:
|
|
ESP_LOGI(TAG, "State: VERIFYING - %s", message.c_str());
|
|
break;
|
|
case OtaState::SUCCESS:
|
|
ESP_LOGI(TAG, "State: SUCCESS - %s", message.c_str());
|
|
break;
|
|
case OtaState::FAILED:
|
|
ESP_LOGE(TAG, "State: FAILED - %s", message.c_str());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
esp_err_t HttpOtaUpdater::httpEventHandle(esp_http_client_event_t* event) {
|
|
auto* updater = static_cast<HttpOtaUpdater*>(event->user_data);
|
|
|
|
if (!updater) {
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
switch (event->event_id) {
|
|
case HTTP_EVENT_ON_CONNECTED:
|
|
ESP_LOGI(TAG, "HTTP connected");
|
|
updater->updateState(OtaState::DOWNLOADING, "Connected to server");
|
|
break;
|
|
|
|
case HTTP_EVENT_ON_HEADER: {
|
|
// 获取文件总大小
|
|
if (strcasecmp(event->header_key, "Content-Length") == 0) {
|
|
updater->m_totalSize = atoi(event->header_value);
|
|
ESP_LOGI(TAG, "Total file size: %d bytes", updater->m_totalSize);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HTTP_EVENT_ON_DATA: {
|
|
if (event->data_len > 0) {
|
|
updater->m_downloadedSize += event->data_len;
|
|
|
|
// 更新进度
|
|
if (updater->m_totalSize > 0 && updater->m_progressCallback) {
|
|
int progress = (updater->m_downloadedSize * 100) / updater->m_totalSize;
|
|
updater->m_progressCallback(progress, 100);
|
|
}
|
|
|
|
// 定期打印下载进度
|
|
if (updater->m_downloadedSize % (100 * 1024) < event->data_len) { // 每100KB打印一次
|
|
ESP_LOGI(TAG, "Downloaded: %d/%d bytes (%.1f%%)",
|
|
updater->m_downloadedSize, updater->m_totalSize,
|
|
(updater->m_downloadedSize * 100.0) / updater->m_totalSize);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HTTP_EVENT_ON_FINISH:
|
|
ESP_LOGI(TAG, "HTTP download finished");
|
|
updater->updateState(OtaState::VERIFYING, "Download completed, verifying firmware");
|
|
break;
|
|
|
|
case HTTP_EVENT_DISCONNECTED:
|
|
ESP_LOGI(TAG, "HTTP disconnected");
|
|
break;
|
|
|
|
case HTTP_EVENT_ERROR:
|
|
ESP_LOGE(TAG, "HTTP error occurred");
|
|
updater->m_errorMessage = "HTTP error occurred";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return ESP_OK;
|
|
} |