Здесь уже были статьи по управлению сценариями “умного” дома через звонки модема, который подключается через навесной монтаж или GPIO колодку.
Теперь разберем еще одно устройство, которое подключается напрямую к USB разъему.
Давайте сразу проясним, что управление критической инфраструктурой через обычный дозвон не является безопасным.
SIP телефония легко имитирует подделку любых номеров, операторы связи не препятствуют блокировке подмен номеров звонящих, а утекающие данные из многочисленных приложений в вашем телефоне могут с легкостью навести социального инженера на взлом важной для вас инфраструктуры. Квартиры - нет, гаражи - нет.
Только общественная инфраструктура, датчики, телеметрия - все то что не ведет к поломке, прямому ущербу или потенциальной угрозе людям.
Данный пример работы с GSM модемом является образовательным материалом для легкого входа в IOT процессы.
Нацеливаться будем на то, чтобы не ограничивать себя подключением к определенному номеру USB порта и сначала в коде проведем анализ подключенных устройств, выделив нам нужный порт USB.
Исходим из того, что вы уже подключили умное реле к своей репке или проделали действия для подготовки среды к запуску скриптов (речь про PHP).
Ну или добро пожаловать:
https://repka-pi.ru/blog/post/81
Некоторые комментарии по коду.
В первой части кода мы проверяем через команду lsusb количество шин и устройств, которое нам выдает система.
Вторым шагом проходим все USB устройства и в описании ищем нужный нам драйвер ch341
udevadm info -a -n /dev/ttyUSB0
Нашли? Отлично. Оно нам и нужно.
Первая команда AT для общения с модемом - это инициализации модема.
Далее следует набор тестовых запросов на идентификатор оборудованя IMEI, название оператора и качество связи. Выводим это все в терминал для своего спокоствия, чтобы убедиться, что подключение к сети состоялось.
Могут быть проблемы с сим картами, которые не относятся к тарифам для работы с модемами.
Один из вариантов это приобрести сим карту с тарифом умные вещи. Он есть у всех операторов. Такие карты гарантированно работают с данным модемом.
Также существуют недокументированные возможности в виде команды AT+SIMEI=<13цифр IMEI смартфона>, но это противозаконно, я об этом только слышал, никогда не делал и вам не советую.
Далее создаем неблокирующее чтение порта и отлавливаем знак перевода строки (enter).
Отловили строку, проверили на условие в цикле, если это то что нам нужно, например звонок, то сверяем номера из списка и производим нужные действия - в данном примере происходит открытие реле.
Напомню из предыдущего поста, что управление реле происходит через создание текстовых файлов папке /var/www/html.
Также пишем два log файла, один с номерами звонящих, второй файл пишет все команды обмена между терминалом sim800.
Для новичков как стартануть.
Открываем терминал на репке
пишем
sudo nano /var/www/html/read_usb_modem.php
Далее копируем текст скрипта(внизу статьи) и вставляем в окно терминала
Далее ctrl+X … (Save?) Y … Enter
SIM800-USB вставлен в USB порт? Запускаем, набираем в терминале
php read_usb_modem.php
Видим что Bus 002 отображает подключенный usb модем
далее скрипт пробегается по всем шинам и выбирает DEV = /dev/ttUSB0
модем стартует, показвает нам IMEI номер устройства, сеть оператора и качество связи.
Качество до 10 - слабенько, но работать будет, все что от 14 и выше можно расслабиться.
Если говорить про возможности SIM800, то у него есть функция работы с пакетными данными через GPRS, например подключение к http/https ресурсам, использование get, post запросов.
Иногда get/post запросы будут забирать управление терминалом - в момент установления пакетной сессии и обращения к вебсерверу, наш код в этот момент попадает в таймаут, небольшой до 40 секунд, но в этот интервал могут быть какие-то критические ожидания, учитывайте это.
Гораздо эффективнее реализовать протокол MQTT, он более легковесный, быстро переключается после звонков и менее требователен к качеству связи.
В целом конечно работа с пакетными данными является в 99% случаях надежной и успешной, но я бы отнес этот канал связи как резервный и использовал бы при отсутствии основного.
А основные функции, которые закрывает данный модем - это работа со звонками и смс, вот это нужно и обязательно использовать.
Надеюсь вам стало на шаг ближе и легче, чтобы перейти к действию и попробовать себя в управлении данным девайсом. На данный модем есть большой туториал PDF с командами и описанием, сюда его в виду объема прикрепить не могу, но вы с легкостью найдете его в поиске sim800 datasheet
Если остались вопросы, прошу в комментарии.
<?php
$start_time=time();
/*Лог создания службы sudo nano /etc/systemd/system/read_usb_modem.service
[Unit]
Description=SIM800 read com data
After=network.target
[Service]
Type=simple
Restart=always
RestartSec=1
AmbientCapabilities=CAP_SYS_RAWIO
User=root
ExecStart=/usr/bin/php /var/www/html/read_usb_modem.php
[Install]
WantedBy=multi-user.target
//----------------------------------------------
Вот и все. Теперь мы можем запустить сервис:
sudo systemctl start read_usb_modem
И автоматически запускаться при загрузке:
sudo systemctl enable read_usb_modem
-----------------------------------------------------
Управляющие команды следующие:
этот скрипт упакован в службу сназванием read_usb_modem
sudo systemctl start read_usb_modem
sudo systemctl restart read_usb_modem
sudo systemctl stop read_usb_modem
Все что касается управления службой
sudo systemctl enable read_usb_modem
sudo systemctl disable read_usb_modem
sudo systemctl status read_usb_modem
*/
ini_set('max_execution_time', 0);
$eol=chr(10).chr(13); //конец строки
//=======================
$dev = '';// sim 800C usb modem port
$terminal=shell_exec('lsusb');
//выводим отладочную информацию в терминал
echo $terminal;
echo "\n===============\n";
$tline=explode("\n",$terminal);
$time_relay_open=0;
$i=0;
foreach ($tline as $fdev)//проходим по всем dev устройствам в поиске нужного контроллера
{
$terminal=shell_exec('udevadm info -a -n /dev/ttyUSB'.$i);
foreach (explode("\n",$terminal) as $t2)
{
if (strpos(strtolower($t2),'ch341')!==false)
{
$dev = '/dev/ttyUSB'.$i;// sim 800C usb modem
echo $dev."\n";
break;
}
}
$i=$i+1;
}
echo "DEV=".$dev."\n";
//=======================================================================================
if(strlen($dev)==0)
{
echo "Модем не подключен к порту usb";
sleep(10);
exit();
}
else
{
exec("stty -F $dev 9600 raw -echo");
if ($handle = fopen($dev, "r+")){
read_usb_modem_add_log("-------------------------");
fwrite($handle, "AT".$eol); //start
sleep(2);
fwrite($handle, "AT+CLIP=1".$eol); //АОН вкл
sleep(5);
fwrite($handle, "AT+GSN".$eol); //GET GSN
$prev_ord=0;//регистрация символа chr(10)
$sim800_terminal="";
$trigger_ring=0;
//========================================================================
while(1==1)
{
//Используем режим неблокирующего чтения
stream_set_timeout($handle, 20);
stream_set_read_buffer($handle, 0);
stream_set_blocking($handle, false);
$rx = fread($handle,1); //ждем чтения одного символа из терминалки
usleep(10000); //10000 = 10ms
//реле открыто более 10 секунд назад - выключаем его
if ($time_relay_open>0 && time()-$time_relay_open>10 && file_exists(__DIR__."/relay1open.txt")==false)
{
$time_relay_open=0;
$fp = fopen(__DIR__."/relay1close.txt", 'w');
fclose($fp);
}
//================================================================
if (ord($rx)==13 && $prev_ord==1) //фиксируем в терминале от sim800 перевод строки. Обрабатываем
{
//========================================================
if(strpos($sim800_terminal,'AT+GSN')!==false)
{
$gsn_num=str_replace(array(chr(13),chr(10)),'',substr($sim800_terminal,strpos($sim800_terminal,'AT+GSN')+strlen("AT+GSN")+1,strlen($sim800_terminal)));
if(ctype_digit($gsn_num))
{
echo "modem imei: ".$gsn_num."\n";
}
else
{
echo "fake imei: ".$gsn_num."\n";
}
fwrite($handle, "AT+COPS?".$eol);
}
//====================
if(strpos($sim800_terminal,'COPS:')!==false) //запрашиваем название оператора связи
{
$begin_str = strpos($sim800_terminal,'"')+1;
$end_str = strpos($sim800_terminal,'"',$begin_str);
if (strlen($end_str)==0) $end_str=$begin_str+10;
$qual=str_replace(array(chr(13),chr(10)),'',substr($sim800_terminal, $begin_str, $end_str-$begin_str));
echo "COPS IS: ".$qual."\n";
fwrite($handle, "AT+CSQ".$eol); // QUAL
}
//====================
if(strpos($sim800_terminal,'CSQ:')!==false) //обрабатываем информацию о качестве связи. 0-10 так себе, 15-31 хорошо
{
$begin_str = strpos($sim800_terminal,"CSQ:")+5;
$end_str = strpos($sim800_terminal,"OK",$begin_str);
if (strlen($end_str)==0) $end_str=$begin_str+10;
$qual=str_replace(array(chr(13),chr(10)),'',substr($sim800_terminal, $begin_str, $end_str-$begin_str));
echo "QUAL IS: $qual \n";
}
//==============================================================
read_usb_modem_add_log($sim800_terminal);
//========================================================
//+CLIP: "+79261234567",145,"",0,"",0
if (strpos($sim800_terminal,"+CLIP:")!==false)
{
$str_clip = strpos($sim800_terminal,"+CLIP:")+6;
$str_begin = strpos($sim800_terminal,'"',$str_clip)+1;
$str_end = strpos($sim800_terminal,'"',$str_begin);
$phone_number = str_replace("+","", substr($sim800_terminal, $str_begin, $str_end-$str_begin));
echo "Calling number: ".$phone_number."\n";
if (strlen($phone_number)>0 && (ctype_digit($phone_number)) && time()-$time_relay_open>10)
{
if (in_array($phone_number, array("79265111111","791111111","7922111111"))) //список акцептованных номеров
{
fwrite($handle, "ATH0".$eol); //завершаем звонок
$fp = fopen(__DIR__."/relay1open.txt", 'w');
fclose($fp);
$time_relay_open=time();
read_usb_modem_add_call_log("phonenumber exist: ".$phone_number);//полный лог звонков
echo "phonenumber exist: ".$phone_number."\n";
}
else//действия на неизвестные номера
{
fwrite($handle, "ATH0".$eol); //завершаем звонок
echo "unknown phone_number: ".$phone_number."\n";
read_usb_modem_add_call_log("unknown phone_number: ".$phone_number);//полный лог звонков
}
}
}
$sim800_terminal="";
}
else
{
$sim800_terminal=$sim800_terminal.$rx;
}
if (ord($rx)==10){$prev_ord=1;} else {$prev_ord=0;}
}
}
else
{
echo "read.usb.modem.cant.connected.exit";
sleep(30);
}
}
//================================FUNC=====================================
//================================FUNC=====================================
//================================FUNC=====================================
function read_usb_modem_add_log($str)//лог терминала
{
$f=fopen(__DIR__."/sim800_log.txt", "a");
fwrite($f,date("Y-m-d H:i:s",time())." - ".$str."\n");
fclose($f);
}
//====================================================================
function read_usb_modem_add_call_log($str)//лог звонков
{
$f=fopen(__DIR__."/sim800_call_log.txt", "a");
fwrite($f,date("Y-m-d H:i:s",time())." - ".$str."\n");
fclose($f);
}
?>
https://repka-pi.ru/blog/post/81 - эта ссылка внутри статьи некорректная, вместо 81 русский текст