1、模塊原理圖
電源部分:使用USB或者直流DC座供電;在使用一個電源模塊將5V轉為3.3V
UART部分,由於模塊使用的是串口,需要使用一個電平轉換芯片,方便我們使用電腦上位機直接測試
核心部分,zigbee封裝的芯片,使用串口與外部通信
2、模塊的特性:
1、配置正常啟動后,可以組成多跳網狀網絡;
2、網絡中的模塊之間可以相互發送數據;
3、串口參數:數據位8,起始位1,停止位1,無校驗。波特率可以修改;默認115200;
3、zigbee模塊傳輸的協議
1、Zigbee模塊是一種自組網多跳無線通信模塊。該模塊工作時,會與周圍的模塊自動組成一個無線多跳網絡,此網絡為 對等網絡,不需要中心節點。模塊無線頻率為2.4GHz~2.45GHz,屬於 全球免費的無線頻段。網絡包含以下可配置參數:
將多個Zigbee模塊配置成地址不相同,信道和網絡ID相同的狀態,模塊將組成一個網絡。
Zigbee模塊使用定向擴散協議尋找路由,這種路由算法會記錄網絡的狀態,每個節點平 均可記錄130個目標節點的路由,在網絡建立后傳輸速度和傳輸延時可到達最優。但這種算 法網絡建立較慢,在節點剛剛啟動時,網絡需要1~5分鍾的時間重新生成路由,在這段時間 內網絡使用洪泛路由進行數據通信,此時網絡的傳輸速度較慢。
2、網絡性能:
3、包分割:
在通信過程中,最常見的場合是單片機通過Uart告訴模塊這樣的信息:
“將數據 00 AE 13 33 發往地址為0003的模塊,目標端口為90,源端口為91。
” 對於單片機,需要將這些信息整理成一個包,通過Uart發給模塊: FE 08 91 90 03 00 00 AE 13 33 FF
遠程地址長度為2byte,使用小端模式進行傳輸,即先傳輸低8位,再傳輸高8位。
傳輸過程中如果遇到數據部分、地址或者端口號中出現FF,則使用FE FD來代替;
如果 出現FE,則用FE FC來代替。以免傳輸過程中出現的包頭和包尾,使接收方誤判斷。在傳輸 中這種替換稱為“轉義”。 包長度不會受到轉義的影響,例如發送的數據為09 FF時,替換為09 FE FD,但包頭中 的數據長度仍然按照2+4來計算,這樣,發送的包如下: FE 06 91 90 03 00 09 FE FD FF 雖然一共傳輸了7個字節,但包長為6。如果地址、端口號中出現了FF、FE也需要進行 轉義。
4、端口:
。端口號的取值范圍是0x00~0xFF,其中0x00~0x7F端口由模塊內部程序占用, 0x80~0xFF端口開放給Uart連接的MCU或者電腦
4、模塊測試的命令集合

1 讀取模塊命令: 查詢IP地址 Send: FE 05 90 21 00 00 01 FF Rec: FE 07 21 90 00 00 21 02 00 FF 讀取網絡ID Send: FE 05 90 21 00 00 02 FF Rec: FE 07 21 90 00 00 22 16 20 FF 讀取信道: Send: FE 05 90 21 00 00 03 FF Rec: FE 06 21 90 00 00 23 13 FF 讀取波特率: Send: FE 05 90 21 00 00 04 FF Rec: FE 06 21 90 00 00 24 08 FF 2 修改模塊的命令 修改IP地址: Send: FE 07 90 21 00 00 11 1F 00 FF Rec: FE 05 21 90 00 00 00 FF 修改網絡ID: Send: FE 07 90 21 00 00 12 91 19 FF Rec: FE 05 21 90 00 00 00 FF 修改信道: Send: FE 06 90 21 00 00 13 12 FF Rec: FE 05 21 90 00 00 00 FF 修改波特率: Send:FE 06 90 21 00 00 14 02 FF Rec:FE 05 21 90 00 00 00 FF 重啟包: Send:FE 05 90 21 00 00 10 FF Rec:無返回(模塊綠燈亮2S 左右) 注意:配置完模塊之后一定要發送重啟包,配置才會生效。 3 模塊間通信: 信道和網絡ID必須相同,IP地址必須不同 模塊0為66 4A,模塊1為99 4A Send: FE 08 91 90 04 00 00 AE 13 33 FF 模塊1的地址為04 00 修正為:FE 08 91 90 66 4A 00 AE 13 33 FF Send: FE 08 91 90 03 00 00 AE 13 33 FF 模塊0的地址為03 00
5、ZigBee協議分析
其中包頭、包尾格式固定,傳輸的數據區域可變,長度可變化;
如果傳輸FE、FF這兩個特殊字符需要使用轉義字符,轉義字符不引入數據長度的變化;
6、ZigBee軟件協議框架
7、軟件代碼分析
1、主程序中根據傳入的命令分別設置不同的模式

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <errno.h> #include "main.h" #define SETMODE 0 #define SENDMODE 1 #define RECMODE 2 extern int uart_init(void); extern int set_config(int fd ,int length,char *cmd[]); extern int send_data(int fd,int length,char *cmd[]); extern int rec_data(int fd); int para(int argc,char *argv[]) { if(argc < 2){ printf("argv is less\n"); printf("Usage: test_zigbee [type] [cmd]\n"); printf(" type: 0--set, 1--send, 2--recv\n"); return -1; } return 0; } int main(int argc, char *argv[]) { int fd=0; if(para(argc,argv)<0){ return -1; } fd = uart_init(); // 串口初始化 if(fd < 0){ return -1; } DPRINTF("atoi(argv[1]) is %d\n",atoi(argv[1])); switch(atoi(argv[1])){ case SETMODE: // 設置模式 DPRINTF("set_config\n"); set_config(fd,argc,argv); break; case SENDMODE: // 發送模式 DPRINTF("send_data\n"); send_data(fd,argc,argv); break; case RECMODE: // 接收模式 DPRINTF("rec_data one time\n"); rec_data(fd); break; default: break; } return 0; }
2、對串口初始化

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop); int uart_init(void) { int fd; char *uart3 = "/dev/ttySAC3"; // 串口設備節點 //打開串口 if((fd = open(uart3,O_RDWR|O_NOCTTY|O_NDELAY))<0){ printf("open %s is failed",uart3); return -1; } else{ set_opt(fd, 115200, 8, 'N', 1); printf("%s init success \r\n",uart3); } printf("uart_init success.\n"); return fd; } int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop) { struct termios newtio,oldtio; if ( tcgetattr( fd,&oldtio) != 0) { perror("SetupSerial 1"); return -1; } bzero( &newtio, sizeof( newtio ) ); newtio.c_cflag |= CLOCAL | CREAD; newtio.c_cflag &= ~CSIZE; switch( nBits ) { case 7: newtio.c_cflag |= CS7; break; case 8: newtio.c_cflag |= CS8; break; } switch( nEvent ) { case 'O': newtio.c_cflag |= PARENB; newtio.c_cflag |= PARODD; newtio.c_iflag |= (INPCK | ISTRIP); break; case 'E': newtio.c_iflag |= (INPCK | ISTRIP); newtio.c_cflag |= PARENB; newtio.c_cflag &= ~PARODD; break; case 'N': newtio.c_cflag &= ~PARENB; break; } switch( nSpeed ) { case 2400: cfsetispeed(&newtio, B2400); cfsetospeed(&newtio, B2400); break; case 4800: cfsetispeed(&newtio, B4800); cfsetospeed(&newtio, B4800); break; case 9600: cfsetispeed(&newtio, B9600); cfsetospeed(&newtio, B9600); break; case 115200: cfsetispeed(&newtio, B115200); cfsetospeed(&newtio, B115200); break; case 460800: cfsetispeed(&newtio, B460800); cfsetospeed(&newtio, B460800); break; default: cfsetispeed(&newtio, B9600); cfsetospeed(&newtio, B9600); break; } if( nStop == 1 ) newtio.c_cflag &= ~CSTOPB; else if ( nStop == 2 ) newtio.c_cflag |= CSTOPB; newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 0; tcflush(fd,TCIFLUSH); if((tcsetattr(fd,TCSANOW,&newtio))!=0) { perror("com set error"); return -1; } return 0; }
3、進入設置模式

#include "main.h" int set_config(int fd ,int length,char *cmd[]) { unsigned char buffer[128],rec_buffer[1]; int i,nByte=0,time=0,num,flag=0; int check_length=0; memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(rec_buffer)); DPRINTF("set_config setdata is : "); for(i = 0; i < length-2; i++){ buffer[i] = strtol(cmd[i+2], NULL, 16); check_length++; if(buffer[i] == 0xfe){ check_length--; } printf("0x%02x ",buffer[i]); } printf("\n"); DPRINTF("length uis %d and %d\n",buffer[1],check_length); //pak_length = pkg_length - num_fe - 2 if(buffer[1] == check_length - 2){ write(fd,buffer,i); } else{ printf("cmd length is err\n"); return -1; } check_length = 0; memset(buffer, 0, sizeof(buffer)); while(1){ while((nByte = read(fd, rec_buffer, 1))>0){ if(rec_buffer[0] == 0xfe && flag != 1){ num = 0; flag = 1; printf("set_config rev data is : "); } if(flag==1){ buffer[num] = rec_buffer[0]; printf("0x%02x ",rec_buffer[0]); check_length++; if(rec_buffer[0] == 0xfe) check_length--; } if(rec_buffer[0] == 0xff){ goto rec_over; } memset(rec_buffer, 0, sizeof(rec_buffer)); num++; nByte = 0; time = 0; } //if overtime 10ms , break. usleep(1); time++; if(time>10000){ DPRINTF("over time\n"); close(fd); break; } } close(fd); return 0; rec_over: printf("\n"); if(buffer[1] == check_length - 2){ DPRINTF("rec length check ok\n"); } else{ DPRINTF("rec length is err\n"); return -1; } close(fd); return 0; }
4、進入到發送數據

#include "main.h" int send_data(int fd,int length,char *cmd[]) { unsigned char buffer[128]; int i,time=0,num,flag=0; int check_length=0; memset(buffer, 0, sizeof(buffer)); printf("set_config setdata is : "); for(i = 0; i < length-2; i++){ buffer[i] = strtol(cmd[i+2], NULL, 16); check_length++; if(buffer[i] == 0xfe){ check_length--; } printf("0x%02x ",buffer[i]); } printf("\n"); DPRINTF("length is %d and %d\n",buffer[1],check_length); //pak_length = pkg_length - num_fe - 2 if(buffer[1] == check_length - 2){ write(fd,buffer,i); } else{ DPRINTF("cmd length is err\n"); return -1; } return 0; }
5、接收數據模式

#include "main.h" int rec_data(int fd) { unsigned char buffer[128],rec_buffer[1]; int nByte,num,flag=0; int check_length=0; char buf[128]; memset(rec_buffer, 0, sizeof(rec_buffer)); memset(buffer, 0, sizeof(buffer)); while(1){ while((nByte = read(fd, rec_buffer, 1))>0){ if(rec_buffer[0] == 0xfe && flag != 1){ num = 0; flag = 1; printf("set_config rev data is : "); } if(flag==1){ buffer[num] = rec_buffer[0]; printf("0x%02x ",rec_buffer[0]); printf("num is %d\n",num); if((num == 6) && (buffer[6] == 0x01)) { //system("echo 1 > /sys/class/gpio/gpio126/value"); system("./relay 1"); } else if((num == 6) && (buffer[6] == 0x02)) { //system("echo 0 > /sys/class/gpio/gpio126/value"); system("./relay 0"); } if((num == 6) && (buffer[6] == 0x03)) { system("./step_motor_app R 4076 3000"); } else if((num == 6) && (buffer[6] == 0x04)) { system("./step_motor_app L 4076 3000"); } check_length++; printf(" check_length is %d\n",check_length); if(rec_buffer[0] == 0xfe) check_length--; } if(rec_buffer[0] == 0xff){ //goto rec_over; num=0; flag=0; check_length=0; memset(buffer, 0, sizeof(buffer)); } memset(rec_buffer, 0, sizeof(rec_buffer)); num++; } usleep(100); } close(fd); return 0; rec_over: printf("\n"); if(buffer[1] == check_length - 2){ DPRINTF("rec length check ok\n"); } else{ DPRINTF("rec length is err\n"); return -1; } close(fd); return 0; }