ESP8266: Gravando dados permanentes na memória Flash 46

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.

Faça seu comentário

Acesse sua conta e participe

46 Comentários

  1. Bom dia! – Consigo gravar com sucesso qualquer WeMOS D1 MINi porém nenhum WeMOS D1 MINI PRO, a placa não é reconhecida, verifiquei que a Bridge USB para o primeiro é a CP2102 e para o PRO é a CP2104, mas não consigo instalar esta, poderiam informar um link para fazer o download correto da biblioteca CP2104? – agradeço.

    1. Olá Pedro,

      O Driver é para ser comum, que você pode encontrar aqui: https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers

      O WeMOS D1 MINI PRO chega a aparecer no gerenciador de dispositivos?

      Abraços!
      Vinícius – Equipe MakerHero

  2. Olá ! É possível alimentar essa placa com uma fonte externa de 5v – 1A através do pino de 5v ? O regulador de tensão irá atuar para baixar para a tensão de 3.3v de operação ?

    1. Olá!

      Sim, é possível alimentar diretamente pelo pino de 5V, mas não haverá 5V no USB devido ao diodo de proteção presente na placa.

      Abraços!
      Vinícius – Equipe MakerHero

  3. o unico problema é quantidade maxima de gravaçao desses estados. pois a memoria flash tem um limite de escrita de 10K

  4. Olá, estou tendo problemas para conectar a minha placa Wemos ao servidor de banco de dados Mysql, já não sei mais onde buscar ajuda….
    O erro é o seguinte:

    Exception (28):
    epc1=0x40202704 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000049 depc=0x00000000

    1. Olá Joshua,

      Tente utilizar o exception decoder: https://github.com/me-no-dev/EspExceptionDecoder

      Ele deve te dar mais informações sobre o que está causando o erro.

      Abraços!
      Vinícius – Equipe MakerHero

  5. olá
    eu executei o primeiro exemplo porém com um pequeno ajuste para que não usasse um botão , coloquei apenas uma função que lia a serial no lugar.
    no meu wemos d1 r1 , reparei que o código é um pouco lento , demora um pouco pra que seja gravado e quando isso acontece ha uma pequena instabilidade na led , fazendo com que ela pisque durante o processo.
    isso é normal ??
    e se for teria como eliminar esses problemas ??
    obrigado desde já!!

    1. Luiz,

      O LED pisca justamente porque o programa está sendo gravado na placa, fique tranquilo.

      Abraços!
      Diogo – Equipe MakerHero

  6. Muito legal, gostaria de armazenar dados e depois gerar um gráfico com esses dados em uma pagina html, gostaria de uma ajuda. Oque me indica para pesquisar sobre ?
    👏👏

    1. Augusto,

      Você pode dar uma olhada em como enviar dados gravados para uma página web 🙂

      Abraços!
      Diogo – Equipe MakerHero

  7. Consigo salvar um arquivo pdf?

    1. Olá Maiquel,

      A princípio ele faz a leitura. Leia mais no link abaixo:
      https://tttapa.github.io/ESP8266/Chap11%20-%20SPIFFS.html

      Abraços!
      Diogo – Equipe MakerHero

  8. Estou fazendo conforme orientado mas insiste a mensagem no rodapé da IDE : “SPiFFS Not defined for NodeMCU…”

    1. Olá, Gilson

      Confira se em Ferramentas > Tamanho Flash (Flash Size) está como 3M SPIFFS.

      Att
      Diogo – Equipe MakerHero

  9. Queria saber se posso usar a placa wemos d1 mini pro para fins comerciais e qual o tipo de licença que ela possuí se é MIT ou GPL. Obrigado

    1. Olá,

      É possível sim.

      Abraços!
      Diogo – Equipe MakerHero

      1. Muito obrigado pelo suporte!!!
        Tenho outra pergunta vi que ela é 3.3v, queria saber se posso trabalhar com ela ligada no USB do computador que fornece 5V

        1. É necessário que ela seja conectada no PC para que seja programada, então não há problemas quanto a isso.

          Abraços!
          Diogo – Equipe MakerHero

  10. Amigo o rodei o códico do led no meu da erro diz que é impossível abrir o arquivo oque é ? Será ?

    1. Olá Giuseppe,

      Você seguiu o seguinte procedimento? “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”

      Abraço!
      Rosana – Equipe MakerHero

  11. Bom dia turma…
    O meu SPIFFS.begin() tbm não retorna verdadeiro de jeito nenhum, já conferi e esta td certinho, tenho certeza que não é o código pois este mesmo já funcionou antes, estou utilizando um Wemos D1. O que poderia ser? alguma configuração?

    1. Bom dia turma….
      Resolvi o meu problema aqui e vou postar pois pode ser o problema de outros tbm, Na IDE do arduino no menu Ferramentas existe a opção “Flash Size”, a minha placa tem 4 Megas de Flash Size e pelo que eu entendi eu posso dividir esta memória entre o sketch do programa e os arquivos que eu queira guardar dentro dela, o meu estava selecionado 4M (no SPIFFS), quer dizer que ele havia reservado 4M para o sketch e não sobrou nada para o sistema de arquivos SPIFFS, mudei para 4M (1M SPIFFS) e funcionou bacana, ainda havia a opção de 2M e 3M caso queira mais espaço no SPIFFS (lembrando que vc só tem 4M total compartilhando com o sketch do programa)

  12. Antes de mais nada, bonito esmalte Giovanni. Era isso 🙂

    1. heheheh eu fico com a parte técnica e desenvolvimento. A parte de modelo de mãos, deixamos para as meninas da empresa haha

  13. Como faço para gravar múltiplas variáveis, ex.: estado dos Led1, Led2 e Led3?

    1. Olá Caue!

      Você poderia criar um arquivo para cada LED.
      Por exemplo:
      writeFile(“on”, “/led1.txt”);
      writeFile(“on”, “/led2.txt”);
      writeFile(“on”, “/led3.txt”);

      Abraço!

        1. Valeu!
          Testa lá, qualquer coisa chama aí!

      1. Giovanni, Boa Noite!
        Eu não consegui colocar o led2, tem mais alguma coisa que tenho que alterar, sou novato em programação caso seja possível enviar um sketch eu agradeço.

        Obrigado!

        1. Olá Vinícius,

          Para adicionar mais um LED você precisa declarar um novo LED e uma nova chave. Exemplo:
          int led1 = D1;
          int chave1 = D0;
          int led2 = D2;
          int chave2 = D5;

          Além disso você precisa alterar o void setup () e o void loop () com as novas novas variáveis.
          Boa sorte no projeto!

          Abraço!
          Rosana – Equipe MakerHero

          1. Rosana,
            Muito obrigado pelo suporte, vou tentar implementar e posto o resultado.

            Vinicius Bacellar
          2. Olá Vínicius!

            Que bom que te ajudamos!

            Abraço,
            Rosana – Equipe MakerHero

          3. Olá Rosana,
            Acrescentei tudo como mencionado a cima porem só funciona quando eu ativo o led1 se eu não ativa-lo não grava os outros leds que no meu caso são 16 reles mais fiz o teste com 4

            Vinicius Bacellar
  14. como usar varias paginas .html de forma simples

    https://forum.arduino.cc/t/solved-multiple-webpages/511530

  15. É possivel fazer no esp32?
    Tentei fazer aqui mas não consegui
    Quais sao as alterações necessárias?
    Obrigado

    1. Ainda não testamos no ESP32.

      Qual foi o erro ou problema encontrado?

      1. meu spiffs.begin não retorna verdadeiro

  16. 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.

  17. 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ê?

      1. Boa noite amigos,

        Estou tendo o mesmo problema do Paulo, no meu caso diz que o arquivo foi carregador, mas não aparece essa porcetagem que foi postada pelo Giovanni, fica assim:

        [SPIFFS] data : D:\Documentos\Arduino\index\data
        [SPIFFS] size : 3052
        [SPIFFS] page : 256
        [SPIFFS] block : 8192
        /index.html

        [SPIFFS] upload : C:\Users\BRUNON~1\AppData\Local\Temp\arduino_build_186024/index.spiffs.bin
        [SPIFFS] address : 0x100000
        [SPIFFS] reset : ck
        [SPIFFS] port : COM5
        [SPIFFS] speed : 115200
        [SPIFFS] python : python3.exe
        [SPIFFS] uploader : D:\Documentos\ArduinoData\packages\esp8266\hardware\esp8266\2.5.1\tools\upload.py

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

    1. Teria que achar um jeito de comunicar o SSD com o ESP8266

  19. 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.