GSM PDU模式发中文短信 网上的资料也有很多,但参考了这么多天才最终搞定,所以还是把自己的一点点体会写出来,做为记录,也方便其它后来者参考。
网上的资料很多都没有讲开发的环境,所以有些地方我还是感到很疑惑。
本人的开发环境:
硬件:GSM模块用的是西门子公司的MC52i, 控制心片为STM32, STM32与MC52i通过串口直接通信
开发环境:IAR ARM版,版本号忘了。
开发语言:C语言,没有用操作系统啥的。
手机卡:手机卡即为我自己手机使用的卡,中国移动动感地带。
一些参考资料:
AT发中文短信:http://bbs.chinaunix.net/thread-1945962-1-1.html
SMS中用Unicode编码发送中文:http://www.pc6.com/infoview/Article_405.html
UTF-8, Unicode, GB2312格式串转换之C语言版:http://www.cnitblog.com/wujian-IT/archive/2007/12/13/37671.html
Unicode 编码介绍:http://www.willar.com/article/article_view.asp?id=474
GSM无线模块短消息PDU 格式说明:
http://www.cnblogs.com/AlphaDu/articles/1233121.html
http://www.dreamfabric.com/sms/
http://read.pudn.com/downloads30/doc/comm/94938/mc35i%E5%8F%8Atc35i%E6%8C%87%E4%BB%A4/qunfa2.pdf
相关参考文档(有官方技术手册):http://download.csdn.net/detail/dlutxie/4488089
由于MC55i与MC52i的命令基本一样,所以我参考的是MC55i的用户手册,在该手册中有详细的设置流程图,里面会讲哪些命令是必须执行,哪些是可选。
先按该手册中的介绍,发送短信模块的初始化如下:
AT+CSCS = “GSM”; //设置终端设备字符集,应该就是设置该GSM模块的字符集吧,默认情况即为GSM字符集。发送的时候:sentCom("AT+CSCS =\ “GSM\”");注意这里用了解引用。
AT + CSCA = +8613800411500//设置信息中心的号码,该号码对于一个手机卡来说是确定的,可以在手机的短信设置里面看到。该命令的设置还可能为AT+CSCA=“+13800411500”,多了个引号,后面还可以带一个参数的,AT+CSCA=+8613800411500,145; 145表示前面给的号码是国际号码,以+号开头,并带有国家标号;或者AT+CSCA=13800411500,129; 129表示不确定前面的号码是否是国家还是国际号码可带可不带国家标号,不要以+号开头。具体的我没有测试,查看的了不是官方文档。如果是要通过串口发送该命令可以写为:sentCom("AT+CSCA=\“+13800411500\”");注意这块有个对引号进行解引用。
AT+CSMS=1; //设置消息服务:GSM 07.05 Phase2+
AT+CNMI=2,1,0,1;//设置收到信息时的提示模式
AT^SMGO=1;//用URC形式提示SMS存储空间是否满,0不提示,1表示提示。
AT+CMGF=0;//设置发送信息的模式,0表示PDU模式,1表示文本模式,默认情况一般是0,但我的手机卡默认似乎是1
AT+SCMP=17, 167,0,0;//如果是文本形式发送,则需设置该参命令
AT&W;//将上面的设置存入用户配置文件中,这样下次上电加载时就可以不用再做这些设置了。这里的用户配置文件指的是在MC52i中还是SIM卡中呢?
好了,上面所说的是官方文档要求的流程,现在来看看我的实际情况吧:
AT+CSCS = “GSM”; 对于该条命令,我将字符集设为UCS2,即AT+CSCS = “USC2”; 不管是文本模式还是PDU模式发送中文还是英文短信,信息都是发送失败,所以最后选择的是默认的GSM字符集就正常了。
AT + CSCA = +13800411500;在用文本模式发送短信时,设置了该信息中心号码,发送短信还出错了,发不出去,忘了是不是和前面的选择字符集有关。我用AT+CSCA=?进行测试,该命令能返回OK,但进行执行读命令:AT+CSCA?却总是出错,有两次返回的却是很长的一串看不懂的数字串:+CSCA:"002B0038003600310033003800300030003400310031003500300030,145",后面的145应该就是表示前面的号码是国际号,也许该信息中心号码是进行了加密?有看到文档说该信息中心号码是存在SIM卡里的,而不是GSM模块里的,发送短信时,GSM模块该会去SIM卡中读取该号码(PS,我没在官文档中明确的看到,当然我看的资料肯定也不全)。所以在用文本模式发送短信时,我没有设置该信息中心号码,短信反而还发送出去了!!!
上面接下来的六条命令除了AT+CMGF=0;外,其它五条命令都可以选用默认值!当然该条命令也可以选用默认值的,就是要看你以何种形式发送短信了。
所以总结来说,如果以文本模式发送短信只需执行如下命令:
sentCom("AT+CMGF=1");//选用文本模式
delay(100);//延时100ms,在MC52i的官方文档中有指出,每条命令执行后一般需要100ms左右才能执行下一条命令,有些命令需要的延时会更长,比如打开网络连接命令。
sentCom("AT+CMGS=18608683863");//设置目的手机号
delay(100);//延时100ms,如果能发送短信,则会反回一个“>”提示符,等待输入短信文本。
print("Please sent me a message back by qq, thx");//发送短信文本,注意以文本形式发送短信是不支持中文的,如果这里输入了中文或中文符号则会出错
print("\x1a");//以十六进制的形式输入Ctrl+Z, 结束短信
delay(5000);//延时5s,注意,这里的延时很重要,当时我延的是2s都没有收到MC52i模块的回复,所以都不知道该命令是否执行成功,最后才发现是延时不够,这块的延时可让我花了些时间,搞不懂问题出中哪了!!MC52i中的命令延时是很重要的,但在网上的一些资料里很少有见到强调这一点的。
如果以PDU的模式发送短信,需要执行的命令如下:
sentCom("AT+CMGF=0");//选用PDU模式
delay(100);//延时100ms
sentCom("AT+CMGS=25");//如果能发送短信,即该命令执行成功则会返回一个">"提示符,等待输入短信文本,写程序的时候可以用一个if判断下。
print("0011000D91685178722902F00008000A5DE54F5C61095FEBFF01");//短信内容为“工作愉快!”四个字加个叹号,
print("\x1a");//以十六进制的形式输入Ctrl+Z, 结束短信
delay(5000);//延时5s,注意,这里的延时很重要,当时我延的是2s都没有收到MC52i模块的回复,所以都不知道该命令是否执行成功,最后才发现是延时不够,这块的延时可让我花了些时间,搞不懂问题出中哪了!!MC52i中的命令延时是很重要的,但在网上的一些资料里很少有见到强调这一点的。
PS:sentCom("AT+CMGS=25");该条命令中的25是从PDUtype开始(即从CSCA后一个字节开始)算起直到最符所有字符的字节数,即字符数/2。对于上面的那条信息,即指:11000D91685178722902F00008000A5DE54F5C61095FEBFF01所占用的字节数,注意是字节数而不是字符数!!如果想偷懒,可以把该字符串复到word07或10中,然后选中,点击状态栏中的字符数即可查看到有多少字符,然后除以2即是字节数。有的文档说这里的长度指的是要发送数据的最大字节数,比如这里的25指最大发送25字节(不包括CSCA),实际发送的字节数可以少于该数,但在我的测试中,该值只能是实际发送数据的字节数,否则就会出错!
0011000D91685178722902F00008000A5DE54F5C61095FEBFF01该条信息的第一个字节00表示选择 AT + CSCA = +8613800411500命令中设置的信息中心号码,故该PDU中就省去了该信息中心号码,但在我的程序中也并没有执行 AT + CSCA = +8613800411500命令,信息还是发送出去了,应该是SIM卡中就有该信息中心号码的信息。
该PDU还可以换成:0891683108401105F011000D91685178722902F00008000A5DE54F5C61095FEBFF01,短信内容为“工作愉快!”四个字再加个叹号, 发送的字节数仍然还是25和上一条PDU是一样的,只是加上了信息中心的号码而已!
在这里对:0891683108401105F011000D91685178722902F00008000A5DE54F5C61095FEBFF01该PDU进行简单的解释下:
首先对该PDU进行分段:0891683108401105F0 1100 0D91685178722902F0 000800 0A5DE54F5C61095FEBFF01
第一段为CSCA,即信息中心号码:08 91 683108401105F0 :08为“91683108401105F0”的字节长度(字符数/2) 91表示国际号码,所以在号码中加有国家标志,86代表中国,该信息中心的实际号码为:8613800411500,将每两位数字为一组,然后前后交换即得到前面的表示形式。更详细的请参考我所提供的资料中有详细介绍。
第二段:1100, 11:该字节即为PDUtype, 表示为发送短信,而不是接收短信,当然该字节中还包含有其它意思,具体的请参考相关文档。00:为TP-MR,信息参考,每发送一条短信,信息中心将对该值自动加1,一般都设为00。
第三段为目的手机号码:0D 91 685178722902F0 ,OD为目的手机号(8615872792200)的字符长度的十六进制表示,十进制为13,注意这里指的是字符长度,而不是字节数,这个计算与信息中心号码的长度计算有区别!!所以该长度不包括91,也不包括补充上来的那个F。91表示国际号码。这里的实际号码为:8615872792200。
第四段为数据编码:000800 这里的08表示采用UCS2编码而并不是网上一些资料说的8位编码,8位编码该位可以用04或者F6等,具体的可以参考相关资料。如果这里改为04,即采用8编码,而我这个PDU的短信内容仍然用Unicode编码,即0A5DE54F5C61095FEBFF01这样,短信发送执行命令返回的是OK,但对方却没有收到短信!!!其它两字节这里不作详细介绍(前面的00为PID,后面的00为信息中心保留信息的时间)。
第五段为信息内容:0A 5DE54F5C61095FEBFF01,0A表示信息的长度,即指这里的“5DE54F5C61095FEBFF01”的字节长度,该中文采用的是Unicode,然后经过转换为UCS2编码。
所以最后该PDU表示的信息为:
信息中心号码为:+8613800411500
目的手机号码为:+8615872792200
短信内容为:工作愉快!
最后附上sentCom()和print函数:
void sentCom(unsigned char *pd)
{
RxCounter = 0; //重置接收缓冲区计数器
print( pd );
print("\r\n"); //发送完命令的标志,也可以只输入:\r
}