Como enviar um arquivo txt salvo no sd card.

Olá pessoal, tudo bom?
Por favor gostaria da ajuda de vocês, se puderem me ajudar.
Gostaria de saber como faço para enviar um arquivo txt que está salvo no sd card conectada a esp 32 para um pc.
Por exemplo: quando o pc se conecta na rede e o usuário abre o navegador e digita o endereço: 192.168.4.1/sdbm, gostaria que ele recebesse o arquivo txt que está dentro do sd card.
Da maneira que estou fazendo a placa abri o arquivo ler linha por linha e transmite o problema é que se tiver muita informação estoura a memoria e demora muito para enviar.

Vou explicar o que estou fazendo:
Estou desenvolvendo um sisteminha, só que ele está apresentando algumas falhas.
Primeiro vou falar como ele deveria funcionar e depois apresentar as falhas para quem puder me ajudar com pelo menos algum dos problemas.

Estou fazendo um sistema que trabalha em 3 modos diferentes, para ele economizar bateria.
Quando energizo o sistema ele já entra no modo 1.
1° modo: Neste modo o sistema desliga a comunicação wifi e Bluetooth e fica lendo a entrada analógica e armazenando o número de serie do sistema, data, horário e temperatura (dados obtidos pelo rtc) e o valor da entrada e salva no cartão micro sd a cada segundo essas informações formando a mensagem: Serie=001000/Data=17.10.2020/horario=11:35:38/Temp=29.00C/bat=3.30 bat é o valor da tensão na entrada analógica.
Quando o sistema está nesse modo 1 ele deve acionar o led rgb na cor verde, que fica piscando para mostrar que o sistema está no modo 1.

Se eu aproximar o imã do sensor de efeito hall o meu sistema deve passar do modo 1 de funcionamento para o modo 2.

2° modo de funcionamento ativa o wi-fi.
Quando o sistema entra no modo 2 de funcionamento ele ativa o wi-fi para que seja possível comunicar celular ou computador com a esp32.
A esp 32 trabalha no mado Ap criando a rede, para que o dispositivo se conecte a ela, devendo conectar na rede criada pela esp32 e o endereço: 192.168.4.1
Quando o sistema entra nesse modo é possível passar alguns comando através dos seguintes endereços:
192.168.4.1/LED //função que muda o estado do led da placa.
192.168.4.1/am //função que apaga os dados do sd card.
192.168.4.1/tr //função que imprime no navegador os dados coletados em tempo real.
192.168.4.1/sdes //função que escreve no micro sd os dados coletados no momento que foi dado o comando.
192.168.4.1/sdbm //função que imprime no navegador todos os dados que estavam salvos no arquivo do sd card.
192.168.4.1/sdbm1 //função que simula sdbm

Quando o sistema está nesse modo 2 ele deve acionar o led rgb na cor azul que fica piscando para indicar que o sistema está no modo 2.

Quando o sistema está no modo 2 e nenhum dispositivo trocou informação com a esp32 por mais de 1 minuto, ou seja mais de 1 minuto sem mandar uma das requisições 192.168.4.1(LED, am,sdes....), ele deve sair do modo 2 e ir para o modo 1.

Se o sistema está no modo 2 e eu aproximar um imã do sensor de efeito hall, o sistema deve sair do modo 2 e ir para o modo3.

3° modo quando o sistema entra nesse modo: o led vermelho irá piscar algumas vezes para indicar que o sistema entrou no modo 3. Quando o led para de piscar o sistema entra em deep sleep.
Estou fazendo dessa maneira porque o usuário não pode ter acesso fisicamente a placa para pressionar um botão liga/ desliga.
Então ele entra no deep sleep para economizar energia.
Quando o equipamento está no modo 3 e eu aproximo o imã do sensor hall o sistema deve ir para o modo 1 de funcionamento.

O foco é ser um sistema confiável nas leituras e na comunicação, mas com baixo consumo de energia já que o mesmo será energizado por baterias.

Os problemas que estou tendo são os seguintes:

  1. em relação ao sd/ card A momentos em que quando quando ligo o sistema ele demora a funcionar porque demora um pouco para reconhecer o sd card. outra coisa que ocorre de vez enquando também é quando estou no modo 2 e volta para o modo 1, de vez quando fica dando erro ao tentar abrir o sd card, consequentemente ele não salva as leituras quando isso ocorre.
    Até pensei em mudar para a memoria interna do esp32, mas o problema é o tamanho do espaço, porque meu sistema ele coleta amostrar a cada segundo e preciso dar certa que meu sistema consegue armazenar até 24 horas de amostras.
    ou seja o sistema tem que ser capaz de armazenar pelo menos 86400 amostras de mensagem do tipo: Serie=001000/Data=17.10.2020/horario=11:35:38/Temp=29.00C/bat=3.30 que é equivalente a 24 horas de coleta de dados, dá aproximadamente 8 MB.
    Como posso melhorar estes aspcetos?

  2. Quando o sistema está no modo 2 e dou o comando 192.168.4.1/sdbm ele deveria escrever na tela do navegador todos os dados que estavam salvos no arquivo do sd card, o problema é que só consegue mostrar, se for menos de 925 amostras e demora uns 15 segundos para essas amostras aparecerem no navegador a partir do momento que dou o comando.
    gostaria que isso fosse mais instantâneo e que eu não tivesse esse problema da limitação das amostras, porque se por exemplo eu tiver 1200 amostras salvas no sd card ele só mostraria 925 amostras e não teria como eu ver qual foi o valor das outras 200.
    Tem uma maneira melhor de fazer isso? pensei ao invés de fazer como estou fazendo de abrir o arquivo e ler as amostrar para mostrar no navegador, enviar o arquivo (.txt)isso é possível? enviar o o arquivo txt pelo navegador quando conecto a rede da placa e digito o comando 192.168.4.1/sdbm. Se for possível, como faço?

não cabe a programação toda, por isso divide em duas postagens.

#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "Rede";
const char* password = "12345678";
WebServer server(80);
WiFiClient cliente;//Cria um objeto "cliente".
//====================================
//led da placa
#define LED 2
////====================================
//bibliotecas para micro sd, rtc.
#include <Wire.h>       
#include "FS.h"
#include "SD.h"
#include "SPI.h"
//-----------------------------------------
char dado = -1;
String content = "";
int solic = 0;
int solic2 = 0;
//-------------------------------


//-----------------------------------------------
//biblioteca do rtc
#include "RTClib.h"
RTC_DS3231 rtc;
//-------------------------------------------------
//--------------------------------------------------
//funções do micro sd
void salvar_dados();
void deleteFile();
void readFile();
//void EditarArquivo();
unsigned long ultimo_valor_salvo = 0;
////---------------------------------------------------
//variaveis do sensor hall
#define INTERVALO_SENSOR        1000   // A cada 1 segundo coleta uma leitura do sensor
//                                       // e armazena no cartão SD
#define INTERVALO_SENSORestado        5000 // Tempo em milissegundos a ser esperado entre
////-----------------------------------------------------

// Variáveis para armazenar a passagem de tempo
unsigned long time_ultimo_sensor;
unsigned long now;
//==========================================

//==========================================
// Indica se a máquina está ligada ou desligada
//int status_equipamento = 0;
///RTC_DATA_ATTR aloca a variável na memoria RTC
RTC_DATA_ATTR int status_equipamento = 0;
//==========================================
//==========================================
int contagempulsos = 0;
////-----------------------------
//---------------------------------------------
String Ns = "001000"; //string porque podemos colocar caracteres diferentes.
////----------------------------------------------------------
const unsigned int analogInPin = 14;//27;//33;    // Analog input - pino analógico
////----------------------------------------------------------
////  algumas constantes para entrada analogica

//// used variables - variáveis utilizadas
unsigned int RAWanalogInput = 0;        // save the RAW value of A0 - armazena o valor RAW de A0
float analogInputVoltage = 0;           // save the converted A0 voltage - armazena a tensão convertida de A0
////-------------------------------------------------
int pino_verde = 25; //33;//35; não estão funcionando o digital write, fica sempre em 1,3v.(esses pinos não podem ser saídas, por causa do vdet)o 33 e 32 pode ser.
int pino_vermelho = 32; //34; não estão funcionando o digital write, fica sempre em 1,3v.(esses pinos não podem ser saídas, por causa do vdet)
int pino_azul = 27; //25;//32;

//int pino_rele=14;//27;

int pino_hall = 34;
unsigned int leiturahall = 0;
float tensaohall = 0;
//---------------------------------------------------------
// Inicia variáveis de tempo
unsigned long millisTarefa1 = millis();
unsigned long millisTarefa2 = millis();
unsigned long millisTarefa3 = millis();

#define WIFI_TIMEOUT 60000 // 1 minuto in milliseconds
int testecomando = 0;
////////////////////////////////////////////


void setup() {
 Serial.begin(115200);
 setCpuFrequencyMhz(80);
 pinMode(LED, OUTPUT);//so para testes não vai ter no final.
 pinMode(pino_azul, OUTPUT);//saída do led rgb azul
 pinMode(pino_vermelho, OUTPUT);//saída do led rgb vermelha
 pinMode(pino_verde, OUTPUT);//saída do led rgb verde
 //pinMode(pino_rele, OUTPUT);
 pinMode(analogInPin, INPUT);//pino 34,entrada do sensor de bateria
 pinMode(pino_hall, INPUT);//pino conectado o sensor hall.

 //=============================================


 //  //=======================================================
 //inicialização do micro sd
 time_ultimo_sensor = 0;
 SPI.begin(); // Inicia SPI bus
 if (!SD.begin()) { // caso o cartao nao tenha iniciado
   Serial.println("Erro ao iniciar a comunicacao com o cartao SD...");
   ESP.restart();
 }
 //=====================================================
 //inicialização do rtc.
 rtc.begin();
 // Inicializando RTC
 if (!rtc.begin()) {
   Serial.println("Não foi possível encontrar o RTC");
   Serial.flush();
   abort();
 }
 else {
   Serial.println("RTC detectado.");
 }

 if (rtc.lostPower()) {
   Serial.println("RTC lost power, let's set the time!");
   rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
   //    // This line sets the RTC with an explicit date & time, for example to set
   //    // January 21, 2014 at 3am you would call:
   //    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
 }

 DateTime now = rtc.now(); //coloquei depois. //cria um objeto com as informaçoes de data e hora
 //===========================================


 //=================================================
 //criação da rede.
 Serial.println();
 Serial.print("Conectando-se a ");
 WiFi.softAP(ssid, password);
 //===================================================
 // Criando handlers(manipuladores) para tratarem as requisições nos endereços desejados
 server.on("/LED", handle_led);//função que pisca o led da placa.
 server.on("/am", handle_am); //função que apaga os dados do sd card.
 server.on("/tr", handle_tr);//função que imprime no navegador os dados coletados em tempo real.
 server.on("/sdes", handle_sdes);//função que escreve no micro sd os dados coletados.
 server.on("/sdbm1", handle_sdbm1); //função que simula sdbm
 server.on("/sdbm", handle_sdbm); //função que imprime no navegador todos os dados que estavam no arquivo do sd card.

 server.begin();
 delay(100);
}
//======================================================

continuação da programação:

void loop() {


 testecomando = 0;
 if (millis() - time_ultimo_sensor >= INTERVALO_SENSORestado) {

   // read the analog input - le a entrada analogica
   leiturahall = analogRead(pino_hall);
   tensaohall = (float)leiturahall * (3.3 / 4096);
   if (tensaohall > 1.6) {
     //server.send(200, "text/html","o sensor detectou e acionou o sistema.");
     status_equipamento = status_equipamento + 1;
   }
   time_ultimo_sensor = millis();//now;
 }

 if (status_equipamento >= 3) {
   status_equipamento = 0;
 }

 if (status_equipamento == 0) { // ele deve entrar nesse modo tbm, toda vez que enrgizo o sistema, por exemplo quando troco de bateria.
   //ele deve entrar nesse modo se o status do equipamento for zero ou quando está no modo 2 e fica mais de 1 minuto sem um dispositivo se comunicar com a esp32, ou seja mais de 1 minuto sem mandar uma das requisições(LED, am,sdes....)
   //ou quando esta no modo três e eu aproximar o imã e ficar com ele em frente o sensor.
   Serial.println("Modo1: equipamento realiza leituras salva no sd, comunicação desligada.");
   //digitalWrite(pino_rele, HIGH);
   Serial.println("Desabilitando o WiFi - Modo WIFI_OFF");
   WiFi.disconnect();
   //WiFi.mode(WIFI_OFF);
   WiFi.mode( WIFI_MODE_NULL );
   btStop();
   Serial.println("Forcando o desligamento do WiFi");
 
   if ((millis() - millisTarefa1) < 400) {
     // Acende o led
     digitalWrite(pino_vermelho, LOW);
     digitalWrite(pino_azul, LOW);
     digitalWrite(pino_verde, HIGH);//parece que esse pino do led rgb não pode receber 3,3v. vou mudar depois para analogWrite para colocar a tensão menor.

   } else {
     // Apaga o led
     digitalWrite(pino_vermelho, LOW);
     digitalWrite(pino_azul, LOW);
     digitalWrite(pino_verde, LOW);
   }
   // Verifica se já passou 2000 milisegundos
   if ((millis() - millisTarefa1) > 2000) {
     millisTarefa1 = millis();
     contagempulsos = 0;
   }
   salvar_dados();

 }

 if (status_equipamento == 1) {
   // ele so entra nesse modo dois quando ele estava no modo 1 e aproximo o imã do sensor.
   //ele ta dando erro as vezes, quando dou alguns dos comandos(LED,sdes....) ele trava tudo. Será se é conflito entre as comunicações serial, i2c, spi...?
   Serial.println("Modo2: equipamento realiza leituras salva no sd, comunicação é ligada, permite realizar as funções que só são possiveis com comunicação.");
   //server.send(200, "text/html","Modo2: equipamento realiza leituras salva no sd, comunicação é ligada, permite realizar as funções que só são possiveis com comunicação.");
   //digitalWrite(pino_rele, HIGH);
   Serial.println("Forcando o religamento do WiFi");
   //                    WiFi.forceSleepWake();
   //                  WiFi.mode(WIFI_ON);
   Serial.print("Conectando-se a ");
   WiFi.softAP(ssid, password);
   // Armazena a primeira tentativa de conecção
   unsigned long startAttemptTime = millis();

   // if(testecomando==0 && millis() - startAttemptTime > WIFI_TIMEOUT){


   // Mantem a tentativa de conexão enquanto não atinge timeout
   while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < WIFI_TIMEOUT) {
     //if(testecomando==0 && millis() - startAttemptTime < WIFI_TIMEOUT){
     delay(10);
     Serial.println("Conectando na rede");

     //String currentLine = "";
     //   // while (client.connected()) {
     //    //  if (client.available()) {
     //        char c = client.read();
     //                  if (currentLine.endsWith("GET /H")) {
     //WiFiClient cliente = server.available();//Diz ao cliente que há um servidor disponivel.
     //   if (cliente == true)//Se houver clientes conectados, ira enviar o HTML.
     //   {
     //      String req = cliente.readStringUntil('\r');//Faz a leitura do Cliente.
     //      Serial.println(req);//Printa o pedido no Serial monitor.
     //      if (req.indexOf("/teste") > -1)//Caso o pedido houver led, inverter o seu estado.
     //      {
     //          server.send(200, "text/html", " funcionando");
     //        }
     server.handleClient();
     if (testecomando == 1) {
       startAttemptTime = 0;
     }
     if ((millis() - millisTarefa2) < 200) {
       // Acende o led
       digitalWrite(pino_verde, LOW); //parece que esse pino do led rgb não pode receber 3,3v. vou mudar depois para analogWrite para colocar a tensão menor.
       digitalWrite(pino_vermelho, LOW);
       digitalWrite(pino_azul, HIGH);
     } else {
       // Apaga o led
       digitalWrite(pino_verde, LOW); //parece que esse pino do led rgb não pode receber 3,3v. vou mudar depois para analogWrite para colocar a tensão menor.
       digitalWrite(pino_vermelho, LOW);
       digitalWrite(pino_azul, LOW);
     }


     // Verifica se já passou 600 milisegundos
     if ((millis() - millisTarefa2) > 600) {
       millisTarefa2 = millis();
       contagempulsos = 0;
     }
     //}
   }

   // garante que esta conectado senao vai para deep sleep
   if (testecomando == 0 && millis() - startAttemptTime > WIFI_TIMEOUT) {
     status_equipamento = 0;
   }

 }



 if (status_equipamento == 2) {
   //esse modo 3 deve entrar toda vez que o sistema está no modo 2 e eu aproximo o imã perto do sensor.
   Serial.println("Modo 3: equipamento entra em hibernação para economizar energia,desliga comunicação,só é necessario que o micro consiga identificar a leitura do sensor que alterna os modos de funcionamento.");
   //server.send(200, "text/html", "Modo 3: equipamento entra em hibernação para economizar energia,desliga comunicação,só é necessario que o micro consiga identificar a leitura do sensor que alterna os modos de funcionamento.");
   //digitalWrite(pino_rele, HIGH);
   //{
   esp_sleep_enable_ext0_wakeup(GPIO_NUM_34, 1); //1 = High, 0 = Low
   if (contagempulsos < 21) { //quantidade de vezes que o led vai piscar na cor vermelha, para indicar que está entrando na hibernação.
     // Verifica se já passou 200 milisegundos
     if ((millis() - millisTarefa3) < 200) {
       digitalWrite(pino_verde, LOW); //parece que esse pino do led rgb não pode receber 3,3v. vou mudar depois para analogWrite para colocar a tensão menor.
       digitalWrite(pino_azul, LOW);
       digitalWrite(pino_vermelho, HIGH);

     } else {
       //
       digitalWrite(pino_verde, LOW); //parece que esse pino do led rgb não pode receber 3,3v. vou mudar depois para analogWrite para colocar a tensão menor.
       digitalWrite(pino_azul, LOW);
       digitalWrite(pino_vermelho, LOW);

     }
     // Verifica se já passou 800 milisegundos
     if ((millis() - millisTarefa3) > 800) {
       millisTarefa3 = millis();
       contagempulsos++;
     }


   }
   else {
     esp_deep_sleep_start();//força o ESP32 entrar em modo SLEEP
   }

 }


 Serial.println("saiu");

}

parte 3 da programação:

//=======================================
void handle_led()
{
 testecomando = 1;
 //função que pisca o led da placa. A medida que é dado o comando é alterado o estado do led.
 digitalWrite(LED, !digitalRead(LED));
 Serial.println("teste");
 server.send(200, "text/html", "LED funcionando");

}
//===========================================

//==========================================
void handle_am()//função que apaga os dados do sd card.

{
 server.send(200, "text/html", "dados apagados");
 deleteFile(SD, "/dados_salvos.txt");
}

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


////===================================================
void handle_tr() //função que imprime no navegador os dados coletados em tempo real.
{

 DateTime now = rtc.now();// faz a chamada do rtc

 //// //////////////////////////////////////////////////////////////////////////////////////
 //// read the analog input - le a entrada analogica
 RAWanalogInput = analogRead(analogInPin);
 analogInputVoltage = (float)RAWanalogInput * (3.3 / 4096);
 ///////////////////////////////////////////////////////////////////////

 String dataMessage = String("Serie=") + "" + String(Ns) + "" + String("/Data=") + "" + String(now.day()) + "." + String(now.month()) + "." + String(now.year()) + "" + String("/horario=") + "" + String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second()) + "" + String("/Temp=") + "" + String(rtc.getTemperature()) + "" + String("C") + "" + "" + String("/bat=") + "" + String(analogInputVoltage) + " \n ";
 //na linha de cima cria a mensagem para imprimir no navegador.
 Serial.println("lendo");
 server.send(200, "text/html", dataMessage);
}
////====================================================






////======================================================
void handle_sdes()//função que escreve no micro sd os dados coletados.
{
 salvar_dados();
 Serial.println("salvando dados no sd");
 server.send(200, "text/html", "salvando dado no sd");
}

void salvar_dados() {
 DateTime now = rtc.now();// faz a chamada do rtc

 // read the analog input - le a entrada analogica
 // Serial.println("Entrou aqui");
 RAWanalogInput = analogRead(analogInPin);
 analogInputVoltage = (float)RAWanalogInput * (3.3 / 4096);

 File file = SD.open("/dados_salvos.txt"); // abre o arquivo
 if (!file) { //caso o arquivo nao tenha sido iniciado ou aberto
   Serial.println("Erro ao abrir arquivo para a leitura");
 }

 //            while (file.available()) { //se tiver dados pra ler...
 //             // Serial.write(file.read()); //mostre ao navegador do cliente
 //            //Serial.println("abriuarquivo");
 //            i=i+1;
 //            }

 file.close(); // fecha o arquivo


 if ((millis() - ultimo_valor_salvo) > 1000) { // caso ja tenha passado 1 segundo do ultimo dado aferido e salvo
   ultimo_valor_salvo = millis();


   // //////////////////////////////////////////////////////////////////////////////////////
   String dataMessage = String("Serie=") + "" + String(Ns) + "" + String("/Data=") + "" + String(now.day()) + "." + String(now.month()) + "." + String(now.year()) + "" + String("/horario=") + "" + String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second()) + "" + String("/Temp=") + "" + String(rtc.getTemperature()) + "" + String("C") + "" + "" + String("/bat=") + "" + String(analogInputVoltage) + " \n "; // cria a string que sera salva no cartao sd

   //       Serial.println(dataMessage);

   EditarArquivo(SD, "/dados_salvos.txt", dataMessage.c_str());


 }


}

void EditarArquivo(fs::FS &fs, const char * local, const char * mensagem) {
 Serial.printf("editando o arquivo: %s\n", local);

 File file = fs.open(local, FILE_APPEND);
 if (!file) {
   Serial.println("Falha ao abrir o arquivo para editar");
   return;
 }
 Serial.print(" Sensor esta registrando "); //

 file.println(mensagem);
 file.close();
}
////=================================================



//=================================================
void handle_sdbm()//função que imprime no navegador todos os dados que estavam salvos no arquivo do sd card.
{
 readFile(SD, "/dados_salvos.txt");
 //content="";
}

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

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

 Serial.print("Read from file: ");
 while (file.available()) {
   //Serial.write(file.read());
   dado = file.read();
   content.concat(dado);
 }
 server.send(200, "text/html", content);
 content = "";
 file.close();
}

a montagem realizada:

https://www.facebook.com/photo/?fbid=2787773048168001&set=gm.3534799143225829