Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Датчик давления IoT:MKR GSM + Arduino Cloud + Google Sheets

Компоненты и расходные материалы

Arduino UNO
× 1
Arduino MKR GSM 1400
× 1
Датчик давления (0–150 фунтов на кв. дюйм)
× 1
Датчик температуры и влажности Adafruit DHT22
× 1
Модуль DS3231 RTC
× 1
Конвертер логического уровня Adafruit - BSS138 - 4-канальный I2C-безопасный Двунаправленный
× 1
LiPo аккумулятор Adafruit 3,7 В (2000 мАч или больше)
× 2
Повышающий преобразователь напряжения
× 1
Базовый стартовый комплект с различными светодиодами и резисторами
× 1
Макет (общий)
× 1

Приложения и онлайн-сервисы

Google Таблицы
Arduino IoT Cloud
Веб-редактор Arduino
IDE Arduino

Об этом проекте

Цель

Целью этого проекта было создание прототипа недорогого устройства для мониторинга давления на промышленное оборудование в удаленных местах с использованием сотовых данных.

Обзор проекта

Ниже приводится пошаговое руководство по проекту, отслеживающее поток данных от датчика давления до уведомления по электронной почте, сгенерированного скриптом, прикрепленным к таблице Google.

Шаг 1. Датчик давления на Uno

Датчик давления преобразует давление в аналоговый электрический сигнал.

Arduino Uno преобразует аналоговый сигнал датчика давления в давление (psi).

Шаг 2:переход к MKR GSM 1400 через последовательный порт

При обмене данными между двумя устройствами Arduino через последовательный порт:

  • Подключите RX на устройстве №1 к TX на устройстве №2
  • Подключите TX на устройстве №1 к RX на устройстве №2
  • У устройств должно быть общее заземление.

Частота передачи данных (от Uno до MKR GSM 1400)

  • НОРМАЛЬНО: Каждые 30 минут (частота передачи) Uno будет последовательно печатать данные в MKR GSM 1400, который будет передавать данные в облако.
  • ВЫСОКИЙ / НИЗКИЙ ТРИГГЕР: Если давление поднимается выше 40 фунтов на квадратный дюйм (highTrigger) или ниже 20 psi (lowTrigger) и остается там более 2 минут (dtLastTriggerLimit), Uno будет последовательно печатать данные на MKR GSM 1400, который будет передавать данные в облако.
  • ОПРОС СПРОСА: Если на выводе A1 Uno высокий уровень, он будет последовательно печатать данные в MKR GSM 1400, который будет передавать данные в облако. Примечание:контакт A1 в эскизе Uno назван «buttonPin». Есть 2 способа, которыми вывод A1 на Uno можно поднять высоко. (1) На макетной плате есть кнопка. (2) Если на контакте A3 на MKR GSM 1400 высокий уровень, он подтолкнет контакт A1 к высокому уровню. Поскольку вывод A3 управляется входом в Arduino Cloud, текущее давление можно получить удаленно в любое время, не дожидаясь регулярно запланированной передачи данных.

Примечания

  • Эскиз Uno можно было изменить так, чтобы в текущей версии можно было контролировать несколько входов, таких как температура, влажность, напряжение батареи и т. д., с высокими и низкими уставками в дополнение к давлению.
  • Код, используемый для преобразования аналогового сигнала с датчика давления в давление (psi), основан на инструкциях, приведенных в следующем видео на YouTube:https://www.youtube.com/watch?v=AB7zgnfkEi4
  • Следующий пост на форуме Arduino, посвященный «Основам последовательного ввода», был очень полезен, когда дело дошло до написания кода для связи от одного устройства к другому с использованием последовательных данных:https://forum.arduino.cc/index. php? topic =288234.0

Код, используемый для Arduino Uno в этом проекте, прилагается с комментариями, объясняющими ключевую информацию.

Имя файла:"InstrumentReader"

Шаг 3. MKR GSM 1400 в облако Arduino через сотовую связь

MKR GSM 1400 обрабатывает последовательные данные от Arduino Uno и передает их в Arduino Cloud, используя сотовые данные.

Стоит отметить, что в коде для MKR GSM 1400 вы увидите Serial1.read, а не Serial.read. Справочный материал на сайте Arduino дает хорошее объяснение. В таблице на изображении ниже показано, что доступ к контактам TX / RX на платах MKR осуществляется через Serial1.

https://www.arduino.cc/reference/en/language/functions/communication/serial/

Arduino Cloud

Этот проект настроен с двумя переменными в Arduino Cloud. На изображении ниже показано, как эти переменные отображаются на панели управления в Arduino Cloud.

Первая переменная с именем «dataStringCloud» фактически представляет собой пакет всех данных с устройства. Этот подход был использован вместо одной переменной для каждого значения, чтобы упростить обработку данных в Google Таблицах. С отдельными именами переменных было трудно отличить значение, оставшееся неизменным, от значения, которое не обновлялось. Данные из этого пакета анализируются в Google Таблицах.

Вторая переменная с именем «pinCloud» используется для управления MKR GSM 1400 из облака Arduino. В скетче есть функция переключения, которая управляет действием в зависимости от значения pinCloud. Когда pinCloud =1, на вывод A1 подается высокий уровень, в результате чего на плате загорается светодиод. Когда pinCloud =2, контакт A3 поднимается высоко, что приводит к отправке Arduino Uno текущих данных, как описано выше.

Код, используемый для Arduino MKR GSM 1400 в этом проекте, прилагается с комментариями, объясняющими ключевую информацию.

Имя файла:"CommunicationsDevice"

Шаг 4. Из Arduino Cloud в Google Таблицы через Webhook

Данные передаются из облака Arduino в Google Таблицы с помощью веб-перехватчика.

Ядром веб-перехватчика является функция doPost, написанная в скрипте файла Google Sheets.

Вот краткое описание того, как настроить веб-перехватчик. Обратите внимание, что процесс начинается в Google Таблицах. Вы не дойдете до Arduino Cloud до конца. Чтобы добраться из пункта А в пункт Б, начните с пункта Б.

  • Создайте новый файл Google Таблиц.
  • Нажмите "Инструменты" на панели инструментов и выберите "Редактор сценариев" в раскрывающемся списке.
  • Напишите код с помощью функции doPost (см. прилагаемый к этому проекту GoogleSheetsScript.js)
  • Нажмите "Опубликовать" на панели инструментов и выберите "Развернуть как веб-приложение ..." в раскрывающемся списке.
  • Появится диалоговое окно с 3 полями.
  • (1) Версия проекта: Всегда используйте раскрывающийся список, чтобы выбрать «Новый». После первого обновления по умолчанию будет использоваться номер текущей версии; если вы не используете раскрывающийся список для выбора «Создать», изменения не вступят в силу.
  • (3) У кого есть доступ к приложению: «Любой, даже анонимный»
  • Нажмите "развернуть" после проверки значений в трех полях.
  • Появится второе диалоговое окно с «Текущим URL-адресом веб-приложения». Это URL-адрес, который вы скопируете и вставите во вкладку веб-перехватчика в Arduino Cloud. Стоит отметить, что этот URL остается неизменным независимо от версии проекта.
  • Нажмите "ОК", готово!

Значительная часть кода JavaScript, используемого в этом проекте, смоделирована по образцу кода, используемого в другом проекте под названием «Arduino IoT Cloud Google Sheets Integration». Ссылка на проект ниже. Я бы порекомендовал это проверить.

https://create.arduino.cc/projecthub/Arduino_Genuino/arduino-iot-cloud-google-sheets-integration-71b6bc?ref=part&ref_id=64347&offset=9

Шаг 5. Используйте Google Таблицы для анализа данных

Используйте Google Таблицы для анализа отдельных значений из dataStringCloud и отображения уникальных записей, переданных из Arduino Cloud

Ссылка ниже - это файл Google Таблиц, который использовался для недавнего тестирования устройства. Ячейки в этом файле окрашены в зависимости от того, как они были заполнены, как указано в легенде на каждом листе.

https://docs.google.com/spreadsheets/d/1XwCir2Llw8RvGPGgZI3Yk6U5a3LeIfUACNuO1Gr_LFQ/edit?usp=sharing

Шаг 6. Используйте Google Таблицы для отправки уведомлений

Возможно, вы заметили, что в файле JavaScript для этого проекта (GoogleSheetsScript.js) есть две функции, упомянутые выше в шаге 4.

  • Функция doPost - передает данные из Arduino Cloud Webhook. Он запускается, когда в Arduino Cloud появляются новые данные.
  • Функция sendEmail - отправляет электронные письма на основе значений, извлеченных из таблицы с именем «Данные» в файле Google Таблиц для проекта. Он запускается один раз в минуту в зависимости от настроек в настройке триггера.

Шаги по настройке триггера для функции sendEmail

  • Откройте файл Google Таблиц.
  • Нажмите "Инструменты" на панели инструментов.
  • В раскрывающемся списке выберите «Редактор сценариев».
  • Из окна редактора скриптов перейдите к:
  • Нажмите "Изменить" на панели инструментов.
  • В раскрывающемся списке выберите «Триггеры текущего проекта».
  • В окне Центра разработчиков G Suite перейдите к:
  • Выберите «Добавить триггер» в правом нижнем углу окна.
  • В открывшемся диалоговом окне выберите запуск функции sendEmail.
  • Примечание. Запуск триггера на основе времени позволяет генерировать уведомления по электронной почте, когда устройство перестает обновляться.

Срок службы батареи

~ 24 часа

Это можно оптимизировать, отключив или сняв дисплеи. Другой вариант - удалить второстепенные датчики, такие как DHT22 и DS3231.

Использование данных

~ 0,7 мегабайт / день

Это можно оптимизировать, уменьшив размер или частоту передачи данных. Например:Чтобы уменьшить размер, отправляйте только давление, а не давление, температуру, влажность и время. Чтобы уменьшить частоту, обновляйте только ежечасно, а не каждые 30 минут.

Стоимость проекта

Итого =241 доллар США

  • Arduino MKR GSM 1400 (70 долларов США)
  • Arduino Uno (22 доллара США)
  • 2 литий-полимерных аккумулятора 3,7 В (30 долларов США).
  • 2 светодиодных дисплея (29 долларов США)
  • Устойчивый к погодным условиям пластиковый ящик (22 доллара США).
  • Датчик давления (19 долларов США)
  • Датчик температуры / влажности - DHT22 (10 долларов США)
  • Модуль RTC - DS3231 (5 долларов США)
  • Повышающий преобразователь напряжения (5 долларов США)
  • Преобразователь логического уровня (4 доллара США)
  • Разное - светодиоды, резисторы, проводка и т. д. (25 долларов США).

Оборудование / Инструменты

Все оборудование и инструменты, используемые для этого проекта, были приобретены у следующих компаний:

  • Интернет-магазин Arduino (https://store.arduino.cc/)
  • Adafruit (https://www.adafruit.com/)
  • Amazon (https://www.amazon.com/)
  • Портовый фрахт
  • Home Depot

В заключение ...

Спасибо, что нашли время изучить этот проект.

Любые вопросы / отзывы / комментарии / предложения приветствуются / приветствуются.

Код

  • InstrumentReader - Скетч для Arduino Uno
  • GoogleSheetsScript.js
InstrumentReader - Эскиз для Arduino Uno Arduino
 // Эскиз 1 из 2 // Arduino Uno // Данные собираются этим устройством и передаются в MKR 1400 по последовательному каналу // Датчик DHT22 Требуется 2 библиотеки, но в коде вызывается только одна. // (1):Библиотека датчиков DHT:https://github.com/adafruit/DHT-sensor-library // (2):Библиотека унифицированных датчиков Adafruit:https://github.com/adafruit/Adafruit_Sensor #include  #include  #include  // Модуль RTC # include  // DHT22 # ​​include "U8glib.h" // Дисплей Velleman 128 x 64 OLED SPI // Примечание. Другая стандартная библиотека U8glib не работала, когда я пытался использовать ее для этого дисплея. Это сработало, когда я использовал библиотеку, рекомендованную производителем. // Библиотека:https://www.velleman.eu/support/downloads/?code=VMA437 // Синтаксис:https://github.com/olikraus/u8glib/wiki/userreference &https://github.com/ olikraus / u8glib / wiki / thelloworld // # include  // Возможность сохранения на SD-карту в Ethernet Shield для Arduino UnoRTClib RTC; #define DHTPIN 11 // Цифровой вывод, подключенный к датчику DHT # define DHTTYPE DHT22 / / DHT 22 (AM2302), AM2321DHT dht (DHTPIN, DHTTYPE); U8GLIB_SH1106_128X64 u8g (3, 4, 6, 7); // (CLK / SCK:3, MOSI:4, CS:6, DC (A0):7) // u8g (sck, mosi, cs, a0 [, сброс]) int y_pos =0; // глобальная переменная // const int chipSelect =10; // Возможность сохранения на SD-карту в Ethernet Shield для Arduino Uno // float fileSizeSD =0.0; // Возможность сохранения на SD-карту в Ethernet Shield для Arduino Unoint i =0; // Подсчет # считываний, сделанных Uno (как # циклов в программе) const int ledPin =9; // индикатор передачи (мигает при передаче данных) const int ledPin2 =8; // нажимаем индикатор передачи (нажимается вручную кнопкой на макете или вывод из MKR 1400 активируется из облака) const int buttonPin =A1; int buttonState =0; int transferFrequency =30; // Частота последовательной печати dataString для отправки данных на второе устройство (минуты) String pTransmitDateTime =""; int transferCounter =0; int pTransmitMinute =0; int ptriggerTransmitAlertIndicator; float cRuntimeAtTriggerStart =0.0; float dtLastTrigger =0.0; float dtLastTrigger =0.0;; int triggerTransmitAlertCounter =0; // Входные переменные для управления Triggerfloat lowTrigger =20.0; float highTrigger =40.0; float dtLastTriggerLimit =2.0; // Если условие выполняется в течение этого промежутка времени, будет сгенерировано предупреждениеvoid setup (void) {Serial.begin (9600); Wire.begin (); dht.begin (); pinMode (ledPin, ВЫХОД); pinMode (ledPin2, ВЫХОД); pinMode (buttonPin, ВВОД); u8g.setRot180 (); // при необходимости перевернуть экран (добавить / удалить комментарии к этой строке для поворота) // Возможность сохранения на SD-карту в Ethernet Shield для Arduino Uno // Serial.print ("Инициализация SD-карты ..."); // if (! SD.begin (chipSelect)) // посмотрим, присутствует ли карта и может ли быть инициализирована // {// Serial.println ("Карта вышла из строя или отсутствует"); // while (1); // больше ничего не делать //} // Serial.println ("карта инициализирована.");} void loop (void) {delay (5000); DateTime now =RTC.now (); float cRuntime =миллис () / 60000; float p =getPressure (); // Serial.println (p); buttonState =digitalRead (buttonPin); // Serial.print ("Кнопка:"); // Serial.println (buttonState); если (buttonState ==1) {digitalWrite (ledPin2, HIGH); задержка (30000); // задержка, позволяющая MKR1400 подготовиться к приему данных, если MKR1400:pingPin нажимает на Uno:buttonPin ВЫСОКОЕ значение} else {digitalWrite (ledPin2, LOW); } float h =dht.readHumidity (); float t =dht.readTemperature (true); // t =dht.readTemperature (true) -> temp, если градусы F &t =dht.readTemperature () -> temp, если градусы C int TransmitIndicator =0; если (now.minute ()% transferFrequency ==0 &&now.minute ()! =pTransmitMinute) {TransmitIndicator =1; pTransmitMinute =now.minute (); pTransmitDateTime =Строка (now.hour ()) + Строка (":") + Строка (now.minute ()) + Строка (":") + Строка (now.second ()); } int triggerStatus =0; if (p <=lowTrigger || p>
 =highTrigger) {// Примечание:переменная, указанная в условии для этого оператора if, оценивается по высоким и низким уставкам // Быстро изменить, какая переменная оценивается - это единственное место, где указана переменная triggerStatus =1; triggerCounter ++; } еще {triggerCounter =0; } если (triggerStatus ==1 &&triggerCounter ==1) {cRuntimeAtTriggerStart =cRuntime; } dtLastTrigger =cRuntime - cRuntimeAtTriggerStart; int triggerTransmitAlertIndicator =0; если ((dtLastTrigger> dtLastTriggerLimit) &&triggerStatus ==1) {triggerTransmitAlertIndicator =1; triggerTransmitAlertCounter ++; } еще {triggerTransmitAlertCounter =0; } если (triggerTransmitAlertCounter> 0 &&triggerTransmitAlertCounter% 10 ==0) {flashLED (2,500); } int triggerPushTransmitAlertIndicator =0; if ((triggerTransmitAlertIndicator ==1 &&triggerTransmitAlertCounter ==1) || ptriggerTransmitAlertIndicator! =triggerTransmitAlertIndicator) // if (TriggerStatus существовал в течение мин. указанного времени для Alert &Count =1, значит, это время превышает первый цикл, в котором time // ИЛИ изменение статуса triggerAlert - это сгенерирует push Alert, если TriggerStatus вернется к 0, что означает, что условия триггера больше не выполняются.) {triggerPushTransmitAlertIndicator =1; flashLED (5 500); задержка (5000); } ptriggerTransmitAlertIndicator =triggerTransmitAlertIndicator; // текущий индикатор сохраняется в предыдущем индикаторе. В следующем цикле переданное здесь значение будет сравниваться со значением, созданным на основе новых значений. // Создание строк String dataString =""; Строка cDateTime =""; Строка cHumTemp =""; Строка cP =""; dataString + ="<" + String (i) + "," + String (triggerTransmitAlertIndicator) + "," + String (dtLastTrigger, 0) + "," + String (buttonState) + "," + String (now.month) ()) + "," + String (now.day ()) + "," + String (now.year ()) + "," + String (now.hour ()) + "," + String (now .minute ()) + "," + String (now.second ()) + "," + String (h) + "," + String (t) + "," + строка (p) + ">"; cDateTime + =String (now.month ()) + "/" + String (now.day ()) + "/" + String (now.year ()) + "" + String (now.hour ()) + ":" + String (now.minute ()) + ":" + String (now.second ()); cHumTemp + =«H:» + String (h) + «% T:» + String (t) + «degF»; cP + ="P:" + String (p) + "psi"; если (TransmitIndicator ==1 || triggerPushTransmitAlertIndicator ==1 || buttonState ==1) {char dataArray [100]; dataString.toCharArray (dataArray, 100); Serial.println (dataArray); flashLED (10 500); transferCounter ++; } // Serial.print ("T:"); // Serial.println (triggerStatus); задержка (100); // немного подождем прибытия всего сообщения // цикл изображения u8g.firstPage (); do {draw (cDateTime, cHumTemp, cP, i, transferCounter, now.minute (), transferFrequency, pTransmitMinute); } while (u8g.nextPage ()); задержка (1000); // writeToSD (dataString); // Возможность сохранения на SD-карту в Ethernet Shield для Arduino Uno i ++;} void draw (String DcDateTime, String DcHumTemp, String DcP, int Di, int DtransmitCounter, int DnowMinute, int DtransmitFrequency, int DpTransmitMinute) {u8g.begin (); u8g.setFont (u8g_font_5x7); // u8g_font_micro // u8g_font_5x7 // u8g_font_5x8 // u8g_font_6x10 u8g.setFontPosTop (); u8g.setPrintPos (0,0); u8g.print (DcDateTime); u8g.setPrintPos (0,8); u8g.print (2); u8g.setPrintPos (10,8); u8g.print (DcHumTemp); u8g.setPrintPos (0,16); u8g.print ("3 #:"); u8g.setPrintPos (30,16); u8g.print (Di); u8g.setPrintPos (50,16); u8g.print (DcP); u8g.setPrintPos (0,24); u8g.print ("4 #t:"); u8g.setPrintPos (30,24); u8g.print (DtransmitCounter); u8g.setPrintPos (50,24); u8g.print ("tFreq:"); u8g.setPrintPos (83,24); u8g.print (DtransmitFrequency); u8g.setPrintPos (0,32); u8g.print (5); u8g.setPrintPos (10,32); u8g.print ("nowMinute:"); u8g.setPrintPos (70,32); u8g.print (DnowMinute); u8g.setPrintPos (0,40); u8g.print (6); u8g.setPrintPos (10,40); u8g.print ("pTransmitMinute:"); u8g.setPrintPos (95,40); u8g.print (DpTransmitMinute); u8g.setPrintPos (0,48); u8g.print (7); u8g.setPrintPos (10,48); u8g.print ("Остаток:"); u8g.setPrintPos (70,48); u8g.print (DnowMinute% DtransmitFrequency);} float getPressure () {int sensorVal =analogRead (A2); // Serial.print ("Значение датчика:"); // Serial.print (sensorVal); float Voltage =(sensorVal * 5.0) /1023.0; // Serial.print ("Volts:"); // Serial.print (напряжение); // Когда давление =0, аналоговый вход =100 // Преобразование аналогового входа в напряжение:аналоговый вход =100 -> Напряжение =100 * (5/1023) =0,4889 с плавающей точкой m =((150-0) / (4.5- 0,4889)); float b =150 - (m * 4.5); // Serial.print ("m ="); // Serial.print (m); // Serial.print ("b ="); // Serial.print ( б); float pressure_psi =((m * Voltage) + b); // Serial.print ("Pressure ="); // Serial.print (pressure_psi); // Serial.println ("psi"); // задержка (200); return pressure_psi;} void flashLED (int num, int t) {для (int z =1; z <=num; z ++) {digitalWrite (ledPin, HIGH); задержка (t); digitalWrite (ledPin, LOW); задержка (t); }} // Возможность сохранения на SD-карту в Ethernet Shield для Arduino Uno // void writeToSD (String dataToWrite) // {// // открыть файл. обратите внимание, что одновременно может быть открыт только один файл, // // поэтому вы должны закрыть этот, прежде чем открывать другой. // Файл dataFile =SD.open ("datalog4.txt", FILE_WRITE); // fileSizeSD =dataFile.size (); // Возвращает размер файла в байтах // fileSizeSD =fileSizeSD / 1000000; // Преобразует байты в МБ. 1 МБ =1e6 байтов // // если файл доступен, записываем в него:// if (dataFile) // {// dataFile.println (dataToWrite); // dataFile.close (); // // печать и на последовательный порт:// // Serial.println (dataToWrite); //} // // если файл не открыт, выдается сообщение об ошибке:// else // {// Serial.println ("ошибка открытия файла datalog1.txt"); //} //} 
GoogleSheetsScript.js JavaScript
 // В этом коде 2 функции // функция doPost - передает данные из Arduino Cloud Webhook. Он запускается, когда в Arduino Cloud появляются новые данные .// Функция sendEmail - отправляет электронные письма на основе значений, извлеченных из таблицы с именем «Data» в файле Google Sheets для проекта. Он запускается один раз в минуту в зависимости от настроек в настройке триггера. Для получения инструкций обратитесь к разделу функции sendEmail сообщения в хабе проекта. // Большая часть этого кода (кроме функции sendEmail) смоделирована по образцу следующего проекта в Arduino Project Hub // https://create.arduino. cc / projecthub / Arduino_Genuino / arduino-iot-cloud-google-sheet-integration-71b6bc? f =1 // Это ссылка на репозиторий GitHub со скриптом Google, используемым для вышеупомянутого проекта в Project Hub. // Эта ссылка была скопирована из описания проекта в Project Hub. // https://github.com/arduino/arduino-iot-google-sheet-script/blob/master/Code.gs// активировать spreasheetvar ss =SpreadsheetApp.getActiveSpreadsheet (); // получаем лист с именем RawDatavar sheet =ss.getSheetByName ("RawData"); var sd =ss.getSheetByName ("Data"); var sref =ss.getSheetByName ("References"); var MAX_ROWS =1440; // максимальное количество строк данных для отображения // 3600s / cloud_int (30s) * num_ore (12h) =(60 * 60 * 12) / 30 =(3600 * 12) / 30 =1440 показаний за 12 часов при 30-секундном обновлении интервал // (60 * 24) / 15 =96 показаний за 24-часовой период с 15-минутным интервалом обновления // 15 дней * 96 показаний / день =1440 показаний // 90 дней * 96 показаний / день =8640 показаний // 365 дней * 96 показаний / день =35040 показаний var HEADER_ROW =1; // индекс строки заголовка var TIMESTAMP_COL =1; // индекс столбца метки времени columnfunction doPost (e) {var cloudData =JSON.parse (e.postData.contents); // это json-объект, содержащий всю информацию, поступающую из IoT Cloud console.log (cloudData); // var webhook_id =cloudData.webhook_id; // на самом деле не использую эти три // var device_id =cloudData.device_id; // var thing_id =cloudData.thing_id; var values ​​=cloudData.values; // это массив объектов json console.log (values); // Сохранение имен и значений из массива значений // Каждое входящее свойство имеет:// имя, которое станет именами столбцов // значение, которое будет записано в строки под заголовком столбца var incLength =values.length; var incNames =[]; var incValues ​​=[]; for (var i =0; i  2018) {// отбрасываем все поступающие сообщения 'late' if (sheet.getRange (HEADER_ROW + 1, 1) .getValue ()! ='') {// HEADER_ROW + 1 =Row # 2 &Column # 1 -> это расположение самой последней временной метки в листе // Если самая последняя временная метка не пустая =(''), тогда сравните текущее время с временной меткой входящих данных // Если самая последняя временная метка пуста, скорее всего, это первый раз, когда скрипт запущен. // В этом случае пропустите этот оператор if и продолжите запись в заголовки столбцов и данные. // Неважно, опаздывают ли данные (текущее время или временная метка входящих данных). var now =новая дата (); // теперь var COMM_TIME =120; // Примечание:изменено на 120, чтобы пропускать больше сообщений, ранее было установлено 5 секунд и работало нормально // грубое завышение времени связи между облаком и приложением if (now.getTime () - date.getTime ()> COMM_TIME * 1000) {// Если разница между текущим временем и отметкой времени больше 5 секунд, данные отбрасываются. // Когда условие в этом операторе If оценивается как истинное, функция останавливается из-за оператора return. возвращение; // «Оператор return останавливает выполнение функции и возвращает значение из этой функции». }} // Этот раздел записывает значения в строку заголовка на основе имен входящих свойств // Другими словами, этот раздел создает имена столбцов // Присваивает имя ячейке, которая находится в строке заголовка и в первой столбец =отметка времени sheet.getRange (HEADER_ROW, 1) .setValue ('отметка времени'); для (var я =0; я  это 2 // столбец 1 - столбец с меткой времени. if (lastCol ==1) {// записываем имена столбцов, начиная с столбца после lastCol ==1, который является столбцом с отметкой времени // incNames - это массив, содержащий имена всех входящих свойств // If lastCol ==1 , write the value from 'i'th location in the incNames array to the header row in column #2 =lastCol + 1 sheet.getRange(HEADER_ROW, lastCol + 1).setValue(incNames[i]); } else { // evaluated if the lastCol !=1 // check if the name is already in header var found =0; for (var col =2; col <=lastCol; col++) { // starting with column 2, iterate through all of the columns up to the lastCol evaluating the if statement enclosed if (sheet.getRange(HEADER_ROW, col).getValue() ==incNames[i]) { // the condition of this If statement compares the value in the header row &column # =col to the 'i'th value in the array of incoming property names // This if statement is evaulated for each iteration of the for loop that it is enclosed in. // The condition is evaluated for all of the columns from column #2 to the last column. // It is checking to see if the 'i'th value in the incNames array exists in any of the columns in the header row. // If the 'i'th value in the incNames array finds a match to any of the values in the header row, set found =1 &exit the for loop with the break statment. found =1; break; // "The break statement breaks the loop and continues executing the code after the loop" } // close if statement evaluated for each iteration of the for loop that it is enclosed in. } // close for loop to check the 'i'th value in the incNames array to the values in the header row. if (found ==0) { // This If statemnt will be evaluated after the preceeding for loop has completed. // If found ==0 it means that the 'i'th value in the incNames array did not match any of the existing values in the header row. // If found ==0, write the 'i'th value in the incNames array to the column after the last column. // If new properties are added to the incoming data over time, the existing columns will not be impacted. The new property will be added to the column after the last column. sheet.getRange(HEADER_ROW, lastCol+1).setValue(incNames[i]); } // close if statement } // close else, since this is the end of the code block inside the main for loop, the next i will be evaluated up to i =incLength // The block of code inside this for loop is evaluated for each value at location i in the incNames array. // The values of i range from 0 to incLength (the number of values in the names array) // In JavaScript the index in arrays starts at 0. In other words, the 1st value in the array is at location =0. } // close main for loop used to write column names (assigning values from names array to header row) // redefine last coloumn and last row since new names could have been added var lastCol =sheet.getLastColumn(); var lastRow =sheet.getLastRow(); // delete last row to maintain constant the total number of rows if (lastRow> MAX_ROWS + HEADER_ROW - 1) { sheet.deleteRow(lastRow); } // insert new row after deleting the last one sheet.insertRowAfter(HEADER_ROW); // reset style of the new row, otherwise it will inherit the style of the header row var range =sheet.getRange('A2:Z2'); //range.setBackground('#ffffff'); range.setFontColor('#000000'); range.setFontSize(10); range.setFontWeight('normal'); // write the timestamp sheet.getRange(HEADER_ROW+1, TIMESTAMP_COL).setValue(date).setNumberFormat("yyyy-MM-dd HH:mm:ss"); // write values in the respective columns for (var col =1+TIMESTAMP_COL; col <=lastCol; col++) { // for loop to assign the value from incValues to the approrpriate column // This block of code was replaced by an if statement checking for blank values after the incoming data is populated. // Copy previous values // This is to avoid empty cells if not all properties are updated at the same time // sheet.getRange(HEADER_ROW+1, col).setValue(sheet.getRange(HEADER_ROW+2, col).getValue()); for (var i =0; i  2018), used to eliminate dupicate messages from the Arduino Cloud} // close doPost functionfunction sendEmail (){ var emailAddress =sd.getRange("V3").getValue(); var lastPressure =sd.getRange("K3").getValue(); // pressure at last update var lastUpdate =sd.getRange("A3").getValue(); // datetime of last update var ssLink ="https://docs.google.com/spreadsheets/d/1XwCir2Llw8RvGPGgZI3Yk6U5a3LeIfUACNuO1Gr_LFQ/edit#gid=1123486497"; var triggerAlertCount =sd.getRange("L6").getValue(); var triggerMonitor =sd.getRange("M3").getValue(); var dtLastStatusChange =sd.getRange("P3").getValue(); var dtLastDeviceUpdate1 =sd.getRange("S3").getValue(); var dtLastDeviceUpdate2 =sd.getRange("S4").getValue(); var emailSentNoUpdate =sd.getRange("T3").getValue(); var emailSentStartedReading =sd.getRange("U3").getValue(); var message ="Last Device Update:" + "\n" + lastUpdate + "\n\n" + " Last Pressure:" + "\n\t" + lastPressure.toFixed(2) + " psi" + "\n\n" + " Link to Spreadsheet:" + "\n\t" + ssLink; if(triggerMonitor ==0){ sd.getRange("L5").setValue(0); sd.getRange("L6").setValue(0); } if(triggerMonitor ==-1 &&triggerAlertCount <=4){ sd.getRange("L3").setValue(lastUpdate); // emailSent sd.getRange("L5").setValue(-1); sd.getRange("L6").setValue(triggerAlertCount + 1); var subject ="Status Change Alert - Outside Setpoints"; MailApp.sendEmail(emailAddress, subject, message); } if(triggerMonitor ==1 &&triggerAlertCount <=4){ sd.getRange("L3").setValue(lastUpdate); // emailSent sd.getRange("L5").setValue(1); sd.getRange("L6").setValue(triggerAlertCount + 1); var subject ="Status Change Alert - Normal"; MailApp.sendEmail(emailAddress, subject, message); } if(emailSentNoUpdate ==0 &&dtLastDeviceUpdate1> 60 &&dtLastDeviceUpdate2> 60){ sd.getRange("T3").setValue(1); // emailSentNoUpdate sd.getRange("U3").setValue(0); // emailSentStartedReading sd.getRange("T4").setValue(now.getTime()); // emailSentNoUpdate var subject ="Alert - Over 60 minutes Since Last Device Update"; MailApp.sendEmail(emailAddress, subject, message); } if(emailSentNoUpdate ==1 &&dtLastDeviceUpdate1 <60){ // removed the following from the condition:&&dtLastDeviceUpdate2> 60 sd.getRange("T3").setValue(0); // emailSentNoUpdate sd.getRange("U3").setValue(1); // emailSentStartedReading sd.getRange("U4").setValue(now.getTime()); // emailSentStartedReading var subject ="Alert - Device Started Updating"; MailApp.sendEmail(emailAddress, subject, message); }} 
CommunicationsDevice - Sketch for MKR 1400

Схема


Производственный процесс

  1. ADLINK в партнерстве с Google Cloud предлагает готовые решения для Интернета вещей
  2. Базовый IoT - RaspberryPI HDC2010 как
  3. Цифровой датчик барометрического давления BMP180 I2C
  4. Регистратор датчиков Python / MicroPython с Google Таблицами
  5. Windows 10 IoT Core на Raspberry Pi 2 - данные датчика Adafruit
  6. Windows 10 IoT Core и SHT15
  7. UnifiedWater v1
  8. IOT - Smart Jar с использованием ESP8266, Arduino и ультразвукового датчика
  9. Башня датчика облаков Arduino
  10. Arduino Apple Watch