之前实现了基于Zigbee的SHT10温湿度数据采集系统,这里来重新复盘一些主要的知识和代码。
写在前面:
- 1 功能介绍:使用Zigbee终端节点采集环境的温度和湿度数据,然后将数据无线发送的Zigbee协调器,最后在电脑端显示获得到的数据。
- 2 我没有让Zigbee终端节点 使用周期定时发送,而是通过电脑端的串口来控制 ,即:当电脑需要数据时,就返回终端节点的数据;不需要时终端节点就不会上传数据。 我这样的做法比较方便,因为选择什么时间上传 和 多久上传一次都可以由自己来决定!
- 3 我设计的Zigbee协调器和Zigbee终端节点没有采用广播的发送方式,因为我之前要控制12个节点,广播的效果实在不太好,而且对首发时间的要求并不算高,所以我采用“单播轮询”的发送方式。
- 4关于地址的选择,我没有使用64位的IEEE地址,我用的16位的短地址PANID,当然这些在AF_DataRequest()函数都写得很清楚。
- 5其余细节在说代码的时候,具体再说。
下面主要介绍一下,Zigbee协调器的SampleApp.c文件的代码:
首先,我们要配置与电脑通信的串口。
这里别忘了在SampleApp.c头文件引入串口头文件#include “hal_uart.h”,我们的波特率设置为115200.
/* 串口基本定义 */
#define MY_DEFINE_UART_PORT 0 //自定义串口号(0,1);
#define RX_MAX_LENGTH 20 //接收缓冲区最大值: 20个字节;
uint8 RX_BUFFER[RX_MAX_LENGTH]; //接收缓冲区;
void UartCallBackFunction(uint8 port , uint8 event); //回调函数声明,定义在最后面;
/* 配置串口 */
halUARTCfg_t uartConfig; //定义串口配置结构体变量;
void Uart_Config(void); //函数声明;
void Uart_Config(void) //函数定义;//结构体的定义函数
{
uartConfig.configured = TRUE; //允许配置;
uartConfig.baudRate = HAL_UART_BR_115200;//波特率;
uartConfig.flowControl = FALSE;
uartConfig.flowControlThreshold = 64; //don't care - see uart driver.
uartConfig.rx.maxBufSize = 128; //串口接收缓冲区大小
uartConfig.tx.maxBufSize = 128; //串口发送缓冲区大小
uartConfig.idleTimeout = 6; //don't care - see uart driver.
uartConfig.intEnable = TRUE; //使能中断
uartConfig.callBackFunc = UartCallBackFunction; //指定回调函数名;
}
- 下面是我的串口回调函数static void UartCallBackFunction(uint8 port , uint8 event)。
- 在里面我用osal_set_event自定义了任务事件,SAMPLEAPP_SEND_PERIODIC_MSG_EVT1,即:当接收到的字符为01或者02或者03时,在任务事件处理函数,SampleApp_ProcessEvent里处理相应的事件。
- 01 控制终端节点1~终端节点4(我试过01,控制6个节点效果也是可以的,很少丢包)
- 02控制终端节点5~终端节点8
- 03控制终端节点9~终端节点12
static void UartCallBackFunction(uint8 port , uint8 event)
{
uint8 shuang[2]={0,0};
uint16 nlen = 0; //接收到字符串大小;
if(event !=HAL_UART_TX_EMPTY)
{
nlen = HalUARTRead(0,shuang,2);
if(nlen > 0) //有数据存在;
{
if((shuang[0] ==0x30)&&(shuang[1]==0x31))//01 //1END~4END
{
chose=1;
x1=1;x2=1;x3=1;x4=1;
osal_set_event(SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT1);
}
else if((shuang[0] ==0x30)&&(shuang[1]==0x32))//02 //4END~8END
{
chose=2;
x5=1;x6=1;x7=1;x8=1;//
osal_set_event(SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT1);
}
else if((shuang[0] ==0x30)&&(shuang[1]==0x33))//03 //9END~12END
{
chose=3;
x9=1;xa=1;xb=1;xc=1;//9~12
osal_set_event(SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT1);
}
}
}
}
下面介绍一下Zigbee协调器的接收消息事件 处理函数SampleApp_MessageMSGCB。
- 这里有的人会问。为什么你的接收咋就没有short address和endpoint,直接上来就判断clusterId?
- 因为这个SampleApp_MessageMSGCB( )函数是属于应用层范畴,你的short address和endpoint在NWK层就被解析了我们在APP层就区分一下clusterId就可以了。
- 接收的数据一共分为两类:其中SAMPLEAPP_P3P1_CLUSTERID是接收1~9号终端节点返回的PANID
- SAMPLEAPP_P3P2_CLUSTERID是接收10~18号节点返回的PANID.(不是我写错了,因为去年我需要控制18个Zigbee终端节点,当然控制12个这么写也没错)
- SAMPLEAPP_P2P1_CLUSTERID~SAMPLEAPP_P2PC_CLUSTERID是1到12号Zigbee终端节点返回的SHT10传感器温度和湿度的DATA。
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
switch ( pkt->clusterId )
{
case SAMPLEAPP_P3P1_CLUSTERID://接受终端 网络ID的镞//ok//1~9
{
Addrtable[(pkt->cmd.Data[0]*10)+(pkt->cmd.Data[1])]=(((uint16)pkt->cmd.Data[2])<<8)|(((uint16)pkt->cmd.Data[3])&0x00ff);
//初始化D2
P1SEL&=0Xfd;//1111 1101
P1DIR|=0x02; //P11定义为输出0000 0010
P1_1 =0;
}
break;
case SAMPLEAPP_P3P2_CLUSTERID://接受终端 网络ID的镞//ok//10~18
{
Addrtable[(pkt->cmd.Data[0]*10)+(pkt->cmd.Data[1])]=(((uint16)pkt->cmd.Data[2])<<8)|(((uint16)pkt->cmd.Data[3])&0x00ff);
//初始化D2
P1SEL&=0Xfd;//1111 1101
P1DIR|=0x02; //P11定义为输出0000 0010
P1_1 =0;
}
break;
case SAMPLEAPP_P2P1_CLUSTERID:
{
if(x1==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
x1=0;
}
}
break;
case SAMPLEAPP_P2P2_CLUSTERID:
{
if(x2==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
x2=0;
}
}
break;
case SAMPLEAPP_P2P3_CLUSTERID:
{
if(x3==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
x3=0;
}
}
break;
case SAMPLEAPP_P2P4_CLUSTERID:
{
if(x4==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
x4=0;
}
}
break;
case SAMPLEAPP_P2P5_CLUSTERID:
{
if(x5==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
x5=0;
}
}
break;
case SAMPLEAPP_P2P6_CLUSTERID:
{
if(x6==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
x6=0;
}
}
break;
case SAMPLEAPP_P2P7_CLUSTERID:
{
if(x7==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
x7=0;
}
}
break;
case SAMPLEAPP_P2P8_CLUSTERID:
{
if(x8==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
x8=0;
}
}
break;
case SAMPLEAPP_P2P9_CLUSTERID:
{
if(x9==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
x9=0;
}
}
break;
case SAMPLEAPP_P2Pa_CLUSTERID://10
{
if(xa==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
xa=0;
}
}
break;
case SAMPLEAPP_P2Pb_CLUSTERID://11
{
if(xb==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
xb=0;
}
}
break;
case SAMPLEAPP_P2Pc_CLUSTERID://12
{
if(xc==1)
{
HalUARTWrite(0, pkt->cmd.Data, pkt->cmd.DataLength);
xc=0;
}
}
break;
}
}
下面介绍一下Zigbee协调器的数据请求函数void SAMPLEAPP_TO_jiedian_shang(uint16 addr)。
- 在AF_DataRequest里我选择数据发送形式为单播,16位
- shortAddr的短地址为各个终端节点的PANID。
- SAMPLEAPP_ENDPOINT为20。
- SAMPLEAPP_P2P_CLUSTERID为4
-这个函数的作用就是依次向终端节点发送数据请求,然后终端节点就会返回温湿度的数据
//数据请求函数
void SAMPLEAPP_TO_jiedian_shang(uint16 addr)
{
SampleApp_P2P_DstAddr.addr.shortAddr=Addrtable[addr];
char theMessageData[]="DATA";
if ( AF_DataRequest( &SampleApp_P2P_DstAddr,
&SampleApp_epDesc,
SAMPLEAPP_P2P_CLUSTERID,//4
(byte)osal_strlen( theMessageData )+1,
(byte * )&theMessageData,
&SampleApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
{
}
else
{
// Error occurred in request to send.
}
}
除了SHT10之外,我还加了烟雾传感器,OLED显示屏和一个指示灯,当温度、湿度超过阈值时,指示灯闪烁。后期让好朋友用SolidWorks给Zigbee板子做了一个外壳,看起来更好一看些。
下一期,有时间再说一下我程序的Zigbee终端节点相关代码解析。