Skip to content

Store the config.json data from ThingerESP32WebConfig.h in NVS using Preferences.h #4

@georgevbsantiago

Description

@georgevbsantiago

Store the config.json data from ThingerESP32WebConfig.h in NVS using Preferences.h

The ThingerESP32WebConfig.h code uses the SPIFFS file system, which is obsolete in favor of LITTLEFS.
However, ensure greater security and isolation by storing the data in NVS using the Preferences.h library.

The data currently stored in config.json can be in an isolated namespace from NVS, ensuring greater security for the stored information.

When saving data to SPIFFS or LITTLEFS, other Steck APIs may format the file system partition.

Code suggestion developed by ChatGPT (considering the Arduino-Thinger library), which stores the config.json data in NVS and also enables data migration in case of firmware updates, ensuring data migration from LittleFS (I use LittleFS on my Steck) to NVS.

Perhaps a new approach to the IOTMP-Arduino library is needed to migrate data from SPIFFS to NVS:

#include <Preferences.h>
#include <LittleFS.h>
#include "ThingerESP32.h"
#include "ThingerWebConfig.h"

#ifndef FORMAT_LITTLEFS_IF_FAILED
#define FORMAT_LITTLEFS_IF_FAILED true
#endif

#define NVS_WIFI_WEBCONFIG_NAMESPACE "thinger"
#define NVS_WIFI_WEBCONFIG_KEY "config"

class pson_nvs_decoder : public protoson::pson_decoder{
public:
    pson_nvs_decoder(const uint8_t* buffer, size_t size) : buffer_(buffer), size_(size), offset_(0)
    {
    }

protected:
    virtual bool read(void* buffer, size_t size){
        if(buffer_ == NULL || offset_ + size > size_) return false;
        memcpy(buffer, buffer_ + offset_, size);
        offset_ += size;
        protoson::pson_decoder::read(buffer, size);
        return true;
    }

private:
    const uint8_t* buffer_;
    size_t size_;
    size_t offset_;
};

class pson_nvs_encoder : public protoson::pson_encoder{
public:
    pson_nvs_encoder(uint8_t* buffer, size_t size) : buffer_(buffer), size_(size), offset_(0)
    {
    }

protected:
    virtual bool write(const void* buffer, size_t size){
        if(buffer_ == NULL || offset_ + size > size_) return false;
        memcpy(buffer_ + offset_, buffer, size);
        offset_ += size;
        protoson::pson_encoder::write(buffer, size);
        return true;
    }

private:
    uint8_t* buffer_;
    size_t size_;
    size_t offset_;
};

class pson_legacy_littlefs_decoder : public protoson::pson_decoder{
public:
    pson_legacy_littlefs_decoder(File& file) : file_(file)
    {
    }

protected:
    virtual bool read(void* buffer, size_t size){
        size_t read = file_.readBytes((char*)buffer, size);
        protoson::pson_decoder::read(buffer, read);
        return read == size;
    }

private:
    File& file_;
};

class ThingerESP32WebConfig : public ThingerWebConfig<ThingerESP32>{

    public:
    ThingerESP32WebConfig(const char* user="", const char* device="", const char* credential="") :
    ThingerWebConfig<ThingerESP32>(user, device, credential) {

    }

     bool clean_credentials() override{
        THINGER_DEBUG("_CONFIG", "Cleaning credentials...");
        bool removed = false;

        Preferences preferences;
        if(preferences.begin(NVS_WIFI_WEBCONFIG_NAMESPACE, false)) {
            THINGER_DEBUG("_CONFIG", "NVS Opened!");
            if (preferences.isKey(NVS_WIFI_WEBCONFIG_KEY)) {
                THINGER_DEBUG("_CONFIG", "Removing Config Key...");
                removed = preferences.remove(NVS_WIFI_WEBCONFIG_KEY) || removed;
                preferences.end();
                if(removed){
                    THINGER_DEBUG("_CONFIG", "Config key removed!");
                }else{
                    THINGER_DEBUG("_CONFIG", "Cannot delete config key!");
                }
            }else{
                THINGER_DEBUG("_CONFIG", "No config key to delete!");
                preferences.end();
            }
        } else {
            THINGER_DEBUG("_CONFIG", "Failed to Open NVS!");
        }

        if(LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)) {
            THINGER_DEBUG("_CONFIG", "Legacy LittleFS Mounted!");
            if (LittleFS.exists(CONFIG_FILE)) {
                THINGER_DEBUG("_CONFIG", "Removing Legacy Config File...");
                if(LittleFS.remove(CONFIG_FILE)){
                    THINGER_DEBUG("_CONFIG", "Legacy config file removed!");
                    removed = true;
                }else{
                    THINGER_DEBUG("_CONFIG", "Cannot delete legacy config file!");
                }
            }else{
                THINGER_DEBUG("_CONFIG", "No legacy config file to delete!");
            }
            LittleFS.end();
        }

        return removed;
    }


    protected:

    bool save_configuration(pson& config) override{
        THINGER_DEBUG("_CONFIG", "Updating Device Info...");
        Preferences preferences;
        if (!preferences.begin(NVS_WIFI_WEBCONFIG_NAMESPACE, false)) {
            THINGER_DEBUG("_CONFIG", "Failed to Open NVS for writing!");
            return false;
        }

        protoson::pson_encoder size_counter;
        size_counter.encode(config);
        size_t config_size = size_counter.bytes_written();

        if(config_size == 0){
            THINGER_DEBUG("_CONFIG", "Configuration serialization generated no data!");
            preferences.end();
            return false;
        }

        uint8_t* buffer = (uint8_t*) malloc(config_size);
        if(buffer == NULL){
            THINGER_DEBUG("_CONFIG", "Cannot allocate buffer for config serialization!");
            preferences.end();
            return false;
        }

        pson_nvs_encoder encoder(buffer, config_size);
        encoder.encode(config);

        bool serialized = encoder.bytes_written() == config_size;
        size_t stored = serialized ? preferences.putBytes(NVS_WIFI_WEBCONFIG_KEY, buffer, config_size) : 0;

        free(buffer);
        preferences.end();

        if(!serialized){
            THINGER_DEBUG("_CONFIG", "Failed to serialize configuration to memory!");
            return false;
        }

        if(stored != config_size){
            THINGER_DEBUG("_CONFIG", "Failed to store configuration in NVS!");
            return false;
        }

        THINGER_DEBUG("_CONFIG", "Done!");
        return true;
    }

    bool migrate_legacy_littlefs_configuration(pson& config){
        THINGER_DEBUG("_CONFIG", "Trying legacy LittleFS migration...");
        if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)) {
            THINGER_DEBUG("_CONFIG", "Failed to Mount Legacy LittleFS!");
            return false;
        }

        if (!LittleFS.exists(CONFIG_FILE)) {
            THINGER_DEBUG("_CONFIG", "No legacy config file found!");
            LittleFS.end();
            return false;
        }

        THINGER_DEBUG("_CONFIG", "Opening Legacy Config File...");
        File configFile = LittleFS.open(CONFIG_FILE, "r");
        if(!configFile){
            THINGER_DEBUG("_CONFIG", "Legacy Config File is Not Available!");
            LittleFS.end();
            return false;
        }

        pson_legacy_littlefs_decoder decoder(configFile);
        bool loaded = decoder.decode(config);
        configFile.close();

        if(!loaded){
            THINGER_DEBUG("_CONFIG", "Failed to decode legacy config file!");
            LittleFS.end();
            return false;
        }

        THINGER_DEBUG("_CONFIG", "Legacy config loaded, saving to NVS...");
        bool migrated = save_configuration(config);
        if(migrated){
            THINGER_DEBUG("_CONFIG", "Migration to NVS completed!");
            if(LittleFS.remove(CONFIG_FILE)){
                THINGER_DEBUG("_CONFIG", "Legacy config file removed after migration!");
            }else{
                THINGER_DEBUG("_CONFIG", "Could not remove legacy config file after migration!");
            }
        }else{
            THINGER_DEBUG("_CONFIG", "Failed to persist migrated config into NVS!");
        }

        LittleFS.end();
        return loaded;
    }

    bool load_configuration(pson& config) override{
        THINGER_DEBUG("_CONFIG", "Opening NVS...");
        Preferences preferences;
        if (!preferences.begin(NVS_WIFI_WEBCONFIG_NAMESPACE, true)) {
            THINGER_DEBUG("_CONFIG", "Failed to Open NVS!");
            return false;
        }

        THINGER_DEBUG("_CONFIG", "NVS Opened!");
        if (!preferences.isKey(NVS_WIFI_WEBCONFIG_KEY)) {
            THINGER_DEBUG("_CONFIG", "No config key available in NVS!");
            preferences.end();
            return migrate_legacy_littlefs_configuration(config);
        }

        size_t config_size = preferences.getBytesLength(NVS_WIFI_WEBCONFIG_KEY);
        if(config_size == 0){
            THINGER_DEBUG("_CONFIG", "Stored configuration is empty!");
            preferences.end();
            return migrate_legacy_littlefs_configuration(config);
        }

        uint8_t* buffer = (uint8_t*) malloc(config_size);
        if(buffer == NULL){
            THINGER_DEBUG("_CONFIG", "Cannot allocate buffer for config loading!");
            preferences.end();
            return false;
        }

        size_t read = preferences.getBytes(NVS_WIFI_WEBCONFIG_KEY, buffer, config_size);
        preferences.end();

        if(read != config_size){
            THINGER_DEBUG("_CONFIG", "Failed to read config blob from NVS!");
            free(buffer);
            return migrate_legacy_littlefs_configuration(config);
        }

        pson_nvs_decoder decoder(buffer, config_size);
        bool result = decoder.decode(config);
        free(buffer);

        if(!result){
            THINGER_DEBUG("_CONFIG", "Failed to decode config blob from NVS!");
            return migrate_legacy_littlefs_configuration(config);
        }

        return result;
    }

};

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions