[Zigbee] Manufacturer Code clarification

Setup: 

nrf52840DK (Zigbee Cordinator based on Zigbee Coordinator sample)

nrf52840DK (ZED)

nrf52840 dongle (Wireshark sniffer)

NCS v2.4.1

Hi,

In my application I am using Manufacturer specific ZCL commands and attributes. All of that seems working fine but I am little bit confused regarding the Manufacturer Code usage.

There are two magic values in NCS: 
0x1234 defined as:

#define ZB_MANUFACTURER_CODE_DSR            0x1234U
/* That is not a joke, our manufacturer code is really 1234! */
/*! @endcond */ /* internals_doc */

#define ZB_DEFAULT_MANUFACTURER_CODE ZB_MANUFACTURER_CODE_DSR

Manufacturer Code of DSR for all applications based on ZBOSS.

0x127F defined as: 

config ZIGBEE_FOTA_MANUFACTURER_ID
	hex "Manufacturer ID"
	default 0x127F
	range 0x0000 0xFFFF
	help
	  This is the ZigBee assigned identifier for each member company.
	  0x127F - Nordic Semiconductor
	  0xFFFF - wild card value has a 'match all' effect.

Manufacturer Code of Nordic Semiconductor used only for FOTA purposes to fill up OTA Header with it.

  1. What happens if the manufacturer code placed in the OTA Header of the received image is different from the one set on the device (0x127F by default)?
  2. Which Manufacturer Code should be used in Cluster descriptor ?
    /** @brief ZCL cluster description. */
    typedef ZB_PACKED_PRE struct zb_zcl_cluster_desc_s
    {
      zb_uint16_t cluster_id; /**< ZCL 16-bit cluster id. @see zcl_cluster_id */
      zb_uint16_t attr_count;  /**< Attributes number supported by the cluster */
      struct zb_zcl_attr_s *attr_desc_list; /**< List of cluster attributes,
             declared using ZB_ZCL_DECLARE_<CLUSTER_NAME>_ATTRIB_LIST() macro */
      zb_uint8_t role_mask;   /**< Cluster role, refer to zcl_cluster_role */
      zb_uint16_t manuf_code; /**< Manufacturer code for cluster and its attributes */
      zb_zcl_cluster_init_t cluster_init;
    } ZB_PACKED_STRUCT
    zb_zcl_cluster_desc_t;
  3. Is it possible to overwrite #ZB_DEFAULT_MANUFACTURER_CODE ?
  4. Why #ZB_DEFAULT_MANUFACTURER_CODE is not overwritten by Nordic Semiconductor Manufacturer Code (0x127F) ? 

I am looking forward to hearing from you,

Pawel

Parents
  • Hello Pawel,

    What happens if the manufacturer code placed in the OTA Header of the received image is different from the one set on the device (0x127F by default)?

    According to the Zigbee FOTA library documentation, the image should only be accepted if the Manufacturer ID of the FOTA header matches, as well as the image type, Hardware version and Firmware version. 

    Regarding question 3 and 4. I assume it is possible to overwrite it, but I am not sure whether this code is even used at all. Unless the Zboss library (not open source) fetches the ZB_DEFAULT_MANUFACTURER_CODE and uses it, it is not used at all in the nRF Connect SDK. Seeing as it builds just fine without the ZB_DEFAULT_MANUFACTURER_CODE_DSR definition, I believe that isn't used anywhere. Remember that this library is not used on Nordic devices only. So perhaps it is used by other chip vendors. 

    To answer your second question, and all of them, I guess. If you intend to create a custom cluster, you must include a manufacturer_code. Before you start selling it, you need to certify it with the Zigbee Alliance, and for this, you are assigned your own company manufacturer code. So I believe they will tell you to use your custom manufacturer code for your own clusters. And I guess you are free to use either Nordic's or your own for the FOTA cluster, and as long as you use the same when generating FOTA images, that should work just fine.

    Best regards,

    Edvin

  • Hi Edvin,

    Thank you for fast response.

    ZB_DEFAULT_MANUFACTURER_CODE is indeed used by the stack. 
    Manufacturer Code 0x1234 (DSR) is used in Node Descriptor Response as an answer for Nordic Descriptor Request sent from parent device. Have a look on the screenshot below: 



    My goal is to have my custom (ofc certified by Zigbee Aliance) manufacturer code everywhere. 
    I am able to set it in Cluster descriptors and for FOTA purposes in OTA Upgrade cluster as well but it seems that the Node Descriptor Response still uses the DSR Manufacturer Code. 
    I would like to change ZB_DEFAULT_MANUFACTURER_CODE to custom one without needing to do the fork of NCS.
    Is it possible ? 

    Thanks in advance,

    Pawel

  • I didn't try to capture a sniffer trace, but I tried building the paplication after commenting out the ZB_MANUFACTURER_CODE_DSR and ZB_DEFAULT_MANUFACTURER CODE from zb_config_common.h, but perhaps it defaults to 0x1234 if t is not present. I tried building the basic light bulb/switch/coordinator trio, but I guess the bulb doesn't provide a manufacturer code, since it is not a custom cluster. 

    Can you try to insert your own manufacturer code in zb_config_common.h (ncs\nrfxlib\zboss\development\include\zb_config_common.h or ncs\nrfxlib\zboss\production\include\zb_config_common.h. Whichever you are using), and see if you are able to change it at all?

    I understand your wish to do so without forking out of NCS, but I just want to check whether this instance of ZB_MANUFACTURER_CODE_DSR is being used at all.

    If it is used, would it be possible to upload your application (or a stripped down version of it), so that I can poke around with the manufacturer codes?

    Best regards,

    Edvin

Reply
  • I didn't try to capture a sniffer trace, but I tried building the paplication after commenting out the ZB_MANUFACTURER_CODE_DSR and ZB_DEFAULT_MANUFACTURER CODE from zb_config_common.h, but perhaps it defaults to 0x1234 if t is not present. I tried building the basic light bulb/switch/coordinator trio, but I guess the bulb doesn't provide a manufacturer code, since it is not a custom cluster. 

    Can you try to insert your own manufacturer code in zb_config_common.h (ncs\nrfxlib\zboss\development\include\zb_config_common.h or ncs\nrfxlib\zboss\production\include\zb_config_common.h. Whichever you are using), and see if you are able to change it at all?

    I understand your wish to do so without forking out of NCS, but I just want to check whether this instance of ZB_MANUFACTURER_CODE_DSR is being used at all.

    If it is used, would it be possible to upload your application (or a stripped down version of it), so that I can poke around with the manufacturer codes?

    Best regards,

    Edvin

Children
  • I understand. I will experiment more with the manufacturer codes to come up with some conclusions. Then, I will try to prepare a sample app for you, as the entire project is quite extensive.

    In the meantime, could you please answer the questions below?

    I would like to ensure correctness in dealing with Manufacturer-specific ZCL clusters, commands, and attributes. I aim to be consistent with the ZCL Specification and NCS throughout the process.

    For standardizing ZCL clusters (e.g., Basic, Metering, etc.) and custom attributes within, I am considering the following options:

    Option 1: The custom attribute has ZB_ZCL_ATTR_MANUF_SPEC access set, implying that the Cluster Descriptor should contain the Manufacturer code. Other devices in the network should then use the ZCL Header with the Manufacturer Specific bit set, along with the Manufacturer Code placed in the header, to perform ZCL:AttrWrite, ZCL:AttrRead, ZCL:ConfigureReporting on that attribute. I assume standard attributes should be accessed without the Manufacturer Specific bit and without the Manufacturer Code.

    Option 2: The custom attribute has ZB_ZCL_ATTR_MANUF_SPEC set, but the Cluster Descriptor does not need to use the Manufacturer Code and be Manufacturer-specific. Other devices in the network should use the ZCL Header with the Manufacturer Specific bit set, along with the Manufacturer Code in the header, to perform ZCL:AttrWrite, ZCL:AttrRead, ZCL:ConfigureReporting on that attribute. I assume standard attributes should be accessed without the Manufacturer Specific bit and without the Manufacturer Code.

    Option 3: The custom attribute does not have ZB_ZCL_ATTR_MANUF_SPEC access set, and the Cluster Descriptor does not need to use the Manufacturer Code and be Manufacturer-specific. Other devices in the network should use a simple ZCL Header without the Manufacturer Specific bit and without the Manufacturer code.

    I couldn't find any information regarding this. I think Option 2 is incorrect, and we should consider Options 1 and 3.

    For a custom ZCL Cluster with custom attributes within:

    All attributes have ZB_ZCL_ATTR_MANUF_SPEC access set, and the Cluster Descriptor contains the Manufacturer Code. Other devices in the network should then use the ZCL Header with the Manufacturer Specific bit set, along with the Manufacturer Code in the header, to perform ZCL:AttrWrite, ZCL:AttrRead, ZCL:ConfigureReporting on these attributes.

    Does this approach sound correct?

    I am looking forward to hearing from you.

    Pawel

  • I have changed the 

    #define ZB_MANUFACTURER_CODE_DSR 0x1234U
    to some dummy value but still Node Descriptor was showing 0x1234. It seems that ZBOSS uses 0x1234 as default internally anyway but I have got good news :) I found following API in zboss_api.h : 
    /**
     * Set Manufacturer code of Node Descriptor Request
     *
     * @param manuf_code - manufacturer code to set
     * @param cb         - callback that is called after manufacturer code setting
    */
    void zb_set_node_descriptor_manufacturer_code_req(zb_uint16_t manuf_code, zb_set_manufacturer_code_cb_t cb);

    I call it in zboss_signal_handler on signal ZB_ZDO_SIGNAL_SKIP_STARTUP indicating that the stack is ready.
    Callback returns RET_OK and Node Descriptor contains my custom Manufacturer Code, so I think the problem is solved.
    Now I am waiting for your answer regarding dealing with Manufacturer-specific ZCL clusters, commands, and attributes and we are done
    Regards,
    Pawel
  • Hello Pawel,

    I am terribly sorry for the delayed reply. 

    pwpot said:
    I couldn't find any information regarding this. I think Option 2 is incorrect, and we should consider Options 1 and 3.

    I agree. But I will forward this to out Zigbee team for sanity checking.

  • Hello Pawel,

    After consulting with our Zigbee team, this is what I got:

    ----------------------------

    You need to use manufacturer code for any manufacturer specific extension.
    As the spec (ZCL8 ch. 2.3.3) states, you can extend the standard in the following ways:

    Add manufacturer specific clusters to a standard device endpoint.

    Add manufacturer specific commands to a standard cluster.

    Add manufacturer specific attributes to a standard cluster.

    In all cases, you need to specify that the extension is manufacturer specific. So in the customer’s case, they will need to add ZB_ZCL_ATTR_MANUF_SPEC to the manufacturer specific attributes. The cluster itself does not need manufacturer code if it is a standard cluster.

    When it comes to communication, all communication regarding the manufacturer specific extension must be transmitted with the manufacturer sub-field set to 1 and with the manufacturer code. That goes both for the device itself and for remote devices, e.g., a remote device sending a read attributes command to read the manufacturer specific attribute.

    ----------------------------

    I hope this helps. Basically, all custom clusters, commands and attributes needs to set the manufacturer sub-field to 1, and use the manufacturer code that corresponds with this feature.

    Best regards,

    Edvin

  • Hi Edvin,

    Thank you for the detailed description.

    I am a little bit unsure about the manufacturer-specific attribute within the standard cluster scenario in terms of attribute reporting.

    See zb_zcl_send_report_attr_command function: 

    void zb_zcl_send_report_attr_command(zb_zcl_reporting_info_t *rep_info, zb_uint8_t param)
    {
      zb_uint8_t *cmd_data;
      zb_zcl_reporting_info_t *cur_rep_info;
      zb_zcl_attr_t *attr_desc;
      zb_uint16_t bytes_avail;
      zb_uint8_t attr_size;
      zb_uint8_t send_mode;
      zb_uint8_t is_manuf_spec;
    
      TRACE_MSG(
          TRACE_ZCL1,
          ">> zb_zcl_send_report_attr_command rep_info %p, param %hd",
          (FMT__P_H, rep_info, param));
    
      attr_desc =
        zb_zcl_get_attr_desc_a(rep_info->ep, rep_info->cluster_id, rep_info->cluster_role, rep_info->attr_id);
    
      is_manuf_spec = !!ZB_ZCL_IS_ATTR_MANUF_SPEC(attr_desc);
    
      /* ZCL spec, 2.4.11 Report Attributes Command */
      /* Read attribute command
         | ZCL header 3 b | Attr Report 1 XX b | Attr Report 1 XX b | ...
    
         Attr Report format
         | attr id 2 b |  Attr data type 1 b | attr value XX b |
      */
      /* Construct packet header */
      /* Use buffer specified by input param */
      cmd_data = ZB_ZCL_START_PACKET(param);
    
      /* NOTE: currently, manufacturer specific is not supported */
      ZB_ZCL_CONSTRUCT_GENERAL_COMMAND_REQ_FRAME_CONTROL_A(
        cmd_data, ZB_ZCL_FRAME_DIRECTION_TO_CLI,
        is_manuf_spec, ZB_ZCL_ENABLE_DEFAULT_RESPONSE);
    
      if (is_manuf_spec)
      {
        zb_af_endpoint_desc_t *ep_desc = zb_af_get_endpoint_desc(rep_info->ep);
        zb_zcl_cluster_desc_t *cluster_desc = get_cluster_desc(ep_desc, rep_info->cluster_id, rep_info->cluster_role);
    
        ZB_ASSERT(ep_desc && cluster_desc && cluster_desc->manuf_code != ZB_ZCL_MANUF_CODE_INVALID);
    
        ZB_ZCL_CONSTRUCT_COMMAND_HEADER_EXT(cmd_data, ZB_ZCL_GET_SEQ_NUM(), ZB_TRUE, cluster_desc->manuf_code, ZB_ZCL_CMD_REPORT_ATTRIB);
      }
      else
      {
        ZB_ZCL_CONSTRUCT_COMMAND_HEADER(cmd_data, ZB_ZCL_GET_SEQ_NUM(), ZB_ZCL_CMD_REPORT_ATTRIB);
      }
    
      cur_rep_info = rep_info;
    
      while (cur_rep_info)
      {
        attr_desc =
          zb_zcl_get_attr_desc_a(cur_rep_info->ep, cur_rep_info->cluster_id, cur_rep_info->cluster_role, cur_rep_info->attr_id);
        TRACE_MSG(TRACE_ZCL3, "attr_desc %p", (FMT__P, attr_desc));
    
        /* attribute description could not be absent, it is checked while accepting configure report
           command */
        ZB_ASSERT(attr_desc);
    
        bytes_avail = ZB_ZCL_GET_BYTES_AVAILABLE(param, cmd_data,
                                                 cur_rep_info->dst.profile_id, cur_rep_info->cluster_id);
        TRACE_MSG(TRACE_ZCL3, "bytes_avail %hd", (FMT__H, bytes_avail));
    
        TRACE_MSG(
            TRACE_ZCL3,
            "attribute: id 0x%x, type 0x%hx",
            (FMT__D_H, attr_desc->id, attr_desc->type));
        attr_size = zb_zcl_get_attribute_size(attr_desc->type, attr_desc->data_p);
        TRACE_MSG(TRACE_ZCL3, "attr_size %hd", (FMT__H, attr_size));
    
        /* Decrement read attr response by sizeof(zb_uint8_t), because attr value size is
         * calculated separately by attr_size  */
        if (bytes_avail >= (sizeof(zb_zcl_report_attr_req_t) - sizeof(zb_uint8_t) + attr_size))
        {
          ZB_ZCL_PACKET_PUT_DATA16_VAL(cmd_data, attr_desc->id);
          ZB_ZCL_PACKET_PUT_DATA8(cmd_data, attr_desc->type);
          cmd_data = zb_zcl_put_attribute_value(cmd_data, attr_desc, attr_desc->data_p, attr_size);
    
          zb_zcl_save_reported_value(cur_rep_info, attr_desc);
    
          ZB_ZCL_CLR_REPORTING_FLAG(cur_rep_info, ZB_ZCL_REPORT_ATTR);
          ZB_ZCL_CLR_REPORTING_FLAG(cur_rep_info, ZB_ZCL_REPORT_IS_ALLOWED);
          ZB_ZCL_CLR_REPORTING_FLAG(cur_rep_info, ZB_ZCL_REPORT_TIMER_STARTED);
          ZB_ZCL_SET_REPORTING_FLAG(cur_rep_info, ZB_ZCL_REPORT_IS_SENT);
        }
        else
        {
          TRACE_MSG(TRACE_ZCL1, "ERROR, buffer is full", (FMT__0));
          break;
        }
    
        /* get next report info for sending report to the same remote device */
        cur_rep_info = zb_zcl_get_next_reporting_info(rep_info, is_manuf_spec);
        TRACE_MSG(TRACE_ZCL1, "cur_rep_info %p", (FMT__P, cur_rep_info));
      }
    
      /* We should always use bindings for reporting (ZCL8, 2.5.7.1.2; ZCL8, 2.5.11.2) */
      send_mode = ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
      TRACE_MSG(TRACE_ZCL1, "ZCL_REPORT send mode ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT:", (FMT__0));
    
      ZB_ZCL_FINISH_N_SEND_PACKET(param, cmd_data,
          rep_info->dst.short_addr,
          send_mode,
          rep_info->dst.endpoint,
          rep_info->ep /* src ep */,
          rep_info->dst.profile_id,
          rep_info->cluster_id,
          zb_zcl_reporting_cb);
    
      TRACE_MSG(TRACE_ZCL1, "<< zb_zcl_send_report_attr_command ", (FMT__0));
    }

    Especially that part : 

     if (is_manuf_spec)
      {
        zb_af_endpoint_desc_t *ep_desc = zb_af_get_endpoint_desc(rep_info->ep);
        zb_zcl_cluster_desc_t *cluster_desc = get_cluster_desc(ep_desc, rep_info->cluster_id, rep_info->cluster_role);
    
        ZB_ASSERT(ep_desc && cluster_desc && cluster_desc->manuf_code != ZB_ZCL_MANUF_CODE_INVALID);
    
        ZB_ZCL_CONSTRUCT_COMMAND_HEADER_EXT(cmd_data, ZB_ZCL_GET_SEQ_NUM(), ZB_TRUE, cluster_desc->manuf_code, ZB_ZCL_CMD_REPORT_ATTRIB);
      }

    is_manuf_spec is determined based on the attribute descriptor.

    Assuming the manufacturer-specific attribute within the standard cluster has a REPORTING access set, then we have to pass the ASSERT condition which is: 

    ZB_ASSERT(ep_desc && cluster_desc && cluster_desc->manuf_code != ZB_ZCL_MANUF_CODE_INVALID)
    AFAIU that single line forces us to give the standard cluster Manufacturer-specific code.

    Let me know what you think.

    Maybe things changed in NCS 2.6.0 and we are not aligned :) 

    I am looking forward to hearing from you

Related