This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

OTA 过程中协调器发送 image response 出现 ZMemError(0x10)

Other Parts Discussed in Thread: Z-STACK, CC2538, CC2530

协议栈:Z-Stack 3.0.2

协调器:CC2538

路由器:EFR32

终端:EFR32

问题描述:

网络中有50个设备,44个路由器和6个终端设备。现在对其中一个终端设备进行OTA升级。升级到了80%的时候协调器会出现发送 image response 命令 会返回ZMemError(0x10),最后导致 OTA 设备发送终止命令,OTA 升级失败。

OTA升级期间没有别的设备进行应用层report之类的交互命令,只有MTO的route request 和 status link 这些维护网络的命令。请问 ZMemError(0x10) 呢?

  • 请问是不是以前发过一个类似的问题,这边已有给出解释: e2echina.ti.com/.../171894
  • 对,那个问题是官方协议栈设备入网时有个内存泄漏bug,我解决了。但是在较大的网络进行 OTA 升级的时候还是会出现 ZMemError(0x10) ,在 50 个节点的网络中 OTA 升级不稳定,容易中断。
  • 你好,

    尝试增大缓冲区试试:

    #define NWK_MAX_DATABUFS_WAITING 16 //8 // Waiting to be sent to MAC
    #define NWK_MAX_DATABUFS_SCHEDULED 10 //5 // Timed messages to be sent
    #define NWK_MAX_DATABUFS_CONFIRMED 10 //5 // Held after MAC confirms
    #define NWK_MAX_DATABUFS_TOTAL 24 //12 // Total number of buffers

    修改 CC2538.icf获取更大内存:

    FLASH = mem:[from 0x00200000 to 0x002797FF];
    NV_MEM = mem:[from 0x00279800 to 0x0027F7FF];
  • 我设置的是:
    #define NWK_MAX_DATABUFS_WAITING 16 //8 // Waiting to be sent to MAC
    #define NWK_MAX_DATABUFS_SCHEDULED 10 //5 // Timed messages to be sent
    #define NWK_MAX_DATABUFS_CONFIRMED 10 //5 // Held after MAC confirms
    #define NWK_MAX_DATABUFS_TOTAL 24 //12 // Total number of buffers

    define region FLASH = mem:[from 0x00200134 to 0x00277FFF];
    define region NV_MEM = mem:[from 0x00279800 to 0x0027F7FF];
    因为我启用了 SBL。
    我在英文论坛找到了一篇有关我描述的问题的文章:
    e2e.ti.com/.../2589323
    从这篇文章你能推测出什么问题么?
  • 你抓包时是否有大量的route request
    可以试试加大下面的两个值。
    CONCENTRATOR_DISCOVERY_TIME
    SRC_RTG_EXPIRY_TIME,
  • 40 多个 router 在进行 route request 的时候是会有大量的 route request 命令,请问这个是怎么影响协议栈内存调度的?
    我的
    CONCENTRATOR_DISCOVERY_TIME = 120
    SRC_RTG_EXPIRY_TIME = 2
  • 我是猜想如果有大量的 route request 会不会造成ZC处理的时频繁回复response造成的,试试看吧,或者你能debug一下抓一下。
  • route request 广播是 coordinator 发起的,它只会发一次吧?在这期间 coordinator 会在哪些地方申请内存?
  • route reply 会占用内存的。
  • zigbee网络测试-节点设备OTA升级中途停止-协调器内存分配失败-OTA失败-Abort.rar

    我看了 route reply 只是别的设备发送给 coordinator 的而已,而且很少,你能帮我看下这个抓包吗?

    前面 OTA 升级时非常顺利的,可是到了后面网络结构会变化,然后 coordinator 要进行 router request 过程修复链路。到最后面 coordinator 就出现zMemError(0x10),OTA 设备收不到 image response 就发送 abort 给 coordinator,升级失败。

  • 你好,

    看起来时由于大量的route request 广播造成的。到最后面去router request 引发了大量的广播。

  • 你好,我有两个问题想问下:
    1、route request 广播是怎么影响协调器协议栈内存的调度管理,它申请了什么内存?
    2、route request 是 Zigbee 路由的一个修复机制,这个命令在网络中是肯定会出现的,我要做什么才能缓解出现 ZMemError(0x10) 的情况,像那个外文链接那样将 image request 从 64 降为 32 吗?除了这个方法还有别的方法吗?

    非常感谢你的解答。
  • coordinator收到了route record,把短地址、路由列表relayList和路由数量relayCount解析出来,最后调用ZDO_SrcRtgIndCB函数。
    我猜想是由于record造成的,但是不是很确定,还是要debug看看。
    至于降低image request确实是个方法,不过最好还是在ota时更新网络路径的好一些。
  • 你是说建议在 OTA 时广播 route request 更新网络路径,减少 route record 命令?
  • /*********************************************************************
    * @fn ZDO_SrcRtgIndCB
    *
    * @brief This function notifies the ZDO available src route record received.
    *
    * @param srcAddr - source address of the source route
    * @param relayCnt - number of devices in the relay list
    * @param relayList - relay list of the source route
    *
    * @return none
    */
    void ZDO_SrcRtgIndCB (uint16 srcAddr, uint8 relayCnt, uint16* pRelayList )
    {
    zdoSrcRtg_t srcRtg;

    srcRtg.srcAddr = srcAddr;
    srcRtg.relayCnt = relayCnt;
    srcRtg.pRelayList = pRelayList;

    if( zdoCBFunc[ZDO_SRC_RTG_IND_CBID] != NULL )
    {
    zdoCBFunc[ZDO_SRC_RTG_IND_CBID]( (void*)&srcRtg );
    }
    }

    你好,我发现 ZDO_SrcRtgIndCB 函数没有调用什么申请内存的函数啊?
  • For concentrator with no memory constraints, it can store all route record entries it receives and use them to send packets to the source devices in the future. Therefore, devices only need to send route record command once. However, for concentrator without source route caching capability, devices always need to send route record commands along with data packets. The concentrator will store the source route temporarily in the memory and then discard it after usage.
    这里说:
    对于没有内存限制的集中器,它可以存储它接收的所有路由记录条目,并在将来使用它们将数据包发送到源设备。因此,设备只需要发送一次路由记录命令。但是,对于没有源路由缓存功能的集中器,设备总是需要发送路由记录命令以及数据包。集中器会将源路由暂时存储在内存中,然后在使用后将其丢弃。
    是底层程序存储了路由记录是吧?
  • 是这样的,传过来之前在底层,开辟了空间,如果再回掉里面你不是用就丢弃了。
  • 需要自己利用 route record 命令的吗?要开发者自己利用 route record 来存储和使用链路?

  • /*********************************************************************
     * @fn      AF_DataRequestSrcRtg
     *
     * @brief   Common functionality for invoking APSDE_DataReq() for both
     *          SendMulti and MSG-Send.
     *
     * input parameters
     *
     * @param  *dstAddr - Full ZB destination address: Nwk Addr + End Point.
     * @param  *srcEP - Origination (i.e. respond to or ack to) End Point Descr.
     * @param   cID - A valid cluster ID as specified by the Profile.
     * @param   len - Number of bytes of data pointed to by next param.
     * @param  *buf - A pointer to the data bytes to send.
     * @param  *transID - A pointer to a byte which can be modified and which will
     *                    be used as the transaction sequence number of the msg.
     * @param   options - Valid bit mask of Tx options.
     * @param   radius - Normally set to AF_DEFAULT_RADIUS.
     * @param   relayCnt - Number of devices in the relay list
     * @param   pRelayList - Pointer to the relay list
     *
     * output parameters
     *
     * @param  *transID - Incremented by one if the return value is success.
     *
     * @return  afStatus_t - See previous definition of afStatus_... types.
     */
    
    afStatus_t AF_DataRequestSrcRtg( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
                               uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
                               uint8 options, uint8 radius, uint8 relayCnt, uint16* pRelayList )

    这有点跑题了:

    使用AF_DataRequestSrcRtg这个函数会在在发送控制指令前,添加relayList的,该函数最终也会调用AF_DataRequest实现控制指令的发送,会调用RTG_AddSrcRtgEntry_Guaranteed把relayList加入到source routing table里面,让协议栈按照这个添加的路径路径去发送,指令会更快的到达目的地址。

  • 没跑题啊,现在不是在说可能是 route record 导致的内存激增吗。
    我搜索了工程代码,没发现在哪里有利用 ZDO_SRC_RTG_IND_CBID 回调,底层有利用 ZDO_SRC_RTG_IND_CBID 回调来自动更新路由链路吗?从我上面发的那个抓包来看,协调器收到 OTA 设备的 route record 命令之后并没有任何反应,然后 OTA 设备就一直在发送 route record 给协调器了。

    5.4.3 Route Record Command 
    For concentrator with no memory constraints, it can store all route record entries it receives and use them to send packets to the source devices in the future. Therefore, devices only need to send route record command once. However, for concentrator without source route caching capability, devices always need to send route record commands along with data packets. The concentrator will store the source route temporarily in the memory and then discard it after usage.

    从 5.4.3 Route Record Command 里的这段话可以看出如果开了源路由缓存功能,设备只需要发送一次 route record 命令?

  • 这个route record是设备更新了 route 路径然后上报的。 route record是客户自己要做的记录,底层只是传上来,用不用客户决定的。你可以试试在网络不做改变的情况下去OTA看看会不会失败。

    其实工程中有例子:

    /***************************************************************************************************
     * @fn      MT_ZdoSrcRtgCB
     *
     * @brief   Handle Src Route from ZDO.
     *
     * @param   pStr  - pointer to the data structure for the src route
     *
     * @return  void*
     */
    void* MT_ZdoSrcRtgCB( void *pStr )
    {
      uint8 len, *pBuf;
      zdoSrcRtg_t *pSrcRtg = pStr;
    
      // srcAddr (2) + relayCnt (1) + relayList( relaycnt * 2 )
      len = 2 + 1 + pSrcRtg->relayCnt * sizeof(uint16);
    
      if (NULL != (pBuf = (uint8 *)osal_mem_alloc(len)))
      {
        uint8 idx, *pTmp = pBuf;
        uint16 *pRelay;
    
        // Packet payload
        *pTmp++ = LO_UINT16(pSrcRtg->srcAddr);
        *pTmp++ = HI_UINT16(pSrcRtg->srcAddr);
        *pTmp++ = pSrcRtg->relayCnt;
    
        // Relay List
        if( ( pRelay = pSrcRtg->pRelayList ) != NULL )
        {
          for( idx = 0; idx < pSrcRtg->relayCnt; idx ++ )
          {
            *pTmp++ = LO_UINT16(*pRelay);
            *pTmp++ = HI_UINT16(*pRelay);
            pRelay++;
          }
        }
        MT_BuildAndSendZToolResponse(((uint8)MT_RPC_CMD_AREQ | (uint8)MT_RPC_SYS_ZDO),
                                             MT_ZDO_SRC_RTG_IND, len, pBuf);
        osal_mem_free(pBuf);
      }
    
      return NULL;
    }

  • 这个 MT 函数只是将接收到的 route record 数据上传到 host ,host 一般要怎么应用这个 route record 呢?
    而且 ZCL 发送命令函数 zcl_SendCommand() 调用的是 AF_DataRequest() 来发送数据而不是 AF_DataRequestSrcRtg(),请问 AF_DataRequestSrcRtg() 在协议栈其它地方有应用到吗?
  • 这个就是客户的应用问题,你可以建一个表去储存route record, 然后自己去修改zcl_SendCommand,让他使用AF_DataRequestSrcRtg,加一些客户的自己程序。
  • OK,我了解了,route record 命令是需要用户自己做应用的,AF_DataRequest() 是不会利用 route record 的对吧?
    那么开启源路由缓存功能后:“CONCENTRATOR_ROUTE_CACHE=TRUE”、“MAX_RTG_SRC_ENTRIES=80”,协调器会自己缓存入网设备的源路由路径,然后在发送的时候会使用这些路径吗?如果会自己存储,那是独立开出80个源路由路径的内存吗?还是要占用“MAXMEMHEAP”的内存?

  • a coordinator is desired to act as a Many-to-One (MTO) data concentrator that records
    network route discovery (by setting CONCENTRATOR_ENABLE and
    CONCENTRATOR_ROUTE_CACHE to true in NWK/ZGlobals.h) then XDATA will be further populated
    by MAX_RTG_SRC_ENTRIES from NWK/nwk_globals.h
    原文如上,不会去去使用,要自己建立表格对应一个节点地址。
  • 你好,你是说Z-Stack3.0.2即使是开了“CONCENTRATOR_ROUTE_CACHE=TRUE”和“MAX_RTG_SRC_ENTRIES=80”也是不会去存储跟主动使用源路由路径,需要用户自己使用 route record 回调函数自己建立源路由表,并且调用“AF_DataRequestSrcRtg”函数发送是吗?
    这份文档能发我吗?
    谢谢
  • 请问有没有 2538 内存优化的文档?
  • 内存优化主要是优化这些表,没有CC2538和CC2530大同小异。
  • 我发现子设备发送给协调器的 route record 命令和协调器广播 route request 之后收到子设备的 link status 都会触发 “void* ZdoSrcRtgCB( void *pStr )”回调函数,请问这个时候我能马上释放掉“pStr ”指针的内存吗?
  • 我在“ZDO_RegisterForZdoCB(ZDO_SRC_RTG_IND_CBID, &ZdoSrcRtgCB);”的“void* ZdoSrcRtgCB( void *pStr )”回调函数中调用“osal_mem_free(pStr);”,协议栈会进入“static void FaultISR(void)”中啊?
  • 你说这个我没有试过,如果不行的话估计不允许吧。在最新的C2652R的3.10 SDK里面是使用的了:
    static void *zdoSrcRtgCB( void *pStr )
    {
    sendMsgToAllCBs( ZS_ZDO_SRC_RTG_IND_CBID, pStr, sendZdoSrcRtgInd );

    return (NULL);
    }

    建议你去问问Ryan,他们不休端午节正好可以方便跟进。
  • OK,我先下载3.10 SDK看看。谢谢你。
  • 你好,C2652R 的 3.10 SDK 能给个链接吗?
  • http://www.ti.com/tool/simplelink-cc13x2-26x2-sdk

    注意3.10使用E版本芯片,如果你的芯片不是E版本,只能用2.30版本SDK

  • 我就说为什么子设备会一直发 route record 给协调器,而且协调器已经收到了,但是子设备还是会发。
    在最新的 3.1 协议栈里面添加了 src rtg 的确认消息给子设备,而 Z-Stack 3.0.2 协议栈是没有的!
    /**************************************************************************************************
    * @fn sendZdoSrcRtgInd
    *
    * @brief Function to send a ZDO Source Route indication
    *
    * @param dstID - Destination Task ID
    * @param pStr - pointer to source route information
    *
    * @return none
    */
    static void sendZdoSrcRtgInd( uint16 dstID, void *pStr )
    {
    zstackmsg_zdoSrcRtgInd_t *pSrcRtgInd;
    zdoSrcRtg_t *pSrcRtg = (zdoSrcRtg_t *)pStr;

    pSrcRtgInd = (zstackmsg_zdoSrcRtgInd_t *)MAP_osal_msg_allocate( sizeof(zstackmsg_zdoSrcRtgInd_t) );
    if ( pSrcRtgInd == NULL )
    {
    // Ignore the message
    return;
    }

    memset( pSrcRtgInd, 0, sizeof(zstackmsg_zdoSrcRtgInd_t) );

    pSrcRtgInd->hdr.event = zstackmsg_CmdIDs_ZDO_SRC_RTG_IND;
    pSrcRtgInd->hdr.status = 0;

    pSrcRtgInd->req.pRelay = OsalPort_malloc( sizeof(uint16_t) * pSrcRtg->relayCnt );
    if ( pSrcRtgInd->req.pRelay == NULL )
    {
    MAP_osal_msg_deallocate( (uint8_t*) pSrcRtgInd );
    return;
    }

    pSrcRtgInd->req.srcAddr = pSrcRtg->srcAddr;
    pSrcRtgInd->req.n_relay = pSrcRtg->relayCnt;
    if ( pSrcRtg->relayCnt )
    {
    int i;
    for ( i = 0; i < pSrcRtg->relayCnt; i++ )
    {
    pSrcRtgInd->req.pRelay[i] = pSrcRtg->pRelayList[i];
    }
    }

    // Send the a subscriber
    MAP_osal_msg_send( dstID, (uint8_t*)pSrcRtgInd );
    }
  • 也就说你用的不是一个版本SDK呗。
    你可以参考移植上去,那这个问题就解决了。
  • 不好意思,刚我没仔细看源码,这个“static void sendZdoSrcRtgInd( uint16 dstID, void *pStr )”是将“route record”消息发送至应用层而已,并没有做什么实质的处理。而我刚刚说的子节点发送“route record”给协调器,协调器接收到了,但是没有做任何处理,导致子节点会一直发送“route record”给协调器,直到协调器进行 120 秒后的“route request”才停止发送。
  • 你好,我抓包发现协调器收到子设备的“route record”之后并没有返回上图的“ACK”给子设备,所以子设备会一直发送“route record”给协调器,协调器应该返回什么“ACK”给子设备?

  • ZC不需要回复这个,我们看看ryan他们有什么想法
  • 是不是设备复位后不知道协调器是带源路由缓存功能的设备,然后每上报一次数据给协调器都会自带一条“route record”?我是默认间隔 120 秒协调器广播一次“route request”命令的。

  • 这么弄
    #define NWK_MAX_DATABUFS_WAITING 16 //8 // Waiting to be sent to MAC
    #define NWK_MAX_DATABUFS_SCHEDULED 10 //5 // Timed messages to be sent
    #define NWK_MAX_DATABUFS_CONFIRMED 10 //5 // Held after MAC confirms
    #define NWK_MAX_DATABUFS_TOTAL 24 //12 // Total number of buffers

    define region FLASH = mem:[from 0x00200134 to 0x00277FFF];
    define region NV_MEM = mem:[from 0x00279800 to 0x0027F7FF];