IoT

Datalogger remoto no Google Sheets com ESP32

Eletrogate 23 de maio de 2024

O que é e como funciona o datalogger?

Denominamos “datalogger” um dispositivo capaz de armazenar diferentes dados de medições de diferentes parâmetros, como temperatura, umidade, pressão atmosférica, leituras de sinais analógicos, entre outros. Com placas microcontroladas, facilmente construímos um datalogger para nossos projetos utilizando um módulo de cartão de memória. Contudo, imagine o conforto que é ter acesso aos dados medidos 24 horas por dia e já organizados em uma planilha para você montar seus gráficos? Você não precisaria tirar o cartão de memória do módulo, tampouco estar próximo ao sistema de medição para ter acesso aos dados medidos se você possuir um datalogger que envia esses dados para você remotamente. Basta que você e a sua placa microcontrolada tenham acesso ao WiFi. Neste tutorial, você aprenderá como funciona este tipo de datalogger.

O datalogger apresentado a seguir funciona coletando dados através do microcontrolador ESP32 e enviando para uma planilha do Google Sheets. Este sistema é útil para monitorar remotamente, por exemplo, dados de sensores localizados em locais distantes de você, em qualquer local do planeta Terra, desde que possua acesso a internet.

É possível medir a temperatura e a umidade de um determinado local, enquanto você está no conforto de sua casa ou seu escritório. Para exemplificar, faremos esta medição: iremos ler valores de umidade e temperatura de um sensor DHT22 em uma planilha online, sem necessidade de monitor serial ou módulo de cartão de memória para isto.


Passos para realização da construção do datalogger

Para construir o datalogger, você deve:

  1. Criar um novo projeto no Google Cloud
    1. Criar uma conta de serviço no novo projeto
      1. Criar uma chave privada na nova conta de serviço
  2. Criar uma planilha no Google Sheets para receber os dados
  3. Baixar as bibliotecas necessárias
  4. Criar o algoritmo do código no Arduino IDE
  5. Montar o circuito com os materiais necessários

Com isso, seu datalogger estará funcionando. Você verá como fazer cada passo neste tutorial.


Criando o novo projeto no Google Cloud

Para que o sistema funcione, é necessário que você crie uma conta de serviço no Google Cloud. Você pode ser direcionado para o site do Google Cloud clicando aqui. É por meio dele que a placa enviará os dados e eles serão inseridos na planilha de forma automática.

Para criar sua conta de serviço, você precisa ter um projeto cadastrado. Cadastre um novo da seguinte forma:

Blog.Eletrogate-Datalogger-Digital-ESP32-1

Após clicar em “Novo Projeto”, dê um nome ao seu projeto e clique em continuar. Pode ser qualquer nome de sua preferência

Blog.Eletrogate-Datalogger-Digital-ESP32-2

Criado seu projeto, selecione-o de acordo com a imagem abaixo:

Blog.Eletrogate-Datalogger-Digital-ESP32-3

Agora, nas três barras no canto superior esquerdo, selecione a opção “IAM e administrador” e “Contas de serviço”.

Blog.Eletrogate-Datalogger-Digital-ESP32-4

Você será redirecionado para esta tela e deverá selecionar a opção “Criar conta de serviço”, conforme a imagem abaixo:

Blog.Eletrogate-Datalogger-Digital-ESP32-5

Dê um nome de sua preferência à conta de serviço e clique em “Criar e continuar”.

Blog.Eletrogate-Datalogger-Digital-ESP32-6

Na segunda etapa, selecione a opção “Proprietário”, que está dentro da caixa “Em uso”.

Blog.Eletrogate-Datalogger-Digital-ESP32-7

Na terceira etapa, clique apenas em “Concluir” e sua conta de serviço estará criada.

Blog.Eletrogate-Datalogger-Digital-ESP32-8

Agora, vamos criar uma chave privada para ela. Você pode fazer isso clicando nos três pontos do lado direito e em “Gerenciar chaves”

Blog.Eletrogate-Datalogger-Digital-ESP32-9

Clique em adicionar uma nova chave e selecione a opção do tipo “Json”. Clique em “Criar”.

Blog.Eletrogate-Datalogger-Digital-ESP32-10

Com sua chave privada criada, será baixado um arquivo com algumas informações sobre sua conta de serviço. Guarde-o. Você precisará dele.

Blog.Eletrogate-Datalogger-Digital-ESP32-11

Por fim, ative as API’s do Google Drive (clique aqui) e Google Sheets (clique aqui).

Pronto. Esta parte está concluída. Vamos para o código.


Bibliotecas

Para a base do código, serão necessárias as bibliotecas:

Você deve baixá-las e adicioná-las à IDE da Arduino, plataforma que será utilizada para programação. Usaremos a biblioteca DHT-Sensor-Library pois, neste exemplo, serão lidos valores de temperatura e umidade de um DHT22

O primeiro passo é adicionar as bibliotecas que serão usadas.

#include <ESP_Google_Sheet_Client.h>
#include <WiFiManager.h> 
#include <ESP32Time.h>
#include <DHT.h>

Criando a planilha

Aqui você precisará do arquivo gerado ao criar a chave privada da conta de serviço.

Abra o arquivo gerado ao criar a chave privada da conta de serviço e identifique os seguintes dados:

  • PROJECT_ID (em amarelo, na imagem abaixo)
  • CLIENT_EMAIL (em roxo, na imagem abaixo)
  • PRIVATE_KEY (em azul, na imagem abaixo)
  • Id da planilha (Disponível no link da planilha)

Blog-Eletrogate-datalogger-esp32-identificacao-dos-dados

Copie o CLIENT_EMAIL do seu arquivo e compartilhe o acesso como editor da sua planilha do Google Sheets com ele.

Blog.eletrogate

Guarde o id da planilha. Na planilha da imagem acima, o link é: https://docs.google.com/spreadsheets/d/10VMaIgI9pm62kL8cJawspliDoKuFwg-y5VLxQGkOCwg/edit#gid=0. A parte em negrito é o id. Cada planilha possui um id diferente. Para que o código funcione para você, você deve criar uma planilha sua, já que, quando este post for ao ar, esta planilha do tutorial já terá sido excluída. Isso também vale para os dados da conta de serviço.

Assim, adicione no código esses dados da seguinte forma:

#include <ESP_Google_Sheet_Client.h>
#include <WiFiManager.h> 
#include <ESP32Time.h>
#include <DHT.h>

#define PROJECT_ID "nomedoprojeto-419718"

#define CLIENT_EMAIL "nomedacontadeservico@nomedoprojeto-419718.iam.gserviceaccount.com"

#define ID_DA_PLANILHA "10VMaIgI9pm62kL8cJawspliDoKuFwg-y5VLxQGkOCwg"

#define PRIVATE_KEY "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCYSnrhgojEVhlW\nqIKMx6i+Vv2uU81ATPsYtUnPHLchQWyJIpc5iVptW7+kHtZp55A9SxXZpiT3NCgM\nTqt5km3yHgrIhyzWhGiIJ21vEFtQnbaFrFDtV/D9QXZygPqONNv1JOdoVwIVHn5Y\n+3HCkgS2uAB/gA3743hSWiQQJA/arGP4RlBwmRphbWUyVU5q4fC10B7c0ySCSXUt\nWrIn5sDpQ/DHXlhXUXaTlKGlTSf5SgBJtYnQkcDZXeFSVaQi2T4iOuH6SLPLxgEA\nzrHZEdc2jmqRLfYQ69ieis3qSFJAdHY4yhgzff0gjJ5JzhN3QY0SoflAsmEQepJX\nCb8OZMM9AgMBAAECggEAIBXrG1mc9p68Qm+I59/GC+o4kvLJhv9hFOTXKul+JOXL\nl/3/dgocEPfYYdv0aUY2M8++yAcy+RlWSbeGdbv16aHOPE8MkZq+ilciPZjo4EyG\nMNYsC8FwPu98pOXLRExbPmQ61Bcm02vEC3vYjTmmABg21KYWbChSCKnAuNzcMUI2\n31Q0G8IYElkdpE101/rzCxjJ/qgOSgY8bytAJZzHM0/fjjcuEvQdDQugCdkgosRu\nq0ZPzXTjBX2y3OJ/VVJXkbwL8TWUF8SyC7z/v0fzIsvR6iMCy70oBtNm+izhu3c0\n11UYQMTqIkmipSDGSaBvqqo94R5WClxHCirCALbDyQKBgQDXUMdjz2mhhlpxHn4/\nBIaldaW5T485f38eAUmXj6/AwTD4/51rFO1IZ13qK0IIPMlXvganh+Qz4H5kB2SE\nZBVDZpT7+Xt9nxXaxEbcYE4Am6HJXsq0ckT2cjOLWFjsElHi2fb0F7TXEmAU1zBp\nHMOt5xIU+qncvG//thMx2meRZQKBgQC1ERTbOvIdt6BJUp7R7juuqbbOL2GABcUx\nFAQ/5UY/tNg9QPrPFR8mak+t/50y4ZPJ3QZEgpgn0re0YjxaFxtcqCj3RfhyWGj5\n/vBYi+sJ6ALZ2PlLiL9VTxKkMCDbbWZ/PoKcD+90aYkfs+qWDI2nRO1K1icDRSlU\nD5U+he14+QKBgHtxR28UqcW+Jjn2NoDgkmVoBqQwVbesPe+w40UuTp/e4rnOqqyK\nqeM5x+EJ/eiFGRXhzyTbRtzrFus4RcuVKY+jz0SK6t7BhyEOKGYOHIpCr8WoP+Sm\nIp18Kcr40ro9Yo8BUZDtynMHiOhJ2upYYGNiN76pWxs/OaLL+0M4qOn1AoGAIzN3\n74sNKPPbCIY0dDGxjqpV7M3PUblNddYjYVyshJQe+gGZAQtlK+29IeGw6QRr2/HJ\nVyZgeFWB+2Khww389s2GcVQ8EdbYScnjZgOFpT5i14FULfge4nHl+N+Wc7JYG+LG\nN5rtfZ9K1kv/Dj7QhzViQItTuFIIz9vbsvUMjtkCgYEAsjfyC6vAuk5T20Bqc//L\n+766JzqWyy6MsR9R3IceupKEcvTv3xNv6eh/277i0bIFiuAITpF35LJ0/li/LgiA\nvYwPNA8lkISXUfAYz+KRmmR0rX2vnQ4QyyVizRL3vRVJ00ni6py5q5yCBRhg48dP\nw6BSMnG5KLGGilDXF0LZYbY=\n-----END PRIVATE KEY-----\n"


Variáveis globais

Vamos ver as outras variáveis globais:

FirebaseJson ValorDaMedicao;
FirebaseJson Resposta;
FirebaseJsonData IntervaloLido;

#define DHT1 26

unsigned long tempo = 0;
unsigned long aux = 0;

bool ready = false;

float TemperaturaAmbienteEmCelciusDHT1 = 0;
float UmidadeRelativaDoAmbienteDHT1 = 0;

WiFiManager wifiManager;

ESP32Time rtc (-10800);

DHT dht_1(DHT1, DHT22); //Inicialização do sensor DHT22

void tokenStatusCallback(TokenInfo info);

As variáveis do tipo FirebaseJson e FirebaseJsonData serão utilizadas, respectivamente, para armazenar um conjunto de dados que serão adicionados e lidos da planilha.

Para exemplificar o funcionamento, usaremos um DHT22 para capturar temperatura e umidade do ambiente. Conectamos o DHT22 na porta 26 do ESP32.  As duas variáveis do tipo unsigned long serão usadas para o millis() (intervalo de tempo entre uma atualização e outra).

A variável booleana “ready” auxilia na verificação de erros entre o microcontrolador ESP32 e a planilha. As duas variáveis do tipo float guardam, respectivamente, temperatura e umidade do ambiente, medidas pelo DHT22.

Logo após, iniciamos o WiFi Manager e o Real Time Clock (RTC) interno do ESP32, com um fuso horário de -3 horas (horário de Brasília), medidos em segundos (3 horas = 10800 segundos).

Além disso, iniciamos o sensor DHT22 e adicionamos o cabeçalho da função que verifica erros de conexão entre a placa e a planilha, muito útil para identificar erros no seu código, por exemplo, que estão impedindo a placa de se conectar corretamente.

A variável de valor inteiro “IntervaloDeMedicao” será usada para guardar um valor lido na planilha. O usuário poderá, durante a execução do código, mudar o valor do intervalo entre uma medição e outra.

Dessa forma, o código, antes do setup(), fica assim:

#include <ESP_Google_Sheet_Client.h>
#include <WiFiManager.h> 
#include <ESP32Time.h>
#include <DHT.h>

#define PROJECT_ID "nomedoprojeto-419718"

#define CLIENT_EMAIL "nomedacontadeservico@nomedoprojeto-419718.iam.gserviceaccount.com"

#define ID_DA_PLANILHA "10VMaIgI9pm62kL8cJawspliDoKuFwg-y5VLxQGkOCwg"

#define PRIVATE_KEY "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCYSnrhgojEVhlW\nqIKMx6i+Vv2uU81ATPsYtUnPHLchQWyJIpc5iVptW7+kHtZp55A9SxXZpiT3NCgM\nTqt5km3yHgrIhyzWhGiIJ21vEFtQnbaFrFDtV/D9QXZygPqONNv1JOdoVwIVHn5Y\n+3HCkgS2uAB/gA3743hSWiQQJA/arGP4RlBwmRphbWUyVU5q4fC10B7c0ySCSXUt\nWrIn5sDpQ/DHXlhXUXaTlKGlTSf5SgBJtYnQkcDZXeFSVaQi2T4iOuH6SLPLxgEA\nzrHZEdc2jmqRLfYQ69ieis3qSFJAdHY4yhgzff0gjJ5JzhN3QY0SoflAsmEQepJX\nCb8OZMM9AgMBAAECggEAIBXrG1mc9p68Qm+I59/GC+o4kvLJhv9hFOTXKul+JOXL\nl/3/dgocEPfYYdv0aUY2M8++yAcy+RlWSbeGdbv16aHOPE8MkZq+ilciPZjo4EyG\nMNYsC8FwPu98pOXLRExbPmQ61Bcm02vEC3vYjTmmABg21KYWbChSCKnAuNzcMUI2\n31Q0G8IYElkdpE101/rzCxjJ/qgOSgY8bytAJZzHM0/fjjcuEvQdDQugCdkgosRu\nq0ZPzXTjBX2y3OJ/VVJXkbwL8TWUF8SyC7z/v0fzIsvR6iMCy70oBtNm+izhu3c0\n11UYQMTqIkmipSDGSaBvqqo94R5WClxHCirCALbDyQKBgQDXUMdjz2mhhlpxHn4/\nBIaldaW5T485f38eAUmXj6/AwTD4/51rFO1IZ13qK0IIPMlXvganh+Qz4H5kB2SE\nZBVDZpT7+Xt9nxXaxEbcYE4Am6HJXsq0ckT2cjOLWFjsElHi2fb0F7TXEmAU1zBp\nHMOt5xIU+qncvG//thMx2meRZQKBgQC1ERTbOvIdt6BJUp7R7juuqbbOL2GABcUx\nFAQ/5UY/tNg9QPrPFR8mak+t/50y4ZPJ3QZEgpgn0re0YjxaFxtcqCj3RfhyWGj5\n/vBYi+sJ6ALZ2PlLiL9VTxKkMCDbbWZ/PoKcD+90aYkfs+qWDI2nRO1K1icDRSlU\nD5U+he14+QKBgHtxR28UqcW+Jjn2NoDgkmVoBqQwVbesPe+w40UuTp/e4rnOqqyK\nqeM5x+EJ/eiFGRXhzyTbRtzrFus4RcuVKY+jz0SK6t7BhyEOKGYOHIpCr8WoP+Sm\nIp18Kcr40ro9Yo8BUZDtynMHiOhJ2upYYGNiN76pWxs/OaLL+0M4qOn1AoGAIzN3\n74sNKPPbCIY0dDGxjqpV7M3PUblNddYjYVyshJQe+gGZAQtlK+29IeGw6QRr2/HJ\nVyZgeFWB+2Khww389s2GcVQ8EdbYScnjZgOFpT5i14FULfge4nHl+N+Wc7JYG+LG\nN5rtfZ9K1kv/Dj7QhzViQItTuFIIz9vbsvUMjtkCgYEAsjfyC6vAuk5T20Bqc//L\n+766JzqWyy6MsR9R3IceupKEcvTv3xNv6eh/277i0bIFiuAITpF35LJ0/li/LgiA\nvYwPNA8lkISXUfAYz+KRmmR0rX2vnQ4QyyVizRL3vRVJ00ni6py5q5yCBRhg48dP\nw6BSMnG5KLGGilDXF0LZYbY=\n-----END PRIVATE KEY-----\n"


FirebaseJson ValorDaMedicao;
FirebaseJson Resposta;
FirebaseJsonData IntervaloLido;

#define DHT1 26

IntervaloDeMedicao = 0;

unsigned long tempo = 0;
unsigned long aux = 0;

bool ready = false;

float TemperaturaAmbienteEmCelciusDHT1 = 0;
float UmidadeRelativaDoAmbienteDHT1 = 0;

WiFiManager wifiManager;

ESP32Time rtc (-10800);

DHT dht_1(DHT1, DHT22);

void tokenStatusCallback(TokenInfo info);

Constituição do void setup()

Abaixo, o void setup(). Iniciaremos o sensor DHT22, a serial e a planilha. Além disso, inicia o processo de conexão do WiFi da placa com a rede local por meio do WiFi manager. Caso você tenha dúvidas de como funciona ou prefere usar outra forma de conexão da placa com o WiFi, há um post no blog que pode ser útil. Você pode acessá-lo clicando aqui.

void setup() {

Serial.begin (115200);

dht_1.begin ();

 GSheet.begin(CLIENT_EMAIL, PROJECT_ID, PRIVATE_KEY);  

   while (WiFi.status () != WL_CONNECTED){  

wifiManager.autoConnect ("ESP32WiFi", "senhaesp32"); //SSID e password do Wifi roteado pela ESP32

   }
  
 }

Com isso, o código fica da seguinte forma:

#include <ESP_Google_Sheet_Client.h>
#include <WiFiManager.h> 
#include <ESP32Time.h>
#include <DHT.h>

#define PROJECT_ID "nomedoprojeto-419718"

#define CLIENT_EMAIL "nomedacontadeservico@nomedoprojeto-419718.iam.gserviceaccount.com"

#define ID_DA_PLANILHA "10VMaIgI9pm62kL8cJawspliDoKuFwg-y5VLxQGkOCwg"

#define PRIVATE_KEY "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCYSnrhgojEVhlW\nqIKMx6i+Vv2uU81ATPsYtUnPHLchQWyJIpc5iVptW7+kHtZp55A9SxXZpiT3NCgM\nTqt5km3yHgrIhyzWhGiIJ21vEFtQnbaFrFDtV/D9QXZygPqONNv1JOdoVwIVHn5Y\n+3HCkgS2uAB/gA3743hSWiQQJA/arGP4RlBwmRphbWUyVU5q4fC10B7c0ySCSXUt\nWrIn5sDpQ/DHXlhXUXaTlKGlTSf5SgBJtYnQkcDZXeFSVaQi2T4iOuH6SLPLxgEA\nzrHZEdc2jmqRLfYQ69ieis3qSFJAdHY4yhgzff0gjJ5JzhN3QY0SoflAsmEQepJX\nCb8OZMM9AgMBAAECggEAIBXrG1mc9p68Qm+I59/GC+o4kvLJhv9hFOTXKul+JOXL\nl/3/dgocEPfYYdv0aUY2M8++yAcy+RlWSbeGdbv16aHOPE8MkZq+ilciPZjo4EyG\nMNYsC8FwPu98pOXLRExbPmQ61Bcm02vEC3vYjTmmABg21KYWbChSCKnAuNzcMUI2\n31Q0G8IYElkdpE101/rzCxjJ/qgOSgY8bytAJZzHM0/fjjcuEvQdDQugCdkgosRu\nq0ZPzXTjBX2y3OJ/VVJXkbwL8TWUF8SyC7z/v0fzIsvR6iMCy70oBtNm+izhu3c0\n11UYQMTqIkmipSDGSaBvqqo94R5WClxHCirCALbDyQKBgQDXUMdjz2mhhlpxHn4/\nBIaldaW5T485f38eAUmXj6/AwTD4/51rFO1IZ13qK0IIPMlXvganh+Qz4H5kB2SE\nZBVDZpT7+Xt9nxXaxEbcYE4Am6HJXsq0ckT2cjOLWFjsElHi2fb0F7TXEmAU1zBp\nHMOt5xIU+qncvG//thMx2meRZQKBgQC1ERTbOvIdt6BJUp7R7juuqbbOL2GABcUx\nFAQ/5UY/tNg9QPrPFR8mak+t/50y4ZPJ3QZEgpgn0re0YjxaFxtcqCj3RfhyWGj5\n/vBYi+sJ6ALZ2PlLiL9VTxKkMCDbbWZ/PoKcD+90aYkfs+qWDI2nRO1K1icDRSlU\nD5U+he14+QKBgHtxR28UqcW+Jjn2NoDgkmVoBqQwVbesPe+w40UuTp/e4rnOqqyK\nqeM5x+EJ/eiFGRXhzyTbRtzrFus4RcuVKY+jz0SK6t7BhyEOKGYOHIpCr8WoP+Sm\nIp18Kcr40ro9Yo8BUZDtynMHiOhJ2upYYGNiN76pWxs/OaLL+0M4qOn1AoGAIzN3\n74sNKPPbCIY0dDGxjqpV7M3PUblNddYjYVyshJQe+gGZAQtlK+29IeGw6QRr2/HJ\nVyZgeFWB+2Khww389s2GcVQ8EdbYScnjZgOFpT5i14FULfge4nHl+N+Wc7JYG+LG\nN5rtfZ9K1kv/Dj7QhzViQItTuFIIz9vbsvUMjtkCgYEAsjfyC6vAuk5T20Bqc//L\n+766JzqWyy6MsR9R3IceupKEcvTv3xNv6eh/277i0bIFiuAITpF35LJ0/li/LgiA\nvYwPNA8lkISXUfAYz+KRmmR0rX2vnQ4QyyVizRL3vRVJ00ni6py5q5yCBRhg48dP\nw6BSMnG5KLGGilDXF0LZYbY=\n-----END PRIVATE KEY-----\n"


FirebaseJson ValorDaMedicao;
FirebaseJson Resposta;
FirebaseJsonData IntervaloLido;

#define DHT1 26

int IntervaloDeMedicao = 0;

unsigned long tempo = 0;
unsigned long aux = 0;

bool ready = false;

float TemperaturaAmbienteEmCelciusDHT1 = 0;
float UmidadeRelativaDoAmbienteDHT1 = 0;

WiFiManager wifiManager;

ESP32Time rtc (-10800);

DHT dht_1(DHT1, DHT22);

void tokenStatusCallback(TokenInfo info);


void setup() {

Serial.begin (115200);

dht_1.begin ();

 GSheet.begin(CLIENT_EMAIL, PROJECT_ID, PRIVATE_KEY);  

   while (WiFi.status () != WL_CONNECTED){  

wifiManager.autoConnect ("ESP32WiFi", "senhaesp32"); //SSID e password do Wifi roteado pela ESP32

   }
  
 }

Escrevendo dados na planilha

Para que você entenda como está funcionando o algoritmo, vamos construir o loop aos poucos.

Primeiro, construiremos o processo de espera entre uma atualização e outra utilizando millis(). Por enquanto, vamos utilizar um intervalo de 10 segundos, mas isso será ajustado no próximo passo.

A função GSheet.ready() retorna verdadeiro (1 ou true) para a variável “ready” se houver conexão entre a placa e a planilha. Caso contrário, retorna falso (0 ou false) para a variável em questão. Caso a conexão não seja estabelecida, o programa executará a verificação de erros e retornará no monitor serial um código de erro. Se este código de erros aparecer, você pode verificar o que ele significa aqui. Caso estiver tudo certo, executará o resto do programa.

void loop() {
tempo = millis();

ready = GSheet.ready();

if (ready == false){

GSheet.setTokenCallback(tokenStatusCallback);}  

if (ready == true && tempo - aux >= 10000){ 

aux = tempo;

}
}

//Verificação de erros caso GSheet.ready() == 0

void tokenStatusCallback(TokenInfo info)
{
    if (info.status == token_status_error)
    {
        GSheet.printf("Token info: type = %s, status = %s\n", GSheet.getTokenType(info).c_str(), GSheet.getTokenStatus(info).c_str());
        GSheet.printf("Token error: %s\n", GSheet.getTokenError(info).c_str());
    }
    else
    {
        GSheet.printf("Token info: type = %s, status = %s\n", GSheet.getTokenType(info).c_str(), GSheet.getTokenStatus(info).c_str());
    }
}

A variável “ready” sendo verdadeira, começamos com a leitura dos dados de umidade e temperatura do sensor DHT22.

if (ready == true && tempo - aux >= 10000){

TemperaturaAmbienteEmCelciusDHT1 = dht_1.readTemperature();
UmidadeRelativaDoAmbienteDHT1 = dht_1.readHumidity (); 

aux = tempo;

}
}

Agora, vamos preparar o conjunto de dados que serão inseridos na planilha. Data e horário, temperatura e umidade.

if (ready == false){

GSheet.setTokenCallback(tokenStatusCallback);} 

if (ready == true && tempo - aux >= 10000){

TemperaturaAmbienteEmCelciusDHT1 = dht_1.readTemperature();
UmidadeRelativaDoAmbienteDHT1 = dht_1.readHumidity (); 

ValorDaMedicao.add ("majorDimension", "COLUMNS");
ValorDaMedicao.set ("values/[0]/[0]", rtc.getDateTime(true));
ValorDaMedicao.set ("values/[1]/[0]", TemperaturaAmbienteEmCelciusDHT1);
ValorDaMedicao.set ("values/[2]/[0]", UmidadeRelativaDoAmbienteDHT1);

aux = tempo;

}
}

Com a função “GSheet.values.append”, vamos acrescentar os dados na planilha. O primeiro parâmetro nada mais é do que a variável “Resposta”. Ela será responsável por guardar os valores inseridos e retornar, caso você deseje, informações sobre esses valores. O ID da planilha serve para a placa se acessar o link da planilha de inserção de dados.

O terceiro parâmetro requer cuidado. Veja que está escrito da seguinte forma: “Página1!A1”. “Página1” se refere ao nome da página da planilha. “A1” se refere à célula que você vai inserir o dado. Caso a célula já esteja ocupada, a célula imediatamente abaixo será preenchida, e assim por diante.

Por fim, o último parâmetro se refere ao conjunto de dados que serão enviados, que estão dentro da variável “ValorDaMedicao”.

void loop() {
tempo = millis();

ready = GSheet.ready(); 

if (ready == false){

GSheet.setTokenCallback(tokenStatusCallback);} 

if (ready == true && tempo - aux >= 10000){

TemperaturaAmbienteEmCelciusDHT1 = dht_1.readTemperature();
UmidadeRelativaDoAmbienteDHT1 = dht_1.readHumidity (); 

ValorDaMedicao.add ("majorDimension", "COLUMNS");
ValorDaMedicao.set ("values/[0]/[0]", rtc.getDateTime(true));
ValorDaMedicao.set ("values/[1]/[0]", TemperaturaAmbienteEmCelciusDHT1);
ValorDaMedicao.set ("values/[2]/[0]", UmidadeRelativaDoAmbienteDHT1);

GSheet.values.append (&Resposta, ID_DA_PLANILHA, "Página1!A1", &ValorDaMedicao);

aux = tempo;

}
}

Assim, o resto do código fica da seguinte forma:

void loop() {
tempo = millis();

ready = GSheet.ready(); 

if (ready == false){

GSheet.setTokenCallback(tokenStatusCallback);} 

if (ready == true && tempo - aux >= 10000){

TemperaturaAmbienteEmCelciusDHT1 = dht_1.readTemperature();
UmidadeRelativaDoAmbienteDHT1 = dht_1.readHumidity (); 

ValorDaMedicao.add ("majorDimension", "COLUMNS");
ValorDaMedicao.set ("values/[0]/[0]", rtc.getDateTime(true));
ValorDaMedicao.set ("values/[1]/[0]", TemperaturaAmbienteEmCelciusDHT1);
ValorDaMedicao.set ("values/[2]/[0]", UmidadeRelativaDoAmbienteDHT1);

GSheet.values.append (&Resposta, ID_DA_PLANILHA, "Página1!A1", &ValorDaMedicao);

aux = tempo;

}
}
 

//Verificação de erros caso GSheet.read() == 0

void tokenStatusCallback(TokenInfo info)
{
    if (info.status == token_status_error)
    {
        GSheet.printf("Token info: type = %s, status = %s\n", GSheet.getTokenType(info).c_str(), GSheet.getTokenStatus(info).c_str());
        GSheet.printf("Token error: %s\n", GSheet.getTokenError(info).c_str());
    }
    else
    {
        GSheet.printf("Token info: type = %s, status = %s\n", GSheet.getTokenType(info).c_str(), GSheet.getTokenStatus(info).c_str());
    }
}

Assim, o código completo fica desta forma. Você já consegue compilá-lo em sua placa, mas vamos aprimorá-lo no próximo passo.

#include <ESP_Google_Sheet_Client.h>
#include <WiFiManager.h> 
#include <ESP32Time.h>
#include <DHT.h>

#define PROJECT_ID "nomedoprojeto-419718"

#define CLIENT_EMAIL "nomedacontadeservico@nomedoprojeto-419718.iam.gserviceaccount.com"

#define ID_DA_PLANILHA "10VMaIgI9pm62kL8cJawspliDoKuFwg-y5VLxQGkOCwg"

#define PRIVATE_KEY "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCYSnrhgojEVhlW\nqIKMx6i+Vv2uU81ATPsYtUnPHLchQWyJIpc5iVptW7+kHtZp55A9SxXZpiT3NCgM\nTqt5km3yHgrIhyzWhGiIJ21vEFtQnbaFrFDtV/D9QXZygPqONNv1JOdoVwIVHn5Y\n+3HCkgS2uAB/gA3743hSWiQQJA/arGP4RlBwmRphbWUyVU5q4fC10B7c0ySCSXUt\nWrIn5sDpQ/DHXlhXUXaTlKGlTSf5SgBJtYnQkcDZXeFSVaQi2T4iOuH6SLPLxgEA\nzrHZEdc2jmqRLfYQ69ieis3qSFJAdHY4yhgzff0gjJ5JzhN3QY0SoflAsmEQepJX\nCb8OZMM9AgMBAAECggEAIBXrG1mc9p68Qm+I59/GC+o4kvLJhv9hFOTXKul+JOXL\nl/3/dgocEPfYYdv0aUY2M8++yAcy+RlWSbeGdbv16aHOPE8MkZq+ilciPZjo4EyG\nMNYsC8FwPu98pOXLRExbPmQ61Bcm02vEC3vYjTmmABg21KYWbChSCKnAuNzcMUI2\n31Q0G8IYElkdpE101/rzCxjJ/qgOSgY8bytAJZzHM0/fjjcuEvQdDQugCdkgosRu\nq0ZPzXTjBX2y3OJ/VVJXkbwL8TWUF8SyC7z/v0fzIsvR6iMCy70oBtNm+izhu3c0\n11UYQMTqIkmipSDGSaBvqqo94R5WClxHCirCALbDyQKBgQDXUMdjz2mhhlpxHn4/\nBIaldaW5T485f38eAUmXj6/AwTD4/51rFO1IZ13qK0IIPMlXvganh+Qz4H5kB2SE\nZBVDZpT7+Xt9nxXaxEbcYE4Am6HJXsq0ckT2cjOLWFjsElHi2fb0F7TXEmAU1zBp\nHMOt5xIU+qncvG//thMx2meRZQKBgQC1ERTbOvIdt6BJUp7R7juuqbbOL2GABcUx\nFAQ/5UY/tNg9QPrPFR8mak+t/50y4ZPJ3QZEgpgn0re0YjxaFxtcqCj3RfhyWGj5\n/vBYi+sJ6ALZ2PlLiL9VTxKkMCDbbWZ/PoKcD+90aYkfs+qWDI2nRO1K1icDRSlU\nD5U+he14+QKBgHtxR28UqcW+Jjn2NoDgkmVoBqQwVbesPe+w40UuTp/e4rnOqqyK\nqeM5x+EJ/eiFGRXhzyTbRtzrFus4RcuVKY+jz0SK6t7BhyEOKGYOHIpCr8WoP+Sm\nIp18Kcr40ro9Yo8BUZDtynMHiOhJ2upYYGNiN76pWxs/OaLL+0M4qOn1AoGAIzN3\n74sNKPPbCIY0dDGxjqpV7M3PUblNddYjYVyshJQe+gGZAQtlK+29IeGw6QRr2/HJ\nVyZgeFWB+2Khww389s2GcVQ8EdbYScnjZgOFpT5i14FULfge4nHl+N+Wc7JYG+LG\nN5rtfZ9K1kv/Dj7QhzViQItTuFIIz9vbsvUMjtkCgYEAsjfyC6vAuk5T20Bqc//L\n+766JzqWyy6MsR9R3IceupKEcvTv3xNv6eh/277i0bIFiuAITpF35LJ0/li/LgiA\nvYwPNA8lkISXUfAYz+KRmmR0rX2vnQ4QyyVizRL3vRVJ00ni6py5q5yCBRhg48dP\nw6BSMnG5KLGGilDXF0LZYbY=\n-----END PRIVATE KEY-----\n"


FirebaseJson ValorDaMedicao;
FirebaseJson Resposta;
FirebaseJsonData IntervaloLido;

#define DHT1 26

int IntervaloDeMedicao = 0;
unsigned long tempo = 0;
unsigned long aux = 0;

bool ready = false;

float TemperaturaAmbienteEmCelciusDHT1 = 0;
float UmidadeRelativaDoAmbienteDHT1 = 0;

WiFiManager wifiManager;

ESP32Time rtc (-10800);

DHT dht_1(DHT1, DHT22);

void tokenStatusCallback(TokenInfo info);


void setup() {

Serial.begin (115200);

dht_1.begin ();

 GSheet.begin(CLIENT_EMAIL, PROJECT_ID, PRIVATE_KEY);  

   while (WiFi.status () != WL_CONNECTED){  

wifiManager.autoConnect ("ESP32WiFi", "senhaesp32"); //SSID e password do Wifi roteado pela ESP32

   }
  
 }

  
void loop() {
tempo = millis();

ready = GSheet.ready();

if (ready == false){

GSheet.setTokenCallback(tokenStatusCallback);} 

if (ready == true && tempo - aux >= 10000){

TemperaturaAmbienteEmCelciusDHT1 = dht_1.readTemperature();
UmidadeRelativaDoAmbienteDHT1 = dht_1.readHumidity (); 

ValorDaMedicao.add ("majorDimension", "COLUMNS");
ValorDaMedicao.set ("values/[0]/[0]", rtc.getDateTime(true));
ValorDaMedicao.set ("values/[1]/[0]", TemperaturaAmbienteEmCelciusDHT1);
ValorDaMedicao.set ("values/[2]/[0]", UmidadeRelativaDoAmbienteDHT1);

GSheet.values.append (&Resposta, ID_DA_PLANILHA, "Página1!A1", &ValorDaMedicao);

aux = tempo;

}
}

//Verificação de erros caso GSheet.read() == 0

void tokenStatusCallback(TokenInfo info)
{
    if (info.status == token_status_error)
    {
        GSheet.printf("Token info: type = %s, status = %s\n", GSheet.getTokenType(info).c_str(), GSheet.getTokenStatus(info).c_str());
        GSheet.printf("Token error: %s\n", GSheet.getTokenError(info).c_str());
    }
    else
    {
        GSheet.printf("Token info: type = %s, status = %s\n", GSheet.getTokenType(info).c_str(), GSheet.getTokenStatus(info).c_str());
    }
}

Lendo dados da planilha

A função GSheet.values.get é usada para capturar dados da planilha. Aqui, usaremos esta função para capturar um valor escrito na célula E1. É nela onde vamos escrever o valor em segundos entre uma atualização e outra. O valor capturado é adicionado dentro da variável do tipo FirebaseJson “Resposta”.

Pegamos o valor que está dentro da variável “Resposta” e a colocamos dentro de uma do tipo “FirebaseJsonData”, que será transformada em valor inteiro e armazenada dentro da variável “IntervaloDeMedicao”.

GSheet.values.get (&Resposta, ID_DA_PLANILHA, "Página1!E1");

Resposta.get (IntervaloLido, "values/[0]/[0]");

if (IntervaloLido.success){ 

  IntervaloDeMedicao = IntervaloLido.to<String>().toInt() * 1000;
}

Agora, vamos ajustar, no inicio do void loop, o algoritmo do intervalo do millis().

Assim, o código completo fica desta forma. Você já consegue compilá-lo em sua placa, mas vamos aprimorá-lo no próximo passo.

void loop() {
tempo = millis();

ready = GSheet.ready();

if (ready == false){

GSheet.setTokenCallback(tokenStatusCallback);} 

if (ready == true && tempo - aux >= IntervaloDeMedicao){

GSheet.values.get (&Resposta, ID_DA_PLANILHA, "Página1!E1"); 

Resposta.get (IntervaloLido, "values/[0]/[0]"); 

if (IntervaloLido.success){ 

IntervaloDeMedicao = IntervaloLido.to<String>().toInt() * 1000; 

}

aux = tempo;

} }

Código completo

Com todas as partes finalizadas, temos o seguinte código:

#include <ESP_Google_Sheet_Client.h>
#include <WiFiManager.h> 
#include <ESP32Time.h>
#include <DHT.h>

#define PROJECT_ID "nomedoprojeto-419718"

#define CLIENT_EMAIL "nomedacontadeservico@nomedoprojeto-419718.iam.gserviceaccount.com"

#define ID_DA_PLANILHA "10VMaIgI9pm62kL8cJawspliDoKuFwg-y5VLxQGkOCwg"

#define PRIVATE_KEY "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCYSnrhgojEVhlW\nqIKMx6i+Vv2uU81ATPsYtUnPHLchQWyJIpc5iVptW7+kHtZp55A9SxXZpiT3NCgM\nTqt5km3yHgrIhyzWhGiIJ21vEFtQnbaFrFDtV/D9QXZygPqONNv1JOdoVwIVHn5Y\n+3HCkgS2uAB/gA3743hSWiQQJA/arGP4RlBwmRphbWUyVU5q4fC10B7c0ySCSXUt\nWrIn5sDpQ/DHXlhXUXaTlKGlTSf5SgBJtYnQkcDZXeFSVaQi2T4iOuH6SLPLxgEA\nzrHZEdc2jmqRLfYQ69ieis3qSFJAdHY4yhgzff0gjJ5JzhN3QY0SoflAsmEQepJX\nCb8OZMM9AgMBAAECggEAIBXrG1mc9p68Qm+I59/GC+o4kvLJhv9hFOTXKul+JOXL\nl/3/dgocEPfYYdv0aUY2M8++yAcy+RlWSbeGdbv16aHOPE8MkZq+ilciPZjo4EyG\nMNYsC8FwPu98pOXLRExbPmQ61Bcm02vEC3vYjTmmABg21KYWbChSCKnAuNzcMUI2\n31Q0G8IYElkdpE101/rzCxjJ/qgOSgY8bytAJZzHM0/fjjcuEvQdDQugCdkgosRu\nq0ZPzXTjBX2y3OJ/VVJXkbwL8TWUF8SyC7z/v0fzIsvR6iMCy70oBtNm+izhu3c0\n11UYQMTqIkmipSDGSaBvqqo94R5WClxHCirCALbDyQKBgQDXUMdjz2mhhlpxHn4/\nBIaldaW5T485f38eAUmXj6/AwTD4/51rFO1IZ13qK0IIPMlXvganh+Qz4H5kB2SE\nZBVDZpT7+Xt9nxXaxEbcYE4Am6HJXsq0ckT2cjOLWFjsElHi2fb0F7TXEmAU1zBp\nHMOt5xIU+qncvG//thMx2meRZQKBgQC1ERTbOvIdt6BJUp7R7juuqbbOL2GABcUx\nFAQ/5UY/tNg9QPrPFR8mak+t/50y4ZPJ3QZEgpgn0re0YjxaFxtcqCj3RfhyWGj5\n/vBYi+sJ6ALZ2PlLiL9VTxKkMCDbbWZ/PoKcD+90aYkfs+qWDI2nRO1K1icDRSlU\nD5U+he14+QKBgHtxR28UqcW+Jjn2NoDgkmVoBqQwVbesPe+w40UuTp/e4rnOqqyK\nqeM5x+EJ/eiFGRXhzyTbRtzrFus4RcuVKY+jz0SK6t7BhyEOKGYOHIpCr8WoP+Sm\nIp18Kcr40ro9Yo8BUZDtynMHiOhJ2upYYGNiN76pWxs/OaLL+0M4qOn1AoGAIzN3\n74sNKPPbCIY0dDGxjqpV7M3PUblNddYjYVyshJQe+gGZAQtlK+29IeGw6QRr2/HJ\nVyZgeFWB+2Khww389s2GcVQ8EdbYScnjZgOFpT5i14FULfge4nHl+N+Wc7JYG+LG\nN5rtfZ9K1kv/Dj7QhzViQItTuFIIz9vbsvUMjtkCgYEAsjfyC6vAuk5T20Bqc//L\n+766JzqWyy6MsR9R3IceupKEcvTv3xNv6eh/277i0bIFiuAITpF35LJ0/li/LgiA\nvYwPNA8lkISXUfAYz+KRmmR0rX2vnQ4QyyVizRL3vRVJ00ni6py5q5yCBRhg48dP\nw6BSMnG5KLGGilDXF0LZYbY=\n-----END PRIVATE KEY-----\n"


FirebaseJson ValorDaMedicao;
FirebaseJson Resposta;
FirebaseJsonData IntervaloLido;

#define DHT1 26

int IntervaloDeMedicao = 0;
unsigned long tempo = 0;
unsigned long aux = 0;

bool ready = false;

float TemperaturaAmbienteEmCelciusDHT1 = 0;
float UmidadeRelativaDoAmbienteDHT1 = 0;

WiFiManager wifiManager;

ESP32Time rtc (-10800);

DHT dht_1(DHT1, DHT22);

void tokenStatusCallback(TokenInfo info);


void setup() {

Serial.begin (115200);

dht_1.begin ();

 GSheet.begin(CLIENT_EMAIL, PROJECT_ID, PRIVATE_KEY);  

   while (WiFi.status () != WL_CONNECTED){  

wifiManager.autoConnect ("ESP32WiFi", "senhaesp32"); //SSID e password do Wifi roteado pela ESP32

   }
  
 }

  
void loop() {
tempo = millis();

ready = GSheet.ready();

if (ready == false){

GSheet.setTokenCallback(tokenStatusCallback);} 

if (ready == true && tempo - aux >= IntervaloDeMedicao){

TemperaturaAmbienteEmCelciusDHT1 = dht_1.readTemperature();
UmidadeRelativaDoAmbienteDHT1 = dht_1.readHumidity (); 

ValorDaMedicao.add ("majorDimension", "COLUMNS");
ValorDaMedicao.set ("values/[0]/[0]", rtc.getDateTime(true));
ValorDaMedicao.set ("values/[1]/[0]", TemperaturaAmbienteEmCelciusDHT1);
ValorDaMedicao.set ("values/[2]/[0]", UmidadeRelativaDoAmbienteDHT1);

GSheet.values.append (&Resposta, ID_DA_PLANILHA, "Página1!A1", &ValorDaMedicao);

GSheet.values.get (&Resposta, ID_DA_PLANILHA, "Página1!E1"); 

Resposta.get (IntervaloLido, "values/[0]/[0]"); 

if (IntervaloLido.success){ 

IntervaloDeMedicao = IntervaloLido.to<String>().toInt() * 1000;
 }

aux = tempo;

}
}

//Verificação de erros caso GSheet.read() == 0

void tokenStatusCallback(TokenInfo info)
{
    if (info.status == token_status_error)
    {
        GSheet.printf("Token info: type = %s, status = %s\n", GSheet.getTokenType(info).c_str(), GSheet.getTokenStatus(info).c_str());
        GSheet.printf("Token error: %s\n", GSheet.getTokenError(info).c_str());
    }
    else
    {
        GSheet.printf("Token info: type = %s, status = %s\n", GSheet.getTokenType(info).c_str(), GSheet.getTokenStatus(info).c_str());
    }
}

 


Circuito e funcionamento

A seguir, o circuito utilizado para o datalogger:

blog-eletrogate-circuito-datalogger-esp32

 

Abaixo, o vídeo de funcionamento do datalogger:

 

No vídeo acima, nas células da coluna A temos a data e o horário, nas da coluna B temos a temperatura e nas da coluna C temos a umidade relativa do ar. Perceba que a medida em que mudamos o valor da célula E1, o intervalo de tempo entre uma medição e outra é modificado. Quando colocado 1 segundo, a planilha passou a ser atualizada de 1 em 1 segundo, quando alterado para 10, a planilha passou a ser alimentada de 10 em 10 segundos.


Considerações finais

Este é um sistema que você pode utilizar como datalogger em seus projetos. Você pode aprimorar este código adicionando verificações de erros que achar conveniente, como rotinas de Watchdog, utilizados para caso o código dentro da placa trave ou algo não funcione corretamente. Contudo, este código já foi testado em campo e obteve êxito na captação de dados, sem perdas ou erros.

Você também pode criar um simples layout na planilha para melhorar a visualização dos dados, veja abaixo um exemplo:

Blog-Eletrogate-datalogger-esp32-exemplo-de-layout

Neste caso, eu inicio minhas medições a partir da linha 11 e leio o dado de intervalo de medição na célula A8.

Caso você queira se debruçar sobre mais funções e outras aplicabilidades que você pode dar à planilha, você pode acessar o site da biblioteca ESP-Google-Sheet-Client, no GitHub.


Sobre o autor


Pedro Cavalcante

Estudante de Sistemas de Energia Renovável no Instituto Federal da Paraíba. Entusiasta da automação em Arduino, ESP32 e Raspberry Pi.


Eletrogate

23 de maio de 2024

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!

Eletrogate Robô

Cadastre-se e fique por
dentro de novidades!