Категории

Умный полив

22.07.2025 09:33 | коды из категории: IOT умный дом

на даче задумал теплицу, а теплица без полива как вобла без пива ) короч полив определяет когда влажность повышена, дождь, влажность почвы, уровень в баке, температура воздуха

про Умный полив

Код для умного полива. по температуре воздуха, по влажности воздуха, влажность почвы и увроень в баке на электромеханическом кране для капельного полива

#include <LiquidCrystal_I2C.h>
#include <Adafruit_Sensor.h>
#include <HTU21D.h>
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>

// Датчики и пины
HTU21D sensor;                   // Датчик температуры/влажности
int zemlyaPin = A3;              // Пин датчика влажности почвы
#define nasos 10                  // Пин управления насосом
#define gerkon 7                 // Пин поплавкового датчика

// Пины для крана
#define valveOpenPin 12          // Реле открытия крана
#define valveClosePin 11          // Реле закрытия крана

// Переменные состояния
int zemlya = 0;                  // Значение датчика почвы
int suhota_zemli = 0;            // Счетчик сухости почвы
int voda_bochka = 0;             // Счетчик уровня воды
int temper = 0;                  // Счетчик температуры
int vlazh = 0;                   // Счетчик влажности
int kolvo = 0;                   // Счетчик поливов
int nasosok = 1;                 // Состояние насоса
int gerkon_triger = 0;           // Триггер поплавкового датчика
int displayPage = 0;             // Страница дисплея
int tttt = 1;                    // Флаг отладки

// Временные метки
tmElements_t lastTempCheck;      // Время последней проверки температуры
tmElements_t lastPolivStart;     // Время начала полива
tmElements_t lastDisplayUpdate;  // Время обновления дисплея

unsigned long polivDuration = 2000000; // Продолжительность полива в мс (например, 3.5 минуты)
unsigned long polivStartTime = 0;      // Время начала полива часы
bool isPolivRunning = false;          // Флаг: идёт ли полив?

// Константы
const byte POLIV_HOUR = 20;      // Час автоматического полива
const int VALVE_TIME = 11000;    // Время открытия/закрытия крана (10 сек)

// Инициализация LCD
LiquidCrystal_I2C lcd(0x27, 20, 4);

// Перечисление состояний крана
enum ValveState {
  VALVE_IDLE,
  VALVE_OPENING,
  VALVE_CLOSING
};

ValveState valveStatus = VALVE_IDLE;
unsigned long valveStartTime = 0;

// Функция вывода времени на дисплей
void printTime(int hours, int minutes) {
  if (hours < 10) lcd.print("0");
  lcd.print(hours);
  lcd.print(":");
  if (minutes < 10) lcd.print("0");
  lcd.print(minutes);
}

void setup() {
  Serial.begin(9600);
  while (!Serial); // Ожидание инициализации Serial

  // Инициализация датчиков
  sensor.begin();

  // Настройка дисплея
  lcd.init();
  lcd.backlight();

  // Настройка пинов
  pinMode(gerkon, INPUT);
  pinMode(nasos, OUTPUT);
  pinMode(valveOpenPin, OUTPUT);
  pinMode(valveClosePin, OUTPUT);
 
  digitalWrite(nasos, HIGH);       // Насос выключен
  digitalWrite(valveOpenPin, HIGH); // Выключить реле открытия
  digitalWrite(valveClosePin, HIGH); // Выключить реле закрытия

  // Инициализация времени
  if (RTC.read(lastTempCheck)) {
    lastPolivStart = lastDisplayUpdate = lastTempCheck;
  }
}

void openValve() {
  if (valveStatus != VALVE_IDLE) return;

  valveStatus = VALVE_OPENING;
  valveStartTime = millis();
  digitalWrite(valveOpenPin, LOW); // Включаем открытие
  digitalWrite(valveClosePin, HIGH); // Убедиться, что закрыто
  Serial.println("Кран: Открываем...");
}

void closeValve() {
  if (valveStatus != VALVE_IDLE) return;

  valveStatus = VALVE_CLOSING;
  valveStartTime = millis();
  digitalWrite(valveClosePin, LOW); // Включаем закрытие
  digitalWrite(valveOpenPin, HIGH); // Убедиться, что открыто
  Serial.println("Кран: Закрываем...");
}

void loop() {
  tmElements_t tm;

  if (RTC.read(tm)) {
    zemlya = analogRead(zemlyaPin); // Чтение датчика почвы
    int buttonState = digitalRead(gerkon); // Чтение поплавкового датчика

    // Обработка состояния крана
    switch (valveStatus) {
      case VALVE_OPENING:
        if (millis() - valveStartTime >= VALVE_TIME) {
          digitalWrite(valveOpenPin, HIGH);
          valveStatus = VALVE_IDLE;
          Serial.println("Кран: Открыт.");
        }
        break;
      case VALVE_CLOSING:
        if (millis() - valveStartTime >= VALVE_TIME) {
          digitalWrite(valveClosePin, HIGH);
          valveStatus = VALVE_IDLE;
          Serial.println("Кран: Закрыт.");
        }
        break;
      default:
        break;
    }

    // Проверка условий каждую минуту в час полива
    if (tm.Hour == POLIV_HOUR && tm.Minute != lastTempCheck.Minute) {
      lastTempCheck = tm;

      // Чтение температуры и влажности
      if (sensor.measure()) {
        float temperature = sensor.getTemperature();
        float humidity = sensor.getHumidity();

        // Обновление счетчиков
        if (temperature > 18) temper++;
        if (humidity > 70) vlazh++;
      }

      // Обновление состояния почвы и бака
      if (zemlya > 260) suhota_zemli++; else suhota_zemli--;
      if (buttonState == 1) voda_bochka++; else voda_bochka--;
    }

    // Сброс счетчиков в 10:00
    if (tm.Hour == 10 && tm.Minute == 0) {
      nasosok = 1;
      vlazh = temper = kolvo = 0;
    }

    // Логика включения полива
if (tm.Hour == POLIV_HOUR && nasosok == 1) {
  if (temper > 10 && vlazh < 10 && voda_bochka > 10 && suhota_zemli > 10 && tm.Minute < 48 ) {
    openValve();
    delay(1000); // короткая пауза, чтобы кран успел открыться
    digitalWrite(nasos, LOW); // Включаем насос
    nasosok = 2;
    isPolivRunning = true;
    polivStartTime = millis(); // Запоминаем время старта
    lastPolivStart = tm;
  }
}

    // Выключение насоса через 10 минут
if (isPolivRunning && (millis() - polivStartTime >= polivDuration)) {
  digitalWrite(nasos, HIGH); // Выключаем насос
  closeValve();              // Закрываем кран
  nasosok = 3;
  isPolivRunning = false;
  kolvo++;
}

    // Обновление дисплея каждые 3 секунды
    if (tm.Second % 3 == 0 && tm.Second != lastDisplayUpdate.Second) {
      lastDisplayUpdate = tm;
      displayPage = (displayPage + 1) % 4;
      lcd.clear();

      switch (displayPage) {
        case 0: // Основная информация
          lcd.setCursor(0, 0);
          lcd.print("bochka:");
          lcd.print(buttonState);
          lcd.setCursor(10, 0);
          printTime(tm.Hour, tm.Minute);

          lcd.setCursor(0, 1);
          lcd.print(zemlya < 260 ? "Vlazhno:" : "Suho:");
          lcd.print(zemlya);
          break;

        case 1: // Показания датчиков
          if (sensor.measure()) {
            float t = sensor.getTemperature();
            float h = sensor.getHumidity();
            lcd.setCursor(0, 0);
            lcd.print("T:"); lcd.print(t,1); lcd.print("C H:"); lcd.print(h,1); lcd.print("%");
          } else {
            lcd.print("error_datchik!");
          }
          lcd.setCursor(0, 1);
          lcd.print("Polivov:"); lcd.print(kolvo);
          break;

        case 2: // Состояние системы
          lcd.setCursor(0, 0);
          lcd.print("Nasos:");
          lcd.print(nasosok == 1 ? "Gotov" : nasosok == 2 ? "Navlivaet" : "Nalito");

          lcd.setCursor(0, 1);
          lcd.print("Kran: ");
          if (valveStatus == VALVE_OPENING) lcd.print("Otkryvayetsya");
          else if (valveStatus == VALVE_CLOSING) lcd.print("Zakryvaetsya");
          else lcd.print("OK");
          break;

case 3: // Счетчики + обратный отсчёт
  lcd.setCursor(0, 0);
  lcd.print("Temp:"); lcd.print(temper);
  lcd.setCursor(0, 1);
  lcd.print("Vlazh:"); lcd.print(vlazh);

  if (isPolivRunning) {
    unsigned long elapsed = millis() - polivStartTime;
    int remainingSec = (polivDuration - elapsed) / 1000;

    lcd.setCursor(9, 0);
    lcd.print("s:");
    int minutes = remainingSec / 60;
    int seconds = remainingSec % 60;
    if (minutes < 10) lcd.print("0");
    lcd.print(minutes);
    lcd.print(":");
    if (seconds < 10) lcd.print("0");
    lcd.print(seconds);
  } else {
    lcd.setCursor(9, 2);
    lcd.print("errrr");
  }
  break;
      }
    }
  }
  delay(200);
}



Вот тут еще есть старые коды, походу метеостанцию делал с поливов. не проверял просто сохранил тут. может че полезное
Описание системы
Эта метеостанция с элементами умной теплицы включает:
Мониторинг окружающей среды:
Температура и влажность (HTU21D)
Атмосферное давление (BMP180)
Уровень CO2 (PWM датчик)
Концентрация вредных газов (CO, NH3, NO2) с помощью MICS6814
Уровень формальдегида (ZE08)
Система управления поливом:
Автоматический полив по расписанию (6:00 и 21:00)
Дополнительный ночной полив насосом (1:00)
Защита от полива при высокой влажности или низкой температуре
Управление кранами и насосом через реле
Интерфейс и безопасность:
LCD дисплей для отображения данных
Кнопочное управление
Звуковая сигнализация при обнаружении опасных уровней газов
Логика работы:
Автоматическое управление поливом по времени
Защита растений от перелива
Мониторинг качества воздуха
Визуализация всех параметров на дисплее
Система предназначена для автоматического контроля условий в теплице или оранжерее с возможностью ручного управления через кнопки.

#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <LiquidCrystal_I2C.h>
#include "SparkFunHTU21D.h"     // Датчик температуры и влажности
#include "SFE_BMP180.h"        // Датчик давления
#include <MICS6814.h>          // Мультигазовый датчик
#include <SoftwareSerial.h>

// Определение пинов
#define relayDOWN 3            // Реле для опускания (закрытия) крана
#define relayUP 4              // Реле для поднятия (открытия) крана
#define relayNASOS 7           // Реле насоса полива
#define pwmPin 6               // Пин датчика CO2 (PWM)
#define buttonPin A6           // Пин кнопки
#define buzzPin 5              // Пин пищалки

// Датчики газа
#define PIN_CO  A2             // Пин датчика CO
#define PIN_NO2 A3             // Пин датчика NO2
#define PIN_NH3 A1             // Пин датчика NH3

// Настройки для формальдегидного датчика
#define MAXLENGTH 9
#define VREF 5.0               // опорное напряжение для аналоговых датчиков
SoftwareSerial mySerial(10, 11); // RX, TX для датчика формальдегида

// Объекты датчиков
HTU21D myHumidity;             // Датчик влажности и температуры
SFE_BMP180 pressure;           // Датчик давления
MICS6814 gas(PIN_CO, PIN_NO2, PIN_NH3); // Мультигазовый датчик
LiquidCrystal_I2C lcd(0x27,16,2); // LCD дисплей I2C

// Переменные для датчика CO2
int prevVal = LOW;
long th, tl, h, l, ppm;

// Переменные для управления системой
int value = 25;
long prev_gaz = 0;
long prev_upbut = 0;
long prev_poliv = 0;
long prev_buzz = 0;

// Флаги состояний
int trig_up = 0;
int trig_down = 0;
int trig_center = 0;
int poliv = 0;
int button = 0;
int gaz = 0;
int sensorValue = 0;
int prev_pump = 0;
int prevrain = 0;
int rain_triger = 0;
int polivkapel_trig = 0;
int polivnasos_trig = 0;
int val = 0;
int davlenie = 0;
int poliv_val = 0;
int kol_vo = 0;  
int fff = 0;

// Тайминги
long nasos_time = 30000;
int KRANdelay = 12000;
unsigned long NASOSdelay = 0;
unsigned long relayUPdelay = 0;
unsigned long relayDOWNdelay = 0;
long KRANtrig = 0;
long NASOStrig = 0;
long pressuredelay = 0;
long poliv_off = 0;

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600); // Для датчика формальдегида
  
  // Настройка пина датчика CO2
  pinMode(pwmPin, INPUT);
  
  // Инициализация LCD
  lcd.init();                     
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("kalibrovka");  
  
  // Инициализация датчиков
  myHumidity.begin();
  pressure.begin();
  gas.calibrate();
  
  // Настройка пинов
  pinMode(buzzPin, OUTPUT);
  pinMode(buttonPin, INPUT);
  pinMode(relayDOWN, OUTPUT);
  pinMode(relayUP, OUTPUT);
  pinMode(relayNASOS, OUTPUT);
  
  // Начальные состояния реле
  digitalWrite(relayDOWN, HIGH);
  digitalWrite(relayUP, HIGH);
  digitalWrite(relayNASOS, HIGH);
  
  delay(200);
  lcd.clear();
}

void loop() {
  // Измерение CO2
  measureCO2();
  
  // Измерение формальдегида
  ze08_PPM();
  
  // Обработка кнопок
  handleButtons();
  
  // Измерение давления
  measurePressure();
  
  // Чтение температуры и влажности
  int humd = myHumidity.readHumidity();
  int temp = myHumidity.readTemperature();
  
  // Управление поливом по времени
  controlWateringSystem(temp, humd);
  
  // Чтение показаний газовых датчиков
  int COCO = gas.measure(CO);
  int NHH = gas.measure(NH3);
  int NOO = gas.measure(NO2);
  
  // Отображение данных на LCD
  updateDisplay(temp, humd, davlenie, COCO, NHH, NOO, ppm);
  
  // Проверка на опасные уровни газов
  checkGasLevels(COCO, NHH, NOO, ppm);
  
  // Управление звуковым сигналом
  controlBuzzer();
}

// Функция измерения CO2
void measureCO2() {
  long tt = millis();
  int myVal = digitalRead(pwmPin);

  if (myVal == HIGH) {
    if (myVal != prevVal) {
      h = tt;
      tl = h - l;
      prevVal = myVal;
    }
  } else {
    if (myVal != prevVal) {
      l = tt;
      th = l - h;
      prevVal = myVal;
      ppm = 5000 * (th - 2) / (th + tl - 4);
      fff = ppm;
    }
  }
  
  // Фильтрация неверных значений CO2
  if (!(fff > 1650 && fff < 1680) && fff != 1480 && fff != 2037 && 
      !(fff > 1240 && fff < 1250) && fff != 199 && fff != 262 && fff != 970) {
    lcd.setCursor(6, 1);
    lcd.print(":" + String(ppm) + "*");
  }
}

// Функция обработки кнопок
void handleButtons() {
  int btnValue = analogRead(buttonPin);
  
  if (btnValue < 700 && btnValue > 500 && millis() - prev_upbut > 2000) {
    prev_upbut = millis();
    trig_center = (trig_center + 1) % 5; // Циклическое изменение от 0 до 4
  }
  
  if (btnValue < 1050 && btnValue > 1000 && millis() - prev_upbut > 2000) {
    prev_upbut = millis();
    trig_up = (trig_up + 1) % 4; // Циклическое изменение от 0 до 3
  }

  if (btnValue < 280 && btnValue > 310 && millis() - prev_upbut > 2000) {
    prev_upbut = millis();
    trig_down = (trig_down + 1) % 4; // Циклическое изменение от 0 до 3
  }
}

// Функция измерения давления
void measurePressure() {
  char status;
  double T, P, p0, a;
  
  status = pressure.startTemperature();
  if (status != 0) {
    delay(status);
    status = pressure.getTemperature(T);
    if (status != 0) {
      status = pressure.startPressure(3);
      if (status != 0) {
        delay(status);
        status = pressure.getPressure(P, T);
        if (status != 0) {
          p0 = pressure.sealevel(P, ALTITUDE);
          davlenie = P;
        }
      }
    }
  }
}

// Функция управления системой полива
void controlWateringSystem(int temp, int humd) {
  tmElements_t tm;
  
  if (RTC.read(tm)) {
    // Утренний полив (6 часов)
    if (tm.Hour == 6 && polivkapel_trig == 0 && poliv_off == 0) {
      polivkapel_trig = 1;
      digitalWrite(relayUP, LOW); // Открыть кран
      relayUPdelay = millis(); 
    }

    // Закрытие крана после 12 секунд
    if (millis() - relayUPdelay > 12000 && polivkapel_trig == 1) {
      digitalWrite(relayUP, HIGH);
      polivkapel_trig = 2;
    }  

    // Вечерний полив (21 час)
    if (tm.Hour == 21 && polivkapel_trig == 3) {
      polivkapel_trig = 4;
      digitalWrite(relayDOWN, LOW); // Закрыть кран
      relayDOWNdelay = millis();
    }    
    
    // Завершение закрытия крана
    if (millis() - relayDOWNdelay > 12000 && polivkapel_trig == 4) {
      digitalWrite(relayDOWN, HIGH);
      polivkapel_trig = 5;
    }  
 
    // Ночной полив насосом (1 час)
    if (tm.Hour == 1 && polivnasos_trig == 0) {
      polivnasos_trig = 1;
      digitalWrite(relayNASOS, LOW); // Включить насос
      NASOSdelay = millis();
    }

    // Выключение насоса через 100 секунд
    if (millis() - NASOSdelay > 100000 && polivnasos_trig == 1) {
      digitalWrite(relayNASOS, HIGH);
      polivnasos_trig = 2;
    }   

    // Сброс триггеров в 3 часа ночи
    if (tm.Hour == 3) {
      polivnasos_trig = 0;
      polivkapel_trig = 0;
    }

    // Закрытие крана при высокой влажности или низкой температуре
    if ((humd > 70 || temp < 15) && polivkapel_trig == 2) {
      polivkapel_trig = 3;
    }
    
    // Общий сброс при неблагоприятных условиях
    if (humd > 70 || temp < 15) {
      poliv_off = 1;
    } else {
      poliv_off = 0;
    }
  }
}

// Функция обновления дисплея
void updateDisplay(int temp, int humd, int press, int CO, int NH3, int NO2, int co2) {
  lcd.setCursor(0, 0);
  lcd.print(temp);
  lcd.print(":");  
  lcd.print(humd);
  lcd.print(":");   
  lcd.print(int(press * 0.75)); // Преобразование в мм рт.ст.
  
  lcd.setCursor(0, 1);
  lcd.print(CO);
  lcd.print(":");    
  lcd.print(NH3);
  lcd.print(":");    
  lcd.print(NO2); 
}

// Функция проверки уровня газов
void checkGasLevels(int CO, int NH3, int NO2, int co2) {
  if (CO > 50 || NH3 > 2 || NO2 > 2 || co2 > 2000) {
    gaz = 1;
  }
  
  // Сброс тревоги через 20 секунд
  if (millis() - prev_gaz > 20000 && gaz == 1) {
    prev_gaz = millis();
    gaz = 0;
  }
}

// Функция управления звуковым сигналом
void controlBuzzer() {
  if (millis() - prev_buzz > 5000 && gaz == 1) {
    prev_buzz = millis();
    digitalWrite(buzzPin, HIGH);
    delay(200);
    digitalWrite(buzzPin, LOW);
  }
}

// Функции для работы с датчиком формальдегида
byte checkSum(byte array[], byte length) {
    byte sum = 0;
    for (int i = 1; i < length - 1; i++) {
        sum += array[i];
    }
    return (~sum) + 1;
}

boolean available1() {
    while (mySerial.available()) {
        for (byte index = 0; index < MAXLENGTH - 1; index++) {
            receivedCommandStack[index] = receivedCommandStack[index + 1];
        }
        receivedCommandStack[MAXLENGTH - 1] = mySerial.read();
         
        byte sumNum = checkSum(receivedCommandStack, MAXLENGTH);
        if ((receivedCommandStack[0] == 0xFF) && (receivedCommandStack[1] == 0x17) && 
            (receivedCommandStack[2] == 0x04) && (receivedCommandStack[MAXLENGTH - 1] == sumNum)) {
            receivedFlag = 1;
            return true;
        }
    }
    return false;
}

float ze08_PPM() {
    if (available1()) {
        receivedFlag = 0;
        float ppb = (unsigned int)(receivedCommandStack[4] * 256) + receivedCommandStack[5];
        float ppmf = ppb / 1000;
        delay(1000);
        lcd.setCursor(9, 0);
        lcd.print(":");
        lcd.print(ppmf);
        return ppmf;
    }
    return 0;
}

Комментарии

Пока нет комментариев. Будьте первым!

Оставить комментарий

← Назад к списку

Важно: Блог-эксперимент

Блог только запустил, все статьи генерирую через нейросеть т.к. лень, возможны ошибки. Просто чтобы вы знали и не запускали ядерный реактор по моим статьям ))
Если у вас есть вопросы, или Нашли неточность? пишите в коментах — вместе поправим и сделаем статью более качественной. Я лично объясню нюансы из практики.

Посетителей сегодня: 0


кто я | книга | контакты без контактов

© Digital Specialist | Не являемся сотрудниками Google, Яндекса и NASA