Sending SMS from 3G / GSM modem

Hi Habr. In this article, I would like to share my experience with a GSM modem, or rather, the experience of sending SMS messages. Below we will describe the implementation of the program in Delphi for sending SMS messages, as well as reading and deleting incoming / outgoing messages from the modem. In my case, it was a HUAWEI modem from MTS. All who are interested, please, under cat.

The birth of an idea


It all started with the fact that in one small chain of stores there was a need to inform the authorities on a daily basis of revenue for each store. Sales information is accumulated in the central office, and it needs to be reported to the head. General Director (the customer) is a very busy person, and as a director he needs to receive information on store revenue in a timely manner, the Internet is sometimes unavailable, and a mobile phone is always at hand, so it was decided to send him an SMS with revenue data for each store on his phone, in a simple and readable form.

SMS example:

08.11.2011
1.Магазин A, 123045 р.
2.Магазин B, 134520 р.
3.Магазин C, 215403 р.
...
;
As it turned out later, this is not all that is required, but more on that later.

At first it was proposed to send SMS via a gate, of which there are a great many now. But the customer immediately rejected this offer for security reasons, because the data on daily revenue is a pretty confidential thing. Then it was decided to send SMS simply from the phone connected to the computer via a USB cable, and as a result, a USB modem was used instead of the phone, which had already been idle for a long time.

Where to begin


Of course, I had to work with the COM port, but I couldn’t communicate with the modem using AT commands. There are quite a lot of AT commands, but everything turned out to be much simpler than I expected, because for our purpose, only 5 commands were required:

AT + CMGF - sets the operation mode: 0-digital or 1-text. This command will be called first, the format of subsequent commands and modem responses depends on this.

AT + CMGS - sending a message, the format of the parameter strongly depends on the mode (i.e. from the last command).

AT + CMGL - reading messages from the modem, you can transfer one of five values ​​as a parameter, it is worth paying attention that depending on the mode (AT + CMGF), you should transfer digital or string values:
image

AT + CMGD - delete one message from the modem, as a parameter we transfer the message number.

AT + CMGR - reading one message from the modem, also transmit the message number.

First results


After several hours (trial / error), I found out that sending an SMS message from a modem is not much harder than doing it from a regular mobile phone. As mentioned above, to send SMS you should use the command "AT + CMGS". And so, he opened hyperterm, connected to the modem (via the COM port), and scribbled the following commands into the port:
AT+CMGF=1 [Enter]
AT+CMGS=+79261234567 [Enter]
hello habr, this is test message [Ctrl+Z]

An example of the same in Delphi:

procedure SendSMS(AComPort: integer; AMsg: String; ANumTel: String); 
var
  hFile: THandle;
  procedure WriteStr(AStr: String); //пишет в порт переданную строку
  var
    LWrited: Cardinal;
  begin
    //Пишем в порт
    WriteFile(hFile, PAnsiChar(AStr)^, Length(AStr), LWrited, nil);
  end;
begin
  //открываем порт
  hFile := Windows.CreateFile(PChar('\\.\COM' + IntToStr(AComPort)), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0); 
  //если открылся
  if (hFile <> INVALID_HANDLE_VALUE) then
  begin
    try
      //устанавливаем текстовый режим
      WriteStr('AT+CMGF=1' + #$D#$A);
      //вводим номер в формате "+79xxxxxxxxx"
      WriteStr('AT+CMGS="'+ANumTel+'"' + #$D#$A);
      //вводим текст сообщения, только латиница
      WriteStr(AMsg + #$D#$A#$1A);
    finally
      //закрываем порт
      Windows.CloseHandle(hFile);
    end;
  end;
end;

Voila, and the pocket-sized device announced the expected event.
But alas, the positive emotions that arose at me at the time of this success did not last long, or more precisely until it was discovered that with the Russian text everything is much more complicated. Firstly, to send messages in Russian, you need to switch the mode from text to digital (AT + CMGF = 0), and secondly, the message itself must be sent in UCS2 encoding. And if at least the first problems, then the second had to tinker.

Text encoding in UCS and vice versa (again on Delphi):

function UCSToAnsi(AStr: AnsiString): AnsiString;
  function Convert(ACnvStr: AnsiString): AnsiChar;
  var
    j: integer;
  begin
    j := StrToIntDef('$'+ACnvStr, 0);
    case j of
      1040..1103: j := j - 848;
      1105: j := 184;
    end;
    Result := Chr(j);
  end;
var
  c, i: integer;
begin
  Result := '';
  c := Length(AStr) div 4;
  for i := 0 to c - 1 do
    Result := Result + Convert(Copy(AStr, i*4+1, 4)); end;
function AnsiToUCS(AStr: AnsiString): AnsiString;
  function Convert(AChar: AnsiChar): AnsiString;
  var
    j: integer;
  begin
    Result := '';
    j := ord(AChar);
    case j of
      192..255: j := j + 848;
      184: j := 1105;
    end;
    Result := IntToHex(j, 4)
  end;
var
  c, i: integer;
begin
  Result := '';
  c := Length(AStr);
  for i := 1 to c do
    Result := Result + Convert(AStr[i]); 
end;

I can’t say that everything turned out immediately and easily, but still it turned out. If earlier I sent to the modem:
AT+CMGF=1 [Enter]
AT+CMGS=+79261234567 [Enter]
hello habr, this is test message [Ctrl+Z]

then in order to send a message in Russian you will need to send:
AT+CMGF=0 [Enter]
AT+CMGS=84 [Enter]
0011000B919762214365F70008C146043F04400438043204350442002004450430043
10440002C0020044D0442043E00200442043504410442043E0432043E043500200441
043E043E043104490435043D04380435 [Ctrl+Z]

First, the mode switch (in digital), then a message is sent (84), and the last line contains: phone number, message text and various settings (such as: SMS center number, whether to save the message to the recipient, etc.).

Delphi example:

procedure SendSMSMessage(AComPort: integer; AMsg: String; ANumTel: String); 
var
  Lng, i:  Integer;
  LRead, LText, LMes, LTel, ANum: String;
  hFile: THandle;
  procedure WriteStr(AStr: String);
  var
    LWrited: Cardinal;
  begin
    //Пишем в порт
    WriteFile(hFile, PAnsiChar(AStr)^, Length(AStr), LWrited, nil);
  end;
begin
  //открываем порт
  hFile := Windows.CreateFile(PChar('\\.\COM' + IntToStr(AComPort)), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0); 
  //если открылся
  if (hFile <> INVALID_HANDLE_VALUE) then
  begin
    ANum := ANumTel;
    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];
    LText := AnsiToUCS(AMsg);
    // Длина и номер SMS центра. 0 - означает, что будет использоваться дефолтный номер.
    LMes := '00'; 
    // SMS-SUBMIT
    LMes := LMes + '11'; 
    // Длина и номер отправителя. 0 - означает что будет использоваться дефолтный номер.
    LMes := LMes + '00'; 
    // Длина номера получателя
    LMes := LMes + IntToHex(Length(ANumTel), 2); 
    // Тип-адреса. (91 указывает международный формат телефонного номера, 81 - местный формат).
    LMes := LMes + '91'; 
    // Телефонный номер получателя в международном формате.
    LMes := LMes + LTel; 
    // Идентификатор протокола
    LMes := LMes + '00'; 
    // Старший полубайт означает сохранять SMS у получателя или нет (Flash SMS),  Младший полубайт - кодировка(0-латиница 8-кирилица).
    LMes := LMes + '08'; 
    // Срок доставки сообщения. С1 - неделя
    LMes := LMes + 'C1'; 
    // Длина текста сообщения.
    LMes := LMes + IntToHex(Trunc(Length(LText)/2),2); 
    LMes := LMes + LText; 
    Lng := Round((Length(LMes)-2)/2);
    WriteStr('AT+CMGF=0' + #$D#$A);
    WriteStr('AT+CMGS=' + StrToInt(Lng) + #$D#$A);
    WriteStr(LMes + #$D#$A#$1A);
    Windows.CloseHandle(hFile);
  end;
end;

Idea development


To say that I was happy when the long-awaited Russian text came to the mobile, instead of “crook,” means to say nothing. The next day I finished the main part of the program, and everything seemed to be. Messages with revenue data are sent to the customer’s phone; life seems to have been a success, but it wasn’t there. About a week later, the customer asked to finalize the application, namely, to make it so that after he received an SMS with the text: he could send a store number in response to this message and receive a new message, but with more detailed information on the specified store.
08.11.2011
1.Магазин A, 123045 р.
2.Магазин B, 134520 р.
3.Магазин C, 215403 р.
...


Well, in general, the logic is simple: the program should store the last sent "report" for all stores and read incoming messages as soon as a message with conditional text appears (for example, "store = 12"), pull out the store number from there, look at the last message sent what was under this number and send detailed information about this store (unfortunately at the moment the customer has not decided on the format and content of the "detailed report", so there is nothing to give as an example). To implement the above logic, the modem is required to: read SMS from memory, delete SMS from memory (so as not to accumulate). For reading messages I used the AT + CMGR and AT + CMGL commands (their brief description was given earlier). Reading all posts will look like:
AT+CMGF=1 [Enter]
AT+CMGL="ALL" [Enter]
+CMGL: 6,"REC READ","778467",,"11/09/03,18:49:40+16"
007700770077002E006D00740073002E00720075
+CMGL: 7,"REC READ","+79261234567",,"11/10/18,18:38:00+16"
04220435044104420020043F044004380435043C043000200073006D0073002004410
43E043E043104490435043D04380439002100200421043C04410020043D043D043D04
3004340430003F0021003F00210028002D005F002D00290020 [Enter]

Everything is simpler than before. Each message consists of 2 lines, the first contains data about the message (such as: from whom, when, message number), and the second contains the text of the message (again in UCS encoding, the UCSToAnsi function was given above). Reading a single message is done as:
AT+CMGF=1 [Enter]
AT+CMGR=7 [Enter]
+CMGR: "REC READ","+79261234567",,"11/10/18,18:38:00+16"
04220435044104420020043F044004380435043C043000200073006D0073002004410
43E043E043104490435043D04380439002100200421043C04410020043D043D043D04
3004340430003F0021003F00210028002D005F002D00290020 [Enter]

Similarly, message deletion occurs. If for example, in my case, send the command AT + CMGD = 7, then at the next AT + CMGL = “ALL” I will no longer see message number 7, because it will be deleted.

Conclusion


And so, the basic commands for working with SMS messages through a GSM modem were disassembled, sending, reading, deleting messages was considered. Lastly, I would like to note that the scope of this use of SMS messages is quite wide (especially because it is possible to organize two-way communication). For example, this: the user sends an SMS, the modem receives, our program reads, performs some actions based on the message text and sends the result to the user. Or vice versa: some kind of event occurs on the PC, and the program sends a message to the user about this event. Good luck with your experiments, thanks!

Sources of the test program here .

Also popular now: