Очередной умный дом (или как потерять 2 месяца из-за одной глупой ошибки)

от автора

Предыстория

я, не знающий своей глупости и наивности
я, не знающий своей глупости и наивности

Начну я свою эпопею с небольшой предыстории. Надо было мне сделать проект для 9 класса (да, сейчас, чтобы тебя допустили до экзаменов, надо сделать проект и защитить его).

Решил практически сразу, что это будет умный дом (хотя даже воробей умнее его), ведь давно хотел сделать что-то такое, но руки так и не доходили, а тут подвернулся отличный случай + немного выпендриться можно.

Данный пост — мой переделанный диплом, да и вообще первая попытка в написании таких вещей )

План

Для успешной и продуктивной работы нужен план, для себя я составил его примерно таким:

  1. Идея и идеология умного дома.

  2. Железо для построения умного дома.

  3. Код для каждой из частей умного дома.

Принципе, для начала это более чем достаточно . Теперь можно разобрать каждый пункт отдельно.

Идея и идеология умного дома

Посидев несколько дней и обдумав все идеи, в моей голове всплыла утопическая идея такой системы, которая не нуждалась бы в базовом модуле, а каждый такой модуль будет полностью автономен. У системы такого типа, в отличии от той, что использует в своей основе базовый модуль (примером может быть умный дом xiaomi), имеется несколько плюсов и минусов.
Начнем с плюсов. Главный плюс — отказоустойчивость, даже если один из управляющих модулей выйдет из строя, датчики все равно будут отправлять данные пользователю. Второй же плюс — возможность связи модулей на больших расстояниях.

А теперь про минусы: самый значительный минус — автономности, ведь на то, чтобы проснуться, подключиться к интернету, отправить данные и уснуть тратиться больше энергии, чем на то, чтобы передать данные по радиоканалу. Следующий минус состоит в необходимости наличия интернета для работы каждого датчика, иначе он просто не сможет отправить данные.
В итоге я составил список функционала, который умный дом должен реализовывать в ИДЕАЛЕ:

  1. Автономные модули.

  2. Взаимозаменяемость модулей.

  3. Масштабируемость системы.

При работе, выяснилось что времени до дедлайна не так уж и много, так что я решил урезать свой список, оставив в нём только автономность и масштабируемость, хотя взаимозаменяемость добавляется в итоговый проект довольно просто.

И да, забыл сказать, модуль — единичное устройство системы умного дома, будь то wi-fi розетка или датчик температуры.

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

Микроконтроллер

Микроконтроллером, который является основой для каждого модуля умного дома, я выбрал всем хорошо знакомый, даже немного заезженный ESP-12F . Он хорош своими возможностями, в которые входит функция WI-FI (как соединение, так и точка доступа) и отличная производительность, которой с лихвой хватило на все мои задумки, а цена за штуку не превышает 100 рублей в Китае, если брать сразу 10 штук.

распиновка и внутреннее устройство esp12F
распиновка и внутреннее устройство esp12F

Вообще есть несколько версий esp12, самая первая — esp12Е, она по всем фронтам уступает более новому esp12F. Основные их отличия в форме антенны, у F-версии она сделана более удачно, и в размере Flash памяти: у E-версии в большинстве случаев она составляет 1МБ, в то время как у F-версии она уже 4МБ. Также, вроде как различия в внутренней компоновки компонентов, которая удачнее в F-версии. Еще можно вспомнить самую новую версию — esp12S, которая практически идентична (если я правильно разобрался) своей начинкой, но сделана в более компактном и удобном корпусе для smd пайки.

три версии esp12 рядом
три версии esp12 рядом

Устройство WI-FI розетки

Теперь можно рассмотреть каждый модуль, всего их будет 3.

Что из себя вообще представляет умная (wifi) розетка — устройство, включаемый в обычную розетку и которое может управлять (как автоматически, так и в ручном режиме) нагрузкой, включенную в это устройство.

Есть два основных способа реализовать это:

1 способ — использовать реле, вместе с транзисторным ключом (реле 5В, а микроконтроллер питается от 3.3В).
2 способ — использовать симистор.

Оба способа хороши, Реле более простое и меньше шансов что оно сгорит, а симистор более дешевый, быстрый, не имеет в себе никаких механических частей, компактней и может пропускать через себя более большие токи.

симистор и реле
симистор и реле

Я решил все же использовать реле, ведь оно было уже куплено и схема была отработана, а с симистор пришлось бы разбираться.

принципиальная схема
принципиальная схема

Как видно на схеме, для правильной работы реле, ему нужен транзисторный ключ, ведь esp12F питается от 3.3В, а реле на 5В, да и такую нагрузку лучше не включать прямо в пин микроконтроллера, может сгореть.

Как AC/DC преобразователь может использоваться любой блок питания без корпуса, я использовал готовый для таких целей блок питания с алика, с 220В переменного на 5В постоянного и 1А на выходе, что даже избыточно, ведь вся схема в работе потребляет не более 0,3-0,2А. Для транзисторного ключа идеально подошел кт315, который уже хорошо известен многим радиолюбителям.

Как было сказано раньше, esp12F питается от 3.3В, а блок питания выдает все 5В, для того, чтобы МК не сгорел, для его питания, сразу после блока питания стоит понижающий линейный преобразователь AMS1117.

печатная плата для wi-fi розетки
печатная плата для wi-fi розетки

Для корпуса были куплены обычная розетка и вилка, которые впоследствии были уничтожены (разобраны на части), а основной корпус был напечатан на 3д принтере.

корпус в реальности и корпус в fusion360
корпус в реальности и корпус в fusion360
внутреннее строение модуля
внутреннее строение модуля

Печатная плата сделана ЛУТом, по рецепту Гайвера, только под утюгом лучше держать около 10 минут, а то краска некорректно перейдет на стеклотекстолит.

плата с стороны компонентов
плата с стороны компонентов
неудачные платы
неудачные платы

Конечно, это не первая версия платы, да и вообще, это мой первый опыт в создании печатных плат самому, до неё было 5 неудачных попыток развести печатку, где-то не так отзеркалил, где-то не так развел, где-то плохо краска перенеслась и дорожки получились плохими.

Устройство модулей датчиков

Оба модулей датчиков имеют практически идентичное внутреннее устройство, различия только в самих датчиках, в одном это DHT11, а в другом DS18B20 и их обвязках.

Модули выполняют очень простую функцию: отправлять показания с датчиков по MQTTT.

принципиальная схема модуля для датчика DS18B20(для DHT11 отличие будет только в резисторе, который будет не 4.7К,а 10К)
принципиальная схема модуля для датчика DS18B20(для DHT11 отличие будет только в резисторе, который будет не 4.7К,а 10К)
печатная плата для модуля датчика(зеленое на плате - проводки на другой стороне)
печатная плата для модуля датчика(зеленое на плате — проводки на другой стороне)

В этот раз корпус был полностью напечатан на 3д принтере, ведь даже так, себестоимость корпусов дешевле, чем покупать уже готовые коробочки, которые придется дорабатывать!

корпус в реале и корпус в fusion 360
корпус в реале и корпус в fusion 360
корпус без крышки
корпус без крышки

Питание модуля идет от 3 батареек типа АА.

Прошивка модулей

Наверное самый интересный и самый долгий пункт….

Сначала я хотел использовать RTOS-SDK от ESP, но понял что уйдет много времени чтобы разобраться в нем, поэтому я остановился на Arduino, хоть и от FreeRTOS пришлось отказаться.

Но, в принципе, для моего решения RTOS не нужна.

Для прошивки можно использовать любой программатор, совместимый с esp, но главное не перепутать перемычки (если они есть конечно) логического уровня и не поставить их на 5в, иначе esp12 может попросту сгореть.

Подключал я по этой схеме… и вот как раз таки та загвоздка, из-за которой я и потратил 2 месяца в пустую…забыл минус микроконтроллера к минусу программатора соединить! Оказывается, не дурак эту схему начертил, а дурак её пытался повторить). Пока искал где проблема, переделал много плат, заказал новые микроконтроллеры, подумал что с ними проблема, новый программатор взял, перерыл форумы с этой проблемой…

Ну да ладно, главное что все заработало.

MQTT

Как я и говорил раньше, решил использовать MQTT для своих целей. Сам протокол состоит из нескольких сущностей:

  • Брокер — сервер, который управляет передачей данных, создает топики.

  • Топик — канал передачи данных, на который может подписываться устройство «подписчик» и в который может публиковать данные устройство «издатель».

  • Подписчик — устройство, подписывающееся на топик и получаемое из этого топика все данные, публикующиеся в него.

  • Издатель — устройство, публикующее в топик данные .

Примерная схема передачи данных по протоколу MQTT. Разные цвета - разные топики.
Примерная схема передачи данных по протоколу MQTT. Разные цвета — разные топики.

Одно и тоже устройство может быть как подписчиком, так и издателем одновременно, причем в разных топиках.

Изначально была идея использовать брокер от yandex iot, но он оказался менее удобным для моего проекта, хотя можно сразу прикрепить сервер обработки и хранения данных, но это стоило бы довольно дорого, поэтому выбрал wqtt, который стоит всего 300р в год и можно добавить поддержку Яндекс Алисы).

Настройка модулей

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

Давайте разберем мою реализацию режима настройки.

Для начала, нам нужно подключить нужные библиотеки:

#include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <EEPROM.h> #include <PubSubClient.h>

Следом идут определение константан для точки доступа самого модуля, wifi к которому она подключается и к MQTT:

#define RELEPIN 4 // пин МК с реле  const char *ssid_ap = "Rozetka_Setup";  //имя точки доступа модуля const char *password_ap = "12345678";  //пароль точки доступа модуля const char *ID = "rele_1";// типовое, статичное ID модуля  String ssid = "";  //имя wifi String password = "";  //пароль wifi String device_id; // пользовательское ID модуля bool setup_mode; // true - первичная настройка модуля, false - основная работа модуля bool rele;// true - реле включено, false - реле выключено  const char *mqtt_server = "M5.WQTT.RU"; // Имя сервера MQTT const int mqtt_port = 2602; // Порт для подключения к серверу MQTT const char *mqtt_user = "user"; // Логин от серверa const char *mqtt_pass = "pass"; // Пароль от сервера  IPAddress local_ip(192, 168, 1, 1);//IP для точки доступа модуля IPAddress gateway(192, 168, 1, 1);//гейт для точки доступа модуля IPAddress subnet(255, 255, 255, 0);//маска для точки доступа модуля  IPAddress local_ip_2(192, 168, 0, 250);//IP для WIFI IPAddress gateway_2(192, 168, 0, 1);//гейт для WIFI IPAddress subnet_2(255, 255, 255, 0);//маска для WIFI  ESP8266WebServer server(80);// server для настройки  WiFiClient wclient; PubSubClient client(wclient, mqtt_server, mqtt_port);

Setup блок программы:

pinMode(RELEPIN, OUTPUT); Serial.begin(115200); EEPROM.begin(256);//подключаем EEPROM setup_mode = EEPROM.read(45);// читаем из EERPOM текущий режим rele = EEPROM.read(60);// читаем из EERPOM текущее состояние реле EEPROM.end();//отключаем EEPROM if (rele) digitalWrite(RELEPIN, HIGH); else digitalWrite(RELEPIN, LOW);  delay(1000); WiFi.softAP(ssid_ap, password_ap);//настраиваем точку доступа(название и пароль) WiFi.softAPConfig(local_ip, gateway, subnet);//настраиваем точку доступа(ip для подключения) delay(100); server.on("/", handle_OnConnect);//handle для первой начальной страницы server.on("/end_setup", handle_EndSetup);//handle для окончания настройки server.on("/action_page", handleForm);//handle для выбора продолжить или закончить настройку server.onNotFound(handle_NotFound); server.begin();//запуск сервера Serial.println("HTTP server started");

Почему я использую подключение по через браузер по типу «http://192.168.1.1/«, я попросту не нашел альтернативы . Конечно, есть технология Mdns, которая позволяет обращаться через браузер просто по имени «http://esp8266/«, но её не поддерживают android устройства ! Я не понимаю почему, но именно так, гугл не может добавить поддержку Mdns, хотя у эпл она давно уже есть…

Как работают hadle ? Изначально мы переходим по адресу http://192.168.1.1/, а последняя «/» и является своеобразным «указателем» на определенный handle, а сам по себе любой handle является программой, который мы настраиваем. Например адрес «http://192.168.1.1/end_setup» указывает на handle, который завершает настройку модуля.

Рассмотрим сами handle:

данный Handle передает пользователю веб-страничку для настройки модуля-розетки, для модулей -датчиков данная страничка будет немного иная —

void handle_OnConnect() {// handle, который отправляет начальную страницу   server.send(200, "text/html", SendHTML()); }  String SendHTML() {//отправка HTML страницы для настройки датчика- розетки   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/> <html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";   ptr += ".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";   ptr += ".button-on {background-color: #1abc9c;}\n";   ptr += ".button-on:active {background-color: #16a085;}\n";   ptr += ".button-off {background-color: #34495e;}\n";   ptr += ".button-off:active {background-color: #2c3e50;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<h1>Настройка WIFI</h1>\n";   ptr += "<h3>Укажите название и пароль от нужной wifi сети</h3>\n";   ptr += "<form action=\"/action_page\">";   ptr += "Название:<br>";   ptr += "<input type=\"text\" name=\"WIFI_NAME\" value=\"тест_название\">";   ptr += "<br>";   ptr += "Пароль:<br>";   ptr += "<input type=\"text\" name=\"WIFI_password\" value=\"123456789\">";   ptr += "<br>";   ptr += "Название модуля:<br>";   ptr += "<input type=\"text\" maxlength=\"14\" name=\"DEVICE_ID\" value=\"розетка\"><br>";   ptr += "Local_ip:<br>";   ptr += "<input type=\"text\" name=\"LOCAL_IP\" value=\"192.168.0.250\">";   ptr += "<br>";   ptr += "Gateway:<br>";   ptr += "<input type=\"text\" name=\"GATEWAY\" value=\"192.168.0.1\">";   ptr += "<br>";   ptr += "<br><br>";   ptr += "<input type=\"submit\" value=\"Закончить\">";   ptr += "</form>";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }  String SendHTML() {//Отправка HTML страницы для настройки модуля - датчика   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/> <html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";   ptr += ".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";   ptr += ".button-on {background-color: #1abc9c;}\n";   ptr += ".button-on:active {background-color: #16a085;}\n";   ptr += ".button-off {background-color: #34495e;}\n";   ptr += ".button-off:active {background-color: #2c3e50;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<h1>Настройка WIFI</h1>\n";   ptr += "<h3>Укажите название и пароль от нужной wifi сети</h3>\n";   ptr += "<form action=\"/action_page\">";   ptr += "Название:<br>";   ptr += "<input type=\"text\" name=\"WIFI_NAME\" value=\"тест_название\">";   ptr += "<br>";   ptr += "Пароль:<br>";   ptr += "<input type=\"text\" name=\"WIFI_password\" value=\"123456789\">";   ptr += "<br>";   ptr += "Тайминг отправки(в секундах):<br>";   ptr += "<input type=\"text\" name=\"TIMING\" value=\"1\">";   ptr += "<br>";   ptr += "Название модуля:<br>";   ptr += "<input type=\"text\" maxlength=\"10\" name=\"DEVICE_ID\" value=\"datc_temp\"><br>";   ptr += "<br><br>";   ptr += "<input type=\"submit\" value=\"Закончить\">";   ptr += "</form>";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }
Меню для настройки: слева - для модуля-датчика, справа - для модуля-розетки
Меню для настройки: слева — для модуля-датчика, справа — для модуля-розетки

По нажатии кнопки «закончить» в действие вступает следующий handle —

void handleForm() {//версия для модуля - розетки   ssid = server.arg("WIFI_NAME");   password = server.arg("WIFI_password");   device_id = server.arg("DEVICE_ID");   local_ip_2.fromString(server.arg("LOCAL_IP"));   gateway_2.fromString(server.arg("GATEWAY"));   write_string_EEPROM(200, server.arg("LOCAL_IP"));   write_string_EEPROM(220, server.arg("GATEWAY"));   Serial.print("WIFI: ");   Serial.println(ssid);    Serial.print("password: ");   Serial.println(password);    server.send(200, "text/html", SendEndHTML()); //Send web page }  void handleForm() {//версия для модуля - датчика   ssid = server.arg("WIFI_NAME");   password = server.arg("WIFI_password");   device_id =  server.arg("DEVICE_ID");   tmi = server.arg("TIMING").toInt();   Serial.print("WIFI: ");   Serial.println(ssid);    Serial.print("password: ");   Serial.println(password);    server.send(200, "text/html", SendEndHTML()); //Send web page }  String SendEndHTML() {   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/><html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<a href='/'><h1>Вернуться к настройки</h1></a>\n";   ptr += "<a href='/end_setup'><h1>Продолжить</h1></a>\n";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }
форма выбора
форма выбора

При нажатии на кнопку «Вернуться к настройки» пользователь вернется на первую страницу, а при нажатии на кнопку «продолжить» сработает следующий handle, который перезапишет в EEPROM ssid и пароль от WIFI, переведет модуль в режим нормальной работы —

void handle_EndSetup() {   write_string_EEPROM(0, ssid);   write_string_EEPROM(20, password);   EEPROM.begin(256);   EEPROM.write(45, false);   setup_mode = false;   EEPROM.commit();   EEPROM.end();   WiFi.softAPdisconnect(true);   ESP.reset(); }

Немного про саму запись в EEPROM. Для записи и чтения строк в него используются две функции —

void write_string_EEPROM (int Addr, String Str) {//Запись строки в EEPROM //Addr - начальный байт, str - строка. Строка не может быть больше 15 символов   byte lng = Str.length();   EEPROM.begin (256);   EEPROM.write(Addr , lng);   unsigned char* buf = new unsigned char[15];   Str.getBytes(buf, lng + 1);   Addr++;   for (byte i = 0; i < lng; i++) {     EEPROM.write(Addr + i, buf[i]);     delay(10);   }   EEPROM.commit();   EEPROM.end(); }  char read_string_EEPROM (int Addr) {// Чтение строки из EEPROM //Addr - начальный байт   EEPROM.begin(256);   byte lng = EEPROM.read(Addr);   char buf = new char[15];   Addr++;   for (byte i = 0; i < lng && i < 15; i++) buf[i] = char(EEPROM.read(i + Addr));   buf[lng] = '\x0';   EEPROM.end();   return buf; } 

Вообще, хорошо бы вместо EEPROM использовать FS.h (файловую систему),но тогда до меня это не дошло.

Режим нормально работы (модуль — розетка)

Теперь, когда наши модули настроены, можно рассмотреть их основные функции.

Рассмотрим SETUP, который запускается при старте модуля в данном режиме:

pinMode(RELEPIN, OUTPUT); Serial.begin(115200); EEPROM.begin(256);//подключаем EEPROM setup_mode = EEPROM.read(45);// читаем из EERPOM текущий режим rele = EEPROM.read(60);// читаем из EERPOM текущее состояние реле EEPROM.end();//отключаем EEPROM if (rele) digitalWrite(RELEPIN, HIGH); else digitalWrite(RELEPIN, LOW);  local_ip_2.fromString(String(read_string_EEPROM(200)));//читаем IP из eeprom      gateway_2.fromString(String(read_string_EEPROM(220)));//читаем гейт из eeprom      WiFi.begin(read_string_EEPROM(0), read_string_EEPROM(20));     int sm = 0;     while (WiFi.status() != WL_CONNECTED) {       delay(500); sm++;       Serial.print(".");       if (sm > 120) {         handle_ReturnSetup();       }     }     WiFi.config(local_ip_2, gateway_2, subnet_2);     Serial.println("WiFi connected");     Serial.print("IP address: ");     Serial.println(WiFi.localIP());     read_auto();     server.on("/", handle_OnConnect_2);     server.on("/select_auto", handle_SelectAuto);     server.on("/rele_auto", handle_ReleAuto);     server.on("/delete_page", handle_Delete);     server.on("/return_setup", handle_ReturnSetup);     server.on("/control_menu", handle_ControlMenu);     server.on("/rele_off", handle_ReleOff);     server.on("/rele_on", handle_ReleOn);     server.on("/auto_off", handle_AutoOff);     server.on("/auto_on", handle_AutoOn);     server.onNotFound(handle_NotFound);     server.begin();     Serial.println("HTTP server started");      if (client.connect(MQTT::Connect(ID).set_auth(mqtt_user, mqtt_pass))) {       Serial.println("Connected to MQTT server");       client.subscribe("/datk"); // подписываемся на топик с данными датчиков       //client.subscribe("/cmd"); // подписываемся на топик с командами       client.set_callback(callback);     } else {       Serial.println("Could not connect to MQTT server");     } 

Обработчик MQTT:

 void perek(bool per) { //per = true = прямой режим, per = true = обратный режим   EEPROM.begin(256);   if (auto_mode) {     if (per) {       rele = 0;       digitalWrite(RELEPIN, LOW);     }     else {       rele = 1;       digitalWrite(RELEPIN, HIGH);     }   }   else {     if (per) {       rele = 1;       digitalWrite(RELEPIN, HIGH);     }     else {       rele = 0;       digitalWrite(RELEPIN, LOW);     }   }   EEPROM.write(60, rele);   EEPROM.commit();   EEPROM.end(); }  void callback(const MQTT::Publish& pub) {   Serial.print(pub.topic()); // выводим в сериал порт название топика   Serial.print(" => ");   Serial.print(pub.payload_string()); // выводим в сериал порт значение полученных данных   String payload = pub.payload_string();   if (String(pub.topic()) == "/datk") // проверяем из нужного ли нам топика пришли данные   {     String tmpstr = "", namest, znachs;     int datat;     int mod = 0;     bool tr = false;     for (int i = 0 ; i < payload.length(); i++) {       if (payload[i] != '#') tmpstr += payload[i]; else {         switch (mod) {           case 0:             namest = tmpstr;             break;           case 1:             datat = tmpstr.toInt();             break;           case 2:             znachs = tmpstr;             break;         }         tmpstr = "";         mod++;       }     }     for (int i = 0; i < lng && !tr; i++) {       if (names[i] == namest) {         data[i] = datat;         tr = true;       }     }     if (!tr) {       lng++;       names[lng - 1] = namest;       data[lng - 1] = datat;       znach[lng - 1] = znachs;     }     if (auto_stat && namest == auto_name) {       switch (auto_oper) {         case 0:           if (auto_data > datat) perek(true);           if (auto_data < datat) perek(false);           break;         case 1:           if (auto_data < datat) perek(true);           if (auto_data > datat) perek(false);           break;         case 2:           if (auto_data = datat) perek(true);           if (auto_data != datat) perek(false);           break;       }     }   } 

Функция «Perek» используется для правильного переключения реле.

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

Первый же handle:

void handle_OnConnect_2() {   server.send(200, "text/html", SendMenuHTML()); }  String SendMenuHTML() {   String ptr = "<!DOCTYPE html> <html lang='ru-RU'><head><meta charset='UTF-8'/><html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";   ptr += ".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";   ptr += ".button-on {background-color: #1abc9c;}\n";   ptr += ".button-on:active {background-color: #16a085;}\n";   ptr += ".button-off {background-color: #34495e;}\n";   ptr += ".button-off:active {background-color: #2c3e50;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<h1>WIFI меню</h1>\n";   ptr += "<h3>Выберете пункт меню:</h3>\n";   ptr += "<br><input type=\"button\" value=\"Вернуться в режим настройки\" onClick=\"document.location = '/return_setup'\" /><br>";   ptr += "<br><input type=\"button\" value=\"Панель управления\" onClick=\"document.location = '/control_menu'\" /><br>";   ptr += "<br><input type=\"button\" value=\"Автоматическое управление розеткой\" onClick=\"document.location = '/rele_auto'\" /><br>";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }

Само меню выглядит так:

Кнопка «Вернуться в режим настройки», как понятно из названия, отправляет модуль обратно в режим настройки.
Кнопка «Панель управления» открывает пользователю меню управления.
Кнопка «Панель управления» открывает пользователю меню автоматического управления розеткой.

Возвращение к меню управления происходит через handle:

void handle_ReturnSetup() {   EEPROM.begin(256);   EEPROM.write(45, true);   setup_mode = true;   EEPROM.commit();   EEPROM.end();   ESP.reset(); }

Для перехода к меню управления используется handle (Больше handle богу handle!):

void handle_ControlMenu() {   server.send(200, "text/html", SendControlMenuHTML()); }  String SendControlMenuHTML() {   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/> <html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px; } h3 {color: #444444;margin-bottom: 50px;} h4 {color: #444444;margin-bottom: 20px;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<h1>Панель управления</h1>\n";   ptr += "<br><input type=\"button\" value=\"Вернуться в меню\" onClick=\"document.location = '/'\" /><br>";   if (rele) {     ptr += "<br><input type=\"button\" value=\"Включить розетку\" onClick=\"document.location = '/rele_off'\" /><br>";   }   if (!rele) {     ptr += "<br><input type=\"button\" value=\"Выключить розетку\" onClick=\"document.location = '/rele_on'\" /><br>";   }   ptr += "<h3>Датчики:</h3>\n";   //ptr += "<h4>Датчики:</h4>\n";   for (int i = 0 ; i < lng; i++) {     ptr += "<h4>" + names[i] + " : " + data[i] + " " + znach[i] + "</h4>";   }   ptr += "<form action=\"/delete_page\">";   ptr += "Название:  ";   ptr += "<input type=\"text\" name=\"DELETE_NAME\" value=\"модуль\">   ";   //ptr += "<br><br>";   ptr += "<input type=\"submit\" value=\"удалить\">";   ptr += "</form>";   ptr += "<br><input type=\"button\" value=\"обновить\" onClick=\"document.location = '/control_menu'\" /><br>";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }
Панель управления
Панель управления

Рассмотрим элементы управления сверху-вниз. В самом верху находится кнопка возвращения в первоначальное меню, следом идет управления реле/розеткой, меняющее свое значение в зависимости от состояние реле, и в зависимости от него используются один из этих handle:

void handle_ReleOff() {//выключание реле   rele = 0;   digitalWrite(RELEPIN, LOW);   EEPROM.begin(256);   EEPROM.write(60, 0);   EEPROM.commit();   EEPROM.end();   server.send(200, "text/html", SendControlMenuHTML()); }  void handle_ReleOn() {//включение реле   rele = 1;   digitalWrite(RELEPIN, HIGH  );   EEPROM.begin(256);   EEPROM.write(60, 1);   EEPROM.commit();   EEPROM.end();   server.send(200, "text/html", SendControlMenuHTML()); }

Далее список датчиков и их показаний, которые можно удалить в следующей форме. Список датчиков обновляется по мере поступления данных, обновление страницы идет с помощью кнопки «обновить».

Удаление датчиков идет с помощью handle:

void handle_Delete() {   String delname = server.arg("DELETE_NAME");   bool tr = false;   for (int i = 0 ; i < lng; i++) {     if (tr) {       names[i - 1] = names[i];       data[i - 1] = data[i];       znach[i - 1] = znach[i];     }     else if (names[i] == delname) {       tr = true;     }   }   if (tr) lng--;   server.send(200, "text/html", SendControlMenuHTML()); }

Программа ищет имя данного датчика и удаляет его, перемещая список на 1 пункт ниже, после найденного элемента.

Поднимемся на уровень выше и перейдем к настройки автоматического управления розеткой.
Сам handle, отвечающий за отправку данной веб-страницы пользователю выглядит так:

void handle_ReleAuto() {   server.send(200, "text/html", SendControlReleAutoHTML()); }  String SendControlReleAutoHTML() {   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/> <html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px; } h3 {color: #444444;margin-bottom: 50px;} h4 {color: #444444;margin-bottom: 20px;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<h1>Настройка автоматического управления розетки </h1>";   if (auto_stat) {     ptr += "<h3>Автоматическое управления включено</h3>";   }   else {     ptr += "<h3>Автоматическое управления выключено</h3>";   }   ptr += "<h3>Текущее условие:" + auto_name + " ";   switch (auto_oper) {     case 0 : ptr += "> ";       break;     case 1 : ptr += "< ";       break;     case 2 : ptr += "= ";       break;   }   if (auto_mode)     ptr += String(auto_data) + " выключать </h3>";   else     ptr += String(auto_data) + " включать </h3>";   ptr += "<h3>Выберете датчик и параметр для него:</h3>\n";   ptr += "<form action=\"/select_auto\">";   ptr += "<select size=\"1\" name=\"DAT_NAME\">";   for (int i = 0 ; i < lng; i++) {     ptr += "<option value=\"" + String(i) + "\">" + names[i] + "</option>";   }   ptr += "</select>  ";   ptr += "<select size=\"1\" name=\"OPER\">";   ptr += "<option value=0>></option>";   ptr += "<option value=1><</option>";   ptr += "<option value=2>=</option>";   ptr += "</select>  ";   ptr += "</select>  ";   ptr += "<input type=\"text\" name=\"DATA_P\" value=\"10\">";   ptr += "<select size=\"1\" name=\"ON_OFF\">";   ptr += "<option value=0>Включать</option>";   ptr += "<option value=1>Выключать</option>";   ptr += "</select>  ";   //ptr += "<br><br>";   ptr += "<input type=\"submit\" value=\"настроить\">";   ptr += "</form>";   ptr += "<br><input type=\"button\" value=\"обновить\" onClick=\"document.location = '/rele_auto'\" /><br>";   if (auto_stat) {     ptr += "<br><input type=\"button\" value=\"выключить автопереключение\" onClick=\"document.location = '/auto_off'\" /><br>";   }   else {     ptr += "<br><input type=\"button\" value=\"включить автопереключение\" onClick=\"document.location = '/auto_on'\" /><br>";   }   ptr += "<br><input type=\"button\" value=\"Вернуться в меню\" onClick=\"document.location = '/'\" /><br>";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }

В начало программы добавляются константы:

String auto_name = "example"; int auto_data = 10; int auto_oper = 0; bool auto_stat = false;// false - не работает, true - работает bool auto_mode = false; //false - включать, true - выключать  String names[10];// массив имен модулей String znach[10];// массив едениц измерения модулей int data[10];// массив данных модулей int lng = 0;//используемая длина
Меню автоматического управления
Меню автоматического управления

Рассмотрим также сверху-вниз, пропуская название сверху. Сначала нам дается информация о том, включено или нет авто управление. Ниже показывается текущее условие. Чтобы перенастроить модуль надо выбрать в форме ниже модуль, условие, значение и действия, а по окончанию нажать кнопку «настроить». Кнопка «обновить» служит для обновления списка датчиков.

Настройка происходит также через handle:

void handle_SelectAuto() {   auto_name = names[server.arg("DAT_NAME").toInt()];   auto_oper = server.arg("OPER").toInt();   auto_data = server.arg("DATA_P").toInt();   auto_mode = server.arg("ON_OFF").toInt();   write_string_EEPROM(70, auto_name);   write_string_EEPROM(110, server.arg("OPER"));   write_string_EEPROM(90, server.arg("DATA_P"));   EEPROM.begin(256);   EEPROM.write(130, auto_mode);   EEPROM.commit();   EEPROM.end();   server.send(200, "text/html", SendControlReleAutoHTML()); }

Включение/выключение авто управление происходит по типу выключения/выключения реле:

void handle_AutoOff() {//отключение авто управления   auto_stat = 0;   server.send(200, "text/html", SendControlReleAutoHTML()); }  void handle_AutoOn() {//включение авто управления   auto_stat = 1;   server.send(200, "text/html", SendControlReleAutoHTML()); } 

Ну и последняя кнопка возвращает обратно в меню.

Режим нормально работы (модуль — датчик)

Для обоих датчиков смысл данного режима одинаков, различия только самих датчиках и в способе обращения к ним.

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

Вся программа содержится в блоке SETUP, рассмотрим сначала датчик на DHT11:

#include "DHT.h"//библиотека DHT  #define DHTTYPE DHT11 uint8_t DHTPin = 5;  int tempC = 0; int humC = 0; int tmi = 10;// тайминг отправки данных DHT dht(DHTPin, DHTTYPE);   void setup() {   pinMode(DHTPin, INPUT);   Serial.begin(115200);   EEPROM.begin(256);   setup_mode = EEPROM.read(85);   EEPROM.end();   device_id = read_string_EEPROM(40);   if (!setup_mode) {//режим нормально работы     dht.begin();     tmi = String(read_string_EEPROM(200)).toInt(); //считываем тайминг отправки данных     local_ip.fromString(String(read_string_EEPROM(200)));     gateway.fromString(String(read_string_EEPROM(220)));     WiFi.begin(read_string_EEPROM(0), read_string_EEPROM(20));     int sm = 0;     while (WiFi.status() != WL_CONNECTED) {       delay(500); sm++;       Serial.print(".");       if (sm > 120) {         ReturnSetup();       }     }          Serial.println("WiFi connected");     tempC = (int)dht.readTemperature(); //считывание температуры в цельсиях   humC = (int)dht.readHumidity(); //считывание влажности воздуха     if (client.connect(MQTT::Connect(ID).set_auth(mqtt_user, mqtt_pass))) {       Serial.println("Connected to MQTT server");     } else {       Serial.println("Could not connect to MQTT server");     }     client.publish("/datk", device_id+"_t" + "#" + String(tempC) + "#градусов#");     client.publish("/datk", device_id+"_hm" + "#" + String(humC) + "#влажности#");     delay(100);     ESP.deepSleep(tmi * 1000000);//модуль засыпает на определенное время   }   else {//режим настройки     delay(1000);     WiFi.softAP(ssid_ap, password_ap);     WiFi.softAPConfig(local_ip, gateway, subnet);     delay(100);     server.on("/", handle_OnConnect);     server.on("/end_setup", handle_EndSetup);     server.on("/action_page", handleForm);     server.onNotFound(handle_NotFound);     server.begin();     Serial.println("HTTP server started");   } }

Теперь SETUP для модуля с ds18b20:

#include <DallasTemperature.h>//библиотека ds18b20  #define ONE_WIRE_BUS 5 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature DS18B20(&oneWire); char temperatureCString[7]; int tempC = 0; int tmi = 10;//тайминг отправки данных  void getTemperature()//считывание температуры с датчика {   do {     DS18B20.requestTemperatures();     tempC = DS18B20.getTempCByIndex(0);     dtostrf(tempC, 2, 2, temperatureCString);     delay(100);   } while (tempC == 85.0 || tempC == (-127.0)); }  void setup() { 	Serial.begin(115200);   EEPROM.begin(256);   setup_mode = EEPROM.read(85);   EEPROM.end();   device_id = read_string_EEPROM(40);   if (!setup_mode) {//режим нормально работы     DS18B20.begin();     tmi = String(read_string_EEPROM(200)).toInt(); //считываем тайминг отправки данных     local_ip.fromString(String(read_string_EEPROM(200)));     gateway.fromString(String(read_string_EEPROM(220)));     WiFi.begin(read_string_EEPROM(0), read_string_EEPROM(20));     int sm = 0;     while (WiFi.status() != WL_CONNECTED) {       delay(500); sm++;       Serial.print(".");       if (sm > 120) {         ReturnSetup();       }     }     getTemperature();//считываем температуру     Serial.println("WiFi connected");     if (client.connect(MQTT::Connect(ID).set_auth(mqtt_user, mqtt_pass))) {       Serial.println("Connected to MQTT server");     } else {       Serial.println("Could not connect to MQTT server");     }     client.publish("/datk", device_id + "#" + String(tempC) + "#градусов#");м;     delay(100);     ESP.deepSleep(tmi * 1000000);//модуль засыпает на определенное время   }   else {//режим настройки     delay(1000);     WiFi.softAP(ssid_ap, password_ap);     WiFi.softAPConfig(local_ip, gateway, subnet);     delay(100);     server.on("/", handle_OnConnect);     server.on("/end_setup", handle_EndSetup);     server.on("/action_page", handleForm);     server.onNotFound(handle_NotFound);     server.begin();     Serial.println("HTTP server started");   } }

Тут также, как и с датчиком — розеткой, если модуль не может подключиться к WIFI более 1 минуты, то модуль переходит в режим настройки. В момент «сна» ESP практически полностью выключена, работает только RTC таймер, а просыпается от того, что пин, подключенный к RTC таймеру и к пину «Reset», подаёт положительный сигнал, перезагружая МК .

Видео с работой системы

Заключение

Принципе, с натяжкой, это можно назвать «системой умного дома». Конечно, она работает, но есть много нюансов, недоработок… Как начальный проект я доволен, буду дорабатывать, например, добавлю поддержку нескольких модулей — розеток, так же более сложны условия и нормальное использование возможностей MQTT. Также не помешает глобальный рефакторинг кода…

Листинги и файлы

Листинг программы для модуля — розетки
#include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <EEPROM.h> #include <PubSubClient.h>  #define RELEPIN 4  const char *ssid_ap = "Rozetka_Setup";  //имя точки доступа модуля const char *password_ap = "12345678";  //пароль точки доступа модуля const char *ID = "rele_1";  String ssid = "";  //имя wifi String password = "";  //пароль wifi String device_id; // ID модуля bool setup_mode; // true - первичная настройка модуля, false - основная работа модуля bool rele;  String auto_name = "example"; int auto_data = 10; int auto_oper = 0; bool auto_stat = false;// false - не работает, true - работает bool auto_mode = false; //false - включать, true - выключать  const char *mqtt_server = "M5.WQTT.RU"; // Имя сервера MQTT const int mqtt_port = 2602; // Порт для подключения к серверу MQTT const char *mqtt_user = "u_RSELYN"; // Логин от серверa const char *mqtt_pass = "bhbtIJue"; // Пароль от сервера  String names[10];// массив имен модулей String znach[10];// массив единиц измерения модулей int data[10];// массив данных модулей int lng = 0;//используемая длина   IPAddress local_ip(192, 168, 1, 1); IPAddress gateway(192, 168, 1, 1); IPAddress subnet(255, 255, 255, 0);  IPAddress local_ip_2(192, 168, 0, 250); IPAddress gateway_2(192, 168, 0, 1); IPAddress subnet_2(255, 255, 255, 0);  ESP8266WebServer server(80);// server для настройки  WiFiClient wclient; PubSubClient client(wclient, mqtt_server, mqtt_port);  void write_string_EEPROM (int Addr, String Str) {   byte lng = Str.length();   EEPROM.begin (256);   EEPROM.write(Addr , lng);   unsigned char* buf = new unsigned char[15];   Str.getBytes(buf, lng + 1);   Addr++;   for (byte i = 0; i < lng; i++) {     EEPROM.write(Addr + i, buf[i]);     delay(10);   }   EEPROM.commit();   EEPROM.end(); }  char *read_string_EEPROM (int Addr) {   EEPROM.begin(256);   byte lng = EEPROM.read(Addr);   char* buf = new char[15];   Addr++;   for (byte i = 0; i < lng && i < 15; i++) buf[i] = char(EEPROM.read(i + Addr));   buf[lng] = '\x0';   EEPROM.end();   return buf; }    void setup() {   pinMode(RELEPIN, OUTPUT);   Serial.begin(115200);   EEPROM.begin(256);   setup_mode = EEPROM.read(45);// читаем из EERPOM текущий режим   rele = EEPROM.read(60);// читаем из EERPOM текущее состояние реле   EEPROM.end();   if (rele) digitalWrite(RELEPIN, HIGH); else digitalWrite(RELEPIN, LOW);   if (!setup_mode) { //если модуль в режиме нормальной работы          local_ip_2.fromString(String(read_string_EEPROM(200)));//читаем IP из eeprom      gateway_2.fromString(String(read_string_EEPROM(220)));//читаем гейт из eeprom       //Serial.println(read_string_EEPROM(0));     //Serial.println(read_string_EEPROM(20));     WiFi.begin(read_string_EEPROM(0), read_string_EEPROM(20));     int sm = 0;     while (WiFi.status() != WL_CONNECTED) {       delay(500); sm++;       Serial.print(".");       if (sm > 120) {         handle_ReturnSetup();       }     }     WiFi.config(local_ip_2, gateway_2, subnet_2);     Serial.println("WiFi connected");     Serial.print("IP address: ");     Serial.println(WiFi.localIP());     read_auto();     server.on("/", handle_OnConnect_2);     server.on("/select_auto", handle_SelectAuto);     server.on("/rele_auto", handle_ReleAuto);     server.on("/delete_page", handle_Delete);     server.on("/return_setup", handle_ReturnSetup);     server.on("/control_menu", handle_ControlMenu);     server.on("/rele_off", handle_ReleOff);     server.on("/rele_on", handle_ReleOn);     server.on("/auto_off", handle_AutoOff);     server.on("/auto_on", handle_AutoOn);     server.onNotFound(handle_NotFound);     server.begin();     Serial.println("HTTP server started");      if (client.connect(MQTT::Connect(ID).set_auth(mqtt_user, mqtt_pass))) {       Serial.println("Connected to MQTT server");       client.subscribe("/datk"); // подписывааемся на топик с данными датчиков       //client.subscribe("/cmd"); // подписывааемся на топик с командами       client.set_callback(callback);     } else {       Serial.println("Could not connect to MQTT server");     }   }   else {//если модуль в режиме настройки     delay(1000);     WiFi.softAP(ssid_ap, password_ap);     WiFi.softAPConfig(local_ip, gateway, subnet);     delay(100);     server.on("/", handle_OnConnect);     server.on("/end_setup", handle_EndSetup);     server.on("/action_page", handleForm);     server.onNotFound(handle_NotFound);     server.begin();     Serial.println("HTTP server started");   } }  void read_auto() {   EEPROM.begin(256);   auto_mode = EEPROM.read(130);   EEPROM.end();   auto_data = String(read_string_EEPROM(90)).toInt();   Serial.println(read_string_EEPROM(90));   auto_oper = String(read_string_EEPROM(110)).toInt();   auto_name = read_string_EEPROM(70); }  void perek(bool per) { //per = true = прямой режим, per = true = обратный режим   EEPROM.begin(256);   if (auto_mode) {     if (per) {       rele = 0;       digitalWrite(RELEPIN, LOW);     }     else {       rele = 1;       digitalWrite(RELEPIN, HIGH);     }   }   else {     if (per) {       rele = 1;       digitalWrite(RELEPIN, HIGH);     }     else {       rele = 0;       digitalWrite(RELEPIN, LOW);     }   }   EEPROM.write(60, rele);   EEPROM.commit();   EEPROM.end(); }  void loop() {   server.handleClient();   if (client.connected()) {     client.loop();   } }  void callback(const MQTT::Publish& pub) {   Serial.print(pub.topic()); // выводим в сериал порт название топика   Serial.print(" => ");   Serial.print(pub.payload_string()); // выводим в сериал порт значение полученных данных   String payload = pub.payload_string();   if (String(pub.topic()) == "/datk") // проверяем из нужного ли нам топика пришли данные   {     String tmpstr = "", namest, znachs;     int datat;     int mod = 0;     bool tr = false;     for (int i = 0 ; i < payload.length(); i++) {       if (payload[i] != '#') tmpstr += payload[i]; else {         switch (mod) {           case 0:             namest = tmpstr;             break;           case 1:             datat = tmpstr.toInt();             break;           case 2:             znachs = tmpstr;             break;         }         tmpstr = "";         mod++;       }     }     for (int i = 0; i < lng && !tr; i++) {       if (names[i] == namest) {         data[i] = datat;         tr = true;       }     }     if (!tr) {       lng++;       names[lng - 1] = namest;       data[lng - 1] = datat;       znach[lng - 1] = znachs;     }     if (auto_stat && namest == auto_name) {       switch (auto_oper) {         case 0:           if (auto_data > datat) perek(true);           if (auto_data < datat) perek(false);           break;         case 1:           if (auto_data < datat) perek(true);           if (auto_data > datat) perek(false);           break;         case 2:           if (auto_data = datat) perek(true);           if (auto_data != datat) perek(false);           break;       }     }   }    /*if (String(pub.topic()) == "/сmd"){     if(pub.payload_string()=="ON")     {       rele = 1;     digitalWrite(RELEPIN, HIGH  );     EEPROM.begin(256);     EEPROM.write(60, 1);     EEPROM.commit();     EEPROM.end();     }     else     {       rele = 0;     digitalWrite(RELEPIN, LOW  );     EEPROM.begin(256);     EEPROM.write(60, 0);     EEPROM.commit();     EEPROM.end();     }     }*/ }   void handle_ReturnSetup() {   EEPROM.begin(256);   EEPROM.write(45, true);   setup_mode = true;   EEPROM.commit();   EEPROM.end();   ESP.reset(); }  void handle_Delete() {   String delname = server.arg("DELETE_NAME");   bool tr = false;   for (int i = 0 ; i < lng; i++) {     if (tr) {       names[i - 1] = names[i];       data[i - 1] = data[i];       znach[i - 1] = znach[i];     }     else if (names[i] == delname) {       tr = true;     }   }   if (tr) lng--;   server.send(200, "text/html", SendControlMenuHTML()); }  void handle_ReleAuto() {   server.send(200, "text/html", SendControlReleAutoHTML()); }   void handle_SelectAuto() {   auto_name = names[server.arg("DAT_NAME").toInt()];   auto_oper = server.arg("OPER").toInt();   auto_data = server.arg("DATA_P").toInt();   auto_mode = server.arg("ON_OFF").toInt();   Serial.println(auto_name);   Serial.println(auto_oper);   Serial.println(auto_data);   Serial.println(auto_mode);   write_string_EEPROM(70, auto_name);   write_string_EEPROM(110, server.arg("OPER"));   write_string_EEPROM(90, server.arg("DATA_P"));   EEPROM.begin(256);   EEPROM.write(130, auto_mode);   EEPROM.commit();   EEPROM.end();   server.send(200, "text/html", SendControlReleAutoHTML()); }  void handle_AutoOff() {   auto_stat = 0;   server.send(200, "text/html", SendControlReleAutoHTML()); }  void handle_AutoOn() {   auto_stat = 1;   server.send(200, "text/html", SendControlReleAutoHTML()); }  void handle_ReleOff() {   rele = 0;   digitalWrite(RELEPIN, LOW);   EEPROM.begin(256);   EEPROM.write(60, 0);   EEPROM.commit();   EEPROM.end();   server.send(200, "text/html", SendControlMenuHTML()); }  void handle_ReleOn() {   rele = 1;   digitalWrite(RELEPIN, HIGH  );   EEPROM.begin(256);   EEPROM.write(60, 1);   EEPROM.commit();   EEPROM.end();   server.send(200, "text/html", SendControlMenuHTML()); }  void handle_OnConnect() {   server.send(200, "text/html", SendHTML()); }  void handle_OnConnect_2() {   server.send(200, "text/html", SendMenuHTML()); }  void handleForm() {   ssid = server.arg("WIFI_NAME");   password = server.arg("WIFI_password");   device_id = server.arg("DEVICE_ID");   local_ip_2.fromString(server.arg("LOCAL_IP"));   gateway_2.fromString(server.arg("GATEWAY"));   write_string_EEPROM(200, server.arg("LOCAL_IP"));   write_string_EEPROM(220, server.arg("GATEWAY"));   Serial.print("WIFI: ");   Serial.println(ssid);    Serial.print("password: ");   Serial.println(password);    server.send(200, "text/html", SendEndHTML()); //Send web page }  void handle_EndSetup() {   write_string_EEPROM(0, ssid);   write_string_EEPROM(20, password);   EEPROM.begin(256);   EEPROM.write(45, false);   setup_mode = false;   EEPROM.commit();   EEPROM.end();   WiFi.softAPdisconnect(true);   ESP.reset(); }  void handle_ControlMenu() {   server.send(200, "text/html", SendControlMenuHTML()); }  void handle_NotFound() {   server.send(404, "text/plain", "Not found"); }  String SendEndHTML() {   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/><html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<a href='/'><h1>Вернуться к настройки</h1></a>\n";   ptr += "<a href='/end_setup'><h1>Продолжить</h1></a>\n";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }  String SendHTML() {   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/> <html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";   ptr += ".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";   ptr += ".button-on {background-color: #1abc9c;}\n";   ptr += ".button-on:active {background-color: #16a085;}\n";   ptr += ".button-off {background-color: #34495e;}\n";   ptr += ".button-off:active {background-color: #2c3e50;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<h1>Настройка WIFI</h1>\n";   ptr += "<h3>Укажите название и пароль от нужной wifi сети</h3>\n";   ptr += "<form action=\"/action_page\">";   ptr += "Название:<br>";   ptr += "<input type=\"text\" name=\"WIFI_NAME\" value=\"тест_название\">";   ptr += "<br>";   ptr += "Пароль:<br>";   ptr += "<input type=\"text\" name=\"WIFI_password\" value=\"123456789\">";   ptr += "<br>";   ptr += "Название модуля:<br>";   ptr += "<input type=\"text\" maxlength=\"14\" name=\"DEVICE_ID\" value=\"розетка\"><br>";   ptr += "Local_ip:<br>";   ptr += "<input type=\"text\" name=\"LOCAL_IP\" value=\"192.168.0.250\">";   ptr += "<br>";   ptr += "Gateway:<br>";   ptr += "<input type=\"text\" name=\"GATEWAY\" value=\"192.168.0.1\">";   ptr += "<br>";   ptr += "<br><br>";   ptr += "<input type=\"submit\" value=\"Закончить\">";   ptr += "</form>";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }  String SendMenuHTML() {   String ptr = "<!DOCTYPE html> <html lang='ru-RU'><head><meta charset='UTF-8'/><html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";   ptr += ".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";   ptr += ".button-on {background-color: #1abc9c;}\n";   ptr += ".button-on:active {background-color: #16a085;}\n";   ptr += ".button-off {background-color: #34495e;}\n";   ptr += ".button-off:active {background-color: #2c3e50;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<h1>WIFI меню</h1>\n";   ptr += "<h3>Выберете пункт меню:</h3>\n";   ptr += "<br><input type=\"button\" value=\"Вернуться в режим настройки\" onClick=\"document.location = '/return_setup'\" /><br>";   ptr += "<br><input type=\"button\" value=\"Панель управления\" onClick=\"document.location = '/control_menu'\" /><br>";   ptr += "<br><input type=\"button\" value=\"Автоматическое управление розеткой\" onClick=\"document.location = '/rele_auto'\" /><br>";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }  String SendControlMenuHTML() {   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/> <html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px; } h3 {color: #444444;margin-bottom: 50px;} h4 {color: #444444;margin-bottom: 20px;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<h1>Панель управления</h1>\n";   ptr += "<br><input type=\"button\" value=\"Вернуться в меню\" onClick=\"document.location = '/'\" /><br>";   if (rele) {     ptr += "<br><input type=\"button\" value=\"Включить розетку\" onClick=\"document.location = '/rele_off'\" /><br>";   }   if (!rele) {     ptr += "<br><input type=\"button\" value=\"Выключить розетку\" onClick=\"document.location = '/rele_on'\" /><br>";   }   ptr += "<h3>Датчики:</h3>\n";   //ptr += "<h4>Датчики:</h4>\n";   for (int i = 0 ; i < lng; i++) {     ptr += "<h4>" + names[i] + " : " + data[i] + " " + znach[i] + "</h4>";   }   ptr += "<form action=\"/delete_page\">";   ptr += "Название:  ";   ptr += "<input type=\"text\" name=\"DELETE_NAME\" value=\"модуль\">   ";   //ptr += "<br><br>";   ptr += "<input type=\"submit\" value=\"удалить\">";   ptr += "</form>";   ptr += "<br><input type=\"button\" value=\"обновить\" onClick=\"document.location = '/control_menu'\" /><br>";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }  String SendControlReleAutoHTML() {   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/> <html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px; } h3 {color: #444444;margin-bottom: 50px;} h4 {color: #444444;margin-bottom: 20px;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<h1>Настройка автоматического управления розетки </h1>";   if (auto_stat) {     ptr += "<h3>Автоматическое управления включено</h3>";   }   else {     ptr += "<h3>Автоматическое управления выключено</h3>";   }   ptr += "<h3>Текущее условие:" + auto_name + " ";   switch (auto_oper) {     case 0 : ptr += "> ";       break;     case 1 : ptr += "< ";       break;     case 2 : ptr += "= ";       break;   }   if (auto_mode)     ptr += String(auto_data) + " выключать </h3>";   else     ptr += String(auto_data) + " включать </h3>";   ptr += "<h3>Выберете датчик и параметр для него:</h3>\n";   ptr += "<form action=\"/select_auto\">";   ptr += "<select size=\"1\" name=\"DAT_NAME\">";   for (int i = 0 ; i < lng; i++) {     ptr += "<option value=\"" + String(i) + "\">" + names[i] + "</option>";   }   ptr += "</select>  ";   ptr += "<select size=\"1\" name=\"OPER\">";   ptr += "<option value=0>></option>";   ptr += "<option value=1><</option>";   ptr += "<option value=2>=</option>";   ptr += "</select>  ";   ptr += "</select>  ";   ptr += "<input type=\"text\" name=\"DATA_P\" value=\"10\">";   ptr += "<select size=\"1\" name=\"ON_OFF\">";   ptr += "<option value=0>Включать</option>";   ptr += "<option value=1>Выключать</option>";   ptr += "</select>  ";   //ptr += "<br><br>";   ptr += "<input type=\"submit\" value=\"настроить\">";   ptr += "</form>";   ptr += "<br><input type=\"button\" value=\"обновить\" onClick=\"document.location = '/rele_auto'\" /><br>";   if (auto_stat) {     ptr += "<br><input type=\"button\" value=\"выключить автопереключение\" onClick=\"document.location = '/auto_off'\" /><br>";   }   else {     ptr += "<br><input type=\"button\" value=\"включить автопереключение\" onClick=\"document.location = '/auto_on'\" /><br>";   }   ptr += "<br><input type=\"button\" value=\"Вернуться в меню\" onClick=\"document.location = '/'\" /><br>";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }
Листинг программы для модуля — датчика DHT11
#include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <EEPROM.h> #include <PubSubClient.h> #include "DHT.h"  #define DHTTYPE DHT11 uint8_t DHTPin = 5;  int tempC = 0; int humC = 0; int tmi = 10; DHT dht(DHTPin, DHTTYPE);   const char *ssid_ap = "Datchik_Setup";  //имя точки доступа модуля const char *password_ap = "12345678";  //пароль точки доступа модуля const char *ID = "temp_1";  String ssid = "";  //имя wifi String password = "";  //пароль wifi String device_id; // ID модуля bool setup_mode ; // true - первичная настройка модуля, false - основная работа модуля  const char *mqtt_server = "M5.WQTT.RU"; // Имя сервера MQTT const int mqtt_port = 2602; // Порт для подключения к серверу MQTT const char *mqtt_user = "u_RSELYN"; // Логин от серверa const char *mqtt_pass = "bhbtIJue"; // Пароль от сервера  ESP8266WebServer server(80);// server для настройки  WiFiClient wclient; PubSubClient client(wclient, mqtt_server, mqtt_port);  IPAddress local_ip(192, 168, 1, 1); IPAddress gateway(192, 168, 1, 1); IPAddress subnet(255, 255, 255, 0);  void write_string_EEPROM (int Addr, String Str) {   byte lng = Str.length();   EEPROM.begin (256);   EEPROM.write(Addr , lng);   unsigned char* buf = new unsigned char[15];   Str.getBytes(buf, lng + 1);   Addr++;   for (byte i = 0; i < lng; i++) {     EEPROM.write(Addr + i, buf[i]);     delay(10);   }   EEPROM.commit();   EEPROM.end(); }  char *read_string_EEPROM (int Addr) {   EEPROM.begin(256);   byte lng = EEPROM.read(Addr);   char* buf = new char[15];   Addr++;   for (byte i = 0; i < lng && i < 15; i++) buf[i] = char(EEPROM.read(i + Addr));   buf[lng] = '\x0';   EEPROM.end();   return buf; }  void setup() {   pinMode(DHTPin, INPUT);   Serial.begin(115200);   EEPROM.begin(256);   setup_mode = EEPROM.read(85);   EEPROM.end();   device_id = read_string_EEPROM(40);   if (!setup_mode) {     dht.begin();     tmi = String(read_string_EEPROM(200)).toInt();     local_ip.fromString(String(read_string_EEPROM(200)));     gateway.fromString(String(read_string_EEPROM(220)));     Serial.println(read_string_EEPROM(0));     Serial.println(read_string_EEPROM(20));     WiFi.begin(read_string_EEPROM(0), read_string_EEPROM(20));     int sm = 0;     while (WiFi.status() != WL_CONNECTED) {       delay(500); sm++;       Serial.print(".");       if (sm > 120) {         ReturnSetup();       }     }          Serial.println("WiFi connected");     tempC = (int)dht.readTemperature();    humC = (int)dht.readHumidity();     if (client.connect(MQTT::Connect(ID).set_auth(mqtt_user, mqtt_pass))) {       Serial.println("Connected to MQTT server");     } else {       Serial.println("Could not connect to MQTT server");     }     client.publish("/datk", device_id+"_t" + "#" + String(tempC) + "#градусов#");     client.publish("/datk", device_id+"_hm" + "#" + String(humC) + "#влажности#");     delay(100);     ESP.deepSleep(tmi * 1000000);   }   else {     delay(1000);     WiFi.softAP(ssid_ap, password_ap);     WiFi.softAPConfig(local_ip, gateway, subnet);     delay(100);     server.on("/", handle_OnConnect);     server.on("/end_setup", handle_EndSetup);     server.on("/action_page", handleForm);     server.onNotFound(handle_NotFound);     server.begin();     Serial.println("HTTP server started");   } }  void loop() {   if (setup_mode)     server.handleClient(); }  void handle_OnConnect() {   server.send(200, "text/html", SendHTML()); }  void ReturnSetup() {   EEPROM.begin(256);   EEPROM.write(85, true);   setup_mode = true;   EEPROM.commit();   EEPROM.end();   ESP.reset(); }   void handleForm() {   ssid = server.arg("WIFI_NAME");   password = server.arg("WIFI_password");   device_id =  server.arg("DEVICE_ID");   tmi = server.arg("TIMING").toInt();   Serial.print("WIFI: ");   Serial.println(ssid);    Serial.print("password: ");   Serial.println(password);    server.send(200, "text/html", SendEndHTML()); //Send web page }  void handle_EndSetup() {   write_string_EEPROM(0, ssid);   write_string_EEPROM(20, password);   write_string_EEPROM(40, device_id);   write_string_EEPROM(200, String(tmi));   EEPROM.begin(256);   EEPROM.write(85, false);   setup_mode = false;   EEPROM.commit();   EEPROM.end();   WiFi.softAPdisconnect(true);   ESP.reset(); }  void handle_NotFound() {   server.send(404, "text/plain", "Not found"); }  String SendEndHTML() {   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/><html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<a href='/'><h1>Вернуться к настройки</h1></a>\n";   ptr += "<a href='/end_setup'><h1>Продолжить</h1></a>\n";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }  String SendHTML() {   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/> <html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";   ptr += ".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";   ptr += ".button-on {background-color: #1abc9c;}\n";   ptr += ".button-on:active {background-color: #16a085;}\n";   ptr += ".button-off {background-color: #34495e;}\n";   ptr += ".button-off:active {background-color: #2c3e50;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<h1>Настройка WIFI</h1>\n";   ptr += "<h3>Укажите название и пароль от нужной wifi сети</h3>\n";   ptr += "<form action=\"/action_page\">";   ptr += "Название:<br>";   ptr += "<input type=\"text\" name=\"WIFI_NAME\" value=\"тест_название\">";   ptr += "<br>";   ptr += "Пароль:<br>";   ptr += "<input type=\"text\" name=\"WIFI_password\" value=\"123456789\">";   ptr += "<br>";   ptr += "Тайминг отправки(в секундах):<br>";   ptr += "<input type=\"text\" name=\"TIMING\" value=\"1\">";   ptr += "<br>";   ptr += "Название модуля:<br>";   ptr += "<input type=\"text\" maxlength=\"10\" name=\"DEVICE_ID\" value=\"datc_temp\"><br>";   ptr += "<br><br>";   ptr += "<input type=\"submit\" value=\"Закончить\">";   ptr += "</form>";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }
Листинг программы для модуля — датчика ds18b20
#include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <EEPROM.h> #include <PubSubClient.h> #include <OneWire.h> #include <DallasTemperature.h>  #define ONE_WIRE_BUS 5 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature DS18B20(&oneWire); char temperatureCString[7]; int tempC = 0; int tmi = 10;  const char *ssid_ap = "Datchik_Setup";  //имя точки доступа модуля const char *password_ap = "12345678";  //пароль точки доступа модуля const char *ID = "temp_1";  String ssid = "";  //имя wifi String password = "";  //пароль wifi String device_id; // ID модуля bool setup_mode ; // true - первичная настройка модуля, false - основная работа модуля  const char *mqtt_server = "M5.WQTT.RU"; // Имя сервера MQTT const int mqtt_port = 2602; // Порт для подключения к серверу MQTT const char *mqtt_user = "u_RSELYN"; // Логин от серверa const char *mqtt_pass = "bhbtIJue"; // Пароль от сервера  ESP8266WebServer server(80);// server для настройки  WiFiClient wclient; PubSubClient client(wclient, mqtt_server, mqtt_port);  IPAddress local_ip(192, 168, 1, 1); IPAddress gateway(192, 168, 1, 1); IPAddress subnet(255, 255, 255, 0);  void write_string_EEPROM (int Addr, String Str) {   byte lng = Str.length();   EEPROM.begin (256);   EEPROM.write(Addr , lng);   unsigned char* buf = new unsigned char[15];   Str.getBytes(buf, lng + 1);   Addr++;   for (byte i = 0; i < lng; i++) {     EEPROM.write(Addr + i, buf[i]);     delay(10);   }   EEPROM.commit();   EEPROM.end(); }  char *read_string_EEPROM (int Addr) {   EEPROM.begin(256);   byte lng = EEPROM.read(Addr);   char* buf = new char[15];   Addr++;   for (byte i = 0; i < lng && i < 15; i++) buf[i] = char(EEPROM.read(i + Addr));   buf[lng] = '\x0';   EEPROM.end();   return buf; }  void getTemperature() {   do {     DS18B20.requestTemperatures();     tempC = DS18B20.getTempCByIndex(0);     dtostrf(tempC, 2, 2, temperatureCString);     delay(100);   } while (tempC == 85.0 || tempC == (-127.0)); }  void setup() {   Serial.begin(115200);   EEPROM.begin(256);   setup_mode = EEPROM.read(85);   EEPROM.end();   device_id = read_string_EEPROM(40);   if (!setup_mode) {     tmi = String(read_string_EEPROM(200)).toInt();     local_ip.fromString(String(read_string_EEPROM(200)));     gateway.fromString(String(read_string_EEPROM(220)));     DS18B20.begin();     Serial.println(read_string_EEPROM(0));     Serial.println(read_string_EEPROM(20));     WiFi.begin(read_string_EEPROM(0), read_string_EEPROM(20));     int sm = 0;     while (WiFi.status() != WL_CONNECTED) {       delay(500); sm++;       Serial.print(".");       if (sm > 120) {         ReturnSetup();       }     }     getTemperature();     Serial.println("WiFi connected");     if (client.connect(MQTT::Connect(ID).set_auth(mqtt_user, mqtt_pass))) {       Serial.println("Connected to MQTT server");     } else {       Serial.println("Could not connect to MQTT server");     }     client.publish("/datk", device_id + "#" + String(tempC) + "#градусов#");     delay(100);     ESP.deepSleep(tmi * 1000000);   }   else {     delay(1000);     WiFi.softAP(ssid_ap, password_ap);     WiFi.softAPConfig(local_ip, gateway, subnet);     delay(100);     server.on("/", handle_OnConnect);     server.on("/end_setup", handle_EndSetup);     server.on("/action_page", handleForm);     server.onNotFound(handle_NotFound);     server.begin();     Serial.println("HTTP server started");   } }  void loop() {   if (setup_mode)     server.handleClient(); }  void handle_OnConnect() {   server.send(200, "text/html", SendHTML()); }  void ReturnSetup() {   EEPROM.begin(256);   EEPROM.write(85, true);   setup_mode = true;   EEPROM.commit();   EEPROM.end();   ESP.reset(); }   void handleForm() {   ssid = server.arg("WIFI_NAME");   password = server.arg("WIFI_password");   device_id =  server.arg("DEVICE_ID");   tmi = server.arg("TIMING").toInt();   Serial.print("WIFI: ");   Serial.println(ssid);    Serial.print("password: ");   Serial.println(password);    server.send(200, "text/html", SendEndHTML()); //Send web page }  void handle_EndSetup() {   write_string_EEPROM(0, ssid);   write_string_EEPROM(20, password);   write_string_EEPROM(40, device_id);   write_string_EEPROM(200, String(tmi));   EEPROM.begin(256);   EEPROM.write(85, false);   setup_mode = false;   EEPROM.commit();   EEPROM.end();   WiFi.softAPdisconnect(true);   ESP.reset(); }  void handle_NotFound() {   server.send(404, "text/plain", "Not found"); }  String SendEndHTML() {   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/><html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<a href='/'><h1>Вернуться к настройки</h1></a>\n";   ptr += "<a href='/end_setup'><h1>Продолжить</h1></a>\n";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }  String SendHTML() {   String ptr = "<!DOCTYPE html><html lang='ru-RU'><head><meta charset='UTF-8'/> <html>\n";   ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";   ptr += "<title>WIFI Control</title>\n";   ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";   ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";   ptr += ".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";   ptr += ".button-on {background-color: #1abc9c;}\n";   ptr += ".button-on:active {background-color: #16a085;}\n";   ptr += ".button-off {background-color: #34495e;}\n";   ptr += ".button-off:active {background-color: #2c3e50;}\n";   ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";   ptr += "</style>\n";   ptr += "</head>\n";   ptr += "<body>\n";   ptr += "<h1>Настройка WIFI</h1>\n";   ptr += "<h3>Укажите название и пароль от нужной wifi сети</h3>\n";   ptr += "<form action=\"/action_page\">";   ptr += "Название:<br>";   ptr += "<input type=\"text\" name=\"WIFI_NAME\" value=\"тест_название\">";   ptr += "<br>";   ptr += "Пароль:<br>";   ptr += "<input type=\"text\" name=\"WIFI_password\" value=\"123456789\">";   ptr += "<br>";   ptr += "Тайминг отправки(в секундах):<br>";   ptr += "<input type=\"text\" name=\"TIMING\" value=\"1\">";   ptr += "<br>";   ptr += "Название модуля:<br>";   ptr += "<input type=\"text\" maxlength=\"14\" name=\"DEVICE_ID\" value=\"datc_temp\"><br>";   ptr += "<br><br>";   ptr += "<input type=\"submit\" value=\"Закончить\">";   ptr += "</form>";   ptr += "</body>\n";   ptr += "</html>\n";   return ptr; }

ссылка на оригинал статьи https://habr.com/ru/post/555830/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *