智能创客,中国最大的极客空间,智能平台,免费教学,视频教程,手把手教你创造儿时梦想!

标题: WIFI作品DIY教程08-《智能开关》电灯开关、插座(nrf24l01/zigbee) [打印本页]

作者: znck007    时间: 2015-1-14 13:58
标题: WIFI作品DIY教程08-《智能开关》电灯开关、插座(nrf24l01/zigbee)
上篇WIFI作品DIY教程07-《wifi家居网关》家居控制中心zigbee/nrf24l01等完成了家居网关。
这篇我们就教大家如何DIY智能开关,可以用来做电灯开关,也可以做插座开关,并这次是3路控制满足一般家庭使用了。



我们需要的配件:多路开关板nrf24l01(或zigbee等)

多路开关板的原理:网关nrf24l01或zigbee发送{ck0030001}到多路开关板,收到字符后判断0001则是0路开,0000则是0路关,0011则是1路开,0010则是1路关,0021则是2路开,0020则是2路关。多路开关板使用arduino控制继电器间接控制220v电路。


第一部分:下载编译源代码
1、安装CH340驱动

在drivers目录里找到CH340驱动,然后按说明安装(之前安装了就不用再安装了)。


2、源代码
[C] 纯文本查看 复制代码


/*
*NRF24l01针脚连接线
* MISO -> 12
* MOSI -> 11
* SCK -> 13
* Configurable:
* CE -> 8
* CSN -> 7
*/
#include <SPI.h>
#include <Mirf.h>
#include <nRF24L01.h>
#include <MirfHardwareSpiDriver.h>
#include <EEPROM.h>

int sid=3;//模块类型
int nid=0;//模块编号


//无线串口通信处理(zigbee/bluetooth等)
unsigned long serial1nowlast;
char serial1buff[129]={0};
char serial1Data;
int serial1i=0;

//NRF24l01
unsigned long nrf24l01nowlast;
char nrf24l01buff[33]={0};
char nrf24l01Data;
int nrf24l01i=0;


//声名变量
int pinIn0=3;
int pinIn1=4;
int pinIn2=5;

int val0;
int val1;
int val2;

int pinOut0=A0;
int pinOut1=A1;
int pinOut2=A2;

int state0=0;
int state1=0;
int state2=0;

void setup()
{
    Serial.begin(115200);

    char client[10]={0};//client
    sprintf(client,"clie%d",sid);
    //初始化Mirf,用于NRF24l01收发
    Mirf_Init(0,client,sid);

    pinMode(pinIn0,INPUT);
    pinMode(pinIn1,INPUT);
    pinMode(pinIn2,INPUT);

    pinMode(pinOut0,OUTPUT);
    pinMode(pinOut1,OUTPUT);
    pinMode(pinOut2,OUTPUT);

    pinMode(pinIn0,INPUT_PULLUP); //将管脚设置为输入并且内部上拉模式
    pinMode(pinIn1,INPUT_PULLUP); //将管脚设置为输入并且内部上拉模式
    pinMode(pinIn2,INPUT_PULLUP); //将管脚设置为输入并且内部上拉模式

    digitalWrite(pinIn0, HIGH);
    digitalWrite(pinIn1, HIGH);
    digitalWrite(pinIn2, HIGH);

    state0 = EEPROM.read(0);
    state1 = EEPROM.read(1);
    state2 = EEPROM.read(2);

    Serial.println("zwifi_kaiguan");

    Serial.print("state0=");
    Serial.print(state0);
    Serial.print("/state1=");
    Serial.print(state1);
    Serial.print("/state2=");
    Serial.println(state2);

    if(state0==255) state0=0;
    if(state1==255) state1=0;
    if(state2==255) state2=0;   

}

void loop()
{

  //检测无线串口数据处理 (zigbee/bluetooth等)
  {  
      unsigned long serial1now = millis();//获取现在的时间
      if(serial1now - serial1nowlast >= 5000)//如果数据间隔超过5秒而清空字符(为了防止数据错乱)
      {
        serial1nowlast = millis();
        memset(serial1buff, 0, 129);
        serial1i=0;
      }

      while( Serial.available() )//如果无线串口有数据
      {
        if(serial1i==0)
        {
          Serial.println("serial->");//打印出来方便调试
        }      
        serial1Data=(char)Serial.read();//读取串口数据
        //Serial.print(serial1Data);////这里不打印,否则检测到{ckxxxx}就认为是命令
        serial1buff[serial1i]=serial1Data;////保存到数组
        serial1i++;////数组长度+1
        if(serial1Data=='}' || serial1i>=129)//如果发现}而说明命令结束(并少于129个字符,太长会出错)
        {               
          serial1nowlast = millis(); //更新当前时间,不然5秒就超时了

          char body[129]={0};
          get_znck_body(serial1buff,body);//获取只是{ckxxxxxx}的字符,因为这是我们的命令格式
          //serial.println(body);

          //如果命令格式真确则发送到无线串口
          if(strstr(body,"{ck") && strstr(body,"}") )
          {
            //Serial.println(body);

            if(strlen(body)>10)
            {
              set_onoff(body);              

            }

          }
          serial1i=0;//字符长度为0
          Serial.println("-------------------");

          delay(100);
        }
      }

  }

  unsigned long nrf24l01now = millis();//获取现在的时间
  if(nrf24l01now - nrf24l01nowlast >= 5000)//如果数据间隔超过5秒而清空字符(为了防止数据错乱)
  {
     nrf24l01nowlast = millis();
     memset(nrf24l01buff, 0, 33);
     nrf24l01i=0;
   }

   byte data[Mirf.payload];
   if(Mirf.dataReady()){//!Mirf.isSending() &&

    Mirf.getData(data);
    Mirf.rxFifoEmpty();   //清理24L01援存
    //Serial.println((char)*data);  

    for (int i = 0; i < Mirf.payload; i++) //把收到的信息拼起来
    {         
      if(nrf24l01i==0)
      {
        Serial.println("nrf24l01->");//打印出来方便调试
      }

      nrf24l01Data=(char)data;
      if( nrf24l01Data=='{') nrf24l01i=0;
      nrf24l01buff[nrf24l01i]=nrf24l01Data;//保存到数组
      nrf24l01i++;////数组长度+1
      if(nrf24l01Data=='}' || nrf24l01i>=33)//如果发现}而说明命令结束(并少于33个字符,太长会出错)
      {               
            nrf24l01nowlast = millis(); //更新当前时间,不然5秒就超时了

            char body[33]={0};
            get_znck_body(nrf24l01buff,body);//获取只是{ckxxxxxx}的字符,因为这是我们的命令格式
            //Serial.println(body);

            //如果命令格式真确则发送到无线串口
            if(strstr(body,"{ck") && strstr(body,"}") )
            {
              //Serial.println(body);

              if(strlen(body)>10)
              {
                  set_onoff(body);  

              }

            }
            memset(nrf24l01buff, 0, 33);
            nrf24l01i=0;//字符长度为0
            Serial.println("-------------------");

            delay(100);
      }
    }
   }   

  val0=digitalRead(pinIn0);//读取数字  
  if(val0==LOW)//检测按键是否按下
  {
    Serial.print("val0 state0=");
    Serial.println(state0);
    if(state0==0)
    {   
       digitalWrite(pinOut0,HIGH);
       state0=1;
       EEPROM.write(0, state0);
       delay(2000);
    }
    else
    {
       digitalWrite(pinOut0,LOW);
       state0=0;
       EEPROM.write(0, state0);
       delay(2000);
    }
  }

  val1=digitalRead(pinIn1);//读取数字  
  if(val1==LOW)//检测按键是否按下
  {
    Serial.print("val1 state1=");
    Serial.println(state1);
    if(state1==0)
    {   
       digitalWrite(pinOut1,HIGH);
       state1=1;
       EEPROM.write(1, state1);
       delay(2000);
    }
    else
    {
       digitalWrite(pinOut1,LOW);
       state1=0;
       EEPROM.write(1, state1);
       delay(2000);
    }
  }

  val2=digitalRead(pinIn2);//读取数字  
  if(val2==LOW)//检测按键是否按下
  {
    Serial.print("val2 state2=");
    Serial.println(state2);
    if(state2==0)
    {   
       digitalWrite(pinOut2,HIGH);
       state2=1;
       EEPROM.write(2, state2);      
       delay(2000);
    }
    else
    {
       digitalWrite(pinOut2,LOW);
       state2=0;
       EEPROM.write(2, state2);
       delay(2000);
    }
  }



  //ON OFF SET
  if(state0==1)
  {
    digitalWrite(pinOut0,HIGH);
  }
  else
  {      
    digitalWrite(pinOut0,LOW);     
  }

  //ON OFF SET
  if(state1==1)
  {
    digitalWrite(pinOut1,HIGH);
  }
  else
  {      
    digitalWrite(pinOut1,LOW);     
  }

  //ON OFF SET
  if(state2==1)
  {
    digitalWrite(pinOut2,HIGH);
  }
  else
  {      
    digitalWrite(pinOut2,LOW);     
  }

}

//初始化Mirf 0初始化1为接收2为发送
void Mirf_Init(int txrx,char *server,int channel){
    //初始化Mirf,用于NRF24l01收发        
    if(txrx==0)  {     
      Mirf.spi = &MirfHardwareSpi;
      Mirf.init();
      Mirf.setRADDR((byte *)server);//设置接收地址
    }

    if(txrx==1)  {     
      Mirf.setRADDR((byte *)server);//设置接收地址
    }
    if(txrx==2)  {
      Mirf.setTADDR((byte *)server);//设置发送地址
    }

    Mirf.payload = sizeof(char);//收发字节
    Mirf.channel = channel;
    Mirf.config();
}

//NRF24l01发送函数
void Mirf_Send(int channel,char *server,char *str){
  Mirf_Init(2,server,channel);
  int bufi=0;
  for(bufi=0;bufi<strlen(str);bufi++){//循环发送
    char words=str[bufi];//发送的字符
    Mirf.send((byte *)&words);//发送命令
    while(Mirf.isSending()){//等待发送完闭
    }
    delay(50);//延时,否则可能出现发送丢失现象
    //Serial.print(words);
  }
  //Serial.println("");
}


//获取只是{ckxxxxxx}的字符,因为这是我们的命令格式
void get_znck_body(char *p,char *s){

  char rechar[33]={0};
  int bufi=0;

  bool isend=false;
  int charnum=0;   

  for(bufi=0;bufi<strlen(p);bufi++){
    //Serial.print(p[bufi]);

    if(p[bufi]=='{'){
      isend=true;
    }
    if(p[bufi]=='}' && isend==true){
      isend=false;
      rechar[charnum]=p[bufi];
      break;
    }
    if(isend){
      if(charnum<33)
      {
        rechar[charnum]=p[bufi];//Serial.print(rechar[charnum]);
        charnum++;        
      }
    }

  }
  //Serial.println("");
  //memcpy(s,rechar,33);
  sprintf(s,"%s",rechar);
}


//
void set_onoff(char *body){

    int s=get_sid(body);
    int n=get_nid(body);
    int d=get_data(body);

    if(  s==sid && n==0 )
    {
      if( d==1 )
      {
        state0=1;
        EEPROM.write(0, state0);         
      }
      if( d==0 )
      {
        state0=0;
        EEPROM.write(0, state0);         
      }
    }

    if(  s==sid && n==1 )
    {
      if( d==1 )
      {
        state1=1;
        EEPROM.write(1, state1);         
      }
      if( d==0 )
      {
        state1=0;
        EEPROM.write(1, state1);         
      }
    }

    if(  s==sid && n==2 )
    {
      if( d==1 )
      {
        state2=1;
        EEPROM.write(2, state2);         
      }
      if( d==0 )
      {
        state2=0;
        EEPROM.write(2, state2);         
      }
    }

    if(  s==sid ){
      char server[10]={0};//server
      sprintf(server,"serv%d",1);
      //Serial.println(server);

      char updateData[33]={0};
      char front[10]={0};
      //memcpy(front,body,9);
      sprintf(front," {ck%03d%03d",s,n);
      sprintf(updateData,"%supdate}",front);
      Serial.println(updateData);

      Mirf_Send(1,server,updateData);

      char client[10]={0};//client
      sprintf(client,"clie%d",sid);
      Mirf_Init(1,client,sid);
    }


}


int get_sid(char *buff){

  if( strstr(buff,"{ck") && strstr(buff,"}") && strlen(buff)>10)
    {
      char charSid[4]={0};
      memcpy(charSid,buff+3,3);
      Serial.println(charSid);
      int intSid=atoi(charSid);
      Serial.println(intSid);
      return intSid;      
    }
    else
    {
      return 0;
    }

}

int get_nid(char *buff){

  if( strstr(buff,"{ck") && strstr(buff,"}") && strlen(buff)>10)
    {
      char charNid[4]={0};
      memcpy(charNid,buff+6,3);
      Serial.println(charNid);
      int intNid=atoi(charNid);
      Serial.println(intNid);
      return intNid;      
    }
    else
    {
      return 0;
    }

}

int get_data(char *buff){

  if( strstr(buff,"{ck") && strstr(buff,"}") && strlen(buff)>10)
    {
      char charData[4]={0};
      memcpy(charData,buff+9,1);
      Serial.println(charData);
      int intData=atoi(charData);
      Serial.println(intData);
      return intData;      
    }
    else
    {
      return 0;
    }

}



值得注意的是,我们采用了EEPROM的3个字节做为记录上次的状态,就算断电再供电,还会是原来的开或闭状态!


刷写arduino程序选哪个板?
请选择板是Arduino Uno,并记得选对对应的串口。

刷写ardruino源代码时,提示库不存在?
请联系qq610854837下载zwifi资料,软件下载目录里的arduino-1.0.5,里面包含了教程使用的所有库。




最后下载程序到arduino多路开关板。




第二部分:测试多路开关板通信

打开:http://192.168.1.1/znck007/mysqltest.php?mode=select


如图填写数据,然天点击添加!数据就会插入到数据库,网关会自动获取当前状态等于0,然后发送到003这个模块!



点击查看数据库记录,会看到你添加的记录。




当刷新发现status=1的话,那么说明网关和多路开关模块通信就正常了。
如果status不等于1,然后num等于6(num是超时计数),那么通信不正常,请用串口检查哪里出现问题!

同时也可以用数据库软件Navicat for MySQL直接操作!




《WIFI作品DIY教程系列》
WIFI作品DIY教程01-《wifi开发板》介绍和联网等配置教程
WIFI作品DIY教程02-《openwrt摄像头》3D外壳和diy介绍
WIFI作品DIY教程03-《WIFI音响/MP3播放器/电台》
WIFI作品DIY教程04-《WIFI烟雾煤气报警器》
WIFI作品DIY教程05-《家居服务器》web server(php+mysql+uhttpd)
WIFI作品DIY教程06-《openwrt后台程序》控制mysql、串口通信等
WIFI作品DIY教程07-《wifi家居网关》家居控制中心zigbee/nrf24l01等
WIFI作品DIY教程08-《智能开关》电灯开关、插座(nrf24l01/zigbee)
WIFI作品DIY教程09-《人体红外检测》安防入侵报警功能WIFI作品DIY教程10-《温湿传感器》DS18B20、DHT11应用。


《Arduino开源智能家居DIY教程系列》
Arduino开源智能家居《花絮1》zigbee小底板DIY成功
Arduino开源智能家居《认识Zigbee》zigbee功能和自组网介绍
Arduino开源智能家居《zigbee开发板》手机/按键点亮LED
Arduino开源智能家居01《网关》升级版网关正式教程(zigbee)
Arduino开源智能家居02《温湿传感器》什么样温湿度才适居
Arduino开源智能家居03《开发板套件》学习zigbee家居-性价比高
Arduino开源智能家居04《插座开关》手机控制:网扇、空调...
Arduino开源智能家居05《红外线》手机红外线控制电器

《百元智能家居DIY教程系列》
《智能家居网关》DIY制作图文教程01-百元智能家居系列
《智能温湿度》DIY制作图文教程02-百元智能家居系列
《智能插座》DIY制作图文教程03-百元智能家居系列
《智能电灯开关》DIY制作图文教程04-百元智能家居系列
《手机红外线》DIY制作图文教程05-百元智能家居系列
关注@智能创客  微信:znck007(打造DIY创客平台)







作者: 韩文博    时间: 2015-1-14 17:48
我是初学者  我看完之后第一个感觉是 我知道要怎么做了 可问题来了 我需要买哪些东西呢?
希望楼主可以把需要买的东西来个淘宝链接
作者: znck007    时间: 2015-1-15 00:23
韩文博 发表于 2015-1-14 17:48
我是初学者  我看完之后第一个感觉是 我知道要怎么做了 可问题来了 我需要买哪些东西呢?
希望楼主可以把需 ...

你好呀。http://item.taobao.com/item.htm? ... &id=41584503200拍下这个联系我们旺旺就可以了。
作者: 老催    时间: 2015-1-16 16:37
编译不通过
作者: 老催    时间: 2015-1-16 16:41
#include
#include
#include
#include
这几个库文件没有?
作者: znck007    时间: 2015-1-16 16:48
老催 发表于 2015-1-16 16:41
#include
#include
#include

编辑的时候可能文字丢失了,已重新更新了。
作者: znck007    时间: 2015-1-16 16:48
老催 发表于 2015-1-16 16:41
#include
#include
#include

编辑的时候可能文字丢失了,已重新更新了。
作者: 坏的刚刚好    时间: 2015-3-3 19:55
zwifi+zigbee+多路开关板一直调试不通过
作者: znck007    时间: 2015-3-4 10:32
坏的刚刚好 发表于 2015-3-3 19:55
zwifi+zigbee+多路开关板一直调试不通过

qq联系。
作者: clanaid    时间: 2015-3-22 19:23
楼主请问下,开关的数据库数据是不是只有一条记录,而其他传感器是不是每几秒就更新一条数据上去,那样服务器端不会太过频繁访问数据库了?这样会不会造成容易奔溃之类的
作者: znck007    时间: 2015-3-24 09:52
clanaid 发表于 2015-3-22 19:23
楼主请问下,开关的数据库数据是不是只有一条记录,而其他传感器是不是每几秒就更新一条数据上去,那样服务 ...

是的,所以最好是1分钟上传一次。不然就用强大的数据库
作者: clanaid    时间: 2015-3-24 14:56
znck007 发表于 2015-3-24 09:52
是的,所以最好是1分钟上传一次。不然就用强大的数据库

能说一下你们数据库的设计是怎样的么
作者: znck007    时间: 2015-4-2 09:52
clanaid 发表于 2015-3-24 14:56
能说一下你们数据库的设计是怎样的么

就是mysql呀。
作者: zyw    时间: 2015-4-2 10:43
代码168 行 错误 nrf24l01Data=(char)data; 去掉  后面的
作者: yjyi6ht3    时间: 2015-5-13 07:38
本帖最后由 创客帝国 于 2015-7-24 10:35 编辑

向楼主致敬











作者: ocvbemoa    时间: 2015-5-22 20:43
本帖最后由 创客帝国 于 2015-7-24 10:35 编辑

顶上去!顶上去!











作者: 青哥    时间: 2015-9-12 08:11
谢谢楼主
作者: yixian520    时间: 2015-9-19 13:43

强强强~~,太好了,谢谢
作者: alex_vip    时间: 2015-10-5 14:53
谢谢分享
作者: drfat    时间: 2016-1-9 09:36
学习学习!
作者: Raspberry    时间: 2016-1-13 21:41
很好的教程,学习了
作者: dpzkm    时间: 2016-2-25 23:42
水平很高啊,值得学习
作者: ridxqqqq    时间: 2016-3-24 15:55
我的倒是刷进去了,但没反应呢网页下面只显示0000000
作者: zmlxh168    时间: 2016-4-19 20:19
好!楼主不错!赞一个!
作者: 场立誉lm3ahvi    时间: 2016-4-25 16:27
非常高兴遇到这么好的帖子 谢谢




欢迎光临 智能创客,中国最大的极客空间,智能平台,免费教学,视频教程,手把手教你创造儿时梦想! (http://luntan.znck007.com/) Powered by Discuz! X3.2