#include "FirmwareUpgrade.h" #include "SystemInfo.h" #include #include #include #include #include #include #include #include #include #define TAG "FirmwareUpgrade" FirmwareUpgrade::FirmwareUpgrade(Http& http) : http_(http) { } FirmwareUpgrade::~FirmwareUpgrade() { } void FirmwareUpgrade::SetCheckVersionUrl(std::string check_version_url) { check_version_url_ = check_version_url; } void FirmwareUpgrade::SetPostData(const std::string& post_data) { post_data_ = post_data; } void FirmwareUpgrade::SetHeader(const std::string& key, const std::string& value) { headers_[key] = value; } void FirmwareUpgrade::CheckVersion() { std::string current_version = esp_app_get_description()->version; ESP_LOGI(TAG, "Current version: %s", current_version.c_str()); if (check_version_url_.length() < 10) { ESP_LOGE(TAG, "Check version URL is not properly set"); return; } for (const auto& header : headers_) { http_.SetHeader(header.first, header.second); } if (post_data_.empty()) { http_.Open("GET", check_version_url_); } else { http_.SetHeader("Content-Type", "application/json"); http_.SetContent(post_data_); http_.Open("POST", check_version_url_); } auto response = http_.GetBody(); http_.Close(); // Response: { "firmware": { "version": "1.0.0", "url": "http://" } } // Parse the JSON response and check if the version is newer // If it is, set has_new_version_ to true and store the new version and URL cJSON *root = cJSON_Parse(response.c_str()); if (root == NULL) { ESP_LOGE(TAG, "Failed to parse JSON response"); return; } cJSON *firmware = cJSON_GetObjectItem(root, "firmware"); if (firmware == NULL) { ESP_LOGE(TAG, "Failed to get firmware object"); cJSON_Delete(root); return; } cJSON *version = cJSON_GetObjectItem(firmware, "version"); if (version == NULL) { ESP_LOGE(TAG, "Failed to get version object"); cJSON_Delete(root); return; } cJSON *url = cJSON_GetObjectItem(firmware, "url"); if (url == NULL) { ESP_LOGE(TAG, "Failed to get url object"); cJSON_Delete(root); return; } firmware_version_ = version->valuestring; firmware_url_ = url->valuestring; cJSON_Delete(root); // Check if the version is newer, for example, 0.1.0 is newer than 0.0.1 has_new_version_ = IsNewVersionAvailable(current_version, firmware_version_); if (has_new_version_) { ESP_LOGI(TAG, "New version available: %s", firmware_version_.c_str()); } else { ESP_LOGI(TAG, "Current is the latest version"); } } void FirmwareUpgrade::MarkCurrentVersionValid() { auto partition = esp_ota_get_running_partition(); if (strcmp(partition->label, "factory") == 0) { ESP_LOGI(TAG, "Running from factory partition, skipping"); return; } ESP_LOGI(TAG, "Running partition: %s", partition->label); esp_ota_img_states_t state; if (esp_ota_get_state_partition(partition, &state) != ESP_OK) { ESP_LOGE(TAG, "Failed to get state of partition"); return; } if (state == ESP_OTA_IMG_PENDING_VERIFY) { ESP_LOGI(TAG, "Marking firmware as valid"); esp_ota_mark_app_valid_cancel_rollback(); } } void FirmwareUpgrade::Upgrade(const std::string& firmware_url) { ESP_LOGI(TAG, "Upgrading firmware from %s", firmware_url.c_str()); esp_ota_handle_t update_handle = 0; auto update_partition = esp_ota_get_next_update_partition(NULL); if (update_partition == NULL) { ESP_LOGE(TAG, "Failed to get update partition"); return; } ESP_LOGI(TAG, "Writing to partition %s at offset 0x%lx", update_partition->label, update_partition->address); bool image_header_checked = false; std::string image_header; if (!http_.Open("GET", firmware_url)) { ESP_LOGE(TAG, "Failed to open HTTP connection"); return; } size_t content_length = http_.GetBodyLength(); if (content_length == 0) { ESP_LOGE(TAG, "Failed to get content length"); http_.Close(); return; } char buffer[4096]; size_t total_read = 0, recent_read = 0; auto last_calc_time = esp_timer_get_time(); while (true) { int ret = http_.Read(buffer, sizeof(buffer)); if (ret < 0) { ESP_LOGE(TAG, "Failed to read HTTP data: %s", esp_err_to_name(ret)); http_.Close(); return; } // Calculate speed and progress every second recent_read += ret; total_read += ret; if (esp_timer_get_time() - last_calc_time >= 1000000 || ret == 0) { size_t progress = total_read * 100 / content_length; ESP_LOGI(TAG, "Progress: %zu%% (%zu/%zu), Speed: %zuB/s", progress, total_read, content_length, recent_read); if (upgrade_callback_) { upgrade_callback_(progress, recent_read); } last_calc_time = esp_timer_get_time(); recent_read = 0; } if (ret == 0) { break; } if (!image_header_checked) { image_header.append(buffer, ret); if (image_header.size() >= sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) { esp_app_desc_t new_app_info; memcpy(&new_app_info, image_header.data() + sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t), sizeof(esp_app_desc_t)); ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version); auto current_version = esp_app_get_description()->version; if (memcmp(new_app_info.version, current_version, sizeof(new_app_info.version)) == 0) { ESP_LOGE(TAG, "Firmware version is the same, skipping upgrade"); http_.Close(); return; } if (esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle)) { esp_ota_abort(update_handle); http_.Close(); ESP_LOGE(TAG, "Failed to begin OTA"); return; } image_header_checked = true; } } auto err = esp_ota_write(update_handle, buffer, ret); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to write OTA data: %s", esp_err_to_name(err)); esp_ota_abort(update_handle); http_.Close(); return; } } http_.Close(); esp_err_t err = esp_ota_end(update_handle); if (err != ESP_OK) { if (err == ESP_ERR_OTA_VALIDATE_FAILED) { ESP_LOGE(TAG, "Image validation failed, image is corrupted"); } else { ESP_LOGE(TAG, "Failed to end OTA: %s", esp_err_to_name(err)); } return; } err = esp_ota_set_boot_partition(update_partition); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to set boot partition: %s", esp_err_to_name(err)); return; } ESP_LOGI(TAG, "Firmware upgrade successful, rebooting in 3 seconds..."); vTaskDelay(pdMS_TO_TICKS(3000)); esp_restart(); } void FirmwareUpgrade::StartUpgrade(std::function callback) { upgrade_callback_ = callback; Upgrade(firmware_url_); } std::vector FirmwareUpgrade::ParseVersion(const std::string& version) { std::vector versionNumbers; std::stringstream ss(version); std::string segment; while (std::getline(ss, segment, '.')) { versionNumbers.push_back(std::stoi(segment)); } return versionNumbers; } bool FirmwareUpgrade::IsNewVersionAvailable(const std::string& currentVersion, const std::string& newVersion) { std::vector current = ParseVersion(currentVersion); std::vector newer = ParseVersion(newVersion); for (size_t i = 0; i < std::min(current.size(), newer.size()); ++i) { if (newer[i] > current[i]) { return true; } else if (newer[i] < current[i]) { return false; } } return newer.size() > current.size(); }