NRF52840 Zigbee CLI example but in code

Hello,

I am trying to configure a Zigbee coordinator as done in the CLI example (for example 'bdb role zc' and 'bdb start') to get data from the multi sensor example, but instead of doing this over the CLI, I am trying to achieve this in the code itself. I am quite lost on where to look for the API calls that the CLI interacts with. Could you guide me to the right documentation?

Thanks,

Sebastiaan

  • Hi Sebastiaan,

    Could you please specify what functionality you want?

    You can find the implementation of CLI commands in the files located in <SDK_ROOT>/components/zigbee/cli. The commands you mention can be found in zigbee_cli_cmd_bdb.c, for example the function cmd_zb_start() for the 'bdb start' command. You will see in the functions that the CLI library does a lot of additional things and checks which are not needed if you are to use the Zigbee functionality directly.

    If you want to implement coordinator functionality you can look at the Zigbee Light Coordinator from the Light Control example, which can be found in <SDK_ROOT>/examples/zigbee/light_control/light_coordinator. This is a simple coordinator with no additional functionality other than being a Zigbee coordinator.

    To set the coordinator role use the following:

    zb_set_network_coordinator_role(IEEE_CHANNEL_MASK);

    For the coordinator you must also set maximum children. This can for example be set to 10:

    zb_set_max_children(MAX_CHILDREN);

    To start the stack ('bdb start') you must use one of the zboss start functions, either zboss_start() or zboss_start_no_autostart(), depending on whether you want to initialize the radio and start commissioning, or if you want to delay commissioning.

    Best regards,

    Marte

  • Hello Marte, thanks for your reply!

    I am just trying to execute the CLI logic but in code

    So I want to have a coordinator that scans the devices in the network (from the multi sensor example the temperature and pressure sensors).

    I want to then bind and subscribe on those as done in the CLI example. Finally, I would like to print out the values received.

    Thanks,

    Sebastiaan 

  • Hi Sebastiaan,

    You will find the implementation of all of these commands in the CLI library files. If you search for a command in the files, e.g. 'zdo match_desc', you should be able to find the relevant code, as the commands are in the comment description of the respective functions.

    To scan for devices with the temperature and pressure clusters in the network you send a match descriptor request with those two clusters, using zb_zdo_match_desc_req(). There is an example of how to use this in the documentation of the function. It is also used in the light switch example to find light bulbs, so you can look there as well. You must create a buffer with the parameters of zb_zdo_match_desc_param_t (see zb_zdo_match_desc_param_s for a description of the parameters), as you can see is done in the examples I mentioned. The command should be broadcasted, so nwk_addr og addr_of_interest should be either ZB_NWK_BROADCAST_ALL_DEVICES or ZB_NWK_BROADCAST_RX_ON_WHEN_IDLE. You can find the available cluster IDs in zb_zcl_cluster_id_e. Use the callback function of the match descriptor request to get the response, so that you get the address and endpoint of the  temperature/pressure device, as in the light switch example (find_light_bulb_cb).

    To bind the endpoints, send a bind request using zb_zdo_bind_req(). An example of how to use this is in the documentation of the function. Create a buffer for zb_zdo_bind_req_param_t (see zb_zdo_bind_req_param_s). Use ZB_APS_ADDR_MODE_16_ENDP_PRESENT as address mode, as you have the 16-bit short address of the device from the match descriptor request.

    The 'zcl subscribe' command configures attribute reporting. For this I would recommend looking at how this is implemented in the CLI library, which you can find in the file zigbee_cli_cmd_attr_report.c. You should also check out Configure reporting command sending and parsing and Report attribute command parsing in our documentation.

    Best regards,

    Marte

  • Hi Marte,

    Thank you for your reply.

    I am currently implementing it as you suggested.

    First, I send a match description with two in clusters (0x0402 and 0x0403). This is then linked to a callback function which checks if there is a match (length > 0).

    In this callback function, I am currently trying to bind to the cluster with hardcoded values. For the simulated pressure and temperature sensor, the long address is "0xf4ce36f7a5db776c" and for the coordinator it is "0xf4ce36bccb118deb".

    I am hardcoding the bind request as following:

    zb_zdo_bind_req_param_t * p_req;
    zb_bufid_t                buffer_test;
    zb_ret_t                  zb_err_code;
    zb_bool_t                 bind;
    buffer_test = zb_buf_get_out();
    
    p_req = ZB_BUF_GET_PARAM(buffer_test, zb_zdo_bind_req_param_t);
    
    // Set temp/pressure sensor addr
    p_req->src_address[0] = 0xF4;
    p_req->src_address[1] = 0xCE;
    p_req->src_address[2] = 0x36;
    p_req->src_address[3] = 0xF7;
    p_req->src_address[4] = 0xA5;
    p_req->src_address[5] = 0xDB;
    p_req->src_address[6] = 0x77;
    p_req->src_address[7] = 0x6C;
    
    p_req->src_endp = 10;
    
    // Set coordinator addr
    p_req->dst_address.addr_long[0] = 0xF4;
    p_req->dst_address.addr_long[1] = 0xCE;
    p_req->dst_address.addr_long[2] = 0x36;
    p_req->dst_address.addr_long[3] = 0xBC;
    p_req->dst_address.addr_long[4] = 0xCB;
    p_req->dst_address.addr_long[5] = 0x11;
    p_req->dst_address.addr_long[6] = 0x8D;
    p_req->dst_address.addr_long[7] = 0xEB;
    
    p_req->dst_endp = 64;
    p_req->cluster_id = 0x0402;
    p_req->req_dst_addr = 0xECA1;
    p_req->dst_addr_mode = ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
    
    zb_zdo_bind_req(buffer_test, zb_bind_callback);
    

    However, when the bind function is executed, it hangs on the line and the callback function is not called. I see on the coordinator debug that it states "Uninplemented signal (signal 21, status 0)

    Any idea how to progress?

  • Hi Sebastiaan,

    You should use ZB_BIND_DST_ADDR_MODE_64_BIT_EXTENDED as destination address mode for the bind request. Additionally, source endpoint and source address should be that of the multi sensor, and not of the coordinator.

    You should not hardcode in the short address of the device, as this address is not static and will change if the network data on the device is erased. This is what you should use the match descriptor for, as the match descriptor will give you the short address and endpoint of the device. Please see the light switch example to see how to use the match descriptor request for this.

    Hardcoding the long addresses will work in this case, as they will not changed, but it is not recommended. A device can get it's own long address with the function zb_osif_get_ieee_eui64():

    zb_ieee_addr_t ieee_addr;
    zb_osif_get_ieee_eui64(ieee_addr);

    Getting another device's long address is a bit more work, but if you have the short address, which you should have from the match descriptor request in this case, you can get it by sending a an IEEE_addr_req with zb_zdo_ieee_addr_req(). In the case of the light switch example where the light bulb short address is m_device_ctx.bulb_params.short_addr you can get the long address with the following code:

    /**@brief A callback called on EUI64 address response.
     *
     * @param[in] bufid Reference number to ZBOSS memory buffer.
     */
    static zb_void_t zb_resolve_ieee_addr_cb(zb_bufid_t bufid)
    {
        zb_zdo_ieee_addr_resp_t * p_resp = (zb_zdo_ieee_addr_resp_t *)zb_buf_begin(bufid);
        zb_char_t addr_buf[2 * 8 + 1];    /* 8 bytes (2 characters) plus one byte for null-terminator. */
    
        if (p_resp->status == ZB_ZDP_STATUS_SUCCESS)
        {
            ZB_LETOH64(m_device_ctx.bulb_params.nwk_addr.addr_long, p_resp->ieee_addr_remote_dev);
            UNUSED_RETURN_VALUE(ieee_addr_to_str(addr_buf, sizeof(addr_buf), m_device_ctx.bulb_params.nwk_addr.addr_long));
            NRF_LOG_INFO("Received EUI64 address for device 0x%04x -> %s.", m_device_ctx.bulb_params.short_addr, NRF_LOG_PUSH(addr_buf));
    
        }
        else
        {
            NRF_LOG_WARNING("Unable to resolve EUI64 source address. Status: %d\r\n",
                            p_resp->status);
        }
    
        zb_buf_free(bufid);
    }
    
    
    /**@brief Resolve EUI64 by sending IEEE address request.
     *
     * @param[in] bufid     Reference number to ZBOSS memory buffer.
     * @param[in] nwk_addr  Network address to be resolved.
     */
    static zb_void_t zb_resolve_ieee_addr()
    {
        zb_zdo_ieee_addr_req_param_t * p_req = NULL;
        zb_uint8_t                     tsn = 0;
        zb_bufid_t                     bufid;
        bufid = zb_buf_get_out();
    
        // Create new IEEE address request and fill with default values.
        p_req = ZB_BUF_GET_PARAM(bufid, zb_zdo_ieee_addr_req_param_t);
        p_req->start_index  = 0;
        p_req->request_type = 0;
        p_req->nwk_addr     = m_device_ctx.bulb_params.short_addr;
        p_req->dst_addr     = p_req->nwk_addr;
        tsn = zb_zdo_ieee_addr_req(bufid, zb_resolve_ieee_addr_cb);
        if (tsn == ZB_ZDO_INVALID_TSN)
        {
            NRF_LOG_WARNING("Failed to send IEEE address request for address: 0x%04x", m_device_ctx.bulb_params.short_addr);
            zb_buf_free(bufid);
        }
    }

    Note that here I have added zb_addr_u nwk_addr to light_switch_sensor_params_t and is using that to store the long address of the light bulb.

    Best regards,

    Marte

Related