



Aprenda neste post como usar o sistema de arquivos LittleFS, que possui mais desempenho do que o sistema SPIFFS, nas placas Raspberry Pi Pico, ESP32 e ESP8266.
Será mostrado ainda neste post:
Neste post, será utilizado os seguintes materiais:
LittleFS é um sistema de arquivos leve e otimizado, desenvolvido especificamente para dispositivos embarcados e de Internet das Coisas (IoT), com suporte a diretórios reais. Ele é projetado para ser eficiente em termos de espaço de armazenamento, consumo de energia e velocidade de acesso, tornando-o uma escolha popular para placas como ESP32, ESP8266 e Raspberry Pi Pico.
O LittleFS destaca-se por sua eficiência no uso de memória, sendo projetado para operar com quantidades reduzidas. Sua utilização de RAM é estritamente controlada, o que significa que o consumo não varia conforme o sistema de arquivos cresce. Além disso, o sistema de arquivos não permite recursão ilimitada e a alocação de memória dinâmica é limitada a buffers configuráveis que podem ser fornecidos de forma estática.
Além de sua eficiência no uso de memória, o LittleFS também foi projetado com foco em memórias flash, oferecendo nivelamento de desgaste em blocos dinâmicos. Além disso, ele possui a capacidade de detectar e contornar blocos defeituosos, garantindo maior confiabilidade e durabilidade no armazenamento de dados.
O sistema de arquivos LittleFS oferece suporte a nomes de arquivo de até 31 caracteres (incluindo seu caminho, caractere barra [‘/’], pontos de extensão de arquivo[‘.’]) + o caractere de terminação de string [‘\0’]), e tantos subdiretórios quanto o espaço permitir. Esse limite pode ser facilmente alcançado quando criamos nomes de arquivos longos ou temos subdiretórios muito aninhados (pasta em pasta). Se o limite for ultrapassado, o compilador não gerará nenhum erro, mas poderá ocorrer erros em tempo de execução, como não poder encontrar o arquivo buscado.
Caso o nome de arquivo não tenha o caractere "/", presume-se que os nomes de arquivo estejam no diretório raiz.
A abertura de arquivos em subdiretórios requer a especificação do caminho completo para o arquivo (ou seja, incluindo a barra inicial. Ex.: "/sub/dir/arquivo.txt"). Os subdiretórios são criados automaticamente quando se tenta criar um arquivo em um subdiretório e, quando o último arquivo em um subdiretório é removido, o próprio subdiretório é excluído automaticamente.
O SPIFFS é o sistema de arquivos foi desenvolvido para aplicativos com restrição de espaço e RAM que utilizam muitos arquivos pequenos e se preocupam com o nivelamento de desgaste estático e dinâmico e não precisam de suporte de diretório real. Mas LittleFS foi criado, e como se ele se concentra em maior desempenho e suporte a diretórios, o SPIFFS se tornou obsoleto para uso em novos projetos. A seguir conta algumas das diferenças entre os sistemas de arquivos SPIFFS e LittleFS:
Para realizar o teste de comparação de velocidade entre SPIFFS e LittleFS foi desenvolvido dois sketches. Um para testar a velocidade de escrita e leitura do sistema de arquivos SPIFFS e outro para o LittleFS. Veja abaixo os sketches de teste dos sistemas de arquivos.
/******************************************************************************
Teste de comparação de tempo de leitura de arquivos no LittleFS e SPIFFS
Sketch de teste do SPIFFS
Criado em 10 de Julho de 2023
por Michel Galvão (https://micsg.com.br)
Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits
https://www.eletrogate.com/
Confira mais detalhes deste sketch em https://blog.eletrogate.com/
******************************************************************************/
// AVISO: O sistema de arquivos será formatado no início do teste!
// Inclusão de Biblitecas
#include "FS.h"
// Inclui a biblioteca SPIFFS.h somente se a placa atual for a ESP32
#ifdef ESP32
#include "SPIFFS.h"
#endif
#define TESTSIZEKB 256 // O tamanho de um arquivo para testar (em kilobytes)
// Formata a velocidade em bytes por segundo
String rate(unsigned long start, unsigned long stop, unsigned long bytes) {
String mensagemDeRetorno; // Declaração da variável para armazenar a mensagem de retorno
if (stop == start) { // Se a contagem inicial e final forem iguais, ...
mensagemDeRetorno = "Inf b/s"; // define "Inf b/s" na mensagem de retorno indicando que a velocidade é infinita, ou seja, não há limite ou restrição na taxa de transferência de dados
} else { // Senão, realiza o cálculo da taxa de velocidade em bytes/segundo
unsigned long delta = stop - start; // Calcula a diferença entre a contagem final e inicial
float r = 1000.0 * (float)bytes / (float)delta; // Calcula a taxa de velocidade em bytes/segundo
if (r >= 1000000.0) {
// Se a taxa for maior ou igual a 1.000.000,0, formata a velocidade em MB/s
mensagemDeRetorno = String(r / 1000000.0, 2) + " MB/s";
} else if (r >= 1000.0) {
mensagemDeRetorno = String(r / 1000.0, 2) + " KB/s"; // Se a taxa for maior ou igual a 1000,0, formata a velocidade em KB/s
} else {
mensagemDeRetorno = String((int)r) + " bytes/s"; // Caso contrário, formata a velocidade em bytes/s
}
}
return mensagemDeRetorno; // Retorna o buffer contendo a taxa de velocidade formatada
}
void realizarSpeedTest() {
// Início do teste de velocidade de escrita/leitura com sistema de arquivos
Serial.println("Começando Teste de Velocidade de escrita/leitura com SPIFFS");
// Mensagem indicando o processo de iniciar o sistema de arquivos
Serial.println("Iniciando sistema de arquivos...");
#ifdef ESP8266 // Verifica se o código está sendo compilado para um dispositivo ESP8266 ou não
if (!SPIFFS.begin()) { // Inicia o sistema de arquivos SPIFFS
Serial.println("Não foi possível iniciar, abortando"); // Caso a inicialização falhe, é mostrada uma mensagem de erro no monitor serial e o programa é encerrado
return;
}
#else // se o código estiver sendo compilado para qualquer dispositivo que não seja ESP8266, como o ESP32
if (!SPIFFS.begin(true)) { // Inicia o sistema de arquivos SPIFFS, formatando-o se algum erro ocorrer
Serial.println("Não foi possível iniciar, abortando"); // Caso a inicialização falhe, é mostrada uma mensagem de erro no monitor serial e o programa é encerrado
return;
}
#endif // marca o final do bloco condicional #ifdef e indica o término da verificação condicional
// Mensagem indicando o processo de formatação do sistema de arquivos
Serial.println("Fazendo formatação...");
if (!SPIFFS.format()) { // Formata o sistema de arquivos
Serial.println("Não foi possível formatar, abortando"); // caso não seja possível formatar o sistema de arquivos, mostra uma mensagem de erro no monitor serial e o programa é encerrado
return;
}
// Declara um array de 256 posições, sendo 1 byte para cada posição
uint8_t data[256];
// Preenche o array de dados com valores de 0 a 255
for (int i = 0; i < 256; i++) {
data[i] = (uint8_t)i;
}
// Imprime uma linha em branco
Serial.println();
// Imprime uma mensagem indicando que o arquivo será escrito no sistema de arquivos
Serial.print("Escrevendo arquivo com tamanho de ");
Serial.print(TESTSIZEKB); // imprime o tamanho do arquivo em kilobytes
Serial.print(" kilobytes (");
Serial.print(TESTSIZEKB * 1024); // converte o tamanho do arquivo de kilobytes para bytes (a cada 1024 bytes, se tem 1 kilobyte)
Serial.println(" bytes) ...(pode demorar um pouco) ");
// Inicia a contagem de tempo e registra o tempo atual, para posterior cálculo de velocidade de escrita
unsigned long start = millis();
// Cria o arquivo para gravação, passando parâmetros, sendo o primeiro o nome do arquivo com sua extensão e o segundo sendo o modo de abertura do arquivo (w: modo de escrita)
File f = SPIFFS.open("/arquivoDeTeste.bin", "w");
// Se o arquivo não puder ser aberto, imprime uma mensagem de erro e aborta o teste
if (!f) {
Serial.println("Não é possível abrir o arquivo para gravação, abortando");
return;
}
// Escreve os dados no arquivo
for (int i = 0; i < TESTSIZEKB; i++) { // laço de repetição 'for' que será executado 256 vezes
for (int j = 0; j < 4; j++) { // laço de repetição 'for' que será executado 4 vezes
f.write(data, 256); // escreve no arquivo o conteúdo do array 'data' que possui 256 bytes
// RESULTADO: ao escrever no arquivo 256 bytes por 4 vezes em que estas 4 vezes são executadas 256 vezes, é escrito ao total 262144 bytes (256 kilobytes):
// 256 bytes * 4 vezes * 256 vezes =
// 256 * 4 * 256 =
// 262144 bytes = 256 kilobytes (262144 /256 = 256 kilobytes)
}
}
// Fecha o arquivo
f.close();
// Interrompe a contagem de tempo e registra o tempo atual, para posterior cálculo de velocidade de escrita
unsigned long stop = millis();
// Imprime uma mensagem indicando o tempo gasto para escrever o arquivo
Serial.print("==> Tempo para gravar ");
Serial.print(TESTSIZEKB); // 256 kilobytes
Serial.print(" kilobytes (");
Serial.print(TESTSIZEKB * 1024); // converte kilobytes em bytes (cada kilobyte tem 1024 bytes)
Serial.print(" bytes) = ");
Serial.print(stop - start); // subtrai o tempo de início do tempo de parada, calculando assim o tempo gasto
Serial.println(" milissegundos");
// Imprime uma mensagem indicando a velocidade de escrita
Serial.print("==> Velocidade de escrita: ");
Serial.println(rate(start, stop, TESTSIZEKB * 1024)); // A velocidade de escrita é impressa no monitor serial, onde é calculada usando a função rate, em que é passado como parâmetros:
// - o tempo de parada (stop),
// - o tempo de início (start) e
// - a quantidade de dados transitado em bytes.
// O tamanho do arquivo é multiplicado por 1024 para obter o tamanho em bytes.
Serial.println();
// Imprime uma mensagem indicando que o arquivo será lido sequencialmente
Serial.print("Lendo arquivo sequencialmente com tamanho de ");
Serial.print(TESTSIZEKB); // imprime o tamanho do arquivo em kilobytes
Serial.print(" kilobytes (");
Serial.print(TESTSIZEKB * 1024); // converte o tamanho do arquivo de kilobytes para bytes (a cada 1024 bytes, se tem 1 kilobyte)
Serial.println(" bytes) ...");
// Inicia a contagem de tempo e registra o tempo atual, para posterior cálculo de velocidade de leitura
start = millis();
// Abre o arquivo para leitura, passando parâmetros, sendo o primeiro o nome do arquivo com sua extensão e o segundo sendo o modo de abertura do arquivo (r: modo de leitura)
f = SPIFFS.open("/arquivoDeTeste.bin", "r");
// Se o arquivo não puder ser aberto, imprime uma mensagem de erro e aborta o teste
if (!f) {
Serial.println("Não é possível abrir o arquivo para leitura, abortando");
return;
}
// Lê o arquivo sequencialmente
for (int i = 0; i < TESTSIZEKB; i++) { // laço de repetição 'for' que será executado 256 vezes
for (int j = 0; j < 4; j++) { // laço de repetição 'for' que será executado 4 vezes
f.read(data, 256); // lê 256 bytes do conteúdo do arquivo e passa para o array 'data'
}
// RESULTADO: ao ler o arquivo com cada leitura tendo 256 bytes, e isto ocorrendo por 4 vezes em que estas 4 vezes são executadas 256 vezes, é lido ao total 262144 bytes (256 kilobytes):
// 256 bytes * 4 vezes * 256 vezes =
// 256 * 4 * 256 =
// 262144 bytes = 256 kilobytes (262144 /256 = 256 kilobytes)
}
// Fecha o arquivo
f.close();
// Interrompe a contagem de tempo e registra o tempo atual, para posterior cálculo de velocidade de leitura
stop = millis();
// Imprime uma mensagem indicando o tempo gasto para ler o arquivo sequencialmente
Serial.print("==> Tempo para ler sequencialmente ");
Serial.print(TESTSIZEKB); // 256 kilobytes
Serial.print(" kilobytes (");
Serial.print(TESTSIZEKB * 1024); // converte kilobytes em bytes (cada kilobyte tem 1024 bytes)
Serial.print(" bytes) = ");
Serial.print(stop - start); // subtrai o tempo de início do tempo de parada, calculando assim o tempo gasto
Serial.println(" milissegundos");
// Imprime uma mensagem indicando a velocidade de leitura
Serial.print("==> Velocidade de leitura: ");
Serial.println(rate(start, stop, TESTSIZEKB * 1024)); // A velocidade de leitura é impressa no monitor serial, onde é calculada usando a função rate, em que é passado como parâmetros:
// - o tempo de parada (stop),
// - o tempo de início (start) e
// - a quantidade de dados transitado em bytes.
// O tamanho do arquivo é multiplicado por 1024 para obter o tamanho em bytes.
Serial.println();
// Imprime uma mensagem indicando o fim do teste
Serial.println("Fim do Teste de Velocidade de escrita/leitura");
}
void setup() {
// Inicializa a porta serial com 115200 bits por segundo de baud rate
Serial.begin(115200);
// Aguarda até que o monitor serial esteja pronto para receber dados, em que o usuário deve digitar e enviar qualquer dado pelo monitor serial
while (Serial.available() == false) {}
// Realiza o teste de velocidade de leitura e escrita no sistema de arquivos
realizarSpeedTest();
// Entra em um loop infinito
while (1) {
delay(10);
}
}
void loop() {}
rate() é criada para calcular e formatar a taxa de velocidade em bytes por segundo com base nos parâmetros fornecidos: tempo inicial, tempo final e quantidade de bytes transferidos. Ela retorna uma String formatada com a velocidade em Bytes/s, Kilobytes/s ou Megabytes/s.
A função realizarSpeedTest() é o núcleo do teste de velocidade. Ela inicia o sistema de arquivos SPIFFS e o formata. Em seguida, marca o tempo de ínicio (na variável start) na variável cria um arquivo chamado "arquivoDeTeste.bin" e grava 256KB de dados nele, divididos em quatro gravações de 256 bytes cada em que este ciclo de quatro gravações é executado 256 vezes. Veja abaixo o cálculo de quanto dado foi escrito:
stop) e a velocidade de escrita é calculada (utilizando a função rate(), passando como parâmetro as vaiáveis start , stop e a quantidade de bytes transferidos) e é exibida no monitor serial.
Após a gravação, o código lê os 256KB de dados do arquivo em quatro gravações de 256 bytes cada em que este ciclo de quatro gravações é executado 256 vezes. Veja abaixo o cálculo de quanto dado foi lido pelo programa:
setup() configura a porta serial com uma taxa de transmissão de 115200 bps e aguarda até que o monitor serial esteja pronto para receber dados (ou seja, aguarda até que o usuário envie qualquer dado pelo monitor serial). Em seguida, chama a função realizarSpeedTest() para executar o teste de velocidade. Por fim, ainda dentro da função setup(), é executado um loop infinito através da estrutura de iteração while passando como parâmetro um valor booleano verdadeiro.
A função loop() é vazia, não tendo nenhum código dentro dela.
/******************************************************************************
Teste de comparação de tempo de leitura de arquivos no LittleFS e SPIFFS
Sketch de teste do LittleFS
Criado em 10 de Julho de 2023
por Michel Galvão (https://micsg.com.br)
Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits
https://www.eletrogate.com/
Confira mais detalhes deste sketch em https://blog.eletrogate.com/
******************************************************************************/
// AVISO: O sistema de arquivos será formatado no início do teste!
// Inclusão de Biblitecas
#include <FS.h>
#include <LittleFS.h>
#define TESTSIZEKB 256 // O tamanho de um arquivo para testar (em kilobytes)
// Formata a velocidade em bytes por segundo
String rate(unsigned long start, unsigned long stop, unsigned long bytes) {
String mensagemDeRetorno; // Declaração da variável para armazenar a mensagem de retorno
if (stop == start) { // Se a contagem inicial e final forem iguais, ...
mensagemDeRetorno = "Inf b/s"; // define "Inf b/s" na mensagem de retorno indicando que a velocidade é infinita, ou seja, não há limite ou restrição na taxa de transferência de dados
} else { // Senão, realiza o cálculo da taxa de velocidade em bytes/segundo
unsigned long delta = stop - start; // Calcula a diferença entre a contagem final e inicial
float r = 1000.0 * (float)bytes / (float)delta; // Calcula a taxa de velocidade em bytes/segundo
if (r >= 1000000.0) {
// Se a taxa for maior ou igual a 1.000.000.0, formata a velocidade em MB/s
mensagemDeRetorno = String(r / 1000000.0, 2) + " MB/s";
} else if (r >= 1000.0) {
mensagemDeRetorno = String(r / 1000.0, 2) + " KB/s"; // Se a taxa for maior ou igual a 1000.0, formata a velocidade em KB/s
} else {
mensagemDeRetorno = String((int)r) + " bytes/s"; // Caso contrário, formata a velocidade em bytes/s
}
}
return mensagemDeRetorno; // Retorna o buffer contendo a taxa de velocidade formatada
}
void realizarSpeedTest() {
// Início do teste de velocidade de escrita/leitura com LittleFS
Serial.println("Começando Teste de Velocidade de escrita/leitura com LittleFS");
// Mensagem indicando o processo de iniciar o sistema de arquivos LittleFS
Serial.println("Iniciando LittleFS...");
#ifdef ESP32 || ESP8266 // Verifica se o código está sendo compilado para um dispositivo ESP8266 ou ESP32
if (!LittleFS.begin(true)) { // Inicia o sistema de arquivos LittleFS, formatando-o se algum erro ocorrer
Serial.println("Não foi possível iniciar, abortando"); // Caso a inicialização falhe, é mostrada uma mensagem de erro no monitor serial e o programa é encerrado
return;
}
#else // se o código estiver sendo compilado para qualquer dispositivo que não seja ESP8266 ou ESP32, como a placa Raspberry Pi Pico
if (!LittleFS.begin()) { // Inicia o sistema de arquivos LittleFS
Serial.println("Não foi possível iniciar, abortando"); // Caso a inicialização falhe, é mostrada uma mensagem de erro no monitor serial e o programa é encerrado
return;
}
#endif // marca o final do bloco condicional #ifdef e indica o término da verificação condicional
// Mensagem indicando o processo de formatação do sistema de arquivos LittleFS
Serial.println("Fazendo formatação...");
if (!LittleFS.format()) { // Formata o sistema de arquivos LittleFS
Serial.println("Não foi possível formatar, abortando"); // caso não seja possível formatar o sistema de arquivos, mostra uma mensagem de erro no monitor serial e o programa é encerrado
return;
}
// Declara um array de 256 posições, sendo 1 byte para cada posição
uint8_t data[256];
// Preenche o array de dados com valores de 0 a 255
for (int i = 0; i < 256; i++) {
data[i] = (uint8_t)i;
}
// Imprime uma linha em branco
Serial.println();
// Imprime uma mensagem indicando que o arquivo será escrito no sistema de arquivos
Serial.print("Escrevendo arquivo com tamanho de ");
Serial.print(TESTSIZEKB); // imprime o tamanho do arquivo em kilobytes
Serial.print(" kilobytes (");
Serial.print(TESTSIZEKB * 1024); // converte o tamanho do arquivo de kilobytes para bytes (a cada 1024 bytes, se tem 1 kilobyte)
Serial.println(" bytes) ...(pode demorar um pouco) ");
// Inicia a contagem de tempo e registra o tempo atual, para posterior cálculo de velocidade de escrita
unsigned long start = millis();
// Cria o arquivo para gravação, passando parâmetros, sendo o primeiro o nome do arquivo com sua extensão e o segundo sendo o modo de abertura do arquivo (w: modo de escrita)
File f = LittleFS.open("/arquivoDeTeste.bin", "w");
// Se o arquivo não puder ser aberto, imprime uma mensagem de erro e aborta o teste
if (!f) {
Serial.println("Não é possível abrir o arquivo para gravação, abortando");
return;
}
// Escreve os dados no arquivo
for (int i = 0; i < TESTSIZEKB; i++) { // laço de repetição 'for' que será executado 256 vezes
for (int j = 0; j < 4; j++) { // laço de repetição 'for' que será executado 4 vezes
f.write(data, 256); // escreve no arquivo o conteúdo do array 'data' que possui 256 bytes
// RESULTADO: ao escrever no arquivo 256 bytes por 4 vezes em que estas 4 vezes são executadas 256 vezes, é escrito ao total 262144 bytes (256 kilobytes):
// 256 bytes * 4 vezes * 256 vezes =
// 256 * 4 * 256 =
// 262144 bytes = 256 kilobytes (262144 /256 = 256 kilobytes)
}
}
// Fecha o arquivo
f.close();
// Interrompe a contagem de tempo e registra o tempo atual, para posterior cálculo de velocidade de escrita
unsigned long stop = millis();
// Imprime uma mensagem indicando o tempo gasto para escrever o arquivo
Serial.print("==> Tempo para gravar ");
Serial.print(TESTSIZEKB); // 256 kilobytes
Serial.print(" kilobytes (");
Serial.print(TESTSIZEKB * 1024); // converte kilobytes em bytes (cada kilobyte tem 1024 bytes)
Serial.print(" bytes) = ");
Serial.print(stop - start); // subtrai o tempo de início do tempo de parada, calculando assim o tempo gasto
Serial.println(" milissegundos");
// Imprime uma mensagem indicando a velocidade de escrita
Serial.print("==> Velocidade de escrita: ");
Serial.println(rate(start, stop, TESTSIZEKB * 1024)); // A velocidade de escrita é impressa no monitor serial, onde é calculada usando a função rate, em que é passado como parâmetros:
// - o tempo de parada (stop),
// - o tempo de início (start) e
// - a quantidade de dados transitado em bytes.
// O tamanho do arquivo é multiplicado por 1024 para obter o tamanho em bytes.
Serial.println();
// Imprime uma mensagem indicando que o arquivo será lido sequencialmente
Serial.print("Lendo arquivo sequencialmente com tamanho de ");
Serial.print(TESTSIZEKB); // imprime o tamanho do arquivo em kilobytes
Serial.print(" kilobytes (");
Serial.print(TESTSIZEKB * 1024); // converte o tamanho do arquivo de kilobytes para bytes (a cada 1024 bytes, se tem 1 kilobyte)
Serial.println(" bytes) ...");
// Inicia a contagem de tempo e registra o tempo atual, para posterior cálculo de velocidade de leitura
start = millis();
// Abre o arquivo para leitura, passando parâmetros, sendo o primeiro o nome do arquivo com sua extensão e o segundo sendo o modo de abertura do arquivo (r: modo de leitura)
f = LittleFS.open("/arquivoDeTeste.bin", "r");
// Se o arquivo não puder ser aberto, imprime uma mensagem de erro e aborta o teste
if (!f) {
Serial.println("Não é possível abrir o arquivo para leitura, abortando");
return;
}
// Lê o arquivo sequencialmente
for (int i = 0; i < TESTSIZEKB; i++) { // laço de repetição 'for' que será executado 256 vezes
for (int j = 0; j < 4; j++) { // laço de repetição 'for' que será executado 4 vezes
f.read(data, 256); // lê 256 bytes do conteúdo do arquivo e passa para o array 'data'
}
// RESULTADO: ao ler o arquivo com cada leitura tendo 256 bytes, e isto ocorrendo por 4 vezes em que estas 4 vezes são executadas 256 vezes, é lido ao total 262144 bytes (256 kilobytes):
// 256 bytes * 4 vezes * 256 vezes =
// 256 * 4 * 256 =
// 262144 bytes = 256 kilobytes (262144 /256 = 256 kilobytes)
}
// Fecha o arquivo
f.close();
// Interrompe a contagem de tempo e registra o tempo atual, para posterior cálculo de velocidade de leitura
stop = millis();
// Imprime uma mensagem indicando o tempo gasto para ler o arquivo sequencialmente
Serial.print("==> Tempo para ler sequencialmente ");
Serial.print(TESTSIZEKB); // 256 kilobytes
Serial.print(" kilobytes (");
Serial.print(TESTSIZEKB * 1024); // converte kilobytes em bytes (cada kilobyte tem 1024 bytes)
Serial.print(" bytes) = ");
Serial.print(stop - start); // subtrai o tempo de início do tempo de parada, calculando assim o tempo gasto
Serial.println(" milissegundos");
// Imprime uma mensagem indicando a velocidade de leitura
Serial.print("==> Velocidade de leitura: ");
Serial.println(rate(start, stop, TESTSIZEKB * 1024)); // A velocidade de leitura é impressa no monitor serial, onde é calculada usando a função rate, em que é passado como parâmetros:
// - o tempo de parada (stop),
// - o tempo de início (start) e
// - a quantidade de dados transitado em bytes.
// O tamanho do arquivo é multiplicado por 1024 para obter o tamanho em bytes.
Serial.println();
// Imprime uma mensagem indicando o fim do teste
Serial.println("Fim do Teste de Velocidade de escrita/leitura");
}
void setup() {
// Inicializa a porta serial com 115200 bits por segundo de baud rate
Serial.begin(115200);
// Aguarda até que o monitor serial esteja pronto para receber dados, em que o usuário deve digitar e enviar qualquer dado pelo monitor serial
while (Serial.available() == false) {}
// Realiza o teste de velocidade de leitura e escrita no sistema de arquivos
realizarSpeedTest();
// Entra em um loop infinito
while (1) {
delay(10);
}
}
void loop() {}
<FS.h> e <LittleFS.h> para trabalhar com os sistemas de arquivos. A constante TESTSIZEKB define o tamanho do arquivo de teste em 256 kilobytes.
A função rate() é criada para calcular e formatar a taxa de velocidade em bytes por segundo com base nos parâmetros fornecidos: tempo inicial, tempo final e quantidade de bytes transferidos. Ela retorna uma mensagem formatada com a velocidade em bytes/s, KB/s ou MB/s.
A função realizarSpeedTest() é o núcleo do teste de velocidade. Ela inicia o sistema de arquivos LittleFS e formata-o. Em seguida, cria um arquivo chamado "arquivoDeTeste.bin" e grava 256KB de dados nele, divididos em quatro gravações de 256 bytes cada em que este ciclo de quatro gravações é executado 256 vezes. Veja abaixo o cálculo de quanto dado foi gravado pelo programa:
rate() é usada para calcular e formatar a velocidade de transferência.
A função setup() configura a porta serial com uma taxa de transmissão de 115200 bps e aguarda até que o monitor serial esteja pronto para receber dados (ou seja, aguarda até que o usuário envie qualquer dado pelo monitor serial). Em seguida, chama a função realizarSpeedTest() para executar o teste de velocidade. Por fim, ainda dentro da função setup(), é executado um loop infinito através da estrutura de iteração while passando como parâmetro um valor booleano verdadeiro.
A função loop() é vazia, não tendo nenhum código dentro dela.
Siga os passos abaixo para configurar a frequência da CPU da placa ESP8266:
| Placas e Configurações | SPIFFS | LittleFS | ||
| Leitura | Escrita | Leitura | Escrita | |
| Raspberry Pi Pico (Frequência de CPU padrão de 133 MHz) | -* | -* | 5.96 MB/s | 89.93 KB/s |
| Raspberry Pi Pico (Frequência de CPU overclock de 250 MHz) | -* | -* | 11.40 MB/s | 100.17 KB/s |
| ESP8266 (Frequência de Flash padrão de 40 MHz + Frequência de CPU padrão de 80 MHz) | 1.52 MB/s | 39.33 KB/s | 2.36 MB/s | 51.98 KB/s |
| ESP8266 (Frequência de Flash padrão de 40 MHz + Frequência de CPU overclock de 160 MHz) | 2.30 MB/s | 42.83 KB/s | 3.32 MB/s | 54.32 KB/s |
| ESP8266 (Frequência de Flash overclock de 80 MHz + Frequência de CPU padrão de 80 MHz) | 1.82 MB/s | 55.65 KB/s | 2.88 MB/s | 62.08 KB/s |
| ESP8266 (Frequência de Flash overclock de 80 MHz + Frequência de CPU overclock de 160 MHz) | 3.05 MB/s | 63.61 KB/s | 4.52 MB/s | 65.90 KB/s |
| ESP32 (Frequência de Flash padrão de 80 MHz + Frequência de CPU padrão de 240 MHz) | 1.31 MB/s | 158.88 KB/s | 2.38 MB/s | 88.65 KB/s |
| ESP32 (Frequência de Flash underclock de 40 MHz + Frequência de CPU padrão de 240 MHz) | 1.32 MB/s | 158.78 KB/s | 2.36 MB/s | 79.32 KB/s |
| ESP32 (Frequência de Flash padrão de 80 MHz + Frequência de CPU underclock de 80 MHz) | 463.15 KB/s | 70.00 KB/s | 848.36 KB/s | 57.86 KB/s |
| ESP32 (Frequência de Flash underclock de 40 MHz + Frequência de CPU underclock de 80 MHz) | 460.71 KB/s | 69.06 KB/s | 851.12 KB/s | 58.01 KB/s |
Veja no gráfico abaixo a análise da velocidade de leitura do sistema de arquivos SPIFFS em relação ao sistema de arquivos LittleFS.
Veja no gráfico abaixo a análise da velocidade de escrita e de leitura do sistema de arquivos SPIFFS em relação ao sistema de arquivos LittleFS.








Neste caso, foi criado um arquivo de texto chamado teste com o seguinte conteúdo:
abc 123

/******************************************************************************
Lendo o conteúdo do arquivo que foi transferido para o LittleFS
Criado em 24 de Julho de 2023
por Michel Galvão (https://micsg.com.br)
Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits
https://www.eletrogate.com/
Confira mais detalhes deste sketch em https://blog.eletrogate.com/
******************************************************************************/
#include <FS.h> // Inclui a biblioteca para trabalhar com sistemas de arquivos no Arduino.
#include <LittleFS.h> // Inclui a biblioteca específica para usar o sistema de arquivos LittleFS.
void setup() {
Serial.begin(115200); // Inicializa a comunicação serial com uma taxa de baud rate de 115200.
if (!LittleFS.begin()) { // Inicializa o sistema de arquivos LittleFS. Se falhar, exibe uma mensagem no monitor serial e retorna.
Serial.println("Falha na montagem do sistema de arquivos LittleFS");
return;
}
Serial.println("Lendo arquivo /teste.txt"); // Exibe no monitor serial a mensagem indicando que o arquivo /teste.txt será lido.
File file = LittleFS.open("/teste.txt", "r"); // Abre o arquivo /teste.txt em modo de leitura ("r") e armazena o objeto de arquivo em 'file'.
if (!file) { // Verifica se o arquivo foi aberto com sucesso. Se não, exibe uma mensagem de erro e retorna.
Serial.println("Falha ao abrir arquivo para leitura");
return;
}
Serial.println("Conteúdo do arquivo: "); // Exibe no monitor serial a mensagem indicando que o conteúdo do arquivo será mostrado.
while (file.available()) { // Enquanto houver dados disponíveis para leitura no arquivo...
Serial.write(file.read()); // Lê um byte do arquivo e o envia para o monitor serial para impressão.
}
file.close(); // Fecha o arquivo após a leitura ter sido concluída.
}
void loop() {}// Vazio, pois não precisamos de um loop contínuo para este sketch.
// O código é executado apenas uma vez na função 'setup()'.
#include <FS.h> e #include <LittleFS.h>. Essas bibliotecas permitem ao programa trabalhar com o sistema de arquivos LittleFS.
A função setup() é executada uma vez quando o ESP8266 é ligado ou reiniciado. Nela, a comunicação serial é iniciada com uma taxa de baud rate de 115200, permitindo que a saída seja mostrada no monitor serial. Em seguida, o programa tenta inicializar o sistema de arquivos LittleFS com a função LittleFS.begin(). Se a inicialização falhar, uma mensagem relatando uma falha na montagem do sistema de arquivos LittleFS é enviada para o monitor serial, e o programa retorna, encerrando a execução.
Após a inicialização bem-sucedida do LittleFS, o programa tenta abrir o arquivo "teste.txt" em modo de leitura ("r") usando a função LittleFS.open(). Se o arquivo não puder ser aberto, por exemplo, se ele não existir, o programa imprime a mensagem "Falha ao abrir arquivo para leitura" no monitor serial e retorna, encerrando a execução.
Caso o arquivo "teste.txt" seja aberto com sucesso, o programa entra em um loop while para ler e imprimir o conteúdo do arquivo. A função file.available() é usada para verificar se ainda há dados para ler no arquivo. Dentro do loop, a função file.read() lê um byte por vez do arquivo, e a função Serial.write() é usada para imprimir esse byte no monitor serial. O loop continua até que todo o conteúdo do arquivo tenha sido lido. Após a leitura ter sido concluída, o arquivo é fechado usando a função file.close().
O loop void loop() {} está vazio, o que significa que após a execução da função setup(), o programa não executa mais nada e fica em um loop infinito sem fazer nada.








Neste caso, foi criado um arquivo de texto chamado teste com o seguinte conteúdo:
abc 123 A placa eh ESP32









Neste caso, foi criado um arquivo de texto chamado teste com o seguinte conteúdo:
abc 123 A placa eh Raspberry Pi Pico


Como visto, para uso do sistema de arquivos LittleFS, é utilizada as bibliotecas FS.h e LittleFS.h. Veja abaixo os principais métodos da biblioteca LittleFS:
bool setConfig(const FSConfig &cfg): permite configurar os parâmetros de um sistema de arquivos antes da montagem. Todos os sistemas de arquivos têm seus próprios "___Config" (ou seja: SDFSConfig, LittleFSConfig ou SPIFFSConfig) com seu conjunto personalizado de opções. Todos os sistemas de arquivos permitem habilitar/desabilitar explicitamente a formatação quando as montagens falham. Se você não chamar o método setConfig antes de executar begin(), obterá o comportamento e a configuração padrão do sistema de arquivos. Por padrão, O SPIFFS formatará automaticamente o sistema de arquivos se não puder montá-lo, enquanto o SDFS não;bool begin(): Este método monta o sistema de arquivos. Deve ser chamado antes que qualquer outra operação no LittleFS seja feita. Retorna true se o sistema de arquivos foi montado com sucesso, false caso contrário;void end(): Este método desmonta o sistema de arquivos;bool format(): Formata o sistema de arquivos. Pode ser chamado antes ou depois da chamada begin. Retorna true se a formatação foi bem-sucedida e false, caso contrário;File open(const char* path, const char* mode): Abre um arquivo. path deve ser um caminho absoluto começando com uma barra (por exemplo, /dir/filename.txt). mode é uma string que especifica o modo de acesso. Pode ser “r”, “w”, “a”, “r+”, “w+”, “a+” (Veja mais abaixo sobre os modos de acesso). O significado desses modos é o mesmo da função fopen() da linguagem C++. O método retorna o objeto File. Para verificar se o arquivo foi aberto com sucesso, use o operador booleano da classe File;
"r" ➜ Arquivo de texto aberto para leitura. O stream (fluxo) é posicionado no início do arquivo;"r+" ➜ Aberto para leitura e escrita. O stream (fluxo) é posicionado no início do arquivo;"w" ➜ Cria um arquivo vazio. Se já existir um arquivo com o mesmo nome, seu conteúdo será descartado e o arquivo será tratado como um novo arquivo vazio. O stream (fluxo) é posicionado no início do arquivo."w+" ➜ Aberto para leitura e escrita. O arquivo é criado se ele não existir, caso contrário, seu conteúdo será descartado e o arquivo será tratado como um novo arquivo vazio. O stream (fluxo) é posicionado no início do arquivo."a" ➜ Aberto para anexar (escrever no final do arquivo). O arquivo é criado se não existir. O stream (fluxo) é posicionado no final do arquivo."a+" ➜ Aberto para leitura e anexação (escrita no final do arquivo). O arquivo é criado se ele não existir. O stream (fluxo) é posicionado no início do arquivo, caso seja feita leitura. Caso seja anexação, o stream (fluxo) é posicionado ao final do arquivo.bool exists(const char* path): Retorna true se existir um arquivo com o caminho especificado (path), caso contrário retorna false ;bool mkdir(const char* path): Retorna true se a criação do diretório foi bem-sucedida, caso contrário retorna false;bool rmdir(const char* path): Retorna true se o diretório foi removido com sucesso, caso contrário retorna false;Dir openDir(const char* path): Abre um diretório dado seu caminho absoluto. Retorna um objeto Dir;bool remove(const char* path): Exclui o arquivo dado seu caminho absoluto. Retorna true se o arquivo foi excluído com sucesso, caso contrário retorna false;bool rename(const char* pathFrom, const char* pathTo): Renomeia o arquivo de pathFrom para pathTo. Os caminhos devem ser absolutos. Retorna true se o arquivo foi renomeado com sucesso, caso contrário retorna false;bool next(): Retorna true enquanto houver arquivos no diretório para iterar. Deve ser chamado antes de chamar as funções fileName(), fileSize()e openFile();String fileName(): Retorna o nome do arquivo atual apontado pelo iterador;size_t fileSize(): Retorna o tamanho do arquivo atual apontado pelo iterador;time_t fileTime(): Retorna o tempo de gravação, com o tipo time_t, do arquivo atual apontado pelo iterador;time_t fileCreationTime(): Retorna o tempo de criação, com o tipo time_t, do arquivo atual apontado pelo iterador;bool isFile() const: Retorna true se o "arquivo" atual apontado pelo iterador for um arquivo, e, caso contrário, retorna false;bool isDirectory() const: Retorna true se o "arquivo" atual apontado pelo iterador interno for um diretório, e, caso contrário, retorna false;bool rewind(): Redefine o ponteiro interno para o início do diretório;bool seek(uint32_t offset, SeekMode mode): Esta função se comporta como a função fseek() da linguagem C++. Retorna true se a posição foi definida com sucesso e false se falhado. Dependendo do valor de mode, ele move a posição atual em um arquivo da seguinte forma:
SeekSet ➜ a posição é definida em tantos bytes quanto definido em offset bytes desde o início;SeekCur ➜ a posição atual é movida por tantos bytes quanto definido em offset;SeekEnd ➜ a posição será definida em tantos bytes quanto definido em offset a partir do final do arquivo;size_t position() const: Retorna a posição atual dentro do arquivo, em bytes;size_t size() const: Retorna o tamanho do arquivo, em bytes;const char* name() const: Retorna o nome "curto" do arquivo (sem caminho), como const char*. Converta-o em String para armazenamento na memória;const char* fullName() const: Retorna o nome do arquivo de caminho completo como const char*;time_t getLastWrite(): Retorna o horário da última gravação do arquivo e válido apenas para arquivos abertos no modo somente leitura. Se um arquivo for aberto para gravação, o tempo retornado pode ser indeterminado;time_t getCreationTime(): Retorna a hora de criação do arquivo, se disponível;bool isFile() const: Retorna true se este "arquivo" apontar para um arquivo real;bool isDirectory() const: Retorna true se este "arquivo" apontar para um diretório;void close(): Fecha o arquivo. Nenhuma outra operação deve ser executada no objeto File após a chamada desta função;File openNextFile(): Abre o próximo arquivo no diretório apontado pelo objeto File;void rewindDirectory(): Redefine o ponteiro de openNextFile para o topo do diretório./******************************************************************************
Exemplo de uso da LittleFS na placa ESP32
Criado em 06 de Agosto de 2023
por Michel Galvão (https://micsg.com.br)
Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits
https://www.eletrogate.com/
Confira mais detalhes deste sketch em https://blog.eletrogate.com/
******************************************************************************/
#include <WiFi.h> // Inclui a biblioteca WiFi
#include "time.h" // Biblioteca para acesso ao servidor NTP
#include "credenciais.h" // arquivo de cabeçalho que armazena as credenciais de acesso à rede WiFi
#include <LittleFS.h> // Inclui a biblioteca específica para usar o sistema de arquivos LittleFS
#include <FS.h> // Inclui a biblioteca para trabalhar com sistemas de arquivos no Arduino
const char* ntpServer1 = "time.google.com"; // Servidor NTP primário
const char* ntpServer2 = "a.ntp.br"; // Servidor NTP secundário
const char* ntpServer3 = "time.cloudflare.com"; // Servidor NTP terciário
long gmtOffset_sec = -3 * 60 * 60; // Deslocamento do horário de Greenwich (GMT) em segundos (fuso horário -3 horas)
const int daylightOffset_sec = 0; // Deslocamento de horário de verão em segundos (nenhum neste caso)
void setup() {
Serial.begin(115200); // Inicializa a comunicação serial
if (!LittleFS.begin(true)) { // Inicializa o sistema de arquivos LittleFS
Serial.println("Falha ao inicializar o sistema de arquivos LittleFS");
return;
}
Serial.println();
Serial.println("-------------------------------------------------------");
Serial.printf("Conectando ao WiFi na rede %s \n", ssid);
WiFi.begin(ssid, senha); // Conecta à rede WiFi utilizando as credenciais definidas em "credenciais.h"
if (WiFi.waitForConnectResult() == WL_CONNECTED) { // Espera a conexão WiFi ser estabelecida
// Exibe mensagem de conexão bem-sucedida
Serial.println("Conectado!");
} else {
// Exibe mensagem de erro na conexão e reinicia o ESP32
Serial.println("Erro na conexão WiFi. Reiniciando...");
delay(2000);
ESP.restart();
}
// Configura o gerenciamento de tempo com os servidores NTP definidos
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2, ntpServer3);
struct tm timeinfo; // Estrutura para armazenar as informações do horário
Serial.println("Obtendo horário do servidor NTP...");
while (!getLocalTime(&timeinfo)) { // Solicita o horário ao servidor NTP
// Exibe mensagem de falha na obtenção do horário e tenta novamente, somente em caso de falha na obtenção
Serial.println("Falha ao obter horário NTP. Tentando novamente...");
delay(2000);
}
Serial.println("Obtido!"); // Exibe mensagem de sucesso na obtenção do horário
String tempoDeInicializacao; // Declaração da variável que irá armazenar o horário de inicialização formatado
tempoDeInicializacao += timeinfo.tm_hour; // Adiciona a hora à string de registro
tempoDeInicializacao += ":";
tempoDeInicializacao += timeinfo.tm_min; // Adiciona o minuto à string de registro
tempoDeInicializacao += ":";
tempoDeInicializacao += timeinfo.tm_sec; // Adiciona o segundo à string de registro
tempoDeInicializacao += " ";
tempoDeInicializacao += timeinfo.tm_mday; // Adiciona o dia do mês à string de registro
tempoDeInicializacao += "/";
tempoDeInicializacao += timeinfo.tm_mon + 1; // Adiciona o mês à string de registro (adicionando 1 pois o valor é baseado em zero)
tempoDeInicializacao += "/";
tempoDeInicializacao += timeinfo.tm_year + 1900; // Adiciona o ano à string de registro (adicionando 1900 pois o valor é baseado em 1900)
File file = LittleFS.open("/registro.txt", "a"); // Abre o arquivo "registro.txt" em modo de anexação
if (!file) { // Verifica se ocorreu uma falha ao abrir o arquivo "registro.txt"
// Exibe mensagem de falha na abertura do arquivo e impede do programa continuar
Serial.println("Falha ao abrir o arquivo registro.txt. Programa impedido de continuar.");
return;
}
// Adiciona conteúdo ao arquivo "registro.txt" e verifica se ocorreu uma falha ao fazer esta operação
if (!file.print("Inicialização detectada em " + tempoDeInicializacao + "\n")) {
// Exibe mensagem de falha na escrita do arquivo
Serial.println("Falha ao adicionar conteúdo ao arquivo registro.txt. Programa impedido de continuar.");
}
Serial.println("Inicialização do ESP32 foi registrada\n"); // Exibe mensagem de sucesso no registro da inicialização do ESP32
Serial.println("Relatório dos horários de inicialização do ESP32:"); // Exibe mensagem informando que será mostrado o relatório de
// horários de inicialização do ESP32.
file.close(); // Fecha o arquivo
file = LittleFS.open("/registro.txt", "r"); // Abre o arquivo "registro.txt" em modo de leitura
while (file.available()) { // Enquanto houver conteúdo disponível no arquivo, o loop será executado
Serial.write(file.read()); // Lê e escreve o conteúdo do arquivo no monitor serial
}
file.close(); // Fecha o arquivo
while (true) {} // Loop infinito, pois não pe feito mais nada
}
void loop() {} // Função loop vazia, pois todo o trabalho é realizado na função setup()
const char * ssid = "SSID"; // define o nome SSID de sua rede WiFi. const char * senha = "SENHA"; // define a senha de sua rede WiFi.
"Exemplo_ESP32.ino" e "credenciais.h". O sketch utiliza as bibliotecas WiFi e time.h para conectar-se a uma rede WiFi e obter a hora atual de um servidor NTP. As credenciais de acesso à rede WiFi (SSID e senha) são armazenadas em um arquivo chamado "credenciais.h".
No início do código, são incluídas as bibliotecas necessárias para a comunicação com a rede WiFi, acesso ao servidor NTP e o uso do sistema de arquivos LittleFS. As informações dos servidores NTP e deslocamentos de horário são definidas em variáveis.
Na função setup(), a comunicação serial é iniciada para possibilitar a depuração e exibição de informações no monitor serial. Em seguida, o sistema de arquivos LittleFS é inicializado. O ESP32 tenta conectar-se à rede WiFi usando as credenciais fornecidas em "credenciais.h". Se a conexão WiFi for estabelecida, uma mensagem de sucesso é exibida; caso contrário, o ESP32 é reiniciado.
O acesso ao servidor NTP é configurado com os dados fornecidos nas variáveis no início do código. Como redundância, é definido três endereços diferentes de servidores NTP, em que caso algum deles falhe, ainda terá outro para obter o horário. A função getLocalTime() é utilizada para obter a hora atual do servidor NTP. O horário é formatado em uma string e registrado em um arquivo chamado "registro.txt" usando a biblioteca LittleFS.
Em seguida, o programa exibe no monitor serial o relatório dos horários de inicialização do ESP32, lendo e exibindo o conteúdo do arquivo "registro.txt".
Por fim, na função setup(), o programa entra em um loop infinito, pois não é feito mais nada no programa.
A função loop() está vazia, pois todo o trabalho é realizado na função setup().
O arquivo "credenciais.h" contém apenas duas constantes: ssid e senha, que armazenam as informações de acesso à rede WiFi. Lembre-se: altere os valores das respectivas variáveis para que o ESP32 possa se conectar em sua rede WiFi com internet.
/******************************************************************************
Exemplo de uso da LittleFS na placa Raspberry Pi Pico
Criado em 06 de Agosto de 2023
por Michel Galvão (https://micsg.com.br)
Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits
https://www.eletrogate.com/
Confira mais detalhes deste sketch em https://blog.eletrogate.com/
******************************************************************************/
#include <LittleFS.h> // Inclui a biblioteca específica para usar o sistema de arquivos LittleFS.
#include <FS.h> // Inclui a biblioteca para trabalhar com sistemas de arquivos no Arduino.
void listarDiretorio(String caminho); // Protótipo da função listarDiretorio
void setup() {
Serial.begin(115200); // Inicializa a comunicação serial com baud rate 115200
delay(5000); // Aguarda 5 segundos
if (!LittleFS.begin()) { // Inicializa o sistema de arquivos LittleFS e verifica se houve falha
// se houve falhas, exibe a mensagem de alerta ao usuário e impede do programa continuar
Serial.println("Falha ao inicializar o sistema de arquivos LittleFS");
return;
}
const char* raiz = "/"; // Define o caminho raiz como uma string contendo apenas o caractere '/'
Serial.println(raiz);
listarDiretorio(raiz); // Chama a função para listar o diretório (incluindo todos arquivos e subdiretórios) a partir do caminho raiz (função recursiva)
}
void loop() {} // Função loop vazia, pois todo o trabalho é realizado na função setup()
// Função recursiva para listar todos os arquivos e subdiretórios de um determinado caminho
void listarDiretorio(String caminho) {
Dir obj = LittleFS.openDir(caminho); // Abre o diretório especificado pelo caminho
while (obj.next()) { // Loop para percorrer os arquivos e subdiretórios do diretório
if (obj.isFile()) { // Verifica se o objeto atual é um arquivo
File file = obj.openFile("r"); // Abre o arquivo atual no modo leitura ("r")
Serial.print('\t'); // Imprime um caractere de tabulação no monitor serial
Serial.printf("%s: ", file.name()); // Imprime o nome do arquivo atual no monitor serial
while (file.available()) { // Loop para ler e imprimir o conteúdo do arquivo
Serial.write(file.read()); // Lê e imprime o próximo byte do arquivo no monitor serial
}
Serial.println(); // Imprime uma nova linha no monitor serial para separar os arquivos
} else if (obj.isDirectory()) { // Verifica se o objeto atual é um diretório
File file = obj.openFile("r"); // Abre o diretório atual no modo leitura ("r")
Serial.println(file.fullName()); // Imprime o caminho completo do diretório no monitor serial
listarDiretorio(file.fullName()); // Chama recursivamente a função para listar o diretório atual (explorando subdiretórios)
}
}
}
setup(), a comunicação serial é iniciada para possibilitar a depuração e exibição de informações no monitor serial. Em seguida, o sistema de arquivos LittleFS é inicializado.
A função listarDiretorio() é a parte central do programa, implementada de forma recursiva. Essa função recebe um caminho como parâmetro e, a partir desse caminho, abre o diretório correspondente. Em seguida, um loop é utilizado para percorrer os arquivos e subdiretórios dentro desse diretório.
Dentro do loop da função listarDiretorio(), são feitas as verificações necessárias para determinar se o objeto atual é um arquivo ou um diretório. Se o objeto é um arquivo, ele é aberto em modo leitura e o nome do arquivo é impresso no monitor serial, seguido do conteúdo do arquivo. Se o objeto é um diretório, seu caminho completo é impresso no monitor serial, e a função listarDiretorio() é chamada recursivamente para explorar os subdiretórios dentro desse diretório.
A função listarDiretorio() continua a exploração dos subdiretórios recursivamente até que todos os arquivos e subdiretórios sejam listados. O uso de recursão permite que o código explore a hierarquia de diretórios sem a necessidade de usar listas para armazenar os caminhos. O programa é projetado para funcionar apenas uma vez no setup(), pois o loop é vazio, não havendo necessidade de executar o código repetidamente.
/******************************************************************************
Exemplo de uso da LittleFS na placa ESP8266
Criado em 06 de Agosto de 2023
por Michel Galvão (https://micsg.com.br)
Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits
https://www.eletrogate.com/
Confira mais detalhes deste sketch em https://blog.eletrogate.com/
******************************************************************************/
#include <LittleFS.h> // Inclui a biblioteca específica para usar o sistema de arquivos LittleFS.
#include <FS.h> // Inclui a biblioteca para trabalhar com sistemas de arquivos no Arduino.
int listarDiretorios(const char *path, int qtdDiretorios = 0); // Protótipo da função para listar diretórios
void criarDiretorio(const char *path); // Protótipo da função para criar diretórios
void deletarDiretorio(const char *path); // Protótipo da função para deletar diretórios
void criarArquivo(const char *path); // Protótipo da função para criar arquivos
void apagarArquivo(const char *path); // Protótipo da função para apagar arquivos
void escreverArquivo(const char *path, const char *linha); // Protótipo da função para escrever em arquivos
void lerArquivo(const char *path); // Protótipo da função para ler arquivos
void anexarArquivo(const char *path, const char *linha); // Protótipo da função para anexar texto em arquivos
void renomearArquivo(const char *path); // Protótipo da função para renomear arquivos
void setup() {
Serial.begin(115200); // Inicializa a comunicação serial
Serial.println();
delay(500); // Aguarda 500ms
Serial.println("Iniciando LittleFS..."); // Imprime mensagem indicando início da inicialização do LittleFS
if (!LittleFS.begin()) { // Verifica se houve falha ao inicializar o sistema de arquivos LittleFS
Serial.println("Falha ao inicializar o sistema de arquivos LittleFS. Programa impedido de continuar");
return; // Encerra o programa caso haja falha
}
Serial.println("Sistema de arquivos LittleFS iniciado!"); // Imprime mensagem indicando que o LittleFS foi iniciado
Serial.println();
Serial.println("Listagem do diretório '/': "); // Imprime mensagem indicando a listagem do diretório raiz
Serial.print(listarDiretorios("/") == 0 ? " -> Diretório vazio\n" : ""); // Verifica se o diretório está vazio e imprime mensagem adequada
delay(2000); // Aguarda 2 segundos (apenas para visualização melhorada no monitor serial)
criarDiretorio("/teste"); // Cria o diretório '/teste'
delay(2000); // Aguarda 2 segundos
Serial.println();
Serial.println("Listagem do diretório '/': "); // Imprime mensagem indicando a listagem do diretório raiz
Serial.print(listarDiretorios("/") == 0 ? " -> Diretório vazio\n" : ""); // Verifica se o diretório está vazio e imprime mensagem adequada
delay(2000); // Aguarda 2 segundos
criarArquivo("/teste/arquivo.txt"); // Cria o arquivo '/teste/arquivo.txt'
delay(2000); // Aguarda 2 segundos
criarArquivo("/teste/arquivo.txt"); // Cria o arquivo '/teste/arquivo.txt' (já existente, apenas para demonstração)
delay(2000); // Aguarda 2 segundos
Serial.println();
Serial.println("Listagem do diretório '/': "); // Imprime mensagem indicando a listagem do diretório raiz
Serial.print(listarDiretorios("/") == 0 ? " -> Diretório vazio\n" : ""); // Verifica se o diretório está vazio e imprime mensagem adequada
delay(2000); // Aguarda 2 segundos
escreverArquivo("/teste/arquivo.txt", "ABC123"); // Escreve "ABC123" no arquivo '/teste/arquivo.txt'
delay(2000); // Aguarda 2 segundos
lerArquivo("/teste/arquivo.txt"); // Lê e imprime o conteúdo do arquivo '/teste/arquivo.txt'
delay(2000); // Aguarda 2 segundos
anexarArquivo("/teste/arquivo.txt", "\nX\nY\nZ\n789"); // Anexa no arquivo '/teste/arquivo.txt' o seguinte conteúdo:
// X
// Y
// Z
// 789
delay(2000); // Aguarda 2 segundos
lerArquivo("/teste/arquivo.txt"); // Lê e imprime o conteúdo do arquivo '/teste/arquivo.txt'
delay(2000); // Aguarda 2 segundos
renomearArquivo("/teste/arquivo.txt", "/arquivoABC.txt"); // Renomeia o arquivo '/teste/arquivo.txt' para '/arquivoABC.txt'
delay(2000); // Aguarda 2 segundos
Serial.println();
Serial.println("Listagem do diretório '/': "); // Imprime mensagem indicando a listagem do diretório raiz
Serial.print(listarDiretorios("/") == 0 ? " -> Diretório vazio\n" : ""); // Verifica se o diretório está vazio e imprime mensagem adequada
delay(2000); // Aguarda 2 segundos
apagarArquivo("/arquivoABC.txt"); // Apaga o arquivo '/arquivoABC.txt'
delay(2000);
deletarDiretorio("/teste"); // Deleta o diretório '/teste' e seu conteúdo
delay(2000); // Aguarda 2 segundos
Serial.println();
Serial.println("Listagem do diretório '/': "); // Imprime mensagem indicando a listagem do diretório raiz
Serial.print(listarDiretorios("/") == 0 ? " -> Diretório vazio\n" : ""); // Verifica se o diretório está vazio e imprime mensagem adequada
delay(2000); // Aguarda 2 segundos
while (true) {
delay(1);
} // Loop infinito, pois não é feito mais nada
}
void loop() {} // Função loop vazia, pois todo o trabalho é realizado na função setup()
int listarDiretorios(const char *path, int qtdDiretorios) { // Função para listar diretórios
Dir obj = LittleFS.openDir(path); // Abre o diretório especificado
while (obj.next()) { // Itera sobre os arquivos e subdiretórios no diretório
File file = obj.openFile("r"); // Abre o arquivo para leitura
Serial.printf("/%s\n", file.fullName()); // Imprime o caminho completo do arquivo
if (obj.isDirectory()) { // Verifica se o objeto é um diretório
qtdDiretorios++; // Incrementa o contador de diretórios
listarDiretorios(file.fullName(), qtdDiretorios); // Chama recursivamente a função para listar subdiretórios
}
}
return qtdDiretorios; // Retorna a quantidade total de diretórios
}
void criarDiretorio(const char *path) { // Função para criar diretório
Serial.println();
Serial.printf("Criando diretório %s\n", path); // Imprime mensagem indicando a criação do diretório
if (!LittleFS.mkdir(path)) { // Verifica se houve falha ao criar o diretório
Serial.printf("Falha ao criar diretório '%s'\n", path); // Imprime mensagem de falha
} else {
Serial.printf("Diretório '%s' criado\n", path); // Imprime mensagem de sucesso
}
}
void deletarDiretorio(const char *path) { // Função para deletar diretório
Serial.println();
Serial.printf("Apagando diretório %s\n", path); // Imprime mensagem indicando a exclusão do diretório
if (LittleFS.exists(path)) { // Verifica se o diretório existe
LittleFS.rmdir(path); // Remove o diretório
Serial.printf("Diretório %s apagado\n", path); // Imprime mensagem indicando que o diretório foi apagado
} else {
Serial.println("O diretório não existe."); // Imprime mensagem de que o diretório não existe
}
}
void criarArquivo(const char *path) { // Função para criar arquivo
Serial.println();
Serial.printf("Criando arquivo %s\n", path); // Imprime mensagem indicando a criação do arquivo
if (!LittleFS.exists(path)) { // Verifica se o arquivo não existe
File arquivo = LittleFS.open(path, "w"); // Cria o arquivo
arquivo.close(); // Fecha o arquivo
Serial.println("Arquivo criado"); // Imprime mensagem de que o arquivo foi criado
} else {
Serial.println("O arquivo já existe."); // Imprime mensagem de que o arquivo já existe
}
}
void apagarArquivo(const char *path) { // Função para apagar arquivo
Serial.println();
Serial.printf("Apagando arquivo %s\n", path); // Imprime mensagem indicando a exclusão do arquivo
if (LittleFS.exists(path)) { // Verifica se o arquivo existe
LittleFS.remove(path); // Remove o arquivo
Serial.println("Arquivo apagado"); // Imprime mensagem indicando que o arquivo foi apagado
} else {
Serial.println("O arquivo não existe."); // Imprime mensagem de que o arquivo não existe
}
}
void escreverArquivo(const char *path, const char *linha) { // Função para escrever conteúdo em um arquivo
Serial.println();
Serial.printf("Escrevendo no arquivo %s\n", path); // Imprime mensagem indicando a escrita no arquivo
if (LittleFS.exists(path)) { // Verifica se o arquivo existe
File arquivo = LittleFS.open(path, "w"); // Abre o arquivo no modo de escrita
arquivo.print(linha); // Escreve a linha no arquivo
arquivo.close(); // Fecha o arquivo
Serial.println("Conteúdo escrito"); // Imprime mensagem indicando que o conteúdo foi escrito
} else {
Serial.println("O arquivo não existe."); // Imprime mensagem de que o arquivo não existe
}
}
void lerArquivo(const char *path) { // Função para ler um arquivo
Serial.println();
Serial.printf("Lendo o arquivo %s\n", path); // Imprime mensagem indicando a leitura do arquivo
if (LittleFS.exists(path)) { // Verifica se o arquivo existe
File arquivo = LittleFS.open(path, "r"); // Abre o arquivo no modo de leitura
while (arquivo.available()) { // Enquanto houver conteúdo disponível no arquivo
Serial.write(arquivo.read()); // Lê e imprime o conteúdo do arquivo
}
arquivo.close(); // Fecha o arquivo
Serial.println("\nConteúdo lido"); // Imprime mensagem indicando que o conteúdo foi lido
} else {
Serial.println("O arquivo não existe."); // Imprime mensagem de que o arquivo não existe
}
}
void anexarArquivo(const char *path, const char *linha) { // Função para anexar conteúdo em um arquivo
Serial.println();
Serial.printf("Anexando conteúdo no arquivo %s\n", path); // Imprime mensagem indicando a anexação de conteúdo no arquivo
if (LittleFS.exists(path)) { // Verifica se o arquivo existe
File arquivo = LittleFS.open(path, "a"); // Abre o arquivo no modo de anexar
arquivo.print(linha); // Anexa a linha ao arquivo
arquivo.close(); // Fecha o arquivo
Serial.println("Conteúdo anexado"); // Imprime mensagem indicando que o conteúdo foi anexado
} else {
Serial.println("O arquivo não existe."); // Imprime mensagem de que o arquivo não existe
}
}
void renomearArquivo(const char *path, const char *novoNome) { // Função para renomear um arquivo
Serial.println();
Serial.printf("Renomeando o arquivo %s para %s\n", path, novoNome); // Imprime mensagem indicando a renomeação do arquivo
if (LittleFS.exists(path)) { // Verifica se o arquivo existe
LittleFS.rename(path, novoNome); // Renomeia o arquivo
Serial.println("Arquivo renomeado"); // Imprime mensagem indicando que o arquivo foi renomeado
} else {
Serial.println("Arquivo não existe"); // Imprime mensagem de que o arquivo não existe
}
}
"Exemplo_ESP8266.ino" e "funcoes.ino".
O arquivo "Exemplo_ESP8266.ino" contém as funções setup() e listarDiretorios(). A função setup() é executada uma vez na inicialização do dispositivo e faz as principais operações desenvolvidadas no arquivo "funcoes.ino".
Neste arquivo, são incluídas as bibliotecas "LittleFS.h" e "FS.h", necessárias para trabalhar com o sistema de arquivos. Em seguida, são declarados os protótipos das funções que serão definidas no arquivo "funcoes.ino". Essas funções são responsáveis por:
setup(), a comunicação serial é iniciada para possibilitar a depuração e exibição de informações no monitor serial. Em seguida, o sistema de arquivos LittleFS é inicializado.
Ainda dentro da função setup(), são realizadas diversas operações usando as funções definidas no arquivo "funcoes.ino".
O arquivo "funcoes.ino" contém a implementação das funções prototipadas no arquivo "Exemplo_ESP8266.ino". Cada função realiza uma operação específica, como criar diretório, deletar diretório, criar arquivo, apagar arquivo, escrever conteúdo em arquivo, ler conteúdo de arquivo, anexar texto em arquivo e renomear arquivo. As funções são bem documentadas com comentários explicativos para facilitar a compreensão de seu funcionamento.
Em resumo, este exemplo utiliza o sistema de arquivos LittleFS para realizar operações em arquivos e diretórios na placa ESP8266. As funções são organizadas nos arquivos "Exemplo_ESP8266.ino" e "funcoes.ino" para manter o código mais claro e modular. O programa usa a comunicação serial para exibir informações sobre as operações realizadas e o estado do sistema de arquivos no monitor serial.
Em conclusão, o sistema de arquivos LittleFS apresenta notáveis vantagens sobre o SPIFFS, tornando-se uma escolha mais vantajosa para aplicações que exigem um eficiente gerenciamento de dados em dispositivos embarcados. A principal vantagem do LittleFS é o desempenho aprimorado em operações de leitura e gravação, especialmente em cenários com grande quantidade e variedade de arquivos, tornando assim o LittleFS uma opção preferencial. Além disso, a portabilidade e o suporte a várias plataformas tornam o LittleFS uma solução versátil e adequada para diversos dispositivos e sistemas embarcados. Gostaríamos de saber se você curtiu este post! Por favor, avalie e deixe um comentário sobre o conteúdo. E não esqueça de nos seguir no Instagram e nos marcar quando fizer algum projeto nosso: @eletrogate. Até a próxima!
|
Aprenda neste post, a usar o sistema de arquivos LittleFS, que possui mais desempenho do que o sistema SPIFFS, nas placas Raspberry Pi Pico, ESP32 e ESP8266.
Encontre tudo na Loja Eletrogate com frete grátis para compras acima de R$ 200