Подключение к MQTT серверу, небольшое зависание

Добрый день.
Как пишут, какие модули не дай новичку, все равно получится метеостанция(с).
По проблеме.
Установив свой первый собранный модуль в колбасный шкаф, для контроля температуры, я понял, что температуру можно не только смотреть дистанционно, но и управлять ей. После чего решил собрать термостат. Сперва хотел собрать его на arduino с сетевым шилдом ENC28J60, но наткнулся на описание модуля esp8266? вернее esp-01.
Поискав в сети примеры, наткнулся на проект термостата ivanUA. Скачал, залил в модуль, ура всё работает, но отключив raspberry на котором крутился тестовый MQTT сервер, тут же получил граблей по лбу, всё повисло. Подумав, что не я один такой умный, полез искать информацию. После чего понял, что нельзя пихать все в одну процедуру loop и лучше использовать таймеры. После чтения форумов изменил код, переписал веб-сервер, добавил шифрованный протокол. Все заработало, при этом если нет сети, можно зайти на локальную точку доступа , всё управление доступно.
Но в один из дней, у провайдера были какие то проблемы в дата-центре и мой VPS сервер на котором поднят MQTT просто не работал. Зайдя на веб сервер, который поднят на модуле, обнаружил, что отправленные команды тормозят на выполнение до 15 секунд. То есть нажав кнопку выключить, реле отключится в пределах от мгновенно до 15 секунд. При этом сам код работает, если температура подойдет к установленному пределу, реле отключится мгновенно. Оказалось, что если есть сеть, но нет связи с MQTT сервером, модуль пытается отправить LWT (что по описаниям и составляет примерно 15 секунд), отправить не может и поэтому подвисает.
На самом деле это не критично, так как опрос датчика, управление реле работает, но немного раздражает. Да и хотя падение VPS сервера довольно редкая ситуация, но хочется что-то придумать, что бы такого не было. К сожалению информации на 99% в интернете просто или ссылки или перепечатки с сайта на сайт.
Нашел информацию, что если все критично лучше использовать RTOS, но пока я не понял как с ней работать и только разбираюсь. Если с “тасками” что-то понятно, то как запихнуть прям в него веб-сервер пока информации не нашел.
Отсюда вопрос, если не сложно, укажите в какую сторону искать информацию для исправления данной проблемы, пока без учета RTOS. Если это не исправимо, простыми средствами, тогда оставлю проблему.
Код основного модуля приложен. Знаю, что корявый, но это мой второй код )))

/* Термо камера на ESP-01 */
#define SENSOR_PIN 0 // Пин датчика GPIO0
#define RELEY_PIN 2  // Пин реле GPIO2 - GND
#define BTN_PIN 3    // Пин кнопки GPIO3 - GND
#define _BTN_DEB_TIME 100
// Подключеные модули
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <PubSubClient.h>
#include <EEPROM.h>
#include <Ticker.h>
#include <microDS18B20.h>
#include "configure.h" // Настройки, EEPROM
// Переменные
WiFiClientSecure ESPclientSsl;      // Клиент с шифрованием
WiFiClient ESPclient;               // Клиент без шифрования
PubSubClient mqttClient;            // MQTT клиент
ESP8266WebServer server(80);        // Веб сервер
MicroDS18B20<SENSOR_PIN> sensor;    // Датчик температуры
const char *ap_ssid = "Chamber-01"; // Имя точки доступа и её пароль.
const char *ap_pass = "";           // Имя модуля в MQTT -> по имени точки доступа
float celsius;
bool sendInfoTopic = 0;
bool btnNew, btnOld;
// Web страницы
#include "page_script.h"
#include "page_css.h"
#include "page_favicon.h"
#include "page_index.h"
#include "page_net.h"
#include "page_termo.h"
#include "page_mqtt.h"
#include "page_sys.h"
// Таймеры
Ticker tmrBtnSet;   // Опрашиваем кнопку
Ticker tmrReleSet;  // Опрашиваем и переключаем реле по температуре
Ticker tmrTempRead; // Опрашиваем температурный датчик
// Ticker tmrMqttSent; // Отправка данных в топик
//
//=====Запуск и инициализация
void setup()
{
  Serial.begin(115200);
  Serial.println("");
  pinMode(RELEY_PIN, OUTPUT); // Пин реле
  pinMode(BTN_PIN, INPUT);    // Пин кнопки
  digitalWrite(RELEY_PIN, HIGH);
  tmrBtnSet.attach_ms(100, btnPress);
  tmrReleSet.attach_ms(100, releStatus);
  tmrTempRead.attach_ms(1500, getTempActual);
  // tmrMqttSent.attach_ms(60000, tmrTestSend);
  EEPROM.begin(512); // Определяем пространство EEPROM 512 Bytes для хранения данных
  ReadConfig();      // Чтение конфигурации из ЕПРОМ
  getTempActual();   // Читаем температуру с датчика
  if (config.mqtt_ssl_enable == 1)
  {                                     // Проверяем, что подключен TLS/SSL протокол
    mqttClient.setClient(ESPclientSsl); // MQTT клиент с шифрованием
  }
  else
  {
    mqttClient.setClient(ESPclient); // MQTT клиент без шифрования
  }
  webSyte();       // Загрузка сайта
  wifiConnected(); // Подключимся к WiFi
}
//=====Основной цикл программы ======
void loop()
{
  (ESP.wdtFeed());
  server.handleClient(); // Разрешаем HTTP серверу отвечать на запросы
  mqttClient.loop();
  if (sendInfoTopic == 1)
  {
    mqqtTopicSent(); // Отправляем данные в топик
  }
  sendInfoTopic = 0;
}
// ===== Веб сервер  =====
void webSyte()
{
  server.on("/", []() { // Главная страница
    server.send_P(200, "text/html", PAGE_index);
  });
  server.on("/favicon.ico", []() { // Страница с иконкой favicon
    server.send_P(200, "image/x-icon", PAGE_favicon, sizeof(PAGE_favicon));
  });
  // Страница WiFi
  server.on("/wifi.html", page_network_configuration_html);      // Страница. Всё, что ниже аналогично
  server.on("/web/wifivalues", load_network_configuration_html); // Загрузка переменных из  EEPROM на страницу
  server.on("/web/wifisave", save_network_configuration_html);   // Сохранение новых значений в EEPROM
  server.on("/web/conectstatus", status_network_values_html);
  server.on("/web/wifiscan", send_network_scan_html);
  // Страница термостата
  server.on("/termo.html", page_termostat_configuration_html);
  server.on("/web/termovalues", load_termostat_configuration_html);
  server.on("/web/termosave", save_termostat_configuration_html);
  server.on("/web/termoonoff", onoff_termostat_configuration_html);
  server.on("/web/termosetpage", setPageTempTimer);
  //  Страница MQTT
  server.on("/mqtt.html", page_mqtt_configuration_html);
  server.on("/web/mqttvalues", load_mqtt_configuration_html);
  server.on("/web/mqttsave", save_mqtt_configuration_html);
  // Страница настроек
  server.on("/system.html", page_system_configuration_html);
  server.on("/web/syssave", save_system_configuration_html);
  // CSS и скрипты
  server.on("/style.css", []()
            { server.send_P(200, "text/plain", PAGE_Style_css); });
  server.on("/microajax.js", []()
            { server.send_P(200, "text/plain", PAGE_microajax_js); });
  server.onNotFound([]()
                    {
    Serial.println("Page Not Found");
    server.send(400, "text/html", "Page not Found"); });
  server.begin();
}
// ===== Функция отправки сообщения в топик ======
void mqqtTopicSent()
{
  if (WiFi.status() == WL_CONNECTED)
  {
    if (celsius != 0) // Температура считана
    {
      if (!mqttClient.connected())
      { // Проверим подключение к MQTT серверу. Если нет подключимся.
        mqtt_connect();
      }
      mqttClient.publish("termo/sub/name", (String(ap_ssid).c_str()));
      mqttClient.publish("termo/sub/temp", (String(celsius).c_str()));
      mqttClient.publish("termo/sub/on", (String(config.tempOn)).c_str()); //
      mqttClient.publish("termo/sub/off", (String(config.tempOff)).c_str());
      mqttClient.publish("termo/sub/ip", (String(WiFi.localIP()[0]) + "." + String(WiFi.localIP()[1]) + "." + String(WiFi.localIP()[2]) + "." + String(WiFi.localIP()[3])).c_str());
      mqttClient.publish("termo/sub/reley", (String(digitalRead(RELEY_PIN) == 0 ? "1" : "0")).c_str());
      mqttClient.publish("termo/sub/status", (String(digitalRead(RELEY_PIN) == 0 ? "Включен" : "Выключен")).c_str());
      mqttClient.publish("termo/sub/err", ("Датчик доступен"));
      // Serial.println("topic Data sent");
    }
    else
    {
      mqttClient.publish("termo/sub/err", ("Ошибка датчика"));
      Serial.println("Temperature sensor error");
    }
  }
}
//===== Функция проверки входящих сообщений от сервера MQTT=====
void mqttСallback(char *topic, byte *payload, int length)
{
  String Text;
  // Serial.println("mqtt callback");
  for (int i = 0; i < length; i++)
    Text += ((char)payload[i]);
  if (String(topic) == "termo/pub/on")
  {
    config.tempOn = Text.substring(0, length + 1).toFloat();
    WriteFloatEEPROM(EETON, config.tempOn);
    EEPROM.commit();
  }
  if (String(topic) == "termo/pub/off")
  {
    config.tempOff = Text.substring(0, length + 1).toFloat();
    WriteFloatEEPROM(EETOFF, config.tempOff);
    EEPROM.commit();
  }
  if (String(topic) == "termo/pub/reley")
  { // Включение реле от MQTT
    if (payload[0] == 49)
      digitalWrite(RELEY_PIN, LOW);
    if (payload[0] == 48)
      digitalWrite(RELEY_PIN, HIGH);
  }
  if (String(topic) == "termo/pub/update")
  {
  }
  sendInfoTopic = 1;
}
//===== Функция подключения к WiFi и создания точки доступа ======
void wifiConnected()
{
  WiFi.mode(WIFI_STA);
  WiFi.begin(config.ssid.c_str(), config.password.c_str());
  for (int i = 0; i < 10; i++)
  {
    if (WiFi.status() == WL_CONNECTED)
    {
      Serial.print("Start WiFi client: ");
      Serial.println(WiFi.localIP().toString());
      return;
    }
    delay(1000);
  }
  WiFi.disconnect();  // Не смогли подключится, запустим точку доступа
  WiFi.mode(WIFI_AP); //
  WiFi.softAP(ap_ssid, ap_pass);
  Serial.println("Start Access Point");
  Serial.println("SSD: " + String(ap_ssid));
  Serial.print("AP address: ");
  Serial.println(WiFi.softAPIP());
}
//===== Подключение к MQTT серверу =======
bool mqtt_connect()
{
  if (WiFi.status() == WL_CONNECTED)
  {
    while (!mqttClient.connected())
    {
      mqttClient.setServer(config.mqtt_server.c_str(), config.mqtt_port); // Параметры сервера
      mqttClient.setCallback(mqttСallback);
      Serial.println("Attempting MQTT connection... ");
      ESPclientSsl.setFingerprint(config.mqtt_ssl.c_str()); // SSL отпечаток
      Serial.print("Connection ");
      if (mqttClient.connect(ap_ssid, config.mqtt_user.c_str(), config.mqtt_pass.c_str(), "termo/life", 1, true, "Не в сети"))
      {

        Serial.println("completed.");
        Serial.println("connected server:port  -> " + (config.mqtt_server) + ":" + (String)(config.mqtt_port));
        yield();
        mqttClient.publish("termo/life", "В сети", true);
        mqqtTopicLoad(); // Подпишемся на топик
      }
      else
      {
        Serial.print("failed, rc=");
        Serial.print(mqttClient.state());
        Serial.println(", try again after");
        Serial.flush();
        mqttClient.disconnect();
      }
      return mqttClient.connected();
    }
  }
  return true;
}
// ===== Подписываемся на топики =====
void mqqtTopicLoad()
{
  mqttClient.subscribe("termo/pub/on");
  mqttClient.subscribe("termo/pub/off");
  mqttClient.subscribe("termo/pub/reley");
  mqttClient.subscribe("termo/pub/update");
}
//===== Функция проверки нажатия кнопки =====
void btnPress()
{
  uint32_t tmr = 0;
  btnNew = digitalRead(BTN_PIN);
  if (btnNew == LOW && btnOld != btnNew && millis() - tmr >= _BTN_DEB_TIME)
  {
    Serial.println("Button press");
    tmr = millis();
    onoff_termostat_configuration_html(); // меняем данные реле на сайте
  }
  btnOld = btnNew;
}
// ===== Функция статуса реле =====
void releStatus()
{
  if (celsius != 0) // Температура считана
  {
    if (celsius <= config.tempOn)
    { //  если температура ниже включаем реле
      digitalWrite(RELEY_PIN, LOW);
    }
  }
  if (celsius >= config.tempOff)
  { // если выше или равна выключаем
    digitalWrite(RELEY_PIN, HIGH);
  }
}
// ===== Температурный датчик =====
void getTempActual()
{
  float old_celsius = celsius;
  if (sensor.readTemp())
    celsius = (sensor.getTemp());
  sensor.requestTemp();
  // Serial.println("Temp = " + String(celsius) + " t°");
  if (celsius != old_celsius)
  {
    sendInfoTopic = 1;
  }
}
```

В MQTT есть понятие QoS. Возможно, что изменение его уровня - то, что нужно.

Однако, MQTT в мониторинге - достаточно скользкий путь, как только будет перейдена граница “просто посмотреть на одну неважную метрику”.

Нет lwt передается сразу при подключении. Пытаться отправить его без связи это совершенно очевидный бред.
Задержка у тебя в while (!mqttClient.connected())
Пиши асинхронно, или в отдельную задачу, а еще лучше на esp32 потому что 8266 место на помойке на сегодня.

Вы имеете ввиду исправить вот тут с 1 на 0? Или что то другое?

      if (mqttClient.connect(ap_ssid, config.mqtt_user.c_str(), config.mqtt_pass.c_str(), "termo/life", 1, true, "Не в сети"))

Спасибо, что пояснили. Я примерно догадывался что из за цикла, но так как время совпадало с описанным, решил, что из за lwt.
По поводу Пиши асинхронно, что подразумевается?
Использовать какую другую библиотеку? Интернет по поводу асинхронный MQTT выдает только ссылку на библиотеку, вот эту ESPAsyncTCP Я пытался использовать её, но заставить ее работать с SSL не смог несмотря на примеры.
Ещё рекомендуют вынести циклы в счетчики с условием и опять же использовать таймеры.
Таймер я так-же пробовал, esp сразу уходит в софтресет. При отключении софт уходит в хардресет, так как, я понял время выполнения превышает установленные значения. Попробую счетчик как рекомендует на сайте AlexGyver.