Мини-кейс 3: Домашняя метеостанция

Метеостанция с показаниями данных с датчиков.

Перед выполнением прочтите

Информация про датчик температуры и влажности dht11 от Амперки: http://wiki.amperka.ru/%D0%BF%D1%80%D0%BE%D0%B4%D1%83%D0%BA%D1%82%D1%8B:troyka-dht11

Информация про библиотеку работы по таймеру GTimer: https://alexgyver.ru/gyvertimer/

Создание мини-проекта

Сделаем мини-проект с RGB модулем, который управляется через интерфейс RainMaker. Представим, что наш RGB модуль будет умной лампочкой, которая может менять цвет.

  1. Нужно собрать устройство. К GPIO 17 подключить датчик температуры и влажности Troyka контактом. Распиновка платы на родительской странице "О ESP32". Питание для него от пина 3.3v.

  2. Создаём новый файл в Arduino IDE с кодом:

    #include <RMaker.h>
    #include <WiFi.h>
    #include <WiFiProv.h>
    #include "DHT.h" // Библиотека для работы с датчиками серии DHT
    #include "GyverTimer.h" // Подключаем библиотеку работы с временем
    
    #define SERVICE_NAME "PROV_3" // Имя устройства при подключении по bluetooth, нужно для настройки, когда плата ещё не настроена на WI-FI точку
    #define POP "abcd3" // ? Пароль для устройства при подключении по bluetooth
    
    #define BOOT_BTN_PIN 0 // GPIO пин кнопки BOOT
    #define LED_BUILTIN 2 // Определить GPIO пин встроенного светодиода
    #define DHT_PIN 16 // GPIO пин для модуля DHT
    
    #define DEFAULT_SERIAL_DEBUG false // Вывод данных в монитор порта
    #define BOARD_LED_DEFAULT_STATE DEFAULT_SERIAL_DEBUG // Состояние встроенного светодиода при старте
    #define DEFAULT_TIME_UPDATE 2 // Стандартное время обновления данных в сек, чистота обновления DHT от 2 сек
    
    #define RMAKER_TIME_UPDATE_NAME "Time update" // Имя параметра времени обновления
    
    int data_time_update = DEFAULT_TIME_UPDATE; // Переменная для хранения времени обновления данных
    int serial_debug_state = DEFAULT_SERIAL_DEBUG; // Переменная для хранения состояния вывода в Serial данных с датчика
    int dht_gpio = DHT_PIN; // Переменная вашего светодиода
    
    DHT dht(DHT_PIN, DHT11); // Создаём объект класса DHT, передаём номер пина и тип датчика (типы сенсоров: DHT11, DHT21, DHT22)
    GTimer myTimer(MS); // Создаём миллисекундный таймер (ms) (по умолч. в режиме интервала)
    
    // Фреймворк предоставляет некоторые стандартные типы устройств, такие как выключатель, лампочка, вентилятор, датчик температуры.
    // Но вы также можете определить пользовательские устройства, используя объект базового класса 'Device', как показано здесь
    Device my_device("WeatherStation", ESP_RMAKER_DEVICE_TEMP_SENSOR, &dht_gpio);
    
    void sysProvEvent(arduino_event_t *sys_event)
    {
        switch (sys_event->event_id) {      
            case ARDUINO_EVENT_PROV_START:
              // Для ESP32
              Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on BLE\n", SERVICE_NAME, POP);
              printQR(SERVICE_NAME, POP, "ble");   
              // Для ESP32S2
              //Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on SoftAP\n", service_name, pop);
              //printQR(service_name, pop, "softap");   
              break;
        }
    }
    
    // Функция обработки изменений
    void write_callback(Device *device, Param *param, const param_val_t val, void *priv_data, write_ctx_t *ctx)
    {
      const char *device_name = device->getDeviceName();
      const char *param_name = param->getParamName();
      
      if (strcmp(param_name, RMAKER_TIME_UPDATE_NAME) == 0) { // Если параметр с именем Time update
        data_time_update = val.val.i; // Записываем значение яркости вашей лампы в переменную, i - это тип int
        myTimer.setInterval(data_time_update * 1000); // Установить новое время таймера
        myTimer.reset(); // Cбросить период таймера
        Serial.printf("Received param %s = %d for %s\n", param_name, data_time_update, device_name);
        param->updateAndReport(val);
      }
    }
    
    void setup()
    {
      Serial.begin(115200); // Инициализируем скорость Serial
      pinMode(BOOT_BTN_PIN, INPUT); // Настраиваем пин кнопки BOOT на INPUT
      pinMode(LED_BUILTIN, OUTPUT); // Настраиваем пин встроенного светодиода в плате
      digitalWrite(LED_BUILTIN, BOARD_LED_DEFAULT_STATE); // Светодиод при запуске примет значение BOARD_LED_DEFAULT_STATE
    
      myTimer.setInterval(DEFAULT_TIME_UPDATE * 1000); // Запуск в режиме интервала на необходимое количество мс
      dht.begin(); // Запускаем dht
      
      Node my_node; // Объявляем Node
      my_node = RMaker.initNode("ESP RainMaker Node"); // Инициализируем Node
    
      // Создаём устройство для управления светодиодом
      my_device.addNameParam(); // Устнаваливаем имя устройства
    
      // Создаём и добавляем слайдер времени обновления
      Param time_update_param(RMAKER_TIME_UPDATE_NAME, ESP_RMAKER_PARAM_SPEED, value(DEFAULT_TIME_UPDATE), PROP_FLAG_READ | PROP_FLAG_WRITE); // Указываем название создаваемого параметра, тип параметра, стандартное значение и задаём права
      time_update_param.addBounds(value(2), value(60), value(1)); // Значение от 0 до 60, если удалить этот параметр, тогда можно будет вводить ЧИСЛО вручную
      time_update_param.addUIType(ESP_RMAKER_UI_SLIDER); // Устанавливаем тип отображения как слайдер
      my_device.addParam(time_update_param); // Добавляем параметр устройству
    
       // Создаём плашку с текстом о температуре в цельсиях
      Param temp_c_param(ESP_RMAKER_DEF_TEMPERATURE_NAME, ESP_RMAKER_PARAM_TEMPERATURE, value("Undefined"), PROP_FLAG_READ); // Указываем название создаваемого параметра, тип параметра, стандартное значение и задаём права
      temp_c_param.addUIType(ESP_RMAKER_UI_TEXT); // Устанавливаем тип отображения как слайдер
      my_device.addParam(temp_c_param); // Добавляем параметр устройству
      
      my_device.addCb(write_callback); // Устанавливаем функцию на обработчик
      my_node.addDevice(my_device); // Добавить девайс на ноду
    
      // Опционально
      RMaker.enableOTA(OTA_USING_PARAMS); // Включить беспроводное обновление
      RMaker.setTimeZone("Europe/Moscow"); // Установить часовой пояс https://rainmaker.espressif.com/docs/time-service.html
      RMaker.enableTZService(); // Кроме того, включите службу часового пояса и позвольте приложениям телефона установить соответствующий часовой пояс
    
      RMaker.enableSchedule(); // Включить расписание для нашего устройства
      
      RMaker.start(); // Запуск сервиса на устройстве
    
      WiFi.onEvent(sysProvEvent);
      WiFiProv.beginProvision(WIFI_PROV_SCHEME_BLE, WIFI_PROV_SCHEME_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_1, POP, SERVICE_NAME);
    }
    
    void loop()
    {
      if (myTimer.isReady()) { // Интервал таймера выполнился
        float temp = dht.readTemperature(); // Считываем и записываем температуру в цельсияъ
        float humi = dht.readHumidity(); // Считываем и записываем влажность в процентах
        my_device.updateAndReportParam(ESP_RMAKER_DEF_TEMPERATURE_NAME, String(temp, 1).c_str()); // Изменяем состояние тумпературы в RainMaker
        if (serial_debug_state) { // Выводим в консоль, если необходимо по нажатию кнопки BOOT
          Serial.printf("Temperature = %.1fºC\n", temp, 1);
          Serial.printf("Humidity = %.1f%%\n", humi, 1);
        }
      }
      
      // Обрабатываем нажатие на кнопка BOOT
      if(digitalRead(BOOT_BTN_PIN) == LOW) {
          delay(100); // Для исключения дребезка на тактовой кнопке
          int startTime = millis(); // Время на номент нажатия кнопки
          while(digitalRead(BOOT_BTN_PIN) == LOW) delay(50); // Цикл, который создаёт паузу для ожидания отжатия кнопки
          int endTime = millis(); // Время на момент отжатия кнопки
          int pressTime = endTime - startTime; // Время, на которое кнопка была нажата
          // Условия по времени нажатия
          if (pressTime > 10000) { // Если кнопка была зажата на более 10 секунд, тогда сбросить всё
            Serial.printf("Reset to factory.\n");
            RMakerFactoryReset(2);
          } else if (pressTime > 3000) { // Иначе если кнопка была зажата больше чем на 3 секунды, но меньше 10, тогда сбросить настройки WI-FI
            Serial.printf("Reset Wi-Fi.\n");
            RMakerWiFiReset(2);
          } else { // Нажатие кнопки переключает флажок о необходимости вывода в монитор порта данных
            serial_debug_state = !serial_debug_state; // Меняем запись в переменной состояния вывода в монитор порта
            Serial.printf("Toggle Debug State to %s.\n", (serial_debug_state ? "true" : "false"));
            if (!serial_debug_state) digitalWrite(LED_BUILTIN, LOW); // Включаем или выключаем встроенный светодиод
            else digitalWrite(LED_BUILTIN, HIGH);
         }
      }
      delay(10);
    }

    Изучите код, прочитайте комментарии к код!

    Обратите своё внимание, что на 83 строке, когда создаётся параметрhumidity_c_paramдефолтное значение (в функции value()) записывается как строка. RainMaker понимает её как массив символов. Если дальше передавать в сервис новые данные для этого параметра без обработки, тогда в монитор порта будет выводится ошибка:"New param value type not same as the existing one.", что значит то, что новое отправленное значение другого типа.

    Поэтому перед отправкой нужно данные переконвертировать в старый тип данных, что делается в строке 108: String(temp, 1).c_str()). Сначала переменная типа float переводится в строку и остётся только 1 знак после запятой. Далее эта строка переводится в массив символов.

    Также обратите внимание на строки 110 и 111, тут в функции printf указывается, что число типа float %.1f выведется только с одним знаком после запятой.

    Прочитайте о функции printf! Ещё обратите внимание в данном мини-кейсе отстутствуют строки, которые отвечают за параметр POWER. Это означает, что в интерфейсе RainMaker не будет кнопки отключения.

    my_device.addPowerParam(true);
    my_device.assignPrimaryParam(my_device.getParamByName(ESP_RMAKER_DEF_POWER_NAME));
  3. Скачайте и добавьте библиотеки DHT sensor library от Adafruit и GyverTimer. Вы можете это сделать через пункт "Управление библиотками" или скачать из интернета и добавить как zip библиотеку.

    Обратите внимание, что библиотека TroykaDHT от разработчиков модуля не работает с ESP32.

  4. В инструментах вы должны выбрать:

    • Плата: ESP32 Arduino > ESP32 Dev Module;

    • Partition Scheme: RainMaker.

    И выберите порт платы. Загрузите прошивку. Никаких пинов для перевода контроллера в режим прошивки зажимать не нужно.

  5. Далее дайдите в монитор порта, установить скорость 115200. Выведется информация о том, как подключить плату к сервису. Там должна быть строка с QR-кодом и ссылка. Скопируйте ссылку и вставьте в адресную строку браузера.

    На этом этапе могут возникнуть две известные проблемы...

    • Плата пишет что-то на английском, но не выдаёт ссылку, тогда нужно сбросить настройки WI-FI, зажав BOOT на 3 секунды и отпустив. * Если плата уже была до этого настроена, тогда она будет выводить после прошивки текст на английском, QR-код уже не выведется. Это нормально, но пользоваться дальше можно!

    • Плата пишет всякие кракозябры, хотя скорость последовательного порта выставлена верно. Вам нужно сбросить настройки прошивки. Для этого зажмите кнопку BOOT на плате на 10 секунд. По истечению времени отпустите, плата перезапустит код и в монитор порта текст станет читабельный и выведется ссылка.

  6. Откройте приложение RainMaker. В приложении RainMaker нажать на кнопку с иконкой ПЛЮС, запуститься сканер QR-кода.

  7. Отсканировать сканером QR-код с страницы с браузера.

  8. В приложении RainMaker выбрать точку доступа для устройства, дать пароль и подождать подключения и всяких других настроек.

  9. На главной странице RainMaker появится или обновится ваш девайс.

Дополнительное задание:

  • Сделать, чтобы в интерфейс RainMaker выводилась ещё и влажность с датчика.

Last updated