#include <DHT.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal_I2C.h>
#include <RTClib.h>
// ===================== ПИНЫ =====================
#define DHTPIN PA0
#define ONE_WIRE_BUS PA1
#define LDR_PIN PA2
#define TRIG_PIN PA3
#define ECHO_PIN PA4
#define MQ2_PIN PA5
#define PIR_PIN PA6
#define PUMP_PIN PB12
#define LAMP_PIN PB13
#define FAN_PIN PB14
#define DHTTYPE DHT22
// ===================== ОБЪЕКТЫ =====================
DHT dht(DHTPIN, DHTTYPE);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature ds18b20(&oneWire);
LiquidCrystal_I2C lcd(0x27, 16, 2);
RTC_DS1307 rtc;
// ===================== ПОРОГИ =====================
const int DRY_SOIL_TEMP = 35; // Если почва горячее 35°C — поливаем (косвенный признак сухости)
const int DARK_LIGHT = 400; // Включать лампу, если темнее
const int BRIGHT_LIGHT = 700; // Выключать лампу, если светлее
const int HOT_TEMP = 28; // Включение вентилятора
const int COLD_TEMP = 22; // Выключение вентилятора
const int LOW_WATER_CM = 10; // Тревога: мало воды (< 10 см)
const int GAS_ALARM = 500; // Порог тревоги газа
// ===================== ПЕРЕМЕННЫЕ =====================
float airTemp = 0, airHum = 0, soilTemp = -127;
int lightLevel = 0, gasLevel = 0;
float waterLevel = 0;
bool motionDetected = false;
bool pumpActive = false, lampActive = false, fanActive = false;
bool lowWaterAlarm = false, gasAlarm = false;
unsigned long pumpStartTime = 0;
const unsigned long PUMP_DURATION = 5000; // 5 секунд полива
// Таймеры
unsigned long lastSensorRead = 0, lastDisplayUpdate = 0;
const unsigned long SENSOR_INTERVAL = 2000;
int displayPage = 0;
const int TOTAL_PAGES = 3;
// ===================== SETUP =====================
void setup() {
Serial.begin(115200);
Serial.println("=== Умная теплица v2.1 (STM32) ===");
// Пины
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(LDR_PIN, INPUT);
pinMode(MQ2_PIN, INPUT);
pinMode(PIR_PIN, INPUT);
pinMode(PUMP_PIN, OUTPUT);
pinMode(LAMP_PIN, OUTPUT);
pinMode(FAN_PIN, OUTPUT);
digitalWrite(TRIG_PIN, LOW);
digitalWrite(PUMP_PIN, LOW);
digitalWrite(LAMP_PIN, LOW);
digitalWrite(FAN_PIN, LOW);
// LCD
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print(" Smart Green");
lcd.setCursor(0, 1);
lcd.print(" House v2.1");
delay(2000);
lcd.clear();
// Датчики
dht.begin();
ds18b20.begin();
// RTC
if (!rtc.begin()) {
lcd.setCursor(0, 0);
lcd.print("RTC ERROR!");
Serial.println("RTC не найден!");
}
if (!rtc.isrunning()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
Serial.println("RTC настроен на время компиляции");
}
delay(1000);
}
// ===================== LOOP =====================
void loop() {
unsigned long now = millis();
// Чтение датчиков
if (now - lastSensorRead >= SENSOR_INTERVAL) {
lastSensorRead = now;
readSensors();
controlLogic();
}
// Обновление дисплея
if (now - lastDisplayUpdate >= 3000) {
lastDisplayUpdate = now;
updateDisplay();
}
// Проверка окончания полива
if (pumpActive && (now - pumpStartTime >= PUMP_DURATION)) {
pumpActive = false;
digitalWrite(PUMP_PIN, LOW);
Serial.println("Полив завершен.");
}
}
// ===================== ЧТЕНИЕ ДАТЧИКОВ =====================
void readSensors() {
// --- DHT22 ---
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t)) airTemp = t;
if (!isnan(h)) airHum = h;
// --- DS18B20 (температура почвы) ---
ds18b20.requestTemperatures();
float st = ds18b20.getTempCByIndex(0);
if (st != DEVICE_DISCONNECTED_C) soilTemp = st;
// --- LDR (освещенность) ---
lightLevel = analogRead(LDR_PIN);
// --- MQ2 (газ/дым) ---
gasLevel = analogRead(MQ2_PIN);
gasAlarm = (gasLevel > GAS_ALARM);
// --- PIR (движение) ---
motionDetected = digitalRead(PIR_PIN);
// --- HC-SR04 (уровень воды в баке) ---
waterLevel = readUltrasonic();
lowWaterAlarm = (waterLevel > 0 && waterLevel < LOW_WATER_CM);
// --- Вывод в Serial ---
Serial.print("Воздух:"); Serial.print(airTemp, 1); Serial.print("C ");
Serial.print(airHum, 1); Serial.print("% | ");
Serial.print("Почва:");
if (soilTemp != -127) { Serial.print(soilTemp, 1); Serial.print("C"); }
else { Serial.print("N/A"); }
Serial.print(" | Свет:"); Serial.print(lightLevel);
Serial.print(" | Бак:"); Serial.print(waterLevel, 1); Serial.print("cm");
Serial.print(" | Газ:"); Serial.print(gasLevel);
Serial.print(" | Движ:"); Serial.println(motionDetected ? "ДА" : "нет");
}
// --- Ручное чтение HC-SR04 (без NewPing) ---
float readUltrasonic() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // таймаут 30 мс (~5 метров)
if (duration == 0) {
return -1; // ошибка: нет эха (датчик не подключен или слишком далеко)
}
float distance = duration * 0.034 / 2.0; // скорость звука 0.034 см/мкс
return distance;
}
// ===================== УПРАВЛЕНИЕ =====================
void controlLogic() {
DateTime now = rtc.now();
// --- ПОЛИВ ---
// Условия: есть вода, почва сухая (горячая), ИЛИ по расписанию
bool scheduledWatering = (now.hour() == 7 && now.minute() == 0) ||
(now.hour() == 19 && now.minute() == 0);
bool drySoil = (soilTemp > DRY_SOIL_TEMP);
if (!pumpActive && waterLevel > LOW_WATER_CM && (drySoil || scheduledWatering)) {
startWatering();
}
// --- ЛАМПА ---
if (lightLevel < DARK_LIGHT && !lampActive) {
lampActive = true;
digitalWrite(LAMP_PIN, HIGH);
Serial.println("Лампа ВКЛ (темно)");
} else if (lightLevel > BRIGHT_LIGHT && lampActive) {
lampActive = false;
digitalWrite(LAMP_PIN, LOW);
Serial.println("Лампа ВЫКЛ (светло)");
}
// --- ВЕНТИЛЯТОР ---
if (airTemp > HOT_TEMP && !fanActive) {
fanActive = true;
digitalWrite(FAN_PIN, HIGH);
Serial.println("Вентилятор ВКЛ (жарко)");
} else if (airTemp < COLD_TEMP && fanActive) {
fanActive = false;
digitalWrite(FAN_PIN, LOW);
Serial.println("Вентилятор ВЫКЛ (прохладно)");
}
// --- ТРЕВОГИ ---
if (lowWaterAlarm) {
Serial.println("!!! МАЛО ВОДЫ В БАКЕ !!!");
}
if (gasAlarm) {
Serial.println("!!! ОБНАРУЖЕН ГАЗ/ДЫМ !!!");
}
}
void startWatering() {
pumpActive = true;
pumpStartTime = millis();
digitalWrite(PUMP_PIN, HIGH);
Serial.println("--> ПОЛИВ НАЧАТ");
}
// ===================== ДИСПЛЕЙ =====================
void updateDisplay() {
lcd.clear();
switch(displayPage) {
case 0:
// === Страница 1: Температура и влажность воздуха ===
lcd.setCursor(0, 0);
lcd.print("Air: ");
lcd.print(airTemp, 1);
lcd.print("\xDFC");
lcd.setCursor(0, 1);
lcd.print("Hum: ");
lcd.print(airHum, 1);
lcd.print("%");
// Значок вентилятора
lcd.setCursor(13, 1);
lcd.print(fanActive ? "FAN" : " ");
break;
case 1:
// === Страница 2: Почва, свет, вода ===
lcd.setCursor(0, 0);
lcd.print("Soil:");
if (soilTemp != -127) { lcd.print(soilTemp, 1); lcd.print("\xDF"); }
else { lcd.print("N/A"); }
lcd.setCursor(9, 0);
lcd.print(lightLevel < DARK_LIGHT ? "DARK" : "LITE");
lcd.setCursor(0, 1);
lcd.print("Wtr:");
if (waterLevel > 0) { lcd.print(waterLevel, 1); lcd.print("cm"); }
else { lcd.print("ERR"); }
lcd.setCursor(10, 1);
lcd.print(pumpActive ? "PUMP" : " ");
// Тревога воды
if (lowWaterAlarm) {
lcd.setCursor(14, 1);
lcd.print("!");
}
break;
case 2:
// === Страница 3: Время, газ, движение ===
DateTime now = rtc.now();
lcd.setCursor(0, 0);
lcd.print("Time: ");
if (now.hour() < 10) lcd.print("0");
lcd.print(now.hour());
lcd.print(":");
if (now.minute() < 10) lcd.print("0");
lcd.print(now.minute());
lcd.setCursor(0, 1);
lcd.print("Gas:");
lcd.print(gasLevel);
lcd.setCursor(10, 1);
if (gasAlarm) lcd.print("ALARM");
else if (motionDetected) lcd.print("MOV");
else lcd.print(" ");
break;
}
displayPage = (displayPage + 1) % TOTAL_PAGES;
}