Berd's Land

_(:з」∠)_

@FENGberd2 years ago

12/18
18:10
蜜汁代码

短消息PDU编码的(伪)正确方式

现在市面上大多数的GSM模块发送消息都支持Text和PDU两种编码方式
Text方式发送短信特简单,但缺点是不能发送中文 <-废话
于是就写了这篇文章记录一下PDU的构造方式

随便Google一下就能查到,PDU编码大概长这样

A:B+C的长度,1Byte
B:SMSC地址类型,1Byte
C:SMSC地址
D:基本参数(TP-MTI/VFP),1Byte
E:信息类型(TP-MR),1Byte
F:目标号码长度,1Byte
G:被叫号码类型,1Byte
H:被叫号码,长度由F中的数据决定
I:协议标识(TP-PID),1Byte
J:数据编码(TP-DCS),1Byte
K:有效期(TP-VP),1Byte
L:用户数据长度(TP-UDL),1Byte 所以说短信长度限制就是这玩意的锅么(雾)
M:用户数据

先说说A,B,C三块数据,这是关于SMSC(短信中心的),F,G,H三块数据也用同样的方法处理,不再重复,但是注意F块长度不包含F字符长度和B块长度
注:如果你不知道SMSC地址,使用AT+CSCA?就能读出来(除非SIM模块坏了或者你手贱设置了错误的SMSC数据)
数据块C:

  1. 将SMSC地址前面的+号去掉smsc=smsc.lstrip('+');
  2. 用F将SMSC地址的长度补全为偶数smsc=smsc.ljust(int(round(len(smsc)/2.0))*2,'F');
  3. 将SMSC地址的奇偶位交换
    smsc=list(smsc);
    for k in range(len(smsc)/2):
    	smsc[k*2],smsc[k*2+1]=smsc[k*2+1],smsc[k*2];
    smsc=''.join(smsc);

数据块B:

一般来说,直接使用91就行了(国际格式号码),如果你要发到小灵通就用81

数据块A:

就是B+C的长度啦,但是B和C都是Hex数据,所以算出来要/2并转成Hex
'%02X'%(len(smsc)/2+1)

数据块D:

这是基本参数数据块,暂时不写吧

数据块E:

信息类型,固定00即可

数据块I:

协议标识,固定00即可(普通GSM类型,点到点)

数据块J:

数据编码,建议UCS2(Unicode编码),值是08

数据块K:

信息有效期,一般写00,五分钟

最后是数据块L,M,这两个数据块就是信息内容

因为上面数据块J写的是08,使用UCS2编码,直接将数据转为UTF8编码的Hex数据即可作为M数据块<-实际上麻烦死了
同样求出长度放到最前面就能作为L数据块,直接贴代码吧…Python的%真的很好用

def ucs2_encode(text):
	result='';
	for b in map(ord,text):
		result=result+'%02X%02X' % (b >> 8,b & 0xFF);
	return '%02X%s' % (len(result)/2,result);

注:使用时需要传入unicode对象,如u'poi'

数据块全部弄好后,将所有数据加一起就可以传入GSM模块啦
最后送上完整代码(只求保留作者;w;)

def pdu_build(smsc,dest_address,content):
	if(len(content)>70):
		raise Exception('Content too long!');
	def phone_process(phone,includeF=True):
		phone=phone.lstrip('+');
		length=len(phone);
		print(length,phone);
		phone=phone.ljust(int(round(len(phone)/2.0))*2,'F');
		phone=list(phone);
		for k in range(len(phone)/2):
			phone[k*2],phone[k*2+1]=phone[k*2+1],phone[k*2];
		print phone;
		if(includeF):
			length=len(phone)/2+1;
		return '%02X91%s'%(length,''.join(phone));
	ucs2='';
	for b in map(ord,content):
		ucs2=ucs2+'%02X%02X' % (b >> 8,b & 0xFF);
	return '%s1100%s000800%02X%s' % (phone_process(smsc),phone_process(dest_address,False),len(ucs2)/2,ucs2);

短消息PDU编码的(伪)正确方式