Предыстория

Начну я свою эпопею с небольшой предыстории. Надо было мне сделать проект для 9 класса (да, сейчас, чтобы тебя допустили до экзаменов, надо сделать проект и защитить его).
Решил практически сразу, что это будет умный дом (хотя даже воробей умнее его), ведь давно хотел сделать что-то такое, но руки так и не доходили, а тут подвернулся отличный случай + немного выпендриться можно.
Данный пост — мой переделанный диплом, да и вообще первая попытка в написании таких вещей )
План
Для успешной и продуктивной работы нужен план, для себя я составил его примерно таким:
-
Идея и идеология умного дома.
-
Железо для построения умного дома.
-
Код для каждой из частей умного дома.
Принципе, для начала это более чем достаточно . Теперь можно разобрать каждый пункт отдельно.
Идея и идеология умного дома
Посидев несколько дней и обдумав все идеи, в моей голове всплыла утопическая идея такой системы, которая не нуждалась бы в базовом модуле, а каждый такой модуль будет полностью автономен. У системы такого типа, в отличии от той, что использует в своей основе базовый модуль (примером может быть умный дом xiaomi), имеется несколько плюсов и минусов.
Начнем с плюсов. Главный плюс — отказоустойчивость, даже если один из управляющих модулей выйдет из строя, датчики все равно будут отправлять данные пользователю. Второй же плюс — возможность связи модулей на больших расстояниях.
А теперь про минусы: самый значительный минус — автономности, ведь на то, чтобы проснуться, подключиться к интернету, отправить данные и уснуть тратиться больше энергии, чем на то, чтобы передать данные по радиоканалу. Следующий минус состоит в необходимости наличия интернета для работы каждого датчика, иначе он просто не сможет отправить данные.
В итоге я составил список функционала, который умный дом должен реализовывать в ИДЕАЛЕ:
-
Автономные модули.
-
Взаимозаменяемость модулей.
-
Масштабируемость системы.
При работе, выяснилось что времени до дедлайна не так уж и много, так что я решил урезать свой список, оставив в нём только автономность и масштабируемость, хотя взаимозаменяемость добавляется в итоговый проект довольно просто.
И да, забыл сказать, модуль — единичное устройство системы умного дома, будь то wi-fi розетка или датчик температуры.
Для приемо-передачи данных я выбрал MQTT, довольно известный и удобный протокол передачи.
Микроконтроллер
Микроконтроллером, который является основой для каждого модуля умного дома, я выбрал всем хорошо знакомый, даже немного заезженный ESP-12F . Он хорош своими возможностями, в которые входит функция WI-FI (как соединение, так и точка доступа) и отличная производительность, которой с лихвой хватило на все мои задумки, а цена за штуку не превышает 100 рублей в Китае, если брать сразу 10 штук.

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

Устройство 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.

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


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


Конечно, это не первая версия платы, да и вообще, это мой первый опыт в создании печатных плат самому, до неё было 5 неудачных попыток развести печатку, где-то не так отзеркалил, где-то не так развел, где-то плохо краска перенеслась и дорожки получились плохими.
Устройство модулей датчиков
Оба модулей датчиков имеют практически идентичное внутреннее устройство, различия только в самих датчиках, в одном это DHT11, а в другом DS18B20 и их обвязках.
Модули выполняют очень простую функцию: отправлять показания с датчиков по MQTTT.


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


Питание модуля идет от 3 батареек типа АА.
Прошивка модулей
Наверное самый интересный и самый долгий пункт….
Сначала я хотел использовать RTOS-SDK от ESP, но понял что уйдет много времени чтобы разобраться в нем, поэтому я остановился на Arduino, хоть и от FreeRTOS пришлось отказаться.
Но, в принципе, для моего решения RTOS не нужна.
Для прошивки можно использовать любой программатор, совместимый с esp, но главное не перепутать перемычки (если они есть конечно) логического уровня и не поставить их на 5в, иначе esp12 может попросту сгореть.

Подключал я по этой схеме… и вот как раз таки та загвоздка, из-за которой я и потратил 2 месяца в пустую…забыл минус микроконтроллера к минусу программатора соединить! Оказывается, не дурак эту схему начертил, а дурак её пытался повторить). Пока искал где проблема, переделал много плат, заказал новые микроконтроллеры, подумал что с ними проблема, новый программатор взял, перерыл форумы с этой проблемой…
Ну да ладно, главное что все заработало.
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/
Добавить комментарий