#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
// --- Настройки ---
const int pirPin = 2; // Пин датчика движения (PIR)
const int doorSensorPin = 3; // Пин датчика двери (Геркон)
const long playDelayMs = 10000; // Задержка перед воспроизведением (10 секунд)
const int trackNumber = 1; // Номер трека для воспроизведения (0001.mp3)
// Определение типа геркона (true = Нормально Разомкнутый, false = Нормально Замкнутый)
// Нормально Разомкнутый (NO): Контакт замыкается (LOW), когда дверь ЗАКРЫТА.
// Нормально Замкнутый (NC): Контакт размыкается (HIGH), когда дверь ЗАКРЫТА.
// Используйте pull-up резистор (внутренний), поэтому логика инвертирована:
// LOW = дверь закрыта (для NO) или открыта (для NC)
// HIGH = дверь открыта (для NO) или закрыта (для NC)
// Мы будем считать, что дверь закрыта, когда на пине LOW (для NO геркона)
// Если у вас NC геркон, поменяйте значение isDoorClosed = (doorState == HIGH);
const bool isDoorNormallyOpen = true; // Поставьте false, если у вас NC геркон
// Пины для DFPlayer Mini
const int softwareTxPin = 10;
const int softwareRxPin = 11;
// ----------------
SoftwareSerial mySoftwareSerial(softwareRxPin, softwareTxPin); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
bool isSomeoneInside = false; // Флаг: есть ли кто-то внутри (дверь закрыта + движение)
bool hasPlayedThisVisit = false; // Флаг: проигрывался ли звук за этот визит
bool delayStarted = false; // Флаг: запущен ли таймер задержки
unsigned long delayStartTime = 0; // Время старта таймера задержки
void setup() {
mySoftwareSerial.begin(9600); // Инициализация связи с DFPlayer
Serial.begin(9600); // Инициализация Serial для отладки (опционально)
pinMode(pirPin, INPUT);
// Используем внутренний подтягивающий резистор для геркона
pinMode(doorSensorPin, INPUT_PULLUP);
Serial.println();
Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
if (!myDFPlayer.begin(mySoftwareSerial)) {
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
while (true); // Остановка, если плеер не найден
}
Serial.println(F("DFPlayer Mini online."));
myDFPlayer.volume(25); // Установить громкость (0~30)
myDFPlayer.pause(); // Убедимся, что плеер не играет при старте
}
void loop() {
// Чтение состояния датчиков
int pirState = digitalRead(pirPin);
int doorState = digitalRead(doorSensorPin);
// Определяем, закрыта ли дверь, в зависимости от типа геркона
bool isDoorClosed;
if (isDoorNormallyOpen) {
isDoorClosed = (doorState == LOW); // Замкнут = Закрыто
} else {
isDoorClosed = (doorState == HIGH); // Разомкнут = Закрыто (т.к. pull-up)
}
// --- Логика состояний ---
if (isDoorClosed) {
// Дверь закрыта
if (!isSomeoneInside) {
// Дверь только что закрыли, сбрасываем флаги для нового визита
isSomeoneInside = true;
hasPlayedThisVisit = false;
delayStarted = false;
Serial.println("Door closed. Ready for motion.");
}
// Если есть движение, дверь закрыта, звук еще не играл и задержка не запущена
if (pirState == HIGH && !hasPlayedThisVisit && !delayStarted) {
delayStarted = true;
delayStartTime = millis(); // Запускаем таймер
Serial.println("Motion detected! Starting delay timer...");
}
// Если таймер задержки запущен и время вышло
if (delayStarted && (millis() - delayStartTime >= playDelayMs)) {
if (!hasPlayedThisVisit) {
Serial.println("Delay finished. Playing sound...");
myDFPlayer.play(trackNumber); // Воспроизвести трек
hasPlayedThisVisit = true; // Установить флаг, что уже играли
delayStarted = false; // Сбросить флаг таймера
}
}
// Если движение пропало, пока шел таймер задержки, сбросить таймер
if (delayStarted && pirState == LOW) {
// Опционально: можно сбросить таймер, если движение пропало ДО истечения задержки
// delayStarted = false;
// Serial.println("Motion stopped during delay. Timer reset.");
// Или можно оставить таймер работать - зависит от желаемого поведения
}
} else {
// Дверь открыта
if (isSomeoneInside) {
// Дверь только что открыли - визит завершен
Serial.println("Door opened. Visit ended.");
isSomeoneInside = false;
hasPlayedThisVisit = false; // Готовимся к следующему визиту
delayStarted = false; // Сбросить таймер на всякий случай
myDFPlayer.pause(); // Остановить воспроизведение, если оно еще шло (на всякий случай)
}
}
// Небольшая задержка для стабильности
delay(50);
}
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
// --- Настройки ---
const int pirPin = 2; // Пин датчика движения (PIR)
const long playDelayMs = 10000; // Задержка перед воспроизведением (10 секунд)
const long inactivityTimeoutMs = 60000; // Время без движения для сброса (60 секунд)
const int trackNumber = 1; // Номер трека для воспроизведения (0001.mp3)
// Пины для DFPlayer Mini
const int softwareTxPin = 10;
const int softwareRxPin = 11;
// ----------------
SoftwareSerial mySoftwareSerial(softwareRxPin, softwareTxPin); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
enum State { IDLE, DELAYING, PLAYED_WAITING_FOR_SILENCE, RESETTING };
State currentState = IDLE;
unsigned long delayStartTime = 0;
unsigned long lastMotionTime = 0;
void setup() {
mySoftwareSerial.begin(9600);
Serial.begin(9600);
pinMode(pirPin, INPUT);
Serial.println(F("Initializing DFPlayer..."));
if (!myDFPlayer.begin(mySoftwareSerial)) {
Serial.println(F("DFPlayer Error!"));
while (true);
}
Serial.println(F("DFPlayer Mini online."));
myDFPlayer.volume(25);
myDFPlayer.pause();
}
void loop() {
int pirState = digitalRead(pirPin);
unsigned long currentTime = millis();
switch (currentState) {
case IDLE:
// Ждем первого движения
if (pirState == HIGH) {
Serial.println("Motion detected. Starting delay.");
currentState = DELAYING;
delayStartTime = currentTime;
lastMotionTime = currentTime; // Фиксируем время последнего движения
}
break;
case DELAYING:
// Идет задержка перед воспроизведением
if (pirState == HIGH) {
lastMotionTime = currentTime; // Обновляем время последнего движения
}
if (currentTime - delayStartTime >= playDelayMs) {
Serial.println("Delay finished. Playing sound.");
myDFPlayer.play(trackNumber);
currentState = PLAYED_WAITING_FOR_SILENCE;
}
// Если движение пропало надолго ВО ВРЕМЯ задержки, можно вернуться в IDLE (опционально)
// if (currentTime - lastMotionTime > inactivityTimeoutMs / 2) { // Например, половина таймаута
// Serial.println("Motion stopped during delay. Resetting.");
// currentState = IDLE;
// }
break;
case PLAYED_WAITING_FOR_SILENCE:
// Звук проигран, ждем, пока человек уйдет (нет движения)
if (pirState == HIGH) {
lastMotionTime = currentTime; // Человек все еще здесь
} else {
// Движения нет, проверяем таймаут тишины
if (currentTime - lastMotionTime >= inactivityTimeoutMs) {
Serial.println("Inactivity timeout reached. Resetting for next visit.");
currentState = IDLE; // Готовы к новому срабатыванию
}
}
break;
// Состояние RESETTING может не понадобиться при такой логике
}
delay(50);
}
import RPi.GPIO as GPIO
import time
import os
import pygame # Используем pygame для простого воспроизведения звука
# --- Настройки ---
PIR_PIN = 17 # GPIO пин для PIR датчика
DOOR_PIN = 18 # GPIO пин для геркона (подключен к GND)
PLAY_DELAY_SEC = 10 # Задержка перед воспроизведением в секундах
SOUND_FILE = "/home/pi/joker_sound.mp3" # Путь к вашему аудиофайлу
# Настройка GPIO
GPIO.setmode(GPIO.BCM) # Используем нумерацию BCM
GPIO.setup(PIR_PIN, GPIO.IN)
# Используем внутренний подтягивающий резистор к VCC
# Геркон замыкает пин на GND при закрытой двери (NO тип)
GPIO.setup(DOOR_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# Инициализация pygame для звука
pygame.mixer.init()
try:
pygame.mixer.music.load(SOUND_FILE)
print(f"Аудиофайл загружен: {SOUND_FILE}")
except pygame.error as e:
print(f"Ошибка загрузки аудиофайла: {e}")
# Можно добавить выход из скрипта или запасной вариант
exit()
# Переменные состояния
is_someone_inside = False
has_played_this_visit = False
delay_timer_active = False
delay_start_time = 0
print("Система запущена. Ожидание событий...")
try:
while True:
door_closed = GPIO.input(DOOR_PIN) == GPIO.LOW # LOW = закрыто (для NO геркона с pull-up)
pir_active = GPIO.input(PIR_PIN) == GPIO.HIGH
# --- Логика состояний ---
if door_closed:
if not is_someone_inside:
print("Дверь закрыта. Визит начался.")
is_someone_inside = True
has_played_this_visit = False
delay_timer_active = False
if pir_active and is_someone_inside and not has_played_this_visit and not delay_timer_active:
print("Обнаружено движение. Запуск таймера задержки...")
delay_timer_active = True
delay_start_time = time.time()
if delay_timer_active and (time.time() - delay_start_time >= PLAY_DELAY_SEC):
if not has_played_this_visit:
print("Задержка прошла. Воспроизведение звука...")
try:
pygame.mixer.music.play()
# Можно добавить ожидание конца воспроизведения, если нужно
# while pygame.mixer.music.get_busy():
# time.sleep(0.1)
print("Воспроизведение завершено.")
except Exception as e:
print(f"Ошибка воспроизведения: {e}")
has_played_this_visit = True
delay_timer_active = False # Сбрасываем таймер после игры
else:
# Если флаг уже стоял, а таймер еще активен (маловероятно), сбросить таймер
delay_timer_active = False
# Опционально: сброс таймера, если движение пропало во время задержки
# if delay_timer_active and not pir_active:
# print("Движение пропало во время задержки. Сброс таймера.")
# delay_timer_active = False
else: # Дверь открыта
if is_someone_inside:
print("Дверь открыта. Визит завершен.")
is_someone_inside = False
has_played_this_visit = False
delay_timer_active = False
# Остановить воспроизведение, если оно еще идет
if pygame.mixer.music.get_busy():
pygame.mixer.music.stop()
print("Воспроизведение остановлено.")
time.sleep(0.1) # Небольшая пауза
except KeyboardInterrupt:
print("Выход из программы.")
finally:
GPIO.cleanup()
pygame.mixer.quit()
Монтаж:
Чтобы действительно вывести ваш квест на новый уровень, важно использовать современные и проверенные подходы в маркетинге. Не упустите возможность присоединиться к нашему Telegram-каналу, где вы найдете ещё больше полезных статей, инструментов и стратегий для роста вашего бизнеса. Подписывайтесь и оставайтесь на шаг впереди!