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
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

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:
```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.

View file

@ -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":

View file

@ -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);
@ -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);
@ -230,12 +250,15 @@ 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) {
switch (evt->event_id)
{
case HTTP_EVENT_ON_HEADER:
if (strncasecmp(evt->header_key, "x-ratelimit-remaining", strlen("x-ratelimit-remaining")) == 0) {
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) {
if (limit < 10)
{
ESP_LOGW(TAG, "Github API Rate Limit Remaining is low: %d", limit);
}
}
@ -255,7 +278,8 @@ static esp_err_t _http_event_handler(esp_http_client_event_t *evt)
}
}
break;
case HTTP_EVENT_DISCONNECTED: {
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)
@ -272,7 +296,8 @@ static esp_err_t _http_event_handler(esp_http_client_event_t *evt)
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;
}
@ -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:");
@ -381,10 +411,10 @@ static esp_err_t validate_image_header(esp_app_desc_t *new_app_info)
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,22 +439,27 @@ 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: {
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)) {
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) {
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");
@ -432,14 +468,16 @@ esp_err_t _http_event_storage_handler(esp_http_client_event_t *evt) {
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)) {
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: {
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)
@ -454,33 +492,39 @@ esp_err_t _http_event_storage_handler(esp_http_client_event_t *evt) {
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) {
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",
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 {
}
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_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;
@ -563,53 +614,69 @@ esp_err_t ghota_update(ghota_client_handle_t *handle) {
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;
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,22 +818,30 @@ 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) {
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: