РЕЛЕ не работает как надо? Не переключается

Товарищи!
Делаю автополив. Создавал макет с диодом и работал ориентируясь на диодик (горит - клапан открыт, не горит - полива нет). Купил потом релюшку, подключил через неё ЭМ Клапан от стиралки (220В) - не работает как надо и всё… Купил модуль реле… И опять не работает.
Написал самый простой скеч - 5 секунд клапан открыт, 5 секунд закрыт. Но все равно не работает. Выясняется, что питание на реле просто не переключается, хоть там HIGH на управляющем контакте, хоть LOW. Оно просто включено всегда, когда подключен пин питания. При чем код исполняется нормально. Поставил диод на другой пин, он зажигается как положено, а реле не переключается.

бело-желтый - питание 5В
синий-коричневый - GND
зелёный - управляющий (16)
оранжевый - диод (22)

Простой код -
#define RELAY_PIN 16 // ESP32 pin GPIO16, which connects to the water valve via the relay

// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin A5 as an output.
Serial.begin(115200);
pinMode(RELAY_PIN, OUTPUT);
pinMode(22, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
digitalWrite(RELAY_PIN, HIGH); // open valve 5 seconds
Serial.println(“Включили клапан!”);
digitalWrite(22, HIGH);
Serial.println(“Включили диод!”);

delay(5000);
Serial.println(“…Подождали…”);
digitalWrite(RELAY_PIN, LOW); // close valve 5 seconds
Serial.println(“Клапан отключён!”);
digitalWrite(22, LOW);
Serial.println(“Диод отключён!”);

delay(5000);
}

вот код самого проекта если что (там не доделан полив по расписанию)

#include <GyverTimer.h>
#include <WiFi.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
WiFiManager wm;


    // замените своими сетевыми учетными данными
const char* ssid = "*********";
const char* password = "**********";

    // установите номер порта веб-сервера на 80
WiFiServer server(80);
    // переменная для хранения HTTP запроса
String header;
    // вспомогательные переменные для хранения текущего состояния вывода 
String toggleIrrState = "off";
String autoIrrState = "off";
String planIrrState = "off";
boolean irrigationState = false;
boolean planSet = false;
    // назначаем выходные переменные контактам GPIO
const int output16 = 16;
    // текущее время
unsigned long currentTime = millis();
    // предыдущее время
unsigned long previousTime = 0;
    // Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;

GTimer DAY_timer(MS);
unsigned long DAY_time = 60000;  // должно быть 24 часа = 86 400 000
GTimer IRRIGATION_timer(MS);
unsigned long IRRIGATION_time = 10000;  // устанавливается время когда клапан будет открыт 2 часа = 7200000

void setup() {
    WiFi.mode(WIFI_STA); // явно заданный режим, esp по умолчанию имеет значение STA+AP    
    
    Serial.begin(115200);
    
    //сбросить настройки - удалить учетные данные для тестирования
    //wm.resetSettings();

    wm.setConfigPortalBlocking(false);
    wm.setConfigPortalTimeout(60);
    //автоматическое подключение с использованием сохраненных учетных данных, если они существуют
    //При сбое подключения запускается точка доступа с указанным именем
    if(wm.autoConnect("AutoConnectAP")){
        Serial.println("connected...yeey :)");
    }
    else {
        Serial.println("Configportal running");
    }
//////////////////////////////////////////////////
    pinMode(output16, OUTPUT);
    digitalWrite(output16, LOW);

    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    }
      // печатаем  локальный IP адрес и запускаем веб-сервер
    Serial.println("");
    Serial.println("WiFi connected.");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
    server.begin();
}

void loop() {
    wm.process();
    // put your main code here, to run repeatedly:
    String but_1 = "Полив ";
    String but_2 = "Автополив 60 минут каждые 24 часа ";
    String but_3 = "Полив по расписанию ";

    if(DAY_timer.isReady()) {  // проверка если таймер завершился, то он запускается снова на заданный интервал (сутки)
      openWaterAtTime();
    }

    if (IRRIGATION_timer.isReady()){        // когда время полива закончилось...
          Serial.print("время полива истекло! - ");
          Serial.println(IRRIGATION_timer.isEnabled());  
      digitalWrite(output16, LOW);        // Клапан закрывается, подача воды прекращвется до следующего дня
      irrigationState = false;
          Serial.println("Клапан ЗАКРЫТ");
    }

  WiFiClient client = server.available();   // прослушиваем входящих клиентов
  if (client) {                             // если подключается новый клиент,
    currentTime = millis();
    previousTime = currentTime;
    Serial.println("New Client.");          // выводим сообщение в последовательный порт
    String currentLine = "";                // создайте строку для хранения входящих данных от клиента
    while (client.connected() && currentTime - previousTime <= timeoutTime) {  // loop while the client's connected
      currentTime = millis();
      if (client.available()) {             // если есть байты для чтения с клиента,
        char c = client.read();             // считайте байт, а затем
        Serial.write(c);                    // распечатайте его на последовательном мониторе
        header += c;
        if (c == '\n') {                    // если байт является символом новой строки
          // если текущая строка пуста, вы получаете два символа новой строки подряд.
          // это конец клиентского HTTP-запроса, поэтому отправьте ответ:
          if (currentLine.length() == 0) {
            // Заголовки HTTP всегда начинаются с кода ответа (например, HTTP/1.1 200 OK)
            // и типа содержимого, чтобы клиент знал, что последует, а затем с пустой строки:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
            
            // включает и выключает GPIO
            if(header.indexOf("GET /") ==0){
              planSet==false;
            }
            if (header.indexOf("GET /toggle/on") >= 0) {
              toggleIrrState = "on";
              digitalWrite(output16, HIGH);
              irrigationState = true;
              Serial.println("Пошёл полив по нажатию кнопки.");
              planSet==false;
            } 
            else if (header.indexOf("GET /toggle/off") >= 0) {
              toggleIrrState = "off";
              digitalWrite(output16, LOW);
              irrigationState = false;
              Serial.println("Кнопку нажали - Полив закончился");
              planSet==false;
            } 

            if (header.indexOf("GET /autoIr/on") >= 0) {
              Serial.println("GPIO 16 on");
              autoIrrState = "on";
                            
              if (!DAY_timer.isEnabled()){    // Если таймера нет, 
                    delay(500);
                    Serial.println(DAY_timer.isEnabled());
                DAY_timer.setInterval(DAY_time); // то запускаем его на сутки
                    Serial.println(DAY_timer.isEnabled());
                openWaterAtTime();
                    delay(500);
              }
              planSet==false;
            } 
            else if (header.indexOf("GET /autoIr/off") >= 0) {
              Serial.println("GPIO 16 off");
              autoIrrState = "off";
              digitalWrite(output16, LOW);
              irrigationState = false;
              DAY_timer.stop();
              IRRIGATION_timer.stop();
              Serial.println("АВТОполив отключен.");
              planSet==false;
            }
          
            if (header.indexOf("GET /planIr/set") >= 0) {
              planSet = true;
            } 

            if (header.indexOf("GET /planIr/on") >= 0) {
              planIrrState = "on";
              digitalWrite(output16, HIGH);
              irrigationState = true;
              Serial.println("Настройка полива по расписанию.");
              planSet==false;
            } 
            else if (header.indexOf("GET /planIr/off") >= 0) {
              planIrrState = "off";
              digitalWrite(output16, LOW);
              irrigationState = false;
              Serial.println("Кнопку нажали - Полив закончился");
              planSet==false;
            } 

            // Отобразить веб-страницу в формате HTML
            client.println("<!DOCTYPE html> <html lang=\"ru\">");
            client.println("<head><meta  name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<meta charset=\"UTF-8\">");
//            client.println("<meta http-equiv=\"refresh\" content=\"25\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            client.println("<title>Полив</title>");
            // CSS для оформления кнопок включения / выключения
            // Не стесняйтесь изменять цвет фона и размер шрифта в соответствии со своими предпочтениями
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #555555;}</style></head>");
            // Заголовок веб-страницы
            client.println("<body><h1>-- СИСТЕМА ПОЛИВА ТЕПЛИЦЫ --</h1>");
            
            // Отображение текущей даты
            client.println("<div id=\"current_date\">");
            client.println("<script src=\"https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js\"></script>");
            client.println("<script>const formattedDate = moment().format('YYYY-MM-DD');");
            client.println("document.getElementById(\"current_date\").innerHTML = formattedDate;</script></div>");
            // Отображение текущего времени
            client.println("<time id=\"currentTime\"></time>");
            client.println("<script> var timeElement = document.getElementById('currentTime');");
            client.println("setInterval(function () {");
            client.println("var currentTime = new Date(); timeElement.textContent = currentTime.toLocaleTimeString();");
            client.println("}, 1000); </script>");

            if (toggleIrrState=="off" && autoIrrState=="off" & planIrrState=="off" && planSet==false) {
              client.println("<p>" + but_1 + getStatusOfButton(toggleIrrState) + "</p>");
              client.println("<p><a href=\"/toggle/on\"><button class=\"button\">Включить</button></a></p>");
              client.println("<p>" + but_2 + getStatusOfButton(autoIrrState) + "</p>");
              client.println("<p><a href=\"/autoIr/on\"><button class=\"button\">Включить</button></a></p>");

              client.println("<p><p>Выбирете с какого дня включить расписание</p>");
              client.println("<p><input id=\"input_date_start\" type=\"date\">");

              client.println("<p><label>Выбирете время начала и окончания полива:</p>");
              client.println("<p><input id=\"input_time_start\" type=\"time\">  <input id=\"input_time_stop\" type=\"time\"></label></p>");

              client.println("<p>" + but_3 + getStatusOfButton(planIrrState) + "</p>");
              client.println("<p><a href=\"/planIr/on\"><button class=\"button\">Включить</button></a></p>");

              client.println("<script> document.getElementById('input_date_start').innerHTML = formattedDate;");
              client.println("  let input_date_start = document.getElementById('input_date_start');");
              client.println("  let input_time_start = document.getElementById('input_time_start');");
              client.println("  let input_time_stop = document.getElementById('input_time_stop');");

              client.println("  input_date_start.addEventListener('input', function (e) {console.log(e.target.value)});");
              client.println("  input_time_start.addEventListener('input', function (e) {console.log(e.target.value)});");
              client.println("  input_time_stop.addEventListener('input', function (e) {console.log(e.target.value)});");

              client.println("  button_irrig_plan.addEventListener('button', function (e){console.log(e.target.value)});");

              client.println("</script>");

            } 
            if (toggleIrrState=="on" && autoIrrState=="off" && planIrrState=="off") {
              client.println("<p>" + but_1 + getStatusOfButton(toggleIrrState) + "</p>");
              client.println("<p><a href=\"/toggle/off\"><button class=\"button button2\">Отключить</button></a></p>");
              client.println("<p>" + but_2 + getStatusOfButton(autoIrrState) + "</p>");
              client.println("<p><a href=\"/autoIr/on\"><button disabled class=\"button\">Включить</button></a></p>");
              client.println("<p>" + but_3 + getStatusOfButton(planIrrState) + "</p>");
              client.println("<p><a href=\"/planIr/on\"><button disabled class=\"button\">Включить</button></a></p>");
            } 
             if (toggleIrrState=="off" && autoIrrState=="on" && planIrrState=="off") {
              client.println("<p>" + but_1 + getStatusOfButton(toggleIrrState) + "</p>");
              client.println("<p><a href=\"/toggle/on\"><button disabled class=\"button\">Включить</button></a></p>");
              client.println("<p>" + but_2 + getStatusOfButton(autoIrrState) + "</p>");
              client.println("<p><a href=\"/autoIr/off\"><button class=\"button button2\">Отключить</button></a></p>");
              client.println("<p>" + but_3 + getStatusOfButton(planIrrState) + "</p>");
              client.println("<p><a href=\"/planIr/on\"><button disabled class=\"button\">Включить</button></a></p>");
            }
            if (toggleIrrState=="off" && autoIrrState=="off" && planIrrState=="on") {
              client.println("<p>" + but_1 + getStatusOfButton(toggleIrrState) + "</p>");
              client.println("<p><a href=\"/toggle/on\"><button disabled class=\"button\">Включить</button></a></p>");
              client.println("<p>" + but_2 + getStatusOfButton(autoIrrState) + "</p>");
              client.println("<p><a href=\"/autoIr/on\"><button disabled class=\"button\">Включить</button></a></p>");
              client.println("<p>" + but_3 + getStatusOfButton(planIrrState) + "</p>");
              client.println("<p><a href=\"/planIr/off\"><button class=\"button button2\">Отключить</button></a></p>");
            }

    
            if(irrigationState){
              client.println("<p><label><b>. . . ИДЁТ ПОЛИВ . . .</label></p>");
            } else 
            client.println("<p><label><b> ---- ВОДА ПЕРЕКРЫТА ---- </label></p>");

            

            client.println("</body></html>");
            // ответ HTTP заканчивается еще одной пустой строкой
            client.println();
            // выходим из цикла while
            break;
          } else { // если появилась новая строка, очистим текущую строку
            currentLine = "";
          }
        } else if (c != '\r') {  // если у вас еще есть что то кроме символа возврата каретки,
          currentLine += c;      // добавляем его в конец текущей строки
        }
      }
    }
    // очищаем переменную заголовка
    header = "";
    // закрываем соединение
    client.stop();
    Serial.println("Client disconnected.");
   // Serial.println("");
  }
}
  // Функция включения полива по таймеру
void openWaterAtTime(){
  digitalWrite(output16, HIGH);  // открываем клапан
  irrigationState = true;
  IRRIGATION_timer.setTimeout(IRRIGATION_time);  // запускается таймер на время полива
      Serial.println("Клапан открылся - ПОЛИВ начался");
      Serial.print("Запущен таймер полива - ");
          Serial.println(IRRIGATION_timer.isEnabled());
}
  // Функция получения статуса кнопки
String getStatusOfButton(String buttonState){  
  if (buttonState=="on"){
    return "> Включен <";
  } 
  if (buttonState == "off"){
    return "- Выключен -";
  } else return "- ошибка определения статуса!";
}

Непонятно, в чем состоит вопрос.
Если реле неисправно, его нужно заменить.
И от кода это никак не зависит.

А если управляющий провод реле в +5 и землю потыкать что происходит?

Такое может, что просто реле неисправно?

Земли общей не вижу на фото.

Это интересно.
Если поменять местами питающий и управление, то зелёная лампочка на реле гаснет, а красная мигает синхронно с диодом, как по исполнению кода, но реле не включается.
Если поменять со штатной раскладки управляющий и GND, то реле начинает включаться и выключаться, но в противофазу с диодом… То есть вроде даже и работает. Но если я нажму кнопку полива в браузере, реле включится, а когда нажму отключить, оно щелкнет, но клапан при этом не отключается. Только из розетки дергать… :man_shrugging:

Земля на макетной плате же - коричневый провод в синюю линию, из неё синий провод в реле

Может потому, что ESP32 3.3 вольта логика, а реле рассчитано на 5В? Проверьте реле на ардуино uno\nano, если есть конечно

И у меня была такая мысль, но мне знакомый сказал, что

“3 вольта должно быть. Или 5 вольт с внешним питанием.”

Но и почему тогда она включает и выключает если с землей провода поменять…
Ардуинки нет другой, только ESP

Если я правильно понял даташит, то максимальное напряжение срабатывания этого реле может достигать 3.75В.
Не факт конечно, но и логики 3.3 В может быть маловато

P.S. Если это оно
https://denkovi.com/Documents/JQC-3FF-S-Z.pdf

Это нормально. У реле 2 контакта. Один в фазе, другой в противофазе. Перевесь на второй и будет в фазе.

А вот здесь интересно. Если реле щёлкнуло, то должен произойти разрыв. А если вместо клапана лампочку подключить - как она себя ведёт?

Такие модули реле обычно управляются нулем. Поэтому, 5В там или 3.3В логика - фиолетово.

Бывают НИЗКОГО и ВЫСОКОГО уровня. Так же видел с возможностью переключения.
Встречал как-то даже указание, что надо именно +5В, или нужен преобразователь уровней

P.S. Например
Screenshot_122
здесь

Напрямую управлять этим реле логикой 3.3 В не получится. Там реле управляется рнр транзистором. Проще всего управлять через оптрон.

Гадать и спорить между собой “о вкусе устриц и кокосовых орехов” мы можем бесконечно долго, пока ТС не соизволит нам разъяснить, что же у него за модуль. Или хотя бы фото крупным планом с маркировкой транзистора.

3 лайка

А если просто поболтать) Я вот седня каменьщиком работаю. Сел отдохнуть, зашел на форум, а тут опять реле не релит). Как тут быть без совета каменьщика.

1 лайк

Вот у меня в точности такой же модуль

Значит, он управляется нулем, т.е. реле включается при подаче 0 на вход (IN).

[quote=“nik182, post:11, topic:14322”]
Это нормально. У реле 2 контакта. Один в фазе, другой в противофазе. Перевесь на второй и будет в фазе.
[/quote]
Да, но должно то наоборот… Клапан подключен к контакту на реле NO.

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

А на управляющий вообще не цеплять ничего?