Мини-кейс 6: Arduino IoT Cloud

Сообщество Arduino запустило свою IoT-платформу под названием Arduino IoT Cloud. Arduino IoT Cloud предоставляет комплексное решение, которое упрощает создание подключенных проектов для разработчиков, энтузиастов Интернета вещей и профессионалов от начала до конца. Платформа поддерживает различные методы взаимодействия, включая HTTP REST API, MQTT, инструменты командной строки, Javascript и WebSockets. Вы можете подключить несколько устройств друг к другу и разрешить им обмениваться данными в режиме реального времени. Вы также можете отслеживать данные из любого места, используя простой пользовательский интерфейс. Чтобы узнать больше об облаке Arduino IoT, вы можете просмотреть часть документации.

В этом руководстве мы будем управлять светодиодом с помощью облачной панели Arduino IoT. Аналогичным образом мы отправим данные датчиков на облачную панель управления и визуализируем их в различных красивых виджетах.

  1. Настройка Arduino IoT Cloud DashBoard

    1. Перейдите на сайт cloud.arduino.cc и зарегистрируйтесь.

    2. Перейдите на страницу главную страницу cloud cloud.arduino.cc/home.

    3. Нажмите на кнопку IoT Cloud.

    4. Откроется следующая страница и нажмите на не на кнопку CREATE THING.

  2. Создание вещи

    Здесь вы можете увидеть раздел настройки, а также раздел эскиза и последовательного монитора.

    1. Теперь вам нужно добавить переменную и дать имя. Например, для управления светодиодом я дам ему имя LED. В типе переменной выберите Light, так как нам нужно отправлять команды 1 и 0. Переменная будет автоматически объявлена ​​как CloudLight LED.

    2. Вы можете назначить разрешение переменной как чтение-запись, так и только чтение. Таким образом, для светодиода вы можете назначить чтение и запись. Наконец, нажмите «ADD VARIABLE». Это позволит успешно создать переменную.

    3. Аналогичным образом добавьте еще переменные для датчика широкого спектра газов Амперки MQ-2.

      1. Для сжиженных углеводородных газов, метана, дыма, водорода с типом целых чисел (Integer Number).

      2. Variable Permission установите «Read Only», чтобы значения нельзя было изменять в облаке.

  3. Настройка устройства

    1. Теперь нам нужно настроить устройство. Для этого выберите «Select Device», а после в окне «SET UP NEW DEVICE». Поскольку ESP8266 является устройством стороннего производителя, мы выберем «Third party device». Из списка выберите Плата ESP8266. И из модели выберите тип «Generic ESP8266 Module». Затем нажмите «CONTINUE».

    2. Назовите устройство как угодно.

    3. Идентификатор устройства и секретный ключ созданы. Это очень важные параметры при программировании. Вам нужно скопировать его или просто нажать «download the PDF».

    4. Вернитесь на панель управления и нажмите на галочку «I saved my device ID and Secret Key» и на кнопку «CONTINUE». Итак, вы наконец завершили настройку устройства.

    5. Настройка сети Теперь нам также необходимо настроить Сеть. Итак, нажмите кнопку «Configure» в блоке «Network». Введите здесь SSID Wi-Fi, пароль и секретный ключ, которые вы скопировали/скачали ранее. Затем нажмите «SAVE».

    6. Настройка панели мониторинга

      1. Перейдите в панель управления (Dashboards), чтобы настроить виджеты. Затем нажмите «BUILD DASHBOARD».

      2. Откроется новая панель управления, которой следует написать имя. Сверху слева есть тумблер, с помощью которого идёт переключение режима работы панели. Переключается между режимом просмотра и редактирования.

      3. Нажмите на «ADD». В этом списке виджетов выберите «Switch», поскольку мы хотим включать и выключать светодиод.

      4. Дайте имя «Led» переключателю.

      5. Затем нажмите на кнопку «Link Variable» и свяжите переменную с светодиодом (LED) из списка.

      6. Затем нажмите «LINK VARIABLE».

      7. Аналогичным образом добавьте ещё оставшиеся виджеты.

        1. Для MQ2_LPG установите тип виджета Value, дайте в окне создания виджета имя виджета LPG.

        2. Для MQ2_Methane тип Gauge и соотвествующее имя виджета, а также Value range (диапазон возможных значений).

        3. Для MQ2_Smoke выбрать тип Chart и соотвествующее имя виджета.

        4. Для MQ2_Hydrogen выберите любой из тех, который были выше и установите соответствующее имя виджета.

      Итак, наша панель управления готова. Вы также можете нажать на кнопку в виде стрелок (Arrenge widgets) и изменить размер виджета или переместить, а если на к кнопку с смартфоном (Show Mobile Layout), то посмотреть и раставить виджеты в виде для мобильного приложения.

  4. Исходный код/программа для модуля WIFI с ESP8266

    1. Зайдите с главной страницы Arduino Cloud в веб-редактор. Слева, выберите пункт Sketchbook. И откроется список проектов, которые хранятся в веб. Исходный код для использования облака Arduino IoT с ESP8266 прост в использовании, поскольку некоторые команды и объявления генерируются автоматически.

    2. Перейдите на вкладку эскиза. Таким образом, будет создан эскиз по умолчанию, основанный на различных переменных. Определены некоторые заголовочные файлы и установлены параметры для светодиода. Поэтому нам нужно изменить код.

    3. Будут отображаться несколько вкладок. Один из них файл скетч формата ino, thingProperties.h, ReadMe.adoc, Secret.

      Вкладке Secret (файл arduino_secrets.h) отображаются SECRET_SSID, SECRET_OPTIONAL_PASS, SECRET_DEVICE_KEY, т.е. имя wifi сети, пароль и ключ девайса.

    4. Установите для браузера Arduino Create Agent, чтобы веб-редактор мог подключится с устройством для прошивки. На следующей странице нажмите, чтобы скачать.

  5. Исходный код/программа для WiFi модуля Амперки

    1. Слева в вкладке «Libraries» в поиске найдите библиотеку «GParser».

    2. Вставьте код в ino файл...

      #include <GParser.h> // Библиотека парсинга данных
      #include <parseUtils.h>
      #include <unicode.h>
      #include <url.h>
      
      #include "thingProperties.h"
      
      void setup() {
        Serial.begin(115200); // Инициализация Serial для общения через последовательный порт
        Serial.setTimeout(5);
        delay(1500); // This delay gives the chance to wait for a Serial Monitor without blocking if none is found
        initProperties(); // Defined in thingProperties.h
        ArduinoCloud.begin(ArduinoIoTPreferredConnection); // Connect to Arduino IoT Cloud
        /*
           The following function allows you to obtain more information
           related to the state of network and IoT Cloud connection and errors
           the higher number the more granular information you’ll get.
           The default is 0 (only errors).
           Maximum is 4
       */
        setDebugMessageLevel(0);
        ArduinoCloud.printDebugInfo();
      }
      
      void loop() {
        ArduinoCloud.update();
        ParseFromSerialInputValues(false); // Парсим и устанавливаем значения
      }
      
      // Парсинг значений из Serial
      void ParseFromSerialInputValues(bool debug) {
        if (Serial.available() > 2) { // Если что-то прислали
          char inputStr[64]; // Массив символов для записи из Serial
          int amount = Serial.readBytesUntil(';', inputStr, 64); // Считать посимвольно до символа конца пакета точки с запятой и записать количество полученных байт в переменную
          inputStr[amount] = NULL; // Если отправляющее устройство не отправит нулевой символ, то он не запишется в буффер и вывод строк будет некорректным, решение дописать вручную и т.о. закрываем строку
          GParser data(inputStr, ','); // Парсим массив символов по символу запятой
          int am = data.split(); // Получаем количество данных, внимание, ломает строку!
          for (int i = 0; i < am; i++) {
            String tmpStr = data[i];
            tmpStr.replace(" ", ""); // Удалить пробел, если он был введёт по ошибке
            tmpStr.trim(); // Удаление ведущими и конечные пробелы
            char tmpCharArr[tmpStr.length()];
            tmpStr.toCharArray(tmpCharArr, tmpStr.length() + 1);
            if (debug) Serial.println(String(i) + ") " + tmpStr); // Вывести начальную строку
            GParser data2(tmpCharArr, ':'); // Парсим массив символов по символу запятой
            int am2 = data2.split(); // Получаем количество данных, внимание, ломает строку!
            if (am2 > 1) { // Если существует не только ключ, а ещё и значение
              String key = data2[0]; // Ключ - первое значение
              int value = data2.getInt(1); // Значение - второе, или data.getInt(1), чтобы получить целое число
              if (debug) Serial.println("key: " + key + ", value: " + String(value)); // Вывод
              // Присваивание значений
              if (key.equals("methane")) {
                mQ2_Methane = value;
              } else if (key.equals("lpg")) {
                mQ2_LPG = value;
              } else if (key.equals("smoke")) {
                mQ2_Smoke = value;
              } else if (key.equals("hydrogen")) {
                mQ2_Hydrogen = value;
              } else if (key.equals("led")) {
                led = value;
              }
            }
          }
          if (debug) Serial.println(); // Перевод на новую строку для разделения значений, которые были введены
        }
      }
      
      /*
        Since LED is READ_WRITE variable, onLEDChange() is
        executed every time a new value is received from IoT Cloud.
      */
      void onLEDChange()  {
        // Add your code here to act upon LED change
        Serial.println("led:" + String(led) + ";");
      }
      
      /*
        Since MQ2LPG is READ_WRITE variable, onMQ2LPGChange() is
        executed every time a new value is received from IoT Cloud.
      */
      void onMQ2LPGChange()  {
        // Add your code here to act upon MQ2LPG change
        
      }
      
      /*
        Since MQ2Methane is READ_WRITE variable, onMQ2MethaneChange() is
        executed every time a new value is received from IoT Cloud.
      */
      void onMQ2MethaneChange()  {
        // Add your code here to act upon MQ2Methane change
        
      }
      
      /*
        Since MQ2Smoke is READ_WRITE variable, onMQ2SmokeChange() is
        executed every time a new value is received from IoT Cloud.
      */
      void onMQ2SmokeChange()  {
        // Add your code here to act upon MQ2Smoke change
        
      }
      
      /*
        Since MQ2Hydrogen is READ_WRITE variable, onMQ2HydrogenChange() is
        executed every time a new value is received from IoT Cloud.
      */
      void onMQ2HydrogenChange()  {
        // Add your code here to act upon MQ2Hydrogen change
        
      }
    3. Над кодом есть раскрывающийся список, в которой нужно выбрать «Select Other Board & Port», чтобы выбрать правильную плату для компиляции и загрузки кода. При этом должена одображаться плата Arduino, через которую мы будем загружать.

    4. В открывшемся окне нужно выбрать плату, воспользовавшийсь поиском и порт, чтобы в этих двух полях стояли галочки, а после нажимаем на кнопку «OK».

    5. Переводим плату в режим загрузки прошивки с помощью комбинации.

    6. Нажимаем на кнопку «Upload and Save» и наблюдаем процесс прошивки в окне под кодом.

    7. На последовательном мониторе (вкладка «Monitor») вы увидите, что соединение MQTT установлено и устройство подключено к сети WiFi. Он также отображает значения.

  6. Исходный код/программа для Arduino

    #include <SoftwareSerial.h>
    #include <GParser.h> // Библиотека парсинга
    #include <TroykaMQ.h> // Библиотека для работы с датчиками MQ (Troyka-модуль)
    #include <EncButton.h> // Библиотека для работы с кнопками
    #include <TimerMs.h> // Библиоткера работа с таймерами
    
    #define BTN_PIN 11 // Пин, к которому подключена кнопка
    #define BTN_LED_PIN A3 // Пин, к которому подключен светодиод кнопки
    #define PIN_MQ2 A1 // Пина, к которому подключен датчик
    #define PIN_MQ2_HEATER 12 // Пина, к которому подключен нагреватель датчика
    
    #define EB_DEB_TIME 30 // Таймаут гашения дребезга кнопки (кнопка)
    #define EB_CLICK_TIME 200 // Таймаут ожидания кликов (кнопка)
    #define EB_HOLD_TIME 300 // Таймаут удержания (кнопка)
    #define EB_STEP_TIME 200 // Таймаут импульсного удержания (кнопка)
    
    #define DEBUG false // Дебаг для печати
    
    SoftwareSerial WiFi_Serial(4, 5); // TX и RX WI-FI модуля
    
    MQ2 mq2(PIN_MQ2, PIN_MQ2_HEATER); // Создаём объект для работы с датчиком и передаём ему номер пина выходного сигнала и нагревателя
    Button btn(BTN_PIN, INPUT); // Создаём объект кнопки
    TimerMs tmr1(500), tmr2(550), tmr3(600), tmr4(650), tmr5(700); // Таймер на 500 мсек
    
    bool btnLedState = false;
     
    void setup()
    {
      Serial.begin(115200); // Открываем последовательный порт
      WiFi_Serial.begin(115200); // Последовательный порт для общения с WiFi модулем
      Serial.setTimeout(5); // Позволяет задать время ожидания данных
      WiFi_Serial.setTimeout(5); // Позволяет задать время ожидания данных
      pinMode(BTN_LED_PIN, OUTPUT); // Настраиваем пин светодиода кнопки на вывод
      mq2.heaterPwrHigh(); // Включаем нагреватель
      Serial.println();
      Serial.println("Heated sensor"); // Сообщение, что пошёл нагрев
      tmr1.setPeriodMode(); // Установить таймеру режим периода
      tmr2.setPeriodMode(); // Установить таймеру режим периода
      tmr3.setPeriodMode(); // Установить таймеру режим периода
      tmr4.setPeriodMode(); // Установить таймеру режим периода
      tmr5.setPeriodMode(); // Установить таймеру режим периода
      tmr1.start(); // Запуск таймера
      tmr2.start(); // Запуск таймера
      tmr3.start(); // Запуск таймера
      tmr4.start(); // Запуск таймера
      tmr5.start(); // Запуск таймера
    }
    
    // Функция опросы кнопки и выполнения действия
    void BtnCheck() {
      btn.tick(); // Опрос состояния кнопки
      if (btn.press()) { // Реагируем на нажатие кнопки
        btnLedState = !btnLedState; // Переписать значение в переменной на противоположеное
        digitalWrite(BTN_LED_PIN, btnLedState); // Переключить состояние на контакте светодиода
        WiFi_Serial.println("led:" + String(btnLedState)); // Отправить WiFi модулю инфу о светодиде
      }
    }
     
    void loop()
    {
      BtnCheck();
      ParseFromSerialInputValues(true); // Считываем по Serial значения
      BtnCheck();
      if (!mq2.isCalibrated() && mq2.heatingCompleted()) {  // если прошёл интервал нагрева датчика и калибровка не была совершена
        mq2.calibrate(); // Выполняем калибровку датчика на чистом воздухе
        if (DEBUG) Serial.println("Ro: " + String(mq2.getRo())); // Выводим сопротивление датчика в чистом воздухе (Ro) в serial-порт
      }
      if (mq2.isCalibrated() && mq2.heatingCompleted()) { // Если прошёл интервал нагрева датчика и калибровка была совершена
        if (tmr1.tick()) {
          int ratio = mq2.readRatio();
          if (DEBUG) Serial.println("Ratio: " + String(ratio) + " ppm"); // Выводим отношения текущего сопротивление датчика к сопротивлению датчика в чистом воздухе (Rs/Ro)
          WiFi_Serial.println("ratio:" + String(ratio) + ";");
        }
        BtnCheck();
        if (tmr2.tick()) {
          int lpg = mq2.readLPG();
          if (DEBUG) Serial.println("LPG: " + String(lpg) + " ppm"); // Сжиженные углеводородные газы в ppm
          WiFi_Serial.println("lpg:" + String(lpg) + ";");
        }
        BtnCheck();
        if (tmr3.tick()) {
          int methane = mq2.readMethane();
          if (DEBUG) Serial.println("Methane: " + String(methane) + " ppm"); // Метан в ppm
          WiFi_Serial.println("methane:" + String(methane) + ";");
        }
        BtnCheck();
        if (tmr4.tick()) {
          int smoke = mq2.readSmoke();
          if (DEBUG) Serial.println("Smoke: " + String(smoke) + " ppm"); // Дым в ppm
          WiFi_Serial.println("smoke:" + String(smoke) + ";");
        }
        BtnCheck();
        if (tmr5.tick()) {
          int hydrogen = mq2.readHydrogen();
          if (DEBUG) Serial.println("Hydrogen: " + String(hydrogen) + " ppm"); // Водород в ppm
          WiFi_Serial.println("hydrogen:" + String(hydrogen) + ";");
        }
        BtnCheck();
      }
    }
    
    // Парсинг значений из Serial и установка значений
    void ParseFromSerialInputValues(bool debug) {
      if (WiFi_Serial.available() > 2) { // Если что-то прислали
        char inputStr[64]; // Массив символов для записи из Serial
        int amount = WiFi_Serial.readBytesUntil(';', inputStr, 64); // Считать посимвольно до символа конца пакета точки с запятой и записать количество полученных байт в переменную
        inputStr[amount] = NULL; // Если отправляющее устройство не отправит нулевой символ, то он не запишется в буффер и вывод строк будет некорректным, решение дописать вручную и т.о. закрываем строку
        GParser data(inputStr, ','); // Парсим массив символов по символу запятой
        int am = data.split(); // Получаем количество данных, внимание, ломает строку!
        for (int i = 0; i < am; i++) {
          String tmpStr = data[i];
          tmpStr.replace(" ", ""); // Удалить пробел, если он был введёт по ошибке
          tmpStr.trim(); // Удаление ведущими и конечные пробелы
          char tmpCharArr[tmpStr.length()];
          tmpStr.toCharArray(tmpCharArr, tmpStr.length() + 1);
          if (debug) Serial.println(String(i) + ") " + tmpStr); // Вывести начальную строку
          GParser data2(tmpCharArr, ':'); // Парсим массив символов по символу запятой
          int am2 = data2.split(); // Получаем количество данных, внимание, ломает строку!
          if (am2 > 1) { // Если существует не только ключ, а ещё и значение
            String key = data2[0]; // Ключ - первое значение
            float value = data2.getInt(1); // Значение - второе
            if (debug) Serial.println("key: " + key + ", value: " + String(value)); // Вывод
            // Присваивание значений и обработка команды
            if (key.equals("led")) {
              btnLedState = value;
              digitalWrite(BTN_LED_PIN, btnLedState);
            }
          }
        }
        if (debug) Serial.println(); // Перевод на новую строку для разделения значений, которые были введены
      }
    }

  7. Соберите устройство и тестируйте

    1. Установите сигнальный пин датчика MQ-2 на A1, а пин E на 12.

    2. Теперь вернитесь на панель управления, чтобы проверить виджет. Как видите, все данные загружаются сюда через определенный интервал.

    3. Также попробуйте приложение для Android.

Модуль WI-Fi Амперки нельзя менять из раза в раз, т.к. при подсоединении модуля к их серверам остаётся какая-то информация о контроллере и если поменять контроллер, то будут ошибки при подсоединении к их серверам.

Если тип данных, который вы ходите записать в облачную переменную отличен от того типа, что установлено в облаке, тогда у вас значение просто не будет изменяться! Напимер, если число float записывать в облачную переменную типа int.

Last updated