#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// === Пины и константы ===
Servo s;
#define trigPin 7
#define echoPin 6
#define buzzPin 8
#define buttonPin 2 // кнопка крышки
#define menuButtonPin 4 // кнопка меню
#define ldrPin A0
#define redPin 9
#define greenPin 10
#define bluePin 11
// LCD I2C (адрес 0x27 — часто бывает, но может быть иной)
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Состояния
bool lidOpen = false;
enum OperatingMode { DAY_MODE, NIGHT_MODE };
OperatingMode currentMode = DAY_MODE;
unsigned long lastButtonTime = 0;
const unsigned long debounce = 60;
bool lastBtn = HIGH;
bool btnBlock = false;
unsigned long lastPingTime = 0;
const unsigned long pingDelay = 250;
int distanceCm = 999;
unsigned long openCount = 0;
unsigned long nightCount = 0;
// Переменные меню
bool menuMode = false;
unsigned long menuPressTime = 0;
bool lastMenuBtn = HIGH;
bool menuBtnBlock = false;
int brightnessLevel = 2; // 0=выкл, 1=тускло, 2=средняя, 3=яркая
// === Функции ===
long measureDistanceCm() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000);
if (duration == 0) return 9999;
return duration * 0.034 / 2;
}
void setRGB(int r, int g, int b) {
analogWrite(redPin, r);
analogWrite(greenPin, g);
analogWrite(bluePin, b);
}
void rgbDistance(int d) {
if (d >= 60) setRGB(255, 0, 0);
else if (d >= 25) setRGB(255, 180, 0);
else setRGB(0, 255, 0);
}
void _setLidState(bool open, bool withBuzzer, bool verbose) {
noTone(buzzPin);
if (open == lidOpen) return;
bool wasClosed = !lidOpen;
lidOpen = open;
int angle = open ? 0 : 90;
s.write(angle);
// Если открываем — увеличиваем счётчик открытий
if (open && wasClosed) {
openCount++;
}
if (open && withBuzzer) {
tone(buzzPin, 2000);
delay(150);
noTone(buzzPin);
delay(40);
tone(buzzPin, 1500);
delay(120);
noTone(buzzPin);
}
if (verbose) {
if (open) Serial.println("Крышка открыта");
else Serial.println("Крышка закрыта");
}
}
void checkButton(OperatingMode mode) {
if (mode == DAY_MODE) return;
bool readBtn = digitalRead(buttonPin);
if (readBtn != lastBtn) lastButtonTime = millis();
if (millis() - lastButtonTime > debounce) {
if (readBtn == LOW && !btnBlock) {
btnBlock = true;
bool shouldOpen = !lidOpen;
bool shouldBuzz = shouldOpen && (mode == NIGHT_MODE);
bool verbose = (mode == NIGHT_MODE);
_setLidState(shouldOpen, shouldBuzz, verbose);
}
if (readBtn == HIGH) btnBlock = false;
}
lastBtn = readBtn;
}
void checkMenuButton() {
bool btn = digitalRead(menuButtonPin);
if (btn != lastMenuBtn) {
menuPressTime = millis();
}
if (millis() - menuPressTime > debounce) {
if (btn == LOW && !menuBtnBlock) {
menuBtnBlock = true;
// Ждём отпуска, чтобы определить короткое или длинное нажатие
unsigned long pressStart = menuPressTime;
// Обработка будет при отпускании — ниже
}
if (btn == HIGH && menuBtnBlock) {
// кнопка отпущена после нажатия
unsigned long pressDuration = millis() - menuPressTime;
if (!menuMode) {
// Вход в меню при коротком нажатии
if (pressDuration < 500) {
menuMode = true;
lcd.clear();
}
} else {
// Уже в меню
if (pressDuration < 500) {
// Короткое — листаем уровни яркости
brightnessLevel++;
if (brightnessLevel > 3) brightnessLevel = 0;
} else {
// Длинное — сохраняем и выходим из меню
applyBrightness();
menuMode = false;
lcd.clear();
}
}
menuBtnBlock = false;
}
}
lastMenuBtn = btn;
}
void applyBrightness() {
int pwm = 0;
switch (brightnessLevel) {
case 1: pwm = 80; break;
case 2: pwm = 150; break;
case 3: pwm = 255; break;
case 0: pwm = 0; break;
}
analogWrite(redPin, pwm);
analogWrite(greenPin, pwm);analogWrite(bluePin, pwm);
}
void showMenu() {
lcd.setCursor(0, 0);
lcd.print("Яркость RGB:");
lcd.setCursor(0, 1);
switch (brightnessLevel) {
case 0: lcd.print("Отключена "); break;
case 1: lcd.print("Тусклая "); break;
case 2: lcd.print("Средняя "); break;
case 3: lcd.print("Яркая "); break;
}
}
void showMain() {
// Здесь ты хочешь показывать время. Для простоты — millis / секунд
unsigned long ms = millis();
unsigned long seconds = (ms / 1000) % 60;
unsigned long minutes = (ms / (1000 * 60)) % 60;
unsigned long hours = (ms / (1000 * 60 * 60)) % 24;
lcd.setCursor(0, 0);
if (hours < 10) lcd.print('0');
lcd.print(hours);
lcd.print(':');
if (minutes < 10) lcd.print('0');
lcd.print(minutes);
lcd.print(':');
if (seconds < 10) lcd.print('0');
lcd.print(seconds);
lcd.setCursor(0, 1);
lcd.print("Открытий: ");
lcd.print(openCount);
}
void setup() {
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(menuButtonPin, INPUT_PULLUP);
pinMode(buzzPin, OUTPUT);
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
s.attach(3);
_setLidState(false, false, true);
// LCD
Wire.begin();
lcd.init();
lcd.backlight();
applyBrightness(); // установить текущую яркость
Serial.println("Система запущена.");
}
void loop() {
checkMenuButton();
if (menuMode) {
showMenu();
return;
}
// Основной режим — система мусорки
int lightLevel = analogRead(ldrPin);
OperatingMode newMode;
if (lightLevel > 800) newMode = NIGHT_MODE;
else newMode = DAY_MODE;
if (newMode != currentMode) {
if (newMode == NIGHT_MODE) nightCount++;
_setLidState(false, false, true);
currentMode = newMode;
return;
}
if (currentMode == NIGHT_MODE) {
if (lidOpen) setRGB(0, 255, 0);
else setRGB(255, 0, 0);
checkButton(NIGHT_MODE);
} else {
if (millis() - lastPingTime > pingDelay) {
lastPingTime = millis();
distanceCm = measureDistanceCm();
}
rgbDistance(distanceCm);
if (distanceCm < 25) {
_setLidState(true, true, true);
} else if (distanceCm >= 25 && lidOpen) {
_setLidState(false, false, true);
}
}
// Обновляем основной экран на LCD
showMain();
}