// // Created by misaki on 2025/9/24. // #include "HttpOtaUpdater.h" #include #include 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(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; }