Recents in Beach


Receba o meu conteúdo GRATUITAMENTE


Como gravar arquivos em microSD com ESP32



Esta é a continuação de um vídeo que já fiz utilizando o display TFT de 1.8 polegadas, que é um display gráfico de 128 pixels por 160, que eu gosto demais. Desta vez, nosso circuito vai utilizar um adaptador e um microSD de 32 giga.

Quer ver o primeiro vídeo? Acesse: Valorize seu projeto: use display gráfico!




Demonstração




Recursos usados


  • ESP32-WROOM
  • Display TFT Lcd 1.8’’
  • Protoboard
  • Jumpers
  • Cartão SD
  • Botão
  • Resistor 10k ohm




Montagem

Display TFT 1.8’’ Pinout




Montagem ESP-WROOM32 com Display TFT1.8’’



Tabela de ligações ESP-WROOM32 e Display TFT1.8’’




Formatação SD card para FAT32 (Recomendação)

·         Formate o cartão SD como FAT32, utilize um adaptador USB como este:


·         Utilize um programa para formatar, como o GUIformat:





Biblioteca necessária

Baixe a biblioteca mySD:





Instale a biblioteca

1.       Clique em Incluir Biblioteca -> Adicionar biblioteca .ZIP


2.       Na tela de pesquisa procure pelo ZIP baixado e clique em abrir



Código

Código ESP-WROOM 32

Declarações e variáveis


#include <Adafruit_GFX.h>    // Biblioteca de gráficos
#include <Adafruit_ST7735.h> // Biblioteca do hardware ST7735
#include <SPI.h> // Biblioteca para comunicação SPI
#include <Fonts/FreeSerif9pt7b.h> // Fonte Serif que é usada no display
#include "SD_File_Record.h" // Biblioteca com as funções referentes ao SD card

// ESP32-WROOM
#define TFT_DC 12 // A0
#define TFT_CS 13 // CS
#define TFT_MOSI 14 // SDA
#define TFT_CLK 27 // SCK                
#define TFT_RST 0  // RESET
#define TFT_MISO 0 // MISO

// Objeto do display tft
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST);

// Variável que guarda uma mensagem de erro caso aconteça algum problema com o display ou SD card
String errorMsg;

// Botão usado para excluir o arquivo do cartão SD
const int buttonPin = 33;

// Flag que impede que o display pisque quando o botão estiver pressionado
bool flag = false;

// Altura da fonte que é usada no display
int fontHeight = 7;

const int MICROSD_PIN_CHIP_SELECT = 21; // Pino serial
const int MICROSD_PIN_MOSI = 19; // Pino serial
const int MICROSD_PIN_MISO = 18; // Pino serial
const int MICROSD_PIN_SCK = 22; // Clock pin

// Instancia um objeto SD_File_Record enviando o nome do arquivo e o tamanho máximo dos registros (sem contar o \r\r) no construtor, ou seja, o tamanho real será de 3+2 = 5
SD_File_Record ObjSD("test.txt", 3);



Setup


void setup() 
{
  // Inicia a serial com velocidade de 9600  
  Serial.begin(115200);
  // Seta botão como entrada (INPUT)
  pinMode(buttonPin, INPUT);

  // Inicia display TFT com a tela preta
  tft.initR(INITR_BLACKTAB);

  // Limpa display, seta a fonte e posiciona cursor no início
  resetDisplay();

  // Exibe na serial "Starting..."
  Serial.print("Starting...");

  // Inicializa cartão SD
  if(!ObjSD.init(MICROSD_PIN_CHIP_SELECT, MICROSD_PIN_MOSI, MICROSD_PIN_MISO, MICROSD_PIN_SCK))
  {
    // Se não obteve sucesso, exibe no display e reinicia o ESP
    tft.println("\nSD begin fail\n");
    Serial.println("SD begin fail");
    delay(1000);
    ESP.restart();
  }

  // Se obteve sucesso exibe no display
  Serial.println("SD card ok");

  // Escreve no arquivo os números sequenciais
  writeNewRecord();   

  // Lê o arquivo e exibe no display
  showFile();
  
  // Função que busca um registro
  // String reg = ObjSD.findRecord(10); // 10 é a posição do registro
}


Limpando display e Exibindo dados


// Limpa o display e posiciona o cursor no início
void resetDisplay()
{
  tft.setFont(&FreeSerif9pt7b);
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextColor(ST7735_WHITE);
  tft.setCursor(0,fontHeight+5);
  tft.setTextSize(1);
}

// Lê e exibe o arquivo
void showFile()
{
  // Variável usada para guardar a linha lida do arquivo txt
  String linha;

  // Contador usado para limpar o display quando o texto não couber mais
  int count = 0;  

  // Exibe no display e na serial o início do arquivo
  Serial.println("# Begin of file #");  
  tft.println("# Begin of file #");

  // Enquanto for possível ler a próxima linha do arquivo
  ObjSD.rewind();
  while(ObjSD.readFileNextLine(&linha, &errorMsg))
  {
    // Se ocorreu algum erro durante a leitura
    if(errorMsg != "")
    {
      // Exibe o erro no display e aborta o while
      tft.println(errorMsg);
      break;
    }

    // Incrementa contador
    count++;

    // Se o contador for divisivel por 6 (qtde de linhas que cabem no display com esta fonte)
    if(count % 6 == 0)
    {
      //Exibe "..." sinalizando que ainda não é o fim do arquivo
      tft.println("...");
      // Aguarda 1.5s para poder visualizar os valores
      delay(1500);
      // Limpa display
      resetDisplay();
    }


Exibindo display e Escrevendo no arquivo


// Exibe a linha obtida no display e na Serial
    tft.println(linha);
    Serial.println(linha);
  } //Fim do while
  
  // Exibe no display e na serial indicando fim do arquivo
  Serial.println("# End of file #");  
  tft.println("# End of file #");
}

// Escreve os números sequencias no arquivo
void writeNewRecord()
{  
  // String que será escrita no arquivo txt
  String line;
  // Inteiro que receberá o número da última linha registrada
  int nLine;

  // Se o arquivo não existe
  if(!ObjSD.fileExists())
    ObjSD.newFile(); // Cria o arquivo

  // Lê a última linha do arquivo
  if(!ObjSD.readFileLastLine(&line,&errorMsg) && errorMsg != "")
    tft.println(errorMsg);
  else 
  if(errorMsg == "") // Se foi possível ler o arquivo
  {
    // Se o arquivo estiver vazio
    if(line == "")
      line = "001"; // Atribui para a String o primeiro valor da sequência (1)
    else // Se o arquivo não estiver vazio
    {
      // Converte String para int e incrementa 1
      nLine = atoi(line.c_str())+1;

      // Insere zeros à esquerda conforme o número atual do registro
      if(nLine < 10)
        line = "00";
      else
      if(nLine < 100)
        line = "0";

       // Converte o novamente o valor para String
      line += String(nLine); 
    }

    // Escreve no arquivo a String "line"
    if(!ObjSD.writeFile(line, &errorMsg))
      tft.println(errorMsg); // Se existir um erro durante a escrita, exibe no display
  }
  else
    tft.println(errorMsg); // Se existir um erro, exibe no display
}


Loop


void loop() 
{  
  // Se o botão foi pressionado e a flag estiver "false"
  if(digitalRead(buttonPin) == HIGH && !flag)
  {    
    // Tenta excluir o arquivo
    if(ObjSD.destroyFile())     
      showFileDeleted(true); // Se obteve sucesso, chama a função "showFileDeleted" enviando valor "true"
    else
      showFileDeleted(false); // Se não obteve sucesso, chama a função "showFileDeleted" enviando valor "false"

    // Impede que o display pisque e que o ESP tente excluir o arquivo mais de uma vez
    flag = true;
  }
  else
  if(digitalRead(buttonPin) == LOW && flag) // Se o botão foi solto e a flag estiver "true"
  {
    // Limpa display
    resetDisplay();

    // Exibe arquivo
    showFile();

    // Impede que o display pisque e que o ESP tente excluir o arquivo mais de uma vez
    flag = false;
  }  

  // Aguarda 10ms
  delay(10);
}


Biblioteca SD_File_Record - Header


// ifndef evita erros de duplicação ao chamar esta lib mais de uma vez
#ifndef SD_File_Record_h
#define SD_File_Record_h

#include <Arduino.h> //Biblioteca Arduino (opcional)
#include <mySD.h> // Biblioteca referente ao cartão SD

// Classe SD_File_Record e suas funções
class SD_File_Record
{    
  // Todas as funções desta lib são publicas, mais detalhes em SD_File_Record.cpp
  public: 
  SD_File_Record(String, int);
  SD_File_Record(String);
  bool init(int, int, int, int);
  bool readFileLastLine(String *, String *);
  bool destroyFile();
  String findRecord(int);
  void rewind();
  bool writeFile(String, String *);
  bool seekFile(int);
  bool readFileNextLine(String *, String *);
  String getFileName();
  void setFileName(String);
  int getSizeRecord();
  void setSizeRecord(int);
  void newFile();
  bool fileExists();
};


Declarações e variáveis, Construtores e init


// Importamos o header referente a lib SD_File_Record
#include "SD_File_Record.h"

// Nome do arquivo em que os registros serão salvos
String fileName;

// Ponteiro do arquivo
File pFile;

/* ############################## Importante ##############################

O registro tem tamanho FIXO de 3 caracteres e seus valores são de:

001
002
003
004
005
...
999

########################################################################## */
// Tamanho do registro (default 3)
int sizeOfRecord = 5; 
// Exemplo: 
// 001\r\n = 5 caracteres

// Construtor que seta o nome do arquivo e o tamanho do registro
SD_File_Record::SD_File_Record(String _fileName, int _sizeOfRecord)
{ 
  fileName = _fileName;
  sizeOfRecord = _sizeOfRecord+2; 
}

// Construtor que seta somente o nome do arquivo deixando o tam do registro default
SD_File_Record::SD_File_Record(String _fileName){ fileName = _fileName; }

// Funcao de inicialização que configura o Cartão SD com os pinos recebidos por parametro
bool SD_File_Record::init(int _MICROSD_PIN_CHIP_SELECT, int _MICROSD_PIN_MOSI, int _MICROSD_PIN_MISO, int _MICROSD_PIN_SCK)
{  return SD.begin(_MICROSD_PIN_CHIP_SELECT, _MICROSD_PIN_MOSI, _MICROSD_PIN_MISO, _MICROSD_PIN_SCK); }



Lendo o arquivo


// Lê a próxima linha do arquivo
bool SD_File_Record::readFileNextLine(String *line, String *errorMsg)
{
  // Se o ponteiro estiver nulo
  if(!pFile)
  {
    // Abre arquivo para leitura
    pFile = SD.open(fileName.c_str(), FILE_READ);

    // Se aconteceu algum erro 
    if(!pFile)
    {
      // Guarda msg de erro
      *errorMsg = "Failed to open the file";
      // Retorna falso
      return false;  
    }
  }

  // Se for possível ler o arquivo
  if(pFile.available())
  {
    // Lê arquivo
    *line = pFile.readStringUntil('\n');
    // Retorna true
    return true;
  }
  
  // Se não for possível ler o arquivo retorna falso
  return false;
}


Posicionamento de ponteiro e escrita no arquivo


//Posiciona ponteiro do arquivo na posição "pos"
bool SD_File_Record::seekFile(int pos)
{
  // Se o ponteiro estiver nulo  
  if(!pFile)
    pFile = SD.open(fileName.c_str(), FILE_READ); // Abre o arquivo para leitura

  // Posiciona o ponteiro na posição multiplicando pelo tamanho do registro
  return pFile.seek(sizeOfRecord*pos);
}

// Escreve no arquivo
bool SD_File_Record::writeFile(String line, String *errorMsg)
{
  // Abre arquivo para escrita
  pFile = SD.open(fileName.c_str(), FILE_WRITE);

  // Se foi possível abrir
  if (pFile) 
  {
    // Escreve registro
    pFile.println(line);
    // Fecha arquivo
    pFile.close();
    // Retorna true
    return true;
  }
  
  // Se não foi possível abrir guarda mensagem de erro e retorna false
  *errorMsg = "Failed to open the file: "+String(fileName);
  return false;  
}



Posicionando no início do arquivo e leitura de última linha


// Posiciona ponteiro no início do arquivo
void SD_File_Record::rewind()
{
  pFile.seek(0);
}

// Lê o último registro do arquivo
bool SD_File_Record::readFileLastLine(String *line, String *errorMsg)
{
  // Variável que guardará o tamanho do arquivo
  int sizeArq;

  // Limpa string
  *errorMsg = "";

  // Se o arquivo está aberto, fecha
  if(pFile)
    pFile.close();

  // Abre o arquivo para leitura
  pFile = SD.open(fileName.c_str(), FILE_READ);

  // Se não foi possível abrir o arquivo
  if(!pFile)
  {
    // Guarda mensagem de erro e retorna false
    *errorMsg = "Failed to open the file: "+String(fileName);
    return false;
  }
    
  // Obtém o tamanho do arquivo
  sizeArq = pFile.size();

  // Se existe ao menos um registro
  if(sizeArq >= sizeOfRecord)
    pFile.seek(sizeArq-sizeOfRecord); // Posiciona o ponteiro no final do arquivo menos o tamanho de um registro (sizeOfRecord)

  // Lê registro retornando o resultado da função "readFileNextLine"
  return readFileNextLine(*&line, *&errorMsg);  
}


Exclusão de arquivo e busca


// Exclui arquivo
bool SD_File_Record::destroyFile()
{
  // Se o arquivo estiver aberto, fecha
  if(pFile)
    pFile.close();

  // Exclui arquivo e retorna o resultado da função "remove"  
  return SD.remove((char*)fileName.c_str());
}


// Função que busca um registro
// "pos" é a posição referente ao registro buscado
String SD_File_Record::findRecord(int pos) 
{
  // Linha que receberá o valor do registro buscado
  String line = "", errorMsg = "";

  // Posiciona na posição desejada
  // Obs. A posição se inicia com zero "0" 
  if(!seekFile(pos))
    return "Seek error"; 
    
  // Lê o registro
  if(!readFileNextLine(&line, &errorMsg))
    return errorMsg;

  return line;    
}


Arquivo existe, novo arquivo e funções get e set


// Verifica se o arquivo existe
bool SD_File_Record::fileExists()
{
  return SD.exists((char*)fileName.c_str());
}

// Cria um novo arquivo, se já existir o arquivo será removido antes
void SD_File_Record::newFile()
{
  if(pFile)
    pFile.close();

  SD.remove((char*)fileName.c_str());
  pFile = SD.open(fileName.c_str(), FILE_WRITE);
  pFile.close();
}

// Obtém o nome do arquivo
String getFileName()
{  return fileName; }

// Seta o nome do arquivo
void setFileName(String _fileName)
{  fileName = _fileName; }

// Obtém o tamanho do registro
int getSizeRecord()
{ return sizeOfRecord-2; }

// Seta o tamanho do registro
void setSizeRecord(int _sizeOfRecord)
{ sizeOfRecord = _sizeOfRecord+2; }




Faça o download dos arquivos:





Postar um comentário

2 Comentários

  1. Muito agradecido. Apenas percebi que a Tabela de ligações ESP-WROOM32 e Display TFT1.8’’ possui para SD_MISO a GPIO28, sendo que penso ser a correta GPIO18, conforme código e imagem Montagem ESP-WROOM32 com Display TFT1.8’’.

    Reitero meus agradecimentos.

    ResponderExcluir
  2. Bom dia, gostaria de saber qual é a velocidade em que os eventos são salvos, gostaria de saber se consigo salvalos em uma taxa de 400Hz ou mais ?

    ResponderExcluir