diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 868c0b8..4ddb98b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,16 +12,17 @@ jobs: fail-fast: true matrix: targets: [esp32, esp32s3] + espidf: [v4.4.6, v5.0.5, v5.1.2, v5.2] runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: 'recursive' - name: esp-idf build - uses: Fishwaldo/esp-idf-ci-action@v1.2 + uses: espressif/esp-idf-ci-action@v1 with: - esp_idf_version: v4.4.3 + esp_idf_version: ${{ matrix.espidf }} target: ${{ matrix.targets }} path: 'examples/esp_ghota_example' - name: Rename artifact diff --git a/README.md b/README.md index 3a462ae..410b256 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ You should be careful with your GitHub PAT and putting it in the source code. I ### esp-idf via Espressif Component Registry: ```bash -idf.py add-dependency Fishwaldo/ghota^0.0.1 +idf.py add-dependency Fishwaldo/ghota^1.0.0 ``` #### Platform IO Registry: @@ -34,7 +34,7 @@ add this to your platform.ini file: ```ini lib_deps = - Fishwaldo/ghota@^0.0.1 + fishwaldo/ghota@^1.0.0 ``` You also need to copy the contents of [Kconfig](Kconfig) into your project's Kconfig file, and run pio run -t menuconfig to configure the component. diff --git a/library.json b/library.json index f81a50e..e81816b 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "esp_ghota", - "version": "0.0.1", + "version": "1.0.0", "description": "a OTA library to upgrade your firmware via the Github Releases API", "keywords": "esp32 ota github", "repository": diff --git a/src/esp_ghota.c b/src/esp_ghota.c index 8582ded..0ecda46 100644 --- a/src/esp_ghota.c +++ b/src/esp_ghota.c @@ -21,18 +21,26 @@ static const char *TAG = "GHOTA"; ESP_EVENT_DEFINE_BASE(GHOTA_EVENTS); -typedef struct ghota_client_handle_t { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +#define PRICONTENT_LENGTH PRId64 +#else +#define PRICONTENT_LENGTH PRId32 +#endif +typedef struct ghota_client_handle_t +{ ghota_config_t config; char *username; char *token; - struct { + struct + { char tag_name[CONFIG_MAX_FILENAME_LEN]; char name[CONFIG_MAX_FILENAME_LEN]; char url[CONFIG_MAX_URL_LEN]; char storageurl[CONFIG_MAX_URL_LEN]; uint8_t flags; } result; - struct { + struct + { char name[CONFIG_MAX_FILENAME_LEN]; char url[CONFIG_MAX_URL_LEN]; } scratch; @@ -43,7 +51,6 @@ typedef struct ghota_client_handle_t { const esp_partition_t *storage_partition; } ghota_client_handle_t; - enum release_flags { GHOTA_RELEASE_GOT_TAG = 0x01, @@ -55,7 +62,6 @@ enum release_flags SemaphoreHandle_t ghota_lock = NULL; - static void SetFlag(ghota_client_handle_t *handle, enum release_flags flag) { handle->result.flags |= flag; @@ -70,16 +76,20 @@ static void ClearFlag(ghota_client_handle_t *handle, enum release_flags flag) handle->result.flags &= ~flag; } -ghota_client_handle_t *ghota_init(ghota_config_t *newconfig) { - if (!ghota_lock) { +ghota_client_handle_t *ghota_init(ghota_config_t *newconfig) +{ + if (!ghota_lock) + { ghota_lock = xSemaphoreCreateMutex(); } - if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdPASS) { + if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdPASS) + { ESP_LOGE(TAG, "Failed to take lock"); return NULL; } ghota_client_handle_t *handle = malloc(sizeof(ghota_client_handle_t)); - if (handle == NULL) { + if (handle == NULL) + { ESP_LOGE(TAG, "Failed to allocate memory for client handle"); xSemaphoreGive(ghota_lock); return NULL; @@ -102,9 +112,13 @@ ghota_client_handle_t *ghota_init(ghota_config_t *newconfig) { asprintf(&handle->config.reponame, CONFIG_GITHUB_REPO); else asprintf(&handle->config.reponame, newconfig->reponame); - +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + const esp_app_desc_t *app_desc = esp_app_get_description(); +#else const esp_app_desc_t *app_desc = esp_ota_get_app_description(); - if (semver_parse(app_desc->version, &handle->current_version)) { +#endif + if (semver_parse(app_desc->version, &handle->current_version)) + { ESP_LOGE(TAG, "Failed to parse current version"); ghota_free(handle); xSemaphoreGive(ghota_lock); @@ -112,10 +126,10 @@ ghota_client_handle_t *ghota_init(ghota_config_t *newconfig) { } handle->result.flags = 0; -// if (newconfig->updateInterval < 60) { -// ESP_LOGE(TAG, "Update interval must be at least 60 Minutes"); -// newconfig->updateInterval = 60; -// } + // if (newconfig->updateInterval < 60) { + // ESP_LOGE(TAG, "Update interval must be at least 60 Minutes"); + // newconfig->updateInterval = 60; + // } handle->config.updateInterval = newconfig->updateInterval; handle->task_handle = NULL; @@ -124,8 +138,10 @@ ghota_client_handle_t *ghota_init(ghota_config_t *newconfig) { return handle; } -esp_err_t ghota_free(ghota_client_handle_t *handle) { - if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdPASS) { +esp_err_t ghota_free(ghota_client_handle_t *handle) +{ + if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdPASS) + { ESP_LOGE(TAG, "Failed to take lock"); return ESP_FAIL; } @@ -142,8 +158,10 @@ esp_err_t ghota_free(ghota_client_handle_t *handle) { return ESP_OK; } -esp_err_t ghota_set_auth(ghota_client_handle_t *handle, const char *username, const char *password) { - if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdPASS) { +esp_err_t ghota_set_auth(ghota_client_handle_t *handle, const char *username, const char *password) +{ + if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdPASS) + { ESP_LOGE(TAG, "Failed to take lock"); return ESP_FAIL; } @@ -153,10 +171,10 @@ esp_err_t ghota_set_auth(ghota_client_handle_t *handle, const char *username, co return ESP_OK; } - static void lwjson_callback(lwjson_stream_parser_t *jsp, lwjson_stream_type_t type) { - if (jsp->udata == NULL) { + if (jsp->udata == NULL) + { ESP_LOGE(TAG, "No user data for callback"); return; } @@ -169,7 +187,8 @@ static void lwjson_callback(lwjson_stream_parser_t *jsp, lwjson_stream_type_t ty } #endif /* Get a value corresponsing to "tag_name" key */ - if (!GetFlag(handle, GHOTA_RELEASE_GOT_TAG)) { + if (!GetFlag(handle, GHOTA_RELEASE_GOT_TAG)) + { if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ @@ -180,14 +199,9 @@ static void lwjson_callback(lwjson_stream_parser_t *jsp, lwjson_stream_type_t ty SetFlag(handle, GHOTA_RELEASE_GOT_TAG); } } - if (!GetFlag(handle, GHOTA_RELEASE_VALID_ASSET) || !GetFlag(handle, GHOTA_RELEASE_GOT_STORAGE)) { - if (jsp->stack_pos == 5 - && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT - && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY - && strcasecmp(jsp->stack[1].meta.name, "assets") == 0 - && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY - && jsp->stack[3].type == LWJSON_STREAM_TYPE_OBJECT - && jsp->stack[4].type == LWJSON_STREAM_TYPE_KEY) + if (!GetFlag(handle, GHOTA_RELEASE_VALID_ASSET) || !GetFlag(handle, GHOTA_RELEASE_GOT_STORAGE)) + { + if (jsp->stack_pos == 5 && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY && strcasecmp(jsp->stack[1].meta.name, "assets") == 0 && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY && jsp->stack[3].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[4].type == LWJSON_STREAM_TYPE_KEY) { ESP_LOGD(TAG, "Assets Got key '%s' with value '%s'", jsp->stack[jsp->stack_pos - 1].meta.name, jsp->data.str.buff); if (strcasecmp(jsp->stack[4].meta.name, "name") == 0) @@ -203,19 +217,25 @@ static void lwjson_callback(lwjson_stream_parser_t *jsp, lwjson_stream_type_t ty ESP_LOGD(TAG, "Got URL for Asset: %s", handle->scratch.url); } /* Now test if we got both name an download url */ - if (GetFlag(handle, GHOTA_RELEASE_GOT_FNAME) && GetFlag(handle, GHOTA_RELEASE_GOT_URL)) { + if (GetFlag(handle, GHOTA_RELEASE_GOT_FNAME) && GetFlag(handle, GHOTA_RELEASE_GOT_URL)) + { ESP_LOGD(TAG, "Testing Firmware filenames %s -> %s - Matching Filename against %s and %s", handle->scratch.name, handle->scratch.url, handle->config.filenamematch, handle->config.storagenamematch); /* see if the filename matches */ - if (!GetFlag(handle, GHOTA_RELEASE_VALID_ASSET) && fnmatch(handle->config.filenamematch, handle->scratch.name, 0) == 0) { + if (!GetFlag(handle, GHOTA_RELEASE_VALID_ASSET) && fnmatch(handle->config.filenamematch, handle->scratch.name, 0) == 0) + { strncpy(handle->result.name, handle->scratch.name, CONFIG_MAX_FILENAME_LEN); strncpy(handle->result.url, handle->scratch.url, CONFIG_MAX_URL_LEN); ESP_LOGD(TAG, "Valid Firmware Found: %s - %s", handle->result.name, handle->result.url); SetFlag(handle, GHOTA_RELEASE_VALID_ASSET); - } else if (!GetFlag(handle, GHOTA_RELEASE_GOT_STORAGE) && fnmatch(handle->config.storagenamematch, handle->scratch.name, 0) == 0) { + } + else if (!GetFlag(handle, GHOTA_RELEASE_GOT_STORAGE) && fnmatch(handle->config.storagenamematch, handle->scratch.name, 0) == 0) + { strncpy(handle->result.storageurl, handle->scratch.url, CONFIG_MAX_URL_LEN); ESP_LOGD(TAG, "Valid Storage Asset Found: %s - %s", handle->scratch.name, handle->result.storageurl); SetFlag(handle, GHOTA_RELEASE_GOT_STORAGE); - } else { + } + else + { ESP_LOGD(TAG, "Invalid Asset Found: %s", handle->scratch.name); ClearFlag(handle, GHOTA_RELEASE_GOT_FNAME); ClearFlag(handle, GHOTA_RELEASE_GOT_URL); @@ -228,51 +248,56 @@ static void lwjson_callback(lwjson_stream_parser_t *jsp, lwjson_stream_type_t ty static esp_err_t _http_event_handler(esp_http_client_event_t *evt) { lwjsonr_t res; - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wswitch" - switch (evt->event_id) { - case HTTP_EVENT_ON_HEADER: - if (strncasecmp(evt->header_key, "x-ratelimit-remaining", strlen("x-ratelimit-remaining")) == 0) { - int limit = atoi(evt->header_value); - ESP_LOGD(TAG, "Github API Rate Limit Remaining: %d", limit); - if (limit < 10) { - ESP_LOGW(TAG, "Github API Rate Limit Remaining is low: %d", limit); - } - } - break; - case HTTP_EVENT_ON_DATA: - if (!esp_http_client_is_chunked_response(evt->client)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" + switch (evt->event_id) + { + case HTTP_EVENT_ON_HEADER: + if (strncasecmp(evt->header_key, "x-ratelimit-remaining", strlen("x-ratelimit-remaining")) == 0) + { + int limit = atoi(evt->header_value); + ESP_LOGD(TAG, "Github API Rate Limit Remaining: %d", limit); + if (limit < 10) { - char *buf = evt->data; - for (int i = 0; i < evt->data_len; i++) - { - res = lwjson_stream_parse((lwjson_stream_parser_t *)evt->user_data, *buf); - if (!(res == lwjsonOK || res == lwjsonSTREAMDONE || res == lwjsonSTREAMINPROG)) - { - ESP_LOGE(TAG, "Lwjson Error: %d", res); - } - buf++; - } + ESP_LOGW(TAG, "Github API Rate Limit Remaining is low: %d", limit); } - break; - case HTTP_EVENT_DISCONNECTED: { - int mbedtls_err = 0; - esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL); - if (err != 0) - { - ESP_LOGE(TAG, "Last esp error code: 0x%x", err); - ESP_LOGE(TAG, "Last mbedtls failure: 0x%x", mbedtls_err); - } - break; } + break; + case HTTP_EVENT_ON_DATA: + if (!esp_http_client_is_chunked_response(evt->client)) + { + char *buf = evt->data; + for (int i = 0; i < evt->data_len; i++) + { + res = lwjson_stream_parse((lwjson_stream_parser_t *)evt->user_data, *buf); + if (!(res == lwjsonOK || res == lwjsonSTREAMDONE || res == lwjsonSTREAMINPROG)) + { + ESP_LOGE(TAG, "Lwjson Error: %d", res); + } + buf++; + } + } + break; + case HTTP_EVENT_DISCONNECTED: + { + int mbedtls_err = 0; + esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL); + if (err != 0) + { + ESP_LOGE(TAG, "Last esp error code: 0x%x", err); + ESP_LOGE(TAG, "Last mbedtls failure: 0x%x", mbedtls_err); + } + break; } - #pragma GCC diagnostic pop + } +#pragma GCC diagnostic pop return ESP_OK; } esp_err_t ghota_check(ghota_client_handle_t *handle) { - if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdPASS) { + if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdPASS) + { ESP_LOGE(TAG, "Failed to get lock"); return ESP_FAIL; } @@ -289,7 +314,7 @@ esp_err_t ghota_check(ghota_client_handle_t *handle) xSemaphoreGive(ghota_lock); return ESP_FAIL; } - stream_parser.udata = (void*)handle; + stream_parser.udata = (void *)handle; char url[CONFIG_MAX_URL_LEN]; snprintf(url, CONFIG_MAX_URL_LEN, "https://%s/repos/%s/%s/releases/latest", handle->config.hostname, handle->config.orgname, handle->config.reponame); @@ -300,7 +325,8 @@ esp_err_t ghota_check(ghota_client_handle_t *handle) .event_handler = _http_event_handler, .user_data = &stream_parser, }; - if (handle->username) { + if (handle->username) + { ESP_LOGD(TAG, "Using Authenticated Request to %s", url); httpconfig.username = handle->username; httpconfig.password = handle->token; @@ -313,7 +339,7 @@ esp_err_t ghota_check(ghota_client_handle_t *handle) esp_err_t err = esp_http_client_perform(client); if (err == ESP_OK) { - ESP_LOGD(TAG, "HTTP GET Status = %d, content_length = %d", + ESP_LOGD(TAG, "HTTP GET Status = %d, content_length = %" PRICONTENT_LENGTH , esp_http_client_get_status_code(client), esp_http_client_get_content_length(client)); } @@ -324,13 +350,13 @@ esp_err_t ghota_check(ghota_client_handle_t *handle) ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_NOUPDATE_AVAILABLE, handle, sizeof(ghota_client_handle_t *), portMAX_DELAY)); xSemaphoreGive(ghota_lock); return ESP_FAIL; - } if (esp_http_client_get_status_code(client) == 200) { if (GetFlag(handle, GHOTA_RELEASE_VALID_ASSET)) { - if (semver_parse(handle->result.tag_name, &handle->latest_version)) { + if (semver_parse(handle->result.tag_name, &handle->latest_version)) + { ESP_LOGE(TAG, "Failed to parse new version"); esp_http_client_cleanup(client); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_NOUPDATE_AVAILABLE, handle, sizeof(ghota_client_handle_t *), portMAX_DELAY)); @@ -341,10 +367,13 @@ esp_err_t ghota_check(ghota_client_handle_t *handle) ESP_LOGI(TAG, "New Version %d.%d.%d", handle->latest_version.major, handle->latest_version.minor, handle->latest_version.patch); ESP_LOGI(TAG, "Asset: %s", handle->result.name); ESP_LOGI(TAG, "Firmware URL: %s", handle->result.url); - if (strlen(handle->result.storageurl)) { + if (strlen(handle->result.storageurl)) + { ESP_LOGI(TAG, "Storage URL: %s", handle->result.storageurl); } - } else { + } + else + { ESP_LOGI(TAG, "Asset: No Valid Firmware Assets Found"); esp_http_client_cleanup(client); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_NOUPDATE_AVAILABLE, handle, sizeof(ghota_client_handle_t *), portMAX_DELAY)); @@ -369,7 +398,8 @@ esp_err_t ghota_check(ghota_client_handle_t *handle) static esp_err_t validate_image_header(esp_app_desc_t *new_app_info) { - if (new_app_info == NULL) { + if (new_app_info == NULL) + { return ESP_ERR_INVALID_ARG; } ESP_LOGI(TAG, "New Firmware Details:"); @@ -379,12 +409,12 @@ static esp_err_t validate_image_header(esp_app_desc_t *new_app_info) ESP_LOGI(TAG, "ESP-IDF: %s", new_app_info->idf_ver); ESP_LOGI(TAG, "SHA256:"); ESP_LOG_BUFFER_HEX(TAG, new_app_info->app_elf_sha256, sizeof(new_app_info->app_elf_sha256)); - + const esp_partition_t *running = esp_ota_get_running_partition(); - ESP_LOGD(TAG, "Current partition %s type %d subtype %d (offset 0x%08x)", + ESP_LOGD(TAG, "Current partition %s type %d subtype %d (offset 0x%08" PRIx32 ")", running->label, running->type, running->subtype, running->address); const esp_partition_t *update = esp_ota_get_next_update_partition(NULL); - ESP_LOGD(TAG, "Update partition %s type %d subtype %d (offset 0x%08x)", + ESP_LOGD(TAG, "Update partition %s type %d subtype %d (offset 0x%08" PRIx32 ")", update->label, update->type, update->subtype, update->address); #ifdef CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK @@ -394,7 +424,8 @@ static esp_err_t validate_image_header(esp_app_desc_t *new_app_info) * esp_https_ota_finish at the end of OTA update procedure. */ const uint32_t hw_sec_version = esp_efuse_read_secure_version(); - if (new_app_info->secure_version < hw_sec_version) { + if (new_app_info->secure_version < hw_sec_version) + { ESP_LOGW(TAG, "New firmware security version is less than eFuse programmed, %d < %d", new_app_info->secure_version, hw_sec_version); return ESP_FAIL; } @@ -408,79 +439,92 @@ static esp_err_t http_client_set_header_cb(esp_http_client_handle_t http_client) return esp_http_client_set_header(http_client, "Accept", "application/octet-stream"); } -esp_err_t _http_event_storage_handler(esp_http_client_event_t *evt) { +esp_err_t _http_event_storage_handler(esp_http_client_event_t *evt) +{ static int output_pos; static int last_progress; - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wswitch" - switch (evt->event_id) { - case HTTP_EVENT_ON_CONNECTED: { - output_pos = 0; - last_progress = 0; - /* Erase the Partition */ - break; - } - case HTTP_EVENT_ON_DATA: - if (!esp_http_client_is_chunked_response(evt->client)) { - ghota_client_handle_t *handle = (ghota_client_handle_t *)evt->user_data; - if (output_pos == 0) { - ESP_LOGD(TAG, "Erasing Partition"); - ESP_ERROR_CHECK(esp_partition_erase_range(handle->storage_partition, 0, handle->storage_partition->size)); - ESP_LOGD(TAG, "Erasing Complete"); - } - - ESP_ERROR_CHECK(esp_partition_write(handle->storage_partition, output_pos, evt->data, evt->data_len)); - output_pos += evt->data_len; - int progress = 100 * ((float)output_pos / (float)handle->storage_partition->size); - if ((progress % 5 == 0) && (progress != last_progress)) { - ESP_LOGV(TAG, "Storage Firmware Update Progress: %d%%", progress); - ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_STORAGE_UPDATE_PROGRESS, &progress, sizeof(progress), portMAX_DELAY)); - last_progress = progress; - } - } - break; - case HTTP_EVENT_DISCONNECTED: { - int mbedtls_err = 0; - esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL); - if (err != 0) - { - ESP_LOGE(TAG, "Last esp error code: 0x%x", err); - ESP_LOGE(TAG, "Last mbedtls failure: 0x%x", mbedtls_err); - } - break; - } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" + switch (evt->event_id) + { + case HTTP_EVENT_ON_CONNECTED: + { + output_pos = 0; + last_progress = 0; + /* Erase the Partition */ + break; } - #pragma GCC diagnostic pop + case HTTP_EVENT_ON_DATA: + if (!esp_http_client_is_chunked_response(evt->client)) + { + ghota_client_handle_t *handle = (ghota_client_handle_t *)evt->user_data; + if (output_pos == 0) + { + ESP_LOGD(TAG, "Erasing Partition"); + ESP_ERROR_CHECK(esp_partition_erase_range(handle->storage_partition, 0, handle->storage_partition->size)); + ESP_LOGD(TAG, "Erasing Complete"); + } + + ESP_ERROR_CHECK(esp_partition_write(handle->storage_partition, output_pos, evt->data, evt->data_len)); + output_pos += evt->data_len; + int progress = 100 * ((float)output_pos / (float)handle->storage_partition->size); + if ((progress % 5 == 0) && (progress != last_progress)) + { + ESP_LOGV(TAG, "Storage Firmware Update Progress: %d%%", progress); + ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_STORAGE_UPDATE_PROGRESS, &progress, sizeof(progress), portMAX_DELAY)); + last_progress = progress; + } + } + break; + case HTTP_EVENT_DISCONNECTED: + { + int mbedtls_err = 0; + esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL); + if (err != 0) + { + ESP_LOGE(TAG, "Last esp error code: 0x%x", err); + ESP_LOGE(TAG, "Last mbedtls failure: 0x%x", mbedtls_err); + } + break; + } + } +#pragma GCC diagnostic pop return ESP_OK; } -esp_err_t ghota_storage_update(ghota_client_handle_t *handle) { - if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdTRUE) { +esp_err_t ghota_storage_update(ghota_client_handle_t *handle) +{ + if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdTRUE) + { ESP_LOGE(TAG, "Failed to take lock"); return ESP_FAIL; - } - if (handle == NULL) { + } + if (handle == NULL) + { ESP_LOGE(TAG, "Invalid Handle"); xSemaphoreGive(ghota_lock); return ESP_ERR_INVALID_ARG; } - if (!strlen(handle->result.storageurl)) { + if (!strlen(handle->result.storageurl)) + { ESP_LOGE(TAG, "No Storage URL"); xSemaphoreGive(ghota_lock); return ESP_FAIL; } - if (!strlen(handle->config.storagepartitionname)) { + if (!strlen(handle->config.storagepartitionname)) + { ESP_LOGE(TAG, "No Storage Partition Name"); xSemaphoreGive(ghota_lock); return ESP_FAIL; } - handle->storage_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, handle->config.storagepartitionname); - if (handle->storage_partition == NULL) { + handle->storage_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, handle->config.storagepartitionname); + if (handle->storage_partition == NULL) + { ESP_LOGE(TAG, "Storage Partition Not Found"); xSemaphoreGive(ghota_lock); return ESP_FAIL; } - ESP_LOGD(TAG, "Storage Partition %s - Type %x Subtype %x Found at %x - size %d", handle->storage_partition->label, handle->storage_partition->type, handle->storage_partition->subtype, handle->storage_partition->address, handle->storage_partition->size); + ESP_LOGD(TAG, "Storage Partition %s - Type %x Subtype %x Found at %" PRIx32 " - size %" PRIu32, handle->storage_partition->label, handle->storage_partition->type, handle->storage_partition->subtype, handle->storage_partition->address, handle->storage_partition->size); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_START_STORAGE_UPDATE, NULL, 0, portMAX_DELAY)); /* give time for the system to react, such as unmounting the filesystems etc */ vTaskDelay(pdMS_TO_TICKS(1000)); @@ -493,7 +537,8 @@ esp_err_t ghota_storage_update(ghota_client_handle_t *handle) { .buffer_size_tx = 2048, }; - if (handle->username) { + if (handle->username) + { ESP_LOGD(TAG, "Using Authenticated Request to %s", config.url); config.username = handle->username; config.password = handle->token; @@ -502,41 +547,46 @@ esp_err_t ghota_storage_update(ghota_client_handle_t *handle) { esp_http_client_handle_t client = esp_http_client_init(&config); ESP_ERROR_CHECK(esp_http_client_set_header(client, "Accept", "application/octet-stream")); esp_err_t err = esp_http_client_perform(client); - if (err == ESP_OK) { - ESP_LOGD(TAG, "HTTP GET Status = %d, content_length = %d", - esp_http_client_get_status_code(client), - esp_http_client_get_content_length(client)); - uint8_t sha256[32] = { 0 }; + if (err == ESP_OK) + { + ESP_LOGD(TAG, "HTTP GET Status = %d, content_length = %" PRICONTENT_LENGTH, + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + uint8_t sha256[32] = {0}; ESP_ERROR_CHECK(esp_partition_get_sha256(handle->storage_partition, sha256)); ESP_LOG_BUFFER_HEX("New Storage Partition SHA256:", sha256, sizeof(sha256)); - ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_FINISH_STORAGE_UPDATE, NULL, 0, portMAX_DELAY)); - } else { + ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_FINISH_STORAGE_UPDATE, NULL, 0, portMAX_DELAY)); + } + else + { ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err)); - ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_STORAGE_UPDATE_FAILED, NULL, 0, portMAX_DELAY)); - + ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_STORAGE_UPDATE_FAILED, NULL, 0, portMAX_DELAY)); } esp_http_client_cleanup(client); xSemaphoreGive(ghota_lock); return ESP_OK; } - -esp_err_t ghota_update(ghota_client_handle_t *handle) { +esp_err_t ghota_update(ghota_client_handle_t *handle) +{ esp_err_t ota_finish_err = ESP_OK; - if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdTRUE) { + if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdTRUE) + { ESP_LOGE(TAG, "Failed to take lock"); return ESP_FAIL; } ESP_LOGI(TAG, "Scheduled Check for Firmware Update Starting"); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_START_UPDATE, NULL, 0, portMAX_DELAY)); - if (!GetFlag(handle, GHOTA_RELEASE_VALID_ASSET)) { + if (!GetFlag(handle, GHOTA_RELEASE_VALID_ASSET)) + { ESP_LOGE(TAG, "No Valid Release Asset Found"); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_UPDATE_FAILED, NULL, 0, portMAX_DELAY)); xSemaphoreGive(ghota_lock); return ESP_FAIL; } int cmp = semver_compare_version(handle->latest_version, handle->current_version); - if ( cmp != 1) { + if (cmp != 1) + { ESP_LOGE(TAG, "Current Version is equal or newer than new release"); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_UPDATE_FAILED, NULL, 0, portMAX_DELAY)); xSemaphoreGive(ghota_lock); @@ -549,7 +599,8 @@ esp_err_t ghota_update(ghota_client_handle_t *handle) { .buffer_size_tx = 4096, }; - if (handle->username) { + if (handle->username) + { ESP_LOGD(TAG, "Using Authenticated Request to %s", httpconfig.url); httpconfig.username = handle->username; httpconfig.password = handle->token; @@ -558,58 +609,74 @@ esp_err_t ghota_update(ghota_client_handle_t *handle) { esp_https_ota_config_t ota_config = { .http_config = &httpconfig, - .http_client_init_cb = http_client_set_header_cb, + .http_client_init_cb = http_client_set_header_cb, }; - + esp_https_ota_handle_t https_ota_handle = NULL; esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle); - if (err != ESP_OK) { + if (err != ESP_OK) + { ESP_LOGE(TAG, "ESP HTTPS OTA Begin failed: %d", err); goto ota_end; } esp_app_desc_t app_desc; err = esp_https_ota_get_img_desc(https_ota_handle, &app_desc); - if (err != ESP_OK) { + if (err != ESP_OK) + { ESP_LOGE(TAG, "esp_https_ota_read_img_desc failed: %d", err); goto ota_end; } err = validate_image_header(&app_desc); - if (err != ESP_OK) { + if (err != ESP_OK) + { ESP_LOGE(TAG, "image header verification failed: %d", err); goto ota_end; } int last_progress = -1; - while (1) { + while (1) + { err = esp_https_ota_perform(https_ota_handle); - if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) { + if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) + { break; } int32_t dl = esp_https_ota_get_image_len_read(https_ota_handle); int32_t size = esp_https_ota_get_image_size(https_ota_handle); int progress = 100 * ((float)dl / (float)size); - if ((progress % 5 == 0) && (progress != last_progress)) { + if ((progress % 5 == 0) && (progress != last_progress)) + { ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_FIRMWARE_UPDATE_PROGRESS, &progress, sizeof(progress), portMAX_DELAY)); ESP_LOGV(TAG, "Firmware Update Progress: %d%%", progress); last_progress = progress; } } - if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) { + if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) + { // the OTA image was not completely received and user can customise the response to this situation. ESP_LOGE(TAG, "Complete data was not received."); - } else { + } + else + { ota_finish_err = esp_https_ota_finish(https_ota_handle); - if ((err == ESP_OK) && (ota_finish_err == ESP_OK)) { + if ((err == ESP_OK) && (ota_finish_err == ESP_OK)) + { ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_FINISH_UPDATE, NULL, 0, portMAX_DELAY)); - if (strlen(handle->result.storageurl)) { + if (strlen(handle->result.storageurl)) + { xSemaphoreGive(ghota_lock); - if (ghota_storage_update(handle) == ESP_OK) { + if (ghota_storage_update(handle) == ESP_OK) + { ESP_LOGI(TAG, "Storage Update Successful"); - } else { + } + else + { ESP_LOGE(TAG, "Storage Update Failed"); } - } else { + } + else + { xSemaphoreGive(ghota_lock); } ESP_LOGI(TAG, "ESP_HTTPS_OTA upgrade successful. Rebooting ..."); @@ -617,8 +684,11 @@ esp_err_t ghota_update(ghota_client_handle_t *handle) { vTaskDelay(1000 / portTICK_PERIOD_MS); esp_restart(); return ESP_OK; - } else { - if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) { + } + else + { + if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) + { ESP_LOGE(TAG, "Image validation failed, image is corrupted"); } ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed 0x%x", ota_finish_err); @@ -636,8 +706,10 @@ ota_end: return ESP_FAIL; } -semver_t *ghota_get_current_version(ghota_client_handle_t *handle) { - if (!handle) { +semver_t *ghota_get_current_version(ghota_client_handle_t *handle) +{ + if (!handle) + { return NULL; } semver_t *cur = malloc(sizeof(semver_t)); @@ -645,11 +717,14 @@ semver_t *ghota_get_current_version(ghota_client_handle_t *handle) { return cur; } -semver_t *ghota_get_latest_version(ghota_client_handle_t *handle) { - if (!handle) { +semver_t *ghota_get_latest_version(ghota_client_handle_t *handle) +{ + if (!handle) + { return NULL; } - if (!GetFlag(handle, GHOTA_RELEASE_VALID_ASSET)) { + if (!GetFlag(handle, GHOTA_RELEASE_VALID_ASSET)) + { return NULL; } semver_t *new = malloc(sizeof(semver_t)); @@ -657,19 +732,27 @@ semver_t *ghota_get_latest_version(ghota_client_handle_t *handle) { return new; } -static void ghota_task(void *pvParameters) { +static void ghota_task(void *pvParameters) +{ ghota_client_handle_t *handle = (ghota_client_handle_t *)pvParameters; ESP_LOGI(TAG, "Firmware Update Task Starting"); - if (handle) { - if (ghota_check(handle) == ESP_OK) { - if (semver_gt(handle->latest_version, handle->current_version) == 1) { + if (handle) + { + if (ghota_check(handle) == ESP_OK) + { + if (semver_gt(handle->latest_version, handle->current_version) == 1) + { ESP_LOGI(TAG, "New Version Available"); ghota_update(handle); - } else { + } + else + { ESP_LOGI(TAG, "No New Version Available"); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_NOUPDATE_AVAILABLE, NULL, 0, portMAX_DELAY)); } - } else { + } + else + { ESP_LOGI(TAG, "No Update Available"); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_NOUPDATE_AVAILABLE, NULL, 0, portMAX_DELAY)); } @@ -680,41 +763,53 @@ static void ghota_task(void *pvParameters) { handle->task_handle = NULL; } -esp_err_t ghota_start_update_task(ghota_client_handle_t *handle) { - if (!handle) { +esp_err_t ghota_start_update_task(ghota_client_handle_t *handle) +{ + if (!handle) + { return ESP_FAIL; } - eTaskState state = eInvalid; + eTaskState state = eInvalid; TaskHandle_t tmp = xTaskGetHandle("ghota_task"); - if (tmp) { + if (tmp) + { state = eTaskGetState(tmp); } - if (state == eDeleted || state == eInvalid) { + if (state == eDeleted || state == eInvalid) + { ESP_LOGD(TAG, "Starting Task to Check for Updates"); - if (xTaskCreate(ghota_task, "ghota_task", 6144, handle, 5, &handle->task_handle) != pdPASS) { + if (xTaskCreate(ghota_task, "ghota_task", 6144, handle, 5, &handle->task_handle) != pdPASS) + { ESP_LOGW(TAG, "Failed to Start ghota_task"); return ESP_FAIL; } - } else { + } + else + { ESP_LOGW(TAG, "ghota_task Already Running"); return ESP_FAIL; } return ESP_OK; } -static void ghota_timer_callback(TimerHandle_t xTimer) { +static void ghota_timer_callback(TimerHandle_t xTimer) +{ ghota_client_handle_t *handle = (ghota_client_handle_t *)pvTimerGetTimerID(xTimer); - if (handle) { + if (handle) + { handle->countdown--; - if (handle->countdown == 0) { + if (handle->countdown == 0) + { handle->countdown = handle->config.updateInterval; ghota_start_update_task(handle); } } } -esp_err_t ghota_start_update_timer(ghota_client_handle_t *handle) { - if (!handle) { +esp_err_t ghota_start_update_timer(ghota_client_handle_t *handle) +{ + if (!handle) + { ESP_LOGE(TAG, "Failed to initialize GHOTA Client"); return ESP_FAIL; } @@ -723,46 +818,54 @@ esp_err_t ghota_start_update_timer(ghota_client_handle_t *handle) { /* run timer every minute */ uint64_t ticks = pdMS_TO_TICKS(1000) * 60; TimerHandle_t timer = xTimerCreate("ghota_timer", ticks, pdTRUE, (void *)handle, ghota_timer_callback); - if ( timer == NULL) { + if (timer == NULL) + { ESP_LOGE(TAG, "Failed to create timer"); return ESP_FAIL; - } else { - if (xTimerStart(timer, 0) != pdPASS) { + } + else + { + if (xTimerStart(timer, 0) != pdPASS) + { ESP_LOGE(TAG, "Failed to start timer"); return ESP_FAIL; - } else { - ESP_LOGI(TAG, "Started Update Timer for %d Minutes", handle->config.updateInterval); + } + else + { + ESP_LOGI(TAG, "Started Update Timer for %" PRIu32 " Minutes", handle->config.updateInterval); } } return ESP_OK; } -char *ghota_get_event_str(ghota_event_e event) { - switch (event) { - case GHOTA_EVENT_START_CHECK: - return "GHOTA_EVENT_START_CHECK"; - case GHOTA_EVENT_UPDATE_AVAILABLE: - return "GHOTA_EVENT_UPDATE_AVAILABLE"; - case GHOTA_EVENT_NOUPDATE_AVAILABLE: - return "GHOTA_EVENT_NOUPDATE_AVAILABLE"; - case GHOTA_EVENT_START_UPDATE: - return "GHOTA_EVENT_START_UPDATE"; - case GHOTA_EVENT_FINISH_UPDATE: - return "GHOTA_EVENT_FINISH_UPDATE"; - case GHOTA_EVENT_UPDATE_FAILED: - return "GHOTA_EVENT_UPDATE_FAILED"; - case GHOTA_EVENT_START_STORAGE_UPDATE: - return "GHOTA_EVENT_START_STORAGE_UPDATE"; - case GHOTA_EVENT_FINISH_STORAGE_UPDATE: - return "GHOTA_EVENT_FINISH_STORAGE_UPDATE"; - case GHOTA_EVENT_STORAGE_UPDATE_FAILED: - return "GHOTA_EVENT_STORAGE_UPDATE_FAILED"; - case GHOTA_EVENT_FIRMWARE_UPDATE_PROGRESS: - return "GHOTA_EVENT_FIRMWARE_UPDATE_PROGRESS"; - case GHOTA_EVENT_STORAGE_UPDATE_PROGRESS: - return "GHOTA_EVENT_STORAGE_UPDATE_PROGRESS"; - case GHOTA_EVENT_PENDING_REBOOT: - return "GHOTA_EVENT_PENDING_REBOOT"; +char *ghota_get_event_str(ghota_event_e event) +{ + switch (event) + { + case GHOTA_EVENT_START_CHECK: + return "GHOTA_EVENT_START_CHECK"; + case GHOTA_EVENT_UPDATE_AVAILABLE: + return "GHOTA_EVENT_UPDATE_AVAILABLE"; + case GHOTA_EVENT_NOUPDATE_AVAILABLE: + return "GHOTA_EVENT_NOUPDATE_AVAILABLE"; + case GHOTA_EVENT_START_UPDATE: + return "GHOTA_EVENT_START_UPDATE"; + case GHOTA_EVENT_FINISH_UPDATE: + return "GHOTA_EVENT_FINISH_UPDATE"; + case GHOTA_EVENT_UPDATE_FAILED: + return "GHOTA_EVENT_UPDATE_FAILED"; + case GHOTA_EVENT_START_STORAGE_UPDATE: + return "GHOTA_EVENT_START_STORAGE_UPDATE"; + case GHOTA_EVENT_FINISH_STORAGE_UPDATE: + return "GHOTA_EVENT_FINISH_STORAGE_UPDATE"; + case GHOTA_EVENT_STORAGE_UPDATE_FAILED: + return "GHOTA_EVENT_STORAGE_UPDATE_FAILED"; + case GHOTA_EVENT_FIRMWARE_UPDATE_PROGRESS: + return "GHOTA_EVENT_FIRMWARE_UPDATE_PROGRESS"; + case GHOTA_EVENT_STORAGE_UPDATE_PROGRESS: + return "GHOTA_EVENT_STORAGE_UPDATE_PROGRESS"; + case GHOTA_EVENT_PENDING_REBOOT: + return "GHOTA_EVENT_PENDING_REBOOT"; } return "Unknown Event"; } \ No newline at end of file