niedziela, 18 grudnia 2022

[13] ESP32 - Arduino - Zapis danych Google Sheet IFTTT

W tym poście chciałbym opisać sposób wprowadzania danych z czujników do dokumentu xls umieszczonego na dysku Google.


Znalezione obrazy dla zapytania arduino esp32
[Źródło: http://paulobrien.co.nz/2017/03/16/esp32-programming-with-arduino-on-windows/]

IFTTT


Na samym początku należy wykonać połączenie z usługą internetową IFTTT. 

Po zalogowaniu na stronę IFTTT należy wybrać New Applet. Następnie WebHooks

Dla zakładki wybieramy opcję Receive a web request. Będzie on uruchamiany za każdym razem jak zostaną przesłane dane do serwisu:


Następnie podajemy nazwę zdarzenia i klikamy przycisk utworzenia "wyzwalacza":


Dalej przechodzimy do części, która ma się wykonać po otrzymaniu danych (Then That):


Wyszukujemy Google Sheet. Jak można zaobserwować na screenie poniżej, możliwości współpracy z serwisem Google są dosyć rozbudowane.


Następnie wybieramy opcję dodawania nowych wierszy do dokumentu:


Teraz należy zalogować się do serwisu Google, dodać uprawnienia oraz wprowadzić sposób ich zapisu:


W darmowej wersji można umieścić maksymalnie 5 kolumn  z danymi, z czego do dyspozycji użytkownika są trzy z nich (Value1, Value2, Value3). Dodatkowo należy wybrać lokalizację plików (domyślnie IFTT/MakerWebooks/<Event>). Nowy dokument zostanie utworzony po przekroczeniu 2000 wierszy.

Teraz wystarczy kliknąć Finish i pierwsza część jest już gotowa.


Następnym krokiem jest przygotowanie programu na ESP32 przesyłającego dane do serwisu IFTTT, który przekaże dane do dokumentu na dysku Google. 

Test połączenia z dyskiem możemy wykonać przez wejście na stronę webhook service page, gdzie w zakładce Documentation można zobaczyć w jaki sposób wykonać wysłanie danych, oraz jak przetestować połączenie:



W celu zadziałania z wprowadzonymi ustawieniami, należy uzupełnić pola w sekcji To trigger an Event with 3 JSON values:


Po wypełnieniu pola danymi otrzymamy następujący wynik:


Wysłane dane powinny zostać dodane do dokumentu na dysku Google:


Wobec tego w celu aktualizowania danych z poziomu arduino należy przygotować następujące dane do przesłania (po nawiązaniu połaczenia z serwerem IFTTT):

Można go wykonać ręcznie, lub posłużyć się dostępną biblioteką na Githubie

Funkcja przesyłająca dane wygląda w takim przypadku następująco:

  1. //https://github.com/Siytek/AnotherIFTTTWebhook/blob/master/AnotherIFTTTWebhook.h
  2. void send_webhook(char *MakerIFTTT_Event, char *MakerIFTTT_Key, char *value1, char *value2, char *value3) {
  3.     // connect to the Maker event server
  4.     client.connect("maker.ifttt.com", 80);
  5.  
  6.     // construct the POST request
  7.     char post_rqst[256];    // hand-calculated to be big enough
  8.  
  9.     char *p = post_rqst;
  10.     p = append_str(p, "POST /trigger/");
  11.     p = append_str(p, MakerIFTTT_Event);
  12.     p = append_str(p, "/with/key/");
  13.     p = append_str(p, MakerIFTTT_Key);
  14.     p = append_str(p, " HTTP/1.1\r\n");
  15.     p = append_str(p, "Host: maker.ifttt.com\r\n");
  16.     p = append_str(p, "Content-Type: application/json\r\n");
  17.     p = append_str(p, "Content-Length: ");
  18.  
  19.     // we need to remember where the content length will go, which is:
  20.     char *content_length_here = p;
  21.  
  22.     // it's always two digits, so reserve space for them (the NN)
  23.     p = append_str(p, "NN\r\n");
  24.  
  25.     // end of headers
  26.     p = append_str(p, "\r\n");
  27.  
  28.     // construct the JSON; remember where we started so we will know len
  29.     char *json_start = p;
  30.  
  31.     // As described - this example reports a pin, uptime, and "hello world"
  32.     p = append_str(p, "{\"value1\":\"");
  33.     p = append_str(p, value1);
  34.     p = append_str(p, "\",\"value2\":\"");
  35.     p = append_str(p, value2);
  36.     p = append_str(p, "\",\"value3\":\"");
  37.     p = append_str(p, value3);
  38.     p = append_str(p, "\"}");
  39.  
  40.     // go back and fill in the JSON length
  41.     // we just know this is at most 2 digits (and need to fill in both)
  42.     int i = strlen(json_start);
  43.     content_length_here[0] = '0' + (i/10);
  44.     content_length_here[1] = '0' + (i%10);
  45.  
  46.     // finally we are ready to send the POST to the server!
  47.     client.print(post_rqst);
  48.     client.stop();
  49. }

W przypadku ręcznego wykonywania, należy przesłać następujące dane:

  1. POST https://maker.ifttt.com/trigger/press_temp_read/with/key/<KLUCZ> HTTP/1.1
  2. Host: maker.ifttt.com
  3. Connection: close
  4. Content-Type: application/json
  5. Content-Length:
  6. 52
  7.  
  8. {"value1":"22.00","value2":"21.50","value3":"51.80"}

ESP32 Program:


Program ma za zadanie wykonać połączenie z WIFI, odczytać dane z czujników DS18B20 oraz DHT22. Kolejnym krokiem jest przesłanie danych do serwera IFTT, po czym układ przechodzi w stan uśpienia na jedną godzinę.  

Na samym początku należy dołożyć potrzebne biblioteki, zdefiniować dane do połączenia z Wifi, stałe do wysłania żądania do serwera, numery pinów do komunikacji z czujnikami. 

  1. #include <WiFi.h>
  2. #include <Wire.h>
  3. #include <OneWire.h>
  4. #include <DallasTemperature.h>
  5. #include "DHT.h"
  6.  
  7. #define DHTTYPE DHT22
  8.  
  9. const char* ssid     = "SSID";
  10. const char* password = "PASS";
  11.  
  12. const char* request = "https://maker.ifttt.com/trigger/<EVENT>/with/key/<KEY>";
  13. const char* server = "maker.ifttt.com";
  14.  
  15. const uint64_t TIME_TO_SLEEP = 3600;
  16.  
  17. const uint8_t DHTPin = 22;
  18. const uint8_t DS18b20Pin = 4;
  19.  
  20. OneWire oneWire(DS18b20Pin);
  21. DallasTemperature sensors(&oneWire);
  22. DHT dht(DHTPin, DHTTYPE);  

Funkcja setup:

  1. void setup() {
  2.   Serial.begin(115200);
  3.   delay(3000);
  4.  
  5.   Serial.println("Program start");
  6.  
  7.   pinMode(DHTPin, INPUT);
  8.   dht.begin();
  9.  
  10.   Serial.println("Init WIFI:");
  11.  
  12.   if(initWifi() == true){
  13.     Serial.println("SendRequest:");
  14.     SendIFTTTRequest();
  15.   }
  16.  
  17.   esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000);    
  18.   esp_deep_sleep_start();
  19. }

Powyżej uruchamiał interfejs UART do przesyłania danych na konsolę, następnie uruchamiam komunikację z czujnikiem DHT22. Kolejnym krokiem jest połączenie z wifi. Jeśli połączenie zostało nawiązane to następuje próba połączenia z serwerem IFTTT i przesłanie do niego danych. Po zakończeniu operacji układ wchodzi w tryb uśpienia Deep Sleep na 60 minut.

  1. bool initWifi() {
  2.   Serial.print("Try to connect: ");
  3.   Serial.print(ssid);
  4.   WiFi.begin(ssid, password);  
  5.  
  6.   int timeout = 100; // 10 seconds
  7.   while(WiFi.status() != WL_CONNECTED  && (timeout > 0)) {
  8.     delay(100);
  9.     Serial.print(".");
  10.     timeout--;
  11.   }
  12.  
  13.   if(WiFi.status() != WL_CONNECTED) {
  14.      Serial.println("/nWifi connection Error");
  15.      return false;
  16.   }
  17.  
  18.   Serial.print("/nWiFi connected, IP address: ");
  19.   Serial.println(WiFi.localIP());
  20.   return true;
  21. }

Funkcja initWifi nawiązuje połączenie z Wifi. W przypadku powodzenia zwraca true, jeśli nie uda się nawiązać połączenia zwracana jest wartość false.

Poniżej funkcja nawiązująca połączenie z serwerem i przesyłające dane:

  1. float GetDHTTemperature(void)
  2. {
  3.   return dht.readTemperature();
  4. }
  5.  
  6. float GetDHTHumid(void)
  7. {
  8.   return dht.readHumidity();
  9. }
  10.  
  11. float GetDS18B20Temperature(void)
  12. {
  13.   sensors.requestTemperatures();
  14.   return sensors.getTempCByIndex(0);  
  15. }
  16.  
  17. void SendIFTTTRequest() {
  18.   WiFiClient client;
  19.  
  20.   float Humid_DHT22 = 0;
  21.   float Temperature_DHT22 = 0;
  22.   float Temperature_DS18B20 = 0;
  23.  
  24.   int retransmission = 5;
  25.  
  26.   while(!client.connect(server, 80) && (retransmission > 0)) {
  27.     Serial.print(".");
  28.     retransmission--;
  29.   }
  30.   Serial.println();
  31.  
  32.   if(!client.connected()) {
  33.     Serial.println("Failed to connect...");
  34.   }
  35.  
  36.   Temperature_DHT22 = GetDHTTemperature();
  37.   Humid_DHT22 = GetDHTHumid();
  38.   Temperature_DS18B20 = GetDS18B20Temperature();
  39.  
  40.   String jsonSensorData = String("{\"value1\":\"") +
  41.                       (Temperature_DHT22) +
  42.                       "\",\"value2\":\"" +
  43.                       (Temperature_DS18B20) +
  44.                       "\",\"value3\":\"" +
  45.                       (Humid_DHT22) + "\"}";
  46.                      
  47.   client.println(String("POST ") + request + " HTTP/1.1");
  48.   client.println(String("Host: ") + server);
  49.   client.println("Connection: close\r\nContent-Type: application/json");
  50.   client.print("Content-Length: ");
  51.   client.println(jsonSensorData.length());
  52.   client.println();
  53.   client.println(jsonSensorData);
  54.  
  55.   int timeout = 50; // 5 seconds            
  56.   while(!client.available() && (timeout > 0)){
  57.     delay(100);
  58.     timeout--;
  59.  
  60.     if(timeout == 0){
  61.       Serial.println("No response from server..");
  62.     }
  63.   }
  64.  
  65.   while(client.available()){
  66.     //Print server response
  67.     Serial.write(client.read());
  68.   }
  69.  
  70.   Serial.println("\nClient close\n");
  71.   client.stop();
  72. }

Po poprawnym przesłaniu danych od serwera otrzymujemy następującą odpowiedź:

  1. HTTP/1.1 200 OK
  2. Content-Type: text/html; charset=utf-8
  3. Content-Length: 55
  4. Connection: close
  5. Date: Mon, 19 Dec 2022 00:43:26 GMT
  6. X-Powered-By: Sad Unicorns
  7. X-Robots-Tag: none
  8. X-Top-SecreTTT: ...
  9. ETag: W/"..."
  10. X-Cache: Miss from cloudfront
  11. Via: ... (CloudFront)
  12. X-Amz-Cf-Pop: WAW51-P1
  13. X-Amz-Cf-Id: ...
  14.  
  15. Congratulations! You've fired the press_temp_read event
  16. Client close

Dane zapisane w pliku na dysku Google można oczywiście dowolnie modyfikować. Nowe przesłane dane zostaną zapisane w następnym wolnym wierszu.

Pliki do projektu można pobrać z dysku Google pod tym linkiem.