27
2019
04

Arduino开源智能家居《认识Zigbee》zigbee功能和自组网介绍

HI创友们,这周工厂已经在打板了,下周就能看到《Arduino开源智能家居》的网关作品了!

这次我们采用Arduino+Zigbee模式,也就是真正意思个的智能家居系统哦亲!

Zigbee是什么,有很多人可能只是知道有这么个东西,但不明白具体是什么,有什么作用!
以下我们用通俗易懂(以比喻为主)的方式来讲一下ZIGBEE。

本帖隐藏的内容

这就是一块Zigbee模块(硬件),有两个1元硬币大。





《功能说明》
一、51单片机
简单的说,他就是一块51单片机(含无线传输功能),用IAR 8.10软件可以直接对他编程,比如点亮LED、收发数据、点击按键(IO)、输入数据(传感器)等。
很多arduino的同学就可以用他来学51单片机啦,一通百通嘛。51单片机其实就这样……

二、无线传输(远)
之前我们用NRF24l01,他的距离比较近,我们用zigbee代替它(这时zigbee等于Nrf24l01),加上天线后可以达上千米。这样的传输距离够用了吧!

三、用最多的智能家居协议
Zigbee是目前来说,全球智能家居用最多的协议之一(国际上是有统一协议规范的,但要按协议写代码然后还要认证)。
这个怎么理解呢?打个比方:如果以后你买个电视,他就能自动连入的你家居网关(zigbee协议一样)。用个词:万能家居网关,控制所有家居!

四、两两通信
一般一个智能家居系统有个网关,和很多传感器、设备(电器),zigbee可以不通过网关,就是(两个传感器)、(传感器和设备)、(设备和设备)之间可以互通信,可以不用通网关。比如:5楼的设备只和5楼的通信,不用经过1楼的总网关。这样就更方便快捷了嘛……

五、自网组(牛b、专业了)

简单的说,他就是小型的3G或CDMA或GSM网络。每个zigbee类式于一个基站,他们可以自己组成一个网。然后你在一个zigbee说话,任何一个点都可以收到(可以加密防窃听)。
举例子了:
你家是一栋别墅有3栋楼,每栋3层。
你想在某个室间控制整个别野,好办了就用zigbee,栋和栋之间的zigbee用天线,他们会自组网,断线自离网,上线自恢复。
只要你在一点操作,哪里都能收得到(也就是你打电话给你妹,你妹如果手机有信号,在哪都可以收得到!)

六、支持65000个节点
ZigBee大规模的组网能力——每个网络65000个节点,而每个蓝牙网络只有8个节点。


不想说了不想说了,反应已经够我们用了!


接下来说一下cdoe,我是个程序员嘛,这段日子都在研究自组网。自组网是最难的,你听我的解说就不会难了!
让我来说说zigbee自组网,杀上代码:


小菜,你只要关心图上的二个文件就好,其它你也不懂的,以后慢慢来!

一、我们双击ZMain.c,找到main( void )

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/*********************************************************************
 * 函数名称:main
 * 功    能:主函数。
 * 入口参数:无
 * 出口参数:无
 * 返 回 值:无
 ********************************************************************/
int main( void )
{
  /* 关闭中断 */
  osal_int_disable( INTS_ALL );
 
  /* 初始化系统时钟及LED等 */
  HAL_BOARD_INIT();
 
  /* 检测供电电压 */
  zmain_vdd_check();
 
  /* 初始化堆栈 */
  zmain_ram_init();
 
  /* 初始化主板外围I/O */
  InitBoard( OB_COLD );
 
  /* 初始化硬件抽象层驱动 */
  HalDriverInit();
 
  /* 初始化系统NV非易失性存储 */
  osal_nv_init( NULL );
 
  /* 初始化基础NV项 */
  zgInit();
 
  /* 初始化MAC */
  ZMacInit();
 
  /* 确定扩展地址 */
  zmain_ext_addr();
 
  /* 初始化应用框架 */
#ifndef NONWK
  afInit(); // AF应用框架不是系统的任务,因此调用它的初始化程序
#endif
 
  /* 初始化操作系统 */
  osal_init_system();
 
  /* 允许中断 */
  osal_int_enable( INTS_ALL );
 
  /* 最终板级初始化 */
  InitBoard( OB_READY );
 
  /* 显示该设备信息 */
  zmain_dev_info();
 
  /* 如果定义了LCD,则在LCD上显示设备信息 */
#ifdef LCD_SUPPORTED
  zmain_lcd_init();
#endif
 
#ifdef WDT_IN_PM1
  /* 如果看门狗被使用,此处使能 */
  WatchDogEnable( WDTIMX );
#endif
  //printf("osal_start_system");
  osal_start_system(); // 进入系统调度,无返回
 
  return ( 0 );
}




二、只关注这个,然后双击它,再按F12进入函数里。

[C] 纯文本查看 复制代码
1
2
/* 初始化操作系统 */
osal_init_system();




三、这里就是系统初始化,我们要看的是,我们的任务如何建立!双击osalInitTasks(),再按F12进入函数里。





四、看到了吗?串口通信被分配任务了。SerialApp_Init( taskID );这个可以是我们自己写的任务。双击,再按F12进入函数里。




五、在这里我们可以自己定义自己的东东了,初始化!

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*********************************************************************
 * 函数名称:SerialApp_Init
 * 功    能:SerialApp的初始化函数。
 * 入口参数:task_id  由OSAL分配的任务ID。该ID被用来发送消息和设定定时
 *           器。
 * 出口参数:无
 * 返 回 值:无
 ********************************************************************/
void SerialApp_Init( uint8 task_id )
{
  halUARTCfg_t uartConfig;     // 定义串口配置结构体变量
 
  SerialApp_MsgID = 0x00;      // 初始化传输序号
  SerialApp_SeqRx = 0xC3;      // 初始化接收序号为十进制195
  SerialApp_TaskID = task_id;  // 获取应用任务ID
 
  /* 初始化发送信息目的地址 */
  SerialApp_DstAddr.endPoint = 0;
  SerialApp_DstAddr.addr.shortAddr = 0;
  SerialApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
   
  /* 初始化响应信息的目的地址 */
  SerialApp_RspDstAddr.endPoint = 0;
  SerialApp_RspDstAddr.addr.shortAddr = 0;
  SerialApp_RspDstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
 
  /* 注册端点描述符 */
  afRegister( (endPointDesc_t *)&SerialApp_epDesc );
 
  /* 注册按键事件,将所有按键事件发送给本应用任务SerialApp_TaskID */
  RegisterForKeys( task_id );
 
  /* 串口初始化 */
  uartConfig.configured           = TRUE;             
  uartConfig.baudRate             = SERIAL_APP_BAUD;   // 波特率
  uartConfig.flowControl          = TRUE;              // 流控使能
  uartConfig.flowControlThreshold = SERIAL_APP_THRESH; // 流控阈值
  uartConfig.rx.maxBufSize        = SERIAL_APP_RX_MAX; // 最大接收量
  uartConfig.tx.maxBufSize        = SERIAL_APP_TX_MAX; // 最大发送量
  uartConfig.idleTimeout          = SERIAL_APP_IDLE;   // 空闲时间
  uartConfig.intEnable            = TRUE;              // 中断使能
/* 若使能了环回测试功能 */
#if SERIAL_APP_LOOPBACK
  uartConfig.callBackFunc         = rxCB_Loopback;     // 回调函数
/* 若未使能环回测试功能 */
#else
  uartConfig.callBackFunc         = rxCB;              // 回调函数
#endif
  HalUARTOpen (SERIAL_APP_PORT, &uartConfig);          // 打开串口
 
    
  /* 若包含了LCD_SUPPORTED编译选项,则在LCD上进行相应的显示 */
#if defined ( LCD_SUPPORTED )
#if defined ( ZIGBEEPRO )
  HalLcdWriteString( "SerialApp(ZigBeePRO)", HAL_LCD_LINE_2 );
#else
  HalLcdWriteString( "SerialApp(ZigBee2007)", HAL_LCD_LINE_2 );
#endif
#endif
 
  /* ZDO信息注册 */
  /* 注册ZDO的簇End_Device_Bind_rsp,将收到的End_Device_Bind_rsp事件
     发送给本应用任务SerialApp_TaskID
   */   
  ZDO_RegisterForZDOMsg( SerialApp_TaskID, End_Device_Bind_rsp );
   
  /* ZDO信息注册 */
  /* 注册ZDO的簇Match_Desc_rsp,将收到的Match_Desc_rsp事件发送给本应
     用任务SerialApp_TaskID
   */ 
  ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp );
}







六、zigbee系统有事件(消息),你可以认为是有动作,就会激活这里。

[C] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*********************************************************************
 * 函数名称:SerialApp_ProcessEvent
 * 功    能:SerialApp的任务事件处理函数。
 * 入口参数:task_id  由OSAL分配的任务ID。
 *           events   准备处理的事件。该变量是一个位图,可包含多个事件。
 * 出口参数:无
 * 返 回 值:尚未处理的事件。
 ********************************************************************/
UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )
{
  /* 系统消息事件 */
  if ( events & SYS_EVENT_MSG )
  {
    afIncomingMSGPacket_t *MSGpkt;
 
    while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(
                                                          SerialApp_TaskID )))
    {
      switch ( MSGpkt->hdr.event )
      {
        /* ZDO信息输入事件 */
        case ZDO_CB_MSG:
          // 调用ZDO信息输入事件处理函数
          SerialApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );
          break;
         
        /* 按键事件 */ 
        case KEY_CHANGE:
          HalUARTWrite(0,"KEY_CHANGE",4);
          // 调用按键事件处理函数
          SerialApp_HandleKeys( ((keyChange_t *)MSGpkt)->state,
                                ((keyChange_t *)MSGpkt)->keys );
          break;
         
        /* AF输入信息事件 */
        case AF_INCOMING_MSG_CMD:
          // 调用输入信息事件处理函数
          SerialApp_ProcessMSGCmd( MSGpkt );
          break;
   
        default:
          break;
      }
 
      osal_msg_deallocate( (uint8 *)MSGpkt );  // 释放存储器
    }
 
    return ( events ^ SYS_EVENT_MSG );  // 返回未处理的事件
  }
 
  /* 发送数据事件 */
  if ( events & SERIALAPP_MSG_SEND_EVT )
  {
    SerialApp_SendData( otaBuf, otaLen );  // 调用发送数据处理函数
 
    return ( events ^ SERIALAPP_MSG_SEND_EVT );
  }
 
  /* 发送数据重传事件 */
  if ( events & SERIALAPP_MSG_RTRY_EVT )
  {
    /* 若重传计数不为0 */
    if ( --rtryCnt )
    {
      /* 发送OTA信息(需要重传的发送数据) */
      AF_DataRequest( &SerialApp_DstAddr,
                      (endPointDesc_t *)&SerialApp_epDesc,
                       SERIALAPP_CLUSTERID1, otaLen, otaBuf,
                      &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS );
       
      /* 在指定时间SERIALAPP_MSG_RTRY_TIMEOUT到时后触发发送数据重传事件 */
      osal_start_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT,
                                            SERIALAPP_MSG_RTRY_TIMEOUT );
    }
    else
    {
      FREE_OTABUF();  // 处理缓冲区
    }
 
    return ( events ^ SERIALAPP_MSG_RTRY_EVT );
  }
 
  /* 响应信息重传事件 */
  if ( events & SERIALAPP_RSP_RTRY_EVT )
  {
    /* 发送OTA信息(需要重传的响应信息)*/
    afStatus_t stat = AF_DataRequest( &SerialApp_RspDstAddr,
                                      (endPointDesc_t *)&SerialApp_epDesc,
                                       SERIALAPP_CLUSTERID2,
                                       SERIAL_APP_RSP_CNT, rspBuf,
                                      &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS );
 
    /* 若发送OTA信息(需要重传的响应信息)不成功*/
    if ( stat != afStatus_SUCCESS )
    {
      /* 在指定时间SERIALAPP_RSP_RTRY_TIMEOUT到时后触发响应信息重传事件 */
      osal_start_timerEx( SerialApp_TaskID, SERIALAPP_RSP_RTRY_EVT,
                                            SERIALAPP_RSP_RTRY_TIMEOUT );
    }
 
    return ( events ^ SERIALAPP_RSP_RTRY_EVT );
  }
 
  /* 若使能了环回测试 */
#if SERIAL_APP_LOOPBACK
  /* 串口重发送事件 */
  if ( events & SERIALAPP_TX_RTRY_EVT )
  { /* 若接收缓冲区中有数据 */
    if ( rxLen )
    { /* 若将接收缓冲区中的数据写入到串口不成功 */
      if ( !HalUARTWrite( SERIAL_APP_PORT, rxBuf, rxLen ) )
      { /* 在指定时间SERIALAPP_TX_RTRY_TIMEOUT后触发串口重发送事件 */
        osal_start_timerEx( SerialApp_TaskID, SERIALAPP_TX_RTRY_EVT,
                                              SERIALAPP_TX_RTRY_TIMEOUT );
      }
      /* 若将接收缓冲区中的数据写入到串口成功 */
      else
      {
        rxLen = 0;  // 清零接收缓冲区中数据长度变量
      }
    }
 
    return ( events ^ SERIALAPP_TX_RTRY_EVT );
  }
#endif
 
  /* 丢弃未知事件 */
  return ( 0 );
}






七、实例,我们在电脑用串口如何发送信息给它,它在哪里收到!

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*********************************************************************
 * 函数名称:rxCB_Loopback
 * 功    能:串口接收回调函数(环回测试时使用)
 * 入口参数:port   串口号
 *           event  串口事件
 * 出口参数:无
 * 返 回 值:无
 ********************************************************************/
static void rxCB_Loopback( uint8 port, uint8 event )
{
  /* 若接收缓冲区中有数据 */
  if ( rxLen )
  {
    /* 若将接收缓冲区中的数据写入到串口不成功 */
    if ( !HalUARTWrite( SERIAL_APP_PORT, rxBuf, rxLen ) )
    {
      /* 在指定时间SERIALAPP_TX_RTRY_TIMEOUT后触发串口重发送事件 */
      osal_start_timerEx( SerialApp_TaskID, SERIALAPP_TX_RTRY_EVT,
                                            SERIALAPP_TX_RTRY_TIMEOUT );
      return// 返回
    }
    /* 若将接收缓冲区中的数据写入到串口成功 */
    else
    { /* 停止串口重发送事件 */
      osal_stop_timerEx( SerialApp_TaskID, SERIALAPP_TX_RTRY_EVT );
    }
  }
 
  /* 若从串口读取数据不成功(读出的数据长度为0) */
  if ( !(rxLen = HalUARTRead( port, rxBuf, SERIAL_APP_RX_CNT )) )
  {
    return// 返回
  }
 
  /* 若将已从串口读取的数据回写到串口成功 */
  if ( HalUARTWrite( SERIAL_APP_PORT, rxBuf, rxLen ) )
  {
    rxLen = 0;  // 清零接收缓冲区中数据长度变量
  }
  /* 若将已从串口读取的数据回写到串口不成功 */
  else
  { /* 在指定时间SERIALAPP_TX_RTRY_TIMEOUT后触发串口重发送事件 */
    osal_start_timerEx( SerialApp_TaskID, SERIALAPP_TX_RTRY_EVT,
                                          SERIALAPP_TX_RTRY_TIMEOUT );
  }




这个就是我们的串口数据了rxBuf,接到后判断是什么字符,然后做相应的处理吧(操控你的世界吧),亲!

8、好了,第一篇zigbee就先写到这里。
让住,很多东西我们可以不用去理解(除非你有能力),就在SerialApp.c写上自己要实现的代码就OK了。
难吗?难吗?难吗?不啊小菜……


当然现在还没有发正式教程,没有IAR 8.10软件下载没有源代码(你没有硬件也没有),所以等期我们的《Arduino开源智能家居》吧。亲……


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。