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 )
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进入函数里。
1 2 | /* 初始化操作系统 */ osal_init_system(); |
三、这里就是系统初始化,我们要看的是,我们的任务如何建立!双击osalInitTasks(),再按F12进入函数里。
四、看到了吗?串口通信被分配任务了。SerialApp_Init( taskID );这个可以是我们自己写的任务。双击,再按F12进入函数里。
五、在这里我们可以自己定义自己的东东了,初始化!
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系统有事件(消息),你可以认为是有动作,就会激活这里。
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 ); } |
七、实例,我们在电脑用串口如何发送信息给它,它在哪里收到!
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开源智能家居》吧。亲……