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

Решил я тут как-то немного изменить свою программку для отправки SMS-сообщений, а именно – добавить возможность отправлять длинные SMS (больше 160 латинских или 70 русских символов). Поковырял иностранные источники, почесал репу, разобрался, сделал, работает. Теперь, в продолжение предыдущих своих статей на тему SMS и PDU, опишу как это делается, может кому пригодится.

Технически, отсылка длинных SMS почти ничем не отличается от отсылки коротких. Вся процедура сводится к разбиению длинного сообщения на короткие и добавлению дополнительного информационного блока в поле TP-UD (TP-User-Data) и отправка этих коротких кусочков. После того, как телефон получил все сообщения, по информации из дополнительного информационного блока он собирает отдельные SMS в одну длинную.

Для тех, кто забыл, что такое PDU и с чем его едят отсылаю к теории: Отправка SMS-сообщений в формате PDU, теория с примерами на C#, часть 1

Итак, приступим к формированию правильной PDU-строки для отправки длинного сообщения.

1. PDU-Type и TP-MR

PDU-Type – Поле флагов. Для отправки простого сообщения туда нужно было поставить “01”. В нашем случае, для отправки длинного сообщения в этом поле должно быть “41”. Т.е. если смотреть по битам, должен быть установлен бит TP-UDHI, который дает понять телефону, что сообщение содержит блок UDH (User Data Header), тот самый дополнительный информационный блок, в котором будет содержаться информация о частях сообщения.

TP-MR (TP-Message-Reference) — поле, следующее за PDU-Type. В случае ошибки доставки сообщение об ошибке будет содержать этот номер, чтобы мы знали, в каком из отосланных нами сообщений произошла ошибка. Наверное для того, чтобы переотправить, если что. Буржуйские источники пишут, что поле TP-MR должно быть разным у каждого короткого сообщения, на которые делится одно длинное. Лично я пробовал ставить везде одинаковое значение – все кусочки сообщения отправляет, длинное на телефон приходит. Однако если отправить следом еще одно сообщение модем отвечает какой-то ошибкой. В результате я тупо увеличиваю это поле на единичку для каждого следующего сообщения. В этом случае все хорошо.

2. Теория поля UDH

Новое поле UDH, которое содержит информацию о частях сообщения, должно находиться сразу после поля TP-UDL (TP-User-Data-Length – длина сообщения). Т.е. поле UDH по сути является частью поля TP-UD (TP-User-Data), т.е. частью текста сообщения, для которого отводится 140 байт.

UDH–поле предназначено не только для отправки длинных сообщений, однако далее я буду рассматривать его именно применительно к нашей задаче. Итак, из чего же состоит UDH? Смотрим на таблички и вникаем в пояснения:

Общий формат UDH для текста, закодированного 7-битной кодировкой (символы ASCII, т.е. только латинские символы):

Поле

Размер

Описание

UDHL 1 байт Длина блока UDH в байтах
UDH n байт Непосредственно UDH-данные
filler 0-6 бит Наполнитель, который выравнивает text по границе септета
text m септетов непосредственно 7-битные символы сообщения

Пояснение: Этот формат UDH, как уже было написано выше, применяется для сообщений, написанных в ASCII кодировке, т.е. латинскими символами. В этом случае сообщение кодируется в 7-битную кодировку. Принцип этой кодировки я уже объяснял в предыдущей статье, повторяться не буду. Скажу лишь, что в 7-битной упаковке (за счет смещения битов) каждый 8 байт является началом отдельного блока, с которого можно начинать нормально декодировать символы. Символы при кодировании как бы “размазываются” по соседним байтам, начало в одном, конец в другом. Вот поэтому и нужен наполнитель (filler) – чтобы после блока UDH выровнять границу начала текста по границе начала септета.

А зачем это нужно? А для совместимости. Если вы будете отправлять длинную SMS на телефон, который с ними работать не умеет, то он сразу начнет декодировать UDH как текст сообщения! Естественно, он из UDH получит бессвязную белиберду, однако если не выровнять символы последующего текста по границе байта – то он вообще весь текст как белиберду декодирует. А правильно выровненный после UDH текст он декодирует нормально, и получится “[белиберда вместо UDH] текст сообщения”.

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

Общий формат UDH для текста, закодированного UCS-2 кодировкой (символы юникода, т.е. на любом языке):

Поле

Размер

Описание

UDHL 1 байт Длина блока UDH в байтах
UDH n байт Непосредственно UDH-данные
text m байт непосредственно символы в кодировке UCS-2

Пояснение: Здесь все гораздо проще – поскольку 7-битного сжатия нет, не нужно извращаться с выравниванием текста по границам байта. Главное не забывать, что в UCS-2 один символ кодируется двумя байтами.

Теперь рассмотрим, из чего состоит блок UDH из вышеуказанных табличек. А состоит он из идентификатора, длины и одного или нескольких информационных элементов (IE):

Поле

Размер

Описание

IEI 1 байт Идентификатор информационного элемента
IEDL 1 байт Длина данных информационного элемента
IED n байт Данные информационного элемента

Вот картинка, дающая наглядное представление:

UDH

Как я уже говорил, UDH–поле предназначено не только для отправки длинных сообщений. Однако нам оно нужно только для этого, посему упрощаем задачу и рассмотрим информационные элементы именно с точки зрения длинных сообщений. Итак, для отправки длинных сообщений поле UDH будет содержать всего один информационный элемент и выглядеть так:

Поле

Значение

Описание

IEI 0х00 или 0х08 Для отправки длинных сообщений всегда равен либо 0х00, либо 0х08. Если 0х00 – то IED1 занимает 1 байт, если 0х08 – то IED1 занимает 2 байта.
IEDL 0х03 или 0х04 Длина данных информационного элемента – 3 или 4 байта, в зависимости от длины поля IED1
IED1 1 или 2 байта Номер-ссылка. Должен быть одинаков для всех частей сообщения
IED2 1 байт Количество частей в сообщении
IED3 1 байт Порядковый номер части сообщения

Проясним вышесказанное. Итак, у нас есть длинное сообщение. Допустим, разбиваем его на 3 коротких. Тогда IED2 будет равно “03”. Первое сообщение будет иметь IED3 равное “01”, второе “02” и третье, соответственно, “03”.

Теперь о IED1: этот номер-ссылка (reference number) нужен для того, чтобы телефон при приеме наших трех коротких сообщений знал, что они принадлежат к одному и тому же большому. Для всех частей одного большого сообщения IED1 должен быть одинаковым! Например, вы посылаете на телефон длинное сообщение разбитое на 3 коротких, а в это же время кто-то еще посылает на этот же телефон длинное сообщение. Короткие сообщения приходят в телефон вперемешку, и для того, чтобы телефон в них не запутался как раз и применяется поле IED1. Оно будет разное у двух длинных сообщений, но одинаковое в пределах частей каждого из них. Таким образом телефон не перепутает кусочки. IED1 может быть абсолютно любым. Я применяю для его формирования датчик случайных чисел.

Еще один момент, связанный с полем IED1 – это его размерность. Она может быть либо 1 либо 2 байта. А зависит это от поля IEI: если IEI = “00”, то размерность IED1 1 байт, т.е. число от 0 до 255. Если IEI = “08”, то размерность IED1 2 байта, т.е. от 0 до 65535. Этот момент нам позже пригодится, из него получится отличная военная хитрость.

Ну и, соответственно, IEDL это длина данных, либо 3 байта (если IEI = “00”) либо 4 байта, (если IEI = “08”).

Таким образом, можно подсчитать длину IE-блока: она равна 5 байт, если reference number кодируется одним байтом, или 6 байт, если reference number кодируется двумя байтами.

Отсюда получается длина UDH-блока: что перед IE-блоком идет байт UDHL, указывающий его длину. Таким образом, полная длина блока UDH составит 6 или 7 байт соответственно.

3. Практика

Переходим к водным процедурам. А именно: будем отправлять длинное сообщение.

3.1 Для начала формируем весь заголовок PDU как обычно, только PDU-Type ставим не “01” а “41”. Не забываем и про TP-MR – просто увеличиваем его на единичку для каждого следующего сообщения.

3.2 Разбиваем длинное сообщение на короткие. Здесь надо определить количество символов, которые можно отправить коротким сообщением. Вариантов тут всего два: либо наше сообщение в ASCII-кодировке, либо в USC-2. На текст сообщения отводится всего 140 байт. Соответственно, в ASCII кодировке мы можем отправить всего 160 символов (за счет 7-битной кодировки), а в USC-2 всего 70.

Однако, следует учесть, что блок UDH располагается именно вначале тех 140 байт, которые отведены для текста сообщения, поэтому, на текст остается уже не 160 и 70 символов а меньше. А сколько именно – будем считать:

Сначала вспомним, что полная длина UDH-блока может быть 6 или 7 байт. Далее:

Для ASCII-сообщения: 7-битное кодирование позволяет упаковать 8 символов в 7 байт, отсюда и получается упомянутая мной выше военная хитрость: мы возьмем UDH-блок равный 7 байтам. А следом пойдет кодированный текст. Это дает нам большой выигрыш: не нужно выравнивать текст по границе септета. И это же означает, что мы теряем 8 байт текста. Значит считаем: 160 – 8 = 152. Итого 152 символа на сообщение.

Для кодировки USC-2: Здесь у нас текст не сжатый, а наоборот: каждый символ текста кодируется двумя байтами. Если мы возьмем длину UDH равную 7 байтам, то получим 140-7=133. А 133 на 2 нацело не делится. Значит, берем длину UDH равную 6 байтам. Тогда все в шоколаде: 140-6=134, 134/2=67. Итого 67 символов на сообщение.

3.3 Теперь мы знаем необходимое количество сообщений. Формируем блок UDH для каждого сообщения в отдельности, в зависимости от типа текста и от количества сообщений:

Для ASCII-сообщения: “06” (длина поля UDH) + “08” (IEI) + ”04” (IEDL) + “XXXX” (reference number, случайное двухбайтовое шестнадцатеричное число) + “ХХ” (IED2, Количество частей в сообщении) + “ХХ” (IED3, Порядковый номер сообщения)

Для кодировки USC-2: “05” (длина поля UDH) + “00” (IEI) + ”03” (IEDL) + “XX” (reference number, случайное однобайтовое шестнадцатеричное число) + “ХХ” (IED2, Количество частей в сообщении) + “ХХ” (IED3, Порядковый номер сообщения)

3.4 Все. Компонуем все вместе, отправляем все сообщения и наслаждаемся жизнью. Если все сделано правильно – сообщение склеится как положено.

Пример 1. Сообщение в кодировке USC-2

Будем отправлять сообщение “Ночь. Улица. Фонарь. Аптека. Я покупаю вазелин. За мной стоят два человека: армян и сумрачный грузин. Вот скрипнула в подъезд пружина и повторилось все как встарь: пустая банка вазелина, аптека, улица, фонарь.” на номер 0(000)000-00-00

Сообщение длинное, 209 символов, что дает при разбивке 4 коротких SMS(209/67). Смотрим внимательно:

SMS №1:

0041000B910000000000F000088C050003FF0401041D043E0447044C002E00200423043B0438
04460430002E00200424043E043D04300440044C002E00200410043F04420435043A0430
002E0020042F0020043F043E043A0443043F0430044E00200432043004370435043B0438
043D002E0020041704300020043C043D043E0439002004410442043E044F044200200434
0432043000200447

41 — PDU-Type, 00 – TP-MR, 8C — TP-UDL

050003FF0401 – UDH: 05 – общая длина UDH, 0003 – идентификатор и длина IE, FF – случайный reference number, 04 – всего 4 сообщения, 01 – это первое сообщение

SMS №2:

0041010B910000000000F000088C050003FF04020435043B043E04320435043A0430003A0020
04300440043C044F043D00200438002004410443043C044004300447043D044B043900200433
0440044304370438043D002E00200412043E044200200441043A04400438043F043D0443043B
0430002004320020043F043E0434044A0435043704340020043F0440044304360438043D04300020

41 — PDU-Type, 01 – TP-MR, 8C — TP-UDL

050003FF0402 – UDH: 05 – общая длина UDH, 0003 – идентификатор и длина IE, FF – случайный reference number, 04 – всего 4 сообщения, 02 – это второе сообщение

SMS №3:

0041020B910000000000F000088C050003FF040304380020043F043E04320442043E04400438043B
043E0441044C00200432044104350020043A0430043A002004320441044204300440044C003A
0020043F0443044104420430044F002004310430043D043A043000200432043004370435043B
0438043D0430002C00200430043F04420435043A0430002C00200443043B043804460430002C

41 — PDU-Type, 02 – TP-MR, 8C — TP-UDL

050003FF0403 – UDH: 05 – общая длина UDH, 0003 – идентификатор и длина IE, FF – случайный reference number, 04 – всего 4 сообщения, 03 – это третье сообщение

SMS №4:

0041030B910000000000F0000816050003FF040400200444043E043D04300440044C002E

41 — PDU-Type, 03 – TP-MR, 16 — TP-UDL

050003FF0404 – UDH: 05 – общая длина UDH, 0003 – идентификатор и длина поля данных, FF – случайный reference number, 04 – всего 4 сообщения, 04 – это четвертое сообщение

Итак, что мы тут видим?

PDU-Type везде 41, т.к. это составное сообщение.

TP-MR просто увеличивается на единичку, ну это я так сделал, главное, чтобы они не совпадали.

TP-UDL – Длину я выделил просто так, на всякий случай.

UDH начинается для всех одинаково: 05 — общая длина UDH, 00 – идентификатор IE, 03 – длина IE-данных, FF – reference number, случайное число, одинаковое для всех коротких сообщений, составляющих одно длинное (у меня случайно получилось FF), 04 – всего 4 сообщения и дальше – для каждого сообщения свой порядковый номер. Все просто, не так ли?

Пример 2: Сообщение в кодировке ASCII

Отправляем то же сообщение, но в транслите: “Noch’. Ulica. Fonar’. Apteka. Ja pokupaju vazelin. Za mnoj stojat dva cheloveka: armjan i sumrachnyj gruzin. Vot skripnula v pod#ezd pruzhina i povtorilos’ vse kak vstar’: pustaja banka vazelina, apteka, ulica, fonar’.

218 символов, разбиваем на 2 сообщения:

SMS №1:

0041000B910000000000F00000A0060804BD010201CEF7187D7281AAECF438EC0219DFEEB0FCE4020
5E1F4F23AEC0229C320F87B5D8787D575903DAC2FB3D36E17481B06B5DD6F35684E7FABC37410
D91E068DD165F6DB5E5E8775A0B0BCAD0EBB4169D0BCDE9687C768775E0D3ACBEBFAB4DB05B
2BEE9A0F95A9E86BBEBEC30C80E82BFC9A3B29E0C82CBEB7A74DA1D06A541F0B79DFE96A7D9

41 — PDU-Type, 00 – TP-MR, A0 — TP-UDL

060804BD010201 – UDH: 06 – общая длина UDH, 0804 – идентификатор и длина IE, BD01 – случайный reference number, 02 – всего 2 сообщения, 01 – это первое сообщение

SMS №2:

0041010B910000000000F000004A060804BD010202EFF909649F9741EBF01A649FD3C3F2930E04AF
CFE9617518240EBBD761903DAC2FB3D3EE300B1486D3CBEB300B5467A7C76116C8FC7687E52717

41 — PDU-Type, 01 – TP-MR, 4A — TP-UDL

060804BD010202 – UDH: 06 – общая длина UDH, 0804 – идентификатор и длина IE, BD01 – случайный reference number, 02 – всего 2 сообщения, 02 – это второе сообщение

Итак, что мы тут видим? Да все то же самое:

PDU-Type везде 41, т.к. это составное сообщение.

TP-MR просто увеличивается на единичку

TP-UDL – Длину я выделил просто так, на всякий случай.

UDH начинается для всех одинаково: 06 — общая длина UDH, 08 – идентификатор IE, 04 – длина IE-данных, BD01 – reference number, двухбайтовое случайное число, одинаковое для всех коротких сообщений, составляющих одно длинное, 02 – всего 2 сообщения и дальше – для каждого сообщения свой порядковый номер.

Заключение

Вот собственно и все, что я хотел сказать, да и пиво кончилось :). На самом деле особо сложного тут нет ничего. Надеюсь кому-нибудь пригодится этот материал. Если будут вопросы – задавайте, отвечу.

Исходный код

Для тех, кто по каким-либо причинам не может разобраться с данной темой, я выложил исходные коды программы SMSSender. Там реализована отправка длинных сообщений, и код, формирующий PDU-текст достаточно понятно комментирован.

Ссылки

Если интересно, приведу несколько ссылок на источники, которыми я пользовался:

http://mobiletidings.com/2009/02/18/combining-sms-messages/
http://mobiletidings.com/2009/02/11/more-on-the-sms-pdu/
Send Long SMS/Multipart SMS/Concatenated SMS
http://isms.ru/article.shtml?art_12

Благодарности

Special Thanx моему читателю ТимЫчу, который несколько месяцев назад пытался сподвигнуть меня на вникание в данный вопрос и даже высылал мне ссылки на некоторые статьи. К сожалению время мне тогда не позволяло заняться этим вопросом, а его ссылки я потерял в груде ежедневной корреспонденции. Но мысль во мне он зародил правильную, и, как только появилось время, я эту мысль реализовал.

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

Комментарии

  1. Прочитал статью, очень порадовала. Как раз недавно возникла проблема по отправке смс. Надо было свою программку написать. Написал все, потом пошла проблема при отправке больших смс — оказывается такой геморрой). Здесь прочитал все понятно, но все равно что-то до конца не хочет работать. Если не трудно, немогли ли поделиться исходниками, чтоб получше в этом разобраться.

  2. Пожалуйста, поделитесь исходниками! Не хочется использовать непонятные стороние компоненты, но и времени разбираться со всем абсолютно нет. Буду очень очень благодарен!!! )
    hash111@yandex.ru

  3. Star, а я чет не понял где «00» перед «41» — номер же смс центра не указан у тебя?, плюс неплохо было бы написать на какой номер ты его отправляешь — указать это в тексте прямо — просто когда отлаживаешь программку, лучше чтоб все совпадало один в один.

    1. Да, начальных нулей не написал, сорри, исправил.
      Номер свой я, естественно, затер нулями, так что отправляем на номер 0-000-000-00-00
      Спасибо за поправку

  4. Путевый ты парень, Star)). я вот тут как раз пишу обмен по SMS в PDU режиме, как раз все процедурки сделал, завтра собираюсь со звонилкой работать, если что найду отпишусь.
    Главное — весело все написал достаточно, читать приятно. А то стандарты читать такая скучная муть, а после твоих статей и стандарты вроде веселей идут.

  5. Приятно что меня не забыли) Я эту тему уже давно описал) Не знаю точно ли…

    http://www.cyberforum.ru/csharp-beginners/thread269949.html

    Вот тема на форуме, где я все выяснял… (прошу не расценивать как рекламу) StarXXX, Надо было мне выслать раньше мануалы… Глядишь, не пришлось бы изобретать велосипед)

  6. StarXXX, спасибо!
    Пара вопросов:
    1. Правильно я понимаю, что команда AT+CMGS= должна транслироваться в модем перед каждой короткой SMS, а не один раз в начале? Заканчивается она чем: ascii-кодом 10 (перевод строки)?
    2. Чем заканчивается передача каждой короткой SMS? Ascii-кодом ctrl+z? Или это только для последней sms, а для промежуточных используется все тот же ascii-код 10?

    1. Все короткие СМС отправляются так же, как и простые одинарные, по абсолютно таким же правилам. Применительно к Вашим вопросам:

      1) AT+CMGS= должна транслироваться в модем перед каждой короткой SMS. А конкретно что именно заканчивается в вашем первом вопросе? Команда AT+CMGS ? Или вся смс?

      2) ctrl+z

      еще раз повторюсь, отправка частей длинных СМС — это отправка нескольких обычных СМС!

    1. TP-VP — поле опциональное. Его наличие зависит от флагов. И флагами я говорю СЦ, что я поле TP-VP не использую. Вот туда оно и делось 🙂

  7. У вас ОПЕЧАТКА.
    В приведённых частях смс в третьей и четвёртой. Вместо «01 – это третье сообщение» должно быть «03 – это третье сообщение» и «04 – это третье сообщение» соответственно.

  8. Пишу программу для отправки длинных смс, столкнулся с такой проблемой: после отправки 1ой части сообщения модем выдает ошибку на команду «AT+CMGS=». 1ая часть приходит нормально. Подскажите, если есть мысли по этому поводу.

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

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

    msg = «Привет! Ты получил сообщение бла?»;
    encodeMsg = «0010»+recipient+»0008″ + msg.Length.ToString() + StringToUCS2(msg);
    countBite = encodedBody.Length/2;
    encodeMsg = «00»+encodeMsg;

    В результате я получаю смс такого сообщения «Привет! Ты получил сообщен»

  10. Вот я разобрался) теперь осталось разобраться как отпавлять длинные сообщения

  11. Меня интересует такой вопрос: я использую библиотеку SMSLib под Java. У меня получается несколько иной заголовок:

    0051000C918390470652320008FF8C0B0504000000000003DD0201
    0051000C918390470652320008FF3C0B0504000000000003DD0202

    Это 2 части одного сообщения (взято сначала PDU до конца заголовка)
    Что примечательно, на мой телефон это сообщение приходит, а на другой телефон — нет.
    Можете что-нибудь посоветовать?
    В лог записалась транскрипция

    SMSC Address: [Length: 0 octets]
    First Octet: 51 [TP-MTI: (SMS-SUBMIT), TP-MMS: (has no messages), TP-RD: (allow duplicates), TP-VPF: (validity format, integer, TP-SRI: (No Status Report), TP-SRR: (No Status Report), TP-UDHI: (has UDH)]
    Message Reference: 00
    Destination Address: [Length: 12 (0C), Type: 81 (10000001), Address: 380974602523]
    TP-PID: 00 (00000000)
    TP-DCS: 08 (UCS2 encoding) (00001000)
    TP-VPF: 10584 hours
    User Data Length: 140 (8C) octets
    User Data Header (pdu) : 0B0504000000000003DD0201
    User Data Header Length: 11 (0B) octets

    UDH Information Elements:
    ConcatInformationElement[00, 03, DD0201][MpRefNo: 221, MpMaxNo: 2, MpSeqNo: 1]
    PortInformationElement[05, 04, 00000000][Dst Port: 0, Src Port: 0]
    Спасибо!!!

  12. В Вашем примере отправки СМС из 4-х кусков — какой длины получаются три первых СМС? Я имею в виду, что если вводить всё с терминала, то какую длинну указывать в команде AT+CMGS= ?

    1. Посмотрите первую часть статьи. Раздел «Отправка» пункт 2: … «AT+CMGS=<длина сообщения>». Эта команда требует пояснения: <длина сообщения> в данном случае подразумевает не длину всего сообщения, а длину блока TPDU! То есть длину без учета номера SMS-центра! Длина считается в байтах.

      Там и пример есть.

  13. Мучаюсь уже который день. Не понимаю. У меня модем хуавей E171. Ну не могу я отправить в UCS2 более 50 символов (я имею ввиду самого текста). Если больше, то выдает просто ERROR без какого-либо кода. Родная программа Коннект менеджер отправляет без проблем. Вот в чем может быть проблема? Может после открытия порта нужно дать какую-то команду на инициализацию или еще что-то? (((((
    Спасибо

    1. была похожая проблема. не отправлялся текст длинной более 53 символов. решение оказалось простое. Буфер для записи at команды в шестнадцатиричном виде был размером FF, т.е. сообщение большей длинны туда не влазило. Изменил на FFF — стало нормально работать.

  14. Уважаемый, StarXXX, есть небольшая просьба, потребует от Вас минимум усилий, необходима небольшая доработка Вашей программы… готов обсудить стоимость, напишите мне пожалуйста. Мой e-mail: post@lucidmind.ru.

  15. Скачал исходные коды — программа просто замечательная!
    У Вас уже даже реализована отправка из командной строки!
    Именно то, что я искал. Она определенно стоит 135р 🙂

    Спасибо!

    Александр.

  16. Искренне надеюсь, что ветка жива 🙁
    В общем такое дело — прогу написал, работает, в основном отправляются сообщения корректно (длинные, на русском языке, в PDU формате), на на некоторые телефоны приходят либо крокозябры, либо пустые сообщения. Кто-нибудь с подобным сталкивался? 🙁

  17. Приветствую, StarXXX!

    Спасибо за программу и за ликбез по теории, мне очень помогло, думаю многим людям тоже! 🙂

    Вопросик имеется: посылаем список СМС (коротких) из программы, запуская SMSSender как внешний процесс для каждой СМС. Примерно в половине случаев SMSSender завершается неупешно на команде AT+CMGS=. При этом, поскольку мы пытаемся в программе перепослать непосланное сообщение, со 2-3, иногда 4 итерации все команды отрабатывают и СМС успешно посылается. При неуспешной попытке в лог никакой диагностики от модема не обнаруживаем (лог ниже в письме скопировал)

    Посмотрел код SMSSender, AT команды выполняются последовательно и синхронно, то есть даем команду, ждем не более 15 секунд (как я понял), обрабатываем результат, даем следующую команду. Судя по тому, что в логе от модема у нас пусто — отваливаемся по таймауту. Не понятно почему. Когда пробуем послать эту же СМУ этими же AT командами из терминала — всегда отправляется успешно.

    Может быть приходилось сталкиваться с такой проблемой? В чем может быть загвоздка? Заранее спасибо!!!

    /Константин

    ******************* Log *********************

    29.10.2013 14:19:39: Command: ConnectToModem
    Error: False
    Modem Reply: Connected OK on port com1
    ———-

    29.10.2013 14:19:39: Command: AT+CMGF=0
    Error: False
    Modem Reply: AT+CMGF=0

    OK

    ———-

    29.10.2013 14:19:54: Command: AT+CMGS=63
    Error: True
    Modem Reply:
    ———-

    29.10.2013 14:19:54: Произошла ошибка при приеме ответа на команду AT+CMGS=63:

    1. Рад что мой ликбез Вам помог. Однако, вряд ли смогу подсказать что-то существенное. Во-первых данным вопросом, как и программой, уже дано не занимаюсь — была задача, сделал, переключился на другую. Сейчас все забылось уже.
      Вообще, сотовые модемы очень капризные штуки. Производители не придерживаются какого-то одного стандарта, то, что нормально работает на одном модеме, криво работает на другом или не работает вообще на третьем.
      Можно было бы поковыряться отладчиком, однако на моих модемах все работало, ковыряться отладчиком надо с Вашим модемом.
      Так что увы, здесь я помочь не смогу.

  18. Спасибо за статьи, очень помогли мне.
    Только вот есть одна проблема, отсылаю склеенные сообшения в USC, сообщения приходят, только у них обрезаются три последних символа в каждой части. Смотрю дебагом, формируются все куски правильно, в модем все уходит как надо.А смс приходит с обрезанными тремя символами в конце каждого куска.
    Может есть мысли из-за чего это, куда копать дальше?

    1. Мучился с такими же симптомами. Переносил код в PHP, при отправке длинных СМС стали пропадать по 3 последних символа из каждого сообщения. Выяснилось, что не правильно считал длину TP-UDL. Почему-то подумал, что туда входит только длина строки с текстом, как оказалось при подсчёте учитывается ещё и длина UDH. На примере 4 СМС в формате PDU:
      TP-UDL(16) = Длина ( UDH(050003FF0404)+TEXT(00200444043E043D04300440044C002E))

  19. Добрый день.

    Спасибо большое, отличная статья. Очень интересная и познавательная.

    Возник один вопрос. Никак не могу понять (может туплю?! :))
    «Пример 2: Сообщение в кодировке ASCII».
    Почему TP-UDL = A0?

    1. Про это было в предыдущей статье, красным
      http://hardisoft.ru/soft/otpravka-sms-soobshhenij-v-formate-pdu-teoriya-s-primerami-na-c-chast-1/
      Длина сообщения (блока TP-UD) в кодировке ASCII указывается не в байтах а в символах. 0хА0 = 160 = 152 (длина текста в символах) + 8 (длина блока UDH в байтах).
      Почему UDH имеет длину 8? 1 байт на длину (UDHL), 6 байт на сам UDH. Откуда берется восьмой, не знаю. Длина сообщения (TP-UDL) вроде в блок TP-UD не входит.

  20. Доброе время сутки!

    Ссылка «исходные коды программы SMSSender» ведет на пустую страницу?!

  21. Ребята, у кого реально работает отправка длинных СМС на кирилице? Поделитесь (ayaklarang{бобик}mail.ru) можно рабочим тестовым примером на Delphi 7/XE3! У меня отправляется только короткие, длинные не идут.

  22. Выражаю благодарность автору статьи за подробное объяснение отправки СМС. Все получилось.

  23. StarXXX,отправка получилась на 5 из 6 телефонов приходит нормально, а на 1 не приходит со всем. Скажите в чем может быть причина? В какую сторону копать? Может задержку поставить между отправками? Буду рад помощи.

  24. Привет авторам. Пару дней пытался собственные сила поднять webapi для отправки сообщений, с одной смс-кой разобрался быстро, все замечательно. А вот длинными сообщениями зашел в тупик, смс-ки вроде как уходят, но адресат не получается, всяко разно пытался с только информации было прочитано, не получилось. Сегодня купил Ваши исходники, думал у Вас там все работает… но при отправке длинных сообщений вылетает ошибка. Можете подсказать в чем проблема? сроки по проекту поджимает. Спасибо.

  25. Ловлю Error 304 при отправке длинной смс.
    function TGSMComander.SendSMSMessage(ASMS: TSMSMessage): Boolean;
    var
    PDUTYPE: AnsiString;
    Lng, i, j, k: Integer;
    LRead:AnsiString;
    LMes, LTel, ANum: AnsiString;
    LText2: AnsiString;
    UDH,UDH_BLOCK: AnsiString;

    procedure Send(LText:AnsiString); // формирование пакета и отправка в модем
    begin
    SetModemMode(mdInt);
    LText := AnsiToUCS(LText);//форматируем текст сообщения в UCS2
    LMes := ’00’; //SCA Длина и номер SMS центра. 0 — означает, что будет использоваться дефолтный номер.
    LMes := LMes + PDUTYPE; //PDU-TYPE
    LMes := LMes + IntToHEX(TP_MR,2); //TP-MR Длина и номер отправителя. 0 — означает что будет использоваться дефолтный номер.
    LMes := LMes + ‘0B’;//TP-DA LMes := LMes + IntToHex(Length(ASms.Number), 2); // Длина номера получателя
    LMes := LMes + ’91’; // Тип-адреса. (91 указывает международный формат телефонного номера, 81 — местный формат).
    LMes := LMes + LTel; // Телефонный номер получателя в международном формате.
    LMes := LMes + ’00’; // TP-PID Идентификатор протокола
    LMes := LMes + ’08’; // TP-DCS Старший полубайт означает сохранять SMS у получателя или нет (FLASH sms), Младший полубайт — кодировка(0 — латиница 8 — кирилица).
    LMes := LMes + ‘C1’; // TP-VP Срок доставки сообщения. С1 — неделя
    LMes := LMes + IntToHex(Trunc(Length(LText)/2),2); // TP-UDL Длина текста сообщения.
    LMes := LMes + UDH_BLOCK;
    LMes := LMes + LText; // TP-UD Эти октеты представляют сообщение «hellohello», преобразованное в 7 битку.
    Lng := Round((Length(LMes)-2)/2);
    Form6.Memo1.Lines.Add(Format(CMD_CMGS, [Lng]));
    Form6.Memo1.Lines.Add(LMes);
    WriteStr(Format(CMD_CMGS, [Lng]));
    WriteStr(LMes + END_CMD);
    end;

    begin
    ANum := ASms.Number;
    if (Length(ANum) mod 2) = 1 then ANum := ANum + ‘F’;
    for i := 1 to Length(ANum) do if i mod 2 = 0 then LTel := LTel + ANum[i] + ANum[i-1];
    Open; // открытие порта
    if Length(ASms.Text)<56
    then
    begin // отправка цельной смс (уложились в размер)
    PDUTYPE:='11';
    UDH_BLOCK:='';
    Send(ASms.Text);
    Result := ReadToOK(LRead);
    If TP_MR0 then inc(i);
    UDH := UDH + IntToHEX(i,2);
    i:=1;
    while LText2» do
    begin
    UDH_BLOCK := UDH + IntToHEX(i,2);
    Form6.Memo1.Lines.Add(‘UDH: ‘+UDH_BLOCK);
    //Form2.Memo1.Lines.Add(‘Отправка куска №’+inttostr(i));
    inc(i);
    Send(Copy(LText2,1,50));
    Delete(LText2,1,50);
    end;
    Result := ReadToOK(LRead); // возврат удачной отправки
    If TP_MR<255 then Inc(TP_MR) else TP_MR := 0;
    end;
    Close; // закрытие порта
    end;

    Текст сообщения:
    АБВГД…ЭЮЯабвгд…эюя (66 символов)

    СМС №1

    AT+CMGS=120
    0041000B919799681820F50008C16405000300020104100411041204130414041500A80416041704180419041A041B041C041D041E041F0420042104220423042404250426042704280429042A042B042C042D042E042F04300431043204330434043504510436043704380439043A043B043C043D043E043F

    СМС №2
    AT+CMGS=52
    0041000B919799681820F50008C1200500030002020440044104420443044404450446044704480449044A044B044C044D044E044F

    Если воспользоваться онлайн-декодером PDU, то я вижу такую инфу:
    Text message
    To: +7999xxxxxxx
    Message: ноп
    Additional information
    PDU type: SMS-SUBMIT
    Reference: 0
    Data header: 6405000300020104100411041204130414041500A80416041704180419041A041B041C041D041E041F0420042104220423042404250426042704280429042A042B042C042D042E042F04300431043204330434043504510436043704380439043A043B043C
    Val. format: None
    Data coding: Unicode

    Original Encoded PDU fields
    SMSC: 00
    PDU header: 41
    TP-MTI: 01
    TP-RD: 00
    TP-VPF: 00
    TP-SRR: 00
    TP-UDHI:40
    TP-RP: 00
    TP-MR: 00
    TP-DA: 0B919799681820F5
    TP-PID: 00
    TP-DCS: 08
    TP-UDL: C1
    TP-UD:
    6405000300020104100411041204130414041500A80416041704180419041A041B041C041D041E041F0420042104220423042404250426042704280429042A042B042C042D042E042F04300431043204330434043504510436043704380439043A043B043C043D043E043F

    т.е. из всего текста вижу только символы с 48-го по 50-й. Подскажите пожалуйста, где в PDU я накосячил?

  26. Спасибо, единственная приличная статья где все грамотно разложено.

    Выложу результат своей возни с получением времени. Если нужно — можете выложить на сайте как отдельную тему
    *** Часы реального времени ***
    Операции с текущим временем. Можно получить информацию о времени с сервера оператора
    при регистрации модема в сети, получить время с NTP сервера по каналу GPRS,
    или установить текущие показания часов реального времени с помощью команды
    AT+CCLK=»yy/MM/dd,hh:mm:ss+zz».
    Чтобы поддерживать работу часов реального времени, нужно поддерживать питание RTC-домена,
    когда с модуля будет снято основное питание. Для этого можно обеспечить подачу питания
    на вход VRTC от конденсатора достаточной емкости либо от батареи/аккумулятора.

    Обновление времени RTC при регистрации в сети можно включить командой AT+CLTS=1.
    Далее нужно перезагрузить модуль либо перерегистрироваться в сети,
    подав последовательность команд AT+CFUN=0 и AT+CFUN=1.
    При регистрации в сети появится новое URC-сообщение вида
    *PSUTTZ:,,,,,,»»,.
    Затем значение текущей даты и времени можно запросить у модуля командой AT+CCLK (см.далее).

    Чтобы получить время по протоколу NTP, можно воспользоваться командами:
    1. Запустить GPRS //Модем — SIM800L, Оператор Megafon
    AT+SAPBR=3,1,»CONTYPE»,»GPRS»; //Ждем «OK»
    AT+SAPBR=3,1,»APN»,»internet»;// задаем точку доступа. Ждем «OK»
    AT+SAPBR=3,1,»USER»,»gdata»;//указываем имя пользователя. Ждем «OK»
    AT+SAPBR=3,1,»PWD»,»gdata»;//указываем пароль.Ждем «OK»
    AT+SAPBR=1,1 //поднимаем GPRS-соединение (Activate bearer context. Ждем «OK»
    2. Задать параметры сервера NTP
    AT+CNTP=»200.20.186.76″,12;//Адрес NTP сервера и временная зона 12 — делится на 4==3(наша)
    Ждем «OK»
    3. Запросить время у сервера
    AT+CNTP — собственно запрос к серверу. Ждем +CNTP: result
    result:1-Success,61-NetError,62-DnsError,63-ConnectError,64-timeout,65 ServerError,66-OperationNotAllow
    Если не 1 — повторить пп.3 несколько раз до result==1
    4. Остановить GPRS
    AT+SAPBR=0,1; //Ждем «OK»
    5. Считать время с модема
    AT+CCLK? Ждем +CCLK: «21/02/12,18:58:14+12»

    Считать время с модема можно командой
    AT+CCLK? Ждем +CCLK: «21/02/12,18:58:14+12»

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *