【环境准备】

1、操作系统:

1
Windows 7 x64 SP1

2、Python 版本:

1
python-3.6.3-amd64

3、第三方组件:

(1)、

1
pip3  install  pyserial

(2)、下载smspdu

1
python3 setup.py build  install


【列出所有串口】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#encoding: utf-8
#author: walker
#date: 2017-10-06 
#summary: Python 用 pyserial 列出所有串口
 
import  serial.tools.list_ports
 
# 打印所有串口名
def  ListPorts():
     serailPortList  =  list (serial.tools.list_ports.comports())
     print ( 'serail port number: %d'  %  len (serailPortList))
     if  len (serailPortList) <  1 :
         print ( "Not found serail port!" )
         return 
         
     for  serailPort  in  serailPortList:
         se  =  serial.Serial( list (serailPort)[ 0 ],  9600 , timeout = 9 )
         print ( 'serail port name:%s'  %  se.portstr)
         se.close()
     
if  __name__  = =  '__main__' :
     ListPorts()


【发送纯英文(ASCII)短信】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#encoding: utf-8
#author: walker
#date: 2017-10-06 
#summary: Python 调用 GSM A6,发送英文短信
 
import  time
import  serial.tools.list_ports
         
class  MessagerEn( object ):
     # 构造函数,打开端口
     def  __init__( self ):        
         self .sePort  =  serial.Serial(port = 'COM3' , baudrate = 19200 , bytesize = 8 , stopbits = 1 , timeout = 3 )
         time.sleep( 3 )
     
     # 析构函数,关闭端口
     def  __del__( self ): 
         self .sePort.close()
     
     # 重新打开端口
     def  reopen( self ):
         print ( 'reopen ...' )
         self .sePort.close()
         self .sePort  =  serial.Serial(port = 'COM3' , baudrate = 19200 , bytesize = 8 , stopbits = 1 , timeout = 3 )
         time.sleep( 3 )
 
     # 执行指令
     # 返回从串口读取的返回值
     def  execCmd( self , cmd, end = '\r' ):
         cmd  =  cmd  +  end
         bCmd  =  cmd.encode( 'ascii' )
         print ( 'write:%s'  %  bCmd)
         self .sePort.write(bCmd)
         response  =  self .sePort.readall()
         print ( 'read :%s'  %  response)
         print ()
         
         return  response
     
     # 发送英文短信
     # 正确返回 True;错误返回 False
     def  __sendMessageEn( self , phoneNumber, message):
         if  not  self .execCmd( 'AT+CMGF=1' ).endswith(b '\r\nOK\r\n' ):      #设置TEXT MODE
             return  False
             
         if  not  self .execCmd( 'AT+CSCS="GSM"' ).endswith(b '\r\nOK\r\n' ):    
             return  False
             
         if  not  self .execCmd( 'AT+CMGS="%s"'  %  phoneNumber).endswith(b '\r\n> ' ):     
             return  False
         
         if  not  self .execCmd(message  +  '\x1A' , end = ' ').endswith(b' \r\nOK\r\n'):      # 十六进制的1A为结束符
             return  False
             
         return  True
     
     # 调用私有方法 __sendMessageEn
     # 在失败时重新打开串口
     def  sendMessage( self , phoneNumber, message):
         for  in  range ( 0 3 ):
             if  self .__sendMessageEn(phoneNumber, message):
                 break
             self .reopen()
     
if  __name__  = =  '__main__' :
     messagerEn  =  MessagerEn()
     messagerEn.sendMessage( '18688754379' 'xxx' )
     messagerEn.sendMessage( '18688754379' 'zzz' )


【发送中文(Unicode)短信】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#encoding: utf-8
#author: walker
#date: 2017-10-06 
#summary: Python 调用 GSM A6,发送中文(Unicode)短信
  
import  time
import  serial.tools.list_ports
          
class  Messager( object ):
     # 构造函数,打开端口
     def  __init__( self ):        
         self .sePort  =  serial.Serial(port = 'COM3' , baudrate = 19200 , bytesize = 8 , stopbits = 1 , timeout = 3 )
         time.sleep( 3 )
      
     # 析构函数,关闭端口
     def  __del__( self ): 
         self .sePort.close()
          
     # 重新打开端口
     def  reopen( self ):
         print ( 'reopen ...' )
         self .sePort.close()
         self .sePort  =  serial.Serial(port = 'COM3' , baudrate = 19200 , bytesize = 8 , stopbits = 1 , timeout = 3 )
         time.sleep( 3 )
  
     # 执行指令
     # 返回从串口读取的返回值
     def  execCmd( self , cmd, end = '\r' ):
         cmd  =  cmd  +  end
         bCmd  =  cmd.encode( 'ascii' )
         print ( 'write:%s'  %  bCmd)
         self .sePort.write(bCmd)
         response  =  self .sePort.readall()
         print ( 'read :%s'  %  response)
         print ()
          
         return  response
      
     # 发送英文短信
     # 正确返回 True;错误返回 False
     def  __sendMessage( self , phoneNumber, message):
         if  not  self .execCmd( 'AT+CMGF=0' ).endswith(b '\r\nOK\r\n' ):      #设置PDU格式
             return  False
              
         if  not  self .execCmd( 'AT+CSCS="GSM"' ).endswith(b '\r\nOK\r\n' ):    
             return  False
          
         pdu_content  =  self .encodeMessage(phoneNumber, message)
         cmd  =  'AT+CMGS=%d'  %  (( len (pdu_content) - 2 ) / 2 )
         if  not  self .execCmd(cmd).endswith(b '\r\n> ' ):    
             return  False
          
         # 十六进制的1A为结束符
         if  not  self .execCmd(pdu_content  +  '\x1A' , end = ' ').endswith(b' \r\nOK\r\n'):    
             return  False
              
         return  True
      
     # 调用私有方法 __sendMessage
     # 在失败时重新打开串口
     def  sendMessage( self , phoneNumber, message):
         for  in  range ( 0 3 ):
             if  self .__sendMessage(phoneNumber, message):
                 break
             self .reopen()
      
     # 将中文信息编码为PDU格式(ucs2)
     def  encodeMessage( self , phoneNumber, message):
         tpdu  =  []
         if  phoneNumber  and  message:             
             tpdu.append( '001100' )
             
             formatAddress  =  self .encodePhoneNumber(phoneNumber)
             tpdu.append(formatAddress)
  
             tpdu.append( '0008AA' )
              
             tpdu.append( '%02X'  %  ( len (message) * 2 ))
             content  =  ' '.join([' % 02X ' % c for c in message.encode(' utf_16_be')])
             tpdu.append(content)
          
         print (''.join(tpdu))
         return  ''.join(tpdu)
     
     # 将手机号编码为pdu所需格式
     def  encodePhoneNumber( self , phoneNumber):
         #号码为11位数字
         if  not  re.match(r '\d{11}' , phoneNumber):
             print ( 'Error phoneNumber: %s'  %  phoneNumber)
         
         addr  =  '0D'   #号码长度13
         addr  + =  '91'       #Type-of-Address,目标地址格式(TON/NPI) 
         #加86、F补成偶数个(14位),分7组翻转
         phoneNumber  =  '86'  +  phoneNumber  +  'F'
         for  in  range ( 0 7 ):       
             addr  + =  phoneNumber[ 2 * i + 1 +  phoneNumber[ 2 * i]
             
         return  addr
      
if  __name__  = =  '__main__' :
     messager  =  Messager()
     messager.sendMessage( '18688754379' '你好' )
     messager.sendMessage( '18688754379' '大家好' )


相关阅读:

1、PDU编码(非常经典)

2、在线PDU格式编码/解码

3、使用python实现短信PDU编码


*** walker ***