專注差異化嵌入式產(chǎn)品解決方案 給智能產(chǎn)品定制注入靈魂給予生命
提供開發(fā)工具、應(yīng)用測試 完善的開發(fā)代碼案例庫分享
從全面的產(chǎn)品導(dǎo)入到強(qiáng)大技術(shù)支援服務(wù) 全程貼心伴隨服務(wù),創(chuàng)造無限潛能!
單片機(jī)通過短信貓收發(fā)短信的方法
短信在現(xiàn)今的生活中起著非常重要的作用,我們每天都會使用它來進(jìn)行信息的接收與發(fā)送,為我們的溝通提供新的手段。從本質(zhì)上說,其實(shí)是一種數(shù)據(jù)傳輸?shù)臋C(jī)制,通過GSM(全球移動通信系統(tǒng))作為其傳輸?shù)牡缆?,從而?shí)現(xiàn)了數(shù)據(jù)的遠(yuǎn)距離傳輸。如果我們把它運(yùn)用于單片機(jī)上,就可以實(shí)現(xiàn)單片機(jī)上的數(shù)據(jù)遠(yuǎn)距離傳輸,這在實(shí)際的應(yīng)用中是十分有用的。如數(shù)據(jù)的遠(yuǎn)程采集與傳輸、環(huán)境的監(jiān)測與報(bào)警等。
然而要實(shí)現(xiàn)單片機(jī)發(fā)送短信,就要依賴于專用的硬件――短信貓(GSM Modem),,它用來負(fù)責(zé)與GSM網(wǎng)絡(luò)進(jìn)行通信,而單片機(jī)則負(fù)責(zé)短信的編解碼。下面介紹短信的編碼,其實(shí)這些編碼我們每天都在使用,只是它藏匿于手機(jī)或終端設(shè)備中,不被我們熟知。
短信編碼
以一個發(fā)送實(shí)例來進(jìn)行講解:
筆者位于哈爾濱市,其短信中心號碼為13800451500,短信接收方號碼采用我本人的號碼15846003114,短信內(nèi)容(最多為140個字節(jié),如果是中文則最多為70個字)為“大家好?。?!”。
先列出最終的編碼,然后再來慢慢分析:
0891683108401505F011000B815148063011F40008A70C59275BB6597DFF01FF01FF01
將以上編碼通過串口寫入短信貓,再輔以相應(yīng)的AT指令與附加信息就可以成功發(fā)送短信。
1.08:短信中心地址長度(可以固定不變)
2.91:短信中心號碼類型(可以固定不變)
3.68:中國地區(qū)代碼(在中國范圍內(nèi)固定不變)
4.3108401505F0:短信服務(wù)中心號碼 13800451500
(根據(jù)地域的不同進(jìn)行變動,實(shí)際情況下,可以固定不變,不論在中國任何地域都通過哈爾濱短信中心,但使用當(dāng)?shù)氐姆?wù)中心收發(fā)會更快。)
可以看到,短信服務(wù)中心號碼采用了一種比較特殊的表示方法。其實(shí)也很簡單就是在短信服務(wù)中心號碼后加一個F,號碼長度就變成了12位,然后對它每兩位中的字符進(jìn)行對調(diào)。在后面的接收方號碼也是采用此種方法進(jìn)行表示的,對這一點(diǎn)的理解的至關(guān)重要的,它將直接關(guān)系到短信發(fā)送的正確與否。
5.1100:發(fā)送短信的編碼方式(可以固定不變)
6.0B:目的地址長度(可以固定不變)
7.81:目的地址類型(可以固定不變)
8. 5148063011F4:目的地址,即接收方號碼 15846003114
如前面所說,接收方號碼亦采用此種方法。
9.0008:發(fā)送中文字符方式
10.A7:(可以固定不變)
11.0C:短信內(nèi)容長度
12. 59275BB6597DFF01FF01FF01:發(fā)送中文字符的UNICODE碼
(根據(jù)發(fā)送信息內(nèi)容進(jìn)行變動)
中文的發(fā)送采用UNICODE編碼。由于手機(jī)上對編程的解碼也是采用UNICODE的,因此只有只用UNICODE編碼才能使短信內(nèi)容成功顯示在手機(jī)上。其次,在向手機(jī)發(fā)送短信時(shí),短信內(nèi)容需要使用內(nèi)存方式的顯示表示,如01FF為一個字符的UNICODE碼,在編碼中它為4個ASCII碼,而它表示是0x01ff,為兩個字節(jié),這一點(diǎn)至關(guān)重要,不采用這種表示方法或表示有錯,則會導(dǎo)致在接收方手機(jī)上無顯示或顯示不正常。
此編碼段是單片機(jī)進(jìn)行數(shù)據(jù)傳輸?shù)暮诵牟糠?,它是真正的?shù)據(jù)。在日常的中文短信編碼中均采用UNICODE,但其本身并不局限于UNICODE,如果接收終端是支持GB2312的,而不支持UNICODE,那么就可以在這里放相應(yīng)的GB碼。脫離開字符編碼,其實(shí)可以放入任何數(shù)據(jù),只是要在上一個編碼段中設(shè)置相應(yīng)的數(shù)據(jù)長度即可。
在筆者的短信收發(fā)系統(tǒng)中,采用西門子的TC35i作為短信貓,由于使用了一種支持GB2312硬件字庫的液晶顯示器,因此在數(shù)據(jù)編碼中直接使用GB碼,成功實(shí)現(xiàn)短信發(fā)送與接收,并在顯示器上對解碼后的內(nèi)容進(jìn)行顯示,達(dá)到了較好的效果。
14.:發(fā)送結(jié)束標(biāo)志(十六進(jìn)制為0x1A),表示短信碼結(jié)束。
結(jié)束標(biāo)志在實(shí)際的操作中很容易遺忘,請讀者加以注意。
UNICODE與GB2312碼
與短信收發(fā)相關(guān)的AT指令
短信編碼還要輔以相應(yīng)的AT指令,才能實(shí)現(xiàn)短信的發(fā)送。AT指令如下:
AT:用以與Modem的握手,返回OK則說明握手成功。
AT+CMGR=X:用以讀取第X條短信。
AT+CMGD=X:用以刪除第X條短信。
AT+CMGS=X:設(shè)置發(fā)送短信的字節(jié)數(shù)為X。
實(shí)際的AT指令集功能非常豐富,在應(yīng)用中也是非常重要,尤其在手機(jī)開發(fā)中更如此。在這里只是實(shí)現(xiàn)簡單的短信收發(fā),只涉及到以上幾條AT指令。讀者如果想進(jìn)行深入的了解,可以翻閱相應(yīng)的手冊。
短信的發(fā)送過程(以下過程中>代表發(fā)送,<代表接收)
通訊握手
>AT(回車 十六進(jìn)制的0x0d 0x0a)
<ok< />
收到OK,說明握手成功。
2. 設(shè)置短信字節(jié)數(shù)
>AT+CMGS=26(回車)
設(shè)置短信字節(jié)數(shù),由筆者的使用經(jīng)驗(yàn),后面的數(shù)值為短信內(nèi)容的字節(jié)數(shù)+14。
如上面的編碼中內(nèi)容為12個字節(jié),則后面的數(shù)值為12+14=26
3. 發(fā)送短信編碼
<>
收到>符號,說明貓正在等待接收短信編碼
>0891683108401505F011000B815148063011F40008A70C59275BB6597DFF01FF01FF01
發(fā)送短信編碼,送送完畢后,如果短信貓已經(jīng)接收,則啟動短信發(fā)送。
4.短信發(fā)送結(jié)果檢測
<+CMGS:45
<ok< />
在短信發(fā)送成功后,會返回“+CMGS”,后面的45,是表示第45條短信。如果沒有返回此字符串則說明發(fā)送失敗。
以上過程可以通過串口調(diào)試工具手工進(jìn)行,而這里需要實(shí)現(xiàn)的是單片機(jī)的自動發(fā)送。握手、設(shè)置短信字節(jié)數(shù)、發(fā)送短信編碼、發(fā)送結(jié)果檢測等都需要單片機(jī)來實(shí)現(xiàn)。
(5)單片機(jī)實(shí)現(xiàn)短信自動發(fā)送
進(jìn)行短信發(fā)送的前提是短信內(nèi)容的正確編碼。經(jīng)過以上對短信發(fā)送過程的分析,可以通過單片機(jī)對其進(jìn)行實(shí)現(xiàn)。下面是實(shí)現(xiàn)程序例程:
/*-------------------------------------------------------------------------
函數(shù)名:PDU_SMS()
功能 :發(fā)送短信
參數(shù)說明:SMS_Center為短信中心號碼 11位
SMS_Telenum為短信接收方的號碼 11位
SMS_Context為短信的內(nèi)容
--------------------------------------------------------------------------------*/
int PDU_SMS(char *SMS_Center, char *SMS_Telenum,
char *SMS_Context,char is_GB)
{
int i,j;
unsigned char len,time;
char lens[3];
time=0;
for(i=0;i<300;i++) PDU_Code[i]=PDU_t[i];
/*----------設(shè)置短信中心號碼--------------*/
for (i = 0, j = 0; i < strlen(SMS_Center) / 2; i++)
{
PDU_Code[6+(j++)] = SMS_Center[2 *i + 1];
PDU_Code[6+(j++)] = SMS_Center[2 *i];
}
PDU_Code[6+j++] = 'F'; //在最后補(bǔ)上的F
PDU_Code[6+j] = SMS_Center[strlen(SMS_Center) - 1];
/*---------------------------------------------*/
/*----------設(shè)置接收號碼--------------*/
for (i = 0, j = 0; i < strlen(SMS_Telenum) / 2; i++)
{
PDU_Code[26+(j++)] = SMS_Telenum[2 *i + 1];
PDU_Code[26+(j++)] = SMS_Telenum[2 *i];
}
PDU_Code[26+j++] = 'F';
PDU_Code[26+j] = SMS_Telenum[strlen(SMS_Telenum) - 1];
/*---------------------------------------------*/
/*----------設(shè)置短信內(nèi)容長度--------------*/
if(is_GB==0)
len = strlen(SMS_Context) *2;
else
len = strlen(SMS_Context);
PDU_Code[44] = (len >> 4) > 9 ? (len >> 4) + 55: (len >> 4) + 48;
PDU_Code[45] = (len &0x0f) > 9 ? (len &0x0f) + 55: (len &0x0f) + 48;
/*---------------------------------------------*/
/*----------編碼短信內(nèi)容--------------*/
if(is_GB==0)//如果不是GB碼,短信內(nèi)容為ascii碼字符串
{
for (i = 0,j=0; i<strlen(SMS_Context);i++)
{
szzh16(SMS_Context[j++],lens);
PDU_Code[46+i*4] = '0';
PDU_Code[46+i*4+1] = '0';
PDU_Code[46+i*4+2] = lens[0];
PDU_Code[46+i*4+3] = lens[1];
}
PDU_Code[46+i*4] = 0x1a;
PDU_Code[46+i*4+1] = 0xff;
}
else
//短信內(nèi)容為GB碼,如果要使手機(jī)能夠顯示,改到UNICODE編碼
{
for (i = 0,j=0; i<strlen(SMS_Context);i++)
{
szzh16((int)SMS_Context[j++],lens);
PDU_Code[46+i*2] = lens[0];
PDU_Code[46+i*2+1] = lens[1];
}
PDU_Code[46+i*2] = 0x1a;
PDU_Code[46+i*2+1] = 0xff;
}
/*---------------------------------------------*/
if(PDU_HandShake())
{
do
{
//LCD_PutChn(5,96,"SS...");
//if(is_GB)
PDU_EnablePDU();
if(is_GB==0)
PDU_SetLength(Strlen(SMS_Context)*2);
else
PDU_SetLength(Strlen(SMS_Context));
PDU_Send(PDU_Code);
//LCD_PutEng(5,96,"SS");
for(i=0;i<25;i++)
delay(20000);
sbuf[counter]=0;
//LCD_PutNum16(5,96,time);
counter=0;
time++;
}
//判斷是否發(fā)送成功,如果不成功繼續(xù)發(fā)送,最多4次,如仍不成功,返回0
while(strpos(sbuf+strlen(sbuf)-20,'G')==-1&&time<4);
if(strpos(sbuf+strlen(sbuf)-20,'G')==-1)
return 0;
else
return 1; //成功的話返回0
}
else
{
//LCD_PutEng(5,96,"LL");
sbuf[counter]=0;
//LCD_PutEng(0,0,sbuf);
counter=0;
return 0;
}
}
以上程序成功實(shí)現(xiàn)短信的發(fā)送,其中的一些函數(shù)限于篇幅可自行實(shí)現(xiàn)。
(6)單片機(jī)對短信的讀取與解碼
單片機(jī)可以通過AT指令對短信貓中的短信進(jìn)行讀取,并對讀入的短信數(shù)據(jù)進(jìn)行分析與解碼。
讀出的短信格式與發(fā)送時(shí)的短信編碼大致是相同的。下面給出相應(yīng)的程序例程,讀者可以在自行實(shí)驗(yàn)中對照驗(yàn)證。
讀取某一條短信,并將其進(jìn)行顯示
/*-------------------------------------------------------
函數(shù)名:LAD_SMS()(short for "Load And Display the Short MessageS")
功能:用戶函數(shù),讀取第n條短信,并在LCD的(x,y)位置顯示出來
作者:于振南
----------------------------------------------------------*/
unsigned char LAD_SMS(unsigned char n,unsigned char x,unsigned char y)
{
unsigned char i,len,t;
char temp[5];
char temp1[3];
//IN_Draw_BlankorBlackRect(0,20,30,72,0);
szzh10(n,temp1);
//將n轉(zhuǎn)為相應(yīng)的字符串,如n=21,則字符串為"21",用以與AT指令拼接。
t=85;
clear_sbuf();
counter=0;
send_s("AT+CMGR=");//AT+CMGR為讀取短信的AT指令
send_s(temp1);//上面所得的字符串
send(0x0d);
send(0x0a);
for(i=0;i<10;i++) delay(10000); //等待讀取完畢
sbuf[counter]=0; //在收到的數(shù)據(jù)末尾附加'\0'
temp[0]=sbuf[23];
<, SPAN style="mso-spacerun: yes"> temp[1]=sbuf[24];
temp[2]=sbuf[25];
temp[3]=0;
if(sbuf[25]!=0x0d) t++;
for(i=0;i<strlen(temp);i++) if(temp[i]==0x0d) temp[i]=0;
len=atoi(temp); //獲取收到的短信內(nèi)容長度
//LCD_PutEng(23,76,"(SM:");
//LCD_PutNum16(27,76,n);
//LCD_PutEng(29,76,")");
if(len==0)
{
//LCD_PutEng(5,96,"EMP");
//LCD_PutChn(x,y,">短信空");
delay(50000);
return 0;
}
len-=20;
if(len>90)
{
//LCD_PutEng(5,96,"MTL");
//LCD_PutChn(x,y,">短信太長");
delay(50000);
return 0;
}
for(i=0;i<len;i++)
{
temp[0]=sbuf[t+2*i];
temp[1]=sbuf[t+2*i+1];
temp[2]=0;
sbuf[i]=_hex_(temp);
//收到的短信內(nèi)容是內(nèi)存方式的顯示表示,轉(zhuǎn)為十六進(jìn)制數(shù)
}
sbuf[i]=0;
Analysis_Pro();//解碼后的內(nèi)容在sbuf中,此函數(shù)對其進(jìn)行顯示輸出
//LCD_PutEng(x,y,inf_bw.Date);
//LCD_PutEng(x,y,sbuf+29);
delay(50000);
clear_sbuf();
counter=0;
return 1;
}
檢測新短信
/*-------------------------------------------------------
函數(shù)名:Check_New()
功能:用戶函數(shù),檢測有無新的短信,如果有返回1,否則返回0
作者:于振南
----------------------------------------------------------*/
unsigned char Check_New()
{int i;
send_s("AT+CMGL=0"); //AT+CMGL=0為讀取新短信的AT指令
send(0x0d);
send(0x0a); //發(fā)送回車
delay(10000); //等待接收完畢
if(sbuf[12]=='O') return 0xff;
if(sbuf[12]=='+')
{
for(i=18;i<23;i++)
if(sbuf[i]==',') sbuf[i]=0;
return atoi(sbuf+19); //返回新短信的位置
}
}
刪除某條短信
/*-------------------------------------------------------
函數(shù)名:Delete()
功能:用戶函數(shù),刪除第n條短信
作者:于振南
----------------------------------------------------------*/
unsigned char Delete(unsigned char n)
{
char t[10];
char t1[5];
strcpy(t,"AT+CMGD="); //AT+CMGD為刪除短信的AT指令
szzh10(n,t1);
strcpy(t+8,t1);
while(Send_AT_CMD(t)!=1);
//LCD_PutEng(5,96,"SM");
//LCD_PutNum16(7,96,n);
//LCD_PutEng(10,96,"De");
delay(60000);
return 1;
}