Manual

do

Maker

.

com

ESP32 e SPIFFS (agora LittleFS): Como ler um arquivo para uma variável

ESP32 e SPIFFS (agora LittleFS): Como ler um arquivo para uma variável

Já escrevi uma quantidade enorme de artigos utilizando SPIFFS, LittleFS e FAT no ESP32, sendo um dos mais interessantes esse aqui, sobre como manipular arquivos. Durante a escrita de um programa, busquei aqui no blog um código pronto para ler um arquivo para uma variável, evitando digitação. Para minha surpresa, todos os exemplos escrevem para a serial. Como todo bom preguiçoso, resolvi então procurar no Google pra dar um Ctrl+Chups, mas adivinha? Todos os exemplo pareciam fotocópia. Pensei:

"Se alguém for procurar um exemplo e não tiver muito costume com a linguagem, vai sofrer pra fazer a leitura".

Atualmente o sistema de arquivos do ESP32 é o LittleFS, que tem a mesma sintaxe do SPIFFS, bastando substituir as respectivas referências. Pode usar esse artigo tranquilamente com LittleFS.

Como ler um arquivo para uma variável no ESP32

Mas como todo bom preguiçoso, sei que vou precisar novamente dessa função, então resolvi fazer e escrever um artigo simples só pra ter um código para um futuro Ctrl+Chups. E como todo bom preguiçoso, prefiro usar String nesse caso, que dá um total de bytes digitados bem menor do que fazer um array de char e manipulá-lo a posteriori. A função para ler um arquivo para uma variável ficou assim:

String readFile(fs::FS &fs, const char * path){
    Serial.printf("Reading file: %s\n", path);

    File file = fs.open(path);
    if(!file || file.isDirectory()){
        Serial.println("Failed to open file for reading");
        return "";
    }

    Serial.print("Read from file: ");
    char msg[20];
    memset(msg,0,sizeof(msg));
    uint8_t i = 0;
    while(file.available()){
        msg[i] = file.read(); 
        i++;
    }
    
    String result = String(msg);
    return result;
}

A conversão para string aqui deu mais trabalho que devolver o array, mas ao receber a variável, fica fácil lidar com ela. No meu código (cujo projeto será apresentado em breve) ficou desse jeito:

...
else if (strcmp(txt,LV_SYMBOL_DOWNLOAD) == 0){
            memset(buf,0,sizeof(buf));
            char target[4];
            lv_roller_get_selected_str(roller_patterns,target,4);
            buf[0] = '/';
            strcat(buf,target);
            String values = readFile(SPIFFS,buf);
            Serial.print("msg: ");
            Serial.println(values);
}

Fácil, hum?

Funções para manipulação do SPIFFS no ESP32

Vou aproveitar e deixar novamente todas as funções que estou utilizando, caso deseje fazer outras manipulações. Estou usando quase todas nesse projeto que será apresentado:

#include <SPIFFS.h>
#include <SD.h>
#include <Arduino.h>
#include <string.h>

String getFilenames(fs::FS &fs, const char * dirname, uint8_t levels){
    uint8_t MAX_CHARS = 150;
    char filenames[MAX_CHARS];
    memset(filenames,0,sizeof(filenames));
    filenames[0] = '|';

    Serial.printf("Listing directory: %s\n", dirname);

    File root = fs.open(dirname);
    if(!root){
        Serial.println("Failed to open directory");
        return filenames;
    }
    if(!root.isDirectory()){
        Serial.println("Not a directory");
        return filenames;
    }

    File file = root.openNextFile();

    while(file){
        strcat(filenames, file.name());
        strcat(filenames, "|");

        file = root.openNextFile();
    }
    //Serial.println(filenames);
    return String(filenames);
}

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
    Serial.printf("Listing directory: %s\n", dirname);

    File root = fs.open(dirname);
    if(!root){
        Serial.println("Failed to open directory");
        return;
    }
    if(!root.isDirectory()){
        Serial.println("Not a directory");
        return;
    }

    File file = root.openNextFile();
    while(file){
        if(file.isDirectory()){
            Serial.print("  DIR : ");
            Serial.println(file.name());
            if(levels){
                listDir(fs, file.name(), levels -1);
            }
        } else {
            Serial.print("  FILE: ");
            Serial.print(file.name());
            Serial.print("  SIZE: ");
            Serial.println(file.size());
        }
        file = root.openNextFile();
    }
}

String readFile(fs::FS &fs, const char * path){
    Serial.printf("Reading file: %s\n", path);

    File file = fs.open(path);
    if(!file || file.isDirectory()){
        Serial.println("Failed to open file for reading");
        return "";
    }

    Serial.print("Read from file: ");
    char msg[20];
    memset(msg,0,sizeof(msg));
    uint8_t i = 0;
    while(file.available()){
        msg[i] = file.read(); 
        i++;
    }
    
    String result = String(msg);
    return result;
}

void writeFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Writing file: %s\n", path);

    File file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("Failed to open file for writing");
        return;
    }
    if(file.print(message)){
        Serial.println("File written");
    } else {
        Serial.println("Write failed");
    }
}

void appendFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Appending to file: %s\n", path);

    File file = fs.open(path, FILE_APPEND);
    if(!file){
        Serial.println("Failed to open file for appending");
        return;
    }
    if(file.print(message)){
        Serial.println("Message appended");
    } else {
        Serial.println("Append failed");
    }
}

void renameFile(fs::FS &fs, const char * path1, const char * path2){
    Serial.printf("Renaming file %s to %s\n", path1, path2);
    if (fs.rename(path1, path2)) {
        Serial.println("File renamed");
    } else {
        Serial.println("Rename failed");
    }
}

void deleteFile(fs::FS &fs, const char * path){
    Serial.printf("Deleting file: %s\n", path);
    if(fs.remove(path)){
        Serial.println("File deleted");
    } else {
        Serial.println("Delete failed");
    }
}

void testFileIO(fs::FS &fs, const char * path){
    File file = fs.open(path);
    static uint8_t buf[512];
    size_t len = 0;
    uint32_t start = millis();
    uint32_t end = start;
    if(file && !file.isDirectory()){
        len = file.size();
        size_t flen = len;
        start = millis();
        while(len){
            size_t toRead = len;
            if(toRead > 512){
                toRead = 512;
            }
            file.read(buf, toRead);
            len -= toRead;
        }
        end = millis() - start;
        Serial.printf("%u bytes read for %u ms\n", flen, end);
        file.close();
    } else {
        Serial.println("Failed to open file for reading");
    }


    file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("Failed to open file for writing");
        return;
    }

    size_t i;
    start = millis();
    for(i=0; i<2048; i++){
        file.write(buf, 512);
    }
    end = millis() - start;
    Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
    file.close();
}

Basta salvar esse conteúdo em um arquivo.h e incluí-lo em seu código principal. Depois é só chamar as funções e seu código principal fica limpinho. Chamei o arquivo de fileHandler.h e em meu programa principal fiz o include:

#include <fileHandler.h>

Espero que essa forma de ler um arquivo para uma variável lhe seja útil, ou ao menos sirva de base para sua própria construção. Ah, esse artigo serve tanto para ESP32 como para ESP8266.

Aproveito para recomendar uma belíssima placa ESP32 com LoRa, caso ainda não tenha seu ESP32, que é essa Wireless Stick ESP32 da CurtoCircuito.

 

Revisão: Ricardo Amaral de Andrade

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.