Содержание статьи
СМС — технология не новая, но она все еще широко распространена. При помощи копеечного GSM-модема можно принимать и отправлять сообщения в свое удовольствие. Нужно всего лишь изучить систему его команд или установить софт, который позволит обойтись без этого.
Давно обещанный «интернет вещей» не так далек, как может показаться. Правда, футуристические статьи, рисующие напичканную датчиками технику, редко касаются одного важного вопроса: каким образом информация с этих датчиков достигает сервера? Если они находятся в доме — это одно дело. А если датчики установлены, скажем, на грузовом автомобиле или вообще в чистом поле? Ответ есть: зачастую информацию с датчиков собирает специальный контроллер, который затем передает ее на сервер по СМС. Тут, впрочем, возникает другой вопрос: как построить сервер, способный взаимодействовать с такими устройствами?
СМС и набор команд Hayes
Современная мобильная связь только кажется вещью в себе. Если смотреть на нее с верной точки зрения, быстро выясняется, что у навороченных смартфонов немало общего со старенькими «Курьерами» и «Спортстерами». И те и другие поддерживают так называемый набор команд Hayes.
Ветераны индустрии помнят замысловатые инициализационные строки, которые приходилось скармливать модему перед подключением к BBS или узлу Фидо. Каждая такая строка — это примитивная программа, составленная из команд Hayes для настройки модема.
Модемы, для которых была разработана первая версия набора команд Hayes, не отличались богатыми возможностями. Кроме настроечных команд, имелись команды, которые позволяли набирать телефонные номера, устанавливать соединение, а затем вешать трубку.
За прошедшие с тех пор тридцать пять лет многое поменялось. Расширенный набор команд, который используется сегодня, фактически можно рассматривать как своеобразный программный интерфейс (API), при помощи которого можно управлять мобильным телефоном.
Каким образом это сделать? Сначала необходимо подключить GSM-устройство к компьютеру. Для автоматического обмена СМС удобнее взять не смартфон, а сотовый модем: он не нуждается во взломе, дешевле стоит и не требует проводов. Подойдет обычный 3G-модем, купленный в ближайшем салоне связи за тысячу рублей.
Перед использованием модема или смартфона на компьютер должны быть установлены необходимые драйверы. А вот софт для выхода в интернет, который часто прилагается к модемам, для нашей задачи не только не потребуется, но даже вреден. Если он захватит доступ к устройству, мы не сможем до него достучаться. Вместо этого нам потребуется UNIX-совместимая ОС и минимальное умение работы с командной строкой.
Первым делом наш путь лежит в каталог /dev/. Где-то в его недрах прячется файл подключенного устройства. Говорящее имя поможет идентифицировать его если не напрямую, то методом исключения. 4G-модем «Мегафон M100-4», использованный автором для опытов, обнаружился на пути /dev/tty.HUAWEIMobile-Pcui. Попробуем связаться с ним при помощи утилиты screen.
screen /dev/tty.HUAWEIMobile-Pcui
Теперь можно узнать, что же мы нашли. Для получения информации об устройстве служит команда ATI. Ответ следует немедленно:
ATI
Manufacturer: huawei
Model: E3276
Revision: 21.260.03.00.209
IMEI: 866991010472747
+GCAP: +CGSM,+DS,+ES
OK
Ценным открытием, что под личиной модема «Мегафон М100-4» скрывается Huawei E3276, сыт не будешь. Пора обратиться к более интересным задачам. При помощи команд Hayes можно ввести пин-код (AT+CPIN="0000"), выяснить силу сигнала (AT+CSQ) или набрать один из служебных номеров — например, узнать баланс (ATD102#, где 102# — это номер).
Большинство устройств принимают команды в одном из двух режимов. По умолчанию, как правило, включен режим PDU (Protocol Data Unit), который требует указывать аргументы в цифровой форме. Чтобы не разучивать еще один шифр, лучше перейти в текстовый режим. Для этого служит команда AT+CMGF=1 (нулевое значение вернет устройство в режим PDU).
Следующая настройка, о которой следует позаботиться, — это режим кодирования. Дело в том, что текстовые сообщения могут быть составлены только из цифр, латинских букв и знаков препинания. Символы, которые не входят в классическую семибитную таблицу ASCII, не поддерживаются.
Для пересылки сообщений, которые написаны на алфавитах, не уместившихся в ASCII, придуман обходной путь: текст переводят в кодировку UTF-16, а затем заменяют каждый символ четырехзначным шестнадцатеричным кодом.
Поддерживает ли наше устройство этот способ? Это можно проверить при помощи команды AT+CSCS=?
AT+CSCS=?
+CSCS: ("IRA","UCS2","GSM")
Ответ модема содержит список поддерживаемых режимов кодирования. Режим GSM здесь соответствует чистому семибитному ASCII. IRA нам тоже не поможет — это так называемый International Reference Alphabet, малоизвестная международная разновидность ASCII. А вот UCS2, один из ранних вариантов UTF-16, — это именно то, что нужно. Стоит заметить, что иногда подходящий вариант, подразумевающий замену символов Unicode шестнадцатеричными цифрами, называется HEX, — все зависит от модели и производителя.
Теперь следует активировать нужный режим:
AT+CSCS="UCS2"
В некоторых случаях для работы с кириллицей может понадобиться настройка DCS — схемы кодирования данных. Для этого служит команда AT+CSMP. Значение четвертого аргумента должно быть равно восьми:
AT+CSMP=1,167,0,8
Есть два основных способа отправки текстовых сообщений при помощи команд Hayes. Первый реализует команда AT+CMGS. Чтобы отправить сообщение, нужно дать ей телефонный номер адресата и нажать Enter. Все, что будет введено далее, рассматривается как текст сообщения. Закончить ввод текста можно при помощи сочетания клавиш Ctrl + z.
AT+CMGS="+79295556924"
Privet!
Перед отправкой сообщения на русском языке придется позаботиться о его перекодировании. Это можно сделать при помощи любого скриптового языка. Вот, например, вариант на Python:
''.join('%04X'%ord(c) for c in u'Привет')
'041F04400438043204350442'&lt
Теперь можно отправлять:
AT+CMGS="+79295556924"
041F04400438043204350442
Второй способ состоит из двух шагов. Первый шаг потребует команды AT+CMGW. Она очень похожа на AT+CMGS, но не отправляет сообщение, а сохраняет его в памяти SIM-карты. На втором шаге сохраненную СМС отправляет другая команда — AT+CMGS. Чтобы отправить сообщение, нужно знать его номер. К примеру, команда отправки сохраненного сообщения под номером 12 выглядит так:
AT+CMGS=12
Этот способ удобен в тех случаях, когда один и тот же текст нужно доставить нескольким адресатам. Вместо того чтобы каждый раз передавать его устройству, достаточно один раз сохранить сообщение в памяти, а затем указывать модему лишь его индекс и номер получателя.
AT+CMGS=12,"+79295556924"
AT+CMGS=12,"+79295556925"
AT+CMGS=12,"+79295556926"
Команда удаления сообщения тоже принимает на входе его номер:
AT+CMGD=3
Покончив с отправкой, займемся приемом. Устройство самостоятельно принимает и сохраняет текстовые сообщения, поэтому задача сводится к извлечению СМС из памяти SIM-карты.
Сообщения хранятся в нескольких папках с разным назначением. Полный список выдаст команда AT+CMGL=?
AT+CMGL=?
+CMGL: ("REC UNREAD","REC READ","STO UNSENT","STO SENT","ALL")
Смысл папок понятен по их названиям: одна из них содержит прочтенные СМС, другая — непрочтенные, две другие — отправленные и неотправленные. Наконец, последняя называется ALL и позволяет увидеть все сообщения без фильтрации по папкам. Этим тоже занимается команда AT+CMGL.
AT+CMGL="ALL"
+CMGL: 1,"REC READ","004200650065006C0069006E0065",,"09/12/18,19:21:01+12"
041204300448002004310430043B0430043D04410020043C0435043D043504350020003100350020044004430431002E002000AB04170432043E043D043E043A0020043704300020044104470435044200200441043E04310435044104350434043D0438043A043000BB002E00200418043D0444043E0020003000360034003000310032
+CMGL: 2,»REC READ","002B00370039003000330033003300330032003700330038",,"09/12/18,20:13:16+12" 041F043804410430043B002E002004270442043E0020043C0435043D044F00200443043204350437043B04380020043A044304340430002D0442043E002004380020043D04350020043E0442043F04430441043A0430044E0442002E
Шестнадцатеричными цифрами закодирован не только текст сообщения, но и имя отправителя. Попытаемся расшифровать первое сообщение. Здесь снова поможет Python:
def decode_sms (txt):
... print ''.join(unichr(int(txt[i:i+4], 16)) for i in range(0, len(txt), 4))
decode_sms('004200650065006C0069006E0065')
Beeline
decode_sms(\
'041204300448002004310430043B0430043D04410020043C0435043D043504350020003100350020044004430431002E002000AB04170432043E043D043E043A0020043704300020044104470435044200200441043E04310435044104350434043D0438043A043000BB002E00200418043D0444043E0020003000360034003000310032')
Ваш баланс менее 15 руб. «Звонок за счет собеседника». Инфо 064012
Время от времени диалог с модемом прерывается сообщениями, которые представляют собой не ответы на введенные команды, а уведомления о внешних событиях. Модемы используют код +CMTI, чтобы сообщить о приеме нового сообщения, а код +CDSI уведомляет о статусе отправляемого СМС.
Gammu и Gammu SMS Daemon
До сих пор мы общались с модемом в интерактивном режиме при помощи терминала. На практике это взаимодействие должно быть полностью автоматизировано. Это не проблема: открыть файл /dev/tty.HUAWEIMobile-Pcui программно ничуть не труднее, чем любой другой. Трудность может состоять в другом. Если планируется поддерживать более одной модели модема, придется разбираться в особенностях и капризах каждой.
Непосредственное управление при помощи команд Hayes — это хороший вариант, когда модем один, его модель известна, тонкости никого не волнуют, а все взаимодействие можно описать парой-тройкой строк кода. Когда запросы выше, стоит обратить внимание на одно из готовых средств для работы с телефонами и модемами.
В этом случае может подойти набор утилит командной строки Gammu — развитие известного в прошлом проекта Gnokii, избавленного от, увы, устаревшей ориентации на продукцию Nokia. Список поддерживаемых Gammu телефонов и GSM-модемов не ограничивается устройствами одного производителя. В нем, впрочем, все же имеются пробелы, поэтому вопросами совместимости лучше озаботиться заранее.
Gammu позволяет извлекать списки принятых и инициированных звонков, открывать телефонные соединения и управлять ими, просматривать телефонные книги, изучать информацию о телефоне и сотовой сети и многое другое, вплоть до работы со встроенным FM-приемником. Разумеется, прием и отправка СМС и MMС тоже входит в список умений этой программы.
Для установки Gammu под OS X следует воспользоваться командой brew install gammu (требуется пакетный менеджер brew). Под Linux поможет apt-get install gammu gammu-smsd или ее эквивалент для другого пакетного менеджера. Пользователям Windows придется отыскать и скачать инсталлятор на сайте проекта.
Работа с Gammu начинается с настройки. Проще всего это сделать при помощи утилиты, которая запускается командой gammu-config. Она поинтересуется «портом» (в нашем случае сюда попадает уже знакомый путь /dev/tty.HUAWEIMobile-Pcui), типом и скоростью соединения, моделью (если ничего подходящего нет, стоит выбрать at — в этот тип входит любое устройство, поддерживающее набор команд Hayes) и запросит несколько менее интересных деталей. Введенная информация будет сохранена в настроечном файле ~/.gammurc, который при необходимости можно отредактировать в любом текстовом редакторе.
Здесь тоже возможны проблемы с кодировками, но их решение проще и прямолинейнее. Чтобы русский язык не вызывал у Gammu паники, в системе должна быть верно настроена локаль и язык. Для этого в OS X и Linux стоит добавить в инициализационный файл (например, ~/.bash_profile) следующие строки:
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
Дальше будет проще. Для отправки сообщения служит команда gammu sendsms TEXT <номер телефона>. Сам текст сообщения нужно направить команде по механизму pipes. Это упрощает отправку по СМС текста, который является результатом работы другой программы.
echo "Привет" | gammu sendsms TEXT +79295556924
В Gammu предусмотрено несколько способов чтения сообщений, но для того, чтобы рассмотреть их все, здесь просто нет места. Ограничимся одной, самой простой командой. Она выводит все СМС, хранящиеся в памяти устройства.
gammu getallsms
Location 1, folder "Inbox", SIM memory, Inbox folder
SMS message
SMSC number : "+79037333332"
Sent : Fri Dec 18 21:17:17 2009 +0300
Coding : Unicode (no compression)
Remote number : "Beeline"
Status : Read
User Data Header : Concatenated (linked) message, ID (16 bit) 8444, part 1 of 2
Общее время Ваших разговоров 4 мин.
Нередко вместо выполнения команды Gammu жалуется на проблемы. Ошибка «Error opening device. Unknown, busy or no permissions» может свидетельствовать о том, что соединение с модемом захватила какая-то другая программа. Возможен и другой вариант: GSM-модемы, как оказалось, не отличаются крепкими нервами и под градом команд склонны виснуть. Чтобы привести их в чувство, устройство приходится вытаскивать из порта USB и затем втыкать снова.
Еще один важный компонент Gammu — это SMS Daemon, программа, которая одним махом решает три четверти задачи построения сервера для взаимодействия по СМС. SMS Daemon работает в фоне, поддерживает контакт с модемом и при получении сообщения выполняет заданные действия. К слову, с перезагрузкой подвисшего модема он тоже справляется.
Настройка SMS Daemon — относительно сложная задача, требующая ручного редактирования настроечного файла. В нем должен появиться блок [smsd], содержащий настройки сервиса хранения СМС и, если это необходимо, задающий путь к обработчику получаемых сообщений.
Сервис хранения СМС ценнее всего. SMS Daemon способен автоматически записывать полученные сообщения в базу данных (поддерживаются среди прочего SQLite, MySQL, PostgreSQL и MS SQL) или складывать в виде файлов в специальную папку.
Обработчик представляет собой программу или скрипт пользователя, автоматически запускаемый после приема сообщения. Информация передается обработчику через переменные окружения. Переменная SMS_MESSAGES содержит число полученных сообщений, значение SMS_1_NUMBER соответствует телефонному номеру отправителя, а SMS_1_TEXT — тексту сообщения.
SMS Daemon можно использовать и для отправки сообщений, хотя это несколько выбивается из круга его обязанностей. Это делается при помощи команды gammu-smsd-inject, действующей в точности как gammu send-sms.
echo "Привет" | gammu-smsd-inject TEXT +79295556924
Интернет-шлюзы СМС
Есть и другой, более радикальный способ избавиться от необходимости нянчиться с капризными железками, а именно: переложить все заботы на плечи специально обученных людей и платить им, чтобы они страдали за тебя. Крупнейший международный сервис такого рода называется Twillio. В России автоматизированный прием и передачу текстовых сообщений можно наладить при помощи сервиса «SMS-центр».
Для отправки сообщений через smsc.ru служит простой программный интерфейс в стиле REST. Все необходимые параметры передаются сервису в виде запроса по GET или POST, а тот возвращает результаты его обработки. Вот пример команды, требующей отправить по телефону 79299999994 текст «Привет», переданный в кодировке UTF-8, и сообщить о результатах в формате JSON (fmt=3).
http://smsc.ru/sys/send.php?login=&psw=&phones=79299999994&mes=Привет test&charset=utf-8&fmt=3
Добавление к этому запросу аргумента cost=1 вынудит сервис подсчитать стоимость отправки такого сообщения (отправления при этом не произойдет). А аргумент flash=1 придает обычному текстовому сообщению зрелищности: оно будет продемонстрировано получателю немедленно, в каком бы приложении он ни находился и чем бы ни занимался.
Некоторые тонкости есть и тут. Во-первых, пароль. Чтобы не гонять его по интернету в открытую, лучше заменить пароль хешем MD5. «SMS-центр» допускает такой вариант. Во-вторых, Sender-ID. Операторы недоверчиво относятся к сообщениям безымянных отправителей и то и дело отказываются их доставлять. Эту проблему можно решить, воспользовавшись подписью Sender-ID, которая принадлежит «SMS-центру», либо зарегистрировав свою собственную.
Для приема СМС в сервисе предусмотрено два метода: Push и Pull. В первом случае пользователь может время от времени сам запрашивать пришедшие сообщения у сервиса. Принцип тот же, что и при отправке:
http://smsc.ru/sys/get.php?get_answers=1&login=&psw=&charset=utf-8&fmt=3
[{
"id": 20032761,
"received": "21.03.2015 12:03:58",
"phone": "79299999994",
"message": "Привет",
"to_phone": "79684455555",
"sent": "21.03.2015 12:02:34"
}]
Чтобы не скачивать каждый раз все сообщения от начала времен, к запросу можно добавить аргумент after_id и передавать в нем идентификационный номер последнего полученного СМС.
Во втором случае можно поручить «SMS-центру» при получении сообщений самостоятельно уведомлять об этом веб-сервис пользователя. Для этого на нем должен быть реализован соответствующий обработчик информации. Выглядеть он может, к примеру, так:
@app.route('/sms')
def smsc_receiver():
phone_from = request.args['phone']
phone_to = request.args['to']
text = request.args['mes']
return process_sms(phone_from, phone_to, text)
Напоследок стоит упомянуть, что далеко не всякая автоматизированная рассылка СМС легальна. По закону, отправитель рекламных сообщений должен заручиться согласием получателя и быть готовым доказывать свою правоту. Абонент, не желающий получать СМС, должен быть немедленно исключен из списков рассылки. Кроме того, есть длинный список запретных тем. От порно, наркотиков, терроризма и других предсказуемых вещей лучше держаться подальше. В противном случае можно нарваться на блокировку телефонного номера, штрафы, а то и кое-что похуже. Тебе это надо?