ZIGBEE提供了report机制(现在只学习了send, receive还没学习)

主要目的是实现attribute属性的report功能,即提供了一种服务端和客户端数据同步的机制

以EMBER的HasampleLightSoc来具体看看report的实现过程,具体步骤如下:

1、设置report参数

void emberAfMainInitCallback(void)
{
EmberAfPluginReportingEntry reportingEntry;
reportingEntry.direction = EMBER_ZCL_REPORTING_DIRECTION_REPORTED; // 设置report方向为send
reportingEntry.endpoint = emberAfPrimaryEndpoint();           // 设置端点
reportingEntry.clusterId = ZCL_ON_OFF_CLUSTER_ID;            // 设置簇ID
reportingEntry.attributeId = ZCL_ON_OFF_ATTRIBUTE_ID;          // 设置属性ID
reportingEntry.mask = CLUSTER_MASK_SERVER;               // 设置簇掩码(确定是服务端还是客户端)  
reportingEntry.manufacturerCode = EMBER_AF_NULL_MANUFACTURER_CODE;  // 设置厂商ID
reportingEntry.data.reported.minInterval = MIN_INTERVAL_S;       // 设置最小数据发送周期
reportingEntry.data.reported.maxInterval = MAX_INTERVAL_S;       // 设置最大数据发送周期
reportingEntry.data.reported.reportableChange = ; // unused  
emberAfPluginReportingConfigureReportedAttribute(&reportingEntry);
}

2、report参数解析和配置过程

EmberAfStatus emberAfPluginReportingConfigureReportedAttribute(const EmberAfPluginReportingEntry* newEntry)
{
EmberAfAttributeMetadata *metadata;
EmberAfPluginReportingEntry entry;
EmberAfStatus status;
uint8_t i, index = NULL_INDEX;
bool initialize = true; // Verify that we support the attribute and that the data type matches. 根据参数寻找匹配的属性,并且返回指向改属性的地址
metadata = emberAfLocateAttributeMetadata(newEntry->endpoint,
newEntry->clusterId,
newEntry->attributeId,
newEntry->mask,
newEntry->manufacturerCode);
if (metadata == NULL) {
return EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE;
} // Verify the minimum and maximum intervals make sense. 核对report周期最大值,最小值确定是否在范围内
if (newEntry->data.reported.maxInterval !=
&& (newEntry->data.reported.maxInterval
< newEntry->data.reported.minInterval)) {
return EMBER_ZCL_STATUS_INVALID_VALUE;
} // Check the table for an entry that matches this request and also watch for
// empty slots along the way. If a report exists, it will be overwritten
// with the new configuration. Otherwise, a new entry will be created and
// initialized. 确定reportingTable是否有和需要配置的参数一样的,如果有在后面会更新report周期等参数,如果没有添加一个新的entry
for (i = ; i < EMBER_AF_PLUGIN_REPORTING_TABLE_SIZE; i++) {
emAfPluginReportingGetEntry(i, &entry);
if (entry.direction == EMBER_ZCL_REPORTING_DIRECTION_REPORTED
&& entry.endpoint == newEntry->endpoint
&& entry.clusterId == newEntry->clusterId
&& entry.attributeId == newEntry->attributeId
&& entry.mask == newEntry->mask
&& entry.manufacturerCode == newEntry->manufacturerCode) {
initialize = false;
index = i;
break;
} else if (entry.endpoint == EMBER_AF_PLUGIN_REPORTING_UNUSED_ENDPOINT_ID
&& index == NULL_INDEX) {
index = i;
}
} // If the maximum reporting interval is 0xFFFF, the device shall not issue
// reports for the attribute and the configuration information for that
// attribute need not be maintained. 如果maxInterval = 0xFFFF,删除该report配置
if (newEntry->data.reported.maxInterval == 0xFFFF) {
if (!initialize) {
removeConfigurationAndScheduleTick(index);
}
return EMBER_ZCL_STATUS_SUCCESS;
} if (index == NULL_INDEX) {
return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE;
} else if (initialize) {
entry.direction = EMBER_ZCL_REPORTING_DIRECTION_REPORTED;
entry.endpoint = newEntry->endpoint;
entry.clusterId = newEntry->clusterId;
entry.attributeId = newEntry->attributeId;
entry.mask = newEntry->mask;
entry.manufacturerCode = newEntry->manufacturerCode;
emAfPluginReportVolatileData[index].lastReportTimeMs = halCommonGetInt32uMillisecondTick();
emAfPluginReportVolatileData[index].lastReportValue = ;
} // For new or updated entries, set the intervals and reportable change.
// Updated entries will retain all other settings configured previously. 不论是新的entry还是更新旧的entry,report周期等参数需要被更新
entry.data.reported.minInterval = newEntry->data.reported.minInterval;
entry.data.reported.maxInterval = newEntry->data.reported.maxInterval;
entry.data.reported.reportableChange = newEntry->data.reported.reportableChange; // Give the application a chance to review the configuration that we have
// been building up. If the application rejects it, we just do not save the
// record. If we were supposed to add a new configuration, it will not be
// created. If we were supposed to update an existing configuration, we will
// keep the old one and just discard any changes. So, in either case, life
// continues unchanged if the application rejects the configuration. 给应用层传递配置信息,如果应用层拒绝,配置不生效,否则配置成功,开启report功能
status = emberAfPluginReportingConfiguredCallback(&entry);
if (status == EMBER_ZCL_STATUS_SUCCESS) {
emAfPluginReportingSetEntry(index, &entry);
scheduleTick();
}
return status;
}

3、report参数设置完毕后,根据当前时间,设置的发送周期,来确定emberAfPluginReportingTickEventControl事件什么时候被激活,进而进行report的发送。

static void scheduleTick(void)
{
uint32_t delayMs = MAX_INT32U_VALUE;
uint8_t i;
for (i = ; i < EMBER_AF_PLUGIN_REPORTING_TABLE_SIZE; i++) {
EmberAfPluginReportingEntry entry;
emAfPluginReportingGetEntry(i, &entry);
if (entry.endpoint != EMBER_AF_PLUGIN_REPORTING_UNUSED_ENDPOINT_ID
&& entry.direction == EMBER_ZCL_REPORTING_DIRECTION_REPORTED) {
uint32_t minIntervalMs = (entry.data.reported.minInterval
* MILLISECOND_TICKS_PER_SECOND);
uint32_t maxIntervalMs = (entry.data.reported.maxInterval
* MILLISECOND_TICKS_PER_SECOND);
uint32_t elapsedMs = elapsedTimeInt32u(emAfPluginReportVolatileData[i].lastReportTimeMs,
halCommonGetInt32uMillisecondTick());
uint32_t remainingMs = MAX_INT32U_VALUE;
if (emAfPluginReportVolatileData[i].reportableChange) { // 如果attribute有变化,并且两次report时间大于minInterval,则report,否则延时等待相应时间
remainingMs = (minIntervalMs < elapsedMs
?
: minIntervalMs - elapsedMs);
} else if (maxIntervalMs) { // 如果attribute没变化,并且两次report时间大于maxInterval,则report,否则延时等待相应时间
remainingMs = (maxIntervalMs < elapsedMs
?
: maxIntervalMs - elapsedMs);
}
if (remainingMs < delayMs) { // 寻找所有reportint table里面的延时时间中最短的一个,然后设置emberAfPluginReportingTickEventControl事件
delayMs = remainingMs;
}
}
}
if (delayMs != MAX_INT32U_VALUE) {
emberAfDebugPrintln("sched report event for: 0x%4x", delayMs);
emberAfEventControlSetDelayMS(&emberAfPluginReportingTickEventControl,
delayMs);
} else {
emberAfDebugPrintln("deactivate report event");
emberEventControlSetInactive(emberAfPluginReportingTickEventControl);
}
}

4、当设置了emberAfPluginReportingTickEventControl事件后,emberAfPluginReportingTickEventHandler会相应的被执行

// EmberEventData structs used to populate the EmberEventData table
#define EMBER_AF_GENERATED_EVENTS \
{ &emberAfIdentifyClusterServerTickCallbackControl1, emberAfIdentifyClusterServerTickCallbackWrapperFunction1 }, \
{ &emberAfPluginConcentratorUpdateEventControl, emberAfPluginConcentratorUpdateEventHandler }, \
{ &emberAfPluginEzmodeCommissioningStateEventControl, emberAfPluginEzmodeCommissioningStateEventHandler }, \
{ &emberAfPluginFormAndJoinCleanupEventControl, emberAfPluginFormAndJoinCleanupEventHandler }, \
{ &emberAfPluginIdentifyFeedbackProvideFeedbackEventControl, emberAfPluginIdentifyFeedbackProvideFeedbackEventHandler }, \
{ &emberAfPluginNetworkFindTickEventControl, emberAfPluginNetworkFindTickEventHandler }, \
{ &emberAfPluginReportingTickEventControl, emberAfPluginReportingTickEventHandler }, \ // 事件和相应处理函数关联表
{ &buttonEventControl, buttonEventHandler }, \ void emberAfPluginReportingTickEventHandler(void)
{
EmberApsFrame *apsFrame = NULL;
EmberAfStatus status;
EmberAfAttributeType dataType;
uint16_t manufacturerCode;
uint8_t readData[READ_DATA_SIZE];
uint8_t i, dataSize;
bool clientToServer;
EmberBindingTableEntry bindingEntry;
uint8_t index, reportSize = , currentPayloadMaxLength = , smallestPayloadMaxLength; for (i = ; i < EMBER_AF_PLUGIN_REPORTING_TABLE_SIZE; i++) {
EmberAfPluginReportingEntry entry;
uint32_t elapsedMs;
emAfPluginReportingGetEntry(i, &entry);
// We will only send reports for active reported attributes and only if a
// reportable change has occurred and the minimum interval has elapsed or
// if the maximum interval is set and has elapsed. 判断当前report条件是否满足(主要是时间),如果不满足继续寻找,直到找到符合的
elapsedMs = elapsedTimeInt32u(emAfPluginReportVolatileData[i].lastReportTimeMs,
halCommonGetInt32uMillisecondTick());
if (entry.endpoint == EMBER_AF_PLUGIN_REPORTING_UNUSED_ENDPOINT_ID
|| entry.direction != EMBER_ZCL_REPORTING_DIRECTION_REPORTED
|| (elapsedMs
< entry.data.reported.minInterval * MILLISECOND_TICKS_PER_SECOND)
|| (!emAfPluginReportVolatileData[i].reportableChange
&& (entry.data.reported.maxInterval ==
|| (elapsedMs
< (entry.data.reported.maxInterval
* MILLISECOND_TICKS_PER_SECOND))))) {
continue;
} status = emAfReadAttribute(entry.endpoint, //读出attribute属性
entry.clusterId,
entry.attributeId,
entry.mask,
entry.manufacturerCode,
(uint8_t *)&readData,
READ_DATA_SIZE,
&dataType);
if (status != EMBER_ZCL_STATUS_SUCCESS) {
emberAfReportingPrintln("ERR: reading cluster 0x%2x attribute 0x%2x: 0x%x",
entry.clusterId,
entry.attributeId,
status);
continue;
} // find size of current report 计算reportSize = 簇ID长度+datetype长度+data长度
dataSize = (emberAfIsThisDataTypeAStringType(dataType)
? emberAfStringLength(readData) +
: emberAfGetDataSize(dataType));
reportSize = sizeof(entry.attributeId) + sizeof(dataType) + dataSize; // If we have already started a report for a different attribute or
// destination, or if the current entry is too big for current report, send it and create a new one.
if (apsFrame != NULL &&
(!(entry.endpoint == apsFrame->sourceEndpoint
&& entry.clusterId == apsFrame->clusterId
&& emberAfClusterIsClient(&entry) == clientToServer
&& entry.manufacturerCode == manufacturerCode) ||
(appResponseLength + reportSize > smallestPayloadMaxLength))) {
if (appResponseLength + reportSize > smallestPayloadMaxLength) {
emberAfReportingPrintln("Reporting Entry Full - creating new report");
}
conditionallySendReport(apsFrame->sourceEndpoint, apsFrame->clusterId);
apsFrame = NULL;
} // If we haven't made the message header, make it.
if (apsFrame == NULL) {
apsFrame = emberAfGetCommandApsFrame();
clientToServer = emberAfClusterIsClient(&entry);
// The manufacturer-specfic version of the fill API only creates a
// manufacturer-specfic command if the manufacturer code is set. For
// non-manufacturer-specfic reports, the manufacturer code is unset, so
// we can get away with using this API for both cases. 填充zcl cammand,数据填充完成后,既可以开始发送
emberAfFillExternalManufacturerSpecificBuffer((clientToServer
? (ZCL_GLOBAL_COMMAND
| ZCL_FRAME_CONTROL_CLIENT_TO_SERVER
| EMBER_AF_DEFAULT_RESPONSE_POLICY_REQUESTS)
: (ZCL_GLOBAL_COMMAND
| ZCL_FRAME_CONTROL_SERVER_TO_CLIENT
| EMBER_AF_DEFAULT_RESPONSE_POLICY_REQUESTS)),
entry.clusterId,
entry.manufacturerCode,
ZCL_REPORT_ATTRIBUTES_COMMAND_ID,
"");
apsFrame->sourceEndpoint = entry.endpoint;
apsFrame->options = EMBER_AF_DEFAULT_APS_OPTIONS;
manufacturerCode = entry.manufacturerCode; // EMAPPFWKV2-1327: Reporting plugin does not account for reporting too many attributes
// in the same ZCL:ReportAttributes message // find smallest maximum payload that the destination can receive for this cluster and source endpoint
smallestPayloadMaxLength = MAX_INT8U_VALUE;
for (index = ; index < EMBER_BINDING_TABLE_SIZE; index++) {
status = (EmberAfStatus)emberGetBinding(index, &bindingEntry);
if (status == (EmberAfStatus)EMBER_SUCCESS && bindingEntry.local == entry.endpoint && bindingEntry.clusterId == entry.clusterId) {
currentPayloadMaxLength = emberAfMaximumApsPayloadLength(bindingEntry.type, bindingEntry.networkIndex, apsFrame);
if (currentPayloadMaxLength < smallestPayloadMaxLength) {
smallestPayloadMaxLength = currentPayloadMaxLength;
}
}
} } // Payload is [attribute id:2] [type:1] [data:N].
emberAfPutInt16uInResp(entry.attributeId);
emberAfPutInt8uInResp(dataType); #if (BIGENDIAN_CPU)
if (isThisDataTypeSentLittleEndianOTA(dataType)) {
uint8_t i;
for (i = ; i < dataSize; i++) {
emberAfPutInt8uInResp(readData[dataSize - i - ]);
}
} else {
emberAfPutBlockInResp(readData, dataSize);
}
#else
emberAfPutBlockInResp(readData, dataSize);
#endif // Store the last reported time and value so that we can track intervals
// and changes. We only track changes for data types that are small enough
// for us to compare. 记录上次report的时间和值,方便下次进行计算
emAfPluginReportVolatileData[i].reportableChange = false;
emAfPluginReportVolatileData[i].lastReportTimeMs = halCommonGetInt32uMillisecondTick();
if (dataSize <= sizeof(emAfPluginReportVolatileData[i].lastReportValue)) {
emAfPluginReportVolatileData[i].lastReportValue = ;
#if (BIGENDIAN_CPU)
MEMMOVE(((uint8_t *)&emAfPluginReportVolatileData[i].lastReportValue
+ sizeof(emAfPluginReportVolatileData[i].lastReportValue)
- dataSize),
readData,
dataSize);
#else
MEMMOVE(&emAfPluginReportVolatileData[i].lastReportValue, readData, dataSize);
#endif
}
} if (apsFrame != NULL) { // 数据发送
conditionallySendReport(apsFrame->sourceEndpoint, apsFrame->clusterId);
}
scheduleTick(); // 计算下一次report时间,并且设置相应的事件,如此就可以周而复始的report数据了
}

总结:

使用report功能,只需要简单的如第一步配置相关的参数既可以了。

配置过程中注意几点:

1、配置的簇,属性,端点,厂家,掩码等参数一定要和现有的属性的相关信息一致,否则系统找不到需要发送的数据内容

2、report的最小周期和最大周期按照用户需求定制。

3、配置的过程,需要在用户需要的地方被正确的调用,例如我这里是在emberAfMainInitCallback处调用,则一上电便开启了report功能

只要相应的绑定表里面有内容,则就直接发送相应的数据到对应的绑定设备。

以上仅供自己学习使用,有错误的地方,欢迎指正。

ZIGBEE report机制分析的更多相关文章

  1. UVM基础之---------uvm report 机制分析

    uvm 中的信息报告机制相对来说比较简单,功能上来说主要分为两部分: 第一通过ID对component的信息报告冗余级别进行控制,针对每个冗余级别进行不同的行为控制.这部分工作主要由uvm_repor ...

  2. Linux mips64r2 PCI中断路由机制分析

    Linux mips64r2 PCI中断路由机制分析 本文主要分析mips64r2 PCI设备中断路由原理和irq号分配实现方法,并尝试回答如下问题: PCI设备驱动中断注册(request_irq) ...

  3. IOS Table中Cell的重用reuse机制分析

    IOS Table中Cell的重用reuse机制分析 技术交流新QQ群:414971585 创建UITableViewController子类的实例后,IDE生成的代码中有如下段落: - (UITab ...

  4. 您还有心跳吗?超时机制分析(java)

    注:本人是原作者,首发于并发编程网(您还有心跳吗?超时机制分析),此文结合那里的留言作了一些修改. 问题描述 在C/S模式中,有时我们会长时间保持一个连接,以避免频繁地建立连接,但同时,一般会有一个超 ...

  5. Java 类反射机制分析

    Java 类反射机制分析 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.在计算机科学领域,反射是一类应用,它们能够自描述和自控制.这类应用通过某 ...

  6. Linux信号(signal) 机制分析

    Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...

  7. Java 动态代理机制分析及扩展

    Java 动态代理机制分析及扩展,第 1 部分 王 忠平, 软件工程师, IBM 何 平, 软件工程师, IBM 简介: 本文通过分析 Java 动态代理的机制和特点,解读动态代理类的源代码,并且模拟 ...

  8. Android内存机制分析1——了解Android堆和栈

    //----------------------------------------------------------------------------------- Android内存机制分析1 ...

  9. memcache redundancy机制分析及思考

    设计和开发可以掌控客户端的分布式服务端程序是件幸事,可以把很多事情交给客户端来做,而且可以做的很优雅.角色决定命运,在互联网架构中,web server必须冲锋在前,注定要在多浏览器版本以及协议兼容性 ...

随机推荐

  1. Android NDK开发 JNI操作java构造方法,普通方法,静态方法(七)

    Android NDK开发 JNI操作java普通.静态.构造方法 1.Jni实例化一个Java类的实例jobject 1.通过FindClas( ),获取Java类的的jclass 2.通过GetM ...

  2. 亲测SQLServer的最大连接数

    很多做架构设计.程序开发.运维.技术管理的朋友可能或多或少有这样的困惑: SQLServer到底支持多少连接数的并发? SQLServer是否可以满足现有的应用吗? 现有的技术架构支持多少连接数的并发 ...

  3. CF 540D——Bad Luck Island——————【概率dp】

    Bad Luck Island time limit per test 2 seconds memory limit per test 256 megabytes input standard inp ...

  4. anaular js loadding效果

    以前用的jquery的时候,用ajax实现,比较好弄,下面是angularjs的方式: //body下面增加div <div data-loading></div> //dir ...

  5. 【linux】关于linux命令

    1. 删除空目录文件夹rmdir [options]      DIRECTORY Ubuntu默认的源是国外的,下载速度会比较慢,cd /etc/apt gedit /etc/apt/

  6. javascript 数组方法拼接html标签

    var htmls = new Array(); htmls.push("<tr class='otherinfotr'>");htmls.push("< ...

  7. html+css杂记

    overflow可设置超出后隐藏 子元素撑开父级元素:①父级元素宽度不固定,②父级元素设置为inline-block或者添加float: html中的中文在浏览器打开为乱码(已经写了<meta ...

  8. (转)轻松解决 MyEclipse、Eclipse 编译时提示 @Override The method of type must override a superclass method 即 @Override 标注问题

    刚才在把工程从其他地方导入到自己机子的 MyEclipse 下时,出现了 The method of type must override a superclass method ,提示的是实现类必须 ...

  9. Azure本月最新活动,速度Mark!

    桃花夭夭,渌水盈盈,就这样四月的脚步迫不及待地来了.为了帮助您能在第一时间了解 Azure 最新的活动,我们推出每月活动合集,你准备好了吗,一起来看吧! 立即访问http://market.azure ...

  10. http缓存基本介绍

    https://www.helloweba.com/view-blog-414.html