mirror of
https://github.com/Fishwaldo/esp_ghota.git
synced 2025-03-15 19:31:37 +00:00
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:
parent
e1f297b16c
commit
335b33062b
4 changed files with 328 additions and 224 deletions
7
.github/workflows/main.yml
vendored
7
.github/workflows/main.yml
vendored
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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":
|
||||
|
|
355
src/esp_ghota.c
355
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,14 +248,17 @@ 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) {
|
||||
#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) {
|
||||
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)
|
||||
|
@ -266,13 +290,14 @@ static esp_err_t _http_event_handler(esp_http_client_event_t *evt)
|
|||
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:");
|
||||
|
@ -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: {
|
||||
#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)) {
|
||||
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)
|
||||
|
@ -450,37 +488,43 @@ esp_err_t _http_event_storage_handler(esp_http_client_event_t *evt) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
#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) {
|
||||
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 };
|
||||
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:
|
||||
|
|
Loading…
Add table
Reference in a new issue