http://habrahabr.ru/post/231869/
Здравствуйте Хабражители!
Думаю многие из вас слышали о Raspberry Pi, более того, думаю довольно большое количество из вас видели его вживую. В начале 2014 года я решил, что пора мне тоже заказать себе парочку RPi и сделать на них что-то интересное. Так как я являюсь iOS разработчиком, я загорелся идеей обязательно прицепить к этому проекту iOS приложение. Ну и т.к. RPi довольно хорошо умеет работать со сторонним железом, я решил что сделаю небольшую охранную систему для личного пользования.
Введение
Первое, что в данном случае нужно было сделать, это определить что же я хочу от этой системы. Я считаю, что для подобной задачи нет ничего лучше, чем карта мыслей. В общем через 5 минут глубоких размышлений получилось вот что:
Примерно через месяц я получил из UK два RPi модели A, т.е. с одним USB, и без Ethernet. Прикупил флэшку на 16Гб и установил на нее Raspbian. И тут начали всплывать первые из подводных камней — драйвера и интернет. У меня уже был выигранный на конкурсе пару лет назад 3G роутер, и именно его я планировал подключить к RPi, для того, чтобы там появился интернет. Как же я ошибался.
Через полчаса я уже стоял в магазине в поисках адаптера USB-Ethernet, продавец с трудом нашел мне один экземпляр компании D-Link. Перед самым выходом я решил подстраховаться и купить еще и wifi адаптер компании ASUS, т.к. время было позднее, а начать работу сильно хотелось именно сегодня, поэтому права на ошибку просто небыло. На обоих девайсах естественно красовалась поддержка Linux.
Придя домой я убил добрый час в попытках установить с диска, а потом найти в интернете свежие драйвера для D-Link. Оказалось, что ребята из этой компании просто перестали поддерживать это устройство 2 года назад! Я понял, что мне продали барахло (
которое я успешно на следующий день поменял на еще две флэшки). Остался план Б, который заработал на удивление без всяких драйверов, я просто вставил адаптер от ASUS в USB и он сразу же определился в системе как сетевая карта. Сразу скажу, работать на RPi model A в режиме графической оболочки просто невозможно, все очень сильно тормозит, после макбука с SSD это про ад. Поэтому я решил его не мучать, просто настроил себе ssh.
Далее встал вопрос о языке, на котором я буду писать систему для работы с железом. Из всех я выбрал Python, так как там есть работа с железом «из коробки», не нужно дополнительно устанавливать ни каких библиотек и тому подобного, ну и плюс я давно хотел изучить этот язык.
Примерно за неделю я освоил все что мне было нужно от Python, это была очень интересная неделя. Помните в 9 эпизоде первого сезона «Теории большого взрыва» главные герои приделали к некоторым приборам в своей квартире выключатели, которыми можно было управлять через интернет, и любой желающий мог включить/выключить у них свет или аудио систему? Я сделал тоже самое со светодиодом, подключенным к RPi. Это конечно не аудио система, но когда светодиод загорался от щелчка мыши на противоположном полушарии планеты, ощущения были непередаваемые :)
Работа с железом
Следующий вопрос, который мне предстояло решить, это как подключить и управлять железом, ведь датчик движения и дыма это не светодиод, там все несколько сложнее. С этим вопросом я обратился к своему хорошему другу, который просто отлично разбирается в схемотехнике и дружит с паяльником лучше меня.
С этого момента над проектом стало работать два человека, а я стал частым посетителем магазина для радиолюбителей.
Более подробно о работе схемы я расскажу в другой статье, а пока вкратце расскажу про то, как все устроено.
На этой схеме слева датчик движения, справа автомобильная сигнализация, а внизу подключение к источнику питания.
Забегая вперед хочу сказать, что автоматика перехода на питание от аккумулятора была исключена из схемы, т.к. был приобретен аккумулятор, который подключается просто паралелльно с сетевым питанием и автоматически начинает питать схему, когда пропадает 220В. А концевой выключатель для двери было решено заменить на герконы.
Для подключения и питания всей схемы были сделаны две отдельные платы, на одной была реализована работа с железом через оптроны, а на другой преобразование сетевого напряжения 220В в постоянное 12В и 5В. От 12В у нас питаются датчики и сирена, а от 5В соответственно RPi.
Все это хорошо, но следующий вопрос был еще более интересный, как же нам получить данные с датчиков. Мы решили вести всю работу с железом через GPIO, честно говоря думаю это единственный путь, остальные входы/выходы предназначены для специфического оборудования, а нам нужно просто получить с датчиков состояние в виде 1 или 0.
GPIO имеет 26 контактов, из них: 2 по 5В, 2 по 3В, 5 земля, остальные могут быть как входами, так и выходами, в зависимости от конфигурации, которую вы задаете.
Программированию системы работы с железом
Я решил сделать трехзвенную архитектуру:
RPi — Web Server — iOS. Во-первых, чтобы повысить отказоустойчивость, т.к. если исключить сервер из цепочки, то вся система становится очень уязвима из-за того, что RPi может выйти из строя и мы ни как не узнаем об этом. Во-вторых, я решил отказаться от посылки смс и дозвона в пользу push-уведомлений, которые конечно же будет слать сервер.
Итак, для программирования системы работы с железом я выбрал Python 2.5. Первое, что нужно сделать, это установить
WebIOPi. Это отличный open-source проект, который позволяет удобно отлаживать работу с оборудованием через браузер. Он показывает статус портов GPIO, позволяет их сконфигурировать на вход/выход и установить вручную на выходе 0 или 1. Устанавливается все это очень просто, на просторах интернета даже есть подробная инструкция на русском языке.
Как я писал выше, в Python «из коробки» есть работа с GPIO, для этого надо просто подключить специальную библиотеку.
import RPi.GPIO as GPIO
GPIO имеет два режима нумерации контактов, по счету на плате и по внутреннему номеру контакта. Я рекомендую использовать нумерацию по счету, это просто проще.
GPIO.setmode(GPIO.BOARD)
Перед запуском системы нам нужно инициализировать все нужные порты, а именно указать как они будут работать, на вход или на выход, а так же указать начальное значение порта.
GPIO.setup(12, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(11, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
Для считывания порта нужно использовать следующий код:
if (GPIO.input(11) == GPIO.HIGH)
Для установки на выходе порта 1 или 0:
GPIO.output(12, GPIO.HIGH)
GPIO.output(12, GPIO.LOW)
Так как датчик движения выдает сигнал на очень короткий промежуток времени, нам нужно считывать порты постоянно, и тут надо сказать, что в RPi.GPIO это реализуется очень удобно. Можно сделать так, чтобы при получении на входе 1 или 0 вызывалась определенная функция. Так же для каждого порта устанавливается частота считывания.
GPIO.add_event_detect(11, GPIO.FALLING, callback=move_sensor_callback, bouncetime=500)
Несколько важных подводных камней. В цикле программы обязательно нужно сделать sleep(1), иначе процессор будет всегда загружен на 100%, так же при выходе из программы рекомендуется выполнять GPIO.cleanup().
try:
while True:
sleep(1)
finally:
GPIO.cleanup()
Далее расскажу про еще более странную проблему, причины которой я не нашел, но нашел вполне адекватный выход. Примерно через 6 часов непрерывной работы скрипт перестает реагировать на сенсоры. Скорее всего отваливается та самая удобная система, которая вызывает callback функции при наступлении определенного события. Решается это очень просто, скрипт нужно иногда перезагружать, например с помощью cron’а. Для большей надежности, в моей системе cron перезагружает скрипт каждый час, плюс каждую минуту он проверяет работает ли скрипт и запускает его, если это не так.
Работу программы я сделал в виде демона, думаю не нужно тут ничего пояснять, это довольно стандартная штука. При желании можно найти примеры в интернете.
Давайте немного расскажу про архитектуру системы. Работу с каждой железякой я вынес в отдельный модуль. Каждый модуль умеет инициализировать порты, включить/выключить свое железо, проводить самодиагностику, отправлять на сервер сообщения, например когда срабатывает датчик движения, ну и конечно же писать свой статус в лог.
Самодиагностика делается довольно просто, при инициализации все железо должно иметь на выходе единицу, поэтому если мы имеем там ноль, значит датчик не работает или находится не в замкнутом положении (геркон). Соответственно вся система построена на том, что датчики имеют 1 в спокойном состоянии, и соответственно 0, когда они срабатывают или что-то ломается.
Вообще система состоит из 4-х датчиков: датчик движения 360°, датчик дыма, 2 геркона. Ну и конечно же сирена, звонкая, автомобильная, в замкнутом помещении просто ломает уши.
На этом первая часть подходит к концу, в следующий раз я расскажу как делалась серверная часть и iOS приложение. Всем спасибо за внимание, по возможности отвечу на все вопросы по железу и софту в комментариях.
Ну и в завершение небольшой спойлер ко второй части :)