Получение и декодирование SMS-сообщений в формате PDU

В предыдущих статьях я рассказывал о формате PDU и отправке SMS-сообщений в этом формате. Тогда передо мной стояла задача именно отправлять SMS. Однако на днях задача стала обратной: принять сообщение, декодировать его и отправить на другой номер. Эдакая переадресация SMS. Программа была написана и успешно работает (подробнее об программе SMS-переадресации здесь: http://hardisoft.ru/news/programma-dlya-pereadresacii-sms-soobshhenij/)

В процессе работы пришлось разбираться с приемом и декодированием, что в результате вылилось в программу для переадресации сообщений и в данную статью. Надеюсь, она поможет разобраться желающим. Итак, приступим.

Теория

Теоретическую часть о формате PDU, а так же ответы на «Главный вопрос жизни, вселенной и всего такого» можно посмотреть в предыдущих статьях цикла:

Отправка SMS-сообщений в формате PDU, теория с примерами на C#, часть 1

Отправка SMS-сообщений в формате PDU, теория с примерами на C#, часть 2

Отправка длинных SMS-сообщений в формате PDU

В этой же статье я не буду останавливаться на общей теории и перейду сразу к приему и декодированию результатов.

Работа с модемом

Прием SMS начинается с модема (в сотовом телефоне тоже есть модем). Модем управляется AT-командами. Рассмотрим кратко устройство модема и возможности получения оттуда SMS.

Режим PDU. Рекомендую перевести модем в PDU-режим перед  всеми операциями командой AT+CMGF=0

Память. У GSM-модемов есть несколько видов памяти, в которых могут храниться SMS-сообщения. Честно говоря, зачем их столько я так и не выяснил, но раз уж есть – будем ковырять.

Память модема делится на три логических секции: первая для просмотра, чтения и удаления сообщений, вторая для сохранения и отправки исходящих сообщений и третья для только что полученных сообщений. В общем, тут все несколько запутано, но вторая и третья секции, в принципе, нас не интересуют. Обычно модем хранит полученные сообщения в первой секции.

Кроме логического деления на секции у модема еще есть несколько видов физической памяти: память SIM-карты, внутренняя память модема и прочее. Каждая физическая память назначается определенной логической секции. Таким образом можно назначить память SIM-карты для чтения и удаления сообщений, а память модема для написания и отправки. В общем, тут логика не совсем прослеживается, если кто-нибудь сможет объяснить такой зверинец более подробно – буду признателен.

Каждая область физической памяти имеет свое имя, для того, чтобы ей можно было оперировать в командах модема. Вот несколько возможных имен областей:

  • SM – Память SIM-карты
  • ME – Память модема/телефона
  • MT – Это общая память SIM-карты и модема, т.е. MT = SM+ME
  • BM – Память для широковещательных сообщений сети
  • SR – Память для отчетов (о доставке и т.п.)

Для чтения сообщений нам понадобится только SM и ME память, либо MT, которая включает в себя и SM и ME.

Учтите, любое чтение идет только из памяти, назначенной для первой секции. Это важно.

Для настройки памяти модема существует специальная AT-команда: AT+CPMS

Формат команды: AT+CPMS=message_storage1[,message_storage2[,message_storage3]]

Как видно из формата, вторую и третью секции памяти указывать не обязательно (они в квадратных скобках). Поэтому мы их указывать и не будем.

Итак. Нам нужно настроить первую секцию памяти (для чтения и удаления) и назначить ей соответствующую физическую область памяти. Делается это так:

AT+CPMS=”SM”, либо  AT+CPMS=”ME”, либо AT+CPMS=”MT”.

Первый вариант указывает на то, что читать будем из SIM карты, второй – из памяти модема, третий – сразу из обоих. Рекомендую воспользоваться именно третьим вариантом.

В ответ на эту команду модем выдаст нечто вроде (ответ модема выделен жирным шрифтом):

AT+CPMS="MT"

+CPMS: 6,100,6,100,0,20

OK

Цифры означают количество сообщений в памяти (например 6) и максимально допустимое количество сообщений (например 100) в каждой секции.

Узнать, какая область назначена для какой секции можно командой AT+CPMS?:

AT+CPMS?

+CPMS: «MT»,6,100,"ME",6,100,"SM",0,20

OK

Самое главное для чтения входящих сообщений – это первая секция, на остальные внимания не обращаем.

Чтение. Теперь, когда область памяти установлена, можно приступить к чтению сообщения.

Для чтения сообщений существует 2 команды: AT+CMGL и AT+CMGR. Разница их в том, что первая команда выдает сразу все сохраненные в памяти сообщения, а вторая – только одно сообщение, индекс которого передается ей в параметрах. Индекс начинается с нуля.

AT+CMGL

Синтаксис: AT+CMGL=<status>, где <status> – статус сообщения в памяти. Возможные статусы:

<status> в текстовом режиме <status> в режиме PDU Пояснение
«REC UNREAD» 0 Полученные непрочитанные сообщения
«REC READ» 1 Полученные прочитанные сообщения
«STO UNSENT» 2 Сохраненные неотправленные сообщения
«STO SENT» 3 Сохраненные отправленные сообщения
«ALL» 4 Все сообщения

AT+CMGR

Синтаксис: AT+CMGR=<index>, где <index> – индекс сохраненного сообщения.

Какой вариант выбрать? Решать вам. Я опишу лишь разницу: AT+CMGL позволят получить сразу все сообщения, причем можно выбрать по статусу — какие именно сообщения нас интересуют. AT+CMGR просто выдает нам указанное сообщение, не взирая на статус. Но нужно знать индекс сообщения.

В общем, в примере ниже, я использовал AT+CMGL=4 чтобы получить все сообщения, AT+CMGL=0 чтобы получить только непрочитанные и AT+CMGR=2 чтобы получить сообщение с индексом 2:

1

Пояснения к примеру:

AT+CMGF=0 – переходим в PDU режим

AT+CPMS=”MT” – Переключаем память SIM и модема на чтение сообщений

AT+CMGL=4 – Читаем все сообщения. Модем возвращает нам все содержимое памяти МТ. Формат такой: каждое сообщение начинается строкой +CMGL с параметрами:

  1. Индекс сообщения
  2. Статус сообщения (1 = «REC READ», прочитано)
  3. <alpha> – не известное мне поле
  4. Длина сообщения

Со следующей строки начинается, собственно, само сообщение в PDU формате. На рисунке терминал, для удобства, вывел его в несколько строк, хотя на самом деле модем передает его одной строкой. Вот это самое сообщение мы и будем декодировать. Следующее сообщение опять начинается строкой +CMGL и т.п. Идем далее.

AT+CMGL=0 – Читаем сообщение со статусом «REC UNREAD», т.е. не прочтенные. Здесь модем вернул просто ОК – это значит что непрочтенных сообщений нет. Если бы были – он бы их вывел в том же формате, что и до этого.

AT+CMGR=2 – Получение одного сообщения с индексом 2. Ответ начинается с +CMGR и параметров:

  1. Статус сообщения (1 = «REC READ», прочитано)
  2. <alpha> – не известное мне поле
  3. Длина сообщения

И со следующей строки опять-таки идет PDU.

Примечание: указанные параметры команд и ответов на команды справедливы только если включен PDU-режим командой AT+CMGF=0. Если модем работает в текстовом режиме, то параметры команд и возвращаемый ими результат отличаются от вышесказанного. Учтите этот нюанс. Мы работаем в режиме PDU, желающих разобраться с текстовым режимом отсылаю к литературе. От себя скажу, что преимуществ у текстового режима нет никаких.

Как видим, сложного ничего нет, приступаем к декодированию текста сообщения.

Ковыряем PDU

Итак, читать из модема сообщения научились, теперь будем их декодировать. Начнем с простого. Возьмем для примера такое сообщение:

07919761989901F0040B919701119905F80000211062320150610CC8329BFD065DDF72363904

Что в переводе на русский язык означает “Hello World!” 🙂

Разберем его по косточкам:

2

TP-SCA (Service Centre address) – Адрес сервис-центра, отправившего сообщение.

Нам оно без надобности, но для проформы разберем: первый байт (07) – длина номера, в данном случае длина равна 7 байт. Следующий байт (91) – Тип номера, в данном случае 91 – международный, Следующие 6 байт – сам номер СЦ, дополненный до четности символом F и переставленными местами парами цифр. Подробнее об этом я писал в статье про отправку SMS, там это детально разжевано.

TP-MTI & Co (Message Type indicator и компания) – важный байт, состоящий из различных флагов, разберем его состав подробнее:

Биты Описание
0
1
Message Type indicator (TP-MTI)
2 More messages to send (TP-MMS)
3 0
4 0
5 Status report indication (TP-SRI)
6 User data header information (TP-UDHI)
7 Reply path (TP-RP)

TP-MTI – Эти два бита определяют тип SMS. Причем трактовка этих битов зависит от “направления” SMS, т.е. от того, отправляется она или принимается. Вот табличка, описывающая доступные типы сообщения

Бит 1 Бит 0 СЦ –> модем Модем –> СЦ
0 0 SMS-DELIVER SMS-DELIVER REPORT
1 0 SMS-STATUS REPORT SMS-COMMAND
0 1 SMS-SUBMIT REPORT SMS-SUBMIT
1 1 RESERVED RESERVED
В нашем случае, используется направление направление СервисЦентр –> модем, т.к. мы принимаем сообщение. В нашем примере байт TP-MTI & Co равен 04, следовательно Бит 1 = Бит 0 = 0, значит тип сообщения – SMS-DELIVER. Так и должно быть.

TP-MMS (More Messages to Send) — Этот бит информирует принимающую сторону, есть ли еще сообщения, ожидающие в сервис центре. Если бит равен нулю — значит в СЦ еще есть сообщения для доставки. Если 1 — значит в СЦ больше сообщений нет. В нашем случае этот бит равен 1, значит в СЦ больше нет сообщений для нашей SIM-карты

TP-SRI (Status Report Indication) — Этот бит показывает, будет ли отправлен отчет о состоянии отправителю. 0 — не будет, 1 — будет. Этот бит относится к биту TP-SRR (Status Report Request) в байте SMS-SUBMIT при отправке. Для чтения сообщения он не пригодится.

TP-UDHI (User Data header Indicator) — Этот бит показывает, что содержит блок TP-UD (User Data). Если бит равен 1 — то блок User Data содержит еще и блок User Data Header, в добавок к сообщению. Если ноль — то блок User Data содержит только сообщение. Для нас это важный бит, поскольку дополнительный блок User Data Header применяется для длинных SMS, в нем содержится информация о количестве частей в SMS и номер текущей части.

TP-RP (Reply Path) — Путь для ответа. Это бит говорит, что от принимающего запрашивается ответ. Если путь ответа запрашивается, отвечающее мобильное устройство может попытаться использовать тот же СЦ, что и отправитель, для более надежной доставки ответа. 1 — запрашивается, 0 — не запрашивается

Возвращаясь к нашему примеру и учитывая вышесказанное, анализ байта TP-MTI & Co (04) говорит нам о том, что это принятое SMS (TP-MTI=0,0 — SMS-DELIVER), что на станции больше нет для нас сообщений (TP-MMS=1) и что это одиночная SMS, а не часть длинной (TP-UDHI=0)

 

TP-OA (THE Originating Address) – Адрес отправителя сообщения. Это поле составное:

  • 1 байт (в нашем случае 0B) – длина номера отправителя
  • 1 байт (в нашем случае 91) – Формат номера
  • n байт – непосредственно сам номер

Длина номера: указывается количество цифр в номере, если это обычный номер телефона или количество полубайт, если это текстовый формат номера в 7-битной кодировке.

Формат номера. Тут тоже не все так просто:

Бит Значение
0
1
2
3
Идентификатор плана нумерации
4
5
6
Тип номера
7 Всегда = 1

Рассмотрим тип номера:

Биты
6 5 4
Значение
0 0 0 Неизвестно. Сотовая сеть не знает, что за формат у номера
0 0 1 Международный формат номера
0 1 0 Внутренний номер страны. Префиксы страны у номера отсутствуют
0 1 1 Служебный номер сети. Сервисный, другими словами. Используется оператором.
1 0 0 Номер подписчика. Используется, когда определенное представление короткого номера сохранено в одном или нескольких СЦ как часть более высокоуровневого приложения
1 0 1 Буквенно-цифровой, закодированный в 7-битной кодировке
1 1 0 Сокращенный номер
1 1 1 Зарезервировано

Нас интересует всего 2 типа номера: 001 и 101, остальное непонятно 🙂 и либо не применяется, либо применяется редко, либо ведет к ожирению 🙂

Далее — идентификатор плана нумерации. Применяется для типов номеров 000, 001, 010 и 101. Нет смысла рассматривать все, так как в сотовых сетях применяется всего 2 плана:

1) для типа номера 101 (буквенно-цифровой) идентификатор плана нумерации всегда 0000

2) для остальных типов идентификатор плана нумерации всегда 0001

Непосредственно сам номер — может быть двух видов, либо набор цифр, например “79101234567”, либо текст, например “InternetSMS”. На вид указывает тип номера из предыдущего байта.

Цифровой номер кодируется по стандартной схеме кодирования номеров, о ней можно прочитать в первой части (Отправка SMS-сообщений в формате PDU, теория с примерами на C#, часть 1).

Текстовый номер – просто строка текста, сжатая в 7-битную кодировку.

Как уже говорилось, длина номера – это либо количество цифр, либо количество полубайт. Т.е. для номера 79101234567 длина будет равна 11 (0B, как в примере), а для номера “InternetSMS” – 20 (14H). Почему 20? Потому, что «InternetSMS» (без пробела) это 11 символов. В 7-битной кодировке они упакуются в 11*0.875 = 9.625 байт. Умножаем на 2 чтобы получить полубайты, получаем 19.25. Округляем в большую сторону, т.е. в 20 полубайт.

Для ясности, разберем еще один пример с текстовым номером: 09D0D432BB2C03. Здесь 09 — длина(9 полубайт), D0 — формат номера. А дальше — сам номер (длиной 5 байт, т.е. 10 полубайт!) По нормальному это «Tele2». 5 символов. При кодировании получается 5*0.875 = 4.375. Умножаем на 2 чтобы получить полубайты, получаем 8.75. Округляем в большую сторону — 9 полубайт. Но полубайтами мы не оперируем, поэтому номер в действительности не 9, а 10 полубайт, т.е. 5 байт. Маразм, но с этим ничего не поделаешь 🙂

Итак, вернемся к нашему примеру: у нас поле TP-OA = “0B 91 9701119905F8”. 0B – в номере 11 цифр или символов, 91 — международный формат номера, цифры то есть. Далее – 9701119905F8 – непосредственно сам номер. Пары переставлены местами, до четности дополнен символом F, все по схеме кодирования номера. Получается, номер отправителя 7-910-119-95-08.

TP-PID (Protocol identifier) – Идентификатор протокола. В случае отправки SMS с телефона/модема на телефон/модем данный байт всегда будет равен 00. Для тех кому интересно, что же это поле означает, могу ответить, что оно описывает высокоуровневые протоколы или сетевые устройства, куда должна быть отправлена, или откуда пришла SMS. Другими словами, Сервисный Центр оператора сотовой связи может поддерживать дополнительные услуги, такие как конвертирование SMS в e-mail, отправка SMS на пейджер, телекс. В этом случае данное поле указывает на что-то типа шлюза, через который пойдет SMS дальше и превратится уже не в SMS а в тыкву… В общем, нам это поле не нужно.

У нас TP-PID как и положено равен 00

TP-DCS (Data coding scheme) – Схема кодирования данных. Описывает, как закодирована наша SMS, т.е. поле TP-UD. Для тех кто не хочет заморачиваться, опишу только то, что нам пригодится: 00 — 7-битная сжатая кодировка, 08 — UCS2, 10 и 18 — то же, только сообщение класса 0, т.е. Flash. Желающим узнать про это поле подробнее, могу персонально объяснить из чего оно состоит.

TP-DCS в нашем примере = 00, что означает простое сообщение в 7-битной кодировке.

TP-SCTS (The service centre time stamp) – Штамп времени сервисного центра. Проще говоря, это время, когда СЦ получил SMS, т.е. время отправки. Данное поле составляет 7 байт, которые означают следующее:

  • Год
  • Месяц
  • День
  • Час
  • Минуту
  • Секунду
  • Временную зону

Причем в каждом байте цифры переставлены местами, как и в номерах СЦ и отправителя. Видимо разработчики пережили в детстве тяжелую психологическую травму …

Применительно к нам данное поле можно декодировать так: 21106232015061 = Год: 12, Месяц: 01, День: 26, Час: 32, Минута: 01, Секунда: 05, Временная зона: 16.

TP-UDL (User Data Length) — длина пользовательских данных, включая User Data header, если он есть. Если SMS закодирована 7-битной кодировкой, то данное поле указывает количество символов в сообщении. Если кодировка UCS2 – то поле указывает количество байт в сообщении.

В нашем примере TP-UDL = 0C, т.е. 12 в десятичном счислении. Поскольку TP-DCS в нашем примере = 00, что означает простое сообщение в 7-битной кодировке, значит сообщение содержит 12 символов. Так и есть, в сообщении “Hello World!” 12 символов. Однако, если посчитать количество байт сообщения (поле TP-UD), то их будет 11 а не 12, сказывается сжатие при 7-битной кодировке. Этот момент важно учитывать.

TP-UD (User Data) – Поле пользовательских данных. Здесь находится сам текст сообщения. В случае, если SMS длинная, т.е. разбита на несколько сообщений, данное поле начинается с заголовка TP-UDH. На наличие этого заголовка указывает бит TP-UDHI поля TP-MTI & Co. По поводу поля TP-UDH я подробно писал здесь: http://hardisoft.ru/soft/otpravka-dlinnyx-sms-soobshhenij-v-formate-pdu/ , пункт “2. Теория поля UDH”, так что здесь повторяться не буду.

У нас TP-UD = “C8329BFD065DDF72363904”, что после декодирования 7-битной кодировки дает нам искомое “Hello World!”

The Конец

Вот, в принципе, вся премудрость. Особо сложного ничего нет, если въехать до конца, то процесс получения SMS достаточно прост. Получение длинных SMS тоже достаточно просто, нужно только дополнительно учитывать поле TP-UDH.

С удовольствием отвечу на вопросы по данной теме, если она кому-то интересна.

Поделиться с друзьями:
  • Добавить ВКонтакте заметку об этой странице
  • Одноклассники
  • Facebook
  • В закладки Google
  • Мой Мир
  • Twitter
  • LiveJournal
  • Яндекс.Закладки
  • LinkedIn
  • Reddit
  • StumbleUpon
  • БобрДобр
  • Memori.ru
  • МоёМесто.ru

Comments

  1. Подскажите, не было у вас такого, что команда «AT+CMGL» с параметром чтения всех СМС работала нестабильно: то возвращает нормально все СМС, то валится с ERROR'ом? Совершенно не могу выявить логику поведения: в разницей в одну минуту может быть и корректное прочтение всех СМС, и ошибка.

          1. Перебирать CMGR'ом индексы от 0 до количества сообщений? Или там может быть непоследовательный винегрет типа 0,5,6,10?

          2. К сожалению, у меня именно винегрет. Удалил СМС номер 0, закрыл программу, снова запустил — пытаюсь считать нулевую СМС — в ответ приходит только ОК, считываю под номером 1 — нормально, возвращается. В связи с этим вопрос: есть возможно узнать индексы существующих СМС, или проверить, есть ли на симке СМС с заданным индексом, потому что все время гонять циклы по максимальной емкости не хочется...

    1. Удаление сообщений: AT+CMGD

      Данная команда используется для удаления одного или нескольких сообщений из наиболее предпочтительной области памяти сообщений («BM» SMS CB ‘RAM память’, «SM» SMSPP память ‘SIM память’ или «SR» SMS Status-Report память)

      Синтаксис команды: AT+CMGD=Index [,DelFalg]

      Задаваемые значения:

      index, если DelFlag=0

      (1-20) Если используется область памяти сообщений «BM»

      Значение целочисленного типа находится в пределах номеров ячеек SIM памяти сообщений, , если используется область памяти «SM» или «SR». Если значение DelFlag отлично от нуля, поле index игнорируется.

      DelFlag

      0 – удалить сообщение в ячейке index.

      1 – удалить все ПРОЧИТАННЫЕ сообщения

      2 – удалить все ПРОЧИТАННЫЕ и ОТПРАВЛЕННЫЕ сообщения

      3 – удалить все ПРОЧИТАННЫЕ, ОТПРАВЛЕННЫЕ и НЕОТПРАВЛЕННЫЕ сообщения

      4 – удалить все сообщения.

      Примечание:

      Если предпочитаемой памятью является «SR», то по мере того как отчеты приобретают статус прочитанных «READ», если DelFlag больше 0, все отчеты статуса SMS удаляются.

    2. Тоесть

      AT+CMGD=2 — удалить сообщение с индексом 2

      AT+CMGD=0,1 — удалить все прочитанные сообщения

      AT+CMGD=0,4 — удалить все сообщения

      Вот так.

      1. чёт по индексу удаляет без проблем, а когда пытаюсь удалить все сообщения или только прочитанные выдает ошибку +CMS ERROR: 500

        в чем может быть проблема?

  2. Доброго времени суток. Подскажите пожалуйста, как настроить общение с модемом? Возможно ли выложить кусок программного кода?

  3. Помогите пожалуйста. А как узнать что из com порта пришли данные ? Использую nrComm

    nrComm1.SendString ('AT+CMGL=4' + #13#10);

    if nrComm1.WaitForBytes (20, 1000) then

    begin

    Memo1.Lines.Add (nrComm1.ReadString);

    end;

    Но не выходит, модем иногда долго думает и потом только отдает данные...Как найти выход?

  4. Здравствуйте. У Вас получилась отличная и актуальная статья.

    Может быть поможете с вопросом по декодированию статус репорта.

    Статус репорт :

    07919761989901F0064E0B919757624605F8317062413410613170624144506100 далее сплошные FFFFFFF.........

    Совершенно не понятно почему между адресом сервис центра и адресом абонента 2 байта 064E. Как понимаю 06 как раз означает статус репорт, но что такое 4Е?

    1. 06 это 110 в двоичной системе.

      Если это входящее сообщение и последние 2 бита = 10 — это значит SMS-STATUS REPORT, все верно.

      следующий байт — код отправленной СМС. Насколько я помню, здесь передается код СМС, для которой пришел статус репорт. Скажем, отправили мы сразу 10 СМС, а потом пришел один статус репорт. К какой СМС он относится? Вот это поле и показывает, к какой. Для этого в исходящей СМС есть поле с кодом. Его же статус репорт и возвращает.

      Проблема только в том, что как формируется поле кода исходящей СМС я так и не разобрался, в документации какая-то хрень написана. Вроде как можно запросить модем сгенерить этот код, но как — х.з.

      В общем, я на этот код просто забиваю.

      Подробнее сказать не могу, ибо давно уже не занимаюсь этим вопросом — почти все забылось.

  5. Брат starxxx ответь Мне на маил VirtuTelepfone@mail.ru есть тема для разговора, хотел обсудить возможность написания для Нас одной програмки, Мы оплатим... кстати форум хорош но нет вкладки котакты это странно, жду на маил Брат

  6. Приходится разбираться с чужой программой.

    В этом деле я новичок, поэтому прошу простить, если вопрос окажется глупым.

    На модем приходят СМС-сообщения. Отосланы они могут быть как в режиме PDU, так и в текстовом.

    Какой режим необходимо установить перед операцией чтения входящих сообщений и как мне отличать сообщения,

    сформированные на передающем конце в том или другом режиме (ведь расшифровываться они должны по-разному)?

    Спасибо.

    1. Входящие сообщения станция передает всегда в PDU, не зависимо от того, как оно было отправлено. Так что тут все просто.

  7. У Вас на картинке приведён терминал. Это стандартный Windows Terminal. В шапке написано PuTTY, скачал эту программу, но там нет диалогового окна. Прошу написать программу (название), которую используете в данной статье

    1. Это не стандартный Windows терминал. Это программа PuTTY. Вы правильно скачали. На счет диалогового окна не совсем ясно. Какое именно окно Вам нужно?

  8. Автору спасибо за статью.

    У меня не получается принять смс.

    Есть модем zte mf667 , перевел его в диагностический режим,поставил драйвера, теперь я вижу его как модем на каком-то порту. Шлю себе смс с телефона , в обычном режиме все ок, в веб интерфейсе вижу смс,

    в диагностическом режиме новые смс не вижу. Причем вижу последние смс некоторые в вебе,поменяв режим на веб, а через порт не в одной из доступных памятей смс я не вижу некоторые смс.

    Такое впечатление,что модем не работает на прием. В чем может быть причина?

    Спасибо.

Добавить комментарий

Ваш e-mail не будет опубликован.