Z-Stack + OSAL操作系统
Zigbee协议栈与Zigbee协议
协议是一系列的通信标准,通信双方需要共同按照这一标准进行正常的数据发射和接收。协议栈是协议的具体实现形式,简单地说就是协议栈是协议和用户之间的一个接口。开发人员通过协议栈来使用这个协议。进而实现数据的收发。
Zigbee的体系结构由称为层的各模块组成。每一层为其上层提供特定的服务:即由数据服务实体提供数据传输服务;管理实体提供所有的其他管理服务。毎个服务实体懣过相应的服务接入点(SAP为其上层提供一个接口,每个服务接入点通过服务原语来完成所对应的功能, ZigBee协议的体系结构如下图所示:
Zigbee基本概念
设备类型(一个网络必须最少有一个协调器,多个路由器和多个终端设备)
-
Coordinator(协调器)
负责启动整个网络,它也是网络的第一个设备。协调器选择一个信道和一个网络ID(也称之为PAN ID),随后启动整个网络,协调器也可以用来协助建立网络中安全层和应用层绑定(bingings)。
-
Router(路由器)
允许其他设备加入网路,多眺路由和协助它自己的由电池供电的终端设备的通讯。
通常,路由器希望一直处于活动状态,因此他必须使用主电源供电。但是当使用树状网络拓扑结构时,允许路由间隔一定的周期操作一次,这样就可以使用电磁给其供电。
-
End-Device(终端设备)
终端设备没有特定的维持网络结构的责任,它可以睡眠或唤醒,终端设备对存储空间(特别是RAM的需要)比较小。
网络结构
上图是一个简单的Zigbee网络示意图,其中红色节点为Coordinator,黄色节点为Router,绿色节点为End-Device。
协议栈规范
协议栈规范由Zigbee联盟定义指定。在同一网络中的设备必须符同一个协议栈规范。
Zigbee联盟为Zigbee协议栈2007定义了两个规范:Zigbee个Zigbee PRO。所有的设备只要遵守了该规范,即使在不同的厂商买的不同的设备同样可以形成网络。
如果开发正改变了规范,它的设备只能在自己的产品中使用。不能与其他的产品通信,更改网络之后的规范称之为“特定网络”规范。
协议栈ID号可以通过查询设备发送的beacon帧获得。在设备加入网络之前,首先需要确认协议栈规范的ID,“特定网络”规范ID号为0;Zigbee协议栈规范的ID号为1;Zigbee PRO协议的ID号为2.协议栈规范的ID(STACK_PROFLLE_ID)在nwk_globals.h中定义:
// nwk_globals.h
// Controls various stack parameter settings
#define NETWORK_SPECIFIC 0
#define HOME_CONTROLS 1
#define ZIGBEEPRO_PROFILE 2
#define GENERIC_STAR 3
#define GENERIC_TREE 4
#if defined ( ZIGBEEPRO )
#define STACK_PROFILE_ID ZIGBEEPRO_PROFILE
#else
#define STACK_PROFILE_ID HOME_CONTROLS
#endif
// 在fwConfig.cfg文件定义
/* Enable Zigbee-Pro */
-ZIGBEEPRO
拓扑结构
支持星状、树(簇)状和网状三种网络拓扑结构。
-
星型拓扑
最简单的一种拓扑形式,包含一个Coordinator(协调者)节点和一系列的End Device(终端)节点,每一个End Device节点只能和Coordinator节点进行通信。两个End Device节点之间通讯必须通过Coordintor节点进行信息的转发。
-
树型拓扑
-
Mesh拓扑(网状拓扑)
在Z-Stack中网络拓扑结构定义如下:
// Controls the operational mode of network
#define NWK_MODE_STAR 0
#define NWK_MODE_TREE 1
#define NWK_MODE_MESH 2
#if ( STACK_PROFILE_ID == ZIGBEEPRO_PROFILE )
#define MAX_NODE_DEPTH 20
#define NWK_MODE NWK_MODE_MESH // 网状网络
#define SECURITY_MODE SECURITY_COMMERCIAL
#if ( SECURE != 0 )
#define USE_NWK_SECURITY 1 // true or false
#define SECURITY_LEVEL 5
#else
#define USE_NWK_SECURITY 0 // true or false
#define SECURITY_LEVEL 0
#endif
信标与非信标模式
Zigbee网络的工作模式可分为信标(Beaeon)和非信标(Non-beaeon)两种模式。
-
信标模式
实现了我网络中所有设备的同步工作和同步休眠。以达到最大程度节省功耗。
协调器负责以一定的时间间隔(一般在15ms-mins之间)想网络广播发送信标帧。
-
非信标模式
只允许终端设备进行周期性休眠,协调器和所有路由器设备必须长期处于工作状态。
父节点为终端设备子节点焕春数据,终端设备主动向其父节点提取数据的机制。实现终端设备的周期性(周期可设置)休眠。
地址定义
Zigbee设备有两种类型的地址。一种是64位IEEE地址,即MAC地址,另一种是16位网络地址。
-
64位地址
全球唯一地址(MAC地址)由设备制造商设置,设备在生命周期一直拥有。
-
16位网络地址
设备加入网络后分配的,在网络中唯一。用来在网路哟中鉴别设备和发送数据。协调器的网络地址为0x0000。
地址分配机制,Zigbee 2007 PRo使用随机地址分配机制,对新加入的节点使用随机地址分配,为保证网络内地址分配不重复,使用其余的随机地址再进行分配。当一个节点加入时,将接收到父节点的的随机分配地址然后后产生”设备声明“(包含分配到的网络地址和IEEE地址)发送至网络中的其余节点。如果另一个节点有着同样的网络地址,则通过路由器广播“网络状态-地址冲突”至网络中的所有节点。所有发生网络地址冲突的节点更改自己的网络地址,然后再发起“设备声明”检测新的网络地址是否冲突。
终端设备不会广播“地址冲突”,他们的父节点会帮助完成。如果一个终端设备发生了”地址冲突“,他们的父节点发送”重新加入“消息至终端设设备,并要求他们更改网络地址,然后终端设备再发起”设备声明“检测新的网络地址是否冲突。
当接收到“设备声明”后,关联表和绑定表将被更新使用心得网络地址,但是路由表不会被更新。
在每个路由加如网络之前,寻址方案需要知道和配是一些参数。
- MAX_DEPTH(最大网络深度)
- MAX_ROUTERS(最多路由数)
- MAX_CHILDREN(最多子节点数)
#if ( STACK_PROFILE_ID == ZIGBEEPRO_PROFILE )
uint8 CskipRtrs[1] = {0};
uint8 CskipChldrn[1] = {0};
- Cm(nwkMaxChildren):每个父节点可以联接的子节点的总个数;
- Rm(nwkMaxRouters):在Cm中可以连接的子节点的总个数;
- Lm:最大网络深度,协调器深度为0;
这三个参数的值在Z-stack中分别由变量CskipChlarn、CskipRtrs、MAX_NODE_DEPTH决定。这三个变量可以在NWK中的nwk_globals.c和nwk_globals.h两个文件中找到。
寻址
为了向一个在ZigBee网络中的设备发送数据,应用程序通常使用AF_DataRequest()函数。数据包将要发送给一个afAddType_t(在AF.h中定义)类型的目标设备。
typedef struct
{
union
{
uint16 shortAddr;
ZLongAddr_t extAddr;
} addr;
afAddrMode_t addrMode;
uint8 endPoint;
uint16 panId; // used for the INTER_PAN feature
} afAddrType_t;
注意,除了网络地址之外,还要制定地址模式参数。目的地址模式可以设置为以下几个值:
typedef enum
{
afAddrNotPresent = AddrNotPresent,
afAddr16Bit = Addr16Bit,
afAddr64Bit = Addr64Bit,
afAddrGroup = AddrGroup,
afAddrBroadcast = AddrBroadcast
} afAddrMode_t;
因为在ZigBee网络中,数据包可以单点传送(unicast),多点传送(multicast)或则广播传送,所以必须有地址模式参数。一个单点传送数据包只发送给一个设备,多点传送数据包则要传送一组设备。二广播数据则要发送给网络中的所有节点。
-
单点传送( Unicast)
Dicast是标准寻址模式,它将数据包发送给一个已经知道网络地址的网络设备。将afAddrMode设置为Addr16Bit并且在数据包中携带目标设备地址。
-
间接传送( ndirect)
当应用程序不知道数据包的目标设备在哪里的时候使用的模式。将模式设为AddrNotPresent并且目标地址没有指定。取代它的是从发送设备的栈的绑定表中査找目标设备。这种特点称之为源绑定。当数据向下发送到达栈中,从绑定表中査找并且使用该目标地址。这样,数据包将被处理成为一个标准的单点传送数据包。如果在绑定表中找到多个设备,则向每个设备都发送一个数据包的拷贝。
-
广播传送( broadcast)
当应用程序需要将数据包发送给网络的每一个设备时,使用这种模式。地址模式设置为AddrBroadcast。目标地址可以设置为下面广播地址的种:NWK BROADCAST SHORTADDR DEVALL(0xFFF数据包将被传送到网络上的所有设备,包括睡眠中的设备。对于睡眠中的设备,数据包将被保留在其父亲节点直到査询到它,或者消息超时( NWK INDIRECT MSG TIMEOUT在f8 wConifg cfg中NWK BROADCAST SHORTADDR DEVRXON( OXFFFD)数据包将被传送到网络上的所有在空闲时打开接收的设备( RXONWHENIDLE),也就是说,除了睡眠中的所有设备。NWK BROADCAST SHORTADDR DEVZCZR(0xFFC)数据包发送给所有的路由器,包括协调器。
-
组寻址( Group Addressing)
当应用程序需要将数据包发送给网络上的一组设备时,使用该模式。地址模式设置为afAddr Group并且addr.shortAddr设置为组|D。在使用这个功能呢之前必须在网络中定义组。(参见Z- -stack Ap|文档中的 aps AddGroup0函数注意组可以用来关联间接寻址。再绑定表中找到的目标地址可能是是单点传送或者是一个组地址。另外,广播发送可以看做是一个组寻址的特例。下面的代码是一个设备怎样加入到个D为1的组当中:
// Group Table Element typedef struct { uint16 ID; // Unique to this table uint8 name[APS_GROUP_NAME_LEN]; // Human readable name of group } aps_Group_t;
设备
一个节点就是一个设备,对应一个无线单片机(CC2530);一个设备有一个射频终端,具有唯一的IEEE地址(64位)和网络地址(16位)在协议栈中不同的设备有相应的配置文件:
- 协调器配置文件:f8wCoord.cfg
- 路由器配置文件:f8wRouter.cfg
- 终端设备配置文件:f8wEndev.cfg
重要设备地址
应用程序可能需要知道它的设备地址和父亲地址。使用下面的函数获取设备地址(在 ZStackAP中定义)
- NLME GetshortAddr(返回本设备的16位网络地址
- NLME GetExtAddr0—返回本设备的64位扩展地址
- 使用下面的函数获取该设备的父亲设备的地址:
- NLME GetCoordShortAddri0—返回本设备的父亲设备的16位网络地址
- NLME GetCoord ExtAddrt0——返回本设备的父亲设备的64位扩展地址
如何使用ZigBee协议栈(一般步骤)
- 组网:调用协议栈的组网函数、加入网络函数,实现网络的建立与节点的加入。
- 发送:发送节点调用协议栈的无线数据发送函数,实现无线数据发送。
- 接收:接收节点调用协议栈的无线数据接收函数,实现无线数据接收
协议栈工作流程
自己添加的应用任务程序在Zstack中的调用过程:
main() → osal_init_system() → osalInitTask() → SampleApp_Init()
-
osal_init_system:初始化操作系统
// Initialize the Memory Allocation System osal_mem_init(); // Initialize the message queue osal_qHead = NULL; // Initialize the timers osalTimerInit(); // Initialize the Power Management System osal_pwrmgr_init(); // Initialize the system tasks. osalInitTasks(); /*重要 任务初始化函数,我们只关心这个函数*/ // Setup efficient search for the first free block of heap. osal_mem_kick();
-
osalInitTasks(); 任务初始化函数,
void osalInitTasks( void ) // 初始化任务,在OSAL_SampleApp.c文件中 { uint8 taskID = 0; // 为当前OSAL中各任务分配存储空间,tasksEvents指向任务数组 tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt); // 给分配的空间清0 osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt)); // 任务优先级由高向低依次排列,高优先级对应的TaskID的值反而小 macTaskInit( taskID++ ); // macTaskInit(0),初始化网络层任务 用户无需考虑 nwk_init( taskID++ ); Hal_Init( taskID++ ); #if defined( MT_TASK ) MT_TaskInit( taskID++ ); #endif APS_Init( taskID++ ); #if defined ( ZIGBEE_FRAGMENTATION ) APSF_Init( taskID++ ); #endif ZDApp_Init( taskID++ ); // ZDApp_Init(4) 用户需考虑 #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT ) ZDNwkMgr_Init( taskID++ );// 初始化网络管理任务 #endif // 用户创建的任务 GenericApp_Init( taskID ); // SampleApp_Init_Init(5),用户需考虑 。重要!!!!! }
任务初始化,就是为系统个任务分配纯初空间,在为各任务分配TaskID;这里的孙旭要注意。系统主循环函数里 tasksEvents[idx] 和 taskArr[idx] 的 idx 与这里taskUD是一一对应关系。
指针数组tasksEvents[] 里面最终分别指向的是各任务存储空间,指针数组tasksArr[] 里面最终分别指向的是各任务事件处理韩数,这两个数组里面的各元素孙媳要一一对应,后面任务调会调用相应的事件处理函数。
SampleApp_Init() 是我们应用协议栈例程的必要函数,用户通常在这里初始化自己的东西。
-
轮询查询
在OSAL_SampleApp.c文件中,taskArr[] 事件数组。
// The order in this table must be identical to the task initialization calls below in osalInitTask. const pTaskEventHandlerFn tasksArr[] = { macEventLoop, nwk_event_loop, Hal_ProcessEvent, #if defined( MT_TASK ) MT_ProcessEvent, #endif APS_event_loop, #if defined ( ZIGBEE_FRAGMENTATION ) APSF_ProcessEvent, #endif ZDApp_event_loop, #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT ) ZDNwkMgr_event_loop, #endif SampleApp_ProcessEvent };
Z-Stack中操作系统是基于优先级的轮转查询式操作系统,执行流程图如下:
-
osal_start_system() 运行操作系统
osal_start_system() 最终调用 osal_run_system()
void osal_run_system( void ) { uint8 idx = 0; osalTimeUpdate(); // 扫描那个事件被触发了,然后设置相应的标志位 Hal_ProcessPoll(); // 轮询TIMER 与 UART do { if (tasksEvents[idx]) // Task is highest priority that is ready. { break; // 得到待处理的最高优先级任务索引号 } } while (++idx < tasksCnt); if (idx < tasksCnt) { uint16 events; halIntState_t intState; HAL_ENTER_CRITICAL_SECTION(intState); // 进入临界区,保护 events = tasksEvents[idx]; // 提取本次需要处理的任务中的事件 tasksEvents[idx] = 0; // 清除本次任务的事件 HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区 activeTaskID = idx; events = (tasksArr[idx])( idx, events ); //通过指针调用任务处理函数,关键!!!! activeTaskID = TASK_NO_TASK; HAL_ENTER_CRITICAL_SECTION(intState); // 进入临界区 tasksEvents[idx] |= events; // 保存未处理事件 Add back unprocessed events to the current task. HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区 } #if defined( POWER_SAVING ) else // Complete pass through all task events with no activity? { osal_pwrmgr_powerconserve(); // Put the processor/system into sleep } #endif /* Yield in case cooperative scheduling is being used. */ #if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0) { osal_task_yield(); } #endif }
events = tasksEvents[idx] 进入tasksEvents[idx] 数组定义,发现恰好是osalInitTasks() 函数里面分配空间初始化过的tasksEvents。而且taskID一一对应。这就是初始化与调用的关系。taskID把任务联系起来了。
-
SampleApp_Init() 用户应用任务初始化函数
void SampleApp_Init( uint8 task_id ) { SampleApp_TaskID = task_id; //osal分配的任务ID随着用户添加任务的增多而改变 SampleApp_NwkState = DEV_INIT;//设备状态设定为ZDO层中定义的初始化状态 SampleApp_TransID = 0; //消息发送ID(多消息时有顺序之分) // Device hardware initialization can be added here or in main() (Zmain.c). // If the hardware is application specific - add it here. // If the hardware is other parts of the device add it in main(). #if defined ( BUILD_ALL_DEVICES ) // The "Demo" target is setup to have BUILD_ALL_DEVICES and HOLD_AUTO_START // We are looking at a jumper (defined in SampleAppHw.c) to be jumpered // together - if they are - we will start up a coordinator. Otherwise, // the device will start as a router. if ( readCoordinatorJumper() ) zgDeviceLogicalType = ZG_DEVICETYPE_COORDINATOR; else zgDeviceLogicalType = ZG_DEVICETYPE_ROUTER; #endif // BUILD_ALL_DEVICES //该段的意思是,如果设置了HOLD_AUTO_START宏定义,将会在启动芯片的时候会暂停启动 //流程,只有外部触发以后才会启动芯片。其实就是需要一个按钮触发它的启动流程。 #if defined ( HOLD_AUTO_START ) // HOLD_AUTO_START is a compile option that will surpress ZDApp // from starting the device and wait for the application to // start the device. ZDOInitDevice(0); #endif // Setup for the periodic message's destination address 设置发送数据的方式和目的地址寻址模式 // Broadcast to everyone 发送模式:广播发送 SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;//广播 SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号 SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF;//指定目的网络地址为广播地址 // Setup for the flash command's destination address - Group 1 组播发送 SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)afAddrGroup; //组寻址 SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号 SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP;//组号0x0001 // Fill out the endpoint description. 定义本设备用来通信的APS层端点描述符 SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号 SampleApp_epDesc.task_id = &SampleApp_TaskID; //SampleApp 描述符的任务ID SampleApp_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;//SampleApp简单描述符 SampleApp_epDesc.latencyReq = noLatencyReqs; //延时策略 // Register the endpoint description with the AF afRegister( &SampleApp_epDesc ); //向AF层登记描述符 // Register for all key events - This app will handle all key events RegisterForKeys( SampleApp_TaskID ); // 登记所有的按键事件 // By default, all devices start out in Group 1 SampleApp_Group.ID = 0x0001;//组号 osal_memcpy( SampleApp_Group.name, "Group 1", 7 );//设定组名 aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group );//把该组登记添加到APS中 #if defined ( LCD_SUPPORTED ) HalLcdWriteString( "SampleApp", HAL_LCD_LINE_1 ); //如果支持LCD,显示提示信息 #endif }
-
SampleApp_ProcessEvent() 用户应用任务的事件处理函数
//用户应用任务的事件处理函数 uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events ) { afIncomingMSGPacket_t *MSGpkt; (void)task_id; // Intentionally unreferenced parameter if ( events & SYS_EVENT_MSG ) //接收系统消息再进行判断 { //接收属于本应用任务SampleApp的消息,以SampleApp_TaskID标记 MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); while ( MSGpkt ) { switch ( MSGpkt->hdr.event ) { // Received when a key is pressed case KEY_CHANGE://按键事件 SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break; // Received when a messages is received (OTA) for this endpoint case AF_INCOMING_MSG_CMD://接收数据事件,调用函数AF_DataRequest()接收数据 SampleApp_MessageMSGCB( MSGpkt );//调用回调函数对收到的数据进行处理 break; // Received whenever the device changes state in the network case ZDO_STATE_CHANGE: //只要网络状态发生改变,就通过ZDO_STATE_CHANGE事件通知所有的任务。 //同时完成对协调器,路由器,终端的设置 SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status); //if ( (SampleApp_NwkState == DEV_ZB_COORD)//实验中协调器只接收数据所以取消发送事件 if ( (SampleApp_NwkState == DEV_ROUTER) || (SampleApp_NwkState == DEV_END_DEVICE) ) { // Start sending the periodic message in a regular interval. //这个定时器只是为发送周期信息开启的,设备启动初始化后从这里开始 //触发第一个周期信息的发送,然后周而复始下去 osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT, SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT ); } else { // Device is no longer in the network } break; default: break; } // Release the memory 事件处理完了,释放消息占用的内存 osal_msg_deallocate( (uint8 *)MSGpkt ); // Next - if one is available 指针指向下一个放在缓冲区的待处理的事件, //返回while ( MSGpkt )重新处理事件,直到缓冲区没有等待处理事件为止 MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); } // return unprocessed events 返回未处理的事件 return (events ^ SYS_EVENT_MSG); } // Send a message out - This event is generated by a timer // (setup in SampleApp_Init()). if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT ) { // Send the periodic message 处理周期性事件, //利用SampleApp_SendPeriodicMessage()处理完当前的周期性事件,然后启动定时器 //开启下一个周期性事情,这样一种循环下去,也即是上面说的周期性事件了, //可以做为传感器定时采集、上传任务 SampleApp_SendPeriodicMessage(); // Setup to send message again in normal period (+ a little jitter) 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); } // Discard unknown events return 0; }
-
分析接收数据函数SampleApp_MessageMSGCB
//接收数据,参数为接收到的数据 void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt ) { uint16 flashTime; byte buf[3]; switch ( pkt->clusterId ) //判断簇ID { case SAMPLEAPP_PERIODIC_CLUSTERID: //收到广播数据 osal_memset(buf, 0 , 3); osal_memcpy(buf, pkt->cmd.Data, 2); //复制数据到缓冲区中 if(buf[0]=='D' && buf[1]=='1') //判断收到的数据是否为"D1" { HalLedBlink(HAL_LED_1, 0, 50, 500);//如果是则Led1间隔500ms闪烁 #if defined(ZDO_COORDINATOR) //协调器收到"D1"后,返回"D1"给终端,让终端Led1也闪烁 SampleApp_SendPeriodicMessage(); #endif } else { HalLedSet(HAL_LED_1, HAL_LED_MODE_ON); } break; case SAMPLEAPP_FLASH_CLUSTERID: //收到组播数据 flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] ); HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) ); break; } }
-
分析发送周期信息 SampleApp_SendPeriodicMessage
//分析发送周期信息 void SampleApp_SendPeriodicMessage( void ) { byte SendData[3]="D1"; // 调用AF_DataRequest将数据无线广播出去 if( AF_DataRequest( &SampleApp_Periodic_DstAddr,//发送目的地址+端点地址和传送模式 &SampleApp_epDesc,//源(答复或确认)终端的描述(比如操作系统中任务ID等)源EP SAMPLEAPP_PERIODIC_CLUSTERID, //被Profile指定的有效的集群号 2, // 发送数据长度 SendData,// 发送数据缓冲区 &SampleApp_TransID, // 任务ID号 AF_DISCV_ROUTE, // 有效位掩码的发送选项 AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) //传送跳数,通常设置为AF_DEFAULT_RADIUS { } else { HalLedSet(HAL_LED_1, HAL_LED_MODE_ON); // Error occurred in request to send. } }
-
AF_DataRequest 发送函数
AF_DataRequest( &SampleApp_Periodic_DstAddr,//发送目的地址+端点地址和传送模式 &SampleApp_epDesc,//源(答复或确认)终端的描述(比如操作系统中任务ID等)源EP SAMPLEAPP_PERIODIC_CLUSTERID, //被Profile指定的有效的集群号 2, // 发送数据长度 SendData,// 发送数据缓冲区 &SampleApp_TransID, // 任务ID号 AF_DISCV_ROUTE, // 有效位掩码的发送选项 AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) //传送跳数,通常设置为AF_DEFAULT_RADIUS
整体流程