Introduction
Hi, we're back again to generate the PDU code while we have known how to decode PDU code. But at first, sorry for the late publishing of my PDU encoder as I was busy for a long time. These classes are written for my own program, but they can be easily implemented in your project. And this tiny demo program can be used to get the correct PDU code from your setting so as to convince your AT command test work. I'll show how to do this later in this article.
Background
It was last year that I did some work on SMS and EMS PDU code encoding and decoding. And I wrote my PDU Decoder to help me get the PDU strings returned from the mobile. That article has been read by more than twenty thousands people all over the world. Some one pointed out the bugs hidden in my decoder and they helped me to improve my decoder.
But my encoder, although I have done it in February this year, I have it shown to the world in the hot summer. I'm sorry again for this.
Like the decoder, this encoder also was created according to GSM 03.40. But I use 3GPP TS 23.040 V6.5.0 for reference. The link to these documents can be found at the end of this article.
OK. Here we start, I'll introduce my encoder step by step and hope it will give you a hand when designing an encoder.
The architecture of this encoder
The most important parts, public class SMS
and ConcatenatedShortMessage
, are under the namespace SMS
and Encoder
. Figure 1 shows this.
The SMS
class is foundation of SMS and EMS. It has the basic elements required for SMS. Figure 2 shows you the SMS
class. I'll explain how this class works in detail later.
Please note that I have set a group of protected
variables prefixed with TP. These variables' names can be easily found in 3GPP or GSM documents. And there're also a set of properties, e.g. ServiceCenterNumber
, TP_Status_Report_Request
. When you set these properties, they prepare the correct PDU string and store them in protected
variables for future use.
This class implements one function GetSMSPDUCode()
to generate PDU code. This function simply joins the protected
variables in a certain order to get a PDU code. Another function FirstOctet()
contains the first octet data in PDU code. We can use it in some particular occasions.
We know that EMS is composed of several SMSs. Each SMS has TP_UserDataHeader
set which is different from simple SMS. We can easily inherent from SMS to create the base of EMS. And please note the Concatenated Short Message is only part of EMS, which allows you to send messages containing more than 160 ASCII characters or 70 Unicode characters. You can inherent the SMS
class and build another portion of EMS.
From figure 2 you can see an Integer
variable named TotalMessages
. It tells you how many SMS PDU Codes are generated. And note the GetEMSPDUCode()
, it returns a number of SMS PDU codes. I'll explain this later.
How it really works?
In order to demonstrate and explain the details about how this encoder works, let us open my demo program in Visual Studio.
First, please find function GetPDU()
in the Form and make a checkpoint. We will trace it down.
Then build and start this program. Fill the blanks according to the picture shown at the beginning of this article and then click Get PDU Code button.
The program stops at the checkpoint. Let's trace down the code. It first check the coding scheme and user data length to ensure if EMS will be used. Please note that the SMSObject
is an object
type. It will be blind to a specific type of SMS or EMS. Going down the code, we see it sets ServiceCenterNumber
and other properties.
Please trace into ServiceCenterNumber
property and you can see it prepares PDU code from your setting. The final result is stored in a protected
variable SC_Number
. Similar process happen to all the properties.
Then, according to the type of SMSObject
, we use GetSMSPDUCode()
or GetEMSPDUCode()
to get PDU codes.
Function details
Let's look into the detail of the GetSMSPDUCode
function:
Public Overridable Function GetSMSPDUCode() As String
Dim PDUCode As String
If TP_DCS = ENUM_TP_DCS.DefaultAlphabet Then
If TP_UD.Length > 280 Then
Throw New Exception("User Data is TOO LONG for SMS")
End If
If TP_DCS = ENUM_TP_DCS.UCS2 Then
If TP_UD.Length > 280 Then
Throw New Exception("User Data is TOO LONG for SMS")
End If
PDUCode = SC_Number
PDUCode += FirstOctet()
PDUCode += Format(TP_MR, "X2")
PDUCode += TP_DA
PDUCode += Format(TP_PID, "X2")
PDUCode += Format(TP_DCS, "X2")
PDUCode += Format(TP_VP, "X2")
PDUCode += Format(TP_UDL, "X2")
PDUCode += TP_UD
Return PDUCode
End Function
This function first checks the coding and determines if the length of user data is longer than the maximum size. Then every prepared PDU code is added to the variable PDUCode
. Note the PDU codes are added by a certain order. So it is an easy task to modify the order or add some more PDU codes simply by changing the order or adding the other PDU codes to PDUCode
. This gives my class flexibility and extensibility.
Here are also two core functions: Encode7Bit()
and EncodeUCS2()
. You can treat UCS2 as Unicode and use AscW
function to get Unicode character.
But Encode7Bit
is much more complex. In order to ease this encoding, I use several help functions such as BitsToHex
, CharTo7Bits
and so on. First I convert hex number to binary and join them together, then I split them into 8 bits. Please refer to GSM documents for detailed information about 7 bit encoding.
Let us take a look at GetEMSPDUCode()
:
Public Function GetEMSPDUCode() As String()
Select Case tp_dcs
Case ENUM_TP_DCS.UCS2
TotalMessages = (TP_UD.Length / 4) \ 66 + ((TP_UD.Length / 4 Mod 66) = 0)
Case ENUM_TP_DCS.DefaultAlphabet
TotalMessages = (tp_ud.Length \ 266) - ((tp_ud.Length Mod 266) = 0)
End Select
Dim Result(TotalMessages) As String
Dim tmpTP_UD As String
Dim i As Integer
TP_UDHI = 2 ^ 6
Dim Reference As Integer = Rnd(1) * 65536
For i = 0 To TotalMessages
Select Case tp_dcs
Case ENUM_TP_DCS.UCS2
tmpTP_UD = Mid(TP_UD, i * 66 * 4 + 1, 66 * 4)
Case ENUM_TP_DCS.DefaultAlphabet
tmpTP_UD = Mid(tp_ud, i * 133 * 2 + 1, 133 * 2)
End Select
Result(i) = SC_Number
Result(i) += FirstOctet()
Result(i) += Format(TP_MR, "X2")
Result(i) += TP_DA
Result(i) += Format(TP_PID, "X2")
Result(i) += Format(TP_DCS, "X2")
Result(i) += Format(TP_VP, "X2")
If tp_dcs = ENUM_TP_DCS.UCS2 Then
TP_UDL = tmpTP_UD.Length / 2 + 6 + 1
End If
If tp_dcs = ENUM_TP_DCS.DefaultAlphabet Then
TP_UDL = Fix((tmpTP_UD.Length + 7 * 2) * 4 / 7)
End If
Result(i) += Format(TP_UDL, "X2")
Result(i) += "060804"
Result(i) += Format(Reference, "X4")
Result(i) += Format(TotalMessages + 1, "X2")
Result(i) += Format(i + 1, "X2")
Result(i) += tmpTP_UD
Next
Return Result
As you can see, the above code is similar to GetSMSPDUCode()
. But you can also see TP_UDHI
is set indicating that TP_UDH
appears in TP_UD
. Note TP_UDL
is calculated during the loop process. As I mentioned in my source code, it also has problems treating TP_UDL
while the TP_DCS
is DefaultAlphabet. To get details on how EMS is created, please refer to 3GPP TS 23.040 V6.5.0.
Let us send an SMS!
I'm happy to show you that it's so easy to send an SMS to a certain number. In this demonstration, I'll show you how to get my account information using my PDU Encoder, Microsoft HyperTerminal and my PDU Decoder. My phone is Siemens M55, and note some AT commands may not work on your phone or GSM Modem.
First let's produce an SMS PDU Code. Start my program, and fill the Number, Options and User Data as shown in Figure 3. Then press "Get PDU Code" button to get PDU code and length for AT which will be used later. Press "Copy to Clipboard" to copy PDU Code to clipboard.
Start HyperTerminal program. I give this session a name "Send SMS Demo" and I choose COM3 which is the port my phone is connected to. Then follow these instructions:
<CR> equals to Enter key of your keyboard.
Step 1: Type AT<CR> and ensure the device is ready.
Step 2: Type AT+CPMS="MT","MT","MT"<CR> to set preferred memory storage. Here I set it to Mobile Terminal.
Step 3: Type AT+CMGS=12<CR>. The number 12.
Step 4: Paste PDU code to HyperTerminal and press CTRL+Z to end the input. At this time your phone will send SMS to the number you specified. Here my destination number is 1861 from which I can get my account info.
Step 5: After a while I can see my phone receive a status report and my account info.
Step 6: Type AT+CMGL=1 to list the incoming message. Here it returns:
+CMGL: 1,1,,166
0891683108200805F0066104818116505013612455005050136124550000FFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+CMGL: 2,1,,83
0891683108200805F00404A1811600085050136164350044672C67085DF24F7F75288BDD8D39003A
00350031002E00340037002052694F5998845B586B3E003A00350039002E0033003600204F1860E0
003A00370039002E00350031
Step 7: Use my PDU Decoder to decode above PDU codes. Note there're some Chinese characters.
SMS_STATUS_REPORT
Send time:2005-5-31 16:42:55 Receive time:2005-5-31 16:42:55 Status:Success
SMS_RECEIVED
From:1861 Time:2005-5-31 16:46:53
±¾ÔÂÒÑʹÓû°·Ñ:51.47 Ê£ÓàÔ¤´æ¿î:59.36 ÓÅ»Ý:79.51
You can see that it's an easy task to manually send and receive SMSs though my tools and HyperTerminal. Be glad to use them to ease your test work!
Reference
Note
You can use and modify my code freely. If you find some bugs or improve my code, please contact me. This will help me to fix bugs and also it will help a lot of people all over the world! Thanks for reading my article and thanks again for using my code!
Contact me