Fix Compiles on ESP-IDF versions > 4.4 (#8)

* fixes for esp-idf > version 5

* Test builds on different versions of esp-idf in github CI
This commit is contained in:
Justin Hammond 2024-02-17 23:19:30 +08:00 committed by GitHub
parent e1f297b16c
commit 335b33062b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 328 additions and 224 deletions

View file

@ -12,16 +12,17 @@ jobs:
fail-fast: true fail-fast: true
matrix: matrix:
targets: [esp32, esp32s3] targets: [esp32, esp32s3]
espidf: [v4.4.6, v5.0.5, v5.1.2, v5.2]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
submodules: 'recursive' submodules: 'recursive'
- name: esp-idf build - name: esp-idf build
uses: Fishwaldo/esp-idf-ci-action@v1.2 uses: espressif/esp-idf-ci-action@v1
with: with:
esp_idf_version: v4.4.3 esp_idf_version: ${{ matrix.espidf }}
target: ${{ matrix.targets }} target: ${{ matrix.targets }}
path: 'examples/esp_ghota_example' path: 'examples/esp_ghota_example'
- name: Rename artifact - name: Rename artifact

View file

@ -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: ### esp-idf via Espressif Component Registry:
```bash ```bash
idf.py add-dependency Fishwaldo/ghota^0.0.1 idf.py add-dependency Fishwaldo/ghota^1.0.0
``` ```
#### Platform IO Registry: #### Platform IO Registry:
@ -34,7 +34,7 @@ add this to your platform.ini file:
```ini ```ini
lib_deps = 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. 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.

View file

@ -1,6 +1,6 @@
{ {
"name": "esp_ghota", "name": "esp_ghota",
"version": "0.0.1", "version": "1.0.0",
"description": "a OTA library to upgrade your firmware via the Github Releases API", "description": "a OTA library to upgrade your firmware via the Github Releases API",
"keywords": "esp32 ota github", "keywords": "esp32 ota github",
"repository": "repository":

View file

@ -21,18 +21,26 @@ static const char *TAG = "GHOTA";
ESP_EVENT_DEFINE_BASE(GHOTA_EVENTS); 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; ghota_config_t config;
char *username; char *username;
char *token; char *token;
struct { struct
{
char tag_name[CONFIG_MAX_FILENAME_LEN]; char tag_name[CONFIG_MAX_FILENAME_LEN];
char name[CONFIG_MAX_FILENAME_LEN]; char name[CONFIG_MAX_FILENAME_LEN];
char url[CONFIG_MAX_URL_LEN]; char url[CONFIG_MAX_URL_LEN];
char storageurl[CONFIG_MAX_URL_LEN]; char storageurl[CONFIG_MAX_URL_LEN];
uint8_t flags; uint8_t flags;
} result; } result;
struct { struct
{
char name[CONFIG_MAX_FILENAME_LEN]; char name[CONFIG_MAX_FILENAME_LEN];
char url[CONFIG_MAX_URL_LEN]; char url[CONFIG_MAX_URL_LEN];
} scratch; } scratch;
@ -43,7 +51,6 @@ typedef struct ghota_client_handle_t {
const esp_partition_t *storage_partition; const esp_partition_t *storage_partition;
} ghota_client_handle_t; } ghota_client_handle_t;
enum release_flags enum release_flags
{ {
GHOTA_RELEASE_GOT_TAG = 0x01, GHOTA_RELEASE_GOT_TAG = 0x01,
@ -55,7 +62,6 @@ enum release_flags
SemaphoreHandle_t ghota_lock = NULL; SemaphoreHandle_t ghota_lock = NULL;
static void SetFlag(ghota_client_handle_t *handle, enum release_flags flag) static void SetFlag(ghota_client_handle_t *handle, enum release_flags flag)
{ {
handle->result.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; handle->result.flags &= ~flag;
} }
ghota_client_handle_t *ghota_init(ghota_config_t *newconfig) { ghota_client_handle_t *ghota_init(ghota_config_t *newconfig)
if (!ghota_lock) { {
if (!ghota_lock)
{
ghota_lock = xSemaphoreCreateMutex(); 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"); ESP_LOGE(TAG, "Failed to take lock");
return NULL; return NULL;
} }
ghota_client_handle_t *handle = malloc(sizeof(ghota_client_handle_t)); 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"); ESP_LOGE(TAG, "Failed to allocate memory for client handle");
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
return NULL; return NULL;
@ -102,9 +112,13 @@ ghota_client_handle_t *ghota_init(ghota_config_t *newconfig) {
asprintf(&handle->config.reponame, CONFIG_GITHUB_REPO); asprintf(&handle->config.reponame, CONFIG_GITHUB_REPO);
else else
asprintf(&handle->config.reponame, newconfig->reponame); 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(); 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"); ESP_LOGE(TAG, "Failed to parse current version");
ghota_free(handle); ghota_free(handle);
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
@ -112,10 +126,10 @@ ghota_client_handle_t *ghota_init(ghota_config_t *newconfig) {
} }
handle->result.flags = 0; handle->result.flags = 0;
// if (newconfig->updateInterval < 60) { // if (newconfig->updateInterval < 60) {
// ESP_LOGE(TAG, "Update interval must be at least 60 Minutes"); // ESP_LOGE(TAG, "Update interval must be at least 60 Minutes");
// newconfig->updateInterval = 60; // newconfig->updateInterval = 60;
// } // }
handle->config.updateInterval = newconfig->updateInterval; handle->config.updateInterval = newconfig->updateInterval;
handle->task_handle = NULL; handle->task_handle = NULL;
@ -124,8 +138,10 @@ ghota_client_handle_t *ghota_init(ghota_config_t *newconfig) {
return handle; return handle;
} }
esp_err_t ghota_free(ghota_client_handle_t *handle) { esp_err_t ghota_free(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 take lock"); ESP_LOGE(TAG, "Failed to take lock");
return ESP_FAIL; return ESP_FAIL;
} }
@ -142,8 +158,10 @@ esp_err_t ghota_free(ghota_client_handle_t *handle) {
return ESP_OK; return ESP_OK;
} }
esp_err_t ghota_set_auth(ghota_client_handle_t *handle, const char *username, const char *password) { 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) { {
if (xSemaphoreTake(ghota_lock, pdMS_TO_TICKS(1000)) != pdPASS)
{
ESP_LOGE(TAG, "Failed to take lock"); ESP_LOGE(TAG, "Failed to take lock");
return ESP_FAIL; 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; return ESP_OK;
} }
static void lwjson_callback(lwjson_stream_parser_t *jsp, lwjson_stream_type_t type) 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"); ESP_LOGE(TAG, "No user data for callback");
return; return;
} }
@ -169,7 +187,8 @@ static void lwjson_callback(lwjson_stream_parser_t *jsp, lwjson_stream_type_t ty
} }
#endif #endif
/* Get a value corresponsing to "tag_name" key */ /* 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 */ 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[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */
&& jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ && 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); SetFlag(handle, GHOTA_RELEASE_GOT_TAG);
} }
} }
if (!GetFlag(handle, GHOTA_RELEASE_VALID_ASSET) || !GetFlag(handle, GHOTA_RELEASE_GOT_STORAGE)) { 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 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)
&& 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); 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) 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); ESP_LOGD(TAG, "Got URL for Asset: %s", handle->scratch.url);
} }
/* Now test if we got both name an download 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); 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 */ /* 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.name, handle->scratch.name, CONFIG_MAX_FILENAME_LEN);
strncpy(handle->result.url, handle->scratch.url, CONFIG_MAX_URL_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); ESP_LOGD(TAG, "Valid Firmware Found: %s - %s", handle->result.name, handle->result.url);
SetFlag(handle, GHOTA_RELEASE_VALID_ASSET); 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); 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); ESP_LOGD(TAG, "Valid Storage Asset Found: %s - %s", handle->scratch.name, handle->result.storageurl);
SetFlag(handle, GHOTA_RELEASE_GOT_STORAGE); SetFlag(handle, GHOTA_RELEASE_GOT_STORAGE);
} else { }
else
{
ESP_LOGD(TAG, "Invalid Asset Found: %s", handle->scratch.name); ESP_LOGD(TAG, "Invalid Asset Found: %s", handle->scratch.name);
ClearFlag(handle, GHOTA_RELEASE_GOT_FNAME); ClearFlag(handle, GHOTA_RELEASE_GOT_FNAME);
ClearFlag(handle, GHOTA_RELEASE_GOT_URL); 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) static esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{ {
lwjsonr_t res; lwjsonr_t res;
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch" #pragma GCC diagnostic ignored "-Wswitch"
switch (evt->event_id) { switch (evt->event_id)
case HTTP_EVENT_ON_HEADER: {
if (strncasecmp(evt->header_key, "x-ratelimit-remaining", strlen("x-ratelimit-remaining")) == 0) { case HTTP_EVENT_ON_HEADER:
int limit = atoi(evt->header_value); if (strncasecmp(evt->header_key, "x-ratelimit-remaining", strlen("x-ratelimit-remaining")) == 0)
ESP_LOGD(TAG, "Github API Rate Limit Remaining: %d", limit); {
if (limit < 10) { int limit = atoi(evt->header_value);
ESP_LOGW(TAG, "Github API Rate Limit Remaining is low: %d", limit); ESP_LOGD(TAG, "Github API Rate Limit Remaining: %d", limit);
} if (limit < 10)
}
break;
case HTTP_EVENT_ON_DATA:
if (!esp_http_client_is_chunked_response(evt->client))
{ {
char *buf = evt->data; ESP_LOGW(TAG, "Github API Rate Limit Remaining is low: %d", limit);
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;
} }
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; return ESP_OK;
} }
esp_err_t ghota_check(ghota_client_handle_t *handle) 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"); ESP_LOGE(TAG, "Failed to get lock");
return ESP_FAIL; return ESP_FAIL;
} }
@ -289,7 +314,7 @@ esp_err_t ghota_check(ghota_client_handle_t *handle)
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
return ESP_FAIL; return ESP_FAIL;
} }
stream_parser.udata = (void*)handle; stream_parser.udata = (void *)handle;
char url[CONFIG_MAX_URL_LEN]; 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); 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, .event_handler = _http_event_handler,
.user_data = &stream_parser, .user_data = &stream_parser,
}; };
if (handle->username) { if (handle->username)
{
ESP_LOGD(TAG, "Using Authenticated Request to %s", url); ESP_LOGD(TAG, "Using Authenticated Request to %s", url);
httpconfig.username = handle->username; httpconfig.username = handle->username;
httpconfig.password = handle->token; 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); esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) 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_status_code(client),
esp_http_client_get_content_length(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)); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_NOUPDATE_AVAILABLE, handle, sizeof(ghota_client_handle_t *), portMAX_DELAY));
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
return ESP_FAIL; return ESP_FAIL;
} }
if (esp_http_client_get_status_code(client) == 200) if (esp_http_client_get_status_code(client) == 200)
{ {
if (GetFlag(handle, GHOTA_RELEASE_VALID_ASSET)) 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_LOGE(TAG, "Failed to parse new version");
esp_http_client_cleanup(client); 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)); 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, "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, "Asset: %s", handle->result.name);
ESP_LOGI(TAG, "Firmware URL: %s", handle->result.url); 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); ESP_LOGI(TAG, "Storage URL: %s", handle->result.storageurl);
} }
} else { }
else
{
ESP_LOGI(TAG, "Asset: No Valid Firmware Assets Found"); ESP_LOGI(TAG, "Asset: No Valid Firmware Assets Found");
esp_http_client_cleanup(client); 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)); 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) 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; return ESP_ERR_INVALID_ARG;
} }
ESP_LOGI(TAG, "New Firmware Details:"); 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, "ESP-IDF: %s", new_app_info->idf_ver);
ESP_LOGI(TAG, "SHA256:"); ESP_LOGI(TAG, "SHA256:");
ESP_LOG_BUFFER_HEX(TAG, new_app_info->app_elf_sha256, sizeof(new_app_info->app_elf_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(); 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); running->label, running->type, running->subtype, running->address);
const esp_partition_t *update = esp_ota_get_next_update_partition(NULL); 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); update->label, update->type, update->subtype, update->address);
#ifdef CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK #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. * esp_https_ota_finish at the end of OTA update procedure.
*/ */
const uint32_t hw_sec_version = esp_efuse_read_secure_version(); 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); 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; 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"); 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 output_pos;
static int last_progress; static int last_progress;
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch" #pragma GCC diagnostic ignored "-Wswitch"
switch (evt->event_id) { switch (evt->event_id)
case HTTP_EVENT_ON_CONNECTED: { {
output_pos = 0; case HTTP_EVENT_ON_CONNECTED:
last_progress = 0; {
/* Erase the Partition */ output_pos = 0;
break; last_progress = 0;
} /* Erase the Partition */
case HTTP_EVENT_ON_DATA: break;
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 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; return ESP_OK;
} }
esp_err_t ghota_storage_update(ghota_client_handle_t *handle) { esp_err_t ghota_storage_update(ghota_client_handle_t *handle)
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"); ESP_LOGE(TAG, "Failed to take lock");
return ESP_FAIL; return ESP_FAIL;
} }
if (handle == NULL) { if (handle == NULL)
{
ESP_LOGE(TAG, "Invalid Handle"); ESP_LOGE(TAG, "Invalid Handle");
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
if (!strlen(handle->result.storageurl)) { if (!strlen(handle->result.storageurl))
{
ESP_LOGE(TAG, "No Storage URL"); ESP_LOGE(TAG, "No Storage URL");
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
return ESP_FAIL; return ESP_FAIL;
} }
if (!strlen(handle->config.storagepartitionname)) { if (!strlen(handle->config.storagepartitionname))
{
ESP_LOGE(TAG, "No Storage Partition Name"); ESP_LOGE(TAG, "No Storage Partition Name");
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
return ESP_FAIL; return ESP_FAIL;
} }
handle->storage_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, handle->config.storagepartitionname); handle->storage_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, handle->config.storagepartitionname);
if (handle->storage_partition == NULL) { if (handle->storage_partition == NULL)
{
ESP_LOGE(TAG, "Storage Partition Not Found"); ESP_LOGE(TAG, "Storage Partition Not Found");
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
return ESP_FAIL; 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)); 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 */ /* give time for the system to react, such as unmounting the filesystems etc */
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
@ -493,7 +537,8 @@ esp_err_t ghota_storage_update(ghota_client_handle_t *handle) {
.buffer_size_tx = 2048, .buffer_size_tx = 2048,
}; };
if (handle->username) { if (handle->username)
{
ESP_LOGD(TAG, "Using Authenticated Request to %s", config.url); ESP_LOGD(TAG, "Using Authenticated Request to %s", config.url);
config.username = handle->username; config.username = handle->username;
config.password = handle->token; 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_http_client_handle_t client = esp_http_client_init(&config);
ESP_ERROR_CHECK(esp_http_client_set_header(client, "Accept", "application/octet-stream")); ESP_ERROR_CHECK(esp_http_client_set_header(client, "Accept", "application/octet-stream"));
esp_err_t err = esp_http_client_perform(client); esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) { if (err == ESP_OK)
ESP_LOGD(TAG, "HTTP GET Status = %d, content_length = %d", {
esp_http_client_get_status_code(client), ESP_LOGD(TAG, "HTTP GET Status = %d, content_length = %" PRICONTENT_LENGTH,
esp_http_client_get_content_length(client)); esp_http_client_get_status_code(client),
uint8_t sha256[32] = { 0 }; esp_http_client_get_content_length(client));
uint8_t sha256[32] = {0};
ESP_ERROR_CHECK(esp_partition_get_sha256(handle->storage_partition, sha256)); ESP_ERROR_CHECK(esp_partition_get_sha256(handle->storage_partition, sha256));
ESP_LOG_BUFFER_HEX("New Storage Partition SHA256:", sha256, sizeof(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)); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_FINISH_STORAGE_UPDATE, NULL, 0, portMAX_DELAY));
} else { }
else
{
ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err)); 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); esp_http_client_cleanup(client);
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
return ESP_OK; 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; 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"); ESP_LOGE(TAG, "Failed to take lock");
return ESP_FAIL; return ESP_FAIL;
} }
ESP_LOGI(TAG, "Scheduled Check for Firmware Update Starting"); 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)); 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_LOGE(TAG, "No Valid Release Asset Found");
ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_UPDATE_FAILED, NULL, 0, portMAX_DELAY)); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_UPDATE_FAILED, NULL, 0, portMAX_DELAY));
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
return ESP_FAIL; return ESP_FAIL;
} }
int cmp = semver_compare_version(handle->latest_version, handle->current_version); 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_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)); ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_UPDATE_FAILED, NULL, 0, portMAX_DELAY));
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
@ -549,7 +599,8 @@ esp_err_t ghota_update(ghota_client_handle_t *handle) {
.buffer_size_tx = 4096, .buffer_size_tx = 4096,
}; };
if (handle->username) { if (handle->username)
{
ESP_LOGD(TAG, "Using Authenticated Request to %s", httpconfig.url); ESP_LOGD(TAG, "Using Authenticated Request to %s", httpconfig.url);
httpconfig.username = handle->username; httpconfig.username = handle->username;
httpconfig.password = handle->token; 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 = { esp_https_ota_config_t ota_config = {
.http_config = &httpconfig, .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_https_ota_handle_t https_ota_handle = NULL;
esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle); 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); ESP_LOGE(TAG, "ESP HTTPS OTA Begin failed: %d", err);
goto ota_end; goto ota_end;
} }
esp_app_desc_t app_desc; esp_app_desc_t app_desc;
err = esp_https_ota_get_img_desc(https_ota_handle, &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); ESP_LOGE(TAG, "esp_https_ota_read_img_desc failed: %d", err);
goto ota_end; goto ota_end;
} }
err = validate_image_header(&app_desc); err = validate_image_header(&app_desc);
if (err != ESP_OK) { if (err != ESP_OK)
{
ESP_LOGE(TAG, "image header verification failed: %d", err); ESP_LOGE(TAG, "image header verification failed: %d", err);
goto ota_end; goto ota_end;
} }
int last_progress = -1; int last_progress = -1;
while (1) { while (1)
{
err = esp_https_ota_perform(https_ota_handle); 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; break;
} }
int32_t dl = esp_https_ota_get_image_len_read(https_ota_handle); 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); int32_t size = esp_https_ota_get_image_size(https_ota_handle);
int progress = 100 * ((float)dl / (float)size); 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_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); ESP_LOGV(TAG, "Firmware Update Progress: %d%%", progress);
last_progress = 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. // the OTA image was not completely received and user can customise the response to this situation.
ESP_LOGE(TAG, "Complete data was not received."); ESP_LOGE(TAG, "Complete data was not received.");
} else { }
else
{
ota_finish_err = esp_https_ota_finish(https_ota_handle); 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)); 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); xSemaphoreGive(ghota_lock);
if (ghota_storage_update(handle) == ESP_OK) { if (ghota_storage_update(handle) == ESP_OK)
{
ESP_LOGI(TAG, "Storage Update Successful"); ESP_LOGI(TAG, "Storage Update Successful");
} else { }
else
{
ESP_LOGE(TAG, "Storage Update Failed"); ESP_LOGE(TAG, "Storage Update Failed");
} }
} else { }
else
{
xSemaphoreGive(ghota_lock); xSemaphoreGive(ghota_lock);
} }
ESP_LOGI(TAG, "ESP_HTTPS_OTA upgrade successful. Rebooting ..."); 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); vTaskDelay(1000 / portTICK_PERIOD_MS);
esp_restart(); esp_restart();
return ESP_OK; 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, "Image validation failed, image is corrupted");
} }
ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed 0x%x", ota_finish_err); ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed 0x%x", ota_finish_err);
@ -636,8 +706,10 @@ ota_end:
return ESP_FAIL; return ESP_FAIL;
} }
semver_t *ghota_get_current_version(ghota_client_handle_t *handle) { semver_t *ghota_get_current_version(ghota_client_handle_t *handle)
if (!handle) { {
if (!handle)
{
return NULL; return NULL;
} }
semver_t *cur = malloc(sizeof(semver_t)); semver_t *cur = malloc(sizeof(semver_t));
@ -645,11 +717,14 @@ semver_t *ghota_get_current_version(ghota_client_handle_t *handle) {
return cur; return cur;
} }
semver_t *ghota_get_latest_version(ghota_client_handle_t *handle) { semver_t *ghota_get_latest_version(ghota_client_handle_t *handle)
if (!handle) { {
if (!handle)
{
return NULL; return NULL;
} }
if (!GetFlag(handle, GHOTA_RELEASE_VALID_ASSET)) { if (!GetFlag(handle, GHOTA_RELEASE_VALID_ASSET))
{
return NULL; return NULL;
} }
semver_t *new = malloc(sizeof(semver_t)); semver_t *new = malloc(sizeof(semver_t));
@ -657,19 +732,27 @@ semver_t *ghota_get_latest_version(ghota_client_handle_t *handle) {
return new; return new;
} }
static void ghota_task(void *pvParameters) { static void ghota_task(void *pvParameters)
{
ghota_client_handle_t *handle = (ghota_client_handle_t *)pvParameters; ghota_client_handle_t *handle = (ghota_client_handle_t *)pvParameters;
ESP_LOGI(TAG, "Firmware Update Task Starting"); ESP_LOGI(TAG, "Firmware Update Task Starting");
if (handle) { if (handle)
if (ghota_check(handle) == ESP_OK) { {
if (semver_gt(handle->latest_version, handle->current_version) == 1) { if (ghota_check(handle) == ESP_OK)
{
if (semver_gt(handle->latest_version, handle->current_version) == 1)
{
ESP_LOGI(TAG, "New Version Available"); ESP_LOGI(TAG, "New Version Available");
ghota_update(handle); ghota_update(handle);
} else { }
else
{
ESP_LOGI(TAG, "No New Version Available"); ESP_LOGI(TAG, "No New Version Available");
ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_NOUPDATE_AVAILABLE, NULL, 0, portMAX_DELAY)); 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_LOGI(TAG, "No Update Available");
ESP_ERROR_CHECK(esp_event_post(GHOTA_EVENTS, GHOTA_EVENT_NOUPDATE_AVAILABLE, NULL, 0, portMAX_DELAY)); 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; handle->task_handle = NULL;
} }
esp_err_t ghota_start_update_task(ghota_client_handle_t *handle) { esp_err_t ghota_start_update_task(ghota_client_handle_t *handle)
if (!handle) { {
if (!handle)
{
return ESP_FAIL; return ESP_FAIL;
} }
eTaskState state = eInvalid; eTaskState state = eInvalid;
TaskHandle_t tmp = xTaskGetHandle("ghota_task"); TaskHandle_t tmp = xTaskGetHandle("ghota_task");
if (tmp) { if (tmp)
{
state = eTaskGetState(tmp); state = eTaskGetState(tmp);
} }
if (state == eDeleted || state == eInvalid) { if (state == eDeleted || state == eInvalid)
{
ESP_LOGD(TAG, "Starting Task to Check for Updates"); 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"); ESP_LOGW(TAG, "Failed to Start ghota_task");
return ESP_FAIL; return ESP_FAIL;
} }
} else { }
else
{
ESP_LOGW(TAG, "ghota_task Already Running"); ESP_LOGW(TAG, "ghota_task Already Running");
return ESP_FAIL; return ESP_FAIL;
} }
return ESP_OK; 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); ghota_client_handle_t *handle = (ghota_client_handle_t *)pvTimerGetTimerID(xTimer);
if (handle) { if (handle)
{
handle->countdown--; handle->countdown--;
if (handle->countdown == 0) { if (handle->countdown == 0)
{
handle->countdown = handle->config.updateInterval; handle->countdown = handle->config.updateInterval;
ghota_start_update_task(handle); ghota_start_update_task(handle);
} }
} }
} }
esp_err_t ghota_start_update_timer(ghota_client_handle_t *handle) { esp_err_t ghota_start_update_timer(ghota_client_handle_t *handle)
if (!handle) { {
if (!handle)
{
ESP_LOGE(TAG, "Failed to initialize GHOTA Client"); ESP_LOGE(TAG, "Failed to initialize GHOTA Client");
return ESP_FAIL; return ESP_FAIL;
} }
@ -723,46 +818,54 @@ esp_err_t ghota_start_update_timer(ghota_client_handle_t *handle) {
/* run timer every minute */ /* run timer every minute */
uint64_t ticks = pdMS_TO_TICKS(1000) * 60; uint64_t ticks = pdMS_TO_TICKS(1000) * 60;
TimerHandle_t timer = xTimerCreate("ghota_timer", ticks, pdTRUE, (void *)handle, ghota_timer_callback); 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"); ESP_LOGE(TAG, "Failed to create timer");
return ESP_FAIL; return ESP_FAIL;
} else { }
if (xTimerStart(timer, 0) != pdPASS) { else
{
if (xTimerStart(timer, 0) != pdPASS)
{
ESP_LOGE(TAG, "Failed to start timer"); ESP_LOGE(TAG, "Failed to start timer");
return ESP_FAIL; 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; return ESP_OK;
} }
char *ghota_get_event_str(ghota_event_e event) { char *ghota_get_event_str(ghota_event_e event)
switch (event) { {
case GHOTA_EVENT_START_CHECK: switch (event)
return "GHOTA_EVENT_START_CHECK"; {
case GHOTA_EVENT_UPDATE_AVAILABLE: case GHOTA_EVENT_START_CHECK:
return "GHOTA_EVENT_UPDATE_AVAILABLE"; return "GHOTA_EVENT_START_CHECK";
case GHOTA_EVENT_NOUPDATE_AVAILABLE: case GHOTA_EVENT_UPDATE_AVAILABLE:
return "GHOTA_EVENT_NOUPDATE_AVAILABLE"; return "GHOTA_EVENT_UPDATE_AVAILABLE";
case GHOTA_EVENT_START_UPDATE: case GHOTA_EVENT_NOUPDATE_AVAILABLE:
return "GHOTA_EVENT_START_UPDATE"; return "GHOTA_EVENT_NOUPDATE_AVAILABLE";
case GHOTA_EVENT_FINISH_UPDATE: case GHOTA_EVENT_START_UPDATE:
return "GHOTA_EVENT_FINISH_UPDATE"; return "GHOTA_EVENT_START_UPDATE";
case GHOTA_EVENT_UPDATE_FAILED: case GHOTA_EVENT_FINISH_UPDATE:
return "GHOTA_EVENT_UPDATE_FAILED"; return "GHOTA_EVENT_FINISH_UPDATE";
case GHOTA_EVENT_START_STORAGE_UPDATE: case GHOTA_EVENT_UPDATE_FAILED:
return "GHOTA_EVENT_START_STORAGE_UPDATE"; return "GHOTA_EVENT_UPDATE_FAILED";
case GHOTA_EVENT_FINISH_STORAGE_UPDATE: case GHOTA_EVENT_START_STORAGE_UPDATE:
return "GHOTA_EVENT_FINISH_STORAGE_UPDATE"; return "GHOTA_EVENT_START_STORAGE_UPDATE";
case GHOTA_EVENT_STORAGE_UPDATE_FAILED: case GHOTA_EVENT_FINISH_STORAGE_UPDATE:
return "GHOTA_EVENT_STORAGE_UPDATE_FAILED"; return "GHOTA_EVENT_FINISH_STORAGE_UPDATE";
case GHOTA_EVENT_FIRMWARE_UPDATE_PROGRESS: case GHOTA_EVENT_STORAGE_UPDATE_FAILED:
return "GHOTA_EVENT_FIRMWARE_UPDATE_PROGRESS"; return "GHOTA_EVENT_STORAGE_UPDATE_FAILED";
case GHOTA_EVENT_STORAGE_UPDATE_PROGRESS: case GHOTA_EVENT_FIRMWARE_UPDATE_PROGRESS:
return "GHOTA_EVENT_STORAGE_UPDATE_PROGRESS"; return "GHOTA_EVENT_FIRMWARE_UPDATE_PROGRESS";
case GHOTA_EVENT_PENDING_REBOOT: case GHOTA_EVENT_STORAGE_UPDATE_PROGRESS:
return "GHOTA_EVENT_PENDING_REBOOT"; return "GHOTA_EVENT_STORAGE_UPDATE_PROGRESS";
case GHOTA_EVENT_PENDING_REBOOT:
return "GHOTA_EVENT_PENDING_REBOOT";
} }
return "Unknown Event"; return "Unknown Event";
} }