В настоящее время существует достаточно много облачных хранилищ данных (clouds), в которых можно хранить данные, считываемые от различных датчиков. Примерами подобных хранилищ являются сервисы Thingspeak и Google Firebase, веб-сервисы Amazon (AWS), MQTT серверы, Adafruit IO и т.п.
В данном проекте мы в качестве облачного хранилища данных будем использовать документы Google (Google Sheet). С помощью платы NodeMCU ESP8266 мы будем считывать данные температуры и влажности с датчика DHT11 и передавать их в документы Google по сети Интернет.
Ранее на нашем сайте мы рассматривали следующие проекты логгеров данных:
- логгер данных температуры и влажности на SD карту и компьютер с помощью Arduino;
- логгер данных с датчика температуры DHT11 на основе MATLAB и Arduino;
- бесконтактный настенный термометр на Arduino с логгером данных на SD карту.
Необходимые компоненты
- NodeMCU ESP8266 (купить на AliExpress).
- Датчик температуры и влажности DHT11 (купить на AliExpress).
- Соединительные провода.
Схема проекта
Схема логгера данных температуры на NodeMCU ESP8266 представлена на следующем рисунке.
Внешний вид собранной конструкции проекта показан на следующем рисунке.
Прежде чем переходить к написанию кода программы нам необходимо получить исходные данные (логин, пароль и т.д.) чтобы наша плата NodeMCU ESP8266 смогла получить доступ к серверу Google для записи наших данных в документы Google.
Создание скрипта в документах Google для записи данных
Выполните следующую последовательность шагов.
1. Зайдите в свой аккаунт Gmail используя имя своей почты и пароль. Создайте себе аккаунт Gmail если у вас его еще нет.
2. Нажмите на иконку App в правом верхнем углу экрана, обведенную зеленым кружком на рисунке ниже, и в открывшейся вкладке нажмите на Docs.
3. Появится экран документов Google (Google Docs). Выберите Sheets в сайдбаре слева.
4. Создайте новый лист документа (Blank Sheet).
5. Лист документа (Blank Sheet) создастся с именем “Untitled Spreadsheet”. Переименуйте его в любое имя, которое вам понравится. Мы переименовали этот лист в “ESP8266_Temp_Logger”. Что перименовать лист документа воспользуйтесь пунктом меню (“File” > “Rename”).
6. Также вы можете добавить в свой документ Google и другие листы, мы в данном проекте использовали только один лист. Поэтому мы переименовали “Sheet1” > “TempSheet” поскольку мы будем загружать в него данные температуры (Temperature).
7. После переименования проекта (Spreadsheet Project) и имени листа (Sheet name) можно приступать к созданию Google скрипта.
8. Перейдите в пункт ‘Tools’ (выделенный зеленым эллипсом на рисунке ниже) и затем нажмите на “<> Script Editor” (выделенный красным эллипсом на рисунке ниже).
9. В результате этого создастся скрипт Google (Google Script) с именем “Untitled project” – вы можете переименовать его на любое другое имя. В нашем случае мы переименовали его в “Untitled project” > “TempLog_Script”.
10. Скопируйте и вставьте код скрипта Google прикрепленный к этому файлу (GoogleScript.gs). Затем отредактируйте имя листа (Sheet name) и идентификатор листа (Sheet ID) в коде программы. Вы можете получить Sheet ID из Sheet URL (адреса листа) как показано на рисунке ниже – то есть вы можете вырезать его из адреса подобного этому: https://docs.google.com/spreadsheets/d/xxxxxxxxyyyyyyzzzzzzzzzz/edit#gid=0, где “xxxxxxxxyyyyyyzzzzzzzzzz” – это ваш Sheet ID.
11. Когда вы скопируете и вставите Google Script он будет выглядеть примерно следующим образом:
12. Сохраните файл. Если вы хотите создать свой собственный лист (sheet), то соответствующим образом измените параметры для доступа к нему: Sheet ID, Sheet Name и Sheet Project Name.
13. На этом мы закончили настройку Google Script в Spreadsheet. Теперь настало время получить главный параметр, а именно Google Script ID (идентификатор скрипта Google), который мы будем использовать в программе для модуля ESP8266. Если вы сделаете ошибку в указании Google Script ID, то тогда ваши данные не смогут записаться в Google Sheet.
Получение Google Script ID
1. Выберите пункт меню ‘Publish’ > ‘Deploy as Web App…’.
2. В качестве версии проекта (“Project version”) укажите “New”. Выберите ваш “your email id” в поле “Execute the app as”. Выберите “Anyone, even anonymous” (любой, даже анонимный) в поле “Who has access to the app” (кто имеет доступ к приложению). Затем нажмите на “Deploy” (разместить). Учтите, что если вы будете делать повторную публикацию (republishing), то выберите самую последнюю версию и затем нажмите снова на Deploy.
3. Вы должны предоставить Google права (разрешения) чтобы он разместил ваш проект как веб-приложение (web app). Для этого нажмите на “Review Permissions”.
4. Выберите ваш Email ID (имя вашей почты) с помощью которой вы создавали ваш проект/электронную таблицу (spreadsheet).
5. Нажмите на “Advanced”.
6. Затем нажмите на “Go to ‘your_script_name’(unsafe)”. В нашем случае это “TempLog_Script”.
7. Нажмите на “Allow” (разрешить) – после этого вы получите разрешение на размещение вашего проекта в качестве веб-приложения.
8. После этого вы увидите новый экран со ссылкой в поле “Current web app URL”. Этот URL содержит ваш Google Script ID. Скопируйте этот URL и сохраните его в надежном месте.
9. После того как вы скопируете код, он будет примерно в следующем формате: <https://script.google.com/macros/s/____Your_Google _ScriptID___/exec>.
В нашем случае мы получили наш Google script ID в виде следующей ссылки:
<https://script.google.com/macros/s/AKfycbxy9wAZKoPIpP53AvqYTFFn5kkqK_-avacf2NU_w7ycoEtlkuNt/exec>, то есть наш Google script ID - “AKfycbxy9wAZKoPIpP53AvqYTFFn5kkqK_-avacf2NU_w7ycoEtlkuNt”.
Сохраните этот Google Script в надежном месте.
Объяснение программы для модуля ESP8266
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
В нашем случае датчик DHT11 подключен к ESP8266 NodeMCU, которая подключена к сети Интернет при помощи технологии WiFi. Мы будем считывать данные с датчика DHT11 и передавать их на Google Sheet.
Первым делом в программе нам необходимо подключить используемые библиотеки. В нашем случае библиотека ESP8266WiFi.h используется для осуществления связи по технологии WiFi, а библиотека HTTPSRedirect.h используется для соединения с сервером Google Spreadsheet. Библиотека DebugMacros.h используется для корректировки/отладки (debug) получаемых данных, а библиотека DHT.h – для взаимодействия с датчиком DHT11.
1 2 3 4 |
#include <ESP8266WiFi.h> #include "HTTPSRedirect.h" #include "DebugMacros.h" #include <DHT.h> |
Укажем контакт, к которому подключен датчик DHT11 – в нашем случае это контакт D4. Также зададим тип датчика DHT (DHTTYPE) – в нашем случае мы используем DHT11.
1 2 |
#define DHTPIN D4 #define DHTTYPE DHT11 |
Инициализируем переменные для хранения данных температуры и влажности.
1 2 3 4 |
float h; float t; String sheetHumid = ""; String sheetTemp = ""; |
Далее укажем SSID и пароль для доступа к нашей сети WiFi.
1 2 |
const char* ssid = "CircuitDigest"; const char* password = "circuitdigestfun"; |
Далее укажем параметры доступа к серверу Google – адрес хоста (host address), Google script ID и номер порта (port number). Адрес хоста и номер порта можете оставить такими, какие они введены в нашем коде программы, но вот Google Scripts ID вы должны изменить на свой.
1 2 3 |
const char* host = "script.google.com"; const char* GScriptId = "AKfycbxy9wAZKoPIpPq5AvqYTFxxxkkqK_avacf2NU_w7ycoEtlkuNt"; const int httpsPort = 443; |
Затем укажем URL адрес листа Google (Google Sheet), в который мы будем записывать наши данные. По сути, это путь, по которому будут записываться наши данные.
1 2 |
String url = String("/macros/s/") + GScriptId + "/exec?value=Temperature"; String url2 = String("/macros/s/") + GScriptId + "/exec?cal"; |
Далее укажем адрес листа Google где мы создали этот лист (Google sheet).
1 2 3 |
String payload_base = "{\"command\": \"appendRow\", \ \"sheet_name\": \"TempSheet\", \ \"values\": "; |
Зададим клиент, который мы будем использовать в программе.
1 |
HTTPSRedirect* client = nullptr; |
Инициализируем последовательную связь для целей отладки со скоростью 115200 бод – вы можете использовать для этого и другие скорости передачи, например, 9600, 57600 и т.д. Также инициализируем датчик DHT11.
1 2 |
Serial.begin(115200); dht.begin(); |
Произведем попытку соединения с сетью WiFi и подождем пока соединение будет установлено.
1 2 3 4 5 |
WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } |
Создадим новое HTTPS соединение. Учтите, что если вы используете HTTPS соединение, то вам перед созданием соединения нужно обязательно использовать команду setInscure(), иначе соединение с сервером не будет установлено.
1 2 3 4 5 |
client = new HTTPSRedirect(httpsPort); client->setInsecure(); Start the respose body i.e. if the server replies then we can print it on serial monitor. client->setPrintResponseBody(true); client->setContentTypeHeader("application/json"); |
Произведем соединение с хостом. В нашем случае это "script.google.com".
1 2 |
Serial.print("Connecting to "); Serial.println(host); |
Произведем попытку соединения 5 раз, если соединение установить не удастся, то на этом прекратим попытки соединиться.
1 2 3 4 5 6 7 8 9 10 |
bool flag = false; for (int i = 0; i < 5; i++) { int retval = client->connect(host, httpsPort); if (retval == 1) { flag = true; break; } else Serial.println("Connection failed. Retrying..."); } |
Мы будем взаимодействовать с сервером с помощью функций GET и POST. Функция GET будет использоваться для считывания ячеек (таблицы, листа), а функция POST – для записи в ячейки. Считаем данные ячейки A1 с Google sheet.
1 |
client->GET(url, host); |
Считаем данные температуры и влажности с датчика DHT11 и сохраним их в соответствующих переменных. Если во время считывания данных произошла ошибка, напечатаем сообщение об этом.
1 2 3 4 5 6 |
h = dht.readHumidity(); t = dht.readTemperature if (isnan(h) || isnan(t)) { Serial.println(F("Failed to read from DHT sensor!")); return; } |
Запишем данные по необходимому нам пути. Данные будут записаны в Google Sheet. Путь данных содержит данные температуры и влажности – параметры sheetTemp и sheetHumid.
1 |
payload = payload_base + "\"" + sheetTemp + "," + sheetHumid + "\"}"; |
Если клиент подключен, то просто передадим данные на Google Sheet с использованием функции POST. Если передача данных не удалась, то сохраним их и увеличим счетчик неудачных попыток соединения на 1.
1 2 3 4 5 6 7 8 |
if (client->POST(url2, host, payload)) { ; } else { ++error_count; DPRINT("Error-count while connecting: "); DPRINTLN(error_count); } |
Если передача данных не удалась три раза, то тогда завершаем все процессы и переводим модуль ESP в режим глубокого сна (deepsleep). Более подробно про использование режима глубокого сна в модулях ESP можно прочитать в этой статье.
1 2 3 4 5 6 7 8 9 |
if (error_count > 3) { Serial.println("Halting processor..."); delete client; client = nullptr; Serial.printf("Final free heap: %u\n", ESP.getFreeHeap()); Serial.printf("Final stack: %u\n", ESP.getFreeContStack()); Serial.flush(); ESP.deepSleep(0); } |
После каждой передачи и считывания данных мы будем делать задержку не менее 2-х секунд, поскольку это рекомендовано библиотеками DHT и HTTPSRedirect.
Все необходимые для проекта библиотеки вы можете скачать в архиве по этой ссылке.
Исходный код программы (скетча)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
/* * ESP822 temprature logging to Google Sheet * CircuitDigest(http://www.circuitdigest.com/) */ #include <ESP8266WiFi.h> #include "HTTPSRedirect.h" #include "DebugMacros.h" #include <DHT.h> #define DHTPIN D4 // к этому контакту подключен датчик DHT11 #define DHTTYPE DHT11 // выбираем dht тип - DHT 11 или DHT22 DHT dht(DHTPIN, DHTTYPE); float h; float t; String sheetHumid = ""; String sheetTemp = ""; const char* ssid = "CircuitDigest"; // замените на ssid для вашей сети wifi const char* password = "circuitdigestfun"; // замените на пароль для вашей сети wifi const char* host = "script.google.com"; const char *GScriptId = "AKfycbxy9wAZKoPIpPq5AvqYTFFn5kkqK_-avacf2NU_w7ycoEtlkuNt"; // замените на ваш google script id const int httpsPort = 443; // the https порт оставляем тем же самым // echo | openssl s_client -connect script.google.com:443 |& openssl x509 -fingerprint -noout const char* fingerprint = ""; //const uint8_t fingerprint[20] = {}; String url = String("/macros/s/") + GScriptId + "/exec?value=Temperature"; // записываем значение температуры в Google Spreadsheet в ячейку A1 // Fetch Google Calendar events for 1 week ahead String url2 = String("/macros/s/") + GScriptId + "/exec?cal"; // записываем в ячейку A непрерывно //replace with sheet name not with spreadsheet file name taken from google String payload_base = "{\"command\": \"appendRow\", \ \"sheet_name\": \"TempSheet\", \ \"values\": "; String payload = ""; HTTPSRedirect* client = nullptr; // used to store the values of free stack and heap before the HTTPSRedirect object is instantiated // so that they can be written to Google sheets upon instantiation void setup() { delay(1000); Serial.begin(115200); dht.begin(); //initialise DHT11 Serial.println(); Serial.print("Connecting to wifi: "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); // используем класс HTTPSRedirect чтобы создать новое соединение TLS client = new HTTPSRedirect(httpsPort); client->setInsecure(); client->setPrintResponseBody(true); client->setContentTypeHeader("application/json"); Serial.print("Connecting to "); Serial.println(host); // пытаемся соединиться с "script.google.com" // пытаемся соединиться максимум 5 раз, после чего прекращаем процесс bool flag = false; for (int i = 0; i < 5; i++) { int retval = client->connect(host, httpsPort); if (retval == 1) { flag = true; break; } else Serial.println("Connection failed. Retrying..."); } if (!flag) { Serial.print("Could not connect to server: "); Serial.println(host); Serial.println("Exiting..."); return; } // Finish setup() function in 1s since it will fire watchdog timer and will reset the chip. //So avoid too many requests in setup() Serial.println("\nWrite into cell 'A1'"); Serial.println("------>"); // fetch spreadsheet data client->GET(url, host); Serial.println("\nGET: Fetch Google Calendar Data:"); Serial.println("------>"); // fetch spreadsheet data client->GET(url2, host); Serial.println("\nStart Sending Sensor Data to Google Spreadsheet"); // delete HTTPSRedirect object delete client; client = nullptr; } void loop() { h = dht.readHumidity(); // Reading temperature or humidity takes about 250 milliseconds! t = dht.readTemperature(); // Read temperature as Celsius (the default) if (isnan(h) || isnan(t)) { // Check if any reads failed and exit early (to try again). Serial.println(F("Failed to read from DHT sensor!")); return; } Serial.print("Humidity: "); Serial.print(h); sheetHumid = String(h) + String("%"); // преобразуем integer (humidity) в строку Serial.print("% Temperature: "); Serial.print(t); Serial.println("°C "); sheetTemp = String(t) + String("°C"); static int error_count = 0; static int connect_count = 0; const unsigned int MAX_CONNECT = 20; static bool flag = false; payload = payload_base + "\"" + sheetTemp + "," + sheetHumid + "\"}"; if (!flag) { client = new HTTPSRedirect(httpsPort); client->setInsecure(); flag = true; client->setPrintResponseBody(true); client->setContentTypeHeader("application/json"); } if (client != nullptr) { if (!client->connected()) { client->connect(host, httpsPort); client->POST(url2, host, payload, false); Serial.print("Sent : "); Serial.println("Temp and Humid"); } } else { DPRINTLN("Error creating client object!"); error_count = 5; } if (connect_count > MAX_CONNECT) { connect_count = 0; flag = false; delete client; return; } // Serial.println("GET Data from cell 'A1':"); // if (client->GET(url3, host)) { // ++connect_count; // } // else { // ++error_count; // DPRINT("Error-count while connecting: "); // DPRINTLN(error_count); // } Serial.println("POST or SEND Sensor data to Google Spreadsheet:"); if (client->POST(url2, host, payload)) { ; } else { ++error_count; DPRINT("Error-count while connecting: "); DPRINTLN(error_count); } if (error_count > 3) { Serial.println("Halting processor..."); delete client; client = nullptr; Serial.printf("Final free heap: %u\n", ESP.getFreeHeap()); Serial.printf("Final stack: %u\n", ESP.getFreeContStack()); Serial.flush(); ESP.deepSleep(0); } delay(3000); // keep delay of minimum 2 seconds as dht allow reading after 2 seconds interval and also for google sheet } |
Здравствуйте! Можете подсказать пожалуйста? С кодом в Ардуинке проблем не было, компиляция прошла успешно, однако при работе с гугл таблицей возникли проблемы, значения не отправляются. Вот что пишет монитор порта:
Start Sending Sensor Data to Google Spreadsheet
Humidity: 51.90% Temperature: 26.00°C
Sent : Temp and Humid
POST or SEND Sensor data to Google Spreadsheet:
Ошибкаbody {background-color: #fff; margin: 0; padding: 0;}.errorMessage {font-family: Arial,sans-serif; font-size: 12pt; font-weight: bold; line-height: 150%; padding-top: 25px;}TypeError: Cannot read properties of null (reading 'getLastRow') (строка 41, файл Код)
Как исправить ошибку?
А у вас url2 правильно сформирован? Попробуйте его вывести в монитор последовательного порта
Cпасибо за ответ! Я пересоздал таблицу и подставил новые значения, в этот раз всё заработало.
Да не за что. Я рад что у вас получилось
Подскажите пожалуйста, как правильно установить библиотеки, которые нужны для этой программы, а самое главное где их найти
Их можно установить с помощью менеджера библиотек (Library Manager) Arduino IDE, пример есть в этой статье. Если через менеджер библиотек не удается установить какую либо библиотеку, то можно через поиск яндекса найти ее на github, скачать оттуда ее архив и установить ее вручную в папку с библиотеками Ардуино.
Спасибо большое за ответ, может вы еще сможете помочь с еще одной проблемой ? Вроде все сделал как в статье, все библиотеки подключил, да и вроде все они работают, все перепроверил, даже в чат гпд загружал - обычно находил ошибки, выдает следующую ошибку: "exit status 1
Compilation error: '' does not name a type", и выделяет красным эти строчки: WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Попробуйте ssid и password ввести не как в приведенной статье сделано (через указатели), а через директиву define как в этой статье.
Всё очень здорово работает в первый раз. Однако при попытке внести изменения в код скрипта, они никак не отражаются на работе web app. Google утверждает, что достаточно просто сохранить изменения в редакторе скриптов и они должны сразу отобразиться в head deployment. Однако этого не происходит. Наверняка вам приходилось редактировать код скрипта. Как вы обновляли его?
Нет, честно говоря я не делал этого. А после изменения скрипта вы выполняли ‘Publish’ > ‘Deploy as Web App…’ и давали гуглу необходимые разрешения, указанные в статье? Во время этих операций вы были залогинены в своем аккаунте на Google или нет? Просто именно в отношении этого пункта гугл с каждым годом все более ужесточает правила и с момента написания статьи у него что-нибудь в его правилах могло измениться
Дело в том, что когда вы вносите изменения в скрипт приложения и публикуете его заново - у вас меняется Script ID. Вам нужно узнать новый Script ID (Начать развертывание -> управление развертываниями) и вставить его в скетч для микроконтроллера.
А какая проблема в том чтобы потом внести изменения в скетч для микроконтроллера? Может, конечно, я уже что то подзабыл потому что эта статья опубликована мною уже достаточно давно.
Схема неправильная!!!
Там нарисован светодиод с резистором!
Да, опечатка вышла. Но схема в данном проекте очень простая, просто подключите датчик DHT11 к контакту D4 модуля ESP8266