Browse Source

minimal support for OTA updates

main
Nicolas Massé 4 years ago
parent
commit
bf0d545980
  1. 4
      .gitignore
  2. 2
      main/CMakeLists.txt
  3. 18
      main/common.c
  4. 3
      main/common.h
  5. 5
      main/main.c
  6. 20
      main/mqtt.c
  7. 94
      main/ota.c
  8. 6
      main/ota.h
  9. 3
      provision/.gitignore
  10. 6
      provision/main/Kconfig.projbuild
  11. 4
      provision/main/provision.c

4
.gitignore

@ -1,3 +1,7 @@
build build
sdkconfig sdkconfig
sdkconfig.old sdkconfig.old
# Used during development to test OTA
# See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/system.html
version.txt

2
main/CMakeLists.txt

@ -1,2 +1,2 @@
idf_component_register(SRCS "main.c" "tic.c" "wifi.c" "mqtt.c" "sntp.c" "common.c" "libteleinfo.cpp" "libteleinfo/src/LibTeleinfo.cpp" idf_component_register(SRCS "main.c" "tic.c" "wifi.c" "mqtt.c" "sntp.c" "common.c" "ota.c" "libteleinfo.cpp" "libteleinfo/src/LibTeleinfo.cpp"
INCLUDE_DIRS ".") INCLUDE_DIRS ".")

18
main/common.c

@ -1,2 +1,20 @@
#include "common.h" #include "common.h"
volatile EventGroupHandle_t services_event_group; volatile EventGroupHandle_t services_event_group;
char* get_nvs_string(nvs_handle_t nvs, char* key) {
size_t required_size;
esp_err_t err = nvs_get_str(nvs, key, NULL, &required_size);
if (err != ESP_OK) {
return NULL;
}
char* value = malloc(required_size);
if (!value) {
return NULL;
}
err = nvs_get_str(nvs, key, value, &required_size);
if (err != ESP_OK) {
free(value);
return NULL;
}
return value;
}

3
main/common.h

@ -4,12 +4,15 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/event_groups.h" #include "freertos/event_groups.h"
#include "nvs_flash.h"
#include "nvs.h"
#define WIFI_CONNECTED_BIT BIT0 #define WIFI_CONNECTED_BIT BIT0
#define MQTT_CONNECTED_BIT BIT1 #define MQTT_CONNECTED_BIT BIT1
#define TIME_SYNC_BIT BIT2 #define TIME_SYNC_BIT BIT2
extern volatile EventGroupHandle_t services_event_group; extern volatile EventGroupHandle_t services_event_group;
char* get_nvs_string(nvs_handle_t nvs, char* key);
#define WAIT_FOR(flags) while ((xEventGroupWaitBits(services_event_group, flags, pdFALSE, pdTRUE, portMAX_DELAY) & (flags)) != (flags)) {} #define WAIT_FOR(flags) while ((xEventGroupWaitBits(services_event_group, flags, pdFALSE, pdTRUE, portMAX_DELAY) & (flags)) != (flags)) {}

5
main/main.c

@ -14,12 +14,17 @@
#include "mqtt.h" #include "mqtt.h"
#include "sntp.h" #include "sntp.h"
#include "common.h" #include "common.h"
#include "esp_ota_ops.h"
#include "ota.h"
// Embedded via component.mk // Embedded via component.mk
extern const uint8_t cacert_pem_start[] asm("_binary_cacert_pem_start"); extern const uint8_t cacert_pem_start[] asm("_binary_cacert_pem_start");
extern const uint8_t cacert_pem_end[] asm("_binary_cacert_pem_end"); extern const uint8_t cacert_pem_end[] asm("_binary_cacert_pem_end");
void app_main(void) { void app_main(void) {
const esp_app_desc_t* current = esp_ota_get_app_description();
ESP_LOGI("main", "Currently running %s version %s", current->project_name, current->version);
// NVS is used to store wifi credentials. So, we need to initialize it first. // NVS is used to store wifi credentials. So, we need to initialize it first.
ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(nvs_flash_init());

20
main/mqtt.c

@ -8,8 +8,6 @@
#include "nvs_flash.h" #include "nvs_flash.h"
#include "esp_event.h" #include "esp_event.h"
#include "esp_netif.h" #include "esp_netif.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_log.h" #include "esp_log.h"
#include "mqtt_client.h" #include "mqtt_client.h"
#include "esp_tls.h" #include "esp_tls.h"
@ -119,24 +117,6 @@ void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event
mqtt_event_handler_cb(event_data); mqtt_event_handler_cb(event_data);
} }
char* get_nvs_string(nvs_handle_t nvs, char* key) {
size_t required_size;
esp_err_t err = nvs_get_str(nvs, key, NULL, &required_size);
if (err != ESP_OK) {
return NULL;
}
char* value = malloc(required_size);
if (!value) {
return NULL;
}
err = nvs_get_str(nvs, key, value, &required_size);
if (err != ESP_OK) {
free(value);
return NULL;
}
return value;
}
void mqtt_init(void) { void mqtt_init(void) {
nvs_handle_t nvs; nvs_handle_t nvs;
esp_err_t err = nvs_open("mqtt", NVS_READONLY, &nvs); esp_err_t err = nvs_open("mqtt", NVS_READONLY, &nvs);

94
main/ota.c

@ -0,0 +1,94 @@
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "esp_log.h"
#include "esp_https_ota.h"
#include "esp_ota_ops.h"
#include "ota.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "common.h"
static const char *OTA_LOGGER = "ota_update";
// Embedded via component.mk
extern const uint8_t cacert_pem_start[] asm("_binary_cacert_pem_start");
extern const uint8_t cacert_pem_end[] asm("_binary_cacert_pem_end");
esp_err_t do_firmware_upgrade(char* firmware_url) {
esp_http_client_config_t config = {
.url = firmware_url,
.cert_pem = (char *)cacert_pem_start,
};
esp_https_ota_config_t ota_config = {
// Can be enabled in an upcoming version of the Espressif SDK
// to save some memory.
// Requires CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=4096 in sdkconfig.
//
//.partial_http_download = true,
//.max_http_request_size = 4096,
.http_config = &config,
};
esp_https_ota_handle_t https_ota_handle = NULL;
esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
if (https_ota_handle == NULL) {
return ESP_FAIL;
}
while (1) {
err = esp_https_ota_perform(https_ota_handle);
if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
break;
}
}
if (err != ESP_OK) {
esp_https_ota_abort(https_ota_handle);
return err;
}
esp_err_t ota_finish_err = esp_https_ota_finish(https_ota_handle);
if (ota_finish_err != ESP_OK) {
return ota_finish_err;
}
esp_restart(); // this function never returns
return ESP_OK;
}
void trigger_ota_update(char* version) {
const esp_app_desc_t* current = esp_ota_get_app_description();
ESP_LOGD(OTA_LOGGER, "Currently running %s version %s", current->project_name, current->version);
if (strcmp(version, current->version) == 0) {
// already to latest version
return;
}
nvs_handle_t nvs;
esp_err_t err = nvs_open("ota", NVS_READONLY, &nvs);
if (err != ESP_OK) {
ESP_LOGE(OTA_LOGGER, "Error (%s) opening NVS handle!\n", esp_err_to_name(err));
return;
}
char* update_url_pattern = get_nvs_string(nvs, "update_url");
nvs_close(nvs);
if (update_url_pattern == NULL) {
return;
}
const size_t buffer_size = 256;
char update_url[buffer_size];
// Format the update URL
if (!snprintf(update_url, buffer_size, update_url_pattern, version)) {
ESP_LOGD(OTA_LOGGER, "trigger_ota_update: snprintf failed!");
free(update_url_pattern);
return;
}
esp_err_t ret = do_firmware_upgrade(update_url);
ESP_LOGW(OTA_LOGGER, "do_firmware_upgrade failed with error %d", ret);
}

6
main/ota.h

@ -0,0 +1,6 @@
#ifndef __OTA_H__
#define __OTA_H__
void trigger_ota_update(char* version);
#endif

3
provision/.gitignore

@ -1,2 +1,5 @@
# contains sensitive data # contains sensitive data
sdkconfig sdkconfig
config.dev
config.prod
sdkconfig.defaults

6
provision/main/Kconfig.projbuild

@ -36,4 +36,10 @@ menu "Provisioning data"
help help
MQTT password. MQTT password.
config FIRMWARE_URL_PATTERN
string "Firmware URL pattern"
default ""
help
Where to download a specific version of the firmware. Complete URL. Must include "%s".
endmenu endmenu

4
provision/main/provision.c

@ -135,6 +135,10 @@ void wifi_init_sta(void)
nvs_set_str(nvs, "password", CONFIG_MQTT_PASSWORD); nvs_set_str(nvs, "password", CONFIG_MQTT_PASSWORD);
nvs_close(nvs); nvs_close(nvs);
ESP_ERROR_CHECK(nvs_open("ota", NVS_READWRITE, &nvs));
nvs_set_str(nvs, "update_url", CONFIG_FIRMWARE_URL_PATTERN);
nvs_close(nvs);
nvs_dumpall(); nvs_dumpall();
/* The event will not be processed after unregister */ /* The event will not be processed after unregister */

Loading…
Cancel
Save