From 414bc414048609d2b8d5edd21f4d9b323966bf24 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 4 Oct 2020 19:10:36 +0200 Subject: [PATCH] configmanager: enhancements for version migration --- include/configmanager.h | 3 +- include/globals.h | 5 +- include/ota.h | 4 +- src/configmanager.cpp | 104 +++++++++++++++++++++++++++------------- src/ota.cpp | 18 ------- 5 files changed, 78 insertions(+), 56 deletions(-) diff --git a/include/configmanager.h b/include/configmanager.h index e7d26438..d196e6ab 100644 --- a/include/configmanager.h +++ b/include/configmanager.h @@ -1,11 +1,12 @@ #ifndef _CONFIGMANAGER_H #define _CONFIGMANAGER_H -#include #include "globals.h" +#include void saveConfig(bool erase = false); bool loadConfig(void); void eraseConfig(void); +int version_compare(const String v1, const String v2); #endif \ No newline at end of file diff --git a/include/globals.h b/include/globals.h index ba9319ff..af963f77 100644 --- a/include/globals.h +++ b/include/globals.h @@ -60,8 +60,10 @@ enum runmode_t { }; // Struct holding devices's runtime configuration -// using packed to avoid compiler padding, because struct will be memcpy'd to byte array +// using packed to avoid compiler padding, because struct will be memcpy'd to +// byte array typedef struct __attribute__((packed)) { + char version[10]; // Firmware version uint8_t loradr; // 0-15, lora datarate uint8_t txpower; // 2-15, lora tx power uint8_t adrmode; // 0=disabled, 1=enabled @@ -80,7 +82,6 @@ typedef struct __attribute__((packed)) { uint8_t monitormode; // 0=disabled, 1=enabled uint8_t runmode; // 0=normal, 1=update uint8_t payloadmask; // bitswitches for payload data - char version[10]; // Firmware version #ifdef HAS_BME680 uint8_t bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor diff --git a/include/ota.h b/include/ota.h index 900ef530..9e53cae1 100644 --- a/include/ota.h +++ b/include/ota.h @@ -6,17 +6,15 @@ #include "globals.h" #include "led.h" #include "display.h" +#include "configmanager.h" #include #include #include #include -#include -#include int do_ota_update(); void start_ota_update(); -int version_compare(const String v1, const String v2); void ota_display(const uint8_t row, const std::string status, const std::string msg); void show_progress(unsigned long current, unsigned long size); diff --git a/src/configmanager.cpp b/src/configmanager.cpp index b74f4e0d..7c1c2dd0 100644 --- a/src/configmanager.cpp +++ b/src/configmanager.cpp @@ -16,16 +16,20 @@ static const char TAG[] = __FILE__; Preferences nvram; -static const char cfgMagicBytes[] = {0x21, 0x76, 0x87, 0x32, 0xf3}; +static const uint8_t cfgMagicBytes[] = {0x21, 0x76, 0x87, 0x32, 0xf4}; static const size_t cfgLen = sizeof(cfg), cfgLen2 = sizeof(cfgMagicBytes); -static char buffer[cfgLen + cfgLen2]; +static uint8_t buffer[cfgLen + cfgLen2]; -// populate runtime config with factory settings +// populate runtime config with device factory settings static void defaultConfig(configData_t *myconfig) { - char version[10]; - snprintf(version, 10, "%-10s", PROGVERSION); + // char version[10]; + // snprintf(version, 10, "%-10s", PROGVERSION); + // memcpy(myconfig->version, version, 10); // Firmware version [exactly 10 + // chars] + memcpy(myconfig->version, &PROGVERSION, + 10); // Firmware version [exactly 10 chars] - // factory settings + // device factory settings myconfig->loradr = LORADRDEFAULT; // 0-15, lora datarate, see paxcounter.conf myconfig->txpower = LORATXPOWDEFAULT; // 0-15, lora tx power myconfig->adrmode = 1; // 0=disabled, 1=enabled @@ -43,11 +47,10 @@ static void defaultConfig(configData_t *myconfig) { myconfig->blescan = 1; // 0=disabled, 1=enabled myconfig->wifiscan = 1; // 0=disabled, 1=enabled myconfig->wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4) - myconfig->vendorfilter = VENDORFILTER; // 0=disabled, 1=enabled - myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) - myconfig->monitormode = 0; // 0=disabled, 1=enabled - myconfig->payloadmask = PAYLOADMASK; // all payload switched on - memcpy(myconfig->version, version, 10); // Firmware version [exactly 10 chars] + myconfig->vendorfilter = VENDORFILTER; // 0=disabled, 1=enabled + myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) + myconfig->monitormode = 0; // 0=disabled, 1=enabled + myconfig->payloadmask = PAYLOADMASK; // all payload switched on #ifdef HAS_BME680 // initial BSEC state for BME680 sensor @@ -55,6 +58,15 @@ static void defaultConfig(configData_t *myconfig) { #endif } +// migrate runtime config from earlier version to current +static void migrateConfig(void) { + // currently no migration rules are implemented, we just reset config to + // factory settings + ESP_LOGI(TAG, "Migrating device configuration from %s to %s", cfg.version, + PROGVERSION); + eraseConfig(); +} + // save current configuration from RAM to NVRAM void saveConfig(bool erase) { ESP_LOGI(TAG, "Storing settings to NVRAM..."); @@ -89,30 +101,58 @@ bool loadConfig() { ESP_LOGW(TAG, "NVRAM initialized, device starts with factory settings"); eraseConfig(); return true; + } - } else { - // simple check that runtime config data matches - if (nvram.getBytesLength(DEVCONFIG) != (cfgLen + cfgLen2)) { - ESP_LOGE(TAG, "configuration invalid"); - return false; + // simple check that runtime config data matches + if (nvram.getBytesLength(DEVCONFIG) != (cfgLen + cfgLen2)) { + ESP_LOGE(TAG, "Configuration invalid"); + return false; + } - } else { - // load device runtime config from nvram and copy it to byte array - nvram.getBytes(DEVCONFIG, buffer, cfgLen + cfgLen2); - nvram.end(); - // validate configuration by checking magic bytes at end of array - if (memcmp(buffer + cfgLen, &cfgMagicBytes, cfgLen2) != 0) { - ESP_LOGW(TAG, "No configuration found"); - return false; + // load device runtime config from nvram and copy it to byte array + nvram.getBytes(DEVCONFIG, buffer, cfgLen + cfgLen2); + nvram.end(); - } else { - // copy byte array into runtime cfg struct - memcpy(&cfg, buffer, cfgLen); - ESP_LOGI(TAG, "Runtime configuration loaded"); - return true; - } - } + // validate loaded configuration by checking magic bytes at end of array + if (memcmp(buffer + cfgLen, &cfgMagicBytes, cfgLen2) != 0) { + ESP_LOGW(TAG, "No configuration found"); + return false; + } + + // copy loaded configuration into runtime cfg struct + memcpy(&cfg, buffer, cfgLen); + ESP_LOGI(TAG, "Runtime configuration v%s loaded", cfg.version); + + // check if config version matches current firmware version + switch (version_compare(PROGVERSION, cfg.version)) { + case -1: // device configuration belongs to newer than current firmware + ESP_LOGE(TAG, "Incompatible device configuration, aborting"); + return false; + case 1: // device configuration belongs to older than current firmware + ESP_LOGW(TAG, "Device was updated, migrating device configuration"); + migrateConfig(); + return true; + default: // device configuration version matches current firmware version + return true; } } -void eraseConfig(void) { saveConfig(true); } +// helper function to convert strings into lower case +bool comp(char s1, char s2) { return (tolower(s1) < tolower(s2)); } + +// helper function to lexicographically compare two versions. Returns 1 if v2 +// is smaller, -1 if v1 is smaller, 0 if equal +int version_compare(const String v1, const String v2) { + + if (v1 == v2) + return 0; + + const char *a1 = v1.c_str(), *a2 = v2.c_str(); + + if (std::lexicographical_compare(a1, a1 + strlen(a1), a2, a2 + strlen(a2), comp)) + return -1; + else + return 1; +} + +void eraseConfig(void) { saveConfig(true); } \ No newline at end of file diff --git a/src/ota.cpp b/src/ota.cpp index 95d780f8..c256559e 100644 --- a/src/ota.cpp +++ b/src/ota.cpp @@ -328,22 +328,4 @@ void show_progress(unsigned long current, unsigned long size) { #endif } -// helper function to convert strings into lower case -bool comp(char s1, char s2) { return tolower(s1) < tolower(s2); } - -// helper function to lexicographically compare two versions. Returns 1 if v2 is -// smaller, -1 if v1 is smaller, 0 if equal -int version_compare(const String v1, const String v2) { - - if (v1 == v2) - return 0; - - const char *a1 = v1.c_str(), *a2 = v2.c_str(); - - if (lexicographical_compare(a1, a1 + strlen(a1), a2, a2 + strlen(a2), comp)) - return -1; - else - return 1; -} - #endif // USE_OTA \ No newline at end of file