configmanager: enhancements for version migration

This commit is contained in:
Klaus K Wilting 2020-10-04 19:10:36 +02:00
parent 1cbf0d7124
commit 414bc41404
5 changed files with 78 additions and 56 deletions

View File

@ -1,11 +1,12 @@
#ifndef _CONFIGMANAGER_H #ifndef _CONFIGMANAGER_H
#define _CONFIGMANAGER_H #define _CONFIGMANAGER_H
#include <Preferences.h>
#include "globals.h" #include "globals.h"
#include <Preferences.h>
void saveConfig(bool erase = false); void saveConfig(bool erase = false);
bool loadConfig(void); bool loadConfig(void);
void eraseConfig(void); void eraseConfig(void);
int version_compare(const String v1, const String v2);
#endif #endif

View File

@ -60,8 +60,10 @@ enum runmode_t {
}; };
// Struct holding devices's runtime configuration // 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)) { typedef struct __attribute__((packed)) {
char version[10]; // Firmware version
uint8_t loradr; // 0-15, lora datarate uint8_t loradr; // 0-15, lora datarate
uint8_t txpower; // 2-15, lora tx power uint8_t txpower; // 2-15, lora tx power
uint8_t adrmode; // 0=disabled, 1=enabled uint8_t adrmode; // 0=disabled, 1=enabled
@ -80,7 +82,6 @@ typedef struct __attribute__((packed)) {
uint8_t monitormode; // 0=disabled, 1=enabled uint8_t monitormode; // 0=disabled, 1=enabled
uint8_t runmode; // 0=normal, 1=update uint8_t runmode; // 0=normal, 1=update
uint8_t payloadmask; // bitswitches for payload data uint8_t payloadmask; // bitswitches for payload data
char version[10]; // Firmware version
#ifdef HAS_BME680 #ifdef HAS_BME680
uint8_t uint8_t
bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor

View File

@ -6,17 +6,15 @@
#include "globals.h" #include "globals.h"
#include "led.h" #include "led.h"
#include "display.h" #include "display.h"
#include "configmanager.h"
#include <Update.h> #include <Update.h>
#include <WiFi.h> #include <WiFi.h>
#include <WiFiClientSecure.h> #include <WiFiClientSecure.h>
#include <BintrayClient.h> #include <BintrayClient.h>
#include <string>
#include <algorithm>
int do_ota_update(); int do_ota_update();
void start_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, void ota_display(const uint8_t row, const std::string status,
const std::string msg); const std::string msg);
void show_progress(unsigned long current, unsigned long size); void show_progress(unsigned long current, unsigned long size);

View File

@ -16,16 +16,20 @@ static const char TAG[] = __FILE__;
Preferences nvram; 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 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) { static void defaultConfig(configData_t *myconfig) {
char version[10]; // char version[10];
snprintf(version, 10, "%-10s", PROGVERSION); // 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->loradr = LORADRDEFAULT; // 0-15, lora datarate, see paxcounter.conf
myconfig->txpower = LORATXPOWDEFAULT; // 0-15, lora tx power myconfig->txpower = LORATXPOWDEFAULT; // 0-15, lora tx power
myconfig->adrmode = 1; // 0=disabled, 1=enabled myconfig->adrmode = 1; // 0=disabled, 1=enabled
@ -47,7 +51,6 @@ static void defaultConfig(configData_t *myconfig) {
myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
myconfig->monitormode = 0; // 0=disabled, 1=enabled myconfig->monitormode = 0; // 0=disabled, 1=enabled
myconfig->payloadmask = PAYLOADMASK; // all payload switched on myconfig->payloadmask = PAYLOADMASK; // all payload switched on
memcpy(myconfig->version, version, 10); // Firmware version [exactly 10 chars]
#ifdef HAS_BME680 #ifdef HAS_BME680
// initial BSEC state for BME680 sensor // initial BSEC state for BME680 sensor
@ -55,6 +58,15 @@ static void defaultConfig(configData_t *myconfig) {
#endif #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 // save current configuration from RAM to NVRAM
void saveConfig(bool erase) { void saveConfig(bool erase) {
ESP_LOGI(TAG, "Storing settings to NVRAM..."); ESP_LOGI(TAG, "Storing settings to NVRAM...");
@ -89,30 +101,58 @@ bool loadConfig() {
ESP_LOGW(TAG, "NVRAM initialized, device starts with factory settings"); ESP_LOGW(TAG, "NVRAM initialized, device starts with factory settings");
eraseConfig(); eraseConfig();
return true; return true;
}
} else {
// simple check that runtime config data matches // simple check that runtime config data matches
if (nvram.getBytesLength(DEVCONFIG) != (cfgLen + cfgLen2)) { if (nvram.getBytesLength(DEVCONFIG) != (cfgLen + cfgLen2)) {
ESP_LOGE(TAG, "configuration invalid"); ESP_LOGE(TAG, "Configuration invalid");
return false; return false;
}
} else {
// load device runtime config from nvram and copy it to byte array // load device runtime config from nvram and copy it to byte array
nvram.getBytes(DEVCONFIG, buffer, cfgLen + cfgLen2); nvram.getBytes(DEVCONFIG, buffer, cfgLen + cfgLen2);
nvram.end(); nvram.end();
// validate configuration by checking magic bytes at end of array
// validate loaded configuration by checking magic bytes at end of array
if (memcmp(buffer + cfgLen, &cfgMagicBytes, cfgLen2) != 0) { if (memcmp(buffer + cfgLen, &cfgMagicBytes, cfgLen2) != 0) {
ESP_LOGW(TAG, "No configuration found"); ESP_LOGW(TAG, "No configuration found");
return false; return false;
}
} else { // copy loaded configuration into runtime cfg struct
// copy byte array into runtime cfg struct
memcpy(&cfg, buffer, cfgLen); memcpy(&cfg, buffer, cfgLen);
ESP_LOGI(TAG, "Runtime configuration loaded"); 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; return 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); } void eraseConfig(void) { saveConfig(true); }

View File

@ -328,22 +328,4 @@ void show_progress(unsigned long current, unsigned long size) {
#endif #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 #endif // USE_OTA