1、系统技术简介
1.1 zigbee网络
无线传感器网络是集信息采集、信息传输、信息处理于一体的综合智能信息系统,具有低成本、低功耗、低数据速率、自组织网络等特点。而Zigbee技术是为低速率传感器和控制网络设计的标准无线网络协议栈,是最适合无线传感器网络的标准。Zigbee无线传感器网络是基于Zigbee技术的无线传感器网络。在许多行业有巨大的应用潜力,如环境监控、物流管理、医疗监控、交通管理和军事侦察等方面的应用。
目前普遍使用的无线传感器网络平台主要有Crossbow公司的Mica2/MicaZ和Microchip公司的PICDEMZ等。MicaZ虽然具有Tinyos操作系统,但是没有结合Zigbee技术;PICDEMZ的Zigbee协议栈不完全符合Zigbee的定义,而且功能简单。因此,设计一种Zigbee无线传感器网络平台,可以更好地开发无线传感器网络的应用和Zigbee技术。
本文在分析Zigbee无线传感器网络的特点和关键技术的基础上,提出Zigbee无线传感器网络平台的设计实现方案,并且使用该无线传感器网络平台进行了智能家居安防的实现的实验。结果表明,该平台实现了Zigbee无线传感器网络的基本功能,可以更好地开发Zigbee技术。
ZigBee无线通信技术作为一种介于无线标记技术和蓝牙之间双向无线通信技术,以其距离短、功耗少、速度慢、成本低、可靠性高的特点,正一步步进入无线通信领域的浪潮中。
该技术是基于IEEE批准的802.15.4无线标准研制开发的有关自组网、安全和应用软件方面的技术。ZigBee无线通信技术主要适合于对距离、功耗以及传输速率要求不高的各种电子设备之间进行数据传输的场景。
1.1.1ZigBee无线通信技术的优势
(1)功耗低。ZigBee无线通信技术的传输速率较低,且其发射功率仅为1mW,故采用ZigBee技术的设备非常省电。同时,ZigBee技术运行时,采用休眠模式,更有效的减少了功耗。
(2)成本低。含有ZigBee无线通信技术的模块,起始时成本在大约在6美元。不过随着ZigBee无线通信技术的不断普及,在未来几年,很有可能降到1.8美元左右。ZigBee无线通信协议免专利费也为ZigBee技术的广泛使用奠定基础。
(3)时延短。通信时延和从休眠状态激活的实验都非常短,故对实验要求较高的无线控制设备上面可以采用ZigBee无线通信技术。
(4)网络容量大。每个ZigBee网络最多可以容纳的设备数量有255个。网络容量大且网络组成较为灵活为ZigBee技术的进一步推广做了铺垫。
1.1.2ZigBee无线通信技术的应用场景
ZigBee无线通信技术起初是希望发展一种易于建构的低成本无线网络。在其发展的初期,ZigBee无线通信技术主要以工业或企业市场为主,致力于工业或企业的感应式网络,如提供感应辨识、灯光与安全控制等功能。随后,将逐渐把市场拓展至家庭应用,给整个社会提供便利。
采用ZigBee无线通信技术的应用场景如下,通常满足其中之一即可:
(1)设备预算少,成本低,数据传输量小;
(2)设备整体体积小,无法放置较大的模块;
(3)只能使用一次性电池,不能进行足够的电力支持;
(4)频繁更换电池或者反复充电难度较大,不易实现;
(5)在通信时需要较大范围的通信覆盖,但网络中仅用于监测的设备繁多。
1.2ARM架构简介
ARM的处理器在智能手机和与平板电脑的地位,如同如同Intel之于PC。ARM公司本身不进行芯片的生产,而只是提供生产、芯片架构设计以及核心架构等等授权。在ARM的Cortex系列处理器之前,ARM公司处理器的命名从ARM1开始一直延续到了ARM11。所以我们常听到“ARM的ARM11采用了ARMV6架构“之类的绕口令,第一个ARM表示的是ARM公司,第二个ARM11表示的处理器型号,第三个ARMV6表示的处理器架构。也许是为了规避这绕口的说法,ARM公司自ARM11之后的处理器就改名叫Cortex。Cortex:大脑皮层的意思。目前Cortex的处理器主要分为三大系列:
1.Cortex-A系列,针对终端应用,手机与PC等,比如A8应用于IPHONE4。Cortex-A系列面向尖端的基于虚拟内存的操作系统和用户应用。
2.Cortex-R系列,应用在实时控制领域,比如硬盘控制、引擎管理、基频的实时处理器核心Cortex-R系列
3.Cortex- M系列,针对成本和功耗敏感的MCU和终端应用,如人机接口设备、工业控制系统和医疗器械。Cortex- M系重点针对微控制器和低成本应用提供了优化。Cortex-M系列也可以跑操作系统,不过得要那种最简单的不带虚拟内存的。
1.3linux系统简介
1.3.1Linux系统介绍
Linux内核最初只是由芬兰人李纳斯•托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的。
Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。
Linux能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。
1.3.2Linux的发行版
Linux的发行版说简单点就是将Linux内核与应用软件做一个打包。
目前市面上较知名的发行版有:Ubuntu、RedHat、CentOS、Debian、Fedora、SuSE、OpenSUSE、Arch Linux、SolusOS 等。
1.3.3Linux应用领域
今天各种场合都有使用各种Linux发行版,从嵌入式设备到超级计算机,并且在服务器领域确定了地位,通常服务器使用LAMP(Linux + Apache + MySQL + PHP)或LNMP(Linux + Nginx+ MySQL + PHP)组合。
目前Linux不仅在家庭与企业中使用,并且在政府中也很受欢迎。
(1) 巴西联邦政府由于支持Linux而世界闻名。
(2) 有新闻报道俄罗斯军队自己制造的Linux发布版的,做为G.H.ost项目已经取得成果.
(3) 印度的Kerala联邦计划在向全联邦的高中推广使用Linux。
(4) 中华人民共和国为取得技术独立,在龙芯过程中排他性地使用Linux。
(5) 在西班牙的一些地区开发了自己的Linux发布版,并且在政府与教育领域广泛使用,如Extremadura地区的gnuLinEx和Andalusia地区的Guadalinex。
(6) 葡萄牙同样使用自己的Linux发布版Caixa Mágica,用于Magalh?es笔记本电脑和e-escola政府软件。
(7) 法国和德国同样开始逐步采用Linux。
本实验采用的Linux发行版本为服务器:CentOS7.3与网关:Ubuntu16.04
2、需求分析
2.1需求概述
本系统结合Zigbee无线传感网络与Linux web服务器实现用户对家中的安全进行必要的检测,Linux服务器可依据相应的传感器信息,及时的做出响应,在本系统中Linux服务器主机,将扮演家居安防管家的角色,而用户只要连接上服务器,就可对家居的安全进行检测,并做出相应的反应,可减少因家居安全不到位与发现不及时而造成的损失。
2.2系统结构信息
2.2.1传感器节点
依据家庭安防的需求,使用各种传感器进行实时监控,提高效率。可以查看的传感器信息有:
(1) 温湿度传感器
温湿度传感器可检测家中的温湿度,一旦超出了一定的范围,服务器便会操作继电器控制电机,驱动风扇等控制家中的温湿度,并向用户发送相应的提示信息。
(2)光照强度传感器
光照传感器可检测光强的强度,一旦光强超标,服务器便会像用户发去相应的提示信息,以便用户做出相应的措施。
(3)烟雾传感器
烟雾传感器可检测家中的烟雾中气体含量,一旦超出了一定的范围,服务器便会操作继电器控制电机,驱动风扇等控制气体含量,并向用户发送相应的提示信息。
(4)火焰传感器
火焰传感器可在预防突发事件时,达到可观的预防效果,比如预防家中火灾并由服务器想用户发送相应的提示信息等,达到减少经济损失的功能。
(5)继电器
服务器依据收到的传感器数值,检测是否数值超标,并作出响应继而控制继电器发动电动机,并可依据电机转动时间的多少来达到控制细节。
2.2.2网关Nanopc-t3
Nanopc-t3作为网关,接收协调器传过来的串口信息通过TCP的socket服务发送给处理数据的服务器。
2.2.3Linux服务器
服务器从nanopct3处接收到传感器信息,并对相应的信息进行解析,对传感器状态进行判断,并做出相应的响应,并向用户发送提示信息。
3、系统设计
系统主框架
3.1系统结构设计
3.1.1Zigbee终端设计
Zigbee终端传感器检测环境数据,并通过zigbee无线网络发送到协调器处,也可接收协调器处传来的控制信息(主要体现在继电器的控制上)。
3.1.1.1zigbee终端的数据封装
Zigbee终端的数据封装格式如下:
一位数据开始位 :!(“!”标识数据头)
一位传感器类型 :1.2.3……(标识相应的终端类型)
十三位数据位 :xxxxx(传感器的数据,如有余则为空即可)
一位状态位 :1或0(标识传感器的状态)
3.1.2协调器设计
协调器负责的功能有下列几项:
1、接收节点信息
2、接收nanopc-t3串口命令
3、发送控制命令
3.1.3nanopct3网关设计
nanopct3网关负责的功能有下列几项:
1、接受协调器传来的串口数据
2、发送串口数据至服务器
3、接收服务器命令
4、通过串口发送服务器命令至协调器
3.1.4用户客户端设计
用户客户端主要的功能有下列几项:
1、用户数量:8位
2、可查询对应节点的信息
3、可查询已有节点信息
4、接收服务器发送的警报信息
3.2数据库结构设计
3.2.1数据表设计
3.2.1.1类型表(category)
数据项 | 存储结构 | 默认 |
---|---|---|
id | int(4) | PRIMARY KEY |
cname | varchar(20) |
3.2.1.2 数据表(datas)
数据项 | 存储结构 | 默认 |
---|---|---|
Did | int(4) | |
Dstate | tinyint(1) | |
Sensor | varchar(30) | |
Dtime | datetime |
3.2.1.3状态表(state)
数据项 | 存储结构 | 默认 |
---|---|---|
Sid | int(4) | PRIMARY KEY |
state | tinyint(1) | PRIMARY KEY |
4、系统实现
4.1Zigbee终端实现
4.1.1温湿度传感器
本实验使用的温湿度传感器件是AM2321,所以该器件的具体实现如下:
在Zigbee网络中发送函数GenericApp_SendTheMessage处理如下:
4.1.2光照强度传感器
本实验使用的温湿度传感器件是002E,所以该器件的具体实现如下:
在Zigbee网络中发送函数GenericApp_SendTheMessage处理如下:
端口初始化如下:
4.1.3烟雾传感器
本实验使用的温湿度传感器件是002E,所以该器件的具体实现如下:
在Zigbee网络中发送函数GenericApp_SendTheMessage处理如下:
端口初始化如下:
4.1.4火焰传感器实现
由于本实验实现时,难以用到火焰检测,故用按键模拟火焰传感器的整个过程。
注册火焰按键事件:
模拟事件发生:
端口初始化:
4.1.5继电器实现
继电器初始化配置:
继电器接收相应的处理命令,做出相应处理:
4.2Zigbee协调器实现
4.2.1协调器的接收处理
将接收的信息发送给nanopxt3网关:
4.2.2协调器的发送处理
将从nanopct3串口(通过串口回调函数)接收到的信息发送出去:
4.3nanopct3网关实现
Nanopct3网关的具体实现如下(通过socket与服务器建立网络连接,串口与协调器建立连接):
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /*串口头文件*/
#include
#include
#include /*socket头文件*/
#define BUFFER_SIZE 1024 /*文件大小*/
static int ret;
static int fd;
#define BAUD_RATE_115200 115200 //波特率为 115200
#define BAUD_RATE_19200 19200 //波特率为 19200
#define DATA_BITS_8 8 //数据位为 8
#define STOP_BITS_1 1 //停止位为 1
#define PARITY_N 'N' //奇偶校验位 无
/*************************************************************
* * 函数名: safe_write(安全写函数)
* * 功 能: 安全写入,如果被中断,则会重新开始写入
* * 参 数: @int fd: 将要读取数据的文件描述词。
* * @const void *vptr: 要写入的字符串
* * @size_t n: 写入的长度
* * 返回值: n : 写入的长度
* * -1: 写入出错,(常见原因:磁盘空间满了或者超过了文件大小限制)
* *************************************************************/
ssize_t safe_write(int fd, const void *vptr, size_t n)
{
size_t nleft; //要写入的长度
ssize_t nwritten; //获取write()返回,接收写入成功的位数
const char *ptr; //要写入的数据
ptr = vptr; nleft = n;
while(nleft > 0)
{
if((nwritten = write(fd, ptr, nleft)) <= 0) //write()写入成功时,会返回写入的数量;否则,返回-1
{
if(nwritten < 0 && errno == EINTR) //如果出错,则写入的数据nwritten为0。(errno 由系统保存的最新的错误值, EINTR 系统中断调用)
nwritten = 0;
else
return -1;
}
nleft -= nwritten; //减去已经写入的长度
ptr += nwritten; //更新指针指向下次要写入的部分
} return(n); //写入完成,则返回写入长度
}
/*************************************************************
* * 函数名: uart_write(串口写函数)
* * 功 能: 串口写入数据
* * 参 数: @int fd: 将要读取数据的文件描述词。
* * @char *w_buf: 要写入的字符串
* * @size_t len: 写入的长度
* * 返回值: n : 写入的长度
* * -1: 写入出错,(常见原因:磁盘空间满了或者超过了文件大小限制)
* *************************************************************/
int uart_write(int fd,const char *w_buf,size_t len)
{
ssize_t cnt = 0;
cnt = safe_write(fd,w_buf,len); //调用safe_write()函数,成功写入时,则会返回写入数据的长度。否则,返回-1
if(cnt == -1)
{
fprintf(stderr,"write error!\n"); //写入出错的信息反馈
return -1;
}
return cnt;
}
/*************************************************************
* * 函数名: safe_read(安全读函数)
* * 功 能: 安全读取,如果被系统中断,则重新读取
* * 参 数: @int fd: 将要读取数据的文件描述词
* * @const void *vptr: 存放读取到的数据的缓冲区
* * @size_t n: 读取的长度
* * 返回值: n : 读取到的长度
* * -1: 读取出错
* *************************************************************/
ssize_t safe_read(int fd,void *vptr,size_t n)
{
size_t nleft; //读取的长度
ssize_t nread; //读取成功返回的长度;失败则返回-1
char *ptr; //读取的数据所存放的缓冲区
ptr=vptr;
nleft=n;
while(nleft > 0)
{
if((nread = read(fd,ptr,nleft)) < 0)
{
if(errno == EINTR) //被信号中断,则读取不成功
nread = 0;
else
return -1;
} else if(nread == 0)
break;
nleft -= nread; //剩下还要读取的数据长度
ptr += nread; //更新下次要读取的起始位置
} return (n-nleft);
}
/*************************************************************
* * 函数名: uart_read(串口读函数)
* * 功 能: 串口读取数据
* * 参 数: @int fd: 将要读取数据的文件描述词
* * @char *r_buf: 存放读取到的数据的缓冲区
* * @size_t n: 读取的长度
* * 返回值: n : 读取到的长度
* * -1: 读取出错
* *************************************************************/
int uart_read(int fd,char *r_buf,size_t len)
{
ssize_t cnt = 0;
fd_set rfds; //定义一个文件描述符集合的结构体
struct timeval time; //时间结构体 两个成员变量:@__time_t tv_sec 秒 @__suseconds_t tv_usec 微妙 /*将文件描述符加入读描述符集合*/
FD_ZERO(&rfds); //将rfds清零,集合不包含任何fd
FD_SET(fd,&rfds); //将fd加入set集合 /*设置超时为30s*/
time.tv_sec = 30;
time.tv_usec = 0; /*实现串口的多路I/O*/
ret = select(fd+1,&rfds,NULL,NULL,&time);
switch(ret)
{
case -1:
fprintf(stderr,"select error!\n");
return -1;
case 0:
fprintf(stderr,"time over!\n");
return -1;
default:
cnt = safe_read(fd,r_buf,len); //调用安全读取函数safe_read(),返回成功读取数据的长度
if(cnt == -1)
{
fprintf(stderr,"read error!\n");
return -1;
}
return cnt;
}
}
/*************************************************************
* * 函数名: uart_open(串口打开文件函数)
* * 功 能: 打开串口对应的设备驱动文件
* * 参 数: @int fd: 将要读取数据的文件描述词
* * @char *pathname: 文件路径
* * 返回值: fd : 文件描述符
* * -1 : 串口文件打开出错
* *************************************************************/
int uart_open(int fd,const char *pathname)
{
assert(pathname); /*打开串口 设置文件权限参数: O_RDWR 读写方式 O_NOCTTY 若文件为终端,那么该终端不会成为调用open()的那个进程的控制终端 O_NONBLOCK 使I/O编程非阻塞模式,在读取不到数据或写入缓冲区已满会马上return,不会阻塞 */
fd = open(pathname,O_RDWR|O_NOCTTY|O_NONBLOCK);
if(fd == -1)
{
perror("Open UART failed!");
return -1;
} /*清除串口非阻塞标志*/
if(fcntl(fd,F_SETFL,0) < 0) //指定文件描述的各种操作,(F_SETFL : 设置文件状态标识)
{
fprintf(stderr,"fcntl failed!\n");
return -1;
}
return fd;
}
/*************************************************************
* * 函数名: uart_set(串口配置函数)
* * 功 能: 配置串口的波特率、数据位、停止位、奇偶校验位、流控
* * 参 数: @int serial_fd: 串口文件描述符
* * @int nSpeed: 波特率
* * @int nBits: 数据位
* * @char nEvent: 奇偶校验位
* * @int nStop: 停止位
* * 返回值: fd : 文件描述符
* * -1 : 串口文件打开出错
* *************************************************************/
int uart_set(int serial_fd, int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newtio,oldtio; // /*保存测试现有串口参数设置,在这里如果串口号等出错,会有相关的出错信息*/
if( tcgetattr( serial_fd,&oldtio) != 0)
{
printf("[FALSE] Funtion tcgetattr( serial_fd,&oldtio) -> %d\n",tcgetattr( serial_fd,&oldtio));
return -1;
} /*结构体初始化为0*/
bzero( &newtio, sizeof( newtio ) ); /*设置字符大小,关流控 CLOCAL: 忽略所有调制解调器的状态行 CREAD: 启用字符接收器 CRTSCTS:启动流控 CSIZE: 启用字符大小位 */
newtio.c_cflag |= CLOCAL | CREAD ; //设置newtio的控制模式c_cflag,启动字符接收器和忽略所有调制解调器的状态行
newtio.c_cflag &= ~CSIZE; //关闭字符大小位
newtio.c_cflag &= ~CRTSCTS; //关闭流控 /*设置数据位*/
switch( nBits )
{
case 7:
newtio.c_cflag |= CS7;
printf(" Set nBits 7!\n");
break;
case 8:
newtio.c_cflag |= CS8;
printf(" Set nBits 8!\n");
break; default:
newtio.c_cflag |= CS8;
printf(" Set nBits 8!\n");
break;
} /*设置奇偶校验位*/
switch( nEvent )
{
case 'o':
case 'O': //奇数
newtio.c_cflag |= PARENB; //启动奇偶校验
newtio.c_cflag |= PARODD; //只使用奇校验
printf(" Set nEvent O!\n");
break;
case 'e':
case 'E': //偶数
newtio.c_cflag |= PARENB; //启动奇偶校验
newtio.c_cflag &= ~PARODD; //只使用偶校验
printf(" Set nEvent E!\n");
break;
case 'n':
case 'N': //无奇偶校验位
newtio.c_cflag &= ~PARENB;
printf(" Set nEvent N!\n");
break;
default:
newtio.c_cflag &= ~PARENB;
printf(" Set nEvent N!\n");
break;
} /*设置波特率*/
switch( nSpeed )
{
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
printf(" Set nSpeed 2400!\n");
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
printf(" Set nSpeed 4800!\n");
break;
case 19200:
cfsetispeed(&newtio, B19200);
cfsetospeed(&newtio, B19200);
printf(" Set nSpeed 9600!\n");
break;
case 57600:
cfsetispeed(&newtio, B57600);
cfsetospeed(&newtio, B57600);
printf(" Set nSpeed 57600!\n");
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
printf(" Set nSpeed 115200!\n");
break;
default:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
printf(" Set nSpeed 115200!\n");
break;
}
/*设置停止位 CSTOPB:10B */
if( nStop == 1 )
{
newtio.c_cflag &= ~CSTOPB;
printf(" Set nStop 1!\n");
}
else if ( nStop == 2 )
{
newtio.c_cflag |= CSTOPB;
printf(" Set nStop 2!\n");
}else
printf("[FLASE] Set nStop erroe!\n"); /*设置等待时间VTIME和最小接收字符VMIN*/
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
printf(" Set VTIME=0,VMIN=0 !\n"); /*处理未接收字符*/
tcflush(serial_fd,TCIFLUSH);
printf(" Flush char of not handle!\n"); /*激活新配置TCSANOW立刻修改*/
if((tcsetattr(serial_fd,TCSANOW,&newtio))!=0)
{
perror("[FLASE] Set newtio!\n");
return -1;
}else
printf(" Set newtio!\n");
return 0;
}
/*************************************************************
* * 函数名: uart_close(串口关闭函数)
* * 功 能: 关闭串口
* * 参 数: @int fd: 串口文件描述符
* * 返回值: 0 : 关闭成功
* *************************************************************/
int uart_close(int fd)
{
assert(fd);
close(fd); /*可以在这里做些清理工作*/
return 0;
}
int main(void)
{
const char w_buf[128] = {"T3:Hello!\n"}; //写入缓冲区
size_t w_len = strlen(w_buf);
char r_buf[BUFFER_SIZE]; //读取缓冲区
bzero(r_buf,BUFFER_SIZE); //初始化为0 //打开串口对应的设备驱动文件
fd = uart_open(fd,"/dev/ttySAC2");/*串口驱动文件 */
if(fd == -1)
{
fprintf(stderr,"uart_open error\n");
exit(EXIT_FAILURE);
} //配置串口参数:波特率:115200 数据位:8位 奇偶校验位:无 停止位:1位
if(uart_set(fd,BAUD_RATE_115200, DATA_BITS_8, PARITY_N, STOP_BITS_1) == -1)
{
fprintf(stderr,"uart set failed!\n");
exit(EXIT_FAILURE);
} //串口写入信息 T3:Hello!
/*socket配置*/
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(11332);
server_addr.sin_addr.s_addr = inet_addr("192.168.8.137");
bzero(&(server_addr.sin_zero), 8);
/*创建socket套接字*/
int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if(server_sock_fd == -1)
{
perror("socket error");
return 1;
}
char recv_msg[BUFFER_SIZE];
char input_msg[BUFFER_SIZE] = "Hallo server!";
ret = uart_write(fd,w_buf,w_len);
if(ret == -1)
{
fprintf(stderr,"uart write failed!\n");
exit(EXIT_FAILURE);
}
if(connect(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) == 0)
{
fd_set client_fd_set;/*新建文件集*/
struct timeval tv;
while(1)
{
tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&client_fd_set);
FD_SET(server_sock_fd, &client_fd_set);
select(server_sock_fd + 1, &client_fd_set, NULL, NULL, &tv);/*select检测*/
bzero(r_buf, BUFFER_SIZE);
if(ret = uart_read(fd,r_buf,1024) == -1)
{
fprintf(stderr,"uart read failed!\n");
exit(EXIT_FAILURE);
}
printf("uart: %s\n",r_buf);
if(send(server_sock_fd,r_buf, 16 , 0) == -1)
{
perror("发送消息出错!\n");
}
printf("send seccessful!!\n");
if(FD_ISSET(server_sock_fd, &client_fd_set))
{
bzero(recv_msg, BUFFER_SIZE);
long byte_num = recv(server_sock_fd, recv_msg, BUFFER_SIZE, 0);
if(byte_num > 0)
{
if(byte_num > BUFFER_SIZE)
{
byte_num = BUFFER_SIZE;
}
recv_msg[byte_num] = '\0';
printf("服务器信息:%s\n", recv_msg);
if(recv_msg[0] == '1')
{
/*发送给协调器*/
uart_write(fd,"1",1);
}
}
else
if(byte_num < 0)
{
printf("接受消息出错!\n");
}
else
{
printf("服务器端退出!\n");
exit(0);
}
}
}
}
//关闭文件
ret = uart_close(fd);
if(ret == -1)
{
fprintf(stderr,"uart_close error\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
4.4Linux服务器实现
服务器负责将从网关接收到的数据进行解析,并做出相应的处理与发送报警信息给用户:
#include
#include
#include
#include
#include
#include
#include
#include
#define BACKLOG 5 /*完成三次握手但没有accept的队列的长度 */
#define CONCURRENT_MAX 8 /*应用层同时可以处理的连接 */
#define SERVER_PORT 11332
#define BUFFER_SIZE 1024
#define QUIT_CMD ".quit"
#define HOST "localhost"/*数据库*/
#define USERNAME "root"
#define PASSWORD "lzx19950822"
#define DATABASE "zigbee"
int client_fds[CONCURRENT_MAX];
int main(int argc, const char * argv[])
{
int i,num;
char input_msg[BUFFER_SIZE];
char recv_msg[BUFFER_SIZE];
MYSQL mysqlfd;/*数据库*/
mysql_init(&mysqlfd);
char *sql;
int seccess;
char strr[200];/*sprintf函数*/
char temp[13],smoke[7],lux[7],flam[13];
/*本地地址 */
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;//inet_addr("192.168.8.130");
bzero(&(server_addr.sin_zero), 8);
/*创建socket */
int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if(server_sock_fd == -1)
{
perror("socket error");
return 1;
}
/*绑定socket */
int bind_result = bind(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if(bind_result == -1)
{
perror("bind error");
return 1;
}
/*listen */
if(listen(server_sock_fd, BACKLOG) == -1)
{
perror("listen error");
return 1;
}
/*fd_set */
fd_set server_fd_set;
int max_fd = -1;
struct timeval tv; /*超时时间设置 */
while(1)
{
tv.tv_sec = 20;
tv.tv_usec = 0;
FD_ZERO(&server_fd_set);
FD_SET(STDIN_FILENO, &server_fd_set);
if(max_fd 0)
{
int index = -1;
for(i = 0; i < CONCURRENT_MAX; i++)
{
if(client_fds[i] == 0)
{
index = i;
client_fds[i] = client_sock_fd;
break;
}
}
if(index >= 0) /*连接成功*/
{
printf("新客户端(%d)加入成功 %s:%d\n", index, inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port));
}
else
{
bzero(input_msg, BUFFER_SIZE);
strcpy(input_msg, "服务器加入的客户端数达到最大值,无法加入!\n");
send(client_sock_fd, input_msg, BUFFER_SIZE, 0);
printf("客户端连接数达到最大值,新客户端加入失败 %s:%d\n", inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port));
}
}
}
for( i =0; i < CONCURRENT_MAX; i++)
{
if(client_fds[i] !=0)
{
if(FD_ISSET(client_fds[i], &server_fd_set))
{
if(fork() == 0)
{
/*处理某个客户端过来的消息 */
bzero(recv_msg, BUFFER_SIZE);
long byte_num = recv(client_fds[i], recv_msg, BUFFER_SIZE, 0);
if (byte_num > 0)
{
if(byte_num > BUFFER_SIZE)
{
byte_num = BUFFER_SIZE;
}
recv_msg[byte_num] = '\0';
printf("客户端(%d):%s\n", i, recv_msg);
//连接数据库
if(mysql_real_connect(&mysqlfd,HOST,USERNAME,PASSWORD,DATABASE,0,NULL,CLIENT_FOUND_ROWS))
{
printf("!!!connect mysql success!\n");
if(recv_msg[0] == '!')/*数据判断与写入*/
{
if(recv_msg[1] == '1')/*温湿度传感器*/
{
for(num = 0;num < 13;num++)
{
temp[num] = recv_msg[num + 2];
}
sprintf(strr, "insert into datas values('%c','%c','%s',NOW());",recv_msg[1],recv_msg[15],temp);
if(temp[0] > '8')
{
for( i = 0; i < CONCURRENT_MAX; i++)
{
if(client_fds[i] != 0)
{
printf("infor client_fds[%d]=%d\n", i, client_fds[i]);
send(client_fds[i],"WARING!! HUMIDITY 80% HIGHT!!",29, 0);
}
}
}
}
if(recv_msg[1] == '2')/*烟雾传感器*/
{
for(num = 0;num < 8;num++)
{
smoke[num] = recv_msg[num + 2];
}
sprintf(strr, "insert into datas values('%c','%c','%s',NOW());",recv_msg[1],recv_msg[15],smoke);
if(smoke[0] != 0)
{
for( i = 0; i < CONCURRENT_MAX; i++)
{
if(client_fds[i] != 0)
{
printf("infor client_fds[%d]=%d\n", i, client_fds[i]);
send(client_fds[i],"WARING!! SMOKE HIGHT!!",22, 0);
}
}
}
}
if(recv_msg[1] == '3')/*光照传感器*/
{
for(num = 0;num < 8;num++)
{
lux[num] = recv_msg[num + 2];
}
sprintf(strr, "insert into datas values('%c','%c','%s',NOW());",recv_msg[1],recv_msg[15],lux);
if(lux[0] != 0)
{
for( i = 0; i < CONCURRENT_MAX; i++)
{
if(client_fds[i] != 0)
{
printf("infor client_fds[%d]=%d\n", i, client_fds[i]);
send(client_fds[i],"WARING!! Lux HIGHT!!",20, 0);
}
}
}
}
if(recv_msg[1] == '4')/*火焰传感器*/
{
for(num = 0;num < 13;num++)
{
flam[num] = recv_msg[num + 2];
}
sprintf(strr, "insert into datas values('%c','%c','%s',NOW());",recv_msg[1],recv_msg[15],flam);
if(recv_msg[15] == '1')
{
for( i = 0; i < CONCURRENT_MAX; i++)
{
if(client_fds[i] != 0)
{
printf("Flam Beeping!!\n");
send(client_fds[i],"1Flam Beeping!!",15,0);
}
}
}
}
}
sql=strr;
mysql_query(&mysqlfd , "set names utf8");/*设置编码,防止中文乱码*/
seccess=mysql_query(&mysqlfd,sql);/*写入数据库*/
if(seccess)
{
printf("!!!Insert Error!\n");
seccess=mysql_query(&mysqlfd,sql);
}
else
{
printf("!!!Insert Success!\n");
}
}
else
{
printf("!!!Connect mysql Failed!\n");
exit(-1);
}
}
else if(byte_num < 0) /*退出连接*/
{
printf("从客户端(%d)接受消息出错.\n", i);
}
else
{
FD_CLR(client_fds[i], &server_fd_set);
client_fds[i] = 0;
printf("客户端(%d)退出了\n", i);
}
}
}
}
}
}
}
return 0;
}
4.5客户端的实现
客户端可查询传感器节点信息与传感信息,并可接收服务器的警报:
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 1024
int main()
{
int sccess = 0;
int cmd;
MYSQL mysql;
MYSQL * mysqlconnect = NULL;
const char * query;
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(11332);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
bzero(&(server_addr.sin_zero), 8);
int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if(server_sock_fd == -1)
{
perror("socket error");
return 1;
}
char recv_msg[BUFFER_SIZE];
char input_msg[BUFFER_SIZE];
mysqlconnect = mysql_init(&mysql);
if(mysqlconnect == NULL){
sccess = mysql_errno(&mysql);
printf("mysql_init error, %s\n", mysql_error(&mysql));
return sccess;
}
printf("mysql_init ok...\n");
mysqlconnect = mysql_real_connect(mysqlconnect, "localhost", "root", "lzx19950822", "zigbee", 0, NULL, CLIENT_FOUND_ROWS);
if(mysqlconnect == NULL){
sccess = mysql_errno(&mysql);
printf("mysql_real_connect error, err is: %s\n", mysql_error(&mysql));
return sccess;
}
printf("mysql_real_connect ok...\n");
if(connect(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) == 0)
{
fd_set client_fd_set;
struct timeval tv;
while(1)
{
tv.tv_sec = 20;
tv.tv_usec = 0;
FD_ZERO(&client_fd_set);
FD_SET(server_sock_fd, &client_fd_set);
/*数据库查询*/
printf("*******************************************\n");
printf("* 1.查询温湿度传感器数据 *\n");
printf("* 2.查询烟雾传感器数据 *\n");
printf("* 3.查询光强传感器数据 *\n");
printf("* 4.查询火焰传感器状态 *\n");
printf("* 5.查询全部传感器状态数据 *\n");
printf("* 6.查询已拥有的传感器 *\n");
printf("*******************************************\n");
printf("input:");
scanf("%d",&cmd);
//fgets(cmd,1, stdin);
if(cmd == 1)
query = "select * from datas where Did=1";
if(cmd == 2)
query = "select * from datas where Did=2";
if(cmd == 3)
query = "select * from datas where Did=3";
if(cmd == 4)
query = "select * from datas where Did=4";
if(cmd == 5)
query = "select * from datas";
if(cmd == 6)
query = "select * from category";
sccess = mysql_query(mysqlconnect, query);
if(sccess != 0){
printf("mysql_query error\n");
return sccess;
}
MYSQL_RES *result = mysql_store_result(&mysql);
if(result == NULL){
printf("mysql_store_result error\n");
return -1;
}
int field_num = mysql_field_count(&mysql);
/*表头 */
MYSQL_FIELD * fields = mysql_fetch_fields(result);
int i = 0;
printf("------------------------------------------\n");
for(i= 0; i < field_num; i++){
printf("%s \t", fields[i].name);
}
printf("\n------------------------------------------\n");
/*表内容 */
MYSQL_ROW row = NULL;
while(row = mysql_fetch_row(result)){
for(i= 0; i < field_num; i++){
printf("%s \t", row[i]);
}
printf("\n");
}
mysql_free_result(result);//释放内存
printf("\n------------------------------------------\n");
select(server_sock_fd + 1, &client_fd_set, NULL, NULL, &tv);
if(FD_ISSET(server_sock_fd, &client_fd_set))
{
bzero(recv_msg, BUFFER_SIZE);
long byte_num = recv(server_sock_fd, recv_msg, BUFFER_SIZE, 0);
if(byte_num > 0)
{
if(byte_num > BUFFER_SIZE)
{
byte_num = BUFFER_SIZE;
}
recv_msg[byte_num] = '\0';
printf("服务器:%s\n", recv_msg);
}
else if(byte_num < 0)
{
printf("接受消息出错!\n");
}
else
{
printf("服务器端退出!\n");
exit(0);
}
}
}
}
mysql_close(mysqlconnect);
printf("mysql_close...\n");
return sccess;
}
5、总结
本系统使用当前流行的ARM Cortex-A53架构芯片S5P6818,并移植功能强大的linux操作系统,实现zigbee网关。为将zigbee无线传感器网络数据传输到互联网提供了一套可行的解决方案。结果表明,此网关系统稳定性高,并具备数据错误检测和纠正能力。由于将传感器数据存储到数据库中,数据的查询和分析都变得非常简单,提高了数据的利用率。