#include <LiquidCrystal_I2C.h>
#include <Servo.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo window;
const int FAN_PIN = 6; // вентилятор (PWM)
const int PUMP_PIN = 11; // помпа
const int LAMP = 5;
const int BUZZER = 9;
const int BAT_PIN = A2; // средняя ножка потенциометра
const int BTN_NEXT = 2;
const int BTN_PREV = 4;
const float BAT_HIGH = 4.3; // заряд хороший
const float BAT_LOW = 4.0; // включаем экономию
const int N = 3; // сколько теплиц
// у каждой теплицы свои пороги
const float TEMP_HOT[N] = {28.0, 29.5, 27.0};
const int SOIL_DRY[N] = {300, 320, 280};
float temps[N];
int soils[N];
int windowAngles[N];
int driest = 0; // номер самой сухой теплицы
int view = 0; // какая теплица на экране
float batVoltage = 4.5;
bool isLowPower = false;
void setup() {
lcd.init();
lcd.backlight();
pinMode(FAN_PIN, OUTPUT);
pinMode(PUMP_PIN, OUTPUT);
pinMode(LAMP, OUTPUT);
pinMode(BUZZER, OUTPUT);
pinMode(BTN_NEXT, INPUT_PULLUP);
pinMode(BTN_PREV, INPUT_PULLUP);
window.attach(10);
window.write(0);
for (int i = 0; i < N; i++) {
temps[i] = 22.0;
soils[i] = 450;
windowAngles[i] = 0;
}
}
float readBattery() {
int raw = analogRead(BAT_PIN);
return raw * (5.0 / 1023.0);
}
// эмуляция датчиков: температуры и почвы трёх теплиц
void simulateSensors() {
unsigned long ms = millis();
for (int i = 0; i < N; i++) {
float baseTemp = 21.0 + i * 1.5; // теплица 0 — прохладнее, теплица 2 — жарче
float noise = sin(ms / (1000.0 * (i + 1))) * 3.0;
temps[i] = baseTemp + noise;
int baseSoil = 400 + i * 40;
soils[i] = constrain(baseSoil + (int)(sin(ms / (800.0 * (i + 2))) * 150), 100, 800);
}
}
void loop() {
// кнопки листают теплицы на экране
if (digitalRead(BTN_NEXT) == LOW) {
view = (view + 1) % N;
while (digitalRead(BTN_NEXT) == LOW) {}
lcd.clear();
}
if (digitalRead(BTN_PREV) == LOW) {
view = (view + N - 1) % N;
while (digitalRead(BTN_PREV) == LOW) {}
lcd.clear();
}
// батарея и режим экономии (с гистерезисом)
batVoltage = readBattery();
if (batVoltage < BAT_LOW) {
isLowPower = true;
} else if (batVoltage > BAT_HIGH) {
isLowPower = false;
}
simulateSensors();
// окна: плавно открываются по температуре своей теплицы
for (int i = 0; i < N; i++) {
if (!isLowPower && temps[i] >= TEMP_HOT[i]) {
int tt = constrain((int)temps[i], (int)TEMP_HOT[i], 35);
windowAngles[i] = map(tt, (int)TEMP_HOT[i], 35, 0, 90);
} else {
windowAngles[i] = 0;
}
}
// серво показывает окно той теплицы, что сейчас на экране
window.write(windowAngles[view]);
// вентилятор: по самой жаркой теплице
float maxTemp = -1000;
for (int i = 0; i < N; i++) {
if (temps[i] > maxTemp) maxTemp = temps[i];
}
int fanLevel = 0;
if ((!isLowPower || maxTemp >= 32.0) && maxTemp > 28.0) {
fanLevel = map(maxTemp - 28.0, 0, 7.0, 0, 255);
if (fanLevel > 255) fanLevel = 255;
}
analogWrite(FAN_PIN, fanLevel);
// помпа: только для самой сухой теплицы, если не экономим
driest = 0;
for (int i = 1; i < N; i++) {
if (soils[i] < soils[driest]) driest = i;
}
bool pumpOn = false;
if (!isLowPower && soils[driest] < SOIL_DRY[driest]) {
pumpOn = true;
}
digitalWrite(PUMP_PIN, pumpOn ? HIGH : LOW);
// лампа: мигает как предупреждение при экономии
if (isLowPower) {
digitalWrite(LAMP, (millis() / 1000) % 2); // моргает раз в секунду
} else {
digitalWrite(LAMP, LOW);
}
// Экран: компактно, с вольтами
lcd.setCursor(0, 0);
lcd.print("GH");
lcd.print(view + 1);
lcd.print(" V:");
lcd.print(batVoltage, 1); // <-- вот тут показываются вольты (с одним знаком после запятой)
lcd.print(" T:");
lcd.print(temps[view], 0);
lcd.setCursor(0, 1);
lcd.print("S:");
lcd.print(soils[view]);
lcd.print(" W:");
lcd.print(windowAngles[view]);
lcd.print(isLowPower ? " LOW" : " OK ");
delay(2000);
}