Предыстория
Начну я свою эпопею с небольшой предыстории. Надо было мне сделать проект для 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;
}