
Недавно было решено установить в многоквартирном доме шлагбаум с управлением через GSM. Причины и необходимость этого решения остаются за рамками статьи, я же хочу написать про то, как «на коленке» сделал интерфейс для управления модулем через Интернет. И даже немного с блэкджеком, управлением базой автомобилей с мобильного телефона и фотографиями моментов въезда с уличной камеры. Возможно кто-то захочет внедрить у себя.
Предупрежу, что в статье не описано готовое решение «из коробки», а скорее proof of concept.
Часть 1. Почему такой контроллер
Перед тем, как ставить GSM-модуль был проведен некоторый обзор рынка таких систем. Хотелось получить недорогое, надежное и отработанное решение. Ну и чтобы «в наличии» было у установщиков, имелся какой-то опыт работы с ним и т.д.
Установщики на выбор предлагали либо ESIM 110/120 за ~12000р с доступом в интернет, либо Doorhan GSM за ~6000р с управлением через SMS или настройкой через USB-шнурок.
Вариант «сделать свой девайс из arduino + gsm модуль за $3» не рассматривался, так решение должно быть точно надежным и обкатанным. Представьте, кто-то не смог заехать домой? Потом неприятностей получишь по полной.
Так же не хотелось лотереи с покупкой устройства в Китае на Али, опять же во избежание проблем с надежностью. Хотя цены начинаются от 1500р.
Минусом ESIM120, кроме стоимости х2 было то, что для выхода в интернет использовался GPRS-интернет. Для кого-то может это и плюс, но в нашем случае тянет за собой расходы на мобильную связь — придется брать тариф с интернетом. Сейчас же на сим-карте подключен тариф без абонентской платы, и для того, чтобы номер не заблокировался, раз в 2-3 месяца планирую подключать из личного кабинета оператора какую нибудь платную подписку за 2-3 рубля в сутки, на одни сутки. Например «погоду», «анекдоты» или что там еще полезно для шлагбаума )
Про Doorhan GSM я знал, что помимо управления через SMS (мало интересно) он подключается по USB к компьютеру и через собственное ПО позволяет управлять базой номеров.
Часть 2. Проброс управления
Так как блок управления шлагбаумом планировалось ставить в 20 метрах от помещения, где расположен домовой IP-видеорегистратор и узел связи местного интернет-провайдера, то было решено взять Doorhan и «нарастить» USB через роутер вроде TP-Link MR3020 стоимостью 1200р, OpenWRT и программ из проекта USBIP
С роутером получилось даже несколько проще — нашел в закромах старый ASUS WL500gP, который для интернета не очень годится по нынешним меркам, зато имеет 2 USB порта. Его и использовал.
Для usbip пришлось поставить старую версию OpenWRT, 12.09, потому как на новых не работает этот модуль ядра. Подключение роутера к сети описывать не буду. Если у кого-то он будет не в локальной сети, возможны варианты с пробросом портов, UPNP или настройкой VPN, на ваш вкус.
Устанавливаем kmod-usbip-server и проверяем, что у нас можно экспортировать
root@OpenWrt:~# opkg install kmod-usbip-server
root@OpenWrt:~# usbip list -l
Local USB devices
=================
- busid 1-1 (0424:2502)
1-1:1.0 -> hub
- busid 1-1.1 (1a86:7523)
1-1.1:1.0 -> ch341
Нам нужен busid устройства, 1-1.1 на котором располагается наш подключенный контроллер GSM. Забегая вперед, оказалось что это банальный COM->USB конвертер на чипе CH341
Выполняем:
root@OpenWrt:~#usbipd -D
root@OpenWrt:~#usbip bind -b 1-1.1
bind device on busid 1-1.1: complete
и в dmesg
usbip-host 1-1.1:1.0: usbip-host: register new device (bus 1 dev 57 ifn 0)
На компьютере с Windows устанавливаем драйвера USBIP и запускаем
usbip -a 10.16.19.19 1-1.1где 10.16.19.19 IP-адрес нашего роутера с OpenWRT. Разумеется, для начала нужно отрыть доступ либо с вашего IP к роутеру в firewall, либо подключаться с локальной сети, либо любой из 1000 других вариантов от VPN до P2P.
Если все прошло успешно, то Windows радостно заявляет, что обнаружено новое устройство USB-to-Serial Converter CH340, даем ей драйвера и в системе появляется COM-порт.
Теперь можем запустить программку из комплекта с контроллером и управлять номерами в базе сидя дома на диване

Часть 3. Контроль въездов
После того, как настроил работу с контроллером на домашнем компьютере и записал номера пользователей, решил немного поковыряться с тем, что доступно по СОМ-порту.
Оказывается, GSM-модуль контроллера периодически шлет AT команды с уровнем сигнала в консоль, а так же при звонке пишет номер телефона, с которого поступает звонок. Управлять модулем AT командами не получилось, видимо они не транслируются от модуля контроллера в модуль модема.

Все равно это довольно интересно. При проработке идеи GSM-контроллером я надеялся, что эти звонки будут фиксироваться в детализации оператора. Но так как соединения не происходит, то записей в детализации нет. Теперь же можно прямо с контроллера собирать логи того, кто открывал шлагбаум. Или кто пытался это сделать.
Для этого на OpenWRT устанавливаем kmod-usb-serial-ch341, отключаем трансляцию USBIP командой usbip unbind -b 1-1.1 и делаем insmod ch341.
После чего прямо на роутере можно подключиться к /dev/ttyUSB0 и смотреть что там происходит со звонками в контроллере.
Для обработки данных для начала написал простейший скриптик, который с помощью curl отсылал данные о входящем звонке на внешний сервер с PHP для обработки и сохранения в базу. С таким же успехом можно писать в локальный файл, правда на роутере памяти не густо.
#!/bin/sh
cat /dev/ttyUSB0 | while read DATA; do
if echo $DATA | grep -q CLIP ; then
curl --silent --output /dev/null --data "data=$DATA" http://1.2.3.4:8081/border.php
fi
done
На сервере создал базу в mysql и пару табличек: с номерами телефонов наших жильцов, и с логом звонков. Стало возможно сопоставить кто в какое время открывал шлагбаум, и пытаются ли им пользоваться с неизвестных номеров.
Вторая идея, которая пришла мне в голову — сделать связку между событием открывания шлагбаума и фото этого события. Дело в том, как я упоминал выше, вокруг дома стоят AHD камеры, которые пишут на регистратор с доступом к потоку по IP. Причем одну из камер специально повернули на шлагбаум, в ожидании что его будут ломать.
Как снять jpeg с нашего китайского регистратора мне найти не удалось, хотя во многих камерах есть Preview URL. Поэтому пошел в лоб — в момент звонка получаю RTSP и делаю из него JPG.
ffmpeg -i "rtsp://2.3.4.5:554/user=user&password=password&channel=1&stream=0.sdp?" -y -f image2 -t 0.001 -ss 00:00:3 -s 1280*720 /tmp/screenshot.jpgС таким же успехом можно было писать маленькие ролики в mp4, но я посчитал это лишним.
Фотографии решил хранить блобами в MySQL. Решение по производительности так себе, зато «таскать» проектик будет проще, не нужно копировать и базу и файлы, все в базе. А нагрузка на него никакая по сути.
В результате лог въездов выглядит как-то так:

Часть 4. Загрузка номеров из SQL базы на сайте в контроллер
Если вы внимательно читали, то наверное заметили — для регистрации въездов USB порт роутера работал в режиме Serial-to-USB преобразователя, а для работы с базой номеров внутри контроллера приходилось «прокидывать» его через USBIP на домашний компьютер и там через программу для Windows вносить изменения. Это не совсем удобно, нужно было делать unbind/bind да еще на домашнем компьютере запускать usbip консольный. Ну и делать это можно было только из дома (ну либо опять RDP/VPN и проч и проч), и тем более не с телефона мобильного. Вдвойне накаляло то, что базу номеров надо было вести и в mdb-формате (программа для контролера может выгружать данные в Access) и в веб-версии.
Беглый гуглеж протокола работы Doorhan GSM ничего не дал. Не исключаю, что это какая-то китайская девайсина под маркой Doorhan, тем не менее. Поэтому вооружился монитором (сниффером) для COM-порта и снял несколько дампов при работе из оригинального приложения.

Получил что-то вроде:
Команда на очистку памяти
aa 02 09 00 00 03 e8 01 00 00 00 00 eeОтвет что все прошло хорошо
aa 20 00 eeКоманда на запись одного номера +79999999999
aa 03 10 00 01 2b 37 39 39 39 39 39 39 39 39 39 39 00 00 eeИз чего сделал такие выводы:
начало посылки АА, конец посылки ЕЕ
Ответ, что команда принята 20 00
Команда на начало записи номера 03. Потом идет количество номеров в посылке. Может быть от 1 до 5, то есть за одну посылку можно отправить сразу несколько номеров подряд, закончив посылку командой ЕЕ, и получить одно подтверждение на блок.
После команды начала записи идет 2 байта (второй точно, первый возможно будет использоваться при количестве номеров больше 256 но не проверял), указывающие порядковый номер записи в ячейке. То есть если писать в пустую память, то сначала это 1, потом 2 и так далее. После чего идет 14 байт номера телефона. Так как наши номера«входят» в 12 байт (+79999999999) то последние 2 байта заняты нулями. Вероятно для всяких международных форматов типа +10. Это не точно, сильно не вникал, потому что хватило этих данных.
Завершается посылка байтом ЕЕ.
С порядковым номером ячейки не совсем понятная ситуация. Допустим, в контроллер записано 10 номеров, память не очищали. Если отправить команду «записать номер в ячейку 5», то номер в этой ячейке после контрольной вычитки не изменится, но в контроллере будет записано 11 номеров, причем 11-й будет пустой. А если дать команду «записать номер в ячейку 11», то он запишется в нее. Вероятно это связано с командами ADD и REPLACE (добавить и изменить), которые можно слать через SMS. Но так как оригинальное приложение делает только полное стирание и последующую перезапись списка, проверить гипотезу не получается. Поэтому свой вариант делал тоже отправкой команды стирания и последовательной записи по одному номеру (для простоты).
Итак, мы подошли к тому, чтобы писать в контроллер через последовательный порт команды на управление списком абонентов. Но порт в роутере, а база с пользователями на внешнем сервере. Переносить ее на роутер конечно можно (получится законченное решение «из коробки») но мне было лень. Пошел другим путем — прокинул /dev/ttyUSB0 по tcp с помощью пакета ser2net, который есть в поставке openwrt.
Конфиг /etc/ser2net.conf простой для неприличия
3333:raw:0:/dev/ttyUSB0:9600,remctlПосле запуска ser2net можно подключиться к роутеру telnet-ом на 3333 порт и проверить результат.
Хочется сделать оговорку: после перезапуска роутера внезапно не работал cat /dev/ttyUSB0. То есть конечно работал, но в консоль писал мусор. Вспомнил, что во время экспериментов на роутере я запускал minicom, который вероятно сделал инициализацию порта. Просто установив режим 9600 8n1 результата не дало, поэтому я посмотрел с помощью stty какие настройки порта стоят в «рабочем» состоянии и вписал в rc.local инициализацию
stty -F /dev/ttyUSB0 9600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echokeНаверняка что-то лишнее, читатели поправят возможно. Ну и если параметры порта указать в ser2net.conf то можно не ставить на роутер пакет stty.
В результате этих манипуляций на сервере с WEB-интерфейсом у нас стала доступна консоль контроллера. Поэтому немного перепишем код обработки звонков. Писал на PHP. Весь код стыдно показывать (я же не программист в конце концов, а использовал вставки из учебника), поэтому суть:
$re = '/CLIP: ".(7\d{10})"/m'; //регэксп на входящий звонок
while (1) {
$f=@fsockopen('tcp://10.16.19.19',3333, $errno, $errstr); //подключимся к роутеру
while ($f && $str=fread($f, 100)) {
$test=preg_match($re, $str, $matches);
if ($test) process_call($matches[1]); //пришел звонок, запишем в базу и сделаем фото
//проверим нужно ли записать телефоны из базы в контроллер и сделаем это, если нужно, используя дескриптор $f
sync_phones($f);
}
if ($f) fclose($f); //что-то пошло не так
sleep(5); //подождем 5 сек и сделаем реконнект
}
process_call каждый думаю реализует как ему нравится, от записи информации в лог-файл, до создания фото/видео въезда и отправки их через Телеграм-бота на телефон супруга/супруги, а так же команды на кофеварку или для разогрева борща.
На sync_phones остановлюсь чуть подробнее, потому как там реализован тот самый «не публичный» алгоритм работы GSM-контроллера Doorhan. И да, рукалицо, я использовал mysql вместо pdo или mysqli.
function sync_phones($f) {
/*
В таблице config хранится переменная update. Если в веб-интерфейсе сделать val=1, то должен запуститься процесс обновления номеров в контроллере. Можно сделать семафор через файл, но раз я все равно использую mysql, то решил сделать так
*/
$result=mysql_query("SELECT * FROM config WHERE name='update' AND val=1");
if (mysql_num_rows($result)==0) return;
$init = pack('H*','aa1100ee'); //какой-то пакет инициализации при подключении программы к контроллеру
$clear = pack('H*','aa0209000003e80100000000ee'); //команда на стирание данных (выдрано с дампа СОМ-порта)
$logfile=fopen("/tmp/border.log","a");
fwrite($logfile,"Update started at ".date('Y-m-d H:i:s')."\n");
fwrite($f,$init);
sleep(1);
$ans=fread($f, 40);
fwrite($logfile,"Send init. Answer ".bin2hex ( $ans )."\n");
fwrite($f,$clear);
sleep(1);
$ans=fread($f, 40);
fwrite($logfile,"Send clear. Answer ".bin2hex ( $ans )."\n");
$result=mysql_query("SELECT * FROM phones ORDER BY pid");
//тут все ясно, select всех номеров из базы пользователей, чтобы записать их в контроллер
$n=1;
while($row=mysql_fetch_array($result)) {
$start = 'aa0310';
$pos = sprintf("%04x",$n);
$phone = bin2hex('+'.$row['phone']).'0000';
$end = 'ee';
//собираем "посылку"
$send=$start.$pos.$phone.$end;
$bin=pack('H*',$send);
fwrite($f,$bin);
$ans=fread($f, 40);
fwrite($logfile,"Write {$n} phone {$row['phone']}. Answer ".bin2hex ( $ans )."\n");
$n++;
}
fwrite($logfile,"End update\n");
fclose($logfile);
//запишем в наш "семафор" время, когда обновление завершено, полезная информация. И сам флажок, что обновление не требуется
mysql_query("UPDATE config SET val=0,result='".bin2hex ( $ans )."',updated=NOW() WHERE name='update'");
}
?>Часть 5. Заключение
В результате я получил (написал) WEB-интерфейс для шлагбаума, куда могу заходить через мобильный браузер, добавлять/удалять/изменять там номера жильцов нашего дома, делать пометки, с привязкой к квартире, ФИО, номеру телефона, номеру авто. Есть фотографии авто, лог въездов через шлагбаум с фото. В будущем, по логам посмотрю кто создает наибольшую нагрузку на шлагбаум — тот и будет оплачивать ремонт :)
Ну и бонусом получил WIFI-точку на парковке возле дома.
Цена вопроса — 0 рублей и выходной за компом.
