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.

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

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

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