Добрый день.
Как пишут, какие модули не дай новичку, все равно получится метеостанция(с).
По проблеме.
Установив свой первый собранный модуль в колбасный шкаф, для контроля температуры, я понял, что температуру можно не только смотреть дистанционно, но и управлять ей. После чего решил собрать термостат. Сперва хотел собрать его на 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;
}
}
```