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; }