ESP Wroom 32 to send CSV file to drive

Hello everyone.
I am developing a code in which I have to send a CSV file to Google Drive. I've been doing some research, and a good way to achieve the goal is to create a Google Drive API, which generates me a JSON file with a "Client_id" and I need to get an access token. However, I am confused, since I have not been able to get this token to implement it in my code. I am working with an ESP Wroom 32
with SD card module, and I have already started the WIFI communication
and creation of the CSV file in the SD, and performed the functions of sending data. However, I'm a bit confused on how to generate the access tokens needed for the CSV file to stay on drive.

If any of you can guide me, I would really appreciate it.

//Wilson Steven Díaz - Ángel Nicolás Farfán 
#include <Arduino.h>
#include <MD_MAX72xx.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <esp_crt_bundle.h>
#include <ssl_client.h>
//Define  
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES	4
#define CHAR_SPACING 1
//VSPI
#define CLK_PIN   18  // or SCK
#define DATA_PIN  23  // or MOSI
#define CS_PIN    5  // or SS
//HSPI
#define SCK  14
#define MISO  27
#define MOSI  13
#define CS  15
SPIClass spi0=SPIClass(HSPI);
MD_MAX72XX mx=MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
WiFiClientSecure client;
#define  DELAYTIME  100  // in milliseconds
#define  DELAYTIME2 500
// Configuración de la red Wi-Fi
const char* ssid = "TORRES DIAZ";
const char* password = "s1nc0ntr4s3n4";
// Configuración de la API de Google Drive
const char* googleDriveHost = "www.googleapis.com";
const int googleDrivePort = 443;
const char* googleDriveUploadPath = "/upload/drive/v3/files?uploadType=multipart";
// Configuración de OAuth 2.0
const char* googleOAuthClientId = "237752972464-duuurqdb86jsn5bjkvc90cbb0va78kp7.apps.googleusercontent.com";//Se pbtiene del archivo JSON creado con la API de Drive
const char* googleOAuthClientSecret9Z = "GOCSPX-4qQaMgIrcItmcSqI9yvh0HaEG1";//Se pbtiene del archivo JSON creado con la API de Drive
// Configuración del token de acceso
const char* googleOAuthAccessToken = "";
const char* googleOAuthRefreshToken = "1//04GInCAQ4phiECgYIARAAGAQSNwF-L9Ir76gXtVXxNyMZAhtzas4rO12WNVeA5UM8McXV4nlPU9Weww2z7Og22fUig1f1GAY7F3o";//Se obtiene del intercambio de URL por token de la API de Drive (Configuración de OAuth 2.0)
const char* caCert = \
"-----BEGIN CERTIFICATE-----\n"
"MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw\n"
"CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU\n"
"MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw\n"
"MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp\n"
"Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA\n"
"A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo\n"
"27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w\n"
"Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw\n"
"TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl\n"
"qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH\n"
"szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8\n"
"Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk\n"
"MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92\n"
"wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p\n"
"aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN\n"
"VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID\n"
"AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E\n"
"FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb\n"
"C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe\n"
"QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy\n"
"h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4\n"
"7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J\n"
"ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef\n"
"MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/\n"
"Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT\n"
"6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ\n"
"0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm\n"
"2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb\n"
"bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c\n"
"-----END CERTIFICATE-----\n";
void setup(){
  Serial.begin(9600);
  // Conexión a la red Wi-Fi
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Conectando a la red Wi-Fi...");
  while (WiFi.status() != WL_CONNECTED){
    Serial.print("...");
    delay(1000);
  }
  Serial.println();
  Serial.println("Conexión Wi-Fi establecida");
  // Obtener un nuevo token de acceso al iniciar
  obtainAccessToken();
  spi0.begin(SCK, MISO, MOSI, CS);
  mx.begin();
  while(!SD.begin(CS, spi0)){
    Serial.println("Falló el montaje de la tarjeta");
  }
  uint8_t cardType = SD.cardType();
  Serial.print("Tipo de tarjeta SD");
  if(cardType == CARD_MMC){
    Serial.println("MMC");
  } else if(cardType == CARD_SD){
    Serial.println("SDSC");
  } else if(cardType == CARD_SDHC){
    Serial.println("SDHC");
  } else {
    Serial.println("UNKNOWN");
  }
  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);
  if(!SD.exists("/hello.csv")){
    createFile(SD, "/hello.csv");
  }
  Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}
void loop(){
  while(true){
    int val1=1;
    int val2=1;
    int val3=1;
    char buff[9]={72, 111, 108, 97, 97, 97, 97, 97};
    scrollText(buff);
    appendFile(SD, "/hello.csv", val1, val2, val3);
    //printText(buff, sizeof(buff), true);
  // Abrir el archivo CSV en la tarjeta SD
  File file = SD.open("/Carreras realizadas.csv", "r");
  while(!file) {
    Serial.println("Error al abrir el archivo CSV");
  }
  // Crear una conexión segura a la API de Google Drive
  client.setCACert(caCert);
  while(!client.connect(googleDriveHost, googleDrivePort)) {
    Serial.println("Error al establecer la conexión segura");
  }
  // Construir el encabezado de la solicitud HTTP
  String boundary = "-----BOUNDARY1234567890";
  String request = "";
  request += "POST " + String(googleDriveUploadPath) + " HTTP/1.1\r\n";
  request += "Host: " + String(googleDriveHost) + "\r\n";
  request += "Content-Type: multipart/related; boundary=" + boundary + "\r\n";
  request += "Authorization: Bearer " + String(googleOAuthAccessToken) + "\r\n";
  request += "Content-Length: " + String(file.size() + 159) + "\r\n";
  request += "\r\n"; 
  // Construir el cuerpo de la solicitud HTTP
  String body = "";
  body += "--" + boundary + "\r\n";
  body += "Content-Type: application/json; charset=UTF-8\r\n\r\n";
  body += "{\"name\": \"Carreras realizadas.csv\"}\r\n";
  body += "--" + boundary + "\r\n";
  body += "Content-Type: application/octet-stream\r\n\r\n";
  // Enviar el encabezado y el cuerpo de la solicitud HTTP
  client.print(request);
  client.print(body);
  // Enviar el contenido del archivo
  while (file.available()){
    char data=file.read();
    client.write(data);
  }
  // Enviar el cierre del cuerpo de la solicitud HTTP
  client.print("\r\n");
  client.print("--" + boundary + "--\r\n");
  // Leer y mostrar la respuesta del servidor
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    Serial.println(line);
  }
  Serial.println("Archivo enviado correctamente a Google Drive");
  // Cerrar el archivo
  file.close();
  client.stop();
  // Esperar antes de enviar otro archivo (opcional)
  //delay(60000);
  }
}
void scrollText(char *p){
  uint8_t charWidth;
  uint8_t cBuf[8];//this should be ok for all built-in fonts
  mx.clear();
  while(*p!='\0'){
    charWidth = mx.getChar(*p++, sizeof(cBuf)/sizeof(cBuf[0]), cBuf);
    for(uint8_t i=0; i<=(charWidth); i++){//allow space between characters
      mx.transform(MD_MAX72XX::TSL);
      if (i<charWidth)
        mx.setColumn(0, cBuf[i]);
      delay(DELAYTIME);
    }
  }
  delay(DELAYTIME);
}//End of Scroll Text

void printText(char *pMsg, int LOS, bool CenterJustify){//copied and modified from library Print the text string to the LED matrix modules specified. Message area is padded with blank columns after printing. And center justified if third argument is "true"
  uint8_t modStart=0;
  uint8_t modEnd=MAX_DEVICES-1;
  uint8_t   state=0;
  uint8_t   curLen;
  uint16_t  showLen;
  uint8_t   cBuf[8];
  int16_t   col=((modEnd + 1)*COL_SIZE)-1;
  int pixelcount=0;
  int ccounter=LOS;
  mx.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);
  do{     // finite state machine to print the characters in the space available
    switch (state){
      case 0: // Load the next character from the font table if we reached end of message, reset the message pointer
        if(*pMsg=='\0'){
          showLen=col-(modEnd*COL_SIZE);  // padding characters
          state=2;
          break;
        }// retrieve the next character form the font file
        showLen=mx.getChar(*pMsg++, sizeof(cBuf)/sizeof(cBuf[0]), cBuf);
        if (ccounter>0){
          pixelcount=(pixelcount+showLen)+CHAR_SPACING;
          ccounter--;
        }
        curLen=0;
        state++;//!!deliberately fall through to next state to start displaying
      case 1: //display the next part of the character
        mx.setColumn(col--, cBuf[curLen++]);// done with font character, now display the space between chars
        if(curLen==showLen){
          showLen=CHAR_SPACING;
          state=2;
        }
        break;
      case 2: // initialize state for displaying empty columns
        curLen=0;
        state++;
      // fall through
      case 3:  // display inter-character spacing or end of message padding (blank columns)
        mx.setColumn(col--, 0);
        curLen++;
        if (curLen==showLen)
          state=0;
        break;
      default:
        col = -1;   // this definitely ends the do loop
    }
  }while (col >= (modStart * COL_SIZE));
  if (CenterJustify){
    for(int i=1; i<=(((MAX_DEVICES*COL_SIZE)-pixelcount)/2); i++){
      mx.transform( MD_MAX72XX::TSR);
    }
  }
  mx.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
}
void createFile(fs::FS &fs, const char * path){
  Serial.printf("Escribiendo archivo: %s\n", path);
  File file = fs.open(path, FILE_WRITE);
  if(file){
    Serial.println("Archivo nuevo, Escribiendo encabezado(Fila 1)");
    file.println("sensor1,sensor2,sensor3");
    file.close();
  }
  else{
    Serial.println("Error archivo");
  }
}
void appendFile(fs::FS &fs, const char * path, int message, int message1, int message2){
  //Serial.printf("Agregando datos: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if(file){
    file.print(message);
    file.print(",");
    file.print(message1);
    file.print(",");
    file.println(message2);
    file.close();
  }
  else{
   //Serial.println("Error al abrir archivo");
  }
}
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 obtainAccessToken() {
  // Conexión a la API de autenticación de Google
  client.setCACert(caCert);
  while(!client.connect(googleDriveHost, googleDrivePort)) {
    Serial.println("Error al conectarse a la API de Google");
  }
  // Construir la solicitud para obtener el token de acceso
  String request = "POST /oauth2/v4/token HTTP/1.1\r\n";
  request += "Host: www.googleapis.com\r\n";
  request += "Content-Type: application/x-www-form-urlencoded\r\n\r\n";
  request += "client_id=" + String(googleOAuthClientId) + "&";
  request += "client_secret=" + String(googleOAuthClientSecret) + "&";
  request += "refresh_token=" + String(googleOAuthRefreshToken) + "&";
  request += "grant_type=refresh_token\r\n";

  // Enviar la solicitud
  client.print(request);

  // Leer y procesar la respuesta del servidor
  while (client.connected()) {
    String line=client.readStringUntil('\n');
    if (line == "\r") {
      break;
    }
  }
  String response = client.readStringUntil('\n');
  Serial.println(response);
  if(response.indexOf("access_token") != -1) {
    // Extraer el token de acceso de la respuesta
    int start = response.indexOf(":") + 2;
    int end = response.lastIndexOf("\"", start);
    googleOAuthAccessToken=(response.substring(start, end)).c_str();
    Serial.print("Token de acceso obtenido correctamente: ");
    Serial.println(googleOAuthAccessToken);
  }
  else{
    Serial.println("Error al obtener el token de acceso");
  }
  // Cerrar la conexión
  client.stop();
}
1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.