• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Нужна помощь реле и esp-01 + сенсорная кнопка

maksimusv

New member
точнее все не так происходит, если включить стартует реле находиться в режиме low, потом через две секунды переходит в режим high, то есть выключается, переходя по ссылкам в броузере никакой реакции, digitalWrite(relaystate,Low) не обрабатывается, потом подключаю кнопку к пину0, состояние реле изменяется, обновляя броузер состояние он офф обновляется, но функция вкл выкл через веб не пашет! 100% код у меня кривой
 

maksimusv

New member
т.е. без этой строки ваше приложение зависает?
Я закоментировал эту строку.
На ПК в броузере фф, обращаюсь к esp.
Вижу страницу с состоянием реле, со ссылками на включение и выключение, переходя по ссылкам вижу в выводе в сериал
Код:
User connected.
Request Received:GET /LED=ON HTTP/1.1
Client disonnected

User connected.
Request Received:GET /LED=OFF HTTP/1.1
Client disonnected
Скажите когда зависание произойдет?
да и обрати внимание, когда ты переходишь по ссылкам, в броузере верхний div не меняет состояние! меняет только от кнопки!
 

Алексей.

Active member
да и обрати внимание, когда ты переходишь по ссылкам, в броузере верхний div не меняет состояние! меняет только от кнопки!
Да толку то! На поставленные вопросы вы не отвечаете. Для ссылки в html контенте строите мимо правил. Какой помощи вы хотите, типа отвечать на вопросы я не стану, напишите мне скетч, но чтоб работал так как я хочу. Тогда ошиблись разделом.
Логика работы у вас в голове и понять её очень не просто, скетч работает так как вы и задумали.
например на строке 88 вы принимаете решение о включении реле
Код:
       if (request.indexOf("/LED=ON") != -1) {
        digitalWrite(relayPin,LOW);
          relayState = HIGH;
       }
устанавливаете для пина LOW и сохраняете relayState = HIGH
далее на строке 121 безусловно устанавливаете для пина значение из relayState т.е. меняете обратно LOW на HIGH
Код:
digitalWrite(relayPin, relayState);
 

maksimusv

New member
Да толку то! На поставленные вопросы вы не отвечаете. Для ссылки в html контенте строите мимо правил. Какой помощи вы хотите, типа отвечать на вопросы я не стану, напишите мне скетч, но чтоб работал так как я хочу. Тогда ошиблись разделом.
Логика работы у вас в голове и понять её очень не просто, скетч работает так как вы и задумали.
например на строке 88 вы принимаете решение о включении реле
Код:
       if (request.indexOf("/LED=ON") != -1) {
        digitalWrite(relayPin,LOW);
          relayState = HIGH;
       }
устанавливаете для пина LOW и сохраняете relayState = HIGH
далее на строке 121 безусловно устанавливаете для пина значение из relayState т.е. меняете обратно LOW на HIGH
Код:
digitalWrite(relayPin, relayState);
Я не прошу написать код, прошу подсказать, где я туплю, задержка для ожидания, от палец поднёс, убрал поднёс, дрожания точнее руки), тоесть вы предлагаете relayState переделать в !relayState
 

maksimusv

New member
Да толку то! На поставленные вопросы вы не отвечаете. Для ссылки в html контенте строите мимо правил. Какой помощи вы хотите, типа отвечать на вопросы я не стану, напишите мне скетч, но чтоб работал так как я хочу. Тогда ошиблись разделом.
Логика работы у вас в голове и понять её очень не просто, скетч работает так как вы и задумали.
например на строке 88 вы принимаете решение о включении реле
Код:
       if (request.indexOf("/LED=ON") != -1) {
        digitalWrite(relayPin,LOW);
          relayState = HIGH;
       }
устанавливаете для пина LOW и сохраняете relayState = HIGH
далее на строке 121 безусловно устанавливаете для пина значение из relayState т.е. меняете обратно LOW на HIGH
Код:
digitalWrite(relayPin, relayState);
Как строить для ссылки по правилам?
 

Алексей.

Active member
тоесть вы предлагаете relayState переделать в !relayState
Я ничего не предлагал ещё. Непонятно для чего выставлять пин в LOW и тут же (в ту же миллисекунду) менять на HIGH.
Как строить для ссылки по правилам?
Читайте rfc.
Вы используете ссылки типа /LED=ON, символ "=" зарезирвирован и используется при построении квери-сринг как разделитель в параметрах ключ=значение
reserved = ";" | "/" | "?" | ":" | "@" | "&" | "="
Но вам, я подозреваю, это не нужно. На простой вопрос "когда зависает если убрать delay" так и нет ответа.
Для чего две переменные lastButtonState и buttonState в котором сохраняется последнее значение из digitalRead тоже не понятно, почему две? Почему lastButtonState статически инициализирован, а buttonState нет?
Для чего в setup, когда кнопка еще не нажата включать реле, а после инициализации wifi уже в loop-е проверять что кнопка все ещё не нажата и выключать реле?
Вы бы сначала разобрались что вам нужно.
 

maksimusv

New member
Я ничего не предлагал ещё. Непонятно для чего выставлять пин в LOW и тут же (в ту же миллисекунду) менять на HIGH.
Читайте rfc.
Вы используете ссылки типа /LED=ON, символ "=" зарезирвирован и используется при построении квери-сринг как разделитель в параметрах ключ=значение
reserved = ";" | "/" | "?" | ":" | "@" | "&" | "="
Но вам, я подозреваю, это не нужно. На простой вопрос "когда зависает если убрать delay" так и нет ответа.
Для чего две переменные lastButtonState и buttonState в котором сохраняется последнее значение из digitalRead тоже не понятно, почему две? Почему lastButtonState статически инициализирован, а buttonState нет?
Для чего в setup, когда кнопка еще не нажата включать реле, а после инициализации wifi уже в loop-е проверять что кнопка все ещё не нажата и выключать реле?
Вы бы сначала разобрались что вам нужно.
lastbutstate нужен что бы запомнить последнее состояние кнопки, так как без этого она бы просто нажать включилась отжал вЫключилась,, а как получить состояние, а потом его изменить?
 

Алексей.

Active member
А другая переменная про запас.
Библиотек для работы с кнопками есть и не одна, но свой код всегда лучше.
 

maksimusv

New member
А другая переменная про запас.
Библиотек для работы с кнопками есть и не одна, но свой код всегда лучше.
Библиотек, даже не знаю где, в гугле мало инфы, может конечно проще запаять контакты на Ttp225 что бы перевести в режим триггера, и менять жёстко состояние, может ссылочками поможете?
 

Алексей.

Active member
Библиотек, даже не знаю где, в гугле мало инфы, может конечно проще запаять контакты на Ttp225
Если для получения состояния кнопки, просто выполняете digitalRead(buttonPin) то поведение не отличается от обычной тактовой кнопки (на тактовой дребезга даже больше), нет никакой разницы какими проводами подключать, красными, желтыми или зелеными.
Библиотеки особенной для вашей кнопки не нужно.
А библиотеки там же, на своем месте, как и в остальных случаях, на гитхабе, другого места я пока не знаю.
может ссылочками поможете?
Если вы не хотите искать самостоятельно, я нашел "arduino button" за вас.
 

maksimusv

New member
Если для получения состояния кнопки, просто выполняете digitalRead(buttonPin) то поведение не отличается от обычной тактовой кнопки (на тактовой дребезга даже больше), нет никакой разницы какими проводами подключать, красными, желтыми или зелеными.
Библиотеки особенной для вашей кнопки не нужно.
А библиотеки там же, на своем месте, как и в остальных случаях, на гитхабе, другого места я пока не знаю.
Если вы не хотите искать самостоятельно, я нашел "arduino button" за вас.
спасибо добрый человек
 

maksimusv

New member
Если для получения состояния кнопки, просто выполняете digitalRead(buttonPin) то поведение не отличается от обычной тактовой кнопки (на тактовой дребезга даже больше), нет никакой разницы какими проводами подключать, красными, желтыми или зелеными.
Библиотеки особенной для вашей кнопки не нужно.
А библиотеки там же, на своем месте, как и в остальных случаях, на гитхабе, другого места я пока не знаю.
Если вы не хотите искать самостоятельно, я нашел "arduino button" за вас.
прочитав про кнопки, все равно не понял, как мне совместить код, что бы обрабатывало события из веб и кнопку!
 

Алексей.

Active member
прочитав про кнопки, все равно не понял, как мне совместить код, что бы обрабатывало события из веб и кнопку!
Определите функцию, которая будет управлять реле, вызывайте её из обработчика нажатий кнопки и из обработчиков запросов к веб серверу.

Почему не использовали библиотеку ESP8266WebServer для обработки http запросов, а взяли WiFiServer из библиотеки ESP8266WiFi?
Для чего глобально определены String request (строка 14) и WiFiClient client (строка 32) которые не используются совсем.
В функции loop, в стеке, String request аж две штуки, одна в области видимости накрывающая другую.
Мусора очень много, что то работает вопреки, а не благодаря.
Такое впечатление, что вы сами не понимаете что делаете.
 

maksimusv

New member
Определите функцию, которая будет управлять реле, вызывайте её из обработчика нажатий кнопки и из обработчиков запросов к веб серверу.

Почему не использовали библиотеку ESP8266WebServer для обработки http запросов, а взяли WiFiServer из библиотеки ESP8266WiFi?
Для чего глобально определены String request (строка 14) и WiFiClient client (строка 32) которые не используются совсем.
В функции loop, в стеке, String request аж две штуки, одна в области видимости накрывающая другую.
Мусора очень много, что то работает вопреки, а не благодаря.
Такое впечатление, что вы сами не понимаете что делаете.
М-да, не хочу задеть и чем то обидеть, но вы походу разбираетесь в этом всем так же как и я, почему, почему, потому, если не передать стринг реквест пустую строку, то будет все время запрос на favicon.ico, а вот это уже мусор, мой вопрос был совсем иным, как использовать два разных метода на одно действие, расписывать поведение программы, которое и так видно что делает смысл? Если разделить эти два метода и использовать по отдельности, работает как надо, но в месте, один следует за другим, и выполняются оба, потому как все в цикле, и состояния всего два, вкл и выкл, код должен быть сложнее, но я не имею опыта в программировании контролёров, мне нужна грамотная подсказка человека который это уже делал, а не того кто просто занимается лечением больных идей, я знаю что мой код кривой, и поэтому я на форуме, не можешь подсказать, включи состояние офф
 

maksimusv

New member
Определите функцию, которая будет управлять реле, вызывайте её из обработчика нажатий кнопки и из обработчиков запросов к веб серверу.

Почему не использовали библиотеку ESP8266WebServer для обработки http запросов, а взяли WiFiServer из библиотеки ESP8266WiFi?
Для чего глобально определены String request (строка 14) и WiFiClient client (строка 32) которые не используются совсем.
В функции loop, в стеке, String request аж две штуки, одна в области видимости накрывающая другую.
Мусора очень много, что то работает вопреки, а не благодаря.
Такое впечатление, что вы сами не понимаете что делаете.
то что предлагаете вы я и так это делаю в лупе, выносить куски кода за луп в отдельные функции, смысл, если они будут вызываться также, и будет происходить тот же бред, куда можно обратиться что бы купить уже этот кусок кода, я согласен на это, потому как логика в моей голове, работает как то иначе
 

Алексей.

Active member
Взял библиотеку для обработки кнопок, почти первую найденную и библиотеку веб-сервера а не вифи-сервера.
Код:
/**
* Версия arduino-ide 1.8.9
* Версия sdk esp8266 2.5.0
* Версия библиотеки OneButton 1.3.0
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <OneButton.h>

#define STASSID "****"
#define STAPSK "****"
// Определяем пин-ы для подключения тактовой кнопки и реле
#define BUTTON_PIN 0
#define RELAY_PIN 2

// Включаем контент, который будем отгружать веб сервером
#include "index.h"

// Задаем переменную, в которой храним состояние пина, которым будем включать реле.
// Включать будем низким уровнем, поэтому начальное состояние HIGH, реле выключено
uint8_t relayPinState = HIGH;

OneButton button;
ESP8266WebServer server;

// Определяем enum для управления реле
enum e_relay_control {
  // Включить
  ON,
  // Выключить
  OFF,
  // Переключить
  TOGGLE
};

// Функция управления реле
void relaySwitch(enum e_relay_control control) {
  switch (control) {
    case ON: relayPinState = LOW; break;
    case OFF: relayPinState = HIGH; break;
    default: relayPinState = relayPinState == LOW ? HIGH : LOW; break;
  }
  digitalWrite(RELAY_PIN, relayPinState);
}

// Обработчик нажатия кнопки (фактически срабатывает на отжатие)
void buttonClick() {
  relaySwitch(TOGGLE);
}

void setup() {
  // Настраиваем пин управления реле
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, relayPinState);

  // Настраиваем кнопку, срабатывание по низкому уровню
  button = OneButton(BUTTON_PIN, true);
  button.attachClick(buttonClick);

  // Настраиваем wifi и ожидаем соединения
  WiFi.mode(WIFI_STA);
  WiFi.begin(STASSID, STAPSK);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }

  // Устанавливаем обработчик запроса корневого uri
  server.on("/", HTTP_GET, []() {
  server.setContentLength(CONTENT_LENGTH_UNKNOWN);
  server.send(200, "text/html");
    server.sendContent_P(_index_h, strlen(_index_h));
  });

  // Устанавливаем обработчик запроса статуса
  server.on("/status", HTTP_GET, []() {
    server.send(200, "text/plain", relayPinState == LOW ? "ON" : "OFF");
  });

  // Устанавливаем обработчик запроса переключения реле
  server.on("/relay", HTTP_POST, []() {
    const String& s = server.arg("plain");
    if (s.equals("ON")) {
      relaySwitch(ON);
    } else if (s.equals("OFF")) {
      relaySwitch(OFF);
    } else if (s.equals("TOGGLE")) {
      relaySwitch(TOGGLE);
    }
    server.send(200, "text/plain", relayPinState == LOW ? "ON" : "OFF");
  });

  // Устанавливаем обработчик остальных запросов
  server.onNotFound([]() {
    server.send(404, "text/plain", "Not found");
  });

  // Инициализируем веб сервер на порту 80
  server.begin(80);
}

void loop() {
  // Выполняем обработку нажатий на кнопку
  button.tick();
  // Выполняем обработку запросов к веб серверу
  server.handleClient();
}
Код:
static const char _index_h[] PROGMEM =
"<!doctype html>\n"
"<html\n"
"<head>\n"
"   <meta charset='utf-8' />\n"
"</head>\n"
"<body onload='get_state(); window.setInterval(\"get_state()\", 1000);'>\n"
"   <div id='relayState'></div>\n"
"   <input value='ON' type='button' onclick=\"exec('ON')\" />\n"
"   <input value='OFF' type='button' onclick=\"exec('OFF')\" />\n"
"   <input value='TOGGLE' type='button' onclick=\"exec('TOGGLE')\" />\n"
"</body>\n"
"<script type='text/javascript'>\n"
"   function get_state() {\n"
"       var request = new XMLHttpRequest();\n"
"       request.open('GET','status',true);\n"
"       request.onreadystatechange = function() {\n"
"           if (request.readyState == 4 && request.status==200) {\n"
"               document.getElementById('relayState').innerHTML = request.responseText;\n"
"           }\n"
"       }\n"
"       request.send();\n"
"   }\n"
"   function exec(message) {\n"
"       var request = new XMLHttpRequest();\n"
"       request.open('POST','relay',true);\n"
"       request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');\n"
"       request.onreadystatechange = function() {\n"
"    if (request.readyState == 4 && request.status==200) {\n"
"               document.getElementById('relayState').innerHTML = request.responseText;\n"
"    }\n"
"    }\n"
"       request.send(message);\n"
"   }\n"
"</script>\n"
"</html>\n";
На ноде-мсу вместо реле - светодиод синий, встроенный, как раз к gpio2 подключен
Вместо кнопки просто провод для беспаечного монтажа, замыкаю и размыкаю им gpio0.
Замкнул-разомкнул светодиод включился и на веб-странице состояние изменилось (раз в секунду скриптом запрашиваю статус), ещё раз замкнул-разомкнул и светодиод выключился.
На веб-странице нажал на кнопку, скриптом отправил запрос, в обработчике запроса выполнил переключение светодиода.
Как то так.
 

flamik

New member
Делал абсолютно подобный девайс и просто инвертировал выход на ttp223b, на самой платке ttp223b есть перемычки для инвертирования выхода.
 

cruelty

New member
Взял библиотеку для обработки кнопок, почти первую найденную и библиотеку веб-сервера а не вифи-сервера.
Код:
/**
* Версия arduino-ide 1.8.9
* Версия sdk esp8266 2.5.0
* Версия библиотеки OneButton 1.3.0
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <OneButton.h>

#define STASSID "****"
#define STAPSK "****"
// Определяем пин-ы для подключения тактовой кнопки и реле
#define BUTTON_PIN 0
#define RELAY_PIN 2

// Включаем контент, который будем отгружать веб сервером
#include "index.h"

// Задаем переменную, в которой храним состояние пина, которым будем включать реле.
// Включать будем низким уровнем, поэтому начальное состояние HIGH, реле выключено
uint8_t relayPinState = HIGH;

OneButton button;
ESP8266WebServer server;

// Определяем enum для управления реле
enum e_relay_control {
  // Включить
  ON,
  // Выключить
  OFF,
  // Переключить
  TOGGLE
};

// Функция управления реле
void relaySwitch(enum e_relay_control control) {
  switch (control) {
    case ON: relayPinState = LOW; break;
    case OFF: relayPinState = HIGH; break;
    default: relayPinState = relayPinState == LOW ? HIGH : LOW; break;
  }
  digitalWrite(RELAY_PIN, relayPinState);
}

// Обработчик нажатия кнопки (фактически срабатывает на отжатие)
void buttonClick() {
  relaySwitch(TOGGLE);
}

void setup() {
  // Настраиваем пин управления реле
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, relayPinState);

  // Настраиваем кнопку, срабатывание по низкому уровню
  button = OneButton(BUTTON_PIN, true);
  button.attachClick(buttonClick);

  // Настраиваем wifi и ожидаем соединения
  WiFi.mode(WIFI_STA);
  WiFi.begin(STASSID, STAPSK);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }

  // Устанавливаем обработчик запроса корневого uri
  server.on("/", HTTP_GET, []() {
  server.setContentLength(CONTENT_LENGTH_UNKNOWN);
  server.send(200, "text/html");
    server.sendContent_P(_index_h, strlen(_index_h));
  });

  // Устанавливаем обработчик запроса статуса
  server.on("/status", HTTP_GET, []() {
    server.send(200, "text/plain", relayPinState == LOW ? "ON" : "OFF");
  });

  // Устанавливаем обработчик запроса переключения реле
  server.on("/relay", HTTP_POST, []() {
    const String& s = server.arg("plain");
    if (s.equals("ON")) {
      relaySwitch(ON);
    } else if (s.equals("OFF")) {
      relaySwitch(OFF);
    } else if (s.equals("TOGGLE")) {
      relaySwitch(TOGGLE);
    }
    server.send(200, "text/plain", relayPinState == LOW ? "ON" : "OFF");
  });

  // Устанавливаем обработчик остальных запросов
  server.onNotFound([]() {
    server.send(404, "text/plain", "Not found");
  });

  // Инициализируем веб сервер на порту 80
  server.begin(80);
}

void loop() {
  // Выполняем обработку нажатий на кнопку
  button.tick();
  // Выполняем обработку запросов к веб серверу
  server.handleClient();
}
Код:
static const char _index_h[] PROGMEM =
"<!doctype html>\n"
"<html\n"
"<head>\n"
"   <meta charset='utf-8' />\n"
"</head>\n"
"<body onload='get_state(); window.setInterval(\"get_state()\", 1000);'>\n"
"   <div id='relayState'></div>\n"
"   <input value='ON' type='button' onclick=\"exec('ON')\" />\n"
"   <input value='OFF' type='button' onclick=\"exec('OFF')\" />\n"
"   <input value='TOGGLE' type='button' onclick=\"exec('TOGGLE')\" />\n"
"</body>\n"
"<script type='text/javascript'>\n"
"   function get_state() {\n"
"       var request = new XMLHttpRequest();\n"
"       request.open('GET','status',true);\n"
"       request.onreadystatechange = function() {\n"
"           if (request.readyState == 4 && request.status==200) {\n"
"               document.getElementById('relayState').innerHTML = request.responseText;\n"
"           }\n"
"       }\n"
"       request.send();\n"
"   }\n"
"   function exec(message) {\n"
"       var request = new XMLHttpRequest();\n"
"       request.open('POST','relay',true);\n"
"       request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');\n"
"       request.onreadystatechange = function() {\n"
"    if (request.readyState == 4 && request.status==200) {\n"
"               document.getElementById('relayState').innerHTML = request.responseText;\n"
"    }\n"
"    }\n"
"       request.send(message);\n"
"   }\n"
"</script>\n"
"</html>\n";
На ноде-мсу вместо реле - светодиод синий, встроенный, как раз к gpio2 подключен
Вместо кнопки просто провод для беспаечного монтажа, замыкаю и размыкаю им gpio0.
Замкнул-разомкнул светодиод включился и на веб-странице состояние изменилось (раз в секунду скриптом запрашиваю статус), ещё раз замкнул-разомкнул и светодиод выключился.
На веб-странице нажал на кнопку, скриптом отправил запрос, в обработчике запроса выполнил переключение светодиода.
Как то так.
Шикарный и рабочий код! Спасибо!
Как сделать на два реле и две кнопки?
Продублировать? И что?
 

cruelty

New member
Вот на копипастил. Двойной wi-fi выключатель с кнопками.
Возможно доработать. Нет синхронизации со страницей и кнопки через секунду пашут. Но работает.
Пины и ip сами выберете, так для демонстрации 0 кнопка и диод встроенный 2

Может кому пригодится.

Код:
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

MDNSResponder mdns;

const char* ssid = "********";
const char* password = "********";

byte arduino_mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
IPAddress ip(192,168,1,242);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);

ESP8266WebServer server(80);

int Relay1 = 2;
int Relay2 = 14;
int But1 = 0;
int But2 = 13;
int flag=0;
int flag1=0;

void setup(void){

  pinMode(Relay1, OUTPUT);
  digitalWrite(Relay1, LOW);
  pinMode(Relay2, OUTPUT);
  digitalWrite(Relay2, LOW);

  delay(100);
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  WiFi.config(ip, gateway, subnet);
 
  Serial.println("");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
   Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
 
  if (mdns.begin("esp8266", WiFi.localIP())) {
    Serial.println("MDNS responder started");
  }
//+++++++++++++++++++++++ START  Relay2 +++++++++++++++++++++++
  server.on("/", [](){
    server.send(200, "text/html", webPage());
  });
  server.on("/socket1On", [](){
    digitalWrite(Relay1, HIGH);
    server.send(200, "text/html", webPage());
    delay(100);
    });
  server.on("/socket1Off", [](){
    digitalWrite(Relay1, LOW);
    server.send(200, "text/html", webPage());
    delay(100);
//+++++++++++++++++++++++ END  Relay1 +++++++++++++++++++++++++

   
//+++++++++++++++++++++++ START  Relay2 +++++++++++++++++++++++
  });
  server.on("/socket2On", [](){
    digitalWrite(Relay2, HIGH);
    server.send(200, "text/html", webPage());
    delay(100);   
  });
  server.on("/socket2Off", [](){
    digitalWrite(Relay2, LOW);
    server.send(200, "text/html", webPage());
    delay(100);
//++++++++++++++++++++++++ END  Relay2 +++++++++++++++++++++++
   
  });
  server.begin();
  Serial.println("HTTP server started");
}

void loop(void){
  server.handleClient();
// ++++++++++++++++++++++++ Buttons +++++++++++++++++++++++++++
if(digitalRead(But1)==HIGH&&flag==0)
{
digitalWrite(Relay1,!digitalRead(Relay1));
flag=1;
}
if(digitalRead(But1)==LOW&&flag==1)
{
flag=0;
}
{
if(digitalRead(But2)==HIGH&&flag1==0)
{
digitalWrite(Relay2,!digitalRead(Relay2));
flag1=1;
}
if(digitalRead(But2)==LOW&&flag1==1)
{
flag1=0;
}
// +++++++++++++++++++++++ Buttons +++++++++++++++++++++++
}
}

String webPage()
{
  String web;
  web += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/> <meta charset=\"utf-8\"><title>Интернет выключатель</title><style>button{color:red;padding: 10px 27px;}</style></head>";
  web += "<h1 style=\"text-align: center;font-family: Open sans;font-weight: 100;font-size: 20px;\">Интернет выключатель</h1><div>";
 
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ Relay-1  ++++++++++++++++++++++++++++++++++++++++++++++++++++
  web += "<p style=\"text-align: center;margin-top: 0px;margin-bottom: 5px;\">Выключатель 1</p>";
  if (digitalRead(Relay1) == 1)
  {
    web += "<div style=\"text-align: center;width: 98px;color:white ;padding: 10px 30px;background-color: #43a209;margin: 0 auto;\">Включен</div>";
  }
  else
  {
    web += "<div style=\"text-align: center;width: 98px;color:white ;padding: 10px 30px;background-color: #ec1212;margin: 0 auto;\">Выключен</div>";
  }
  web += "<div style=\"text-align: center;margin: 5px 0px;\"> <a href=\"socket1On\"><button>ON</button></a>&nbsp;<a href=\"socket1Off\"><button>OFF</button></a></div>";
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ Relay-1  ++++++++++++++++++++++++++++++++++++++++++++++++++++

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ Relay-2  ++++++++++++++++++++++++++++++++++++++++++++++++++++
  web += "<p style=\"text-align: center;margin-top: 0px;margin-bottom: 5px;\">Выключатель 2</p>";
  if (digitalRead(Relay2) == 1)
  {
    web += "<div style=\"text-align: center;width: 98px;color:white ;padding: 10px 30px;background-color: #43a209;margin: 0 auto;\">Включен</div>";
  }
  else
  {
    web += "<div style=\"text-align: center;width: 98px;color:white ;padding: 10px 30px;background-color: #ec1212;margin: 0 auto;\">Выключен</div>";
  }
  web += "<div style=\"text-align: center;margin: 5px 0px;\"> <a href=\"socket2On\"><button>ON</button></a>&nbsp;<a href=\"socket2Off\"><button>OFF</button></a></div>";
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ Relay-2  ++++++++++++++++++++++++++++++++++++++++++++++++++++
   
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ REFRESH ++++++++++++++++++++++++++++++++++++++++++++++++++++
  web += "<div style=\"text-align:center;margin-top: 20px;\"><a href=\"/\"><button style=\"width:158px;\">Обновить</button></a></div>";
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ REFRESH ++++++++++++++++++++++++++++++++++++++++++++++++++++
 
  web += "</div>";
  return(web);
}
 
Сверху Снизу