文章目录
前言
为什么要讲PDU编码呢?
因为上一遍: Linux下的串口编程(实现AT指令的收发)只是实现了再串口下发送普通AT指令,但是我们 这个项目最终实现的是短信收发的短信猫,所以还要实现在串口下发送和接收短信,而在我们的EC200U-CN中发送SMS(短消息)会有两种模式:一种是TEXT模式,另一种就是PDU模式,PDU模式既支持英文,也支持中文;而TEXT模式则只支持纯英文和数字模式,我们发短信不可能不发中文吧,所以现在知道为什么要学这个PDU编码了吧!
1. 什么是PDU编码?
PDU编码是一种用来发送和接收SMS信息(短消息)的模式,相当于一个数据包,它不仅支持中文短信,也能发送英文短信,PDU模式被所有的手机所支持,可以使用任何的字符集,这也是手机的默认编码方式。
收发短消息的方法还有Blocl Mode, Text Mode ,常用用的就是Text和PDU 模式,但是text模式只能发送英文,不能发送中文。
2. 什么是Unicode编码、什么是UTF-8编码以及怎么转换的?
PDU编码中后面的内容(上面所说的M)就是代表了短消息内容的Unicode编码,而我们linux下终端输入的编码默认为UTF-8编码,所以涉及到UTF-8编码转为Unicode编码,然后才是PDU编码的组成。
2.1 什么是Unicode编码呢?
Unicode编码是包含了世界上所有的语言的每一个符号,每一个符号都可以用一个独一无二的的编码来表示,这样就解决了因不同语言编码问题导致不同乱码的问题。
Unicode 的缺点:Unicode 只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储:无法区别Unicode 和ASCII:计算机无法区分三个字节表示一个符号(Unicode)还是分别表示三个符号(ASCII)。
另外,我们知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储空间来说是极大的浪费。此时UTF-8代码诞生了。
2.2 什么是UTF-8编码呢?
UTF-8 是在互联网上使用最广的一种Unicode 的实现方式。
UTF-8 是一种变长的编码方式。它可以使用1-6 个字节表示一个符号,根据不同的符号而变化字节长度。
后面也是根据这个规则来进行UTF-8编码转为Unicode编码的。
2.3 代码实现UTF-8编码转换为Unicode编码
/*-----------------------------------------------------------------------
| funtion:UTF-8 code to unicode.
|
| argument: pdu: The pdu code required parameters.
|
| return: rv: success: == 0 ; error: < 0
|-----------------------------------------------------------------------*/
int utf8_to_unicode(pdu_ctx_t *pdu)
{
int i = 0;
char *temp = pdu->hex_unicode;
int hex_len = 0;
char byte1, byte2, byte3, byte4; // High byte to low byte.
if(!pdu)
{
printf("%s,Invalid input arguments\n",__func__);
return -1;
}
hex_len = sizeof(pdu->hex_unicode);
memset(temp,0,hex_len);
pdu->hex_unicode_len = 0;
while(pdu->send_message[i])
{
if((pdu->send_message[i] > 0x00) && (pdu->send_message[i] <= 0x7E)) // one byte
{
*temp = 0;
temp++;
*temp = pdu->send_message[i];
temp++;
i++;
pdu->hex_unicode_len += 2;
}
else if(0xC0 == (pdu->send_message[i] & 0xE0)) //two bytes: eg: 1100 1110 & 1110 0000 = 1100 0000(0xC0)
{
byte1 = pdu->send_message[i];
byte2 = pdu->send_message[i+1];
/* check the top two bits of low byte whether is '10' */
if((byte2 & 0xC0) != 0x80)
{
printf("UTF-8 codes have any questions\n");
return -2;
}
*temp = ((byte1 >> 2) & 0x07);
temp++;
*temp = ((byte1 << 6) + (byte2 & 0x3F));
temp++;
i += 2;
pdu->hex_unicode_len += 2;
}
else if(0xE0 == (pdu->send_message[i] & 0xF0)) //three bytes: eg: 1110 0010 & 1111 0000 = 1110 0000(0xE0)
{
byte1 = pdu->send_message[i];
byte2 = pdu->send_message[i+1];
byte3 = pdu->send_message[i+2];
/* check the top two bits of low byte whether is '10' */
if(((byte2 & 0xC0) != 0x80) || ((byte3 & 0xC0) != 0x80))
{
printf("UTF-8 codes have any questions\n");
return -3;
}
*temp = ((byte1 << 4) + ((byte2 >> 2) & 0x0F));
temp++;
*temp = ((byte2 << 6) + (byte3 & 0x3F));
temp++;
i += 3;
pdu->hex_unicode_len += 2;
}
else if(0xF0 == (pdu->send_message[i] & 0xF8)) //four bytes: eg: 1111 0111 & 1111 1000 = 1111 0000(0xF0)
{
byte1 = pdu->send_message[i];
byte2 = pdu->send_message[i+1];
byte3 = pdu->send_message[i+2];
byte4 = pdu->send_message[i+3];
/* check the top two bits of low byte whether is '10' */
if(((byte2 & 0xC0) != 0x80) || ((byte3 & 0xC0) != 0x80) || ((byte4 & 0xC0) != 0x80))
{
printf("UTF-8 codes have any questions\n");
return -4;
}
*temp = (((byte1 << 2) & 0x1C) + ((byte2 >> 4) & 0x03));
temp++;
*temp = ((byte2 << 4) + ((byte3 >> 2) & 0x0F));
temp++;
*temp = ((byte3 << 6) + (byte4 & 0x3F));
temp++;
i += 4;
pdu->hex_unicode_len += 3;
}
else
{
printf("UTF-8 codes have any questions\n");
return -5;
}
}
return 0;
}
3. 短信中心号的处理
PDU编码中包括处理过的短信中心号,所以我们要所以它是怎么处理的(使用AT+CSCA?获取原始的短信中心号码)。
规则:
代码实现
获取原始的短信中心号:
/*-----------------------------------------------------------------------
| funtion:Gain center number(AT+CSCA?).
|
| argument: 1、receive_phone: receiver phone number.
| 2、serial: serial port property struct.
|
| return: rv: success: == 0 ; error: < 0
|-----------------------------------------------------------------------*/
int gain_center_number(char *center_number,serial_ctx_t *serial)
{
int rv = 0;
atcmd_ctx_t atcmd;
char *ptr = NULL;
if(!center_number || !serial)
{
printf("%s,Invalid input arguments\n",__func__);
return -1;
}
memset(&atcmd,0,sizeof(atcmd_ctx_t));
strncpy(atcmd.send_at_buf,"AT+CSCA?\r",sizeof(atcmd.send_at_buf));
strncpy(atcmd.succe_at_buf,"CSCA",sizeof(atcmd.succe_at_buf));
atcmd.use_len = sizeof(atcmd.use_buf);
atcmd.resp_timeout = 2;
rv = send_recv_atcmd(serial,&atcmd);
if(rv < 0)
{
printf("send_resp_serial() of %s failure and rv: %d\n",__func__,rv);
return -2;
}
if(!atcmd.use_buf)
{
printf("%s not copy AT response to use_buf\n",__func__);
return -3;
}
ptr = strstr(atcmd.use_buf,"CSCA"); // return the first address of CSCA
ptr += 7; // address >> 7 bytes get the first address of "+86***********"
strncpy(center_number,ptr,CENTER_NUMBER_LEN);
return 0;
}
处理:
/*-----------------------------------------------------------------------
| funtion:Handle center number to unicode.
|
| argument: pdu: The pdu code required parameters.
|
| return: rv: success: == 0 ; error: < 0
|-----------------------------------------------------------------------*/
int handle_center_number(pdu_ctx_t *pdu)
{
int i;
char temp;
char transfer[54] = {0};
if(!pdu)
{
printf("%s,Invalid input arguments\n",__func__);
return -1;
}
/* Remove the first '+' and add 'F' to end */
memset(transfer,0,sizeof(transfer));
snprintf(transfer,sizeof(transfer),"%s%s",&pdu->center_number[1],"F");
/* Parity bit swap */
for(i=0; i < strlen(transfer); i+=2)
{
temp = transfer[i];
transfer[i] = transfer[i+1];
transfer[i+1] = temp;
}
/* add head length divided by 2 hex(08) and '91' */
memset(pdu->pdu_center_number,0,sizeof(pdu->pdu_center_number));
snprintf(pdu->pdu_center_number,sizeof(pdu->pdu_center_number),"0891%s",transfer);
return 0;
}
4. 收件人号码处理
规则:
代码实现
/*-----------------------------------------------------------------------
| funtion:Handle receiver number to unicode.
|
| argument: pdu: The pdu code required parameters.
|
| return: rv: success: == 0 ; error: < 0
|-----------------------------------------------------------------------*/
int handle_recv_phone(pdu_ctx_t *pdu)
{
int i;
char temp;
char transfer[56] = {0};
char *head = "11000D91";
char *tail = "000800";
if(!pdu)
{
printf("%s,Invalid input arguments\n",__func__);
return -1;
}
/* Remove the first '+' and add 'F' to end */
memset(transfer,0,sizeof(transfer));
snprintf(transfer,sizeof(transfer),"%s%s",&pdu->recv_phone[1],"F");
/* Parity bit swap */
for(i=0; i < strlen(transfer); i+=2)
{
temp = transfer[i];
transfer[i] = transfer[i+1];
transfer[i+1] = temp;
}
/* add head and tail */
memset(pdu->pdu_recv_phone,0,sizeof(pdu->pdu_recv_phone));
snprintf(pdu->pdu_recv_phone,sizeof(pdu->pdu_recv_phone),"%s%s%s",head,transfer,tail);
return 0;
}
5. PDU编码流程图
6. 字节流转换为字符流
为什么要这样呢?因为发送给串口的buf,必须是字符串类型的,也就是所谓的字符流,而目前,我们获取到的Unicode属于字节流的数据,是不能直接发送给串口的,就算发送了也不会成功,所以必须将字节流的数据转为字符流,才可以进行接下来的其他拼接与发送,下面是代码:
/*-----------------------------------------------------------------------
| funtion:A byte stream is a character stream(hex to str). eg: 0x1A --> ‘1’ ,‘A’
|
| argument: pdu: The pdu code required parameters.
|
| return: rv: success: == 0 ; error: < 0
|-----------------------------------------------------------------------*/
int hex_to_str(pdu_ctx_t *pdu)
{
int i;
int source_len;
unsigned char HighByte; // high bytes
unsigned char LowByte; // low bytes
if(!pdu)
{
printf("%s,Invalid input arguments\n",__func__);
return -1;
}
source_len = pdu->hex_unicode_len;
for(i = 0; i < source_len; i++)
{
HighByte = pdu->hex_unicode[i] >> 4;
LowByte = pdu->hex_unicode[i] & 0x0f;
HighByte += 0x30;
if(HighByte <= 0x39)
pdu->str_unicode[i*2] = HighByte;
else
pdu->str_unicode[i*2] = HighByte + 0x07;
LowByte += 0x30;
if(LowByte <= 0x39)
pdu->str_unicode[i*2+1] = LowByte;
else
pdu->str_unicode[i*2+1] = LowByte + 0x07;
}
//printf("str: %s\n",pdu->str_unicode);
return 0;
}
7. 封装组合(得到PDU编码)
/*-----------------------------------------------------------------------
| funtion:Packet data to PDU code.
|
| argument: pdu: The pdu code required parameters.
|
| return: rv: success: == 0 ; error: < 0
|-----------------------------------------------------------------------*/
int pdu_packet(pdu_ctx_t *pdu)
{
int rv = 0;
char temp[512] = {0};
if(!pdu)
{
printf("%s,Invalid input arguments\n",__func__);
return -1;
}
if(handle_center_number(pdu) < 0)
{
printf("handle center number failure\n");
return -2;
}
if(handle_recv_phone(pdu) < 0)
{
printf("handle center number failure\n");
return -3;
}
rv = utf8_to_unicode(pdu);
if(rv < 0)
{
printf("utf8 convert to unicode failure and rv: %d\n",rv);
return -4;
}
//printf("hex_unicode: %x\n",pdu->hex_unicode);
if(hex_to_str(pdu) < 0)
{
printf("hex convert str failure\n");
return -5;
}
/* strlen(pdu->str_unicode)/2 add head of str unicode */
memset(temp,0,sizeof(temp));
snprintf(temp,sizeof(temp),"%02X%s",strlen(pdu->str_unicode)/2,pdu->str_unicode);
/* receive phone number connect to new str_unicode(temp) */
strcat(pdu->pdu_recv_phone,temp);
pdu->at_cmgs = (int)(strlen(pdu->pdu_recv_phone)/2); // use to AT command AT+CMGS = pdu->at_cmgs
/* center number connect to receive phone and save pdu buf */
memset(pdu->pdu_buf,0,sizeof(pdu->pdu_buf));
snprintf(pdu->pdu_buf,sizeof(pdu->pdu_buf),"%s%s",pdu->pdu_center_number,pdu->pdu_recv_phone);
//printf("pdu_buf: %s\n",pdu->pdu_buf);
return 0;
}
总结
提示:因为我是完成了这个项目才写的博客,所以这段代码时从代码中提取出来的。看看你有些变量看不懂,但是逻辑就是这个逻辑,可以继续关注我的下一篇博客就可以理解了
- 转码的的过程中只要是要理解他们之间转换的规则,一个位都会影响到最后的结果;
- 主要位运算的过程有没有出错,特别是字节流转字符流的时候,我当时就在那出错了,然后又折腾了好久。