The core coding method of Q PDU has been clear. How do I implement short messages with the AT command?
A In the above, we have discussed the encoding mode of the 7-bit, 8bit, and ucs2, and give the code. Now, focus on the PDU stroner coding and decoding process, and the AT command implementation method for GSM 07.05. These are the core code of the underlying, in order to ensure the portability of the code, we don't have to use the MFC class as much as possible, use ANSI C standard library functions. First, define the following constants and structures:
// User information encoding method
#define GSM_7bit 0
#define GSM_8bit 4
#define GSM_UCS2 8
// Short message parameter structure, encoding / decoding shared
// where the string ends in 0
Typedef struct {
Char SCA [16]; // Short Message Service Center Number (SMSC Address)
CHAR TPA [16]; // Target number or reply number (TP-DA or TP-RA)
Char TP_PID; // User Information Protocol Identifier (TP-PID)
CHAR TP_DCS; // User Information Coding Method (TP-DCS)
Char tp_scts [16]; // Service time stamp string (TP_SCTS), used in reception
CHAR TP_UD [161]; / / Original user information (TP-UD before code or decoding)
CHAR INDEX; // Short message number, used in reading
} SM_PARAM;
Everyone has noticed the number and time in the PDU string, both two-two strings. Using the following two functions can be positive and reverse transform:
/ / The normal sequence string is converted to two two-reversal strings. If the length is odd, the 'F' is even more
// such as "8613851872468" -> "683158812764f8"
// psrc: Source string pointer
// PDST: Target string pointer
// nsrclength: Source string length
// Return: Target String Length
INT GSMINVERTNUMBERS (Const Char * PSRC, Char * PDST, INT NSRCLENGTH)
{
INT NDSTLENGTH; / / Target String Length
Char ch; // Used to save a character
// Copy string length
Ndstlength = nsrclength;
// Two two times
For (int i = 0; i { CH = * psrc ; // Save the first character appearing first * PDST = * psrc ; // The character appeared after copying * PDST = ch; // Copy the character appeared first } // Source string length is odd? IF (Nsrclength & 1) { * (PDST-2) = 'f'; // 补 'f' NDSTLENGTH ; // Target string length plus 1 } // Output string adds an end conference * PDST = '/ 0'; / / Return to the target string length Return ndstlength; } // Two-two strings are converted to a normal sequential string // 如: "683158812764f8" -> "8613851872468" // psrc: Source string pointer // PDST: Target string pointer // nsrclength: Source string length // Return: Target String Length INT GSMSERIALIZENUMBERS (Const Char * PSRC, Char * PDST, INT NSRCLENGTH) { INT NDSTLENGTH; / / Target String Length Char ch; // Used to save a character // Copy string length Ndstlength = nsrclength; // Two two times For (int i = 0; i { CH = * psrc ; // Save the first character appearing first * PDST = * psrc ; // The character appeared after copying * PDST = ch; // Copy the character appeared first } // The last character is 'f'? IF (* (PDST-1) == 'f') { PDST -; ndstlength -; // Target string length minus 1 } // Output string adds an end conference * PDST = '/ 0'; / / Return to the target string length Return ndstlength; } The following is a full string of PDUs. To simplify programming, some fields use a fixed value. // PDU encoding, used to prepare, send short messages // psrc: Source PDU parameter pointer // PDST: Target PDU string pointer // Return: Target PDU Strings Length INT GSMENCODEPDU (Const Sm_Param * Psrc, Char * PDST) { INT NLENGTH; / / String length INT NDSTLENGTH; / / Target PDU String Length Unsigned char buf [256]; // internal buffer // SMSC address information NLENGTH = Strlen (PSRC-> SCA); // SMSC address string length BUF [0] = (char) (NLENGTH & 1) == 0? Nlength: NLENGTH 1) / 2 1; // SMSC address information length BUF [1] = 0x91; // Fixed: with international format numbers NdstLength = GSMBytes2String (buf, pdst, 2); // Convert 2 bytes to the target PDU string NDSTLENGTH = GSMINVERTNUMBERS (PSRC-> SCA, & PDST [NDSTLENGTH], NLENGTH); // Convert SMSC to Target PDU Strings // TPDU segment basic parameters, target address, etc. NLENGTH = STRLEN (PSRC-> TPA); // TP-DA address string length BUF [0] = 0x11; // Yes Send SMS (TP-MTI = 01), TP-VP with relative format (TP-VPF = 10) BUF [1] = 0; // TP-MR = 0 BUF [2] = (char) NLENGTH; / / Target Address Digital Number (TP-DA Address String Real length) BUF [3] = 0x91; // Fixed: with international format numbers NDSTLENGTH = GSMBYTES2STRING (BUF, & PDSTLENGTH], 4); // Convert 4 bytes to the target PDU string NDSTLENGTH = GSMINVERTNUMBERS (PSRC-> TPA, & PDST [NDSTLENGTH], NLENGTH); // Convert TP-DA to the target PDU string // TPDU segment protocol identification, coding method, user information, etc. NLENGTH = Strlen (PSRC-> TP_UD); // The length of the user information string BUF [0] = PSRC-> TP_PID; // Protocol Identification (TP-PID) BUF [1] = PSRC-> TP_DCS; // User Information Coding Method (TP-DCS) BUF [2] = 0; // Validity (TP-VP) is 5 minutes IF (psrc-> tp_dcs == GSM_7bit) { // 7-bit encoding method BUF [3] = NLENGTH; / / The length of the coding NLENGTH = GSMENCODE7BIT (PSRC-> TP_UD, & BUF [4], NLENGTH 1) 4; // Convert TP-DA to Target PDU Strings } ELSE IF (psrc-> tp_dcs == GSM_UCS2) { // UCS2 encoding method BUF [3] = GSMENCODEUCS2 (PSRC-> TP_UD, & BUF [4], NLENGTH); // Convert TP-DA to Target PDU Strings NLENGTH = BUF [3] 4; // Nlength is equal to this segment data length } Else { // 8-bit encoding method BUF [3] = Gsmencode8bit (PSRC-> TP_UD, & BUF [4], NLENGTH); // Convert TP-DA to the target PDU string NLENGTH = BUF [3] 4; // Nlength is equal to this segment data length } NDSTLENGTH = GSMBYTES2STRING (BUF, & PDSTLENGTH], NLENGTH); // Convert this section data to the target PDU string / / Return to the target string length Return ndstlength; } // PDU decoding for receiving, reading short messages // psrc: Source PDU string pointer // PDST: Target PDU parameter pointer // Return: User Information String Length INT gsmdecodepdu (const char * psrc, sm_param * pdst) { INT NDSTLENGTH; / / Target PDU String Length Unsigned char TMP; // Interim byte variable Unsigned char buf [256]; // internal buffer // SMSC address information GSMString2bytes (PSRC, & TMP, 2); // Take the length TMP = (TMP - 1) * 2; // SMSC number string length PSRC = 4; // Remove the pointer GSMSerializenumBers (PSRC, PDST-> SCA, TMP); // Convert SMSC number to target PDU string PSRC = TMP; / / Movement // TPDU segment basic parameters, reply address, etc. GSMSTRING2BYTES (PSRC, & TMP, 2); // Take basic parameters PSRC = 2; // Remove the pointer IF (TMP & 0x80) { / / Contain the reply address, retrieve the address information GSMSTRING2BYTES (PSRC, & TMP, 2); // Take the length IF (TMP & 1) TMP = 1; // Adjust the parity PSRC = 4; // Remove the pointer GSMSERIALIZENUMBERS (PSRC, PDST-> TPA, TMP); // Take the TP-RA number PSRC = TMP; / / Movement } // TPDU segment protocol identification, encoding method, user information, etc. GSMString2bytes (psrc, (unsigned char *) & pdst-> tp_pid, 2); // Take the protocol identifier (TP-PID) PSRC = 2; // Remove the pointer GSMString2bytes (PSRC, (unsigned char *) & pdst-> tp_dcs, 2); // Take the encoding method (TP-DCS) PSRC = 2; // Remove the pointer GSMSerializenumBers (PSRC, PDST-> TP_SCTS, 14); // Service Timestamp String (TP_SCTS) PSRC = 14; // Remove the pointer GSMSTRING2BYTES (PSRC, & TMP, 2); // User Information Length (TP-UDL) PSRC = 2; // Remove the pointer IF (PDST-> TP_DCS == GSM_7bit) { // 7-bit decoding NDSTLENGTH = GSMSTRING2BYTES (PSRC, BUF, TMP & 7? (int) TMP * 7/4 2: (int) TMP * 7/4); // Format Conversion GSMDecode7bit (buf, pdst-> tp_ud, ndstlength); // Convert to TP-DU NDSTLENGTH = TMP; } Else IF (pdst-> tp_dcs == GSM_UCS2) { // UCS2 decoding ndstlength = GSMString2bytes (PSRC, BUF, TMP * 2); // Format Conversion NDSTLENGTH = GSMDECodeucs2 (BUF, PDST-> TP_UD, NDSTLENGTH); // Convert to TP-DU } Else { // 8-bit decoding ndstlength = GSMString2bytes (PSRC, BUF, TMP * 2); // Format Conversion NDSTLENGTH = GSMDECode8bit (buf, pdst-> tp_ud, ndstlength); // Convert to TP-DU } / / Return to the target string length Return ndstlength; } According to GSM 07.05, send a short message to use the AT CMGS command, read the short message with the at cmgr command, list short messages with the at cmgl command, delete the short message with the AT CMGD command. However, the AT cmgl command can read all short messages, so we use it to read the short message function without using AT CMGR. Here is the implementation code for sending, reading, and deleting short messages: // Send a short message // psrc: Source PDU parameter pointer Bool GsmsendMessage (const sm_param * psrc) { Int npduLength; // PDU string length Unsigned char nsmsclength; // SMSC string length INT NLENGTH; / / The data length received by the serial port Char cmd [16]; // Command string CHAR PDU [512]; // PDU string CHAR ANS [128]; // Answering npduLength = Gsmencodepdu (PSRC, PDU); // According to PDU parameters, encoded PDU strings STRCAT (PDU, "/ x01a"); // Ended Ctrl-Z GSMString2bytes (PDU, & nsmsclength, 2); // Take the SMSC information length in the PDU string NSMSCLENGTH ; // plus the length byte itself // The length in the command does not include the length of the SMSC information, with a data byte count Sprintf (CMD, "AT CMGS =% D / R", NPDulength / 2 - nsmsClength; // Generate Commands WriteComm (CMD, Strlen (CMD)); // Pre-output command string NLENGTH = Readcomm (ANS, 128); // Read answering data / / Determine if "/ R / N>" can be found IF (NLENGTH == 4 && Strncmp (ANS, "/ R / N>", 4) == 0) { WriteComm (PDU, Strlen (PDU)); // Get affirmative answers, continue to output a PDU string NLENGTH = Readcomm (ANS, 128); // Read answering data / / Determine the success of " CMS ERROR" according to Can I find it? IF (NLENGTH> 0 && Strncmp (ANS, " CMS Error", 10)! = 0) { Return True; } } Return False; } // Read short messages // Use cmgl instead of cmgr, you can read all short messages in one time // PMSG: Short message buffer must be large enough // Return: Short message INT GSMReadMessage (SM_PARAM * PMSG) { INT NLENGTH; / / The data length received by the serial port INT NMSG; // short message count value CHAR * PTR; / / internal data pointer Char cmd [16]; // Command string CHAR ANS [1024]; // Answering NMSG = 0; PTR = ANS; Sprintf (CMD, "AT CMGL / R"); // Generate Commands WriteComm (CMD, Strlen (CMD)); // Output Command Strings NLENGTH = Readcomm (ANS, 1024); // Read answer data / / Determine the success of " CMS ERROR" according to Can I find it? IF (NLENGTH> 0 && Strncmp (ANS, " CMS Error", 10)! = 0) { // Recycled each short message to " cmgl:" While (PTR = strstr (PTR, " CMGL:"))! = null) { PTR = 6; // Skip " CMGL:" SSCANF (PTR, "% D", & PMSG-> index); // Read the serial number Trace ("INDEX =% D / N", PMSG-> index); PTR = strstr (PTR, "/ r / n"); // Look for a row PTR = 2; // Skip "/ R / N" GSmDecodePdu (PTR, PMSG); // PDU string PMSG ; // Prepare to read a short message NMSG ; // short message count plus 1 } } Return NMSG; } // Delete short messages // Index: Short message number, starting from 1 Bool GsmdeeeteMessage (Const Int Index) { INT NLENGTH; / / The data length received by the serial port Char cmd [16]; // Command string CHAR ANS [128]; // Answering Sprintf (CMD, "AT CMGD =% D / R", INDEX); // Generate Commands / / Output command string WriteComm (CMD, Strlen (CMD)); // Read answer data NLENGTH = Readcomm (ANS, 128); / / Determine the success of " CMS ERROR" according to Can I find it? IF (NLENGTH> 0 && Strncmp (ANS, " CMS Error", 10)! = 0) { Return True; } Return False; } The WriteComm and READCOMM functions are used above the AT command, which is used to read and write the serial port, depending on the specific operating system. In the Windows environment, in addition to the MSCOMM control, and some ready-made serial communication classes, some Windows APIs can be simply invoked. The following is the main code implemented using the API, pay attention to the synchronization (blocking) mode of the timeout control. // Serial port equipment handle Handle HCOMM; // Open the serial port // pport: serial port name or device path, available "COM1" or "//./com1", recommended the latter // nbaudrate: baud rate // nparity: parity // NBYTESIZE: Data byte width // nstopbits: Stop bit Bool OpenComm (Const Char * Pport, Int Nbaudrate, Int NPARITY, INT NBYTESIZE, INT NSTOPBITS) { DCB DCB; // Serial Pull CommTIMEOUTS TIMEOUTS = {// Serial port timeout control parameters 100, // Read characters interval timeout: 100 ms 1, // read the time of each character: 1 ms (n characters for N ms) 500, // Basic (extra) Read time: 500 ms 1, // Write the time of each character: 1 ms (n characters for N ms) 100}; // Basic (additional) Writing timeout: 100 ms hcomm = cretefile (pport, // serial port name or device path Generic_read | generic_write, // Read and write 0, // Sharing method: exclusive null, // default security descriptor Open_existing, // creation method 0, // Do not set file properties NULL); // Do not need to refer to the template file IF (hcomm == invalid_handle_value) return false; // Open serial port failed Getcommstate (hcomm, & dcb); // Take DCB Dcb.baudrate = NBAUDRATE DCB.BYTESIZE = NBYTESIZE; DCB.PARITY = NPARITY; DCB.Stopbits = NStopbits; Setcommstate (HCOMM, & DCB); // Set DCB SetupComm (hcomm, 4096, 1024); // Setting the input and output buffer size Setcommtimeouts (hcomm, & timeouts); // Set timeout Return True; } // Turn off the serial port Bool closecomm () { Return CloseHandle (HCOMM); } // write serial port // PDATA: Data buffer pointer to be written // Nlength: The data length to be written Void WriteComm (void * pdata, int NLENGTH) { DWORD DWNUMWRITE; / / The data length issued by the serial port Writefile (HCOMM, PDATA, (DWORD) NLENGTH, & DWNUMWRITE, NULL } // Read the serial port // PDATA: The data buffer pointer to be read // Nlength: The maximum data length to be read // Return: Data length actually read Int Readcomm (void * pdata, int NLENGTH) { DWORD DWNUMREAD; / / The data received by the serial port Readfile (HCOMM, PDATA, (DWORD) NLENGTH, & DWNUMREAD, NULL Return (int) DWNumRead; } Q What questions need to pay attention to when using the AT command to communicate with a mobile phone? A No AT command is sent to the phone, it may return success or fail. For example, when sending a short message with the AT CMGS command, if it is just that the phone is ringing or talking, it will return a " cms error". Therefore, after sending the command, the response of the phone should be detected, and then reissue after failure. Moreover, since only one communication port, transmission and reception cannot be performed simultaneously. If the serial pass-through credit timeout control synchronization (blocking) mode, the general practice is to package the send / receive processing in a work subline. Because there are more code, it will not be detailed here. In the attached DEMO, the complete sub-thread and the source code of the application interface are included. Q Is the AT command, is it supported by all manufacturers? A ETSI GSM 07.05 specification until 1998 to form the final Release version (Ver 7.0.1), before and later, it does not rule out the possibility of different manufacturers in DTE-DCE's short message AT commands. The AT commands in several PDU patterns we use are basic commands. From the principle, each manufacturer's mobile phone and the GSM module should be supported, but there may be fine differences. Q User Information (TP-UD) In addition to short messages in the general sense, it can also be pictures and sound data. What are the norms about the phone ringtones and pictures formats? A is a unified ringtone, picture format, Motorola and Ericsson, Siemens, Alcatel, and the like, and announced in February 2002. These manufacturers have the same format. But another mobile phone giant Nokia did not participate in standard development, ringtone, picture formats different from them. So there is no unified specification. EMS did not exceed GSM 07.05, just the TP-UD data section contains a certain format. The phone ringtones, picture format of each manufacturer can check the relevant website. Q User Information (TP-UD) can actually be any custom data, is it? A Yes, although there will be garbled on the phone. In this case, there is no meaning of the encoding method. But pay attention to still have to comply with the specification. For example, if the 7-bit encoding method is specified, the TP-UDL should be equal to 8/7 of the actual data length (in the one, not a round). In applications with SMS, point-to-point or multi-point to a point of data communication, various custom data, such as GPS information, environmental monitoring information, encrypted personal information, and so on. If you want to send a normal short message while transmitting custom data, the easiest way is to add an identification flag in front of the data, such as "ffff" to distinguish custom data and normal short messages. [Related resources] ◆ This article Demo Source: SMSTEST.ZIP (31 KB) ◆ ETSI official website: http://www.etsi.org ◆ Aidide Download Center: http://www.ascend-tech.com. CN / DOWNLOAD.HTML ◆ BHW98 column: http://www.9cbs.net/develop/author/netauthor/bhw98/ First release: 2003-03-23 Last revision: 2003-03-26