要求打通这样的一条通讯链路:传感器 ⇌ ZigBee网络 ⇌ ARM网关 ⇌ 服务器 ⇌ Internet网络 ⇌ 用户。存档并分享源代码。
文章目录
01-设计篇
最后效果: Client打开应用程序(C/S模式),发送命令请求数据,Server服务多个Client,接到Client命令然后封装发送给ARM网关,ARM网关负责与ZigBee协调器串口通讯转发命令,ZigBee协调器根据命令进行广播/单播请求数据,ZigBee终端收到数据包后采集数据,原路返回直到给Client显示。
传感器 ⇌ ZigBee网络
使用ZigBee协议栈进行数据采集和传输,实际上是ZigBee终端(带传感器)与ZigBee协调器的通讯,终端负责传感器数据采集和数据包传输,协调器负责组网,连接多个终端,最后负责转发数据包(数据包可能是ARM网关的命令),不作任何数据处理。
ZigBee网络 ⇌ ARM网关
使用串口通讯,ARM网关与ZigBee协调器相连,从底层的ZigBee网络转换为Internet网络,ZigBee协调器负责上报数据和接收ARM网关下发的命令,ARM网关则负责接收数据和发出命令(命令来自服务器)
ARM网关 ⇌ 服务器
使用socket的TCP编程进行连接,ARM网关中运行Linux系统,配置网卡进行Internet联网,与特定IP的socket(服务器)进行连接,同时保持与ZigBee协调器的串口通讯。ARM网关负责接收服务器的命令和上报数据,服务器则负责接收数据和下发命令(命令来自用户)。
服务器 ⇌ Internet网络⇌ 用户
使用socket的TCP编程进行数据报的发送和接收,采用C/S传输模式,提供一个PC的终端客户程序,程序会主动与指定的服务器连接,并在PC界面下接收用户命令,经过封装后通过Internet网络发送给服务器。用户主要访问服务器的数据库以及请求实时数据,服务器主要作数据库储存和数据帧处理。
02-硬件篇
使用到的一些ZigBee节点和ARM以及一些驱动器
名称 | 用途 | 通讯接口 |
---|---|---|
ZigBee终端 | 传感器数据采集 | ZigBee协议栈 |
ZigBee协调器 | 组网、转发数据 | ZigBee协议栈、串口 |
ARM(nano) | 数据转换、转发数据 | socket、串口 |
各种驱动器(如继电器、电机等) | 实现远程开关 | 普通IO |
各种电器(小风扇、水泵等) | 被远程控制 | 普通IO |
上文并没有说应用,因为目的只是打通一条链路,至于后期的应用,可以是农业大棚、养殖户、小花园等等。
03-软件篇
分为3部分:ZigBee协议栈编程、ARM-Linux编程和Linux-socket编程
ZigBee协议栈编程
使用的ZigBee节点是CC2530这一款,协议栈版本是ZStack-CC2530-2.5.1a,开发基于Sample例程。
出厂商附带了很多传感器的使用例程,稍加修改就可以直接使用,最后对协议栈的串口、数据发送AF_DataRequest和数据处理SampleApp_MessageMSGCB进行详细编程即可,传感器数据在终端处就要进行数据格式封装处理,自定义的数据格式如下:
更多的ZigBee协议栈编程可以直接下载文末的源代码查看。
ARM-Linux编程
小白使用的是友善之臂的nano-PC,板载Linux系统,虽然文档不多,裸机编程比较困难,但是直接使用Linux编程就可以了
对于ARM的操作,主要是操作串口通讯、管理员输入操作和服务器请求,这3个IO的并发操作使用Linux的文件集进行,等待文件集中描述符的变动,以响应某个动作,以下是文件集的相关操作:
void FD_Init(void)
{
/*将文件描述符加入读描述符集合*/
FD_ZERO(&fds); //清0
FD_SET(scanf_fd,&fds); //加入键盘输入文件描述符
FD_SET(serial_fd,&fds); //加入串口文件描述符
FD_SET(server_sockfd,&fds);//加入服务器文件描述符
/* 超时1秒0毫秒 */
timeout.tv_sec = 1;
timeout.tv_usec = 0;
}
/*----键盘输入IO-----*/
if(FD_ISSET(scanf_fd,&fds))
{
……
}
/*----串口输入IO-----*/
if(FD_ISSET(serial_fd,&fds))
{
……
}
/*----服务器请求IO-----*/
if(FD_ISSET(server_sockfd,&fds))
{
……
}
Linux-socket编程
比较重要的就是socket编程了,先在ARM板载Linux上启动应用程序,使用文件集等待Server的连接,在一台服务器的Linux系统上运行Server,socket连接ARM,同时等待Client的接入,以下是Server等待Client的相关代码:
//等待客户端连接请求到达
struct sockaddr_in remote_addr = *client_addr; //客户端网络地址结构体
if((client_sockfd=accept(server_sockfd,(struct sockaddr *)&remote_addr,&sin_size))<0)
{
perror("[FLASE] Server accepting\n");
return -1;
}
else
{
*client_addr = remote_addr;
//IP地址获取
char addr[20] = {0};
struct sockaddr* t = (struct sockaddr*)&remote_addr; //强制转换地址
inet_ntop(AF_INET, (void *)t, addr, sizeof(addr)); //网络字节顺序IP地址->点分IP
//数据库操作
char str_sql[128] = {0};
connectDB();
sprintf(str_sql,"INSERT INTO client (login_time,IP,contrl_time,contrl_action) VALUES (datetime('now'),'%s',datetime('now'),'logo_in');",addr);
sqlite_ret = sqlite3_exec(db, str_sql, callback, 0, &zErrMsg);
if( sqlite_ret != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
return false;
}
else
printf("[OK] Successfully records insert for client\n");
closeDB();
//提示信息
printf("[OK] Server accept client %d\n",remote_addr.sin_addr.s_addr);
printf("[OK] IP=%s\n",addr);
printf("[OK] Server accepting\n");
return client_sockfd;
}
04-测试篇
因为小白需要用账号密码登陆上网,不能直接连接ARM,可以借助使用云服务器,小白就偷懒不用了,因此只在局域网内进行测试。
![](https://i-blog.csdnimg.cn/blog_migrate/fd641b1273036754a040d1b0beb1e100.gif)
以下是小白录制的桌面视频的缩减版(暂时不支持插入视频,用GIF代替)
![](https://i-blog.csdnimg.cn/blog_migrate/9f79671a30c39855887c34871c124f02.gif)
网盘链接
最后分享所有的源代码和文档: 百度网盘 密码: 2u7z
![](https://i-blog.csdnimg.cn/blog_migrate/83758969a740f827872c3a295430bcb6.png)