Maximum payload size for Zigbee cluster command

Hello,

Setup:

nrf5340

ncs 2.9.1

Zigbee r23 add-on

I’ve implemented a custom ZCL cluster that acts as a data pipe. I’m trying to determine what the actual maximum payload size is for a ZCL command and whether APS fragmentation is functiona

Based on ZBOSS documentation, I believe I should be able to send roughly 8 APS fragments × ~80 bytes per APS payload ≈ ~640 bytes. As far as I know, APS fragmentation is automatic and does not require any Kconfig options.

In this ticket from 2 years ago:  [Zigbee] ZCL Tunneling cluster / APS Fragmentation 

In that thread, the poster found the maximum working payload was 52 bytes, and a Nordic engineer commented that “APS fragmentation won’t work.”
The thread did not end with a resolution.

In testing with my own configuration I've found that:

  • Using zb_buf_get_out, max working payload: 52 bytes

  • Using zb_buf_get_out_delayed_ext, max working payload: ~79 bytes (which matches expected APS single-frame MTU under security)

  • Sending > ~79 bytes:

    • zb_zcl_finish_and_send_packet_new() returns RET_OK

    • No frame is ever transmitted

    • No error callback or logs

  • Sending 256 bytes (requires APS fragmentation):

    • ZBOSS fatal error inside the stack

These results match the symptoms described in the older thread, which suggested APS fragmentation may not be functional.

My question:

Is APS fragmentation expected to work in the Zigbee R23 add-on for NCS 2.9.1 on nRF5340?
If so, is there any configuration required to enable it?
Or is this a known limitation of the current ZBOSS integration?

Here is the relevant part my implementation:

static void construct_command_frame(void)
{
    AppCommand_t *cmd = &app_ctx.command;

    app_ctx.sequence_number = ZCL_CTX().seq_number;

    app_ctx.packet_info.ptr =
        ZB_ZCL_START_PACKET_REQ(app_ctx.packet_info.buffer)

    ZB_ZCL_CONSTRUCT_SPECIFIC_COMMAND_REQ_FRAME_CONTROL(
        app_ctx.packet_info.ptr,
        cmd->default_response)
    ZB_ZCL_CONSTRUCT_COMMAND_HEADER_REQ(
        app_ctx.packet_info.ptr,
        ZB_ZCL_GET_SEQ_NUM(),
        cmd->command_id);

    for (zb_uint8_t i = 0; i < cmd->payload_length; i++)
    {
        ZB_ZCL_PACKET_PUT_DATA8(app_ctx.packet_info.ptr, cmd->payload[i]);
    }
}

static void send_command_frame(zb_uint8_t bufid)
{
    zb_ret_t zb_err_code;
    AppZclPacketInfo_t *packet_info = &app_ctx.packet_info;
    packet_info->buffer = bufid;

    construct_command_frame();

    zb_err_code = zb_zcl_finish_and_send_packet_new(
        packet_info->buffer,
        packet_info->ptr,
        &packet_info->destination_addr,
        packet_info->destination_addr_mode,
        packet_info->destination_endpoint,
        packet_info->source_endpoint,
        packet_info->profile_id,
        packet_info->cluster_id,
        NULL,
        0,
        packet_info->disable_aps_ack,
        0);
    if (zb_err_code != RET_OK)
    {
        LOG_ERR("Can not send ZCL frame");

        zb_osif_disable_all_inter();
        zb_buf_free(packet_info->buffer);
        zb_osif_enable_all_inter();
    }
}

AppReturnCode_t Zigbee_SendCustomResponse(uint8_t *payload, size_t payload_length)
{
    AppReturnCode_t errCode = AppReturnCode_Success;
    zb_bufid_t bufid;
    AppCommand_t *cmd;
    AppZclPacketInfo_t *packet_info;

    cmd         = &app_ctx.command;
    packet_info = &app_ctx.packet_info;

    memcpy(cmd->payload, payload, payload_length);
    cmd->payload_length = payload_length;

    packet_info->source_endpoint  = GENERIC_ENDPOINT;
    packet_info->disable_aps_ack  = ZB_FALSE;

    zb_ret_t err = zb_buf_get_out_delayed_ext(send_command_frame,
                                              0,
                                              cmd->payload_length);
    require_action(err == RET_OK, exit, errCode = AppReturnCode_NoResources);

exit:
    return errCode;
}


 Please let me know if you need any additional context or information to provide assistance.

Thanks,

Logan

Parents Reply Children
Related