基于ZIGBEE和ESP8266的智能生态系统(一)

mac2024-05-16  33

前言

博主是利用两个zigbee作为终端分别采集不同区域的温湿度和光照数据,采集的数据与设定的阈值进行比较,再控制电机等进行自我调节,终端可利用点播方式将状态传至另一个作为协调器的zigbee,协调器再通过串口发至esp8266,8266收到正确数据后上传至机智云,手机上的机智云app即可以显示各地的状态(包括温湿度,阈值,电机状态)也可以远程控制。

设备准备

ZigBee用的是网蜂CC2530核心板,温湿度传感器用的是DHT11,光照用的是BH1750,电机等先用的是LED灯代替,之后用继电器控制,esp8266用的是ESP8266 NodeMcu。

ZigBee部分

博主是直接在温湿度的例程上修改的,因为原有的例程已经有了温湿度采集,所以只要将光照添加进去即可。

bh1750.c

static unsigned char IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; SDA_IN();//SDAéè???aê?è? for(i=0;i<8;i++ ) { SCL=0; Delay_us(2); SCL=1; receive<<=1; if(SDA)receive++; Delay_us(1); } if (!ack) IIC_NAck();//·¢?ínACK else IIC_Ack(); //·¢?íACK return receive; } /************************************** 向IIC总线发送一个字节数据 **************************************/ static void BH1750_SendByte(char dat) { unsigned int i; SDA_OUT(); SCL = 0; for (i=0; i<8; i++) //8???? { //dat <<= 1; //???????? if((dat&0X80)>>7) { SDA = 1; } else { SDA = 0; }//???? dat<<=1; //????? Delay_us(2); //?? SCL = 1; Delay_us(2); SCL = 0; //????? Delay_us(2); //?? } } void Single_Write_BH1750(unsigned char REG_Address) { BH1750_Start(); //起始信号 BH1750_SendByte(SlaveAddress); //发送设备地址+写信号 while(IIC_Wait_Ack()); BH1750_SendByte(REG_Address); //内部寄存器地址,请参考中文pdf22页 while(IIC_Wait_Ack()); BH1750_Stop(); //发送停止信号 Delay_ms(5); } //********************************************************* // //连续读出BH1750内部数据 // //********************************************************* void Multiple_read_BH1750(void) { unsigned char i; BH1750_Start(); //起始信号 BH1750_SendByte(SlaveAddress+1); //发送设备地址+读信号 while(IIC_Wait_Ack()); BUF[0] = IIC_Read_Byte(1); BUF[1] = IIC_Read_Byte(0); BH1750_Stop(); Delay_ms(5); } //初始化BH1750,根据需要请参考pdf进行修改**** void Init_BH1750(void) { P1SEL &= 0x80; P1SEL &= 0x40; SDA_OUT(); SCL_OUT(); Single_Write_BH1750(0x01); // power on Single_Write_BH1750(0x10); // H- resolution mode Delay_ms(180); //延时180ms } int BH1750_READ(void) { float temp; int data; Multiple_read_BH1750(); //连续读出数据,存储在BUF中 data = BUF[0]; data = (data<<8) | BUF[1];//合成数据 temp = (float)data/1.2; data = (int)temp; return data; }

bh1750.h

#define SlaveAddress 0x46 //定义器件在IIC总线中的从地址,根据SA0地址引脚不同修改 #define SCL P1_6 //IIC时钟引脚定义 #define SDA P1_7 //IIC数据引脚定义 #define SDA_OUT() P1DIR |= 0x80 #define SDA_IN() P1DIR &= ~0x80 #define SCL_OUT() P1DIR |= 0x40

BH1750主要用的是I2C通信,买模块的话卖家会给51的例程,其实差不多,我用的以前32上写的移植过来修改的。大家修改的话主要就是延时函数,还有IO口需要改,网蜂给了一些例程中也有I2C控制的传感器,看看就容易懂了。ZigBee的IO口配置主要就是这两个寄存器PXSEL和PXDIR。

然后进入SampleApp.c,在前面定义一些变量,详情看注释

#define LED P1_4 #define FAN P1_5 #define open 1 #define close 0 int fan_start = 20; //温度的阈值,超过就启动风扇 int led_start = 100; //光照的阈值,低于就开启LED unsigned char manual = 0; //手动的标志

然后在SampleApp_Init中加入一些初始化,其余不变。

void SampleApp_Init( uint8 task_id ) { SampleApp_TaskID = task_id; SampleApp_NwkState = DEV_INIT; SampleApp_TransID = 0; MT_UartInit();//串口初始化 MT_UartRegisterTaskID(task_id);//登记任务号 HalUARTWrite(0,"Hello World\n",12); //(串口0,'字符',字符个数。) P0SEL &= 0xbf; //温湿度IO P1SEL &= 0x30; //继电器IO P1.4,1.5 P1DIR |= 0x30; //继电器IO输出 LED = close; //初始化时关闭 FAN = close; //初始化时关闭 Init_BH1750(); //光照初始化 .............

接着在周期事件中,每五秒采一次数据发送至协调器。

if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT ) { int light; int temp, humidity; uint8 buf[30]; //温度+提示符 DHT11(); //温度检测 temp = wendu_shi*10 + wendu_ge; humidity = shidu_shi*10 + shidu_ge; light = BH1750_READ(); if (light > 255) //设置一个上限 { light = 255; } if (manual == 0) //若是自动 { if (light <= led_start) { LED = open; } else { LED = close; } if (temp >= fan_start) { FAN = open; } else { FAN = close; } } sprintf(buf, "@A%d%d%d#%d#%d#%d#%d#", LED, FAN, temp, humidity, light, fan_start, led_start); SampleApp_SendPointToPointMessage(buf);//点播函数 osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT, (SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) ); // return unprocessed events return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT); }

这里主要就是在原有基础上加入了光照,然后将数据存入buf中发送,@A%d%d%d#%d#%d#%d#%d#,是数据的格式,A代表着区域名,即A点,后两位是继电器状态,紧接着温湿度和光照等,由于数据时变化的,所以这些用#分开判断。@是我设置的一个帧头,不设置帧头数据可能会错误,这是博主在调试时候发现的一个bug。不知道为什么当协调器收到8266发过来的数据时,过长的数据好像会打印出来,为了避免8266接收到错误数据,所以设置了一个帧头,不然8266很容易进入异常。

接下来看到case SAMPLEAPP_POINT_TO_POINT_CLUSTERID:

case SAMPLEAPP_POINT_TO_POINT_CLUSTERID: HalUARTWrite(0,&pkt->cmd.Data[0],strlen(pkt->cmd.Data)); //调试用 HalUARTWrite(0,"\n",1); if (!strcmp(pkt->cmd.Data, "manual!")) //开启手动模式 { manual = open; } else if (!strcmp(pkt->cmd.Data, "auto!")) //开启自动模式 { manual = close; } if (!strcmp(pkt->cmd.Data, "A_LED_ON!") && (manual == open)) { LED = open; } else if (!strcmp(pkt->cmd.Data, "A_LED_OFF!") && (manual == open)) { LED = close; } else if (!strcmp(pkt->cmd.Data, "A_FAN_ON!") && (manual == open)) { FAN = open; } else if (!strcmp(pkt->cmd.Data, "A_FAN_OFF!") && (manual == open)) { FAN = close; } else if (!strncmp(pkt->cmd.Data, "A_LED_START:", 12)) { led_start = extract(pkt->cmd.Data + 12); } else if (!strncmp(pkt->cmd.Data, "A_FAN_START:", 12)) { fan_start = extract(pkt->cmd.Data + 12); } break;

这里又有一个注意点,协调器发过来的数据中,最后默认都有一个!号,如果知道的朋友也可以告诉我一下为啥,我这里就软件处理了一下。如果是手动模式的话就可以机智云远程控制LED和风扇,此时关闭自动调节。 else if (!strncmp(pkt->cmd.Data, “A_LED_START:”, 12)) { led_start = extract(pkt->cmd.Data + 12); } 这个是接收阈值的改变,手动自动只是控制LED这些设备,因为数据后有个!而且发过来的是字符,所以自己写了一个函数,将需要的数据提取出来。

uint16 mypow(uint16 x, uint16 y) { if (y == 0) { return 1; } while(--y) { x *= x; } return x; } uint16 extract(uint8 *buf) { uint8 *temp = buf; uint16 num = 0; uint16 data = 0; while(*buf++ != '!') { num++; } while(num--) { data += ((*temp++) - '0') * mypow(10,num); } return data; }

至此终端部分大致完成,下一篇会讲协调器部分。这个小项目只是一个简单的模型,没有做的特别的完善,大家可以根据需求增添自己的idea,博主也是小白,欢迎大佬指点。

最新回复(0)