Default handler doesn't relay set color command

Hi,

I have an nrf52833-dk and adapted the light_bulb example in order to handle two endpoints. The second endpoint is a copy from the first and the same as in the light_bulb example. When I send a level control command everything works as expected and the default handler relays the information to the zcl_device_cb function. However with the color command this does not happen. For debugging purposes I temporarily created a custom endpoint handler and looked at the command that is received by the device: 

This is the command that is not relayed to the zcl_device_cb function by the default handler. Is this standard behaviour?

Parents
  • Have you implemented the Color control cluster at the endpoint you are sending this packet to? 

    Light Bulb example doesn’t implement it by default.

    If ZCL command targets cluster that does not present at the endpoint, the ZCL device callback will not be called - Zigbee stack doesn’t process this frame further (similarly to receiving command that targets endpoint that is not present at the device). If you want to have Color cluster functionality at the given endpoint, you should add Color cluster to the endpoint, then the stack will process Color cluster command and inform application about any change in value of Color cluster’s attributes via ZCL device callback.

    If you want to receive callback for every ZCL command that is received at the endpoint, you should register an endpoint handler for given endpoint.

  • Hi,

    Thank you for sharing the project.

    I did some testing and for Level Control, zcl_device_cb is ony called when sending commands with On/Off. So you must send "Step (with On/Off)"  (command ID 0x06) instead of the regular "Step" (command ID 0x02) command. 

    As for Color Control, the command and zcl_device_cb worked as expected for both endpoints. I used Zigbee shell to test it and sent the Move to Color command with the following:

    zcl cmd 0xff10 1 0x0300 0x07 -l 0000aaaa0200

    This is what the payload of the packet looked like:

    As you can see from the log, zcl_device_cb was called for both endpoints:

    If the payload of the command was incorrect or the color values were outside the valid range, it did not enter zcl_device_cb. This might be what the issue is in your case, since I did not make any changes to your code.
    If you get a sniffer log, you should be able to see if the payload is correct or not. If it is incorrect, the device will send a Default Response packet with the status field showing the problem, similar to this:

    Best regards,
    Marte

  • Thanks to your help I was able to debug it further. When I send a single "move to color" command at either endpoint, everything works as expected. So I have the same behaviour as you. However if I send two commands in a very short amount of time, then only the second command will be relayed to zcl_device_cb. Important note: the color for the first endpoint is different from the command before.

    The commands that are being sent:

    The first and the second "move to color" command in detail:

    The first and second response:

    Moreover I did implement a test handler for both endpoints with:

    ZB_AF_SET_ENDPOINT_HANDLER(DIMMABLE_LIGHT_ENDPOINT, test_handler);
    ZB_AF_SET_ENDPOINT_HANDLER(DIMMABLE_LIGHT_ENDPOINT_2, test_handler);

    and 

    zb_uint8_t test_handler(zb_uint8_t param) {
        LOG_INF("test handler reached");
        zb_bufid_t zcl_cmd_buf = param;
        volatile zb_zcl_parsed_hdr_t *cmd_info = ZB_BUF_GET_PARAM(zcl_cmd_buf, zb_zcl_parsed_hdr_t);
        LOG_INF("destination endpoint: %d", cmd_info->addr_data.common_data.dst_endpoint);
        return 0;
    };

    Now I can see that when I send two endpoints in a short sequence my test_handler gets called two times, one for each endpoint. However, only the second command also gets relayed to the default handler as well. The package that does not get relayed can be seen in the original post.

  • Hi,

    I have asked the development team about this, and I will update you when I hear back from them.

    Best regards,
    Marte

  • Hi,

    The developers have made a fix for this. Please try replacing the files zcl_main.c and zcl_color_control_commands.c in nrfxlib/zboss/production/src/zcl with the ones I have attached here.

    Here are the changes between the files:

    diff --git a/zboss/production/src/zcl/zcl_main.c b/zboss/production/src/zcl/zcl_main.c
    index a560f4d0..29f0e72f 100644
    --- a/zboss/production/src/zcl/zcl_main.c
    +++ b/zboss/production/src/zcl/zcl_main.c
    @@ -364,7 +364,7 @@ zb_zcl_cluster_check_value_t zb_zcl_get_cluster_check_value(zb_uint16_t cluster_
       zb_uindex_t i = 0;
       ZVUNUSED(cluster_role);
     
    -  TRACE_MSG(TRACE_ERROR, "zb_zcl_get_cluster_check_value: cluster_id %d cluster_role %hd",
    +  TRACE_MSG(TRACE_ZCL3, "zb_zcl_get_cluster_check_value: cluster_id %d cluster_role %hd",
                 (FMT__D_H, cluster_id, cluster_role));
     
       /* Search if we already have handler */
    @@ -389,7 +389,7 @@ zb_zcl_cluster_write_attr_hook_t zb_zcl_get_cluster_write_attr_hook(zb_uint16_t
       zb_uindex_t i = 0;
       ZVUNUSED(cluster_role);
     
    -  TRACE_MSG(TRACE_ERROR, "zb_zcl_get_cluster_write_attr_hook: cluster_id %d cluster_role %hd",
    +  TRACE_MSG(TRACE_ZCL3, "zb_zcl_get_cluster_write_attr_hook: cluster_id %d cluster_role %hd",
                 (FMT__D_H, cluster_id, cluster_role));
     
       /* Search if we already have handler */
    

    diff --git a/zboss/production/src/zcl/zcl_color_control_commands.c b/zboss/production/src/zcl/zcl_color_control_commands.c
    index b9f110b5..59a2c55a 100644
    --- a/zboss/production/src/zcl/zcl_color_control_commands.c
    +++ b/zboss/production/src/zcl/zcl_color_control_commands.c
    @@ -1004,7 +1004,11 @@ static void zb_zcl_process_color_control_move_to_loop(zb_uint8_t param)
       if (rm_time_unit!=0)
       {
         zb_int32_t unit_max = is_16bit ? ZB_UINT16_MAX : ZB_UINT8_MAX;
    -    zb_int32_t all_delta = (loop_data.finish_value - loop_data.start_value);
    +    zb_int32_t all_delta =
    +      (loop_data.finish_value > loop_data.start_value) ?
    +      (loop_data.finish_value - loop_data.start_value) :
    +      (loop_data.start_value - loop_data.finish_value);
    +    zb_int32_t step_value;
     
         if((loop_data.attr_id == ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID)
             || (loop_data.attr_id == ZB_ZCL_ATTR_COLOR_CONTROL_ENHANCED_CURRENT_HUE_ID)
    @@ -1020,8 +1024,17 @@ static void zb_zcl_process_color_control_move_to_loop(zb_uint8_t param)
           }
         }
     
    -    val = (zb_int32_t)(all_delta * delta_time / loop_data.transition_time + loop_data.start_value);
    -    if(val > unit_max)
    +    step_value =
    +      (delta_time == 0U) ?
    +      (all_delta) :
    +      (zb_int32_t)(all_delta * delta_time / loop_data.transition_time);
    +
    +    val =
    +      (loop_data.finish_value > loop_data.start_value) ?
    +      (loop_data.start_value + step_value) :
    +      (loop_data.start_value - step_value);
    +
    +    if (val > unit_max)
         {
           val -= unit_max;
         }
    @@ -1045,8 +1058,20 @@ static void zb_zcl_process_color_control_move_to_loop(zb_uint8_t param)
     
         if (rm_time_unit!=0)
         {
    -      zb_int32_t all_delta = (loop_data.finish_value2 - loop_data.start_value2);
    -      val = (zb_int32_t)(all_delta * delta_time / loop_data.transition_time + loop_data.start_value2);
    +      zb_int32_t all_delta =
    +        (loop_data.finish_value2 > loop_data.start_value2) ?
    +        (loop_data.finish_value2 - loop_data.start_value2) :
    +        (loop_data.start_value2 - loop_data.finish_value2);
    +
    +      zb_int32_t step_value =
    +        (delta_time == 0U) ?
    +        (all_delta) :
    +        (zb_int32_t)(all_delta * delta_time / loop_data.transition_time);
    +
    +      val =
    +        (loop_data.finish_value2 > loop_data.start_value2) ?
    +        (loop_data.start_value2 + step_value) :
    +        (loop_data.start_value2 - step_value);
         }
         else
         {
    

    zcl_color_control_commands.c

    /*
     * ZBOSS Zigbee 3.0
     *
     * Copyright (c) 2012-2022 DSR Corporation, Denver CO, USA.
     * www.dsr-zboss.com
     * www.dsr-corporation.com
     * All rights reserved.
     *
     *
     * Use in source and binary forms, redistribution in binary form only, with
     * or without modification, are permitted provided that the following conditions
     * are met:
     *
     * 1. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 2. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 3. This software, with or without modification, must only be used with a Nordic
     *    Semiconductor ASA integrated circuit.
     *
     * 4. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS OR
     * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE LIABLE
     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
     * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    /* PURPOSE: Zigbee cluster library, the main module
    */
    
    #define ZB_TRACE_FILE_ID 2074
    
    #include "zb_common.h"
    
    /* #define ZB_ZCL_ENABLE_DEFAULT_ALARMS_PROCESSING */
    
    #if defined (ZB_ENABLE_ZCL)
    
    #include "zb_zcl.h"
    #include "zb_aps.h"
    #include "zcl/zb_zcl_common.h"
    
    #if !(defined ZB_ZCL_DISABLE_REPORTING)
    #include "zcl/zb_zcl_reporting.h"
    #endif
    
    #include "zcl/zb_zcl_cvc_commands.h"
    #include "zcl/zcl_ota_upgrade_commands.h"
    #ifdef ZB_ENABLE_HA_SAS
    #include "ha/zb_ha_sas.h"
    #endif
    #include "zb_bdb_internal.h"
    #include "zb_zdo.h"
    #include "zb_aps.h"
    
    #include "zdo_wwah_stubs.h"
    
    static zb_uint_t zb_zcl_get_header_size(zb_bufid_t buf)
    {
      zb_uint8_t* zcl_packet = zb_buf_begin(buf);
      return ZB_ZCL_GET_HEADER_SIZE(*zcl_packet);
    }
    
    #if defined HA_CERTIFICATION
    zb_bool_t zb_zcl_rejected_command(zb_uint8_t param);
    #endif /* HA_CERTIFICATION */
    
    void zb_zcl_init()
    {
      TRACE_MSG(TRACE_ZCL1, ">>zcl_init", (FMT__0));
    
      ZB_BZERO(&ZCL_CTX(), sizeof(zb_zcl_globals_t));
    
      ZB_BZERO(&ZCL8_CTX(), sizeof(zb_zcl8_globals_t));
    /*
      min_interval and max_interval are set to ZERO value by BZERO call
    #if !(defined ZB_ZCL_DISABLE_REPORTING)
      ZCL_CTX().reporting_ctx.min_interval == ZB_ZCL_TIMEOUT_ZERO;
      ZCL_CTX().reporting_ctx.max_interval == ZB_ZCL_TIMEOUT_ZERO;
    #endif
    */
    
      ZCL_CTX().runtime_buf = zb_buf_get_any();
    
      ZB_ASSERT(ZCL_CTX().runtime_buf);
    
    #if !(defined ZB_ZCL_DISABLE_REPORTING)
    /*
      Initialize reporting context, allocate free out buffer for reporting.
      TODO: comment if reporting is not supported.
    */
      zb_zcl_init_reporting_ctx();
    #endif
    
    #if defined ZB_ENABLE_HA_SAS
      zb_ha_process_sas();
    #endif /* defined ZB_ENABLE_HA_SAS */
    
      TRACE_MSG(TRACE_ZCL1, "<<zcl_init", (FMT__0));
    }
    
    zb_uint8_t zb_zcl_handle(zb_uint8_t param)     /* VS Fixed */
    {
      zb_zcl_parsed_hdr_t *cmd_info_ptr =
        (zb_zcl_parsed_hdr_t *)ZB_BUF_GET_PARAM(param, zb_zcl_parsed_hdr_t);
      zb_uint8_t status; /* zcl_status */
      zb_bool_t processed = ZB_FALSE;
    
      TRACE_MSG(TRACE_ZCL1, ">> zb_zcl_handle param %hd", (FMT__H, param));
    
      TRACE_MSG(
          TRACE_ZCL2,
          "f type %d cmd %d",
          (FMT__D_D, cmd_info_ptr->is_common_command, cmd_info_ptr->cmd_id));
    
      status = zb_zcl_check_accept_command(param);
      if (status == ZB_ZCL_STATUS_SUCCESS)
      {
        if (cmd_info_ptr->is_common_command)
        {
          processed = zb_zcl_handle_general_commands(param);
        }
        else
        {
          processed = zb_zcl_handle_specific_commands(param);
        }
    
        if (!processed)
        {
          /* TODO: return correct status by zb_zcl_handle_general_commands() */
          TRACE_MSG(TRACE_ZCL1, "command is not processed", (FMT__0));
          /* ZCL8: CCB 2477: use UNSUP_COMMAND instead of any other Unsupported command status */
          if (zb_zcl_get_backward_compatible_statuses_mode() == ZB_ZCL_STATUSES_ZCL8_MODE)
          {
            status = ZB_ZCL_STATUS_UNSUP_CMD;
          }
          else
          {
            if (cmd_info_ptr->is_manuf_specific)
            {
              status = cmd_info_ptr->is_common_command ?
                ZB_ZCL_STATUS_UNSUP_MANUF_GEN_CMD : ZB_ZCL_STATUS_UNSUP_MANUF_CLUST_CMD;
            }
            else
            {
              status = cmd_info_ptr->is_common_command ?
                ZB_ZCL_STATUS_UNSUP_GEN_CMD : ZB_ZCL_STATUS_UNSUP_CLUST_CMD;
            }
          }
        }
      } /* zb_zcl_check_accept_command() */
      else
      {
    #if defined HA_CERTIFICATION
        if (zb_zcl_rejected_command(param) )
        {
          processed = ZB_TRUE;
        }
        else
    #endif /* HA_CERTIFICATION */
          TRACE_MSG(TRACE_ZCL1, "skip command processing", (FMT__0));
      }
    
      if (processed)
      {
        status = ZB_ZCL_STATUS_SUCCESS;
      }
    
      TRACE_MSG(TRACE_ZCL1, "<< zb_zcl_handle, ret %hd", (FMT__H, status));
    
      return status;
    }
    
    void zb_zcl_deinit()
    {
      TRACE_MSG(TRACE_ZCL1, ">>zcl_deinit", (FMT__0));
    
      TRACE_MSG(TRACE_ZCL1, "<<zcl_deinit", (FMT__0));
    }
    
    /* FIXME: why is this routine in zcl_main.c while it used only once from zb_zcl_process_device_command which is in zcl_common.c?? */
    /* Function parses ZCL header and puts values to zb_zcl_parsed_hdr_t
     * structure. Also, it
     * 1) copies values APSDE data indication - it will be used during ZCL command handling
     * 2) check if command processing allowed by WWAH cluster */
    
    zb_zcl_status_t zb_zcl_parse_header(zb_uint8_t param, zb_zcl_parsed_hdr_t *cmd_info)
    {
      zb_apsde_data_indication_t *ind = ZB_BUF_GET_PARAM(param, zb_apsde_data_indication_t);
      zb_uint8_t *zcl_header;
      zb_zcl_status_t ret = ZB_ZCL_STATUS_SUCCESS;
      zb_uint8_t frame_type;
    
      TRACE_MSG(
          TRACE_ZCL1,
          ">> zb_zcl_parse_header: param %hd h 0x%hx sz %hd, dst endp %hd",
          (FMT__H_H_H_H, param, zb_buf_get_handle(param), zb_buf_len(param), ind->dst_endpoint));
    
      /* buffer should have enough data for parsing command! */
      if (zb_buf_len(param) < zb_zcl_get_header_size(param))
      {
        TRACE_MSG(TRACE_ERROR, "ERROR: buffer should have enough data for parsing command!", (FMT__0));
        return ZB_ZCL_STATUS_MALFORMED_CMD;
      }
    
      ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).source.addr_type = ZB_ZCL_ADDR_TYPE_SHORT;
      ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).source.u.short_addr = ind->src_addr;
      ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).src_endpoint = ind->src_endpoint;
      ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).dst_addr = ind->dst_addr;
      ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).dst_endpoint = ind->dst_endpoint;
      ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).fc = ind->fc;
    
    #if (defined ZB_ENABLE_SE) || (defined ZB_ZCL_SUPPORT_CLUSTER_WWAH)
      ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).aps_key_from_tc = ind->aps_key_from_tc;
    #endif
    
      cmd_info->cluster_id = ind->clusterid;
      cmd_info->profile_id = ind->profileid;
    
      zcl_header = zb_buf_begin(param);
    
      /* ZCL frame format */
      /* frame control 1b | manufacturer code 0/2b | TSN 1b | cmd id 1b | payload XXb */
    
      /* frame control */
      /* frame type | manufacturer specific | direction | disable default resp */
    
      /* Parse ZCL frame control */
      frame_type = ZB_ZCL_GET_FRAME_TYPE(*zcl_header);
      cmd_info->is_common_command = (frame_type == ZB_ZCL_FRAME_TYPE_CLUSTER_SPECIFIC ) ? ZB_FALSE : ZB_TRUE;
      if( frame_type != ZB_ZCL_FRAME_TYPE_CLUSTER_SPECIFIC && frame_type != ZB_ZCL_FRAME_TYPE_COMMON)
      {
        ret = ZB_ZCL_STATUS_INVALID_FIELD;
      }
      cmd_info->cmd_direction = ZB_ZCL_GET_DIRECTION(*zcl_header);
      cmd_info->disable_default_response = (ZB_ZCL_GET_DISABLE_DEFAULT_RESPONSE(*zcl_header) != 0U )?(ZB_TRUE ):(ZB_FALSE );
      if (ZB_ZCL_GET_MANUF_SPECIFIC(*zcl_header) != 0U)
      {
        cmd_info->is_manuf_specific = ZB_TRUE;
        zcl_header++;
        ZB_ZCL_PACKET_GET_DATA16(&(cmd_info->manuf_specific), zcl_header);
      }
      else
      {
        /* there is no manufacturer specific sub field, get TSN field */
        cmd_info->is_manuf_specific = ZB_FALSE;
        zcl_header++;
      }
    
      /* Get TSN */
      cmd_info->seq_number = *zcl_header;
      zcl_header++;
    
      /* Get command ID */
      cmd_info->cmd_id = *zcl_header;
    
      /* Note: there was cut of ZCL header here. Moved to zb_zcl_process_device_command() - the only caller of this routine. */
      TRACE_MSG(TRACE_ZCL1, "<< zb_zcl_parse_header ret = %hd", (FMT__H, ret));
    
      return ret;
    }
    
    zb_ret_t zb_zcl_add_cluster_handlers(zb_uint16_t cluster_id,
                                         zb_uint8_t cluster_role,
                                         zb_zcl_cluster_check_value_t cluster_check_value,
                                         zb_zcl_cluster_write_attr_hook_t cluster_write_attr_hook,
                                         zb_zcl_cluster_handler_t cluster_handler)
    {
      zb_ret_t ret = RET_EMPTY;
    
      TRACE_MSG(TRACE_ZCL1, ">> zb_zcl_add_cluster_handlers cluster 0x%x role %hd",
                (FMT__H_D, cluster_id, cluster_role));
      TRACE_MSG(TRACE_ZCL1, "check_value %p write_attr_hook %p handler %p",
                (FMT__P_P_P, cluster_check_value, cluster_write_attr_hook, cluster_handler));
      if ((cluster_handler != NULL || cluster_check_value != NULL || cluster_write_attr_hook != NULL)
          /*cstat !MISRAC2012-Rule-13.5 !MISRAC2012-Rule-13.6 */
          /* After some investigation, the following violations of Rule 13.5 and 13.6 seem to be false
           * positives. There are no side effect to 'ZCL_CTX()'. This violation seems to be caused by
           * the fact that 'ZCL_CTX()' is an external macro, which cannot be analyzed by C-STAT. */
          && ZCL_CTX().zcl_handlers_cnt < ZB_ARRAY_SIZE(ZCL_CTX().zcl_handlers))
      {
        zb_uindex_t i = 0;
        /* Search if we already have handler */
        while (i < ZCL_CTX().zcl_handlers_cnt)
        {
          if (ZCL_CTX().zcl_handlers[i].cluster_id == cluster_id
              /*cstat !MISRAC2012-Rule-13.5 */
              /* After some investigation, the following violation of Rule 13.5 seems to be a false
               * positive. There are no side effect to 'ZCL_CTX()'. This violation seems to be caused by
               * the fact that 'ZCL_CTX()' is an external macro, which cannot be analyzed by C-STAT. */
              && ZCL_CTX().zcl_handlers[i].cluster_role == cluster_role)
          {
            break;
          }
          ++i;
        }
        if (i == ZCL_CTX().zcl_handlers_cnt)
        {
          /* add new entry */
          ZCL_CTX().zcl_handlers[i].cluster_id = cluster_id;
          ZCL_CTX().zcl_handlers[i].cluster_role = cluster_role;
          ZCL_CTX().zcl_handlers[i].cluster_handler = cluster_handler;
          ZCL_CTX().zcl_handlers[i].cluster_check_value = cluster_check_value;
          ZCL_CTX().zcl_handlers[i].cluster_write_attr_hook = cluster_write_attr_hook;
          ++ZCL_CTX().zcl_handlers_cnt;
          ret = RET_OK;
        }
        else
        {
          ret = RET_ALREADY_EXISTS;
        }
      }
    
      TRACE_MSG(TRACE_ZCL1, "<< zb_zcl_add_cluster_handlers cluster ret %d", (FMT__D, ret));
    
      return ret;
    }
    
    zb_zcl_cluster_handler_t zb_zcl_get_cluster_handler(zb_uint16_t cluster_id, zb_uint8_t cluster_role)
    {
      zb_uindex_t i = 0;
      zb_zcl_cluster_handler_t ret = NULL;
    
      TRACE_MSG(TRACE_ZCL1, ">> zb_zcl_get_cluster_handler: cluster_id 0x%x cluster_role %hd",
                (FMT__D_H, cluster_id, cluster_role));
    
      /* Search if we already have handler */
      while (i < ZCL_CTX().zcl_handlers_cnt)
      {
        if (ZCL_CTX().zcl_handlers[i].cluster_id == cluster_id
            /*cstat !MISRAC2012-Rule-13.5 */
            /* After some investigation, the following violation of Rule 13.5 seems to be a false
             * positive. There are no side effect to 'ZCL_CTX()'. This violation seems to be caused by
             * the fact that 'ZCL_CTX()' is an external macro, which cannot be analyzed by C-STAT. */
            && (ZCL_CTX().zcl_handlers[i].cluster_role == cluster_role
                /* || ZCL_CTX().zcl_handlers[i].cluster_role == ZB_ZCL_CLUSTER_MIXED_ROLE */))
        {
          ret = ZCL_CTX().zcl_handlers[i].cluster_handler;
          break;
        }
        ++i;
      }
    
      TRACE_MSG(TRACE_ZCL1, "<< zb_zcl_get_cluster_handler ret %p", (FMT__P, ret));
    
      return ret;
    }
    
    zb_zcl_cluster_check_value_t zb_zcl_get_cluster_check_value(zb_uint16_t cluster_id, zb_uint8_t cluster_role)
    {
      zb_uindex_t i = 0;
      ZVUNUSED(cluster_role);
    
      TRACE_MSG(TRACE_ZCL3, "zb_zcl_get_cluster_check_value: cluster_id %d cluster_role %hd",
                (FMT__D_H, cluster_id, cluster_role));
    
      /* Search if we already have handler */
      while (i < ZCL_CTX().zcl_handlers_cnt)
      {
        if (ZCL_CTX().zcl_handlers[i].cluster_id == cluster_id
            /*cstat !MISRAC2012-Rule-13.5 */
            /* After some investigation, the following violation of Rule 13.5 seems to be a false
             * positive. There are no side effect to 'ZCL_CTX()'. This violation seems to be caused by
             * the fact that 'ZCL_CTX()' is an external macro, which cannot be analyzed by C-STAT. */
            && ZCL_CTX().zcl_handlers[i].cluster_role == cluster_role)
        {
          return ZCL_CTX().zcl_handlers[i].cluster_check_value;
        }
        ++i;
      }
      return NULL;
    }
    
    zb_zcl_cluster_write_attr_hook_t zb_zcl_get_cluster_write_attr_hook(zb_uint16_t cluster_id, zb_uint8_t cluster_role)
    {
      zb_uindex_t i = 0;
      ZVUNUSED(cluster_role);
    
      TRACE_MSG(TRACE_ZCL3, "zb_zcl_get_cluster_write_attr_hook: cluster_id %d cluster_role %hd",
                (FMT__D_H, cluster_id, cluster_role));
    
      /* Search if we already have handler */
      while (i < ZCL_CTX().zcl_handlers_cnt)
      {
        if (ZCL_CTX().zcl_handlers[i].cluster_id == cluster_id
            /*cstat !MISRAC2012-Rule-13.5 */
            /* After some investigation, the following violation of Rule 13.5 seems to be a false
             * positive. There are no side effect to 'ZCL_CTX()'. This violation seems to be caused by
             * the fact that 'ZCL_CTX()' is an external macro, which cannot be analyzed by C-STAT. */
            && ZCL_CTX().zcl_handlers[i].cluster_role == cluster_role)
        {
          return ZCL_CTX().zcl_handlers[i].cluster_write_attr_hook;
        }
        ++i;
      }
      return NULL;
    }
    
    #ifdef ZB_CONTROL4_NETWORK_SUPPORT
    zb_ret_t zb_zcl_add_control4_cluster_handlers(zb_uint16_t cluster_id, zb_uint8_t cluster_role,
                                                  zb_zcl_cluster_check_value_t cluster_check_value,
                                                  zb_zcl_cluster_write_attr_hook_t cluster_write_attr_hook,
                                                  zb_zcl_cluster_handler_t cluster_handler)
    {
      zb_ret_t ret = RET_EMPTY;
    
      TRACE_MSG(TRACE_ZCL1, "zb_zcl_add_control4_cluster_handlers cluster 0x%x role %hd",
                (FMT__H_D, cluster_id, cluster_role));
    
      if (cluster_handler || cluster_check_value || cluster_write_attr_hook)
      {
        zb_uindex_t i = (cluster_role == ZB_ZCL_CLUSTER_SERVER_ROLE ? 0 : 1);
    
        /* add new entry */
        ZCL_CTX().control4_zcl_handlers[i].cluster_id = cluster_id;
        ZCL_CTX().control4_zcl_handlers[i].cluster_role = cluster_role;
        ZCL_CTX().control4_zcl_handlers[i].cluster_handler = cluster_handler;
        ZCL_CTX().control4_zcl_handlers[i].cluster_check_value = cluster_check_value;
        ZCL_CTX().control4_zcl_handlers[i].cluster_write_attr_hook = cluster_write_attr_hook;
        ret = RET_OK;
      }
    
      return ret;
    }
    
    zb_zcl_cluster_handler_t zb_zcl_get_control4_cluster_handler(zb_uint16_t cluster_id, zb_uint8_t cluster_role)
    {
      zb_uindex_t i = (cluster_role == ZB_ZCL_CLUSTER_SERVER_ROLE ? 0 : 1);
    
      TRACE_MSG(TRACE_ZCL1, "zb_zcl_get_control4_cluster_handler: cluster_id 0x%x cluster_role %hd",
                (FMT__D_H, cluster_id, cluster_role));
    
      /* Search if we already have handler */
      if (ZCL_CTX().control4_zcl_handlers[i].cluster_id == cluster_id &&
          ZCL_CTX().control4_zcl_handlers[i].cluster_role == cluster_role)
      {
        return ZCL_CTX().control4_zcl_handlers[i].cluster_handler;
      }
    
      return NULL;
    }
    
    zb_zcl_cluster_check_value_t zb_zcl_get_control4_cluster_check_value(zb_uint16_t cluster_id, zb_uint8_t cluster_role)
    {
      zb_uindex_t i = (cluster_role == ZB_ZCL_CLUSTER_SERVER_ROLE ? 0 : 1);
    
      TRACE_MSG(TRACE_ERROR, "zb_zcl_get_control4_cluster_check_value: cluster_id %d cluster_role %hd",
                (FMT__D_H, cluster_id, cluster_role));
    
      /* Search if we already have handler */
      if (ZCL_CTX().control4_zcl_handlers[i].cluster_id == cluster_id
          && ZCL_CTX().control4_zcl_handlers[i].cluster_role == cluster_role)
      {
        return ZCL_CTX().control4_zcl_handlers[i].cluster_check_value;
      }
    
      return NULL;
    }
    
    zb_zcl_cluster_write_attr_hook_t zb_zcl_get_control4_cluster_write_attr_hook(zb_uint16_t cluster_id, zb_uint8_t cluster_role)
    {
      zb_uindex_t i = (cluster_role == ZB_ZCL_CLUSTER_SERVER_ROLE ? 0 : 1);
    
      TRACE_MSG(TRACE_ERROR, "zb_zcl_get_control4_cluster_write_attr_hook: cluster_id %d cluster_role %hd",
                (FMT__D_H, cluster_id, cluster_role));
    
      /* Search if we already have handler */
      if (ZCL_CTX().control4_zcl_handlers[i].cluster_id == cluster_id
          && ZCL_CTX().control4_zcl_handlers[i].cluster_role == cluster_role)
      {
        return ZCL_CTX().control4_zcl_handlers[i].cluster_write_attr_hook;
      }
    
      return NULL;
    }
    
    static zb_bool_t zb_zcl_check_control4_cluster_handler(zb_uint8_t endpoint)
    {
      zb_bool_t res = ZB_FALSE;
    
      ZVUNUSED(endpoint);
    
    #ifdef ZB_ED_FUNC
      res = (zb_bool_t )(zb_control4_network_permitted() && endpoint == ZB_CONTROL4_NETWORK_ENDPOINT);
    #endif /* ZB_ED_FUNC */
    
      return res;
    }
    #endif /* ZB_CONTROL4_NETWORK_SUPPORT */
    
    zb_zcl_cluster_handler_t zb_zcl_get_cluster_handler_internal(zb_uint8_t endpoint, zb_uint16_t cluster_id, zb_uint8_t cluster_role)
    {
      zb_zcl_cluster_handler_t cluster_handler;
    
      ZVUNUSED(endpoint);
    
    #ifdef ZB_CONTROL4_NETWORK_SUPPORT
      if (zb_zcl_check_control4_cluster_handler(endpoint))
      {
        cluster_handler = zb_zcl_get_control4_cluster_handler(cluster_id, cluster_role);
      }
      else
    #endif /* ZB_CONTROL4_NETWORK_SUPPORT */
      {
        cluster_handler = zb_zcl_get_cluster_handler(cluster_id, cluster_role);
      }
    
      return cluster_handler;
    }
    
    zb_zcl_cluster_check_value_t zb_zcl_internal_get_cluster_check_value(zb_uint8_t endpoint, zb_uint16_t cluster_id, zb_uint8_t cluster_role)
    {
      zb_zcl_cluster_check_value_t cluster_check_value;
    
      ZVUNUSED(endpoint);
    
    #ifdef ZB_CONTROL4_NETWORK_SUPPORT
      if (zb_zcl_check_control4_cluster_handler(endpoint))
      {
        cluster_check_value = zb_zcl_get_control4_cluster_check_value(cluster_id, cluster_role);
      }
      else
    #endif /* ZB_CONTROL4_NETWORK_SUPPORT */
      {
        cluster_check_value = zb_zcl_get_cluster_check_value(cluster_id, cluster_role);
      }
    
      return cluster_check_value;
    }
    
    zb_zcl_cluster_write_attr_hook_t zb_zcl_internal_get_cluster_write_attr_hook(zb_uint8_t endpoint, zb_uint16_t cluster_id, zb_uint8_t cluster_role)
    {
      zb_zcl_cluster_write_attr_hook_t cluster_write_attr_hook;
    
      ZVUNUSED(endpoint);
    
    #ifdef ZB_CONTROL4_NETWORK_SUPPORT
      if (zb_zcl_check_control4_cluster_handler(endpoint))
      {
        cluster_write_attr_hook = zb_zcl_get_control4_cluster_write_attr_hook(cluster_id, cluster_role);
      }
      else
    #endif /* ZB_CONTROL4_NETWORK_SUPPORT */
      {
        cluster_write_attr_hook = zb_zcl_get_cluster_write_attr_hook(cluster_id, cluster_role);
      }
    
      return cluster_write_attr_hook;
    }
    
    
    zb_bool_t zb_zcl_handle_specific_commands(zb_uint8_t param)
    {
      zb_bool_t processed;
      zb_zcl_parsed_hdr_t *cmd_info = ZB_BUF_GET_PARAM(param, zb_zcl_parsed_hdr_t);
    
      TRACE_MSG(TRACE_ZCL1, "> zb_zcl_handle_specific_commands param %hd", (FMT__H, param));
    
      {
        zb_zcl_cluster_handler_t cluster_handler;
    
        cluster_handler = zb_zcl_get_cluster_handler_internal(cmd_info->addr_data.common_data.dst_endpoint,
                                                              cmd_info->cluster_id,
                                                              cmd_info->cmd_direction == ZB_ZCL_FRAME_DIRECTION_TO_SRV ?
                                                              ZB_ZCL_CLUSTER_SERVER_ROLE :
                                                              ZB_ZCL_CLUSTER_CLIENT_ROLE);
        if (cluster_handler != NULL)
        {
          processed = cluster_handler(param);
        }
        else
        {
          TRACE_MSG(TRACE_ZCL1, "Cluster 0x%04x was not processed", (FMT__D, cmd_info->cluster_id));
          processed = ZB_FALSE;
        }
      }
    
      TRACE_MSG(TRACE_ZCL1, "< zb_zcl_handle_specific_commands processed %hd", (FMT__H, processed));
      return processed;
    }/* zb_uint8_t zb_zcl_handle_specific_commands(zb_uint8_t param) */
    
    #if defined HA_CERTIFICATION
    /* During HA certification came out that for some commands which
     * should not be handled (command with direction "to server" is sent
     * to client for example) we need to send a response anyway, but with
     * "empty" body */
    zb_bool_t zb_zcl_rejected_command(zb_uint8_t param)
    {
      zb_zcl_parsed_hdr_t *cmd_info = ZB_BUF_GET_PARAM(param, zb_zcl_parsed_hdr_t);
      zb_zcl_cluster_desc_t *cluster_desc;
      zb_af_endpoint_desc_t *endpoint_desc;
      zb_bool_t ret = ZB_FALSE;
    
      TRACE_MSG(TRACE_ZCL1, ">> zb_zcl_rejected_command %hd", (FMT__H, param));
    
      endpoint_desc = zb_af_get_endpoint_desc(ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).dst_endpoint);
      cluster_desc = get_cluster_desc(ep_desc, cmd_info->cluster_id,
                                      (cmd_info->cmd_direction == ZB_ZCL_FRAME_DIRECTION_TO_SRV) ?
                                      ZB_ZCL_CLUSTER_SERVER_ROLE : ZB_ZCL_CLUSTER_CLIENT_ROLE);
    
      ZB_ZCL_DEBUG_DUMP_HEADER(cmd_info);
    
      /* if command is manufacturer specific - skip handling it by default */
      TRACE_MSG(TRACE_ZCL2, "manuf_spec %hd", (FMT__H, cmd_info->is_manuf_specific));
      if (cluster_desc &&
          !cmd_info->is_manuf_specific &&
          cmd_info->is_common_command)
      {
        switch(cmd_info->cmd_id)
        {
        case ZB_ZCL_CMD_DISCOVER_COMMANDS_RECEIVED:
        case ZB_ZCL_CMD_DISCOVER_COMMANDS_GENERATED:
          ret = zb_zcl_handle_general_commands(param);
          break;
    
        case ZB_ZCL_CMD_DISCOVER_ATTR_EXT:
        {
          zb_uint8_t *resp_data;
          resp_data = ZB_ZCL_START_PACKET(ZCL_CTX().runtime_buf);
    
          /* generate frame control taking into account manufacturer specific */
          ZB_ZCL_CONSTRUCT_GENERAL_COMMAND_RESP_FRAME_CONTROL_EXT(
            resp_data, cmd_info->is_manuf_specific, ZB_ZCL_REVERT_DIRECTION(cmd_info->cmd_direction));
          /* Generate command header taking into account manufacturer specific */
          ZB_ZCL_CONSTRUCT_COMMAND_HEADER_EXT(
            resp_data, cmd_info->seq_number, cmd_info->is_manuf_specific, cmd_info->manuf_specific,
            ZB_ZCL_CMD_DISCOVER_ATTR_EXT_RES);
          /* There is the same beginning for both Discover Attr and Discover attr ext Resp */
          ZB_ZCL_PACKET_PUT_DATA8(resp_data, ZB_ZCL_DISC_COMPLETE);
    
          ZB_ZCL_FINISH_N_SEND_PACKET(ZCL_CTX().runtime_buf, resp_data,
              ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).source.u.short_addr,
              ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
              ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).src_endpoint,
              ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).dst_endpoint,
              cmd_info->profile_id,
              cmd_info->cluster_id,
              NULL);
    
          /* Use runtime buffer to send request, use received buffer with
           * request data_buf as runtime buffer */
          ret = ZB_TRUE;
        }
        break;
    
    
        case ZB_ZCL_CMD_DISCOVER_COMMANDS_RECEIVED_RES:
        case ZB_ZCL_CMD_DISCOVER_COMMANDS_GENERATED_RES:
        case ZB_ZCL_CMD_DISCOVER_ATTR_EXT_RES:
          ret = ZB_FALSE;
          break;
    
        default:
          ret = ZB_FALSE;
          break;
        }
      }
    
      TRACE_MSG(TRACE_ZCL1, "<< zb_zcl_rejected_command %hd", (FMT__H, ret));
      return ret;
    }
    #endif /* HA_CERTIFICATION */
    
    
    void zb_zcl_send_cmd_tsn(
      zb_uint8_t param,
      const zb_addr_u *dst_addr,
      zb_aps_addr_mode_t dst_addr_mode,
      zb_uint8_t dst_ep,
      zb_zcl_frame_direction_t direction,
      zb_uint8_t src_ep,
      const void *payload,
      zb_uint8_t payload_size,
      zb_zcl_put_payload_cb_t put_payload,
      zb_zcl_cluster_id_t cluster_id,
      zb_zcl_disable_default_response_t def_resp,
      zb_uint8_t cmd_id,
      zb_uint8_t tsn,
      zb_callback_t cb
      )
    {
      zb_uint16_t profile_id = get_profile_id_by_endpoint(src_ep);
      zb_uint32_t bytes_avail;
    #ifdef APS_FRAGMENTATION
      /* zb_uint16_t payload_out_cnt = 3; */
      /* It was static variable initially in SE code. Seems can use a runtime buffer for it. Hope no conflict here. */
      /* zb_uint8_t *payload_temp_out; */
    #endif
      zb_uint8_t *cmd_ptr;
    
      TRACE_MSG(TRACE_ZCL2, "zb_zcl_send_cmd: param %hd (cluster_id 0x%x cmd_id %d)", (FMT__H_D_D, param, cluster_id, cmd_id));
      TRACE_MSG(TRACE_ZCL2, "payload size %hd", (FMT__H, payload_size));
      cmd_ptr = ZB_ZCL_START_PACKET(param);
      ZB_ZCL_CONSTRUCT_SET_FRAME_CONTROL(*cmd_ptr,
                                         ZB_ZCL_FRAME_TYPE_CLUSTER_SPECIFIC,
                                         ZB_ZCL_NOT_MANUFACTURER_SPECIFIC,
                                         direction,
                                         def_resp);
      cmd_ptr++;
      ZB_ZCL_CONSTRUCT_COMMAND_HEADER(cmd_ptr, tsn, cmd_id);
    
    #ifdef APS_FRAGMENTATION
      bytes_avail = (ZB_ASDU_MAX_FRAG_LEN - ZB_ZCL_GET_BYTES_WRITTEN(param, cmd_ptr));
    #else
      bytes_avail = (ZB_ZCL_HI_MAX_PAYLOAD_SIZE - ZB_ZCL_GET_BYTES_WRITTEN(param, cmd_ptr));
    #endif
    
      if (bytes_avail < payload_size)
      {
        TRACE_MSG(TRACE_ERROR, "zb_zcl_send_cmd: ERROR, buffer is full (cluster_id %d cmd_id %d)", (FMT__D_D, cluster_id, cmd_id));
        zb_buf_free(param);
        return;
      }
      else
      {
        TRACE_MSG(TRACE_ZCL3, "zb_zcl_send_cmd: buffer have enough space (bytes_avail = %hd)", (FMT__H, bytes_avail));
      }
      if (put_payload != NULL && payload != NULL)
      {
        cmd_ptr = put_payload(cmd_ptr, payload);
      }
      else if (put_payload == NULL && payload != NULL)
      {
        ZB_MEMCPY(cmd_ptr, payload, payload_size);
        cmd_ptr += payload_size;
      }
      else
      {
        /* MISRA rule 15.7 requires empty 'else' branch. */
      }
    
      TRACE_MSG(TRACE_ZCL3, "zb_zcl_send_cmd addr_type %d to " TRACE_FORMAT_64,
                (FMT__D_A, dst_addr_mode, TRACE_ARG_64(dst_addr->addr_long)));
    
      ZB_ZCL_FINISH_N_SEND_PACKET(param, cmd_ptr,
                                  *dst_addr,
                                  dst_addr_mode,
                                  dst_ep,
                                  src_ep,
                                  profile_id,
                                  cluster_id,
                                  cb);
    }
    
    void zb_zcl_send_cmd(
      zb_uint8_t param,
      const zb_addr_u *dst_addr,
      zb_aps_addr_mode_t dst_addr_mode,
      zb_uint8_t dst_ep,
      zb_zcl_frame_direction_t direction,
      zb_uint8_t src_ep,
      const void *payload,
      zb_uint8_t payload_size,
      zb_zcl_put_payload_cb_t put_payload,
      zb_zcl_cluster_id_t cluster_id,
      zb_zcl_disable_default_response_t def_resp,
      zb_uint8_t cmd_id,
      zb_callback_t cb
      )
    {
      zb_zcl_send_cmd_tsn(param, dst_addr, dst_addr_mode, dst_ep, direction,
      src_ep, payload, payload_size, put_payload, cluster_id, def_resp, cmd_id, ZB_ZCL_GET_SEQ_NUM(), cb);
    }
    
    
    /* Helper for sending default responses */
    zb_bool_t zb_zcl_send_default_handler(zb_uint8_t param,
      const zb_zcl_parsed_hdr_t *cmd_info, zb_zcl_status_t status)
    {
      zb_bool_t ret;
    
      TRACE_MSG(TRACE_ZCL3, ">zb_zcl_send_default_handler param %hd, status: %hd", (FMT__H_H, param, status));
    
      do
      {
        if (!ZB_ZCL_CHECK_IF_SEND_DEFAULT_RESP(*cmd_info, status))
        {
          ret = ZB_FALSE;
          break;
        }
    
        ZB_ZCL_SEND_DEFAULT_RESP_DIRECTION(
          param,
          ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).source.u.short_addr,
          ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
          ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).src_endpoint,
          ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).dst_endpoint,
          cmd_info->profile_id,
          cmd_info->cluster_id,
          cmd_info->seq_number,
          cmd_info->cmd_id,
          status,
          ZB_ZCL_REVERT_DIRECTION(cmd_info->cmd_direction));
    
        param = 0;
        ret = ZB_TRUE;
      } while (0);
    
      if (param != 0u)
      {
        zb_buf_free(param);
      }
    
      TRACE_MSG(TRACE_ZCL3, "<zb_zcl_send_default_handler %d", (FMT__D, ret));
      return ret;
    }
    
    void zb_zcl_process_command_finish(zb_bufid_t buffer, zb_zcl_parsed_hdr_t *pcmd_info, zb_uint8_t status)
    {
      if (ZB_ZCL_CHECK_IF_SEND_DEFAULT_RESP(*(pcmd_info), status))
      {
        ZB_ZCL_SEND_DEFAULT_RESP(
          (buffer),
          ZB_ZCL_PARSED_HDR_SHORT_DATA((pcmd_info)).source.u.short_addr,
          ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
          ZB_ZCL_PARSED_HDR_SHORT_DATA((pcmd_info)).src_endpoint,
          ZB_ZCL_PARSED_HDR_SHORT_DATA((pcmd_info)).dst_endpoint,
          (pcmd_info)->profile_id,
          (pcmd_info)->cluster_id,
          (pcmd_info)->seq_number,
          (pcmd_info)->cmd_id,
          (status));
      }
      else
      {
        TRACE_MSG( TRACE_ZCL3, "Default response disabled", (FMT__0));
        zb_buf_free(buffer);
      }
    }
    
    void zb_zcl_process_command_finish_new(zb_bufid_t buffer, zb_zcl_parsed_hdr_t *pcmd_info, zb_uint8_t status)
    {
      if (ZB_ZCL_CHECK_IF_SEND_DEFAULT_RESP(*(pcmd_info), status))
      {
        ZB_ZCL_SEND_DEFAULT_RESP_NEW(
          (buffer),
          ZB_ZCL_PARSED_HDR_SHORT_DATA((pcmd_info)).source.u.short_addr,
          ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
          ZB_ZCL_PARSED_HDR_SHORT_DATA((pcmd_info)).src_endpoint,
          ZB_ZCL_PARSED_HDR_SHORT_DATA((pcmd_info)).dst_endpoint,
          (pcmd_info)->profile_id,
          (pcmd_info)->cluster_id,
          (pcmd_info)->seq_number,
          (pcmd_info)->cmd_id,
          (status),
          ZB_APS_FC_IS_SECURE((pcmd_info)->addr_data.common_data.fc));
      }
      else
      {
        TRACE_MSG( TRACE_ZCL3, "Default response disabled", (FMT__0));
        zb_buf_free(buffer);
      }
    }
    
    void zb_zcl_send_default_resp_ext(zb_uint8_t param,
                                      const zb_zcl_parsed_hdr_t *cmd_info, zb_zcl_status_t status)
    {
      if (ZB_ZCL_CHECK_IF_SEND_DEFAULT_RESP(*cmd_info, status))
      {
        ZB_ZCL_SEND_DEFAULT_RESP_EXT(
          param,
          ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).source.u.short_addr,
          ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
          ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).src_endpoint,
          ZB_ZCL_PARSED_HDR_SHORT_DATA(cmd_info).dst_endpoint,
          cmd_info->profile_id,
          cmd_info->cluster_id,
          cmd_info->seq_number,
          cmd_info->cmd_id,
          status,
          ZB_ZCL_REVERT_DIRECTION(cmd_info->cmd_direction),
          cmd_info->is_manuf_specific,
          cmd_info->manuf_specific,
          NULL);
      }
      else
      {
        zb_buf_free(param);
      }
    }
    
    #endif /* defined (ZB_ENABLE_ZCL) */
    

    Best regards,
    Marte

  • Hi,

    This has been fixed in nRF Connect SDK v2.6.0.

    Best regards,
    Marte

Reply Children
No Data
Related