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

前言

博主是利用两个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,博主也是小白,欢迎大佬指点。

你可能感兴趣的:(物联网)