Товарищи!
Делаю автополив. Создавал макет с диодом и работал ориентируясь на диодик (горит - клапан открыт, не горит - полива нет). Купил потом релюшку, подключил через неё ЭМ Клапан от стиралки (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 "- ошибка определения статуса!";
}