quarta-feira, julho 10, 2019

ESP32/Arduino: NVS e Preferences

No post anterior falamos na emulação de EEProm no suporte ao Arduino da ESP32, vamos ver agora as formas recomendadas pela ExpressIf para armazenamento não volátil de informações.

Non-volatile Storage (NVS)

A NVS é uma biblioteca do ESP-IDF (IoT Development Framework, o conjunto de ferramentas e bibliotecas para desenvolvimento de aplicações nativas do ESP-32). O seu objetivo é armazenar informações na Flash (e eventualmente em outras formas de armazenamento não volátil). As informações são armazenadas na forma de pares chave/valor. Grupos de pares são armazenados em namespaces, para facilitar a separação das informações de várias aplicações ou módulos.

Atualmente a NVS salva as informações em uma partição da flash com tipo data e subtipo nvs. Normalmente esta partição tem o nome "nvs".

As chaves e namespaces são cadeias ASCII terminadas por nul, com até 15 caracteres (fora o nul final). Vários tipos de valores são suportados:
  • Inteiros de 8, 16, 32 ou 64 bits, com ou sem sinal
  • Cadeias ASCII terminadas por nul
  • Dados binários de tamanho variável
A documentação detalha a implementação, vou chamar a atenção apenas para os detalhes que acho mais importantes:
  • Os pares são armazenados de forma sequencial na flash, na forma de entradas de 32 bytes salvas em páginas da flash
  • A busca por uma chave é feita procurando sequencialmente em uma lista de hashs das chaves. Desta forma a comparação é rápida (por usar um hash), porém a busca pode ser lenta se existir um número grande de pares.
  • Após realizar escritas é necessário fazer um commit
Um exemplo simples de uso direto do NVS para criar um contador de resets (adaptado do exemplo oficial):
// Includes do ESP-IDF
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"

// Definições dos meus nomes
char *my_namespace = "DQSoft";
char *chave = "Resets";

// Iniciação
void setup() {
  // Inicia serial
  Serial.begin(115200);
  
  // Inicia NVS
  esp_err_t err = nvs_flash_init();
  if (err != ESP_OK) {
    Serial.print ("Erro ao iniciar NVS: ");
    Serial.println (esp_err_to_name(err));
    return;
  }

  // Acessar o namespace
  nvs_handle my_handle;
  err = nvs_open(my_namespace, NVS_READWRITE, &my_handle);
  if (err != ESP_OK) {
    Serial.print ("Erro ao acessar namespace: ");
    Serial.println (esp_err_to_name(err));
    return;
  }

  // Tenta ler o contador de resets
  int32_t cont_resets = 0;
  err = nvs_get_i32(my_handle, chave, &cont_resets);
  cont_resets++;
  Serial.print ("Resets: ");
  Serial.println (cont_resets);

  // Atualiza
  err = nvs_set_i32(my_handle, chave, cont_resets);
  if (err != ESP_OK) {
    Serial.print ("Erro ao acessar atualizar: ");
    Serial.println (esp_err_to_name(err));
    return;
  }
  err = nvs_commit(my_handle);
  if (err != ESP_OK) {
    Serial.print ("Erro no commit: ");
    Serial.println (esp_err_to_name(err));
    return;
  }
  nvs_close (my_handle);
}

void loop() {
  delay(100);
}
Preferences

A biblioteca Preferences é uma camada fina por cima da biblioteca NVS, criando uma interface mais parecida com outras bibliotecas Arduino.

O programa abaixo faz o mesmo que o exemplo anterior, porém usa a biblioteca Preferences.

// Acesso à biblioteca
#include <Preferences.h>
Preferences preferences;

// Definições dos meus nomes
char *my_namespace = "DQSoft";
char *chave = "Resets";

// Iniciações
void setup() {
  // Inicia serial
  Serial.begin(115200);
  
  // Inicia acesso às nossas preferences
  preferences.begin(my_namespace, false);

  // Tenta ler o contador de resets
  int32_t cont_resets;
  cont_resets = preferences.getInt (chave, 0);
  cont_resets++;
  Serial.print ("Resets: ");
  Serial.println (cont_resets);

  // Atualiza
  preferences.putInt (chave, cont_resets);
  preferences.end();
}

void loop() {
  delay(100);
}

Nenhum comentário: