ESP8266: Gravando dados permanentes na memória Flash 7

Neste post iremos mostrar como tomar proveito da memória flash SPI encontrada nos módulos ESP8266 para armazenamento de dados e arquivos, utilizando seu sistema de arquivos SPIFFS. Com os conhecimentos adquiridos aqui, você poderá ir além, criando, por exemplo, um datalogger de sensores e até mesmo hospedando uma página web.

Para melhor exemplificar os conceitos de utilização da memória Flash, utilizaremos a placa Wemos D1 Mini Pro e faremos dois exemplos.

Placa Wemos

O primeiro exemplo liga/desliga um LED e salva seu estado na memória. O estado ficará salvo mesmo quando o Wemos for desligado. Ao ligar novamente o Wemos, o sistema busca na memória o estado do LED acionando o mesmo de acordo com seu estado.

No segundo exemplo iremos mostrar como carregar um arquivo index.html contendo algumas linhas de HTML na memória flash e visualizar esse arquivo através de um navegador de internet.

Biblioteca SPIFFS

Para utilizar o sistema de arquivos SPIFFS (SPI Flash File System) é necessário incluir nos programas a biblioteca FS.h, que já é nativa da IDE Arduino com suporte a placas ESP8266.

#include <FS.h>

A biblioteca nos possibilita algumas manipulações básicas de arquivos como open, close, read e write e as funções básicas da biblioteca são mostradas abaixo.

Inicia o sistema de arquivos.

SPIFFS.begin()

Apaga todos os arquivos na região de memória reservada.

SPIFFS.format()

Abre um arquivo no modo selecionado. O nome do arquivo deve ser um diretório iniciado com uma “/”, exemplo: “/log.txt”. O modo pode ser “r”, “w”, “a”, “r+”, “w+”, “a+”, seguindo o mesmo padrão da função fopen da linguagem C.

SPIFFS.open(arquivo, modo)

Para saber mais sobre a memória Flash dos módulos ESP8266 e seu sistema de arquivos acesse aqui.

Circuito do Exemplo 1

Para realizar o exemplo 1 deste tutorial, utilizamos um circuito bem simples que consiste de um LED, resistores 1K e uma chave push-button. Os componentes são conectados conforme o esquema abaixo:

Circuito Wemos e Led - Memória Flash

Exemplo 1 – Salvando estado do LED na memória

O primeiro exemplo utiliza-se de um arquivo para salvar o estado on/off de um LED. Apertando o botão podemos mudar o estado. Mesmo se a placa for desligada o estado ficará salvo na memória. Quando ligamos novamente a placa o sistema busca o estado salvo na memória e atualiza o LED de acordo.

Vale notar que o programa deve ser gravado duas vezes. Na primeira vez a linha 62 ( writeFile(“off”, “/state.txt”) ) deve ser descomentada, criando um arquivo na memória. Na segunda e demais programações, a linha deve ser comentada, pois o arquivo já existirá na memória.

Veja o exemplo 1 abaixo:

#include <FS.h>

int led = D1; 
int chave = D0;

/**
  * @desc escreve conteúdo em um arquivo
  * @param string state - conteúdo a se escrever no arquivo
  * @param string path - arquivo a ser escrito
*/
void writeFile(String state, String path) { 
  //Abre o arquivo para escrita ("w" write)
  //Sobreescreve o conteúdo do arquivo
  File rFile = SPIFFS.open(path,"w+"); 
  if(!rFile){
    Serial.println("Erro ao abrir arquivo!");
  } else {
    rFile.println(state);
    Serial.print("gravou estado: ");
    Serial.println(state);
  }
  rFile.close();
}

/**
  * @desc lê conteúdo de um arquivo
  * @param string path - arquivo a ser lido
  * @return string - conteúdo lido do arquivo
*/
String readFile(String path) {
  File rFile = SPIFFS.open(path,"r");
  if (!rFile) {
    Serial.println("Erro ao abrir arquivo!");
  }
  String content = rFile.readStringUntil('\r'); //desconsidera '\r\n'
  Serial.print("leitura de estado: ");
  Serial.println(content);
  rFile.close();
  return content;
}

/**
  * @desc inicializa o sistema de arquivos
*/
void openFS(void){
  //Abre o sistema de arquivos
  if(!SPIFFS.begin()){
    Serial.println("\nErro ao abrir o sistema de arquivos");
  } else {
    Serial.println("\nSistema de arquivos aberto com sucesso!");
  }
}

void setup() {
  pinMode(led, OUTPUT);
  pinMode(chave, INPUT); 
  Serial.begin(9600);   
  openFS();
  
  // no primeiro upload de programa o arquivo state.txt deve ser criado com o conteúdo "off"
  // no segundo upload a linha deve ser comentada.
  //writeFile("off", "/state.txt");

  // verifica o último estado do LED e ativa de acordo
  String state = readFile("/state.txt");
  if(state == "on") 
  {
    digitalWrite(led, HIGH);
  }
  else if(state == "off") 
  {
    digitalWrite(led, LOW);
  }    
}

void loop() {
  /*
   * verifica o estado anterior salvo no arquivo, 
   * ativa o LED de acordo 
   * e escreve novo estado no arquivo.
   */
  if(digitalRead(chave) == LOW)
  {
    String state = readFile("/state.txt");    
    if(state == "off") 
    {
      writeFile("on", "/state.txt");
      digitalWrite(led, HIGH);
    }
    else if(state == "on") 
    {
      writeFile("off", "/state.txt");
      digitalWrite(led, LOW);      
    }
    while(digitalRead(chave) == LOW);   
  }
}

Abaixo você confere esse teste em funcionamento:

Exemplo 2 – Salvando uma página Web index.html na memória flash

O exemplo 2 busca um arquivo index.html salvo na memória e exibe essa página em um navegador. Veja o código abaixo:

#include <FS.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

const char* ssid = "Nome da rede Wifi";
const char* password = "Senha da rede Wifi";

String webPage = "";

ESP8266WebServer server(80);

void handleRoot() {
  server.send(200, "text/html", webPage);
}

void readFile(void) {
  //Faz a leitura do arquivo HTML
  File rFile = SPIFFS.open("/index.html","r");
  Serial.println("Lendo arquivo HTML...");
  webPage = rFile.readString();
//  while(rFile.available()) {
//    String line = rFile.readStringUntil('\n');
//    webPage += line;
//  }
  Serial.println(webPage);
  rFile.close();  
}

void setup() {
  Serial.begin(9600);
  SPIFFS.begin();

  if(SPIFFS.exists("/index.html"))
  {
    Serial.println("\n\nfile exists!");
  }
  else Serial.println("\n\nNo File :(");

  readFile();

  WiFi.begin(ssid, password);
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  
  server.begin();
}

void loop() {
  server.handleClient();
}

Para que o código acima funcione é necessário gravar o arquivo index.html na memória flash do Wemos. Para isso existe uma ferramenta para IDE Arduino que nos possibilita carregar arquivos inteiros para memória flash.

Primeiramente baixe a ferramenta neste link.

Extraia o conteúdo para o diretório tools da pasta de instalação do Arduino. No caso do meu sistema, que é Linux, o caminho do diretório ficou assim:

../Arduino/tools/ESP8266FS/tool/esp8266fs.jar

Reinicie a IDE Arduino e você deverá ver a nova ferramenta no menu Ferramentas.

ESP8266 Sketch Data Upload

Essa ferramenta carrega qualquer arquivo que estiver dentro de uma pasta chamada “data”. A pasta data deve estar dentro da pasta do seu sketch.

Wemos - Pastas

Coloque o arquivo index.html abaixo dentro da sua pasta data.

<!DOCTYPE html>
<html>
  <body>
      <h1>Hello Wemos SPIFFS!</h1>
  </body>
</html>

E então grave o arquivo utilizando a ferramenta mostrada acima.

Mais informações sobre a ferramenta de upload de arquivos aqui.

Se tudo der certo, acesse o IP do seu Wemos em um navegador e você deverá ver a página carregada como abaixo:

HTML Wemos

Conclusão 

Neste post você aprendeu a usar a memória Flash SPI dos módulos ESP8266 para gravar arquivos permanentemente. Essa técnica nos traz uma diversa gama de possibilidades como salvar senhas de rede WiFi, salvar informações de log, dados de sensores, um ID específico para cada módulo ESP8266, salvar páginas Web completas com arquivos html, javascript e css. O melhor de tudo é que esses dados persistem mesmo na falta de energia.

Gostou usar a memória Flash do ESP8266? Deixe seu comentário logo abaixo. Em caso de dúvidas, caso queira trocar uma ideia, ou até mesmo dividir seu projeto, acesse nosso Fórum!

Posts Relacionados

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

7 Comentários

  1. Muito bom artigo estava procurando por esta informação. É possível também colocar figuras e arquivos css, js separados independentes na memoria SPIffs se puder fazer um tutorial sobre este assunto agradeço.

    1. Olá Francisco! Obrigado pela leitura do post. Ainda não temos um tutorial específico de como gravar toda a página web incluindo CSS e JS como arquivos separados, é basicamente o mesmo princípio. Mas existem outros tutoriais pela internet como os dois seguintes que mostram como:
      https://diyprojects.io/esp8266-web-server-part-1-store-web-interface-spiffs-area-html-css-js/#.WnmQ7HWnGyI
      https://techtutorialsx.com/2016/10/15/esp8266-http-server-serving-html-javascript-and-css/

      Recomendo a leitura.

  2. Seria possível fazer o mesmo com um ssd. Ou seja, a página está em um ssd e o ESP servi-la?

    Leones Moura dos Santos
    1. Teria que achar um jeito de comunicar o SSD com o ESP8266

  3. Olá Giovanni, estou usando o ESP8266 NODEMCU e fiz o exemplo 1 com resultados positivos e o exemplo 2 conecta no WIFI, mostra o IP e não abre a página.
    Já instalei a ferramenta para Upload e aparece na aba ferramentas e parece tudo certo.
    No monitor aparece “No File 🙁 ” e “Lendo arquivo HTML” e quando eu digito o ip no Chrome não dá erro mas a tela fica em branco.
    O index.html esta na pasta Data do projeto.
    o ESP8266 nodemcu é compativel com este exemplo? já li vários artigo e não encontrei a resposta ainda.

    1. Olá Paulo!

      Acabei de fazer um teste aqui e funcionou normalmente com o NodeMCU. As duas placas funcionam com mesmo princípio. As duas tem memória flash conectada ao chip esp8266.

      Quando clicar em fazer upload dos dados deve aparecer as seguintes mensagens no terminal:

      [SPIFFS] data : /home/giobauermeister/Arduino/sketches/wemos-spiffs-exemplo2/data
      [SPIFFS] size : 1004
      [SPIFFS] page : 256
      [SPIFFS] block : 8192
      /index.html
      /iro.min.js
      [SPIFFS] upload : /tmp/build2dd4fd3727999e0d8dd9ca5a32e0c4ae.spiffs/wemos-spiffs-exemplo2.spiffs.bin
      [SPIFFS] reset : nodemcu
      [SPIFFS] port : /dev/ttyUSB0
      [SPIFFS] speed : 115200
      [SPIFFS] address: 0x300000

      Uploading 1028096 bytes from /tmp/build2dd4fd3727999e0d8dd9ca5a32e0c4ae.spiffs/wemos-spiffs-exemplo2.spiffs.bin to flash at 0x00300000
      …………………………………………………………………….. [ 7% ]
      …………………………………………………………………….. [ 15% ]
      …………………………………………………………………….. [ 23% ]
      …………………………………………………………………….. [ 31% ]
      …………………………………………………………………….. [ 39% ]
      …………………………………………………………………….. [ 47% ]
      …………………………………………………………………….. [ 55% ]
      …………………………………………………………………….. [ 63% ]
      …………………………………………………………………….. [ 71% ]
      …………………………………………………………………….. [ 79% ]
      …………………………………………………………………….. [ 87% ]
      …………………………………………………………………….. [ 95% ]
      …………………………………….. [ 100% ]

      Apareceu o mesmo pra você?

  4. Muito legal o tutorial, estou apanhando para apagar o arquivo, mandei a informação em branco sem querer agora não consigo enviar outro rsrs.