Но в большинстве вышеописанных сценариев использования SMS нельзя положиться на внешние SMS-шлюзы или API, так как система отправки уведомлений должна работать даже тогда, когда пропадает доступ в интернет. Особенно это важно для охранных систем, которые должны функционировать независимо от внешних источников питания.
Очевидное решение всех этих проблем заключается в поддержке собственного SMS-шлюза.

Как, с минимальными затратами, самостоятельно сделать такой шлюз?
Если в двух словах описать решение этой задачи, то оно заключается в использовании пакета
gammu-smsd
и кода API с GitHub. А подробный ответ на этот вопрос приведён ниже.Компоненты
▍Raspberry Pi

На самом деле — неважно, какой именно одноплатник Raspberry Pi выбрать. Можно даже использовать самый первый — тот, что появился в 2007 году.
Если говорить об операционной системе, то тут подойдёт и Raspbian, и Alpine Linux (да и, в общем-то, любая ОС, в которой, без особых сложностей, можно установить необходимые нам пакеты). Я выбрал Alpine из-за того, что она работает с RAM-диска, то есть, внезапное отключение питания не повредит данные на SD-карте. Но я тут приведу и инструкции по настройке Raspbian.
▍3G/4G USB-модем

В моём проекте используется USB-модем Huawei E303. Не все USB-модемы подойдут для подобных проектов, но практически все модемы Huawei этой серии недороги и их легко достать. Если у вас уже есть USB-модем, можно заранее поискать в интернете сведения о том, работает ли он с Raspberry Pi, чтобы не оказалось так, что он вам не подойдёт.
Понятно, что понадобится ещё и SIM-карта с тарифным планом, поддерживающим работу с SMS-сообщениями.
Шаг 1: подготовка Raspberry Pi
Я исхожу из предположения о том, что у вас уже имеется работающий одноплатный компьютер Raspberry Pi, на котором вы можете выполнять команды — либо по ssh, либо подключив к нему мышь, клавиатуру и монитор.
Нам понадобятся некоторые пакеты.
Вот команда для их установки в Apline Linux:
apk add gammu gammu-smsd php php-json usb-modeswitch usbutils git
Вот — команда для Raspbian:
apt install gammu gammu-smsd php php-json usb-modeswitch git
Большинство USB-модемов при подключении к компьютеру, по умолчанию, работают в режиме устройства хранения данных (storage mode), а нам нужен режим модема (modem mode). Для того чтобы понять, в каком именно режиме работает ваш модем — подключите его к настольному компьютеру. Если система монтирует его в виде USB-диска (обычно содержащего установщики разных программ и драйверов) — вам надо переключить его в режим модема.
Переключение режима устройства в различных модемах выполняется немного по-разному (тут, опять же — Google вам в помощь). В моём случае всё чудесным образом переключилось с помощью такой команды:
usb_modeswitch -W -v 12d1 -p 14fe -K -P 14ac -M "55534243000000000000000000000011060000000000000000000000000000"
Команда
lsusb
возвращает Bus 001 Device 005: ID 12d1:1c05 HUAWEI HUAWEI Mobile
. 12d1
— это код поставщика для Huawei, а строка, идущая за этим кодом (в моём случае — 1c05
) — это ID продукта. Мы используем данные, полученные с помощью lsusb
, при вызове usb_modeswitch
.Вы, чтобы изменить режим работы своего модема, можете обратиться к Arch Wiki или к Ubuntuusers Wiki. Там должно быть всё необходимое.
Если система, после подключения модема, создаст
/dev/ttyUSB0
, это значит, что всё сделано правильно.pi:~# ls -al /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 0 Dec 3 12:10 /dev/ttyUSB0
После этого аппаратная часть нашего проекта — Raspberry Pi и модем — готова к дальнейшей работе.
Шаг 2: установка Gammu
Что такое Gammu? Процитирую разработчиков проекта:
Утилита командной строки Gammu — это средство, дающее доступ к широкому набору телефонных возможностей.
В этой утилите уже реализованы сложные механизмы, ответственные за разбор сообщений и за их отправку по мобильным сетям. А нам нужно лишь сообщить утилите о том, как ей общаться с модемом, который мы настроили на предыдущем шаге.
Для того чтобы узнать о том, видит ли утилита модем, можно выполнить команду
gammu identify
. Если всё нормально — в ответ вы получите примерно следующее:vpnpi:~# gammu identify
Device : /dev/ttyUSB0
Manufacturer : Huawei
Model : E303 (E303)
Firmware : 21.157.01.00.199
IMEI : 860000000000619
SIM IMSI : 230000000000006
Пока всё нормально. Создадим теперь конфигурационный файл со следующим содержимым и сохраним его в папке
/etc/gammurc
:[gammu]
device = /dev/ttyUSB0
name = Bob
connection = at
logfile = /var/log/gammu.log
[smsd]
service = files
logfile = syslog
#PIN = 1234
# Увеличьте значение для получения отладочной информации
debuglevel = 0
# Пути, по которым хранятся сообщения
inboxpath = /var/spool/gammu/inbox/
outboxpath = /var/spool/gammu/outbox/
sentsmspath = /var/spool/gammu/sent/
errorsmspath = /var/spool/gammu/error/
Имя (поле
Name
) может быть любым. Я выбрал Bob
— так зовут «абонента», от имени которого отправляются сообщения.Если для работы с вашей SIM-картой нужен PIN-код — его можно задать в соответствующем поле раздела
[smsd]
.▍Быстрая проверка возможности отправки SMS
Для того чтобы проверить, что всё, что мы до сих пор настраивали, правильно работает, и то, что мы можем отправлять SMS-сообщения — достаточно выполнить такую команду:
echo "some message" | gammu --sendsms TEXT 0664xxxxxxx
Ясно, что вам надо будет, как минимум, подставить сюда свой номер телефона.
Попробуем:
vpnpi:~# echo "Hello from your Pi" | gammu --sendsms TEXT 0664xxxxxxx
If you want break, press Ctrl+C…
Sending SMS 1/1…waiting for network answer..OK, message reference=22
Заглянем в телефон.

Работает! На телефон пришло сообщение.
На этом можно и остановиться, автоматизировав отправку сообщений средствами командной строки. Но мне хотелось бы отправлять и получать сообщения с помощью API, к которому можно обращаться с любого устройства, находящегося в моей домашней сети, поэтому в описании этого проекта присутствует ещё два шага.
Шаг 3: подготовка к созданию API для отправки и получения SMS-сообщений
Мы, для отправки сообщений, использовали команду
gammu
. Но получение сообщений — операция более сложная, её непросто автоматизировать. Правда — её можно было бы назвать сложной в том случае, если бы создатели Gammu не написали бы ещё и утилиту gammu-smsd
.▍Утилита gammu-smsd
На вопрос о том, что такое
gammu-smsd
, как и прежде, лучше всего способны ответить разработчики проекта:Gammu SMS Daemon — это программа, которая периодически сканирует SMS-модем на предмет полученных сообщений, сохраняет их в заданном месте и, кроме того, отправляет сообщения, которые находятся в хранилище и ждут отправки. Это — программа, которая отлично подходит для автоматизации управления большими объёмами принятых или отправленных сообщений.
Получается, что
gammu-smsd
— это демон, который ожидает появления новых входящих или исходящих SMS-сообщений.Так как в вышеприведённом конфигурационном файле для Gammu уже содержится всё, что нужно для
gammu-smsd
, нам осталось лишь создать папки, где gammu
будет хранить данные:mkdir -p /var/spool/gammu/inbox/
mkdir -p /var/spool/gammu/outbox/
mkdir -p /var/spool/gammu/sent/
mkdir -p /var/spool/gammu/error/
▍Отправка сообщений
Запустим
gammu-smsd
в режиме демона (то есть — позволим программе выполняться в фоновом режиме) и скажем ей о том, где находится созданный нами ранее конфигурационный файл:gammu-smsd -d -c /etc/gammurc
Так как теперь у нас имеется демон, который общается с модемом, пользоваться ранее рассмотренным способом отправки сообщений с помощью
gammu
мы уже не сможем. Правда, мы, всё же, можем их отправлять самостоятельно, но для этого нам понадобится команда gammu-smsd-inject
, созданная для использования совместно с gammu-smsd
. Теперь отправка сообщений выглядит как постановка их в локальную очередь, а демон будет брать сообщения из этой очереди и отправлять их.Пример команды для отправки сообщения будет выглядеть так:
gammu-smsd-inject TEXT 0664xxxxxxx -unicode -text "hello world from the daemon!"
Заглянем в телефон.

Мы, как и прежде, получили сообщение. В этот раз его доставка может занять на несколько секунд дольше, чем прежде. Такое ощущение, что демон проверяет очередь исходящих сообщений с некоторой периодичностью.
pi:~# gammu-smsd-inject TEXT 0664xxxxxxx -unicode -text "hello world from the daemon!"
gammu-smsd-inject[2964]: Warning: No PIN code in /etc/gammu-smsdrc file
gammu-smsd-inject[2964]: Created outbox message OUTC20211203_193330_00_0664xxxxxxx_sms0.smsbackup
Written message with ID /var/spool/gammu/outbox/OUTC20211203_193330_00_0664xxxxxxx_sms0.smsbackup
▍Получение сообщений
Пришло время отправить SMS нашей системе и выяснить, дойдёт ли оно до получателя. Для этого достаточно ответить на одно из полученных от системы сообщений и посмотреть, появится ли оно в виде файла в папке
/var/spool/gammu/inbox/
.Отправляем сообщение, ждём несколько секунд и заглядываем в папку.
pi:~# ls /var/spool/gammu/inbox/
IN20211203_194458_00_+43664xxxxxxx_00.txt
pi:~# cat /var/spool/gammu/inbox/IN20211203_194458_00_+43664xxxxxxx_00.txt
Hello also from the outside world!
Каждое из полученных сообщений сохраняется в отдельном файле. Можно догадаться, что в имени файла вида
IN20211203_194458_00_+43664xxxxxxx_00.txt
закодированы дата, время, телефон отправителя и номер части сообщения (для SMS, длина которых превышает 140 символов, так как они разбиваются на части).Шаг 4: API для работы с сообщениями
Нам нужен простой механизм для отправки и получения сообщений с использованием API, такой, для применения которого не нужно устанавливать множество плагинов и пакетов. Я написал пару весьма компактных PHP-скриптов, которые заточены именно под эту задачу. Найти их можно здесь.
Для того чтобы они функционировали, нужен работающий демон
gammu-smsd
.В директории, где лежат соответствующие .php-файлы (
send.php
и get.php
) нужно выполнить команду php -S 0.0.0.0:8080
. Благодаря этому обратиться к ним из сети сможет любая нуждающаяся в них сущность.▍Отправка SMS с помощью API
Теперь всё делается до крайности просто и понятно. Для отправки сообщения достаточно обратиться по адресу такого вида:
http://ip.of.your.pi/send.php?phone=0664xxxxxxx&text=Testmessage
В ответ придёт JSON-объект, в котором будут сведения о том, удалась ли отправка сообщения (
status:ok
) или нет (status:error
).{
"status": "ok",
"log": "2021-12-04 15:43:39\ngammu-smsd-inject TEXT 0664xxxxxxx -unicode -text 'Testmessage'\ngammu-smsd-inject[2669]: Warning: No PIN code in /etc/gammu-smsdrc file\ngammu-smsd-inject[2669]: Created outbox message OUTC20211204_164340_00_0664xxxxxxx_sms0.smsbackup\nWritten message with ID /var/spool/gammu/outbox/OUTC20211204_164340_00_0664xxxxxxx_sms0.smsbackup\n\n\n"
}
▍Получение SMS с помощью API
Получить сообщение с помощью нашего API тоже очень просто. Достаточно обратиться по адресу такого вида:
http://ip.of.your.pi/get.php
В ответ, в виде JSON-объекта, придут все полученные сообщения:
curl -s http://ip.of.your.pi/get.php | jq .
[
{
"id": "f0a7789a657bb34eddd17c8e64609c48",
"timestamp": 1638636342,
"year": "2021",
"month": "12",
"day": "04",
"time": "16:45",
"test": "04.12.2021 16:45:42",
"sender": "+43664xxxxxxx",
"message": "Hello bob!"
},
{
"id": "c358d0a4ca868c1d7d2eedab181eddd6",
"timestamp": 1638636414,
"year": "2021",
"month": "12",
"day": "04",
"time": "16:46",
"test": "04.12.2021 16:46:54",
"sender": "+43664xxxxxxx",
"message": "Hello "
}
]
Теперь можно интегрировать систему отправки и получения SMS-сообщений в любые проекты. Если вы это сделаете — сообщите мне. Если захотите — я могу об этом рассказать в своём блоге.
Вопросы и ответы
▍Почему тут используются PHP-скрипты собственной разработки, а не gammu-python?
Мне хотелось, чтобы код, обеспечивающий работу API, занимал бы как можно меньше места и потреблял бы как можно меньше ресурсов. Я попробовал несколько Python-реализаций подобного механизма, и ни один из них не выглядел достаточно компактным и простым. Для их использования нужно было компилировать код с множеством подпакетов. Кроме того, моему Raspberry Pi не хватало памяти на компиляцию некоторых из этих пакетов. Да и оказалось, что быстрее написать небольшой PHP-скрипт, чем заниматься оптимизацией Python-пакетов.
▍Что если пользователь отправит длинное SMS-сообщение?
API может обрабатывать сообщения, разбиваемые при отправке на части, даже в том случае, если между частями одного сообщения, находящегося в очереди, находятся другие сообщения. Поэтому данная возможность должна работать без дополнительных настроек, так как API самостоятельно собирает сообщения, разбитые на части.
▍Можно ли работать с MMS?
Gammu поддерживает MMS (Multimedia Messaging Service, служба мультимедийных сообщений). Но такие сообщения хранятся в бинарном формате, и я пока не нашёл документацию, посвящённую тому, как с ними работать. Буду весьма благодарен за любые сведения об этом.
Пригодится ли SMS-шлюз в каком-нибудь из ваших проектов?

Комментарии (27)
Gengenid
05.01.2022 16:12+23g/4g роутеры без дополнительного внешнего железа позволяют отправлять смс через http запросы.
Elsajalee
05.01.2022 21:21А подробнее нельзя? Каким образом к ним по HTTP обратиться?
PavelBelyaev
05.01.2022 23:28+3Я давненько писал статью, почти все модемы с веб мордой и иногда без нее принимают по rest запросы, можно простым curl кидать запросы и не только для смс, а например чтобы включить или выключить мобильный инет...
Gengenid
06.01.2022 00:05Я подробностей не помню. У них у всех есть веб-интерфейс, в том числе и функция отправки смс через него. Я смотрел, там очень просто все. Но это было в 2018, я точный синтаксис запроса не скажу, да и разный он наверное у разных производителей.
Alexsey
05.01.2022 16:57+5Задумка хорошая, реализация уровня "я пока еще не собрал все шишки".
Сборка sms шлюзов на consumer 3g/4g свистках без отдельного watchdog'а, который будет постоянно дергать модем по питанию это бесполезное занятие. В рандомные моменты модем либо перестанет отвечать на запросы, либо перестанет слать SMS. Поможет только дерганье по питанию.
В свое время решил проблему купив промышленный модем местного производства, есть подозрение что делали его на заказ для минобороны и просто сливали лишний инвентарь.
Felan
05.01.2022 19:11А если вместо модема старый телефон использовать. Ну андроид, например. Тоже все проблемы без watchdog'а будут?
Elsajalee
05.01.2022 21:24+1Лучше посмотрите в сторону ttgo-t-call.
Для IoT самое то, отдельный, независимый. Работает в WiFi сети (ESP32), SIM800L, при необходимости можно подключить nrf24l01.
На собственной прошивке проблем с зависаниями\перезагрузками нет, uptime более 5 недель уже необднократно был.
Alexsey
06.01.2022 04:02Честно говоря даже не пытался. Я вообще не слышал чтобы андроид себя умел прокидывать себя в хост систему как AT-совместимый COM порт.
Felan
06.01.2022 18:36В каком смысле умел? Сам по себе конечно не умеет. Надо небольшую программку написать.
Я имелл ввиду проблемы на которые вы намекали. Типа зависаний.
mapnik
06.01.2022 14:43Насколько я понял по виду конфигурационных файлов и опций командной строки, gammu — это бывший gnokii. В такм случае да, он будет работать со старыми телефонами, начиная с 3310 и позднее. И да, нокии, которые нокии, не виснут. Годами.
praeivis
05.01.2022 18:09+3Про SIM800L не слышали?
kiff
05.01.2022 19:19Вот как раз хотел узнать об этом подробнее, есть ли дока, как ее подцепить через gammu?
SnakeSolid
05.01.2022 21:21Если я правильно понимаю, то gammu просто пишет AT команды в
/dev/ttyUSB0
. Гипотетически, если UART от SIM800 пробросить в/dev/ttyS0 и указать его в конфиге gammu
, то оно должно работать.Elsajalee
05.01.2022 23:40+1Оно и практически работает, через CP2102 (и аналогичные). Вот только SIM800L иногда зависает (крайне редко, причём это точно не связано с качеством питания; и не с количеством проходящих СМС); и ничего вы без wd не сделаете в этом случае. А когда он подключен к микроконтроллеру, то при отсутствии AT команд мы его перезагрузить можем пином enable.
ol_x
06.01.2022 15:30Давно делал прям идентичную штуку, там все на AT - https://habr.com/ru/post/261387/
quwy
05.01.2022 21:23+4Использовал пхожее решение на базе Orange PI еще 8 лет назад для интернет-магазина. Кучу кода написал, отладил все, а в итоге как только через шлюз пошла ~сотня сообщений в сутки, мне стали быстро блочить симку. Новая симка работала сутки, потом снова блок. Перешел на другого оператора, так он стал блокировать еще яроснее, через часы.
Попытка решить вопрос с тех-поддержкой успехом не увенчалась: fair use policy и нас не волнует, что у тебя пакет с SMS-безлимитом. Покупай услугу рассылки по конской цене или вали отсюда.
Так что подобная штука годится разве что в качестве семейного или IoT-шлюза, где сообщения шлются на более-менее стабильный список номеров. Для коммерческого применения, где почти каждое сообщение идет на новый номер в свой конец страны -- не проканает.
Elsajalee
05.01.2022 21:27-1Как, с минимальными затратами, самостоятельно сделать такой шлюз?
Минимальными затратами чего? Денег - очевидно, что нет. Времени - возможно.
Yoh
06.01.2022 13:43+2В декабре Мегафон ввел платные имена отправителей, как итог подобное делал для получения уведомлений о каких-либо событиях. Использую белые списки в телефоне, чтобы ночью лишний раз не дергаться на левые звонки / сообщения. Платить за это 2 000 рублей в месяц операторам за несколько сообщений в месяц считаю избыточным.
В качестве модема Huawei E160G, в начале года были некоторые наработки, работал около 3 месяцев круглосуточно без зависаний. Сейчас дописан мониторинг, который проверяет состояние модема (наличие сети, проверка баланса и тому подобное), если модем перестает отвечать, то через /sys/bus/usb/drivers/usb/unbind происхоидт переподключение модема, но пока такое происходило только в рамках тестирования :)
Loggus66
На два месяца бы раньше эту статью. Так уже успел наковырять py-скрипт для трансляции кириллицы в UCS2, найти gammu и реализовать прототип, а потом узнал, что ничего не надо уже.