ESP8266 NodeMCU MQTT – публикация показаний датчика BME280 (Arduino IDE)

Узнайте, как публиковать показания датчика BME280 (температура, влажность и давление) через MQTT с ESP8266 на любую платформу, поддерживающую MQTT, или любой MQTT-клиент. В качестве примера мы опубликуем показания датчика на панель Node-RED Dashboard, а ESP8266 будет запрограммирован с помощью Arduino IDE.

ESP8266 NodeMCU MQTT публикация показаний датчика BME280 Arduino IDE

Рекомендуемое чтение: Что такое MQTT и как он работает

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

На следующей диаграмме показан общий обзор проекта, который мы создадим.

ESP8266 NodeMCU BME280 публикация показаний Node-RED MQTT обзор проекта
  • ESP8266 запрашивает показания температуры от датчика BME280;

  • Показания температуры публикуются в топике esp/bme280/temperature;

  • Показания влажности публикуются в топике esp/bme280/humidity;

  • Показания давления публикуются в топике esp/bme280/pressure;

  • Node-RED подписан на эти топики;

  • Node-RED получает показания датчика и отображает их на индикаторах (gauges);

  • Вы можете получать показания на любой другой платформе, поддерживающей MQTT, и обрабатывать их по своему усмотрению.

Предварительные условия

Прежде чем приступить к этому руководству, убедитесь, что вы выполнили следующие предварительные условия.

Arduino IDE

Мы будем программировать ESP8266 с помощью Arduino IDE, поэтому убедитесь, что у вас установлено дополнение ESP8266.

MQTT-брокер

Установка Mosquitto MQTT-брокера на Raspberry Pi

Для использования MQTT вам нужен брокер. Мы будем использовать брокер Mosquitto, установленный на Raspberry Pi. Прочитайте Как установить брокер Mosquitto на Raspberry Pi.

Вы можете использовать любой другой MQTT-брокер, включая облачный MQTT-брокер. Мы покажем вам, как это сделать в коде далее.

Если вы не знакомы с MQTT, обязательно прочитайте наше вводное руководство: Что такое MQTT и как он работает.

Библиотеки MQTT

Для использования MQTT с ESP8266 мы будем использовать библиотеку Async MQTT Client.

Установка библиотеки Async MQTT Client

  1. Нажмите здесь, чтобы скачать библиотеку Async MQTT client. У вас должен появиться файл .zip в папке загрузок

  2. Распакуйте .zip файл, и вы получите папку async-mqtt-client-master

  3. Переименуйте папку из ~~**async-mqtt-client-master**~~ в async_mqtt_client

  4. Переместите папку async_mqtt_client в папку библиотек вашей Arduino IDE

  5. Наконец, перезапустите Arduino IDE

В качестве альтернативы вы можете перейти в Sketch > Include Library > Add .ZIP library и выбрать только что скачанную библиотеку.

Установка библиотеки ESPAsync TCP

Для использования MQTT с ESP8266 вам также необходима библиотека ESPAsync TCP.

  1. Нажмите здесь, чтобы скачать библиотеку ESPAsync TCP client. У вас должен появиться файл .zip в папке загрузок

  2. Распакуйте .zip файл, и вы получите папку ESPAsyncTCP-master

  3. Переименуйте папку из ~~**ESPAsyncTCP-master**~~ в ESPAsyncTCP

  4. Переместите папку ESPAsyncTCP в папку библиотек вашей Arduino IDE

  5. Наконец, перезапустите Arduino IDE

В качестве альтернативы вы можете перейти в Sketch > Include Library > Add .ZIP library и выбрать только что скачанную библиотеку.

Библиотеки датчика BME280

Для получения показаний от модуля датчика BME280 мы будем использовать библиотеку Adafruit_BME280. Вам также необходимо установить библиотеку Adafruit_Sensor. Выполните следующие шаги для установки библиотек в вашей Arduino IDE:

  1. Откройте Arduino IDE и перейдите в Sketch > Include Library > Manage Libraries. Должен открыться Library Manager.

  2. Найдите «adafruit bme280» в поле поиска и установите библиотеку.

Установка библиотеки BME280 в Arduino IDE

Для использования библиотеки BME280 вам также необходимо установить Adafruit Unified Sensor. Выполните следующие шаги для установки библиотеки в вашей Arduino IDE:

  1. Найдите «Adafruit Unified Sensor» в поле поиска. Прокрутите вниз до конца, чтобы найти библиотеку и установить её.

Установка библиотеки Adafruit Unified Sensor Driver

После установки библиотек перезапустите Arduino IDE.

Чтобы узнать больше о датчике BME280, прочитайте: ESP8266 с BME280 с использованием Arduino IDE (давление, температура, влажность)

Необходимые компоненты

Для этого руководства вам понадобятся следующие компоненты:

Вы можете использовать ссылки выше или перейти напрямую на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!

Схема подключения

Подключите BME280 к ESP8266, как показано на следующей схеме: вывод SDA подключён к GPIO 4, а вывод SCL подключён к GPIO 5.

ESP8266 NodeMCU BME280 датчик температуры влажности давления подключение I2C схема

Код

Скопируйте следующий код в вашу Arduino IDE. Чтобы он работал для вас, вам нужно вставить ваши сетевые учётные данные, а также данные MQTT-брокера.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp8266-nodemcu-mqtt-publish-bme280-arduino/

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <ESP8266WiFi.h>
#include <Ticker.h>
#include <AsyncMqttClient.h>

#define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"

// Raspberri Pi Mosquitto MQTT Broker
#define MQTT_HOST IPAddress(192, 168, 1, XXX)
// For a cloud MQTT broker, type the domain name
//#define MQTT_HOST "example.com"
#define MQTT_PORT 1883

// Temperature MQTT Topics
#define MQTT_PUB_TEMP "esp/bme280/temperature"
#define MQTT_PUB_HUM "esp/bme280/humidity"
#define MQTT_PUB_PRES "esp/bme280/pressure"

// BME280 I2C
Adafruit_BME280 bme;
// Variables to hold sensor readings
float temp;
float hum;
float pres;

AsyncMqttClient mqttClient;
Ticker mqttReconnectTimer;

WiFiEventHandler wifiConnectHandler;
WiFiEventHandler wifiDisconnectHandler;
Ticker wifiReconnectTimer;

unsigned long previousMillis = 0;   // Stores last time temperature was published
const long interval = 10000;        // Interval at which to publish sensor readings

void connectToWifi() {
  Serial.println("Connecting to Wi-Fi...");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}

void onWifiConnect(const WiFiEventStationModeGotIP& event) {
  Serial.println("Connected to Wi-Fi.");
  connectToMqtt();
}

void onWifiDisconnect(const WiFiEventStationModeDisconnected& event) {
  Serial.println("Disconnected from Wi-Fi.");
  mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
  wifiReconnectTimer.once(2, connectToWifi);
}

void connectToMqtt() {
  Serial.println("Connecting to MQTT...");
  mqttClient.connect();
}

void onMqttConnect(bool sessionPresent) {
  Serial.println("Connected to MQTT.");
  Serial.print("Session present: ");
  Serial.println(sessionPresent);
}

void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
  Serial.println("Disconnected from MQTT.");

  if (WiFi.isConnected()) {
    mqttReconnectTimer.once(2, connectToMqtt);
  }
}

/*void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
  Serial.println("Subscribe acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
  Serial.print("  qos: ");
  Serial.println(qos);
}

void onMqttUnsubscribe(uint16_t packetId) {
  Serial.println("Unsubscribe acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
}*/

void onMqttPublish(uint16_t packetId) {
  Serial.print("Publish acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
}

void setup() {
  Serial.begin(115200);
  Serial.println();

  // Initialize BME280 sensor
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
  wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);

  mqttClient.onConnect(onMqttConnect);
  mqttClient.onDisconnect(onMqttDisconnect);
  //mqttClient.onSubscribe(onMqttSubscribe);
  //mqttClient.onUnsubscribe(onMqttUnsubscribe);
  mqttClient.onPublish(onMqttPublish);
  mqttClient.setServer(MQTT_HOST, MQTT_PORT);
  // If your broker requires authentication (username and password), set them below
  //mqttClient.setCredentials("REPlACE_WITH_YOUR_USER", "REPLACE_WITH_YOUR_PASSWORD");

  connectToWifi();
}

void loop() {
  unsigned long currentMillis = millis();
  // Every X number of seconds (interval = 10 seconds)
  // it publishes a new MQTT message
  if (currentMillis - previousMillis >= interval) {
    // Save the last time a new reading was published
    previousMillis = currentMillis;
    // New BME280 sensor readings
    temp = bme.readTemperature();
    //temp = 1.8*bme.readTemperature() + 32;
    hum = bme.readHumidity();
    pres = bme.readPressure()/100.0F;

    // Publish an MQTT message on topic esp/bme280/temperature
    uint16_t packetIdPub1 = mqttClient.publish(MQTT_PUB_TEMP, 1, true, String(temp).c_str());
    Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_TEMP, packetIdPub1);
    Serial.printf("Message: %.2f \n", temp);

    // Publish an MQTT message on topic esp/bme280/humidity
    uint16_t packetIdPub2 = mqttClient.publish(MQTT_PUB_HUM, 1, true, String(hum).c_str());
    Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_HUM, packetIdPub2);
    Serial.printf("Message: %.2f \n", hum);

    // Publish an MQTT message on topic esp/bme280/pressure
    uint16_t packetIdPub3 = mqttClient.publish(MQTT_PUB_PRES, 1, true, String(pres).c_str());
    Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_PRES, packetIdPub3);
    Serial.printf("Message: %.3f \n", pres);
  }
}

Посмотреть исходный код

Как работает код

Следующий раздел импортирует все необходимые библиотеки.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <ESP8266WiFi.h>
#include <Ticker.h>
#include <AsyncMqttClient.h>

Вставьте ваши сетевые учётные данные в следующие строки.

#define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"

Вставьте IP-адрес Raspberry Pi, чтобы ESP8266 подключился к вашему брокеру.

#define MQTT_HOST IPAddress(192, 168, 1, 106)

Если вы используете облачный MQTT-брокер, вставьте доменное имя брокера, например:

#define MQTT_HOST "example.com"

Определите порт MQTT.

#define MQTT_PORT 1883

Температура, влажность и давление будут публиковаться в следующих топиках:

#define MQTT_PUB_TEMP "esp/bme280/temperature"
#define MQTT_PUB_HUM "esp/bme280/humidity"
#define MQTT_PUB_PRES "esp/bme280/pressure"

Инициализируйте объект Adafruit_BME280 с именем bme.

Adafruit_BME280 bme;

Переменные temp, hum и pres будут хранить значения температуры, влажности и давления от датчика BME280.

float temp;
float hum;
float pres;

Создайте объект AsyncMqttClient с именем mqttClient для управления MQTT-клиентом и таймеры для переподключения к MQTT-брокеру и маршрутизатору при потере соединения.

AsyncMqttClient mqttClient;
Ticker mqttReconnectTimer;

WiFiEventHandler wifiConnectHandler;
WiFiEventHandler wifiDisconnectHandler;
Ticker wifiReconnectTimer;

Затем создайте вспомогательные переменные таймера для публикации показаний каждые 10 секунд. Вы можете изменить время задержки в переменной interval.

unsigned long previousMillis = 0;  // Stores last time temperature was published
const long interval = 10000;       // Interval at which to publish sensor readings

Функции MQTT: подключение к Wi-Fi, подключение к MQTT и события Wi-Fi

Мы не добавляли комментарии к функциям, определённым в следующем разделе кода. Эти функции поставляются с библиотекой Async Mqtt Client. Названия функций довольно понятны сами по себе.

Например, функция connectToWifi() подключает ваш ESP8266 к маршрутизатору:

void connectToWifi() {
  Serial.println("Connecting to Wi-Fi...");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}

Функция connectToMqtt() подключает ваш ESP8266 к MQTT-брокеру:

void connectToMqtt() {
  Serial.println("Connecting to MQTT...");
  mqttClient.connect();
}

Функция onMqttConnect() выполняется после установления сессии с брокером.

void onMqttConnect(bool sessionPresent) {
  Serial.println("Connected to MQTT.");
  Serial.print("Session present: ");
  Serial.println(sessionPresent);
}

Функции MQTT: отключение и публикация

Если ESP8266 теряет соединение с MQTT-брокером, вызывается функция onMqttDisconnect, которая выводит сообщение в Serial Monitor.

void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
  Serial.println("Disconnected from MQTT.");
  if (WiFi.isConnected()) {
    mqttReconnectTimer.once(2, connectToMqtt);
  }
}

Когда вы публикуете сообщение в MQTT-топик, вызывается функция onMqttPublish(). Она выводит идентификатор пакета в Serial Monitor.

void onMqttPublish(uint16_t packetId) {
  Serial.print("Publish acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
}

По сути, все эти функции, которые мы только что упомянули, являются функциями обратного вызова (callback). Поэтому они выполняются асинхронно.

setup()

В функции setup() инициализируйте датчик BME280.

if (!bme.begin(0x76)) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
}

Следующие две строки создают обработчики, которые позволят как MQTT-брокеру, так и Wi-Fi-соединению переподключиться в случае потери связи.

wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);

Наконец, назначьте все функции обратного вызова. Это означает, что эти функции будут вызываться автоматически при необходимости. Например, когда ESP8266 подключается к брокеру, он автоматически вызывает функцию onMqttConnect() и так далее.

mqttClient.onConnect(onMqttConnect);
mqttClient.onDisconnect(onMqttDisconnect);
//mqttClient.onSubscribe(onMqttSubscribe);
//mqttClient.onUnsubscribe(onMqttUnsubscribe);
mqttClient.onPublish(onMqttPublish);
mqttClient.setServer(MQTT_HOST, MQTT_PORT);

Аутентификация брокера

Если ваш брокер требует аутентификации, раскомментируйте следующую строку и вставьте свои учётные данные (имя пользователя и пароль).

mqttClient.setCredentials("REPlACE_WITH_YOUR_USER", "REPLACE_WITH_YOUR_PASSWORD");

Наконец, подключитесь к Wi-Fi.

connectToWifi();

loop()

В функции loop() вы создаёте таймер, который позволит получать новые показания от датчика BME280 и публиковать их в соответствующем топике каждые 10 секунд.

unsigned long currentMillis = millis();
// Every X number of seconds (interval = 10 seconds)
// it publishes a new MQTT message
if (currentMillis - previousMillis >= interval) {
  // Save the last time a new reading was published
  previousMillis = currentMillis;
  // New BME280 sensor readings
  temp = bme.readTemperature();
  //temp = 1.8*bme.readTemperature() + 32;
  hum = bme.readHumidity();
  pres = bme.readPressure()/100.0F;

Узнайте больше о получении показаний от датчика BME280: ESP8266 с датчиком BME280: руководство по температуре, влажности и давлению.

Публикация в топики

Для публикации показаний в соответствующие MQTT-топики используйте следующие строки:

uint16_t packetIdPub1 = mqttClient.publish(MQTT_PUB_TEMP, 1, true, String(temp).c_str());
uint16_t packetIdPub2 = mqttClient.publish(MQTT_PUB_HUM, 1, true, String(hum).c_str());
uint16_t packetIdPub3 = mqttClient.publish(MQTT_PUB_PRES, 1, true, String(pres).c_str());

По сути, используйте метод publish() объекта mqttClient для публикации данных в топик. Метод publish() принимает следующие аргументы (по порядку):

  • MQTT-топик (const char*)

  • QoS (uint8_t): качество обслуживания – может быть 0, 1 или 2

  • Флаг retain (bool): флаг сохранения

  • Полезная нагрузка (const char*) – в данном случае полезная нагрузка соответствует показаниям датчика

QoS (quality of service, качество обслуживания) – это способ гарантировать доставку сообщения. Он может быть одним из следующих уровней:

  • 0: сообщение будет доставлено один раз или не доставлено вовсе. Сообщение не подтверждается. Нет возможности дублирования сообщений;

  • 1: сообщение будет доставлено как минимум один раз, но может быть доставлено более одного раза;

  • 2: сообщение всегда доставляется ровно один раз;

  • Узнайте больше о MQTT QoS.

Загрузка кода

Включив Raspberry Pi с запущенным MQTT-брокером Mosquitto, загрузите код на ваш ESP8266.

Откройте Serial Monitor на скорости 115200 бод, и вы увидите, что ESP8266 начинает публиковать сообщения в топики, которые мы определили ранее.

ESP8266 NodeMCU публикация показаний BME280 MQTT Serial Monitor

Подготовка панели Node-RED Dashboard

ESP8266 публикует показания температуры каждые 10 секунд в топики esp/bme280/temperature, esp/bme280/humidity и esp/bme280/pressure. Теперь вы можете использовать любую панель управления, поддерживающую MQTT, или любое другое устройство, поддерживающее MQTT, для подписки на эти топики и получения показаний.

В качестве примера мы создадим простой поток (flow) с использованием Node-RED для подписки на эти топики и отображения показаний на индикаторах.

Если у вас не установлен Node-RED, следуйте следующим руководствам:

Запустив Node-RED на вашем Raspberry Pi, перейдите по IP-адресу Raspberry Pi с портом :1880.

http://raspberry-pi-ip-address:1880

Интерфейс Node-RED должен открыться. Перетащите три узла MQTT in и три узла gauge на поток.

Node-RED перетащите 6 узлов MQTT In и Gauge

Нажмите на узел MQTT и отредактируйте его свойства.

Свойства узла MQTT In для ESP8266 публикация температуры Node-RED Flow

Поле Server относится к MQTT-брокеру. В нашем случае MQTT-брокер – это Raspberry Pi, поэтому установлено значение localhost:1883. Если вы используете облачный MQTT-брокер, вам следует изменить это поле.

Вставьте топик, на который хотите подписаться, и QoS. Предыдущий узел MQTT подписан на топик esp/bme280/temperature.

Нажмите на другие узлы MQTT in и отредактируйте их свойства с тем же сервером, но для других топиков: esp/bme280/humidity и esp/bme280/pressure.

Нажмите на узлы gauge и отредактируйте их свойства для каждого показания. Следующий узел настроен для показаний температуры. Отредактируйте другие узлы для остальных показаний.

ESP8266 NodeMCU Gauge температура Node-RED Flow

Соедините узлы, как показано ниже:

ESP8266 NodeMCU MQTT публикация температуры влажности давления Node-RED Flow

Наконец, разверните ваш поток (нажмите кнопку в правом верхнем углу).

Кнопка Deploy Node-RED

В качестве альтернативы вы можете перейти в Menu > Import и скопировать следующий код в Clipboard для создания вашего потока Node-RED.

[{"id":"5a45b8da.52b0d8","type":"mqtt in","z":"b01416d3.f69f38","name":"","topic":"esp/bme280/temperature","qos":"1","datatype":"auto","broker":"8db3fac0.99dd48","x":310,"y":60,"wires":[["3042e15e.80a4ee"]]},{"id":"3042e15e.80a4ee","type":"ui_gauge","z":"b01416d3.f69f38","name":"","group":"37de8fe8.46846","order":2,"width":0,"height":0,"gtype":"gage","title":"Temperature","label":"ºC","format":"{{value}}","min":0,"max":"40","colors":["#00b500","#f7df09","#ca3838"],"seg1":"","seg2":"","x":590,"y":60,"wires":[]},{"id":"8ff168f0.0c74a8","type":"mqtt in","z":"b01416d3.f69f38","name":"","topic":"esp/bme280/humidity","qos":"1","datatype":"auto","broker":"8db3fac0.99dd48","x":300,"y":140,"wires":[["29251f29.6687c"]]},{"id":"29251f29.6687c","type":"ui_gauge","z":"b01416d3.f69f38","name":"","group":"37de8fe8.46846","order":2,"width":0,"height":0,"gtype":"gage","title":"Humidity","label":"%","format":"{{value}}","min":"30","max":"100","colors":["#53a4e6","#1d78a9","#4e38c9"],"seg1":"","seg2":"","x":580,"y":140,"wires":[]},{"id":"294f7eea.999d72","type":"mqtt in","z":"b01416d3.f69f38","name":"","topic":"esp/bme280/pressure","qos":"1","datatype":"auto","broker":"8db3fac0.99dd48","x":300,"y":220,"wires":[["58610d70.bb9764"]]},{"id":"58610d70.bb9764","type":"ui_gauge","z":"b01416d3.f69f38","name":"","group":"37de8fe8.46846","order":4,"width":0,"height":0,"gtype":"gage","title":"Pressure","label":"hPa","format":"{{value}}","min":0,"max":"1200","colors":["#b366ff","#8000ff","#440088"],"seg1":"","seg2":"","x":580,"y":220,"wires":[]},{"id":"8db3fac0.99dd48","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"37de8fe8.46846","type":"ui_group","z":"","name":"BME280","tab":"53b8c8f9.cfbe48","order":1,"disp":true,"width":"6","collapse":false},{"id":"53b8c8f9.cfbe48","type":"ui_tab","z":"","name":"Home","icon":"dashboard","order":2,"disabled":false,"hidden":false}]

Посмотреть исходный код

Демонстрация

Перейдите по IP-адресу вашего Raspberry Pi с добавлением :1880/ui.

http://raspberry-pi-ip-address:1880/ui

Вы должны получить доступ к текущим показаниям датчика BME280 на панели Dashboard. Вы можете использовать другие типы узлов панели для отображения показаний различными способами.

ESP8266 NodeMCU MQTT публикация температуры влажности давления Node-RED Dashboard

Вот и всё! Ваша плата ESP8266 публикует показания температуры, влажности и давления BME280 в Node-RED через MQTT.

Заключение

MQTT – это отличный протокол связи для обмена небольшими объёмами данных между устройствами. В этом руководстве вы узнали, как публиковать показания температуры, влажности и давления от датчика BME280 с ESP8266 в различные MQTT-топики. Затем вы можете использовать любое устройство или платформу домашней автоматизации для подписки на эти топики и получения показаний.

Вместо BME280 вы можете использовать другой датчик:

Мы надеемся, что это руководство было полезным для вас. Если вы хотите узнать больше об ESP8266, ознакомьтесь с нашими ресурсами:

Спасибо за чтение.