转眼间ZigBee课程也上完了,本次完成了一个课程设计,希望大家能够互相探讨,解决问题,若有错误的地方麻烦大家及时提醒,我将不胜感激!当然我是和两位搭档一起合作完成,我只负责了stm32和单片机部分,希望大家多指教~~
设计方案概述
设计方案
在大棚蔬菜中安置温湿度传感器与温湿度警报显示装置,通过ZigBee通信技术,串口通信技术将采集到的该区域温湿度上传至控制系统,下发给显示装置。控制系统由STM32F103ZET6和协调器组成,接收数据信息,整理,按区域节点显示在控制端的LCD屏,判断并产生报警信号。显示装置实时显示温湿度,根据信号产生报警,关闭报警来提醒人们。
设计图
资源说明
1个STM32F103ZET6开发板
2个STC89C52单片机
3个ZigBee开发板(1个协调器,2个终端节点)
通信过程中的信息格式
为了快点完成课设,所以通信的格式特别的简洁,也不规范,考虑的因素很少,希望大家可以自己设计
五位字符(数据信息):
第一位:节点编号(1,2)
第二位:温度的十位
第三位:温度的个位
第四位:湿度的十位
第五位:湿度的个位
五位字符(警报信息):
第一位:警报标志(3代表1号节点警报,4代表2号节点警报)
第二位:固定为0
第三位:固定为0
第四位:固定为0
第五位:控制开关警报
模块介绍
显示报警模块
通过STC89C52单片机与采集模块进行串口通信
功能
1.将接收到的信息在数码管上显示
2.通过按键可以切换查看温湿度以及节点号
3.当收到报警信号时,报警(数码管显示全0),当接收到取消报警信号时,恢复正常显示
采集模块
作为终端节点的ZigBee开发板(CC2530)
功能
1.采集温湿度将数据发送给协调器和显示报警模块
2.接收协调器报警信号,将报警信号发送给显示报警模块
中控模块
中控模块由两部分组成:
1.一块作为协调器的ZigBee开发板
2.STM32F103ZET6开发板
它们之间通过串口通信
协调器功能
1.组建ZigBee网络
2.将采集模块上传的温湿度数据通过串口发送给STM32F103ZET6开发板
3.接收STM32F103ZET6开发板发出的报警信号并广播给终端节点
STM32F103ZET6开发板的功能
- 接收来自协调器的串口数据,并将数据信息显示到LCD屏
- 判断温度是否超过阈值,并产生报警
- 产生报警后通过按键发送报警信号或关闭报警信号给协调器
具体实现
接下来为大家介绍一下个模块的部分代码
显示报警模块
1. 接收串口信息:接受到信息产生中断,在中断服务函数中将数据存入buf数组中,由于涉及到控制信号的接收所以设置一个alterflag标志位来控制信号数据不存入buf中,只需将第五个控制位存入变量n中
void receive(void) interrupt 4{
if(RI==1)
{
//FENG=1;
if(i==5) //接收到5个后将下标置0,为下次接受做准备
i=0;
RI=0;
receiv=SBUF; //将缓存变量SBUF中数据存入变量receiv
if(i==0&&(receiv=='4'||receiv=='3') //通过数据的第一位判断是否是警报信号
alterflag=0; //警报信号标志置为0
else if(alterflag) //如果不是警报信号而是温湿度数据则存入buf中
buf[i]=receiv-0x30;
else if(i==4&&alterflag==0) //是警报信号将信号中第五个控制位存入变量n中
n=receiv;
i++;
flag=1; //接收到数据标志
}
}
2. 数码管显示:根据一些标志来判断显示的信息,数据显示:第一个为节点号,第二个为温湿度的十位,第三个为温湿度的个位。报警显示:全部显示0
void lcdshow()
{
if(alterflag==0) //是报警信号
{
if(n=='1') //报警信号第五个控制位为1则为报警
{
buf[0]=0; //数码管全部显示0
buf[1]=0;
buf[2]=0;
buf[3]=0;
buf[4]=0;
}
else if(n=='0') //报警信号第五个控制位为0则为取消报警
{
alterflag=1;
}
}
if(sign) //根据按键信息来显示温度还是湿度 sign:1为温度 0为湿度
{
P1=0xf1;
P0=led_mod[buf[2]];
delayms(5);
P1=0xf2;
P0=led_mod[buf[1]];
delayms(5);
P1=0xf4;
P0=led_mod[buf[0]];
delayms(5);
}
else
{
P1=0xf1;
P0=led_mod[buf[4]];
delayms(5);
P1=0xf2;
P0=led_mod[buf[3]];
delayms(5);
P1=0xf4;
P0=led_mod[buf[0]];
delayms(5);
}
3. 按键切换显示内容:判断是否按下,按下改变按键标志
if(KEY==0) //按键是否按下
{
delayms(10); //消抖
if(KEY==0)
{
while(KEY==0); //当按键松开
sign=!sign; //改变按键的标志
}
}
采集模块
1. 采集温湿度将数据发送给协调器和显示报警模块
终端节点加入网络后触发SEND_DATA_EVENT事件调用下面函数。
void GenericApp_SendTheMessage( void )
{
DHT11(); //温度采集
Data[0] = 2+0x30;//用于表示自己的节点号
Data[1] = wendu_shi+0x30;
Data[2] = wendu_ge+0x30;
Data[3] = shidu_shi+0x30;
Data[4] = shidu_ge+0x30;
AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
6,
(uint8 *)&Data,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS );//发送到协调器
HalUARTWrite(0, Data,5);//发送到显示报警模块
}
其中DHT11()为温度采集函数,其代码如下
void DHT11(void) //温湿传感启动
{
P0DIR |= 0x10;
wenshi=0;
Delay_ms(19); //>18MS 主机拉低18ms
wenshi=1;
//主机延时40us
Delay_10us();
Delay_10us();
Delay_10us();
Delay_10us();
wenshi=1;
P0DIR &= ~0x10;//重新配置IO口方向 0001 0000
if(!wenshi)
{
ucharFLAG=2;
//判断从机是否80us的低电平,响应信号是否结束
while((!wenshi)&&ucharFLAG++);
ucharFLAG=2;
while((wenshi)&&ucharFLAG++);
COM();//读湿度的高八位
ucharRH_data_H_temp=ucharcomdata;
COM();//读湿度的低八位
ucharRH_data_L_temp=ucharcomdata;
COM();//读温度的高八位
ucharT_data_H_temp=ucharcomdata;
COM();//读温度的低八位
ucharT_data_L_temp=ucharcomdata;
COM();
ucharcheckdata_temp=ucharcomdata;//校验和
//数据校验
wenshi=1;
uchartemp=(ucharT_data_H_temp+ucharT_data_L_temp+ucharRH_data_H_temp+ucharRH_data_L_temp);
if(uchartemp==ucharcheckdata_temp)//校验结果正确则收集数据
{
ucharRH_data_H=ucharRH_data_H_temp;//收集湿度
ucharRH_data_L=ucharRH_data_L_temp;
ucharT_data_H=ucharT_data_H_temp;//收集温度
ucharT_data_L=ucharT_data_L_temp;
ucharcheckdata=ucharcheckdata_temp;
}
wendu_shi=ucharT_data_H/10;
wendu_ge=ucharT_data_H%10;
shidu_shi=ucharRH_data_H/10;
shidu_ge=ucharRH_data_H%10;
}
else //没用成功读取,返回0
{
wendu_shi=0;
wendu_ge=0;
shidu_shi=0;
shidu_ge=0;
}
}
void COM(void) // 温湿写入
{
uchar i;
for(i=0;i<8;i++)
{
ucharFLAG=2;
//如果高电平 或者uchatFLAG到达0 退出循环
while((!wenshi)&&ucharFLAG++);
Delay_10us();
Delay_10us();
Delay_10us();
uchartemp=0;
if(wenshi)// 如果P0_4是高电平,
uchartemp=1;//超过30us依然为高电平,即数据为1;否则数据为0
ucharFLAG=2;
//如果是高电平并且ucharFLAG++ 一直为真 直到255 当做延时
//ucharFLAG变成0 停止执行 但是wenshi没有变成0 说明没有响应
while((wenshi)&&ucharFLAG++); //超时则跳出for循环
if(ucharFLAG==1)
break;
ucharcomdata<<=1;
ucharcomdata|=uchartemp; //判断数据是0还是1
}
}
2. 接收协调器报警信号,将报警信号发送给显示报警模块
收到协调器广播消息根据数据的第一位判断是否为发送给自己的,其中3表示1号节点,4表示2号节点。(其中烧另外一个节点时判断改为判断"3")
void GenericApp_MessageMSGCB(afIncomingMSGPacket_t *pkt )
{
uint8 buf[5];
switch(pkt->clusterId)
{
case (GENERICAPP_CLUSTERID):
osal_memcpy(buf,pkt -> cmd.Data,5);
if(osal_memcmp(buf,"4",1))//只判断第一位
{
HalUARTWrite(0, buf,5);//发送到显示报警模块
}
}
}
中控模块
协调器
1. 组建ZigBee网络
网络组建成功点亮黄灯,收到终端节点传来的数据后点亮蓝灯。
2. 将采集模块上传的温湿度数据通过串口发送给STM32F103ZET6
void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
uint8 buf[10];
switch ( pkt->clusterId )
{
case GENERICAPP_CLUSTERID:
uint8 endbuf[2]={0x0d,0x0a};
HalLedSet(HAL_LED_1,HAL_LED_MODE_ON);
osal_memcpy(&buf,pkt->cmd.Data,5);存储终端节点的数据
HalUARTWrite(0, buf,5);//发送给STM32F103ZET6
HalUARTWrite(0, endbuf,2);//以换行符结尾
break;
}
}
3. 接收STM32F103ZET6发出的报警信号并广播给终端节点
用回调函数来处理报警信号,并以广播模式发出
GenericApp_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;//广播模式
GenericApp_DstAddr.endPoint =GENERICAPP_ENDPOINT;
GenericApp_DstAddr.addr.shortAddr = 0xFFFF;//所有节点
以下为回调函数
static void rxCB(uint8 port, uint8 event)
{
uint8 buf[5];
HalUARTRead(0,buf,5);//读取STM32F103ZET6发来的报警信号
AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
5,
(uint8*)&buf,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS );//广播给网络下的所有节点
}
STM32F103ZET6
1.接收串口信息:在串口中断服务函数中将收到的数据保存在串口缓存中,根据接收到信息标志USART_RX_STA的最高位来判断是否收到数据,然后调用DataHandle函数将缓存中的数据存入对应的变量中
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN //(200)个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
DataHandle函数:
void DataHandle()
{
char num = USART_RX_BUF[0]; //根据数据的第一位判断是哪个节点
if(num =='1') //1节点
{
number1[0]=USART_RX_BUF[0]; //存下节点标号
number1[1]='\0';
temp1[0]=USART_RX_BUF[1]; //存下温度的十位
temp1[1]=USART_RX_BUF[2]; //存下温度的个位
temp1[2]='\0';
hum1[0]=USART_RX_BUF[3]; //存下湿度的十位
hum1[1]=USART_RX_BUF[4]; //存下湿度的个位
hum1[2]='\0';
}
else if(num=='2') //2节点
{
number2[0]=USART_RX_BUF[0];
number2[1]='\0';
temp2[0]=USART_RX_BUF[1];
temp2[1]=USART_RX_BUF[2];
temp2[2]='\0';
hum2[0]=USART_RX_BUF[3];
hum2[1]=USART_RX_BUF[4];
hum2[2]='\0';
}
}
2.LCD显示:在循环里面调用显示函数LCD_show,根据报警标志来显示相应的报警信息
void LCD_show()
{
LCD_ShowString(30,40,200,16,16,"number:");
LCD_ShowString(120,40,200,16,16,number1);
LCD_ShowString(30,60,200,16,16,"temp:");
LCD_ShowString(120,60,200,16, 16,temp1);
LCD_ShowString(30,80,200,16,16,"humidity:");
LCD_ShowString(120,80,200,16,16,hum1);
LCD_ShowString(30,100,200,16,16,"number:");
LCD_ShowString(120,100,200,16,16,number2);
LCD_ShowString(30,120,200,16,16,"temp:");
LCD_ShowString(120,120,200,16,16,temp2);
LCD_ShowString(30,140,200,16,16,"humidity:");
LCD_ShowString(120,140,200,16,16,hum2);
LCD_ShowString(30,280,200,16,16,"lowtemp:15 hightemp:19");
if(ALERT1==true && ALERT2==false) //节点1产生报警
{
LCD_ShowString(30,175,200,16,24,"ALERT1");
LCD_ShowString(30,210,200,16,24,"PRESS KEY0");
}
else if(ALERT1==false && ALERT2==true) //节点2产生报警
{
LCD_ShowString(30,175,200,16,24,"ALERT2");
LCD_ShowString(30,210,200,16,24,"PRESS KEY0");
}
else if(ALERT1 && ALERT2) //节点1,2都产生报警
{
LCD_ShowString(30,175,200,16,24,"ALERT");
LCD_ShowString(30,210,200,16,24,"PRESS KEY0");
}
}
3.判断温度是否超过阈值,并产生报警:根据收到数据转化位十进制数与阈值比较,若超过阈值则将报警标志置1,具体函数在接收到信息后进来判断,函数为ALERT01
void ALERT01()
{
int temprture1 = (temp1[0]-0x30)*10+(temp1[1]-0x30); //温度转化为十进制数
int temprture2 = (temp2[0]-0x30)*10+(temp2[1]-0x30);
if((temprture1<=lowtemp && temprture1>0) || temprture1>=hightemp) //判断节点1温度是否超过阈值
{
ALERT1 = true; //改变节点1报警标志
}
if((temprture2<=lowtemp && temprture2>0) || temprture2>=hightemp) //判断节点2温度是否超过阈值
{
ALERT2 = true; //改变节点2报警标志
}
}
4.按键下发报警信息:当产生报警后可以按KEY1下发报警信息给协调器,按KEY0可取消报警,下发取消报警信息
KEY1的中断服务函数:
void EXTI3_IRQHandler(void)
{
delay_ms(10);//消抖
if(ALERT1 || ALERT2) //有报警
{
if(ALERT1)
{
uart_send_buff(USART1,"30001",5); //第一位3与第五位1表示1号节点的报警信息
}
if(ALERT2)
{
uart_send_buff(USART1,"40001",5); //第一位4与第五位1表示2号节点的报警信息
}
LCD_Clear(WHITE);
EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE3上的中断标志位
}
}
KEY0的中断服务函数:
void EXTI4_IRQHandler(void)
{
delay_ms(10);//消抖
if(ALERT1 || ALERT2) //有报警
{
if(ALERT1)
{
ALERT1=false; //1节点警报标志置位false
uart_send_buff(USART1,"30000",5); //第一位3与第五位0表示1号节点的取消报警信息
}
if(ALERT2)
{
ALERT2=false; ////2节点警报标志置位false
uart_send_buff(USART1,"40000",5); //第一位4与第五位0表示2号节点的取消报警信息
}
LCD_Clear(WHITE);
EXTI_ClearITPendingBit(EXTI_Line4); //清除LINE4上的中断标志位
}
}
5.下发报警信号函数uart_send_buff:
void uart_send_buff(USART_TypeDef* USARTx, u8 *buf,u8 len)
{
u8 j=0 ;
LED0=!LED0; //红灯状态反转
for(j=0;j<len;j++) //循环发送给定长度
{
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET); //循环,直到发送完毕
USART_SendData(USARTx,buf[j]); //发送数据
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020010515275091.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMzk4MzQy,size_16,color_FFFFFF,t_70)
结果展示
采集模块采集到温湿度通过串口发给显示报警模块
由图可知当前1号节点温度数值为:26,2号节点温度值为:24
当按下按键时,切换查看湿度
由图可知当前1号节点湿度数值为:17,2号节点湿度值为:16
采集模块将采集到的温湿度发给协调器,协调器通过串口输出可以看到1,2节点的温湿度
协调器通过串口将温湿度发给STM32控制板显示在LCD上并且由于温度过高发出警报
Number:1
Temp:26
Humidity:17
Number:2
Temp:24
Humidity:16
Lowtemp:15 hightemp:19
当按下按键1时通过串口发出警报信号给协调器
警报信号30001表示给1号节点的警报信号
警报信号40001表示给2号节点的警报信号
协调器在将警报信号广播给采集模块,采集模块收到信号后通过串口将信号发个显示报警模块,产生报警(数码管显示全0)
当按下按键0时通过串口发出取消警报信号给协调器
警报信号30000表示给1号节点的取消警报信号
警报信号40000表示给2号节点的警报信号
协调器在将警报信号广播给采集模块,采集模块收到信号后通过串口将信号发个显示报警模块,取消报警信号,当下次温湿度数据来到时恢复显示。
由图可知当前1号节点温度数值为:25,2号节点温度值为:24
总结
这门课程也学完了,虽然没什么机会去实践,去将所学的知识好好利用起来,希望自己以后能够多加深入,研究!