// Ідентифікатор шаблону
#define BLYNK_TEMPLATE_ID "TMPL4US3oywG6"
// Назва шаблону
#define BLYNK_TEMPLATE_NAME "PR4"
// Токен авторизації
#define BLYNK_AUTH_TOKEN "oWLSer2HblVsCuFGt2JUVwwSJG-YOBgm"
#include <WiFi.h>
#include <BlynkSimpleEsp32.h>
#include <ESP32Servo.h>
// Налаштування пінів
#define POT_PIN 34
#define LDR_PIN 35
#define PIR_PIN 14
#define SERVO_PIN 13
#define LED_PIN 18
// Створення обʼєктів сервопривода й таймера
Servo servoCurtains;
BlynkTimer timer;
char auth[] = BLYNK_AUTH_TOKEN; // Токен авторизації
char ssid[] = "Wokwi-GUEST"; // Спеціальний Wi-Fi для Wokwi
char pass[] = ""; // Пароль Wi-Fi
// Змінні стану (прапорець режиму, прапорець для одноразових сповіщень та час останнього зафіксованого руху)
bool autoMode = true;
bool alertLight = false;
unsigned long lastMotion = 0;
// Останнє відоме положення сервопривода та світлодіода
int lastServoPos = -1;
int lastLedState = -1;
// Обробка зміни режиму
BLYNK_WRITE(V1){
// Зчитування стану кнопки
autoMode = param.asInt();
Serial.print("Mode changed: ");
// Вивід режиму в консоль
Serial.println(autoMode ? "Auto" : "Manual");
}
// Ручне керування світлодіодом
BLYNK_WRITE(V2){
// Якщо обрано ручний режим роботи пристрою
if (!autoMode){
// Зчитування значення кнопки
int state = param.asInt();
// Увімкнення або вимкнення світлодіода
digitalWrite(LED_PIN, state);
// Оновлення стану
lastLedState = state;
// Повідомлення про увімкнення чи вимкнення освітлення
Serial.print("Lights manually switched to ");
Serial.println(state == HIGH ? "ON" : "OFF");
} else{
// Ігнорування натискання кнопки, якщо обрано автоматичний режим роботи пристрою
Serial.println("Auto mode is enabled, lights cannot be controlled!");
Blynk.virtualWrite(V2, lastLedState);
}
}
// Ручне керування "шторами" (сервоприводом)
BLYNK_WRITE(V3){
// Якщо обрано ручний режим роботи пристрою
if (!autoMode){
// Отримання кута від слайдера
int pos = param.asInt();
// Поворот сервопривода на отриманий кут
servoCurtains.write(pos);
// Запис позиції сервопривода
lastServoPos = pos;
// Повідомлення про зміну кута сервопривода (регулювання положення "штор")
Serial.printf("Curtains manually moved to %d degrees\n", pos);
} else{
// Ігнорувати регулювання положення "штор", якщо обрано автоматичний режим роботи пристрою
Serial.println("Auto mode is enabled, curtains cannot be controlled!");
Blynk.virtualWrite(V3, lastServoPos);
}
}
void updSystem(){
// Зчитування рівня освітленості
int light = analogRead(LDR_PIN);
// Зчитування порога освітленості з потенціометра
int threshold = analogRead(POT_PIN);
// Зчитування стану датчика руху
int motion = digitalRead(PIR_PIN);
// Конвертація показників освітленості у відсотки
int lightInPercent = map(light, 0, 4095, 0, 100);
// Конвертація порога освітленості у відсотки
int thresholdInPercent = map(threshold, 0, 4095, 0, 100);
// Вивід поточної інформації в термінал
Serial.printf(
"Light: %d%% | Threshold: %d%% | Motion: %d\n",
lightInPercent,
thresholdInPercent,
motion
);
// Перевірка, чи рівень освітленості менше за встановлений поріг
bool isDark = (lightInPercent < thresholdInPercent);
// Якщо зафіксовано рух, то виконуємо запис поточного часу
if (motion == HIGH){
lastMotion = millis();
// Автоматичний режим активовано, але рівень освітленості вищий за встановлений поріг
if (autoMode && !isDark && !alertLight){
Serial.println("Auto mode is enabled. Movement has been detected, but the light stays OFF");
// Блокування повторного виведення повідомлення
alertLight = true;
}
// Активовано ручний режим
else if (!autoMode && !alertLight){
Serial.println("Manual mode is enabled. Movement has been detected, but the AUTO lighting is OFF");
// Блокування повторного виведення повідомлення
alertLight = true;
}
} else{
// Скидання прапорця до наступного руху
alertLight = false;
}
// Перевірка, чи було зафіксовано рух протягом останніх 10 секунд
bool recentMotion = (lastMotion > 0 && (millis() - lastMotion < 10000));
// Автоматичний режим
if (autoMode){
// Якщо стемніло, тоді закрити "штори". Якщо посвітлішало, тоді відкрити "штори"
int targetServo = isDark ? 180 : 0;
// Виконання дії лише в тому випадку, якщо змінився стан
if (targetServo != lastServoPos){
// Зміна положення сервопривода ("штор")
servoCurtains.write(targetServo);
// Оновлення стану
lastServoPos = targetServo;
// Оновлення положення слайдера в Blynk
Blynk.virtualWrite(V3, targetServo);
// Виведення повідомлення про положення "штор"
Serial.print("Curtains are ");
Serial.println(targetServo == 180 ? "closed" : "opened");
// Якщо "штори" закриті, відбувається надсилання повідомлення в Blynk
if(targetServo == 180){
Blynk.logEvent("night_mode");
}
}
// Увімкнення світла, якщо стемніло та є рух
int targetLed = (isDark && recentMotion) ? HIGH : LOW;
// Виконання дій у тому випадку, якщо змінився стан
if (targetLed != lastLedState){
// Увімкнення чи вимкнення LED (освітлення)
digitalWrite(LED_PIN, targetLed);
// Оновлення стану
lastLedState = targetLed;
// Оновлення стану кнопки в Blynk
Blynk.virtualWrite(V2, targetLed);
// Виведення повідомлення про статус освітлення
Serial.print("The light is ");
Serial.println(targetLed == HIGH ? "ON" : "OFF");
}
}
// Відображення рівня світла на графіку
Blynk.virtualWrite(V0, lightInPercent);
// Відображення порогового значення для закриття "штор"
Blynk.virtualWrite(V4, thresholdInPercent);
// Відображення статусу освітлення (увімкнено чи вимкнено)
Blynk.virtualWrite(V5, motion);
}
void setup() {
// Ініціалізація консолі
Serial.begin(115200);
// Налаштування піна світлодіода як виходу
pinMode(LED_PIN, OUTPUT);
// Налаштування піна датчика руху як входу
pinMode(PIR_PIN, INPUT);
// Підключення сервопривода до піна керування
servoCurtains.attach(SERVO_PIN);
// Повідомлення про підключення до Wi-Fi та Blynk
Serial.println("Connecting...");
// Підключення до Wi-Fi та Blynk
Blynk.begin(auth, ssid, pass);
// Виведення повідомлення, якщо виконано успішне зʼєднання
if (Blynk.connected()){
Serial.println("Connected!");
}
// Встановлення інтервалу оновлення даних
timer.setInterval(1000L, updSystem);
}
void loop() {
// Взаємодія з хмарою
Blynk.run();
// Таймер (виклик updSystem щосекунди)
timer.run();
}