在上一篇:(七)Linux搭载4G模块——AT指令实现短信包的获取和删除,我们知道了怎么用AT指令取获取存储器中的短信包,而且短信包的格式分为两种,分别是Text格式和PDU格式。PDU格式可以解码出中英文,所以后面获取短信我们都用PDU格式。
下面是我收到某个号码的PDU包,短信内容是"你好"。
PDU包的构成=中心号码长度+中心号码+TPDU头字节+源号码长度+源号码+协议标识+数据编码方案+日期时间+数据长度+数据
PDU包解码和我们发送信息时打包的规则都差不多,逆着来就好。不过纯英文PDU包和中文PDU包的数据部分解码是很大不同的,英文需要用到7bit解码方法,中文的先从字符流转字节流,再将字节流的Unicde格式转成UTF8格式即可。具体原理可看此文章:中英文短信解码
具体原理可看此篇文章:字符流与字节流的相互转换原理及代码(Linux C)
int str_to_byte(char *source, char *dest, int source_len)
{
char highbyte;//高字节
char lowbyte;//低字节
int i = 0;
//在所接收的的信息字符串中,只有数字和字母,所以只对数字和字母作处理即可
for (i; 2*i < source_len; i ++)
{
highbyte = toupper(source[i*2]);//将所有小写字母转成大写
lowbyte = toupper(source[i*2 +1]);
if(highbyte <= 0x39)
highbyte -= 0x30;
else
highbyte -= 0x37;
if(lowbyte <= 0x39)
lowbyte -= 0x30;
else
lowbyte -= 0x37;
dest[i] = (highbyte << 4) | lowbyte;
//printf("dest[%d]=%02x\n",i, dest[i]);
}
return 0;
}
这里Unicode编码转UTF8编码使用了Linux系统下的iconv系列的函数,需要了解它们的使用方法。
#include <iconv.h>
iconv_t iconv_open(const char* tcode, const char* fromecode);
iconv_open()函数申请一个转换描述符,转换字符序列从编码fromcode到编码tcode
转换描述符包含转换状态,调用icvon_open()以后,转换处于初始状态;调用icvon()以后,改变装换描述符的转换状态。注意iconv_open()返回的是一个iconv_t类型的变量,然后供iconv()和iconv_close()使用。
返回值:iconv_open()函数返回一个新申请的返回描述符。 出错时返回( iconv_t)-1
size_t iconv(iconv_t cd,
char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft);
iconv()函数实现编码转换,此函数从inbuf中读取字符,转换后输出到outbuf中,inbytesleft用以记录还未转换的字符数,outbytesleft用以记录输出缓冲的剩余空间。注意:inbuf和outbuf都必须是有存储空间的不能定义为常量。
int iconv_close(iconv_t cd);
iconv_close()函数用于关闭转换句柄,释放资源。
int unicode_to_utf8(char *source, size_t source_len, char *dest, size_t dest_len)
{
int i = 0;
char temp;
int retval = -1;
iconv_t cd;
char unicode_buf[512] = {0};
if (!source)
{
printf("Invalid argument!\n");
return -1;
}
str_to_byte(source, unicode_buf, strlen(source));
//注意这里一定不能使用strlen来求长度,因为字节序中有英文的话遇上0x00就会停止计算长度,求出来的长度比实际长度小。
for (i; i < sizeof(unicode_buf); i +=2)
{
temp = unicode_buf[i];
unicode_buf[i] = unicode_buf[i+1];
unicode_buf[i+1] = temp;
}
cd = iconv_open("UTF-8","UNICODE");
if (cd < 0)
{
printf("iconv_open() failure!\n");
return -2;
}
char *source_ptr = unicode_buf;
retval = iconv(cd, &source_ptr, &source_len, &dest, &dest_len);
if(retval < 0)
{
printf("Unicode to utf-8 failure:%s\n", strerror(errno));
iconv_close(cd);
return -3;
}
//printf("Unicode to utf-8 successfully:%s\n", dest);
iconv_close(cd);
return 0;
}
GSM默认采用7bit编码,实际上,7位编码是种压缩算法,因为,ASCII码(不包括扩展ASCII),其值小于0x80,最高位bit8是0,被忽略了;而7bit编码就利用了这一位来存储数据;其编码时,依次将下一7位编码的后几位逐次移至前面,形成新的8位编码。
int decode7bit(const unsigned char* source, char* dest, int source_len)
{
int nsrc = 0;
int ndst = 0;
int nbyte = 0;
unsigned char nleft = 0;
if (!source||!dest||(source_len<=0))
{
printf("[%s]Invalid parameter\n",__func__);
return -1;
}
while (nsrc < source_len)
{
*dest = ((*source << nbyte) | nleft) & 0x7f;
nleft = *source >> (7-nbyte);
dest++;
ndst++;
nbyte++;
if(nbyte == 7)
{
*dest = nleft;
dest++;
ndst++;
nbyte = 0;
nleft = 0;
}
source++;
nsrc++;
}
*dest = 0;
return ndst;
}
int english_decod(char *source, char *dest, int source_len)
{
char temp_buf[512] = {0};
if (!source|| !dest)
{
printf("Invalid argument.\n");
return -1;
}
str_to_byte(source, temp_buf, source_len);
decode7bit(temp_buf, dest, strlen(temp_buf));
return 0;
}
我们知道在整个PDU包中,包括了中心号码长度,源号码长度,数据长度。但我们获取到它们的时候是16进制的字符串的形式,这就需要我们进行计算再使用它们了,下面是我写的一个算法,把它们计算转成了整数形式,这样用的时候就方便多了。
int calculate_len(char *ptr)
{
int len;
int highbyte;
int lowbyte;
if (*ptr < 0x39)
highbyte = (*ptr - 0x30)*16;
else
highbyte = (*ptr-0x37)*16;
//printf("highbyte:%d\n", highbyte);
ptr++;
if (*ptr < 0x39)
lowbyte = *ptr - 0x30;
else
lowbyte = *ptr - 0x37;
//printf("lowbyte:%d\n", lowbyte);
len = highbyte + lowbyte;
//printf("len:%d\n", len);
return len;
}
int get_all_sms(ttyusb_ctx_t *ttyusb_ctx, char *sms_buf, int sms_buf_len)
{
int retval = -1;
char temp_buf[1024] = {0};
char *start_ptr = NULL;
int center_number_len = 0;
char phone_number[32] = {0};
int phone_len = 0;
int phone_flag = 0;
int encoding = 0;//编码
int data_len = 0;
char send_buf[128] = {0};
char time_buf[13] = {0};
int index = 0;
int j = 0;
char temp;
char data_buf[1024] = {0};
if (!ttyusb_ctx)
{
log_error("[%s]Invalid argument.\n", __func__);
return -1;
}
retval = send_at_cmd(ttyusb_ctx, "AT+CSDH=1\r", "OK", NULL, 0);
if(retval < 0)
{
printf("Send command AT+CSDH=1 failure!\n");
return -2;
}
while (1)
{
memset(send_buf, 0 , sizeof(send_buf));
sprintf(send_buf, "AT+CMGR=%d\r", index);
memset(temp_buf, 0, sizeof(temp_buf));
retval = send_at_cmd(ttyusb_ctx, send_buf, "+CMGR", temp_buf, sizeof(temp_buf));
if(retval < 0)
{
printf("There is no index:%d\n", index);
break;
return -3;
}
start_ptr = strstr(temp_buf, "+CMGR");
start_ptr = strstr(start_ptr, "\r\n");
start_ptr += 2;//让指针指向PDU首部
center_number_len = calculate_len(start_ptr);//计算中心号码长度
start_ptr = start_ptr + 2 + center_number_len*2 + 2;
//printf("电话长度:%s\n",start_ptr);
phone_len = calculate_len(start_ptr);//计算发送人号码长度
//printf("phone_len:%d\n", phone_len);
//如果号码是奇数位,补上一个F计算
if(phone_len%2 != 0)
{
phone_len++;
phone_flag = 1;
}
//获取发送人号码
strncpy(phone_number, start_ptr+2+2, phone_len);
for(j = 0; j < phone_len; j +=2)
{
temp = phone_number[j];
phone_number[j] = phone_number[j+1];
if(phone_flag && ((phone_len -2) == j))
{
phone_number[j+1] ='\0';
}
else
{
phone_number[j+1] = temp;
}
}
printf("PDU package:%s\n", temp_buf);
printf("SMS Index:%d\n", index);
printf("Sender:%s\n", phone_number);
//log_debug("target_number:%s\n", target_number);
//printf("phone_number:%s\n", phone_number);
start_ptr = start_ptr + 2 + 2 + phone_len + 2;
//printf("数据编码开始:%s\n",start_ptr);
encoding = calculate_len(start_ptr);//获取编码格式
start_ptr = start_ptr + 2 ;
memset(time_buf, 0 ,sizeof(time_buf));
snprintf(time_buf, 12+1, "%s", start_ptr);
printf("Receive time:");
for(j = 0; j < 12; j +=2)
{
temp = time_buf[j];
time_buf[j] = time_buf[j+1];
time_buf[j+1] = temp;
printf("-%c%c", time_buf[j], time_buf[j+1]);
}
printf("\n");
start_ptr = start_ptr + 12 + 2;
data_len = calculate_len(start_ptr);
data_len = data_len*2;
start_ptr = start_ptr +2;
memset(data_buf, 0, data_len);
snprintf(data_buf, data_len+1, "%s", start_ptr);
if(encoding)
{
memset(sms_buf, 0, sms_buf_len);
retval = unicode_to_utf8(data_buf, sizeof(data_buf), sms_buf, sms_buf_len);
}
else
{
//log_debug("This is the English code\n");
english_decod(start_ptr, sms_buf, data_len);
}
printf("SMS:%s\n", sms_buf);
start_ptr = NULL;
index++;
printf("-----------------------------------------------------------------------------------------------------\n");
}
}
int get_specified_sms(ttyusb_ctx_t *ttyusb_ctx, char *sms_buf, int sms_buf_len, char *target_number)
{
int retval = -1;
char temp_buf[1024] = {0};
char *start_ptr = NULL;
int center_number_len = 0;
char phone_number[32] = {0};
int phone_len = 0;
int phone_flag = 0;
int encoding = 0;//编码
int data_len = 0;
char send_buf[128] = {0};
char time_buf[13] = {0};
int index = 0;
int j = 0;
char temp;
char data_buf[1024] = {0};
int sms_flag = 1;
if (!ttyusb_ctx)
{
log_error("[%s]Invalid argument!\n", __func__);
return -1;
}
retval = send_at_cmd(ttyusb_ctx, "AT+CSDH=1\r", "OK", NULL, 0);
if(retval < 0)
{
printf("Send command AT+CSDH=1 failure!\n");
return -2;
}
while (1)
{
memset(send_buf, 0 , sizeof(send_buf));
sprintf(send_buf, "AT+CMGR=%d\r", index);
memset(temp_buf, 0, sizeof(temp_buf));
retval = send_at_cmd(ttyusb_ctx, send_buf, "OK", temp_buf, sizeof(temp_buf));
if(retval < 0)
{
printf("There is no message %d\n", index);
return -3;
}
start_ptr = strstr(temp_buf, "+CMGR");
if(NULL == start_ptr)
{
printf("There is no message %d\n", index);
break;
}
//printf("PDU包:%s\n", start_ptr);
start_ptr = strstr(start_ptr, "\r\n");
start_ptr += 2;//让指针指向PDU首部
center_number_len = calculate_len(start_ptr);//计算中心号码长度
start_ptr = start_ptr + 2 + center_number_len*2 + 2;
//printf("电话长度:%s\n",start_ptr);
phone_len = calculate_len(start_ptr);//计算发送人号码长度
//printf("phone_len:%d\n", phone_len);
//如果号码是奇数位,补上一个F计算
if(phone_len%2 != 0)
{
phone_len++;
phone_flag = 1;
}
//获取发送人号码
strncpy(phone_number, start_ptr+2+2, phone_len);
for(j = 0; j < phone_len; j +=2)
{
temp = phone_number[j];
phone_number[j] = phone_number[j+1];
if(phone_flag && ((phone_len -2) == j))
{
phone_number[j+1] ='\0';
}
else
{
phone_number[j+1] = temp;
}
}
if(strncmp(target_number, phone_number, strlen(phone_number)))
{
start_ptr = NULL;
index++;
continue;
}
printf("PDU package:%s\n", temp_buf);
printf("SMS Index:%d\n", index);
printf("Sender:%s\n", phone_number);
//log_debug("target_number:%s\n", target_number);
//printf("phone_number:%s\n", phone_number);
start_ptr = start_ptr + 2 + 2 + phone_len + 2;
//printf("数据编码开始:%s\n",start_ptr);
encoding = calculate_len(start_ptr);//获取编码格式
start_ptr = start_ptr + 2 ;
snprintf(time_buf, 12+1, "%s", start_ptr);
printf("Receive time:");
for(j = 0; j < 12; j +=2)
{
temp = time_buf[j];
time_buf[j] = time_buf[j+1];
time_buf[j+1] = temp;
printf("-%c%c", time_buf[j], time_buf[j+1]);
}
printf("\n");
start_ptr = start_ptr + 12 + 2;
data_len = calculate_len(start_ptr);
data_len = data_len*2;
start_ptr = start_ptr +2;
//log_debug("start_ptr3:%s\n",start_ptr);
memset(data_buf, 0, data_len);
snprintf(data_buf, data_len+1, "%s", start_ptr);
//printf("The data PDU is %s\n", data_buf);
memset(sms_buf, 0, sms_buf_len);
if(encoding)
{
retval = unicode_to_utf8(data_buf, sizeof(data_buf), sms_buf, sms_buf_len);
}
else
{
//log_debug("This is the English code\n");
english_decod(start_ptr, sms_buf, data_len);
}
printf("SMS:%s\n", sms_buf);
start_ptr = NULL;
index++;
printf("-----------------------------------------------------------------------------------------------------\n");
}
}
int delete_sms(ttyusb_ctx_t *ttyusb_ctx, char *target_number)
{
int retval = -1;
char temp_buf[1024] = {0};
char *start_ptr = NULL;
int center_number_len = 0;
char phone_number[32] = {0};
int phone_len = 0;
int phone_flag = 0;
char send_buf[128] = {0};
char time_buf[13] = {0};
int index = 0;
int j = 0;
char temp;
char data_buf[1024] = {0};
int sms_flag = 1;
if (!ttyusb_ctx)
{
printf("[%s]Invalid argument!\n", __func__);
return -1;
}
retval = send_at_cmd(ttyusb_ctx, "AT+CSDH=1\r", "OK", NULL, 0);
if(retval < 0)
{
printf("Send command AT+CSDH=1 failure!\n");
return -2;
}
while (1)
{
memset(send_buf, 0 , sizeof(send_buf));
sprintf(send_buf, "AT+CMGR=%d\r", index);
memset(temp_buf, 0, sizeof(temp_buf));
retval = send_at_cmd(ttyusb_ctx, send_buf, "OK", temp_buf, sizeof(temp_buf));
if(retval < 0)
{
printf("There is no message %d\n", index);
return -3;
}
start_ptr = strstr(temp_buf, "+CMGR");
if(NULL == start_ptr)
{
printf("There is no message %d\n", index);
break;
}
//printf("数据开始:%s\n", start_ptr);
start_ptr = strstr(start_ptr, "\r\n");
start_ptr += 2;//让指针指向PDU首部
center_number_len = calculate_len(start_ptr);//计算中心号码长度
start_ptr = start_ptr + 2 + center_number_len*2 + 2;
//printf("电话长度:%s\n",start_ptr);
phone_len = calculate_len(start_ptr);//计算发送人号码长度
//printf("phone_len:%d\n", phone_len);
//如果号码是奇数位,补上一个F计算
if(phone_len%2 != 0)
{
phone_len++;
phone_flag = 1;
}
//获取发送人号码
strncpy(phone_number, start_ptr+2+2, phone_len);
for(j = 0; j < phone_len; j +=2)
{
temp = phone_number[j];
phone_number[j] = phone_number[j+1];
if(phone_flag && ((phone_len -2) == j))
{
phone_number[j+1] ='\0';
}
else
{
phone_number[j+1] = temp;
}
}
if(strncmp(target_number, phone_number, strlen(phone_number)))
{
start_ptr = NULL;
index++;
continue;
}
memset(send_buf, 0 , sizeof(send_buf));
sprintf(send_buf,"AT+CMGD=%d\r",index);
retval = send_at_cmd(ttyusb_ctx, send_buf, "OK", NULL, 0);
if(retval < 0)
{
printf("[%s]Failed to delte the information\n", __func__);
return -3;
}
printf("Delete message %d ok!\n",index);
start_ptr = NULL;
index++;
printf("-----------------------------------------------------------------------------------------------------\n");
}
}