> For the complete documentation index, see [llms.txt](https://itdocs.kvantorium21.ru/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://itdocs.kvantorium21.ru/it-engineering/i.-internet-veshei/case-1b/mini-keis-3-domashnyaya-meteostanciya.md).

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

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

![](https://1234485992-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LuCyLoyEs8r9SqV2MS6%2Fuploads%2F110WlstTBPn2iPr2pMd7%2Fimage.png?alt=media\&token=32e1f9ab-8858-477b-bd67-307ddceeafa5)

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

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

Информация про библиотеку работы по таймеру GTimer:\
<https://alexgyver.ru/gyvertimer/>
{% endhint %}

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

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

1. Нужно собрать устройство. К `GPIO 16` подключить датчик температуры и влажности Troyka контактом. [Распиновка платы на родительской странице "О ESP32"](/it-engineering/i.-internet-veshei/case-1b/o-esp32.md#raspinovka). Питание для него от пина `3.3v`.
2. Создаём новый файл в Arduino IDE с кодом:

   <pre class="language-cpp" data-line-numbers><code class="lang-cpp">#include &#x3C;RMaker.h>
   #include &#x3C;WiFi.h>
   #include &#x3C;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, &#x26;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(NETWORK_PROV_SCHEME_BLE, NETWORK_PROV_SCHEME_HANDLER_FREE_BTDM, NETWORK_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);
   }
   </code></pre>

   <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p>Изучите код, прочитайте комментарии к код!</p><p><br>Обратите своё внимание, что на 83 строке, когда создаётся параметр<strong><code>humidity_c_param</code></strong><code>дефолтное значение (в функции value()) записывается как строка. RainMaker понимает её как массив символов. Если дальше передавать в сервис новые данные для этого параметра без обработки, тогда в монитор порта будет выводится ошибка:</code><em><code>"New param value type not same as the existing one."</code></em>, что значит то, что новое отправленное значение другого типа.</p><p>Поэтому перед отправкой нужно данные переконвертировать в старый тип данных, что делается в строке 108: <code>String(temp, 1).c_str())</code>. Сначала переменная типа float переводится в строку и остётся только 1 знак после запятой. Далее эта строка переводится в массив символов.</p><p></p><p>Также обратите внимание на строки 110 и 111, тут в функции printf указывается, что число типа float %.1f выведется только с одним знаком после запятой.</p><p>Прочитайте о функции <a href="http://www.c-cpp.ru/content/printf">printf</a>!<br><br>Ещё обратите внимание в данном мини-кейсе отстутствуют строки, которые отвечают за параметр POWER. Это означает, что в интерфейсе RainMaker не будет кнопки отключения.</p><pre class="language-cpp"><code class="lang-cpp">my_device.addPowerParam(true);
   my_device.assignPrimaryParam(my_device.getParamByName(ESP_RMAKER_DEF_POWER_NAME));
   </code></pre></div>
3. Скачайте и добавьте библиотеки **`DHT sensor library от Adafruit`**  и [**`GyverTimer`**](https://alexgyver.ru/gyvertimer/). Вы можете это сделать через пункт `"Управление библиотками"` или скачать из интернета и добавить как zip библиотеку.

   <div data-gb-custom-block data-tag="hint" data-style="warning" class="hint hint-warning"><p>Обратите внимание, что библиотека <a href="https://github.com/amperka/TroykaDHT"><strong><code>TroykaDHT</code></strong></a> от разработчиков модуля не работает с ESP32.</p></div>
4. В инструментах вы должны выбрать:

   * Плата: ESP32 Arduino > ESP32 Dev Module;
   * Partition Scheme: RainMaker.

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

   <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p>На этом этапе могут возникнуть две известные проблемы...</p><ul><li>Плата пишет что-то на английском, но не выдаёт ссылку, тогда нужно сбросить настройки WI-FI, зажав <code>BOOT</code> на 3 секунды и отпустив.<br>* Если плата уже была до этого настроена, тогда она будет выводить после прошивки текст на английском, QR-код уже не выведется. Это нормально, но пользоваться дальше можно!</li><li>Плата пишет всякие кракозябры, хотя скорость последовательного порта выставлена верно. Вам нужно сбросить настройки прошивки. Для этого зажмите кнопку <code>BOOT</code> на плате на 10 секунд. По истечению времени отпустите, плата перезапустит код и в монитор порта текст станет читабельный и выведется ссылка.</li></ul></div>
6. Откройте приложение `RainMaker`. В приложении RainMaker нажать на кнопку с иконкой `ПЛЮС`, запуститься сканер QR-кода.
7. Отсканировать сканером QR-код с страницы с браузера.
8. В приложении `RainMaker` выбрать точку доступа для устройства, дать пароль и подождать подключения и всяких других настроек.
9. На главной странице `RainMaker` появится или обновится ваш девайс.

{% hint style="success" %}
**Дополнительное задание:**

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