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):
  1. // Includes do ESP-IDF  
  2. #include "esp_system.h"  
  3. #include "nvs_flash.h"  
  4. #include "nvs.h"  
  5.   
  6. // Definições dos meus nomes  
  7. char *my_namespace = "DQSoft";  
  8. char *chave = "Resets";  
  9.   
  10. // Iniciação  
  11. void setup() {  
  12.   // Inicia serial  
  13.   Serial.begin(115200);  
  14.     
  15.   // Inicia NVS  
  16.   esp_err_t err = nvs_flash_init();  
  17.   if (err != ESP_OK) {  
  18.     Serial.print ("Erro ao iniciar NVS: ");  
  19.     Serial.println (esp_err_to_name(err));  
  20.     return;  
  21.   }  
  22.   
  23.   // Acessar o namespace  
  24.   nvs_handle my_handle;  
  25.   err = nvs_open(my_namespace, NVS_READWRITE, &my_handle);  
  26.   if (err != ESP_OK) {  
  27.     Serial.print ("Erro ao acessar namespace: ");  
  28.     Serial.println (esp_err_to_name(err));  
  29.     return;  
  30.   }  
  31.   
  32.   // Tenta ler o contador de resets  
  33.   int32_t cont_resets = 0;  
  34.   err = nvs_get_i32(my_handle, chave, &cont_resets);  
  35.   cont_resets++;  
  36.   Serial.print ("Resets: ");  
  37.   Serial.println (cont_resets);  
  38.   
  39.   // Atualiza  
  40.   err = nvs_set_i32(my_handle, chave, cont_resets);  
  41.   if (err != ESP_OK) {  
  42.     Serial.print ("Erro ao acessar atualizar: ");  
  43.     Serial.println (esp_err_to_name(err));  
  44.     return;  
  45.   }  
  46.   err = nvs_commit(my_handle);  
  47.   if (err != ESP_OK) {  
  48.     Serial.print ("Erro no commit: ");  
  49.     Serial.println (esp_err_to_name(err));  
  50.     return;  
  51.   }  
  52.   nvs_close (my_handle);  
  53. }  
  54.   
  55. void loop() {  
  56.   delay(100);  
  57. }  
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.

  1. // Acesso à biblioteca  
  2. #include <Preferences.h>  
  3. Preferences preferences;  
  4.   
  5. // Definições dos meus nomes  
  6. char *my_namespace = "DQSoft";  
  7. char *chave = "Resets";  
  8.   
  9. // Iniciações  
  10. void setup() {  
  11.   // Inicia serial  
  12.   Serial.begin(115200);  
  13.     
  14.   // Inicia acesso às nossas preferences  
  15.   preferences.begin(my_namespace, false);  
  16.   
  17.   // Tenta ler o contador de resets  
  18.   int32_t cont_resets;  
  19.   cont_resets = preferences.getInt (chave, 0);  
  20.   cont_resets++;  
  21.   Serial.print ("Resets: ");  
  22.   Serial.println (cont_resets);  
  23.   
  24.   // Atualiza  
  25.   preferences.putInt (chave, cont_resets);  
  26.   preferences.end();  
  27. }  
  28.   
  29. void loop() {  
  30.   delay(100);  
  31. }  

Nenhum comentário: