17 ноября 2021

👮 Raspberry Pi начеку: делаем за час охранную систему с помощью датчика расстояния и камеры

Пишу об IT и на Python. kungurov.net
Создаем охранную систему с помощью Raspberry Pi, датчика расстояния HC-SR04 и камеры. Снимки нарушителя границ безопасности отправляем на почту и получаем уведомление в мобильное приложение IFTTT.
👮 Raspberry Pi начеку: делаем за час охранную систему с помощью датчика расстояния и камеры

Что делаем?

Делаем простую сигнализацию на ультразвуковом датчике расстояния HC-SR04. Когда датчик обнаруживает препятствие, мы получаем уведомление в мобильном приложении IFTTT о срабатывании сигнализации, а камера делает 3 снимка, которые отправляются с Gmail-почты на любую другую почту.

IFTTT – платформа, связывающая сервисы, системы умного дома для создания автоматических процессов. Например, включить музыку в Spotify в 9:55 минут, если температура за окном меньше 10°C и в Google-календаре нет событий. В IFTTT доступны тысячи готовых апплетов.

Что понадобится?

  • Raspberry Pi 3/4.
  • Резисторы 1 кОм и 2 кОм по 1 шт.
  • Ультразвуковой датчик расстояния HC-SR04.
  • Модуль камеры Raspberry Pi Camera v1.3 или v2.1.

Установка операционной системы

Скачаем операционную систему Raspberry Pi OS и установим ее на microSD-карту с помощью balenaEtcher.

Ультразвуковой датчик HC-SR04

Что такое ультразвук?

Человек слышит звуки в диапазоне от 20 (низкий бас) до 20 000 Гц (высокочастотный свист). Ультразвук имеет частоту более 20 000 Гц и поэтому человек его не слышит.

👮 Raspberry Pi начеку: делаем за час охранную систему с помощью датчика расстояния и камеры

Датчик HC-SR04 работает, как сонар:

  • Отправляет ультразвуковой импульс.
  • Ловит отраженный от препятствия импульс.
  • Вычисляет сколько времени потребовалось импульсу, чтобы достигнуть препятствия и вернуться обратно.
Принцип работы ультразвукового датчика расстояния HC-SR04
Принцип работы ультразвукового датчика расстояния HC-SR04

Характеристики датчика HC-SR04:

  • Напряжение питания: 5 В.
  • Диапазон расстояний: 2-400 см.
  • Эффективный угол наблюдения: 15°.
  • Рабочий угол наблюдения: 30°.
Диаграмма направленности ультразвукового датчика расстояния HC-SR04
Диаграмма направленности ультразвукового датчика расстояния HC-SR04

Как вычислить пройденное расстояние?

Зная скорость распространения звука в среде и временной интервал, можем вычислить расстояние:

S=V×t

Датчик считает расстояние туда-обратно. Нам нужна половина этого значения:

S=V×t2

Скорость звука при температуре 20°C составляет 34 300 см/с.

После подстановки значений получаем:

S=17150×t

Нумерация пинов Raspberry Pi

В Малине используют две системы нумерации пинов:

  • GPIO.BOARD – пины нумеруются слева направо и сверху вниз от 1 до 40.
  • GPIO.BCM – пины нумеруются по Broadcom SOC и называются GPIO [номер]. На рис. в скобках указаны альтернативные значения пинов.

Мы будем использовать нумерацию GPIO.BCM со значениями пинов по умолчанию.

Распиновка GPIO на Raspberry Pi 4
Распиновка GPIO на Raspberry Pi 4

Схема подключения HC-SR04 к Raspberry Pi

  • Пин питания VCC подключаем к пину 5V на Малине (красная линия).
  • Пин земли GND подключаем к пину GND на Малине (синяя линия).
  • Пин Echo подключаем к GPIO 26 (зеленая линия).
  • Пин Trig подключаем к GPIO 19 (оранжевая линия).
Схема подключения ультразвукового датчика расстояния HC-SR04 к Raspberry Pi 4
Схема подключения ультразвукового датчика расстояния HC-SR04 к Raspberry Pi 4

После получения импульса на пине Echo устанавливается логический уровень 5 В, а Малина работает с логикой 3.3 В. Чтобы понизить напряжение добавим в схему два резистора разных номиналов, образующих делитель напряжения. Для расчета номиналов воспользуемся онлайн-калькулятором:

Расчет делителя напряжения
Расчет делителя напряжения

Нам нужны резисторы 1 и 2 кОм. Можно подобрать резисторы других номиналов. Главное – получить напряжение не выше 3.3 В, чтобы не испортить Малину.

На макетной плате поменяем положение датчика на «от» Raspberry Pi:

Подключения датчика расстояния HC-SR04 к Raspberry Pi 4 на монтажной плате
Подключения датчика расстояния HC-SR04 к Raspberry Pi 4 на монтажной плате

Подключение камеры

Установим шлейф камеры в Малину:

Подключение камеры к Raspberry Pi 4
Подключение камеры к Raspberry Pi 4

Присоединим шлейф к камере аналогичным образом:

👮 Raspberry Pi начеку: делаем за час охранную систему с помощью датчика расстояния и камеры

Камера по умолчанию отключена. Для активации камеры зайдем в Меню PreferencesRaspberry Pi Configuration:

👮 Raspberry Pi начеку: делаем за час охранную систему с помощью датчика расстояния и камеры

Перейдем во вкладку Interfaces и активируем камеру:

Активация камеры в Raspberry Pi 4
Активация камеры в Raspberry Pi 4

Создание апплета IFTTT

Зарегистрируемся на сайте IFTTT и перейдем на страницу создания апплета.

В условие If This добавим Receive a web request, в условие Then ThatSend a notification from IFTTT app.

Создание апплета IFTTT
Создание апплета IFTTT

В условии If This назовем событие signal:

👮 Raspberry Pi начеку: делаем за час охранную систему с помощью датчика расстояния и камеры

Теперь перейдем на страницу Вебхуков и кликнем по кнопке Documentation:

👮 Raspberry Pi начеку: делаем за час охранную систему с помощью датчика расстояния и камеры

Впишем название события signal и получим ссылку, которая активирует наше событие.

👮 Raspberry Pi начеку: делаем за час охранную систему с помощью датчика расстояния и камеры

Установим мобильное приложение IFTTT на Android или iOS, чтобы получать уведомления.

Установка библиотек

Установим библиотеку yagmail для отправки почты с аккаунта Gmail:

        pip3 install yagmail
    

Остальные библиотеки – RPi.GPIO для работы с пинами и picamera для управления камерой – предустановлены.

Запускаем код

Импортируем необходимые библиотеки:

        from picamera import PiCamera
import RPi.GPIO as GPIO
import datetime
import requests
import yagmail
import time
import os
    

Напишем функцию create_shots_folder(), которая создает папку для снимков:

        # функция, проверяющая наличие/создающая папку shots
def create_shots_folder():
    # если папки shots не существует
    if not os.path.exists('shots'):
        # то создать папку shots в текущем каталоге
        os.mkdir('shots')
    

Функция take_shots(number_of_shots) управляет камерой и принимает на вход в качестве аргумента количество снимков number_of_shots:

        # функция, управляющая камерой
def take_shots(number_of_shots):
    # инициализация камеры
    camera = PiCamera()
    # задаем разрешение снимка
    camera.resolution = (1024, 768)
    # список, в который добавим пути к снимкам
    shots = []
    # цикл, который number_of_shots-раз сделает снимки с паузой 0.5 сек
    for i in range(number_of_shots):
        # получим текущую дату и время без миллисекунд
        time_of_the_crime = datetime.datetime.today().replace(microsecond=0)
        # присвоим снимку имя: текущая дата и время плюс расширение .jpg
        shot_name = str(time_of_the_crime) + '.jpg'
        # получим путь к снимку
        shot_path = os.path.join(os.getcwd(), 'shots', shot_name)
        # сделаем снимок и сохраним его в папку shots под именем shot_name
        camera.capture(shot_path)
        print('Сделал снимок:', shot_name)
        # добавим путь к снимку в список снимков shots
        shots.append(shot_path)
        # пауза между снимками в секундах
        pause = 0.5
        time.sleep(pause)

    # отключим камеру
    camera.close()

    return shots
    

Здесь:

  • shot_name – название снимка: текущая дата и время без миллисекунд.
  • shot_path – путь к снимку.
  • shots – список с путями.
  • pause – пауза между снимками.

Функция send_email() отправляет снимки с Gmail-почты на любую другую почту:

        def send_email(sender_email, sender_email_password, recipient_email, number_of_shots):
    # список с путями снимков
    shots = take_shots(number_of_shots)
    # senders_email - Gmail-адрес отправителя
    # senders_email_password - пароль от Gmail
    email = yagmail.SMTP(user=sender_email, password=sender_email_password)
    # subject - тема письма
    # contents - содержимое письма
    # attachments - вложения (снимки)
    email.send(to=recipient_email, subject='Проникновение в помещение', contents='Тревога!', attachments=shots)
    print('Снимки отправлены на почту.\n')
    
  • sender_email – адрес Gmail-почты и пароль от нее sender_email_password.
  • recipient_email – почтовый адрес получателя.

Чтобы отправить почту через скрипт, нужно открыть доступ для небезопасных приложений. Для этого зайдите в профиль Гугл-почтыБезопасностьНенадежные приложения, у которых есть доступ к аккаунту и откройте доступ ненадежным приложениям.

👮 Raspberry Pi начеку: делаем за час охранную систему с помощью датчика расстояния и камеры

Функция send_ifttt_notification() отправляет POST-запрос в IFTTT, активируя апплет:

        def send_ifttt_notification():
    # ссылка на апплет IFTTT
    link = 'https://maker.ifttt.com/trigger/НАЗВАНИЕ_ПРИЛОЖЕНИЯ/with/key/КЛЮЧ'
    # отправляем post-запрос в IFTTT, чтобы сработал апплет
    requests.post(link)
    print('Уведомление отправлено в приложение IFTTT.')
    

Пины нужно объявлять один раз, поэтому напишем отдельную функцию setup_GPIO() и вызовем ее в начале работы отдельно один раз:

        def setup_GPIO():
    # отключим уведомления об ошибках
    GPIO.setwarnings(False)
    # используем нумерацию выводов BCM
    GPIO.setmode(GPIO.BCM)
    # пин Trig
    TRIGGER = 19
    # пин Echo
    ECHO = 26
    # установим режим работы пина TRIGGER на Выход
    GPIO.setup(TRIGGER, GPIO.OUT)
    # установим режим работы пина ECHO на Вход со стягивающим резистором
    GPIO.setup(ECHO, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    return TRIGGER, ECHO
    

Здесь:

  • TRIGGER и ECHO подключены к пинам GPIO 19 и 26 соответственно.
  • GPIO.setup(TRIGGER, GPIO.OUT) – пин TRIGGER установлен на выходной сигнал.
  • GPIO.setup(ECHO, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) – пин ECHO установлен на входной сигнал со стягивающим резистором GPIO.PUD_DOWN, который устанавливает на пине изначальное значение LOW. Без этой опции на пине из-за помех может появиться логическая единица и мы получим ложное срабатывание и первый цикл while в функции ultrasonic_detection() не сработает.

Функция pause() добавляет паузу после объявления пинов. Без паузы датчик работает некорректно.

        def ultrasonic_detection(TRIGGER, ECHO):
    # подадим импульс, т . е. установим состояние пина на HIGH
    GPIO.output(TRIGGER, GPIO.HIGH)
    # длительность импульса 0.00001 сек
    time.sleep(0.00001)
    # установим состояние пина TRIGGER на LOW
    GPIO.output(TRIGGER, GPIO.LOW)
    # считываем состояние пина ECHO
    # пока ничего не происходит, фиксируем текущее время start
    while GPIO.input(ECHO) == 0:
        start = time.time()
    # если обнаружено движение, зафиксируем время end
    while GPIO.input(ECHO) == 1:
        end = time.time()
    

Здесь:

  • GPIO.output(TRIGGER, GPIO.HIGH) – чтобы запустить датчик в работу, подаем на пин Trigger импульс длительностью 10 микросекунд (time.sleep(0.00001))
  • while GPIO.input(ECHO) == 0 – пока отраженного сигнала нет, присваиваем переменной start текущее время.
  • while GPIO.input(ECHO) == 1 – при получении отраженного сигнала, фиксируем время end.
        # рассчитаем длительность сигнала
    signal_duration = end - start
    # рассчитаем расстояние до объекта
    distance = round(signal_duration * 17150, 2)
    
  • distance – длительность сигнала, округленная до второго значения после запятой.
            # если объект обнаружен на расстоянии от 3 до 15 см
    if 3 < distance < 15:
        print("Замечено движение на расстоянии", distance, "см. от датчика.")
        # отправим уведомление в приложение IFTTT
        send_ifttt_notification()
        # отправим снимки на почту
        send_email(sender_email='gmail', sender_email_password='gmail_password',
                 recipient_email='email', number_of_shots=3)
    

Если препятствие обнаружено на расстоянии 3-15 см, то в приложение IFTTT отправляется уведомление, а на почту – снимки.

        def main():
    create_shots_folder()
    TRIGGER, ECHO = setup_GPIO()
    pause(10)
    while True:
        try:
            ultrasonic_detection(TRIGGER, ECHO)
        except KeyboardInterrupt:
            GPIO.cleanup()


if __name__ == "__main__":
    main()
    

Здесь:

  • except KeyboardInterrupt: GPIO.cleanup() – возвращает пины в начальное состояние при выходе из программы через CTRL + C.

При обнаружение движения скрипт будет выводить следующее:

👮 Raspberry Pi начеку: делаем за час охранную систему с помощью датчика расстояния и камеры

У меня ничего не работает

На СтакОверфлоу у многих возникает проблема с тем, что второй цикл while GPIO.input(ECHO) == 1: не срабатывает, то есть сигнал ECHO всегда равен 0. Возможно, резисторы не касаются дорожек макетной платы. Установите ножки строго вертикально, чтобы они не изгибались и попадали на дорожку.

Расположение дорожек на макетной плате
Расположение дорожек на макетной плате

Если и это не помогло, то запустите скрипт и завершите работу, нажав Ctrl + C, чтобы сработало исключение KeyboardInterrupt: GPIO.cleanup() и пины вернулись к исходным значениям.

Если температура окружающей среды изменилась, то подкорректируйте скорость звука в среде.

Со звукопоглощающими материалами датчик HC-SR04 работать не будет.

GitHub

Код лежит в репозитории ultrasonic-rpi-alarm.

***

В этой статье мы:

  • научились программировать Raspberry Pi 3/4;
  • работать с пинами GPIO;
  • управлять камерой Raspberry Pi Camera и датчиком расстояния HC-SR04;
  • отправлять почту с вложениями;
  • создавать апплеты IFTTT.

Материалы по теме

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
PHP Developer
от 200000 RUB до 270000 RUB
Golang разработчик (middle)
от 230000 RUB до 300000 RUB

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ