Zigbee入门指导(三)
——一个接近实用的WSN系统
logiclimit
在之前的两篇指导当中,大家已经了解到了基于Ti CC2430的Zigbee开发的一些基本的概念和知识,也成功的运行了Ti自带的例程,然而这些与实用还相差甚远。作为入门指导的最后一篇,将对如何建立一个实用的WSN系统进行探讨。
Zigbee的出现是为了满足WSN(Wireless Sensor Network)的要求,一般而言WSN有以下几个特征:
1.采集点众多,分布面积广
2.网络节点间的位置关系不定,节点动态加入或脱离网络
3.采集点无法和市电网络相连,依赖于电池供电,要求有很好的节电及电源管理
为了实现节能的特性,还跟CC2430模块以外的采集模块有关,本文主要关注的是CC2430自身的管理使用,故对外界数据的采集简化为从AD中采集数据。目标系统将具备以下功能:
1.协调器建立网络,终端节点加入网络
2.节点能采集多种数据
从例程中选用一个合适的范例作为模板可以大大缩短开发时间,节约成本。选用SimpleApp作为模板。SimpleApp中有两个例程,一个是控制器-开关,一个是收集器-传感器,将使用收集器-传感器例程。收集器-传感器例程中以传感器终端的温度及电源电压为数据源,传感器定时采集这两个数据,送往收集器,收集器收到数据后通过串口传给PC机。可以说SimpleApp本身就是一个接近实用的WSN例程,本文的目标在于学习SimpleApp的使用,并加上一个通过AD采集数据的功能。此外,由于SimpleApp的传感器终端启动后就一直采集发送数据,无法由收集器控制其采集的开启/关停,将增添由PC发送指令到收集器,再由收集器发送指令控制某终端的某项采集功能的开启/关闭。
一、建立新工程
为了和例程SimpleApp区分开,新建工程命名为WSNApp,其中只包含Collector和Sensor,且只为EB板设置,本节内容不会对程序的运行有实质影响,修改工程名只是为了管理上的方便。也可不修改工程名,只做功能上的修改,直接跳到下一节的内容。先原地复制SimpleApp工程文件夹,并修改目录名为WSNApp。WSNApp中有两个目录,为
修改WSNApp.eww,先将所有的SimpleApp替换为WSNApp。里面的代码结构如下
member>
project>WSNAppproject>
configuration>SimpleCollectorDBconfiguration>
member>
member>
project>WSNAppproject>
configuration>SimpleCollectorEBconfiguration>
member>
member>
project>WSNAppproject>
configuration>SimpleControllerDBconfiguration>
member>
每个member下是相应的project及相关的设置,删除所有的DB及Controller、Switch。最后只保留以下内容
member>
project>WSNAppproject>
configuration>SimpleCollectorEBconfiguration>
member>
member>
project>WSNAppproject>
configuration>SimpleSensorEBconfiguration>
member>
修改WSNApp.ewp,WSNApp.ewp有8902行,如果所用的文本编辑器不支持XML语言会改得比较痛苦。先将所有的SimpleApp替换为WSNApp。WSNApp.ewp存放的是工程的目录结构,WSNApp.ewp中的configuration段定义的是各个project 的设置,还有group段定义的是各个project中各文件的关联关系。删除有关DB及Controller、Switch的configuration段。修改App的group段的中的DB及Controller、Switch段落。
修改WSNApp.ewd,先删除有关DB及Controller、Switch的configuration段即可。
二、Collector的修改
Collector程序要添加PC和Collector的间的通讯、通过无线发送指令到Sensor。要为Collector添加一个用户事件,用于发送数据。
1、串口设置
Ti的协议栈中已经有了有关的串口通信的驱动,可直接调用,使用串口有关的指令,需包含"hal_uart.h"头文件,在SimpleCollector.c和sapi.c中均要包含。要添加预编译指令HAL_UART。使用halUARTCfg_t来配置串口,配置串口时需配置输入/输出缓冲区的大小等信息。由于用户task的初始化在sapi.c中,串口的初始化也放在sapi.c中。先在sapi.h中定义以下常量
#ifdef COLLECTOR
#define SERIAL_PORT_THRESH 48
#define SERIAL_PORT_RX_MAX 64
#define SERIAL_PORT_TX_MAX 64
#define SERIAL_PORT_IDLE 6
#define SERIAL_PORT_RX_CNT 80
#endif
void SAPI_Init( byte task_id )
{
uint8 startOptions;
#ifdef COLLECTOR
halUARTCfg_t uartConfig;
#endif
#ifdef COLLECTOR
uartConfig.configured = TRUE; // 2430 don't care.
uartConfig.baudRate = HAL_UART_BR_38400; // CC2430 only allow 38.4k or 115.2k
uartConfig.flowControl = HAL_UART_FLOW_OFF; // Turn off flow control to fit most serial ports' setting
uartConfig.flowControlThreshold = SERIAL_PORT_THRESH;
uartConfig.rx.maxBufSize = SERIAL_PORT_RX_MAX;
uartConfig.tx.maxBufSize = SERIAL_PORT_TX_MAX;
uartConfig.idleTimeout = SERIAL_PORT_IDLE; // 2430 don't care.
uartConfig.intEnable = TRUE; // 2430 don't care.
uartConfig.callBackFunc = rxCB;
HalUARTOpen (HAL_UART_PORT_0, &uartConfig);
#endif
// Set an event to start the application
osal_set_event(task_id, ZB_ENTRY_EVENT);
}
#if defined( MT_TASK )
debug_str( (uint8 *)buf );
#endif
HalUARTWrite ( HAL_UART_PORT_0, buf, 32 );
2、接收来自PC的数据
收到数据后要通知OS收到了串口数据,在SimpleCollector.c中定义了相关的事件。
#define SERIAL_PORT_MSG_RCV_EVT 0x0008
定义指向数据的指针和数据长度
static uint8 *otaBuf = NULL;
static uint8 otaLen;
rxCB的定义如下
void rxCB( uint8 port, uint8 event )
{
uint8 *buf, len;
if (!otaBuf)
{
if ( !(buf = osal_mem_alloc( SERIAL_PORT_RX_CNT )) )
{
return;
}
}
len = HalUARTRead( port, buf, SERIAL_PORT_RX_CNT );
if ( !len ) // Length is not expected to ever be zero.
{
osal_mem_free( buf );
return;
}
otaBuf = buf;
otaLen = len;
osal_set_event( sapi_TaskID, SERIAL_PORT_MSG_RCV_EVT );
}
当数据接收完毕后,触发事件SERIAL_PORT_MSG_RCV_EVT。
3、Collector向Sensor发送指令
从sapi.c中的
if ( events & ( ZB_USER_EVENTS ) )
{
// User events are passed to the application
zb_HandleOsalEvent( events );
// Do not return here, return 0 later
}
可知,在zb_HandleOsalEvent()中处理用户自定义的事件,当收到PC传来的数据后触发了SERIAL_PORT_MSG_RCV_EVT,将无线发送指令的程序作为此事件的响应,原有的SimpleCollector.c中的void zb_HandleOsalEvent( uint16 event )是空的,为其添加以下内容
void zb_HandleOsalEvent( uint16 event )
{
if ( event & SERIAL_PORT_MSG_RCV_EVT )
{
Collector_SendData( otaBuf, otaLen );
}
}
其中Collector_SendData()的定义如下
void Collector_SendData( uint8 *buf, uint8 len )
{
volatile uint16 tempDstAddr;
tempDstAddr = buf[0];
tempDstAddr = tempDstAddrtempDstAddr += buf[1];zb_SendDataRequest( tempDstAddr, COLLECTOR_CMD_ID, 1, &buf[2], 0, 0, AF_DEFAULT_RADIUS );osal_mem_free( otaBuf );}
其中的zb_SendDataRequest()即为通过无线发送指令的函数,其中的参数COLLECTOR_CMD_ID是Endpoint中的发送cluster。原有的Collector程序只能接受无线数据,不能发送无线数据,是通过设置发送cluster为实现的,增添发送cluster
#define NUM_OUT_CMD_COLLECTOR 1
const cId_t zb_OutCmdList[NUM_OUT_CMD_COLLECTOR] =