博主是利用两个zigbee作为终端分别采集不同区域的温湿度和光照数据,采集的数据与设定的阈值进行比较,再控制电机等进行自我调节,终端可利用点播方式将状态传至另一个作为协调器的zigbee,协调器再通过串口发至esp8266,8266收到正确数据后上传至机智云,手机上的机智云app即可以显示各地的状态(包括温湿度,阈值,电机状态)也可以远程控制。
ZigBee用的是网蜂CC2530核心板,温湿度传感器用的是DHT11,光照用的是BH1750,电机等先用的是LED灯代替,之后用继电器控制,esp8266用的是ESP8266 NodeMcu。
博主是直接在温湿度的例程上修改的,因为原有的例程已经有了温湿度采集,所以只要将光照添加进去即可。
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,博主也是小白,欢迎大佬指点。